kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

ranlib.c (7725B)


      1 #include <kit/archive.h>
      2 #include <kit/core.h>
      3 #include <kit/object.h>
      4 #include <stdint.h>
      5 
      6 #include "driver.h"
      7 #include "inputs.h"
      8 
      9 /* `kit ranlib` — refresh / add a System-V `/` symbol-index member at the
     10  * head of an existing POSIX `ar` archive. Equivalent to `kit ar s ARCHIVE`,
     11  * which is reserved by the POSIX ar grammar but not yet implemented in the
     12  * ar tool. ranlib is the conventional name and the one Makefiles invoke.
     13  *
     14  * Operation: read all members, rebuild the archive at the same path with
     15  * symbol_index=1 and per-member globally-defined symbols filled in. Long
     16  * member names are preserved via the `//` extended-name table. Reproducible
     17  * output via SOURCE_DATE_EPOCH (same epoch handling as `kit ar`).
     18  *
     19  * Per-member symbol collection lives in driver/inputs.c
     20  * (driver_collect_obj_global_syms) — shared with ar / strip. */
     21 
     22 #define RANLIB_TOOL "ranlib"
     23 
     24 void driver_help_ranlib(void) {
     25   driver_printf(
     26       "%.*s",
     27       KIT_SLICE_ARG(KIT_SLICE_LIT(
     28           "kit ranlib — refresh the symbol index of an `ar` archive\n"
     29           "\n"
     30           "USAGE\n"
     31           "  kit ranlib ARCHIVE.a\n"
     32           "\n"
     33           "DESCRIPTION\n"
     34           "  Reads every member of ARCHIVE.a, rebuilds the archive in place\n"
     35           "  with a System-V `/` symbol-index member at the head. Member "
     36           "names,\n"
     37           "  contents, and order are preserved. Reproducible: when\n"
     38           "  SOURCE_DATE_EPOCH is set to a positive integer, that value is\n"
     39           "  written to ar_date for every member.\n"
     40           "\n"
     41           "  Equivalent to `kit ar s ARCHIVE.a` (the bare `s` modifier is\n"
     42           "  reserved by the POSIX ar grammar but not yet implemented).\n"
     43           "\n"
     44           "OPTIONS\n"
     45           "  -h, --help        Show this help and exit\n"
     46           "\n"
     47           "EXIT CODES\n"
     48           "  0   success           1   archive I/O error           2   bad "
     49           "usage\n")));
     50 }
     51 
     52 static uint64_t ranlib_epoch_from_env(void) {
     53   const char* s = driver_getenv("SOURCE_DATE_EPOCH");
     54   uint64_t v = 0;
     55   if (!s || !*s) return 0;
     56   for (; *s; ++s) {
     57     if (*s < '0' || *s > '9') return 0;
     58     v = v * 10 + (uint64_t)(*s - '0');
     59   }
     60   return v;
     61 }
     62 
     63 int driver_ranlib(int argc, char** argv) {
     64   DriverEnv env;
     65   KitContext ctx;
     66   KitFileData old_fd = {0};
     67   KitSlice input;
     68   KitArIter* it = NULL;
     69   KitArMember m;
     70   KitArInput* members = NULL;
     71   char* name_storage = NULL;
     72   size_t name_bytes_total = 0;
     73   uint32_t nmembers = 0;
     74   uint32_t i;
     75   KitArMemberSymbols* msyms = NULL;
     76   void** sym_allocs = NULL;
     77   size_t* sym_alloc_szs = NULL;
     78   KitWriter* out = NULL;
     79   KitArWriteOptions opts = {0};
     80   const char* archive_path;
     81   int have_old = 0;
     82   int rc = 1;
     83 
     84   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
     85     driver_help_ranlib();
     86     return 0;
     87   }
     88   if (argc != 2) {
     89     driver_errf(RANLIB_TOOL, "usage: kit ranlib ARCHIVE.a");
     90     return 2;
     91   }
     92   archive_path = argv[1];
     93 
     94   driver_env_init(&env);
     95   ctx = driver_env_to_context(&env);
     96 
     97   if (ctx.file_io->read_all(ctx.file_io->user, archive_path, &old_fd) !=
     98       KIT_OK) {
     99     driver_errf(RANLIB_TOOL, "failed to read: %.*s",
    100                 KIT_SLICE_ARG(kit_slice_cstr(archive_path)));
    101     goto out;
    102   }
    103   have_old = 1;
    104   input.data = old_fd.data;
    105   input.len = old_fd.size;
    106 
    107   /* Pass 1: count members and total name bytes (member names returned by
    108    * the iterator alias an internal buffer overwritten on each next(), so
    109    * we stash a stable copy). */
    110   if (kit_ar_iter_new(&ctx, &input, &it) != KIT_OK) {
    111     driver_errf(RANLIB_TOOL, "not an archive: %.*s",
    112                 KIT_SLICE_ARG(kit_slice_cstr(archive_path)));
    113     goto out;
    114   }
    115   for (;;) {
    116     KitIterResult r = kit_ar_iter_next(it, &m);
    117     if (r != KIT_ITER_ITEM) break;
    118     nmembers++;
    119     name_bytes_total += m.name.len + 1;
    120   }
    121   kit_ar_iter_free(it);
    122   it = NULL;
    123 
    124   if (nmembers == 0) {
    125     /* Empty archive — still rewrite with an empty symbol index (matches
    126      * GNU ranlib's behaviour). */
    127     if (ctx.file_io->open_writer(ctx.file_io->user, archive_path, &out) !=
    128         KIT_OK) {
    129       driver_errf(RANLIB_TOOL, "failed to open: %.*s",
    130                   KIT_SLICE_ARG(kit_slice_cstr(archive_path)));
    131       goto out;
    132     }
    133     opts.epoch = ranlib_epoch_from_env();
    134     opts.long_names = 1;
    135     opts.symbol_index = 1;
    136     rc = kit_ar_write(out, NULL, 0, &opts) == KIT_OK ? 0 : 1;
    137     goto out;
    138   }
    139 
    140   members = (KitArInput*)driver_alloc_zeroed(
    141       &env, (size_t)nmembers * sizeof(*members));
    142   if (!members) {
    143     driver_errf(RANLIB_TOOL, "out of memory");
    144     goto out;
    145   }
    146   if (name_bytes_total > 0) {
    147     name_storage = (char*)driver_alloc_zeroed(&env, name_bytes_total);
    148     if (!name_storage) {
    149       driver_errf(RANLIB_TOOL, "out of memory");
    150       goto out;
    151     }
    152   }
    153 
    154   /* Pass 2: copy names and member byte-spans into our parallel arrays. */
    155   if (kit_ar_iter_new(&ctx, &input, &it) != KIT_OK) {
    156     driver_errf(RANLIB_TOOL, "iter re-open failed");
    157     goto out;
    158   }
    159   {
    160     size_t cursor = 0;
    161     uint32_t k = 0;
    162     while (k < nmembers) {
    163       KitIterResult r = kit_ar_iter_next(it, &m);
    164       char* dst;
    165       size_t j;
    166       if (r != KIT_ITER_ITEM) break;
    167       dst = name_storage + cursor;
    168       for (j = 0; j < m.name.len; ++j) *dst++ = m.name.s[j];
    169       *dst++ = '\0';
    170       members[k].name.s = name_storage + cursor;
    171       members[k].name.len = m.name.len;
    172       members[k].bytes.data = m.data;
    173       members[k].bytes.len = m.size;
    174       cursor = (size_t)(dst - name_storage);
    175       k++;
    176     }
    177   }
    178   kit_ar_iter_free(it);
    179   it = NULL;
    180 
    181   /* Pass 3: collect per-member global symbols. */
    182   msyms = (KitArMemberSymbols*)driver_alloc_zeroed(
    183       &env, (size_t)nmembers * sizeof(*msyms));
    184   sym_allocs =
    185       (void**)driver_alloc_zeroed(&env, (size_t)nmembers * sizeof(*sym_allocs));
    186   sym_alloc_szs = (size_t*)driver_alloc_zeroed(
    187       &env, (size_t)nmembers * sizeof(*sym_alloc_szs));
    188   if (!msyms || !sym_allocs || !sym_alloc_szs) {
    189     driver_errf(RANLIB_TOOL, "out of memory");
    190     goto out;
    191   }
    192   for (i = 0; i < nmembers; ++i) {
    193     void* blob = NULL;
    194     size_t blob_size = 0;
    195     const KitSlice* names = NULL;
    196     uint32_t count = 0;
    197     if (driver_collect_obj_global_syms(&env, &ctx, RANLIB_TOOL,
    198                                        &members[i].bytes, &blob, &blob_size,
    199                                        &names, &count) != 0) {
    200       goto out;
    201     }
    202     sym_allocs[i] = blob;
    203     sym_alloc_szs[i] = blob_size;
    204     msyms[i].names = names;
    205     msyms[i].count = count;
    206   }
    207 
    208   if (ctx.file_io->open_writer(ctx.file_io->user, archive_path, &out) !=
    209       KIT_OK) {
    210     driver_errf(RANLIB_TOOL, "failed to open: %.*s",
    211                 KIT_SLICE_ARG(kit_slice_cstr(archive_path)));
    212     goto out;
    213   }
    214   opts.epoch = ranlib_epoch_from_env();
    215   opts.long_names = 1;
    216   opts.symbol_index = 1;
    217   opts.member_symbols = msyms;
    218   rc = kit_ar_write(out, members, nmembers, &opts) == KIT_OK ? 0 : 1;
    219   if (rc == 0 && kit_writer_status(out) != KIT_OK) rc = 1;
    220 
    221 out:
    222   if (out) kit_writer_close(out);
    223   if (it) kit_ar_iter_free(it);
    224   if (sym_allocs) {
    225     for (i = 0; i < nmembers; ++i) {
    226       if (sym_allocs[i]) driver_free(&env, sym_allocs[i], sym_alloc_szs[i]);
    227     }
    228     driver_free(&env, sym_allocs, (size_t)nmembers * sizeof(*sym_allocs));
    229   }
    230   if (sym_alloc_szs)
    231     driver_free(&env, sym_alloc_szs, (size_t)nmembers * sizeof(*sym_alloc_szs));
    232   if (msyms) driver_free(&env, msyms, (size_t)nmembers * sizeof(*msyms));
    233   if (name_storage) driver_free(&env, name_storage, name_bytes_total);
    234   if (members) driver_free(&env, members, (size_t)nmembers * sizeof(*members));
    235   if (have_old) ctx.file_io->release(ctx.file_io->user, &old_fd);
    236   driver_env_fini(&env);
    237   return rc;
    238 }