kit

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

objcopy.c (27649B)


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