kit

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

strip.c (20180B)


      1 #include <kit/archive.h>
      2 #include <kit/core.h>
      3 #include <kit/object.h>
      4 #include <stdint.h>
      5 #include <string.h>
      6 
      7 #include "driver.h"
      8 #include "inputs.h"
      9 
     10 /* `kit strip` — drop debug sections and / or unwanted symbols from a
     11  * relocatable object or static archive, then write the result back. Scope
     12  * for the first cut matches the CTOOLCHAIN.md plan: relocatable .o and
     13  * .a inputs only — linked ELF (ET_EXEC / ET_DYN) is rejected.
     14  *
     15  * Operations (the last one wins; default is --strip-all):
     16  *   --strip-debug        drop sections whose kind is KIT_SEC_DEBUG
     17  *   --strip-unneeded     drop debug + symbols not referenced by any reloc
     18  *   --strip-all          drop debug + every non-essential symbol (default)
     19  *
     20  * Filters applied on top of the operation:
     21  *   --keep-symbol=NAME, -K NAME    keep NAME even if the operation would drop
     22  * it
     23  *   --strip-symbol=NAME, -N NAME   always drop NAME
     24  *
     25  * I/O:
     26  *   -o PATH              write output to PATH (else rewrite the input in place)
     27  */
     28 
     29 #define STRIP_TOOL "strip"
     30 
     31 void driver_help_strip(void) {
     32   driver_printf(
     33       "%.*s",
     34       KIT_SLICE_ARG(KIT_SLICE_LIT(
     35           "kit strip — drop debug sections and/or symbols\n"
     36           "\n"
     37           "USAGE\n"
     38           "  kit strip [OPTIONS] FILE\n"
     39           "\n"
     40           "OPERATIONS (last one wins; default is --strip-all)\n"
     41           "  --strip-debug         remove debug-info sections\n"
     42           "  --strip-unneeded      remove debug + symbols not needed by "
     43           "relocs\n"
     44           "  --strip-all           remove debug + all non-essential symbols\n"
     45           "\n"
     46           "SYMBOL FILTERS (may repeat)\n"
     47           "  --keep-symbol=NAME, -K NAME   keep NAME even when the operation\n"
     48           "                                would otherwise drop it\n"
     49           "  --strip-symbol=NAME, -N NAME  always drop NAME\n"
     50           "\n"
     51           "OUTPUT\n"
     52           "  -o PATH               write to PATH (default: rewrite FILE in "
     53           "place)\n"
     54           "\n"
     55           "INPUTS\n"
     56           "  FILE may be a relocatable .o or a static .a archive. Linked\n"
     57           "  executables / shared libraries are not supported yet.\n"
     58           "\n"
     59           "EXIT CODES\n"
     60           "  0   success           1   I/O or strip error           2   bad "
     61           "usage\n")));
     62 }
     63 
     64 typedef enum StripOp {
     65   STRIP_OP_DEBUG,
     66   STRIP_OP_UNNEEDED,
     67   STRIP_OP_ALL,
     68 } StripOp;
     69 
     70 typedef struct StripOpts {
     71   StripOp op;
     72   const char** keep;
     73   uint32_t nkeep;
     74   uint32_t cap_keep;
     75   const char** strip;
     76   uint32_t nstrip;
     77   uint32_t cap_strip;
     78   const char* output;
     79   const char* input;
     80 } StripOpts;
     81 
     82 static int name_in_list(KitSlice name, const char* const* list, uint32_t n) {
     83   uint32_t i;
     84   if (!name.len) return 0;
     85   for (i = 0; i < n; ++i) {
     86     if (list[i] && kit_slice_eq_cstr(name, list[i])) return 1;
     87   }
     88   return 0;
     89 }
     90 
     91 static int push_name(DriverEnv* env, const char*** arr, uint32_t* n,
     92                      uint32_t* cap, const char* name) {
     93   if (*n >= *cap) {
     94     uint32_t newcap = *cap ? *cap * 2u : 8u;
     95     const char** nb =
     96         (const char**)driver_alloc_zeroed(env, (size_t)newcap * sizeof(*nb));
     97     if (!nb) {
     98       driver_errf(STRIP_TOOL, "out of memory");
     99       return -1;
    100     }
    101     if (*arr) {
    102       memcpy(nb, *arr, (size_t)(*n) * sizeof(*nb));
    103       driver_free(env, (void*)*arr, (size_t)(*cap) * sizeof(*nb));
    104     }
    105     *arr = nb;
    106     *cap = newcap;
    107   }
    108   (*arr)[(*n)++] = name;
    109   return 0;
    110 }
    111 
    112 static int parse_name_arg(int* i, int argc, char** argv, const char* flag,
    113                           const char* short_flag, const char** out) {
    114   const char* a = argv[*i];
    115   size_t flen = kit_slice_cstr(flag).len;
    116   /* --flag=NAME ; prefix match */
    117   if (driver_strneq(a, flag, flen) && a[flen] == '=') {
    118     *out = a + flen + 1;
    119     return 1;
    120   }
    121   /* --flag NAME (no '='): treat as "next argv" form, used when --flag is
    122    * passed without value. Not standard; skip. */
    123   if (driver_streq(a, flag)) {
    124     if (*i + 1 >= argc) return -1;
    125     *out = argv[++(*i)];
    126     return 1;
    127   }
    128   /* -K NAME / -N NAME */
    129   if (short_flag && driver_streq(a, short_flag)) {
    130     if (*i + 1 >= argc) return -1;
    131     *out = argv[++(*i)];
    132     return 1;
    133   }
    134   return 0;
    135 }
    136 
    137 /* Collect the set of KitObjSymbol ids targeted by any reloc whose
    138  * containing section will survive emit — relocs inside the
    139  * about-to-be-removed KIT_SEC_DEBUG sections don't count. Otherwise a
    140  * symbol that's referenced only from DWARF (e.g. main's debug_info entry)
    141  * keeps every function symbol alive even though the on-disk relocs
    142  * holding it won't make it to the output. */
    143 static int collect_needed_syms(DriverEnv* env, KitObjFile* of,
    144                                KitObjSymbol** needed_out, uint32_t* n_out,
    145                                uint32_t* cap_out) {
    146   KitObjRelocIter* rit = NULL;
    147   KitObjSymbol* arr = NULL;
    148   uint32_t n = 0, cap = 0;
    149 
    150   if (kit_obj_reliter_new(of, &rit) != KIT_OK) {
    151     driver_errf(STRIP_TOOL, "out of memory");
    152     return 1;
    153   }
    154   for (;;) {
    155     KitObjReloc r;
    156     KitIterResult ir = kit_obj_reliter_next(rit, &r);
    157     uint32_t k;
    158     int seen = 0;
    159     if (ir != KIT_ITER_ITEM) break;
    160     if (r.sym == KIT_OBJ_SYMBOL_NONE) continue;
    161     /* Skip relocs hosted in a debug section — that section is being
    162      * dropped, so its relocs don't actually "need" their targets. */
    163     if (r.section != KIT_SECTION_NONE) {
    164       KitObjSecInfo hi;
    165       if (kit_obj_section(of, r.section, &hi) == KIT_OK &&
    166           hi.kind == KIT_SEC_DEBUG) {
    167         continue;
    168       }
    169     }
    170     for (k = 0; k < n; ++k) {
    171       if (arr[k] == r.sym) {
    172         seen = 1;
    173         break;
    174       }
    175     }
    176     if (seen) continue;
    177     if (n >= cap) {
    178       uint32_t newcap = cap ? cap * 2u : 32u;
    179       KitObjSymbol* nb =
    180           (KitObjSymbol*)driver_alloc_zeroed(env, (size_t)newcap * sizeof(*nb));
    181       if (!nb) {
    182         kit_obj_reliter_free(rit);
    183         if (arr) driver_free(env, arr, (size_t)cap * sizeof(*arr));
    184         driver_errf(STRIP_TOOL, "out of memory");
    185         return 1;
    186       }
    187       if (arr) {
    188         memcpy(nb, arr, (size_t)n * sizeof(*arr));
    189         driver_free(env, arr, (size_t)cap * sizeof(*arr));
    190       }
    191       arr = nb;
    192       cap = newcap;
    193     }
    194     arr[n++] = r.sym;
    195   }
    196   kit_obj_reliter_free(rit);
    197   *needed_out = arr;
    198   *n_out = n;
    199   *cap_out = cap;
    200   return 0;
    201 }
    202 
    203 static int id_in_set(KitObjSymbol id, const KitObjSymbol* arr, uint32_t n) {
    204   uint32_t i;
    205   for (i = 0; i < n; ++i) {
    206     if (arr[i] == id) return 1;
    207   }
    208   return 0;
    209 }
    210 
    211 /* The core strip pass: drop debug sections, then walk symbols and apply
    212  * keep/strip lists and the operation policy. Mutations are issued
    213  * against the builder; emit-time sweep cleans up cascades (orphan
    214  * relocs against removed sections, dropped group memberships, etc.). */
    215 static int strip_one_builder(DriverEnv* env, KitObjFile* of, KitObjBuilder* b,
    216                              const StripOpts* opts) {
    217   uint32_t i, nsec;
    218   int filter_syms = (opts->op == STRIP_OP_UNNEEDED || opts->op == STRIP_OP_ALL);
    219   KitObjSymbol* needed = NULL;
    220   uint32_t nneeded = 0, cap_needed = 0;
    221   KitObjSymIter* sit = NULL;
    222   int rc = 1;
    223 
    224   /* Step 1: drop debug sections (every supported op does this). */
    225   nsec = kit_obj_nsections(of);
    226   for (i = 0; i < nsec; ++i) {
    227     KitObjSecInfo si;
    228     if (kit_obj_section(of, i, &si) != KIT_OK) continue;
    229     if (si.kind == KIT_SEC_DEBUG) {
    230       kit_obj_builder_remove_section(b, i);
    231     }
    232   }
    233 
    234   /* Step 2: compute the needed-sym set. */
    235   if (filter_syms) {
    236     if (collect_needed_syms(env, of, &needed, &nneeded, &cap_needed) != 0) {
    237       return 1;
    238     }
    239   }
    240 
    241   /* Step 3: walk symbols and apply filters. */
    242   if (kit_obj_symiter_new(of, &sit) != KIT_OK) {
    243     driver_errf(STRIP_TOOL, "out of memory");
    244     goto done;
    245   }
    246   for (;;) {
    247     KitObjSymInfo si;
    248     KitIterResult ir = kit_obj_symiter_next(sit, &si);
    249     int drop = 0;
    250     if (ir != KIT_ITER_ITEM) break;
    251     /* --strip-symbol wins over --keep-symbol if both list the same name. */
    252     if (opts->nstrip && name_in_list(si.name, opts->strip, opts->nstrip)) {
    253       drop = 1;
    254     } else if (opts->nkeep && name_in_list(si.name, opts->keep, opts->nkeep)) {
    255       drop = 0;
    256     } else if (filter_syms) {
    257       /* Keep undefined externals so the .o stays linkable; keep symbols
    258        * targeted by a surviving reloc; drop everything else. Note that
    259        * section symbols defined in removed debug sections are already
    260        * tombstoned by the emit-time sweep cascade — no explicit handling
    261        * needed here. */
    262       if (si.kind == KIT_SK_UNDEF) {
    263         drop = 0;
    264       } else if (id_in_set(si.id, needed, nneeded)) {
    265         drop = 0;
    266       } else {
    267         drop = 1;
    268       }
    269     }
    270     if (drop) {
    271       kit_obj_builder_remove_symbol(b, si.id);
    272     }
    273   }
    274   kit_obj_symiter_free(sit);
    275   rc = 0;
    276 
    277 done:
    278   if (needed) driver_free(env, needed, (size_t)cap_needed * sizeof(*needed));
    279   return rc;
    280 }
    281 
    282 static int strip_object_bytes(DriverEnv* env, const KitContext* ctx,
    283                               const KitSlice* input, const StripOpts* opts,
    284                               uint8_t** out_data, size_t* out_size) {
    285   KitObjFile* of = NULL;
    286   KitObjBuilder* b;
    287   KitWriter* w = NULL;
    288   size_t n = 0;
    289   const uint8_t* data;
    290   uint8_t* copy;
    291   int rc = 1;
    292 
    293   *out_data = NULL;
    294   *out_size = 0;
    295 
    296   if (kit_obj_open(ctx, KIT_SLICE_NULL, input, &of) != KIT_OK) {
    297     driver_errf(STRIP_TOOL, "not a recognized object");
    298     return 1;
    299   }
    300   b = kit_obj_file_builder(of);
    301   if (!b) {
    302     driver_errf(STRIP_TOOL, "no builder for object");
    303     kit_obj_free(of);
    304     return 1;
    305   }
    306 
    307   if (strip_one_builder(env, of, b, opts) != 0) {
    308     kit_obj_free(of);
    309     return 1;
    310   }
    311 
    312   if (kit_writer_mem(env->heap, &w) != KIT_OK || !w) {
    313     driver_errf(STRIP_TOOL, "out of memory");
    314     kit_obj_free(of);
    315     return 1;
    316   }
    317   if (kit_obj_builder_emit(b, w) != KIT_OK) {
    318     driver_errf(STRIP_TOOL, "emit failed");
    319     kit_writer_close(w);
    320     kit_obj_free(of);
    321     return 1;
    322   }
    323   data = kit_writer_mem_bytes(w, &n);
    324   copy = (uint8_t*)driver_alloc(env, n ? n : 1u);
    325   if (!copy) {
    326     driver_errf(STRIP_TOOL, "out of memory");
    327     kit_writer_close(w);
    328     kit_obj_free(of);
    329     return 1;
    330   }
    331   if (n) memcpy(copy, data, n);
    332   kit_writer_close(w);
    333   kit_obj_free(of);
    334 
    335   *out_data = copy;
    336   *out_size = n;
    337   rc = 0;
    338   return rc;
    339 }
    340 
    341 static uint64_t strip_epoch_from_env(void) {
    342   const char* s = driver_getenv("SOURCE_DATE_EPOCH");
    343   uint64_t v = 0;
    344   if (!s || !*s) return 0;
    345   for (; *s; ++s) {
    346     if (*s < '0' || *s > '9') return 0;
    347     v = v * 10 + (uint64_t)(*s - '0');
    348   }
    349   return v;
    350 }
    351 
    352 /* Strip every object member of an archive, write a fresh archive with
    353  * a refreshed System-V symbol index. Non-object members pass through
    354  * unchanged. */
    355 static int strip_archive(DriverEnv* env, const KitContext* ctx,
    356                          const KitSlice* input, const StripOpts* opts,
    357                          const char* output_path) {
    358   KitArIter* it = NULL;
    359   KitArMember m;
    360   KitArInput* members = NULL;
    361   uint8_t** owned_data = NULL;
    362   size_t* owned_size = NULL;
    363   char* name_storage = NULL;
    364   size_t name_bytes_total = 0;
    365   uint32_t nmembers = 0, k;
    366   KitArMemberSymbols* msyms = NULL;
    367   void** sym_allocs = NULL;
    368   size_t* sym_alloc_szs = NULL;
    369   KitWriter* out = NULL;
    370   KitArWriteOptions opts_ar = {0};
    371   int rc = 1;
    372 
    373   /* Pass 1: count members + total name bytes. */
    374   if (kit_ar_iter_new(ctx, input, &it) != KIT_OK) {
    375     driver_errf(STRIP_TOOL, "not an archive");
    376     return 1;
    377   }
    378   for (;;) {
    379     KitIterResult r = kit_ar_iter_next(it, &m);
    380     if (r != KIT_ITER_ITEM) break;
    381     nmembers++;
    382     name_bytes_total += m.name.len + 1;
    383   }
    384   kit_ar_iter_free(it);
    385   it = NULL;
    386 
    387   if (nmembers) {
    388     members = (KitArInput*)driver_alloc_zeroed(
    389         env, (size_t)nmembers * sizeof(*members));
    390     owned_data = (uint8_t**)driver_alloc_zeroed(
    391         env, (size_t)nmembers * sizeof(*owned_data));
    392     owned_size = (size_t*)driver_alloc_zeroed(
    393         env, (size_t)nmembers * sizeof(*owned_size));
    394     if (!members || !owned_data || !owned_size) {
    395       driver_errf(STRIP_TOOL, "out of memory");
    396       goto done;
    397     }
    398   }
    399   if (name_bytes_total) {
    400     name_storage = (char*)driver_alloc_zeroed(env, name_bytes_total);
    401     if (!name_storage) {
    402       driver_errf(STRIP_TOOL, "out of memory");
    403       goto done;
    404     }
    405   }
    406 
    407   /* Pass 2: walk members; strip object members, pass others through. */
    408   if (kit_ar_iter_new(ctx, input, &it) != KIT_OK) {
    409     driver_errf(STRIP_TOOL, "iter re-open failed");
    410     goto done;
    411   }
    412   {
    413     size_t cursor = 0;
    414     k = 0;
    415     while (k < nmembers) {
    416       KitIterResult r = kit_ar_iter_next(it, &m);
    417       size_t j;
    418       char* dst;
    419       KitBinFmt fmt;
    420       KitSlice mbytes;
    421       if (r != KIT_ITER_ITEM) break;
    422       dst = name_storage + cursor;
    423       for (j = 0; j < m.name.len; ++j) *dst++ = m.name.s[j];
    424       *dst++ = '\0';
    425       members[k].name.s = name_storage + cursor;
    426       members[k].name.len = m.name.len;
    427       cursor = (size_t)(dst - name_storage);
    428 
    429       mbytes.data = m.data;
    430       mbytes.len = m.size;
    431       fmt = kit_detect_fmt(m.data, m.size);
    432       if (fmt == KIT_BIN_ELF || fmt == KIT_BIN_COFF || fmt == KIT_BIN_MACHO ||
    433           fmt == KIT_BIN_WASM) {
    434         uint8_t* sd = NULL;
    435         size_t ss = 0;
    436         if (strip_object_bytes(env, ctx, &mbytes, opts, &sd, &ss) != 0) {
    437           kit_ar_iter_free(it);
    438           it = NULL;
    439           goto done;
    440         }
    441         owned_data[k] = sd;
    442         owned_size[k] = ss;
    443         members[k].bytes.data = sd;
    444         members[k].bytes.len = ss;
    445       } else {
    446         members[k].bytes.data = m.data;
    447         members[k].bytes.len = m.size;
    448       }
    449       k++;
    450     }
    451   }
    452   kit_ar_iter_free(it);
    453   it = NULL;
    454 
    455   /* Pass 3: rebuild the System-V symbol index from the new bytes. */
    456   if (nmembers) {
    457     msyms = (KitArMemberSymbols*)driver_alloc_zeroed(
    458         env, (size_t)nmembers * sizeof(*msyms));
    459     sym_allocs = (void**)driver_alloc_zeroed(
    460         env, (size_t)nmembers * sizeof(*sym_allocs));
    461     sym_alloc_szs = (size_t*)driver_alloc_zeroed(
    462         env, (size_t)nmembers * sizeof(*sym_alloc_szs));
    463     if (!msyms || !sym_allocs || !sym_alloc_szs) {
    464       driver_errf(STRIP_TOOL, "out of memory");
    465       goto done;
    466     }
    467     for (k = 0; k < nmembers; ++k) {
    468       void* blob = NULL;
    469       size_t blob_size = 0;
    470       const KitSlice* names = NULL;
    471       uint32_t count = 0;
    472       if (driver_collect_obj_global_syms(env, ctx, STRIP_TOOL,
    473                                          &members[k].bytes, &blob, &blob_size,
    474                                          &names, &count) != 0) {
    475         goto done;
    476       }
    477       sym_allocs[k] = blob;
    478       sym_alloc_szs[k] = blob_size;
    479       msyms[k].names = names;
    480       msyms[k].count = count;
    481     }
    482   }
    483 
    484   if (ctx->file_io->open_writer(ctx->file_io->user, output_path, &out) !=
    485       KIT_OK) {
    486     driver_errf(STRIP_TOOL, "failed to open: %.*s",
    487                 KIT_SLICE_ARG(kit_slice_cstr(output_path)));
    488     goto done;
    489   }
    490   opts_ar.epoch = strip_epoch_from_env();
    491   opts_ar.long_names = 1;
    492   opts_ar.symbol_index = 1;
    493   opts_ar.member_symbols = msyms;
    494   rc = kit_ar_write(out, members, nmembers, &opts_ar) == KIT_OK ? 0 : 1;
    495   if (rc == 0 && kit_writer_status(out) != KIT_OK) rc = 1;
    496 
    497 done:
    498   if (out) kit_writer_close(out);
    499   if (it) kit_ar_iter_free(it);
    500   if (sym_allocs) {
    501     for (k = 0; k < nmembers; ++k) {
    502       if (sym_allocs[k])
    503         driver_collect_obj_global_syms_free(env, sym_allocs[k],
    504                                             sym_alloc_szs[k]);
    505     }
    506     driver_free(env, sym_allocs, (size_t)nmembers * sizeof(*sym_allocs));
    507   }
    508   if (sym_alloc_szs)
    509     driver_free(env, sym_alloc_szs, (size_t)nmembers * sizeof(*sym_alloc_szs));
    510   if (msyms) driver_free(env, msyms, (size_t)nmembers * sizeof(*msyms));
    511   if (owned_data) {
    512     for (k = 0; k < nmembers; ++k) {
    513       if (owned_data[k]) driver_free(env, owned_data[k], owned_size[k]);
    514     }
    515     driver_free(env, owned_data, (size_t)nmembers * sizeof(*owned_data));
    516   }
    517   if (owned_size)
    518     driver_free(env, owned_size, (size_t)nmembers * sizeof(*owned_size));
    519   if (members) driver_free(env, members, (size_t)nmembers * sizeof(*members));
    520   if (name_storage) driver_free(env, name_storage, name_bytes_total);
    521   return rc;
    522 }
    523 
    524 int driver_strip(int argc, char** argv) {
    525   DriverEnv env;
    526   KitContext ctx;
    527   StripOpts opts;
    528   KitFileData input_fd = {0};
    529   KitSlice input;
    530   KitWriter* w = NULL;
    531   uint8_t* out_data = NULL;
    532   size_t out_size = 0;
    533   int have_input = 0;
    534   int rc = 1;
    535   int i;
    536 
    537   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
    538     driver_help_strip();
    539     return 0;
    540   }
    541 
    542   memset(&opts, 0, sizeof opts);
    543   opts.op = STRIP_OP_ALL;
    544   driver_env_init(&env);
    545   ctx = driver_env_to_context(&env);
    546 
    547   for (i = 1; i < argc; ++i) {
    548     const char* a = argv[i];
    549     const char* val = NULL;
    550     int matched;
    551     if (driver_streq(a, "--strip-debug")) {
    552       opts.op = STRIP_OP_DEBUG;
    553       continue;
    554     }
    555     if (driver_streq(a, "--strip-unneeded")) {
    556       opts.op = STRIP_OP_UNNEEDED;
    557       continue;
    558     }
    559     if (driver_streq(a, "--strip-all") || driver_streq(a, "-s")) {
    560       opts.op = STRIP_OP_ALL;
    561       continue;
    562     }
    563     if (driver_streq(a, "-o")) {
    564       if (i + 1 >= argc) {
    565         driver_errf(STRIP_TOOL, "-o requires a path");
    566         rc = 2;
    567         goto done;
    568       }
    569       opts.output = argv[++i];
    570       continue;
    571     }
    572     matched = parse_name_arg(&i, argc, argv, "--keep-symbol", "-K", &val);
    573     if (matched < 0) {
    574       driver_errf(STRIP_TOOL, "%.*s requires a symbol name",
    575                   KIT_SLICE_ARG(kit_slice_cstr(a)));
    576       rc = 2;
    577       goto done;
    578     }
    579     if (matched) {
    580       if (push_name(&env, &opts.keep, &opts.nkeep, &opts.cap_keep, val) != 0) {
    581         rc = 1;
    582         goto done;
    583       }
    584       continue;
    585     }
    586     matched = parse_name_arg(&i, argc, argv, "--strip-symbol", "-N", &val);
    587     if (matched < 0) {
    588       driver_errf(STRIP_TOOL, "%.*s requires a symbol name",
    589                   KIT_SLICE_ARG(kit_slice_cstr(a)));
    590       rc = 2;
    591       goto done;
    592     }
    593     if (matched) {
    594       if (push_name(&env, &opts.strip, &opts.nstrip, &opts.cap_strip, val) !=
    595           0) {
    596         rc = 1;
    597         goto done;
    598       }
    599       continue;
    600     }
    601     if (a[0] == '-' && a[1] != '\0') {
    602       driver_errf(STRIP_TOOL, "unknown option: %.*s",
    603                   KIT_SLICE_ARG(kit_slice_cstr(a)));
    604       rc = 2;
    605       goto done;
    606     }
    607     if (opts.input) {
    608       driver_errf(STRIP_TOOL, "only one input file is supported");
    609       rc = 2;
    610       goto done;
    611     }
    612     opts.input = a;
    613   }
    614 
    615   if (!opts.input) {
    616     driver_errf(STRIP_TOOL, "missing input file");
    617     rc = 2;
    618     goto done;
    619   }
    620 
    621   if (ctx.file_io->read_all(ctx.file_io->user, opts.input, &input_fd) !=
    622       KIT_OK) {
    623     driver_errf(STRIP_TOOL, "failed to read: %.*s",
    624                 KIT_SLICE_ARG(kit_slice_cstr(opts.input)));
    625     goto done;
    626   }
    627   have_input = 1;
    628   input.data = input_fd.data;
    629   input.len = input_fd.size;
    630 
    631   {
    632     KitBinFmt fmt = kit_detect_fmt(input.data, input.len);
    633     const char* out_path = opts.output ? opts.output : opts.input;
    634     if (fmt == KIT_BIN_AR) {
    635       rc = strip_archive(&env, &ctx, &input, &opts, out_path);
    636       goto done;
    637     }
    638     if (strip_object_bytes(&env, &ctx, &input, &opts, &out_data, &out_size) !=
    639         0) {
    640       goto done;
    641     }
    642     if (ctx.file_io->open_writer(ctx.file_io->user, out_path, &w) != KIT_OK) {
    643       driver_errf(STRIP_TOOL, "failed to open: %.*s",
    644                   KIT_SLICE_ARG(kit_slice_cstr(out_path)));
    645       goto done;
    646     }
    647     kit_writer_write(w, out_data, out_size);
    648     if (kit_writer_status(w) != KIT_OK) {
    649       driver_errf(STRIP_TOOL, "write failed: %.*s",
    650                   KIT_SLICE_ARG(kit_slice_cstr(out_path)));
    651       goto done;
    652     }
    653     rc = 0;
    654   }
    655 
    656 done:
    657   if (w) kit_writer_close(w);
    658   if (out_data) driver_free(&env, out_data, out_size);
    659   if (have_input) ctx.file_io->release(ctx.file_io->user, &input_fd);
    660   if (opts.keep)
    661     driver_free(&env, (void*)opts.keep,
    662                 (size_t)opts.cap_keep * sizeof(*opts.keep));
    663   if (opts.strip)
    664     driver_free(&env, (void*)opts.strip,
    665                 (size_t)opts.cap_strip * sizeof(*opts.strip));
    666   driver_env_fini(&env);
    667   return rc;
    668 }