kit

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

objcopy.c (24539B)


      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 objcopy` — copy + transform an object file. v1 scope is the
     11  * high-traffic build-system subset called out in CTOOLCHAIN.md:
     12  *
     13  *   --remove-section=NAME       drop the named section
     14  *   --only-section=NAME         drop every section except NAME (may repeat)
     15  *   --rename-section=OLD=NEW    rename a section
     16  *   --redefine-sym=OLD=NEW      rename a symbol
     17  *   --globalize-symbol=NAME     promote a symbol to SB_GLOBAL
     18  *   --localize-symbol=NAME      demote a symbol to SB_LOCAL
     19  *   --weaken-symbol=NAME        flip a symbol to SB_WEAK
     20  *   --strip-debug               drop KIT_SEC_DEBUG sections
     21  *   --strip-all                 drop debug + every non-essential symbol
     22  *   --strip-unneeded            drop debug + symbols not needed by relocs
     23  *   --add-section=NAME=FILE     append a new section with FILE's bytes
     24  *   --update-section=NAME=FILE  replace NAME's bytes with FILE's bytes
     25  *   -O <bfdname>                emit in a different object format
     26  *
     27  * Linked ELF (ET_EXEC / ET_DYN) input is out of scope for v1.
     28  *
     29  * Usage: kit objcopy [OPTIONS] INPUT [OUTPUT]
     30  * If OUTPUT is omitted, INPUT is rewritten in place. */
     31 
     32 #define OBJCOPY_TOOL "objcopy"
     33 
     34 void driver_help_objcopy(void) {
     35   driver_printf(
     36       "%.*s",
     37       KIT_SLICE_ARG(KIT_SLICE_LIT(
     38           "kit objcopy — copy and transform an object file\n"
     39           "\n"
     40           "USAGE\n"
     41           "  kit objcopy [OPTIONS] INPUT [OUTPUT]\n"
     42           "\n"
     43           "SECTION OPS\n"
     44           "  --remove-section=NAME       drop section NAME (may repeat)\n"
     45           "  --only-section=NAME         keep only section NAME (may repeat)\n"
     46           "  --rename-section=OLD=NEW    rename section OLD to NEW\n"
     47           "  --add-section=NAME=FILE     append a new section with FILE's "
     48           "bytes\n"
     49           "  --update-section=NAME=FILE  replace NAME's bytes with FILE's "
     50           "bytes\n"
     51           "\n"
     52           "SYMBOL OPS\n"
     53           "  --redefine-sym=OLD=NEW      rename a symbol (may repeat)\n"
     54           "  --globalize-symbol=NAME     set NAME's binding to global\n"
     55           "  --localize-symbol=NAME      set NAME's binding to local\n"
     56           "  --weaken-symbol=NAME        set NAME's binding to weak\n"
     57           "\n"
     58           "STRIP OPS\n"
     59           "  --strip-debug, --strip-unneeded, --strip-all\n"
     60           "                              same semantics as `kit strip`\n"
     61           "\n"
     62           "FORMAT\n"
     63           "  -O BFDNAME                  emit as a different format.  "
     64           "Recognized\n"
     65           "                              names: elf*, mach-o / macho*, coff*, "
     66           "wasm*\n"
     67           "\n"
     68           "EXIT CODES\n"
     69           "  0   success           1   I/O or strip error           2   bad "
     70           "usage\n")));
     71 }
     72 
     73 typedef enum CopyOp {
     74   COPY_OP_NONE,
     75   COPY_OP_STRIP_DEBUG,
     76   COPY_OP_STRIP_UNNEEDED,
     77   COPY_OP_STRIP_ALL,
     78 } CopyOp;
     79 
     80 typedef struct NamePair {
     81   const char* old_name;
     82   const char* new_name;
     83 } NamePair;
     84 
     85 typedef struct CopyOpts {
     86   CopyOp op;
     87   /* Section ops */
     88   const char** remove_sections;
     89   uint32_t nremove;
     90   uint32_t cap_remove;
     91   const char** only_sections;
     92   uint32_t nonly;
     93   uint32_t cap_only;
     94   NamePair* rename_sections;
     95   uint32_t nrename_sec;
     96   uint32_t cap_rename_sec;
     97   NamePair* add_sections;
     98   uint32_t nadd;
     99   uint32_t cap_add;
    100   NamePair* update_sections;
    101   uint32_t nupdate;
    102   uint32_t cap_update;
    103   /* Symbol ops */
    104   NamePair* redefine_syms;
    105   uint32_t nredef;
    106   uint32_t cap_redef;
    107   const char** globalize;
    108   uint32_t nglob;
    109   uint32_t cap_glob;
    110   const char** localize;
    111   uint32_t nloc;
    112   uint32_t cap_loc;
    113   const char** weaken;
    114   uint32_t nweak;
    115   uint32_t cap_weak;
    116   /* Format conversion */
    117   int have_output_fmt;
    118   KitObjFmt output_fmt;
    119   /* I/O */
    120   const char* input;
    121   const char* output;
    122 } CopyOpts;
    123 
    124 static int name_in_list(KitSlice name, const char* const* list, uint32_t n) {
    125   uint32_t i;
    126   if (!name.len) return 0;
    127   for (i = 0; i < n; ++i) {
    128     if (list[i] && kit_slice_eq_cstr(name, list[i])) return 1;
    129   }
    130   return 0;
    131 }
    132 
    133 static int push_str(DriverEnv* env, const char*** arr, uint32_t* n,
    134                     uint32_t* cap, const char* s) {
    135   if (*n >= *cap) {
    136     uint32_t newcap = *cap ? *cap * 2u : 4u;
    137     const char** nb =
    138         (const char**)driver_alloc_zeroed(env, (size_t)newcap * sizeof(*nb));
    139     if (!nb) return -1;
    140     if (*arr) {
    141       memcpy(nb, *arr, (size_t)(*n) * sizeof(*nb));
    142       driver_free(env, (void*)*arr, (size_t)(*cap) * sizeof(*nb));
    143     }
    144     *arr = nb;
    145     *cap = newcap;
    146   }
    147   (*arr)[(*n)++] = s;
    148   return 0;
    149 }
    150 
    151 static int push_pair(DriverEnv* env, NamePair** arr, uint32_t* n, uint32_t* cap,
    152                      const char* old_name, const char* new_name) {
    153   if (*n >= *cap) {
    154     uint32_t newcap = *cap ? *cap * 2u : 4u;
    155     NamePair* nb =
    156         (NamePair*)driver_alloc_zeroed(env, (size_t)newcap * sizeof(*nb));
    157     if (!nb) return -1;
    158     if (*arr) {
    159       memcpy(nb, *arr, (size_t)(*n) * sizeof(*nb));
    160       driver_free(env, *arr, (size_t)(*cap) * sizeof(*nb));
    161     }
    162     *arr = nb;
    163     *cap = newcap;
    164   }
    165   (*arr)[*n].old_name = old_name;
    166   (*arr)[*n].new_name = new_name;
    167   (*n)++;
    168   return 0;
    169 }
    170 
    171 /* Parse VAL of `--flag=VAL` or take the next argv. */
    172 static int take_value(int* i, int argc, char** argv, const char* flag,
    173                       const char** out) {
    174   const char* a = argv[*i];
    175   size_t flen = kit_slice_cstr(flag).len;
    176   if (driver_strneq(a, flag, flen) && a[flen] == '=') {
    177     *out = a + flen + 1;
    178     return 1;
    179   }
    180   if (driver_streq(a, flag)) {
    181     if (*i + 1 >= argc) return -1;
    182     *out = argv[++(*i)];
    183     return 1;
    184   }
    185   return 0;
    186 }
    187 
    188 /* Split "old=new" / "name=file" at the first '='. */
    189 static int split_pair(DriverEnv* env, const char* spec, const char** out_left,
    190                       const char** out_right) {
    191   const char* eq = driver_strchr(spec, '=');
    192   size_t llen;
    193   char* left;
    194   if (!eq || eq == spec || !eq[1]) return -1;
    195   llen = (size_t)(eq - spec);
    196   left = (char*)driver_alloc_zeroed(env, llen + 1u);
    197   if (!left) return -1;
    198   memcpy(left, spec, llen);
    199   left[llen] = '\0';
    200   *out_left = left;
    201   *out_right = eq + 1;
    202   return 0;
    203 }
    204 
    205 static int parse_fmt_name(const char* name, KitObjFmt* out) {
    206   if (!name) return -1;
    207   /* Canonical bare names (elf/coff/pe/macho/wasm) come from the obj layer's
    208    * single source of truth. */
    209   if (kit_obj_fmt_from_name(name, out) == KIT_OK) return 0;
    210   /* binutils-name compatibility (not a second source of truth): accept the
    211    * BFD-style decorated spellings objcopy users pass, e.g. "elf64-x86-64",
    212    * "mach-o-arm64", "coff-x86-64", "pe-i386". */
    213   if (driver_strneq(name, "elf", sizeof("elf") - 1)) {
    214     *out = KIT_OBJ_ELF;
    215     return 0;
    216   }
    217   if (driver_strneq(name, "mach", sizeof("mach") - 1)) {
    218     *out = KIT_OBJ_MACHO;
    219     return 0;
    220   }
    221   if (driver_strneq(name, "coff", sizeof("coff") - 1) ||
    222       driver_strneq(name, "pe-", sizeof("pe-") - 1)) {
    223     *out = KIT_OBJ_COFF;
    224     return 0;
    225   }
    226   if (driver_strneq(name, "wasm", sizeof("wasm") - 1)) {
    227     *out = KIT_OBJ_WASM;
    228     return 0;
    229   }
    230   return -1;
    231 }
    232 
    233 /* Lookup a symbol by name; KIT_OBJ_SYMBOL_NONE if not found. */
    234 static KitObjSymbol find_sym_id(KitObjFile* of, const char* name) {
    235   KitObjSymInfo si;
    236   if (kit_obj_symbol_by_name(of, kit_slice_cstr(name), &si) != KIT_OK) {
    237     return KIT_OBJ_SYMBOL_NONE;
    238   }
    239   return si.id;
    240 }
    241 
    242 /* Lookup a section by name; KIT_SECTION_NONE if not found. */
    243 static KitObjSection find_sec_id(KitObjFile* of, const char* name) {
    244   KitObjSection s = KIT_SECTION_NONE;
    245   if (kit_obj_section_by_name(of, kit_slice_cstr(name), &s) != KIT_OK) {
    246     return KIT_SECTION_NONE;
    247   }
    248   return s;
    249 }
    250 
    251 static int apply_strip_pass(DriverEnv* env, KitObjFile* of, KitObjBuilder* b,
    252                             const CopyOpts* opts) {
    253   uint32_t i, nsec;
    254   KitObjSymbol* needed = NULL;
    255   uint32_t nneeded = 0, cap_needed = 0;
    256   KitObjSymIter* sit = NULL;
    257   int filter_syms =
    258       (opts->op == COPY_OP_STRIP_UNNEEDED || opts->op == COPY_OP_STRIP_ALL);
    259   int rc = 1;
    260 
    261   if (opts->op == COPY_OP_NONE) return 0;
    262 
    263   /* Always drop debug sections for any strip op. */
    264   nsec = kit_obj_nsections(of);
    265   for (i = 0; i < nsec; ++i) {
    266     KitObjSecInfo si;
    267     if (kit_obj_section(of, i, &si) != KIT_OK) continue;
    268     if (si.kind == KIT_SEC_DEBUG) kit_obj_builder_remove_section(b, i);
    269   }
    270   if (!filter_syms) return 0;
    271 
    272   /* Collect reloc-targeted sym ids, skipping relocs in debug sections. */
    273   {
    274     KitObjRelocIter* rit = NULL;
    275     if (kit_obj_reliter_new(of, &rit) != KIT_OK) {
    276       driver_errf(OBJCOPY_TOOL, "out of memory");
    277       return 1;
    278     }
    279     for (;;) {
    280       KitObjReloc r;
    281       KitIterResult ir = kit_obj_reliter_next(rit, &r);
    282       uint32_t k;
    283       int seen = 0;
    284       if (ir != KIT_ITER_ITEM) break;
    285       if (r.sym == KIT_OBJ_SYMBOL_NONE) continue;
    286       if (r.section != KIT_SECTION_NONE) {
    287         KitObjSecInfo hi;
    288         if (kit_obj_section(of, r.section, &hi) == KIT_OK &&
    289             hi.kind == KIT_SEC_DEBUG) {
    290           continue;
    291         }
    292       }
    293       for (k = 0; k < nneeded; ++k) {
    294         if (needed[k] == r.sym) {
    295           seen = 1;
    296           break;
    297         }
    298       }
    299       if (seen) continue;
    300       if (nneeded >= cap_needed) {
    301         uint32_t newcap = cap_needed ? cap_needed * 2u : 32u;
    302         KitObjSymbol* nb = (KitObjSymbol*)driver_alloc_zeroed(
    303             env, (size_t)newcap * sizeof(*nb));
    304         if (!nb) {
    305           kit_obj_reliter_free(rit);
    306           if (needed)
    307             driver_free(env, needed, (size_t)cap_needed * sizeof(*needed));
    308           driver_errf(OBJCOPY_TOOL, "out of memory");
    309           return 1;
    310         }
    311         if (needed) {
    312           memcpy(nb, needed, (size_t)nneeded * sizeof(*needed));
    313           driver_free(env, needed, (size_t)cap_needed * sizeof(*needed));
    314         }
    315         needed = nb;
    316         cap_needed = newcap;
    317       }
    318       needed[nneeded++] = r.sym;
    319     }
    320     kit_obj_reliter_free(rit);
    321   }
    322 
    323   /* Walk syms and drop unneeded ones. */
    324   if (kit_obj_symiter_new(of, &sit) != KIT_OK) {
    325     driver_errf(OBJCOPY_TOOL, "out of memory");
    326     goto done;
    327   }
    328   for (;;) {
    329     KitObjSymInfo si;
    330     KitIterResult ir = kit_obj_symiter_next(sit, &si);
    331     uint32_t k;
    332     int in_needed = 0;
    333     if (ir != KIT_ITER_ITEM) break;
    334     if (si.kind == KIT_SK_UNDEF) continue;
    335     for (k = 0; k < nneeded; ++k) {
    336       if (needed[k] == si.id) {
    337         in_needed = 1;
    338         break;
    339       }
    340     }
    341     if (!in_needed) kit_obj_builder_remove_symbol(b, si.id);
    342   }
    343   kit_obj_symiter_free(sit);
    344   rc = 0;
    345 done:
    346   if (needed) driver_free(env, needed, (size_t)cap_needed * sizeof(*needed));
    347   return rc;
    348 }
    349 
    350 /* Apply --only-section: every section whose name isn't on the list is
    351  * dropped. Sections of KIT_SEC_TEXT/RODATA/DATA/BSS are user-visible;
    352  * symbol-table / strtab / etc. are also affected. */
    353 static void apply_only_sections(KitObjFile* of, KitObjBuilder* b,
    354                                 const CopyOpts* opts) {
    355   uint32_t i, n;
    356   if (!opts->nonly) return;
    357   n = kit_obj_nsections(of);
    358   for (i = 0; i < n; ++i) {
    359     KitObjSecInfo si;
    360     if (kit_obj_section(of, i, &si) != KIT_OK) continue;
    361     if (!name_in_list(si.name, opts->only_sections, opts->nonly)) {
    362       kit_obj_builder_remove_section(b, i);
    363     }
    364   }
    365 }
    366 
    367 static int run_transforms(DriverEnv* env, const KitContext* ctx, KitObjFile* of,
    368                           KitObjBuilder* b, const CopyOpts* opts) {
    369   uint32_t i;
    370 
    371   /* --strip-* */
    372   if (apply_strip_pass(env, of, b, opts) != 0) return 1;
    373 
    374   /* --only-section overrides --remove-section because they're an inverse
    375    * pair; if both are passed, --only-section's keep-set is authoritative. */
    376   if (opts->nonly) {
    377     apply_only_sections(of, b, opts);
    378   } else if (opts->nremove) {
    379     for (i = 0; i < opts->nremove; ++i) {
    380       KitObjSection sid = find_sec_id(of, opts->remove_sections[i]);
    381       if (sid != KIT_SECTION_NONE) kit_obj_builder_remove_section(b, sid);
    382     }
    383   }
    384 
    385   /* --rename-section */
    386   for (i = 0; i < opts->nrename_sec; ++i) {
    387     KitObjSection sid = find_sec_id(of, opts->rename_sections[i].old_name);
    388     if (sid == KIT_SECTION_NONE) {
    389       driver_errf(
    390           OBJCOPY_TOOL, "rename-section: '%.*s' not found",
    391           KIT_SLICE_ARG(kit_slice_cstr(opts->rename_sections[i].old_name)));
    392       return 1;
    393     }
    394     KitSym ns =
    395         kit_sym_intern(kit_obj_builder_compiler(b),
    396                        kit_slice_cstr(opts->rename_sections[i].new_name));
    397     kit_obj_builder_rename_section(b, sid, ns);
    398   }
    399 
    400   /* --update-section */
    401   for (i = 0; i < opts->nupdate; ++i) {
    402     KitObjSection sid = find_sec_id(of, opts->update_sections[i].old_name);
    403     KitFileData fd = {0};
    404     if (sid == KIT_SECTION_NONE) {
    405       driver_errf(
    406           OBJCOPY_TOOL, "update-section: '%.*s' not found",
    407           KIT_SLICE_ARG(kit_slice_cstr(opts->update_sections[i].old_name)));
    408       return 1;
    409     }
    410     if (ctx->file_io->read_all(ctx->file_io->user,
    411                                opts->update_sections[i].new_name,
    412                                &fd) != KIT_OK) {
    413       driver_errf(
    414           OBJCOPY_TOOL, "update-section: cannot read %.*s",
    415           KIT_SLICE_ARG(kit_slice_cstr(opts->update_sections[i].new_name)));
    416       return 1;
    417     }
    418     kit_obj_builder_section_replace_bytes(b, sid, fd.data, fd.size);
    419     ctx->file_io->release(ctx->file_io->user, &fd);
    420   }
    421 
    422   /* --add-section: create a new SEC_OTHER PROGBITS section and write its
    423    * contents from the on-disk file. */
    424   for (i = 0; i < opts->nadd; ++i) {
    425     KitObjSectionDesc desc;
    426     KitObjSection nsid;
    427     KitFileData fd = {0};
    428     if (ctx->file_io->read_all(ctx->file_io->user,
    429                                opts->add_sections[i].new_name, &fd) != KIT_OK) {
    430       driver_errf(
    431           OBJCOPY_TOOL, "add-section: cannot read %.*s",
    432           KIT_SLICE_ARG(kit_slice_cstr(opts->add_sections[i].new_name)));
    433       return 1;
    434     }
    435     memset(&desc, 0, sizeof desc);
    436     desc.name = kit_sym_intern(kit_obj_builder_compiler(b),
    437                                kit_slice_cstr(opts->add_sections[i].old_name));
    438     desc.kind = KIT_SEC_OTHER;
    439     desc.flags = 0;
    440     desc.align = 1;
    441     desc.entsize = 0;
    442     if (kit_obj_builder_section(b, &desc, &nsid) != KIT_OK) {
    443       driver_errf(
    444           OBJCOPY_TOOL, "add-section: failed to create '%.*s'",
    445           KIT_SLICE_ARG(kit_slice_cstr(opts->add_sections[i].old_name)));
    446       ctx->file_io->release(ctx->file_io->user, &fd);
    447       return 1;
    448     }
    449     kit_obj_builder_write(b, nsid, fd.data, fd.size);
    450     ctx->file_io->release(ctx->file_io->user, &fd);
    451   }
    452 
    453   /* --redefine-sym */
    454   for (i = 0; i < opts->nredef; ++i) {
    455     KitObjSymbol sid = find_sym_id(of, opts->redefine_syms[i].old_name);
    456     if (sid == KIT_OBJ_SYMBOL_NONE) continue; /* tolerate missing */
    457     kit_obj_builder_rename_symbol(
    458         b, sid,
    459         kit_sym_intern(kit_obj_builder_compiler(b),
    460                        kit_slice_cstr(opts->redefine_syms[i].new_name)));
    461   }
    462 
    463   /* --globalize-symbol / --localize-symbol / --weaken-symbol */
    464   for (i = 0; i < opts->nglob; ++i) {
    465     KitObjSymbol sid = find_sym_id(of, opts->globalize[i]);
    466     if (sid != KIT_OBJ_SYMBOL_NONE)
    467       kit_obj_builder_symbol_set_bind(b, sid, KIT_SB_GLOBAL);
    468   }
    469   for (i = 0; i < opts->nloc; ++i) {
    470     KitObjSymbol sid = find_sym_id(of, opts->localize[i]);
    471     if (sid != KIT_OBJ_SYMBOL_NONE)
    472       kit_obj_builder_symbol_set_bind(b, sid, KIT_SB_LOCAL);
    473   }
    474   for (i = 0; i < opts->nweak; ++i) {
    475     KitObjSymbol sid = find_sym_id(of, opts->weaken[i]);
    476     if (sid != KIT_OBJ_SYMBOL_NONE)
    477       kit_obj_builder_symbol_set_bind(b, sid, KIT_SB_WEAK);
    478   }
    479 
    480   return 0;
    481 }
    482 
    483 static int copy_one_object(DriverEnv* env, const KitContext* ctx,
    484                            const char* input_name, const KitSlice* input,
    485                            const CopyOpts* opts, const char* output_path) {
    486   KitObjFile* of = NULL;
    487   KitObjBuilder* b;
    488   KitWriter* w = NULL;
    489   KitStatus st;
    490   int rc = 1;
    491 
    492   if (kit_obj_open(ctx, kit_slice_cstr(input_name), input, &of) != KIT_OK) {
    493     driver_errf(OBJCOPY_TOOL, "%.*s: not a recognized object",
    494                 KIT_SLICE_ARG(kit_slice_cstr(input_name)));
    495     return 1;
    496   }
    497   b = kit_obj_file_builder(of);
    498   if (!b) {
    499     driver_errf(OBJCOPY_TOOL, "%.*s: no builder",
    500                 KIT_SLICE_ARG(kit_slice_cstr(input_name)));
    501     kit_obj_free(of);
    502     return 1;
    503   }
    504   if (run_transforms(env, ctx, of, b, opts) != 0) {
    505     kit_obj_free(of);
    506     return 1;
    507   }
    508   if (ctx->file_io->open_writer(ctx->file_io->user, output_path, &w) !=
    509       KIT_OK) {
    510     driver_errf(OBJCOPY_TOOL, "cannot open %.*s",
    511                 KIT_SLICE_ARG(kit_slice_cstr(output_path)));
    512     kit_obj_free(of);
    513     return 1;
    514   }
    515   if (opts->have_output_fmt) {
    516     st = kit_obj_builder_emit_as(b, opts->output_fmt, w);
    517   } else {
    518     st = kit_obj_builder_emit(b, w);
    519   }
    520   if (st != KIT_OK) {
    521     driver_errf(OBJCOPY_TOOL, "emit failed");
    522     kit_writer_close(w);
    523     kit_obj_free(of);
    524     return 1;
    525   }
    526   kit_writer_close(w);
    527   kit_obj_free(of);
    528   rc = 0;
    529   return rc;
    530 }
    531 
    532 int driver_objcopy(int argc, char** argv) {
    533   DriverEnv env;
    534   KitContext ctx;
    535   CopyOpts opts;
    536   KitFileData in_fd = {0};
    537   KitSlice input;
    538   int have_in = 0;
    539   int rc = 1;
    540   int i;
    541   const char* out_path;
    542 
    543   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
    544     driver_help_objcopy();
    545     return 0;
    546   }
    547 
    548   memset(&opts, 0, sizeof opts);
    549   opts.op = COPY_OP_NONE;
    550   driver_env_init(&env);
    551   ctx = driver_env_to_context(&env);
    552 
    553   for (i = 1; i < argc; ++i) {
    554     const char* a = argv[i];
    555     const char* val = NULL;
    556     int matched;
    557     if (driver_streq(a, "--strip-debug")) {
    558       opts.op = COPY_OP_STRIP_DEBUG;
    559       continue;
    560     }
    561     if (driver_streq(a, "--strip-unneeded")) {
    562       opts.op = COPY_OP_STRIP_UNNEEDED;
    563       continue;
    564     }
    565     if (driver_streq(a, "--strip-all") || driver_streq(a, "-S")) {
    566       opts.op = COPY_OP_STRIP_ALL;
    567       continue;
    568     }
    569     if (driver_streq(a, "-O")) {
    570       if (i + 1 >= argc) {
    571         driver_errf(OBJCOPY_TOOL, "-O requires a format name");
    572         rc = 2;
    573         goto done;
    574       }
    575       if (parse_fmt_name(argv[++i], &opts.output_fmt) != 0) {
    576         driver_errf(OBJCOPY_TOOL, "unknown output format: %.*s",
    577                     KIT_SLICE_ARG(kit_slice_cstr(argv[i])));
    578         rc = 2;
    579         goto done;
    580       }
    581       opts.have_output_fmt = 1;
    582       continue;
    583     }
    584     matched = take_value(&i, argc, argv, "--remove-section", &val);
    585     if (matched < 0) goto missing_value;
    586     if (matched) {
    587       if (push_str(&env, &opts.remove_sections, &opts.nremove, &opts.cap_remove,
    588                    val) != 0)
    589         goto oom;
    590       continue;
    591     }
    592     matched = take_value(&i, argc, argv, "--only-section", &val);
    593     if (matched < 0) goto missing_value;
    594     if (matched) {
    595       if (push_str(&env, &opts.only_sections, &opts.nonly, &opts.cap_only,
    596                    val) != 0)
    597         goto oom;
    598       continue;
    599     }
    600     matched = take_value(&i, argc, argv, "--rename-section", &val);
    601     if (matched < 0) goto missing_value;
    602     if (matched) {
    603       const char *left, *right;
    604       if (split_pair(&env, val, &left, &right) != 0) {
    605         driver_errf(OBJCOPY_TOOL, "rename-section: expected OLD=NEW (got %.*s)",
    606                     KIT_SLICE_ARG(kit_slice_cstr(val)));
    607         rc = 2;
    608         goto done;
    609       }
    610       if (push_pair(&env, &opts.rename_sections, &opts.nrename_sec,
    611                     &opts.cap_rename_sec, left, right) != 0)
    612         goto oom;
    613       continue;
    614     }
    615     matched = take_value(&i, argc, argv, "--add-section", &val);
    616     if (matched < 0) goto missing_value;
    617     if (matched) {
    618       const char *left, *right;
    619       if (split_pair(&env, val, &left, &right) != 0) {
    620         driver_errf(OBJCOPY_TOOL, "add-section: expected NAME=FILE (got %.*s)",
    621                     KIT_SLICE_ARG(kit_slice_cstr(val)));
    622         rc = 2;
    623         goto done;
    624       }
    625       if (push_pair(&env, &opts.add_sections, &opts.nadd, &opts.cap_add, left,
    626                     right) != 0)
    627         goto oom;
    628       continue;
    629     }
    630     matched = take_value(&i, argc, argv, "--update-section", &val);
    631     if (matched < 0) goto missing_value;
    632     if (matched) {
    633       const char *left, *right;
    634       if (split_pair(&env, val, &left, &right) != 0) {
    635         driver_errf(OBJCOPY_TOOL,
    636                     "update-section: expected NAME=FILE (got %.*s)",
    637                     KIT_SLICE_ARG(kit_slice_cstr(val)));
    638         rc = 2;
    639         goto done;
    640       }
    641       if (push_pair(&env, &opts.update_sections, &opts.nupdate,
    642                     &opts.cap_update, left, right) != 0)
    643         goto oom;
    644       continue;
    645     }
    646     matched = take_value(&i, argc, argv, "--redefine-sym", &val);
    647     if (matched < 0) goto missing_value;
    648     if (matched) {
    649       const char *left, *right;
    650       if (split_pair(&env, val, &left, &right) != 0) {
    651         driver_errf(OBJCOPY_TOOL, "redefine-sym: expected OLD=NEW (got %.*s)",
    652                     KIT_SLICE_ARG(kit_slice_cstr(val)));
    653         rc = 2;
    654         goto done;
    655       }
    656       if (push_pair(&env, &opts.redefine_syms, &opts.nredef, &opts.cap_redef,
    657                     left, right) != 0)
    658         goto oom;
    659       continue;
    660     }
    661     matched = take_value(&i, argc, argv, "--globalize-symbol", &val);
    662     if (matched < 0) goto missing_value;
    663     if (matched) {
    664       if (push_str(&env, &opts.globalize, &opts.nglob, &opts.cap_glob, val) !=
    665           0)
    666         goto oom;
    667       continue;
    668     }
    669     matched = take_value(&i, argc, argv, "--localize-symbol", &val);
    670     if (matched < 0) goto missing_value;
    671     if (matched) {
    672       if (push_str(&env, &opts.localize, &opts.nloc, &opts.cap_loc, val) != 0)
    673         goto oom;
    674       continue;
    675     }
    676     matched = take_value(&i, argc, argv, "--weaken-symbol", &val);
    677     if (matched < 0) goto missing_value;
    678     if (matched) {
    679       if (push_str(&env, &opts.weaken, &opts.nweak, &opts.cap_weak, val) != 0)
    680         goto oom;
    681       continue;
    682     }
    683     if (a[0] == '-' && a[1] != '\0') {
    684       driver_errf(OBJCOPY_TOOL, "unknown option: %.*s",
    685                   KIT_SLICE_ARG(kit_slice_cstr(a)));
    686       rc = 2;
    687       goto done;
    688     }
    689     if (!opts.input) {
    690       opts.input = a;
    691     } else if (!opts.output) {
    692       opts.output = a;
    693     } else {
    694       driver_errf(OBJCOPY_TOOL, "unexpected argument: %.*s",
    695                   KIT_SLICE_ARG(kit_slice_cstr(a)));
    696       rc = 2;
    697       goto done;
    698     }
    699   }
    700 
    701   if (!opts.input) {
    702     driver_errf(OBJCOPY_TOOL, "missing input file");
    703     rc = 2;
    704     goto done;
    705   }
    706   out_path = opts.output ? opts.output : opts.input;
    707 
    708   if (ctx.file_io->read_all(ctx.file_io->user, opts.input, &in_fd) != KIT_OK) {
    709     driver_errf(OBJCOPY_TOOL, "cannot read %.*s",
    710                 KIT_SLICE_ARG(kit_slice_cstr(opts.input)));
    711     goto done;
    712   }
    713   have_in = 1;
    714   input.data = in_fd.data;
    715   input.len = in_fd.size;
    716 
    717   rc = copy_one_object(&env, &ctx, opts.input, &input, &opts, out_path);
    718 
    719 done:
    720   if (have_in) ctx.file_io->release(ctx.file_io->user, &in_fd);
    721   if (opts.remove_sections)
    722     driver_free(&env, (void*)opts.remove_sections,
    723                 (size_t)opts.cap_remove * sizeof(*opts.remove_sections));
    724   if (opts.only_sections)
    725     driver_free(&env, (void*)opts.only_sections,
    726                 (size_t)opts.cap_only * sizeof(*opts.only_sections));
    727   if (opts.rename_sections)
    728     driver_free(&env, opts.rename_sections,
    729                 (size_t)opts.cap_rename_sec * sizeof(*opts.rename_sections));
    730   if (opts.add_sections)
    731     driver_free(&env, opts.add_sections,
    732                 (size_t)opts.cap_add * sizeof(*opts.add_sections));
    733   if (opts.update_sections)
    734     driver_free(&env, opts.update_sections,
    735                 (size_t)opts.cap_update * sizeof(*opts.update_sections));
    736   if (opts.redefine_syms)
    737     driver_free(&env, opts.redefine_syms,
    738                 (size_t)opts.cap_redef * sizeof(*opts.redefine_syms));
    739   if (opts.globalize)
    740     driver_free(&env, (void*)opts.globalize,
    741                 (size_t)opts.cap_glob * sizeof(*opts.globalize));
    742   if (opts.localize)
    743     driver_free(&env, (void*)opts.localize,
    744                 (size_t)opts.cap_loc * sizeof(*opts.localize));
    745   if (opts.weaken)
    746     driver_free(&env, (void*)opts.weaken,
    747                 (size_t)opts.cap_weak * sizeof(*opts.weaken));
    748   driver_env_fini(&env);
    749   return rc;
    750 
    751 missing_value:
    752   driver_errf(OBJCOPY_TOOL, "%.*s requires a value",
    753               KIT_SLICE_ARG(kit_slice_cstr(argv[i])));
    754   rc = 2;
    755   goto done;
    756 oom:
    757   driver_errf(OBJCOPY_TOOL, "out of memory");
    758   rc = 1;
    759   goto done;
    760 }