kit

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

objdump.c (54501B)


      1 #include <kit/archive.h>
      2 #include <kit/core.h>
      3 #include <kit/disasm.h>
      4 #include <kit/dwarf.h>
      5 #include <kit/object.h>
      6 #include <stdio.h>
      7 #include <string.h>
      8 
      9 #include "driver.h"
     10 
     11 /* `kit objdump` — print section/symbol info, disassembly, hex contents,
     12  * and relocations for object files and archives. Archives are auto-detected
     13  * by magic; each member is dumped in turn. All display logic lives here;
     14  * the library supplies data-access APIs. */
     15 
     16 #define OBJDUMP_TOOL "objdump"
     17 
     18 #define MAX_J_FILTERS 16
     19 
     20 typedef struct ObjdumpOpts {
     21   int f;          /* -f: file header           */
     22   int h;          /* -h: section headers       */
     23   int t;          /* -t: symbol table          */
     24   int d;          /* -d: disasm exec sections  */
     25   int D;          /* -D: disasm all sections   */
     26   int r;          /* -r: relocations           */
     27   int s;          /* -s: hex section contents  */
     28   int p;          /* -p / --private-headers: program/dynamic headers (image) */
     29   int T;          /* -T / --dynamic-syms: dynamic symbol table */
     30   int R;          /* -R / --dynamic-reloc: dynamic relocations */
     31   unsigned dwarf; /* --dwarf: bitmask of OBJDUMP_DWARF_* (0 = off) */
     32   const char* j[MAX_J_FILTERS];
     33   int nj;
     34 } ObjdumpOpts;
     35 
     36 /* --dwarf section selectors. */
     37 #define OBJDUMP_DWARF_INFO 0x1u
     38 #define OBJDUMP_DWARF_ABBREV 0x2u
     39 #define OBJDUMP_DWARF_LINE 0x4u
     40 #define OBJDUMP_DWARF_STR 0x8u
     41 #define OBJDUMP_DWARF_ALL                                           \
     42   (OBJDUMP_DWARF_INFO | OBJDUMP_DWARF_ABBREV | OBJDUMP_DWARF_LINE | \
     43    OBJDUMP_DWARF_STR)
     44 
     45 static void objdump_usage(void) {
     46   driver_errf(OBJDUMP_TOOL, "%.*s",
     47               KIT_SLICE_ARG(KIT_SLICE_LIT(
     48                   "usage: kit objdump [-h] [-t] [-d] [-D] [-r] [-s] [-p] "
     49                   "[-T] [-R] [-j NAME ...] input...\n"
     50                   "       kit objdump --help    for full option reference")));
     51 }
     52 
     53 void driver_help_objdump(void) {
     54   driver_printf(
     55       "%.*s",
     56       KIT_SLICE_ARG(KIT_SLICE_LIT(
     57           "kit objdump — print info about object files and archives\n"
     58           "\n"
     59           "USAGE\n"
     60           "  kit objdump [options] input ...\n"
     61           "\n"
     62           "DESCRIPTION\n"
     63           "  Prints section/symbol info, disassembly, hex contents, and\n"
     64           "  relocations for object files (ELF / COFF / Mach-O / Wasm) and "
     65           "`ar`\n"
     66           "  archives. Archives are auto-detected by magic; each member is\n"
     67           "  dumped in turn with an `archive(member)` label.\n"
     68           "\n"
     69           "  With no operation flags the default is `-h -t` (section headers "
     70           "+\n"
     71           "  symbol table), matching GNU objdump's default-ish behaviour.\n"
     72           "\n"
     73           "OPERATIONS (any combination)\n"
     74           "  -f                Print the file header: architecture, format,\n"
     75           "                    section / symbol counts, HAS_RELOC / HAS_SYMS\n"
     76           "                    flags, and (for PE images) image base,\n"
     77           "                    entry point, and subsystem.\n"
     78           "  -h                Print section headers (idx, name, size, align,\n"
     79           "                    flags). For COFF inputs the raw\n"
     80           "                    IMAGE_SCN_* Characteristics value is appended\n"
     81           "                    on a continuation line and COMDAT groups are\n"
     82           "                    printed after the section table. NOTE: this is\n"
     83           "                    the GNU objdump meaning of -h — it does NOT\n"
     84           "                    print this help; use --help.\n"
     85           "  -t                Print the symbol table\n"
     86           "  -d                Disassemble executable sections\n"
     87           "  -D                Disassemble all sections\n"
     88           "  -r                Print relocation records\n"
     89           "  -s                Print section contents as a hex+ASCII dump\n"
     90           "  -p, --private-headers\n"
     91           "                    Print the linked-image view: entry point, load\n"
     92           "                    segments (program headers), and dynamic\n"
     93           "                    dependencies. For PE images, the optional\n"
     94           "                    header, data directories, and import lists.\n"
     95           "  -T, --dynamic-syms\n"
     96           "                    Print the dynamic symbol table (.dynsym /\n"
     97           "                    export table). Empty for relocatable objects.\n"
     98           "  -R, --dynamic-reloc\n"
     99           "                    Print dynamic relocation records. Empty for\n"
    100           "                    relocatable objects.\n"
    101           "  -x                Aggregate: -f -h -r -t\n"
    102           "  --dwarf[=LIST]    Dump DWARF debug sections. LIST is a comma-\n"
    103           "                    separated subset of info, abbrev, line, str;\n"
    104           "                    bare --dwarf dumps all four.\n"
    105           "\n"
    106           "FILTERS\n"
    107           "  -j NAME           Restrict output to the named section. "
    108           "Repeatable;\n"
    109           "                    affects -h / -t / -d / -D / -r / -s.\n"
    110           "\n"
    111           "SUPPORTED INPUTS\n"
    112           "  *.o / *.obj       Single object file (ELF / COFF / Mach-O / "
    113           "Wasm)\n"
    114           "  *.a               POSIX `ar` archive — every object member is "
    115           "dumped\n"
    116           "                    in turn with an `<archive>(<member>)` label\n"
    117           "\n"
    118           "GETTING HELP\n"
    119           "  --help            Show this help and exit (-h is `section "
    120           "headers`)\n"
    121           "\n"
    122           "EXAMPLES\n"
    123           "  kit objdump -d a.o\n"
    124           "  kit objdump -h -j .text -j .rodata a.o\n"
    125           "  kit objdump -t libfoo.a\n"
    126           "\n"
    127           "EXIT CODES\n"
    128           "  0   success           1   parse / I/O error           2   bad "
    129           "usage\n")));
    130 }
    131 
    132 /* ---- PE/COFF display helpers ----
    133  *
    134  * PE executables / DLLs now open through kit_obj_open like every other
    135  * format, so the image data (optional-header scalars, data directories,
    136  * imports) arrive through the neutral image API — kit_obj_image_info, the
    137  * raw-fields iterator (KitObjImageRaw), and the dependency iterator. The
    138  * helpers below only map PE numeric constants to the symbolic names
    139  * objdump prints (data-directory index, subsystem). */
    140 
    141 /* Data-directory index (IMAGE_DIRECTORY_ENTRY_IMPORT). */
    142 #define PE_DIR_IMPORT 1u
    143 
    144 /* COFF-specific Characteristics bits we surface as tags. Kept in sync
    145  * with src/obj/coff.h's IMAGE_SCN_* values; objdump only needs the
    146  * diagnostic-visible subset. */
    147 #define OBJDUMP_IMAGE_SCN_LNK_INFO 0x00000200u
    148 #define OBJDUMP_IMAGE_SCN_LNK_REMOVE 0x00000800u
    149 #define OBJDUMP_IMAGE_SCN_LNK_COMDAT 0x00001000u
    150 #define OBJDUMP_IMAGE_SCN_GPREL 0x00008000u
    151 #define OBJDUMP_IMAGE_SCN_MEM_DISCARDABLE 0x02000000u
    152 #define OBJDUMP_IMAGE_SCN_MEM_SHARED 0x10000000u
    153 
    154 static int j_match(const ObjdumpOpts* o, KitSlice name);
    155 
    156 
    157 /* Names match the IMAGE_DIRECTORY_ENTRY_* index. Keep aligned with the
    158  * order in coff.h to avoid drift. */
    159 static const char* pe_dir_name(uint32_t i) {
    160   switch (i) {
    161     case 0:
    162       return "EXPORT";
    163     case 1:
    164       return "IMPORT";
    165     case 2:
    166       return "RESOURCE";
    167     case 3:
    168       return "EXCEPTION";
    169     case 4:
    170       return "SECURITY";
    171     case 5:
    172       return "BASERELOC";
    173     case 6:
    174       return "DEBUG";
    175     case 7:
    176       return "ARCHITECTURE";
    177     case 8:
    178       return "GLOBALPTR";
    179     case 9:
    180       return "TLS";
    181     case 10:
    182       return "LOAD_CONFIG";
    183     case 11:
    184       return "BOUND_IMPORT";
    185     case 12:
    186       return "IAT";
    187     case 13:
    188       return "DELAY_IMPORT";
    189     case 14:
    190       return "COM_DESCRIPTOR";
    191     case 15:
    192       return "RESERVED";
    193     default:
    194       return "?";
    195   }
    196 }
    197 
    198 static const char* pe_subsystem_name(uint16_t s) {
    199   switch (s) {
    200     case 1:
    201       return "NATIVE";
    202     case 2:
    203       return "WINDOWS_GUI";
    204     case 3:
    205       return "WINDOWS_CUI";
    206     case 5:
    207       return "OS2_CUI";
    208     case 7:
    209       return "POSIX_CUI";
    210     case 9:
    211       return "WINDOWS_CE_GUI";
    212     case 10:
    213       return "EFI_APPLICATION";
    214     case 11:
    215       return "EFI_BOOT_SERVICE_DRIVER";
    216     case 12:
    217       return "EFI_RUNTIME_DRIVER";
    218     case 13:
    219       return "EFI_ROM";
    220     case 14:
    221       return "XBOX";
    222     case 16:
    223       return "WINDOWS_BOOT_APPLICATION";
    224     default:
    225       return "UNKNOWN";
    226   }
    227 }
    228 
    229 
    230 /* Render objdump's "file format" spelling: the obj layer's canonical bare
    231  * name (elf/coff/macho/wasm) plus the bitwidth suffix this tool presents.
    232  * wasm carries no suffix; an out-of-range fmt renders "unknown". The bare
    233  * name is the single source of truth (kit_obj_fmt_name); the suffix is
    234  * legitimate tool presentation. `buf` must hold at least FMT_STR_CAP bytes. */
    235 #define FMT_STR_CAP 16
    236 static const char* fmt_str(KitObjFmt fmt, uint8_t ptr_size, char* buf,
    237                            size_t cap) {
    238   const char* base = kit_obj_fmt_name(fmt);
    239   if (!base) return "unknown";
    240   if (fmt == KIT_OBJ_WASM) return base; /* wasm has no bitwidth suffix */
    241   snprintf(buf, cap, "%s%s", base, ptr_size == 8 ? "64" : "32");
    242   return buf;
    243 }
    244 
    245 static const char* arch_str(KitArchKind arch) {
    246   switch (arch) {
    247     case KIT_ARCH_X86_64:
    248       return "x86_64";
    249     case KIT_ARCH_X86_32:
    250       return "i386";
    251     case KIT_ARCH_ARM_64:
    252       return "arm64";
    253     case KIT_ARCH_ARM_32:
    254       return "arm";
    255     case KIT_ARCH_RV64:
    256       return "riscv64";
    257     case KIT_ARCH_RV32:
    258       return "riscv32";
    259     case KIT_ARCH_WASM:
    260       return "wasm32";
    261   }
    262   return "unknown";
    263 }
    264 
    265 /* Collected PE optional-header escape-hatch view, gathered from the neutral
    266  * raw-fields iterator (KitObjImageRaw): the 16 data directories plus the
    267  * Subsystem / DllCharacteristics scalars. Returns 0 if the image carries no
    268  * raw fields (i.e. not a PE image). */
    269 typedef struct PeRaw {
    270   uint16_t subsystem;
    271   uint16_t dllchars;
    272   uint32_t dir_rva[16];
    273   uint32_t dir_size[16];
    274 } PeRaw;
    275 
    276 static int pe_collect_raw(KitObjFile* f, PeRaw* out) {
    277   KitObjImageRawIter* it = NULL;
    278   KitObjImageRaw r;
    279   memset(out, 0, sizeof *out);
    280   if (kit_obj_image_rawiter_new(f, &it) != KIT_OK) return 0;
    281   while (kit_obj_image_rawiter_next(it, &r) == KIT_ITER_ITEM) {
    282     if (r.tag < 16) {
    283       out->dir_rva[r.tag] = (uint32_t)r.value;
    284       out->dir_size[r.tag] = (uint32_t)r.extra;
    285     } else if (r.tag == KIT_OBJ_RAW_PE_SUBSYSTEM) {
    286       out->subsystem = (uint16_t)r.value;
    287     } else if (r.tag == KIT_OBJ_RAW_PE_DLLCHARS) {
    288       out->dllchars = (uint16_t)r.value;
    289     }
    290   }
    291   kit_obj_image_rawiter_free(it);
    292   return 1;
    293 }
    294 
    295 /* PE-image `-p`: the GNU objdump "PE32+ private headers" view — optional
    296  * header highlights, data directories, and the import tables — rendered
    297  * entirely from the neutral image API (kit_obj_image_info + the raw-fields
    298  * iterator + the dependency iterator). */
    299 static void dump_pe_private(KitObjFile* f, const char* label) {
    300   PeRaw raw;
    301   KitObjImageInfo info;
    302   KitTargetSpec target = kit_obj_target(f);
    303   KitObjDepIter* dit = NULL;
    304   KitObjDepInfo dep;
    305   uint32_t i;
    306   int have_imports = 0;
    307   if (!pe_collect_raw(f, &raw)) return;
    308   if (kit_obj_image_info(f, &info) != KIT_OK) return;
    309 
    310   driver_printf("\n%.*s:\tPE32+ private headers\n",
    311                 KIT_SLICE_ARG(kit_slice_cstr(label)));
    312   driver_printf("  Magic:               0x20b  (PE32+)\n");
    313   driver_printf("  Machine:             %.*s\n",
    314                 KIT_SLICE_ARG(kit_slice_cstr(arch_str(target.arch))));
    315   driver_printf("  ImageBase:           0x%llx\n",
    316                 (unsigned long long)info.image_base);
    317   driver_printf(
    318       "  AddressOfEntryPoint: 0x%llx\n",
    319       (unsigned long long)(info.entry > info.image_base
    320                                ? info.entry - info.image_base
    321                                : 0));
    322   driver_printf("  Subsystem:           %u  (%.*s)\n", (unsigned)raw.subsystem,
    323                 KIT_SLICE_ARG(kit_slice_cstr(pe_subsystem_name(raw.subsystem))));
    324   driver_printf("  DllCharacteristics:  0x%04x\n", (unsigned)raw.dllchars);
    325 
    326   driver_printf("\nData Directories:\n");
    327   driver_printf("  Idx Name            RVA        Size\n");
    328   for (i = 0; i < 16; ++i) {
    329     if (raw.dir_rva[i] == 0 && raw.dir_size[i] == 0) continue;
    330     driver_printf("  %2u  %-14s  0x%08x 0x%08x\n", i, pe_dir_name(i),
    331                   raw.dir_rva[i], raw.dir_size[i]);
    332   }
    333 
    334   if (kit_obj_depiter_new(f, &dit) == KIT_OK) {
    335     while (kit_obj_depiter_next(dit, &dep) == KIT_ITER_ITEM) {
    336       uint32_t k;
    337       if (!have_imports) {
    338         driver_printf("\nThe Import Tables:\n");
    339         have_imports = 1;
    340       }
    341       driver_printf("  DLL Name: %.*s\n", KIT_SLICE_ARG(dep.name));
    342       for (k = 0; k < dep.nimports; ++k)
    343         driver_printf("    Name:     %.*s\n", KIT_SLICE_ARG(dep.imports[k]));
    344     }
    345     kit_obj_depiter_free(dit);
    346   }
    347   driver_printf("\n");
    348 }
    349 
    350 static char sym_bind_char(KitSymBind b) {
    351   switch (b) {
    352     case KIT_SB_LOCAL:
    353       return 'l';
    354     case KIT_SB_GLOBAL:
    355       return 'g';
    356     case KIT_SB_WEAK:
    357       return 'w';
    358   }
    359   return ' ';
    360 }
    361 
    362 static char sym_kind_char(KitSymKind k) {
    363   switch (k) {
    364     case KIT_SK_FUNC:
    365       return 'F';
    366     case KIT_SK_OBJ:
    367       return 'O';
    368     case KIT_SK_SECTION:
    369       return 'S';
    370     case KIT_SK_FILE:
    371       return 'f';
    372     case KIT_SK_TLS:
    373       return 'T';
    374     case KIT_SK_ABS:
    375       return 'A';
    376     case KIT_SK_COMMON:
    377       return 'C';
    378     case KIT_SK_UNDEF:
    379       return 'U';
    380     case KIT_SK_NOTYPE:
    381       return 'n';
    382     case KIT_SK_IFUNC:
    383       return 'i';
    384   }
    385   return ' ';
    386 }
    387 
    388 static int j_match(const ObjdumpOpts* o, KitSlice name) {
    389   int i;
    390   if (o->nj == 0) return 1;
    391   for (i = 0; i < o->nj; ++i) {
    392     if (kit_slice_eq_cstr(name, o->j[i])) return 1;
    393   }
    394   return 0;
    395 }
    396 
    397 /* Compose the comma-separated flag tag list GNU objdump prints in -h.
    398  * For COFF inputs, `coff_chars` is the raw IMAGE_SECTION_HEADER.Characteristics
    399  * value; for other formats it should be 0. */
    400 static void render_sec_flags(const KitObjSecInfo* sec, KitObjFmt fmt,
    401                              uint32_t coff_chars, char* buf, size_t cap) {
    402   size_t n = 0;
    403   const char* tags[16];
    404   int nt = 0;
    405   int i;
    406   int is_bss = (sec->kind == KIT_SEC_BSS);
    407 
    408   if (!is_bss && sec->size > 0) tags[nt++] = "CONTENTS";
    409   if (sec->flags & KIT_SF_ALLOC) tags[nt++] = "ALLOC";
    410   if ((sec->flags & KIT_SF_ALLOC) && !is_bss) tags[nt++] = "LOAD";
    411   if ((sec->flags & KIT_SF_ALLOC) && !(sec->flags & KIT_SF_WRITE))
    412     tags[nt++] = "READONLY";
    413   if (sec->flags & KIT_SF_EXEC) tags[nt++] = "CODE";
    414   if ((sec->flags & KIT_SF_WRITE) && !(sec->flags & KIT_SF_EXEC) &&
    415       (sec->flags & KIT_SF_ALLOC))
    416     tags[nt++] = "DATA";
    417   if (sec->flags & KIT_SF_TLS) tags[nt++] = "TLS";
    418   if (sec->flags & KIT_SF_MERGE) tags[nt++] = "MERGE";
    419   if (sec->flags & KIT_SF_STRINGS) tags[nt++] = "STRINGS";
    420   if (sec->kind == KIT_SEC_DEBUG) tags[nt++] = "DEBUGGING";
    421 
    422   if (fmt == KIT_OBJ_COFF) {
    423     if (coff_chars & OBJDUMP_IMAGE_SCN_LNK_COMDAT) tags[nt++] = "LINK_ONCE";
    424     if (coff_chars & OBJDUMP_IMAGE_SCN_LNK_INFO) tags[nt++] = "LINK_INFO";
    425     if (coff_chars & OBJDUMP_IMAGE_SCN_LNK_REMOVE) tags[nt++] = "LINK_REMOVE";
    426     if (coff_chars & OBJDUMP_IMAGE_SCN_MEM_DISCARDABLE)
    427       tags[nt++] = "DISCARDABLE";
    428     if (coff_chars & OBJDUMP_IMAGE_SCN_MEM_SHARED) tags[nt++] = "SHARED";
    429     if (coff_chars & OBJDUMP_IMAGE_SCN_GPREL) tags[nt++] = "GPREL";
    430   }
    431 
    432   for (i = 0; i < nt && n + 1 < cap; ++i) {
    433     const char* t = tags[i];
    434     if (i > 0 && n + 1 < cap) buf[n++] = ',';
    435     while (*t && n + 1 < cap) buf[n++] = *t++;
    436   }
    437   if (sec->entsize && n + 1 < cap) {
    438     char tmp[32];
    439     int k = snprintf(tmp, sizeof tmp, "%.*sentsize=%u",
    440                      KIT_SLICE_ARG(kit_slice_cstr(n ? "," : "")), sec->entsize);
    441     size_t j;
    442     for (j = 0; k > 0 && j < (size_t)k && n + 1 < cap; ++j) buf[n++] = tmp[j];
    443   }
    444   buf[n] = '\0';
    445 }
    446 
    447 static void dump_sections(KitObjFile* f, const ObjdumpOpts* opts) {
    448   uint32_t nsec = kit_obj_nsections(f);
    449   KitObjFmt fmt = kit_obj_fmt(f);
    450   uint32_t i;
    451   char flagbuf[160];
    452 
    453   driver_printf("Sections:\n");
    454   driver_printf("Idx Name                 Size      Align  Flags\n");
    455   for (i = 0; i < nsec; ++i) {
    456     KitObjSecInfo sec;
    457     uint32_t raw_type = 0;
    458     if (kit_obj_section(f, i, &sec) != KIT_OK) continue;
    459     if (!j_match(opts, sec.name)) continue;
    460     kit_obj_section_format_flags(f, i, &raw_type, NULL);
    461     render_sec_flags(&sec, fmt, raw_type, flagbuf, sizeof(flagbuf));
    462     driver_printf(
    463         "%3u %-20s %08llx  2**%-2u  %.*s\n", i,
    464         sec.name.len ? sec.name.s : "(anon)", (unsigned long long)sec.size,
    465         sec.align ? (unsigned)__builtin_ctz(sec.align ? sec.align : 1) : 0,
    466         KIT_SLICE_ARG(kit_slice_cstr(flagbuf)));
    467     /* Show the raw IMAGE_SCN_* value on a continuation line for COFF
    468      * inputs — useful when diagnosing why a section ended up with the
    469      * tags it did. The hex is much shorter than printing every set bit
    470      * by name, and the tag list above already covers the bits that
    471      * change behaviour at link time. */
    472     if (fmt == KIT_OBJ_COFF && raw_type) {
    473       driver_printf(
    474           "                                              Characteristics: "
    475           "0x%08x\n",
    476           raw_type);
    477     }
    478   }
    479   driver_printf("\n");
    480 }
    481 
    482 /* GNU objdump prints COMDAT group membership immediately after the
    483  * section header table. The reader exposes groups uniformly across
    484  * formats (ELF SHT_GROUP and COFF COMDAT both arrive here) so we just
    485  * iterate.  Output is silent when the object carries no groups. */
    486 static void dump_groups(KitObjFile* f, const ObjdumpOpts* opts) {
    487   KitObjGroupIter* it = NULL;
    488   KitObjGroupInfo g;
    489   int printed_header = 0;
    490   (void)opts;
    491 
    492   if (kit_obj_groupiter_new(f, &it) != KIT_OK) return;
    493   while (kit_obj_groupiter_next(it, &g) == KIT_ITER_ITEM) {
    494     uint32_t k;
    495     if (!printed_header) {
    496       driver_printf("COMDAT groups:\n");
    497       printed_header = 1;
    498     }
    499     driver_printf("  group %.*s (signature sym #%u, %u section%.*s)\n",
    500                   KIT_SLICE_ARG(g.name.len ? g.name : KIT_SLICE_LIT("(anon)")),
    501                   (unsigned)g.signature, (unsigned)g.nsections,
    502                   KIT_SLICE_ARG(kit_slice_cstr(g.nsections == 1 ? "" : "s")));
    503     for (k = 0; k < g.nsections; ++k) {
    504       KitObjSection sid = g.sections[k];
    505       KitObjSecInfo si;
    506       if (sid == KIT_SECTION_NONE) continue;
    507       if (kit_obj_section(f, sid, &si) != KIT_OK) continue;
    508       driver_printf(
    509           "    [%3u] %.*s\n", (unsigned)sid,
    510           KIT_SLICE_ARG(si.name.len ? si.name : KIT_SLICE_LIT("(anon)")));
    511     }
    512   }
    513   kit_obj_groupiter_free(it);
    514   if (printed_header) driver_printf("\n");
    515 }
    516 
    517 /* `dynamic` selects the dynamic symbol table (.dynsym / export trie) via
    518  * kit_obj_dynsymiter_new instead of the static .symtab. Both share the
    519  * KitObjSymInfo shape and the same _next/_free, so the body is identical. */
    520 static void dump_symbols(KitObjFile* f, const ObjdumpOpts* opts, int dynamic) {
    521   KitObjSymIter* it = NULL;
    522   KitObjSymInfo sym;
    523   KitStatus st;
    524 
    525   driver_printf(dynamic ? "DYNAMIC SYMBOL TABLE:\n" : "SYMBOL TABLE:\n");
    526   st = dynamic ? kit_obj_dynsymiter_new(f, &it) : kit_obj_symiter_new(f, &it);
    527   if (st != KIT_OK) return;
    528   for (;;) {
    529     KitIterResult r = kit_obj_symiter_next(it, &sym);
    530     KitSlice secname;
    531     if (r != KIT_ITER_ITEM) break;
    532     if (sym.section == KIT_SECTION_NONE) {
    533       secname = KIT_SLICE_LIT("*UND*");
    534     } else {
    535       KitObjSecInfo sec;
    536       if (kit_obj_section(f, sym.section, &sec) != KIT_OK) continue;
    537       secname = sec.name.len ? sec.name : KIT_SLICE_LIT("(none)");
    538       if (opts->nj && !j_match(opts, secname)) continue;
    539     }
    540     driver_printf(
    541         "%016llx %c   %c %-18s %016llx %.*s\n", (unsigned long long)sym.value,
    542         sym_bind_char(sym.bind), sym_kind_char(sym.kind), secname.s,
    543         (unsigned long long)sym.size,
    544         KIT_SLICE_ARG(sym.name.len ? sym.name : KIT_SLICE_LIT("(none)")));
    545   }
    546   kit_obj_symiter_free(it);
    547   driver_printf("\n");
    548 }
    549 
    550 static void dump_hex(KitObjFile* f, const ObjdumpOpts* opts) {
    551   uint32_t nsec = kit_obj_nsections(f);
    552   uint32_t i;
    553 
    554   for (i = 0; i < nsec; ++i) {
    555     KitObjSecInfo sec;
    556     size_t len = 0;
    557     const uint8_t* data = NULL;
    558     size_t ofs;
    559 
    560     if (kit_obj_section(f, i, &sec) != KIT_OK) continue;
    561     if (!j_match(opts, sec.name)) continue;
    562     if (kit_obj_section_data(f, i, &data, &len) != KIT_OK) continue;
    563     if (!data || len == 0) continue;
    564 
    565     driver_printf(
    566         "Contents of section %.*s:\n",
    567         KIT_SLICE_ARG(sec.name.len ? sec.name : KIT_SLICE_LIT("(anon)")));
    568     for (ofs = 0; ofs < len; ofs += 16) {
    569       size_t j;
    570       char ascii[17];
    571       driver_printf(" %04llx ", (unsigned long long)ofs);
    572       for (j = 0; j < 16; ++j) {
    573         if (ofs + j < len) {
    574           driver_printf("%02x", data[ofs + j]);
    575           ascii[j] = (data[ofs + j] >= 32 && data[ofs + j] < 127)
    576                          ? (char)data[ofs + j]
    577                          : '.';
    578         } else {
    579           driver_printf("  ");
    580           ascii[j] = ' ';
    581         }
    582         if ((j & 1) == 1) driver_printf(" ");
    583       }
    584       ascii[16] = '\0';
    585       driver_printf(" %.*s\n", KIT_SLICE_ARG(kit_slice_cstr(ascii)));
    586     }
    587   }
    588   driver_printf("\n");
    589 }
    590 
    591 static void dump_relocs(KitObjFile* f, const ObjdumpOpts* opts) {
    592   KitObjRelocIter* it = NULL;
    593   KitObjReloc r;
    594   uint32_t cur_sec = (uint32_t)-1;
    595   int printed_header = 0;
    596   int emitted_any = 0;
    597 
    598   if (kit_obj_reliter_new(f, &it) != KIT_OK) return;
    599   for (;;) {
    600     KitObjSecInfo sec;
    601     KitIterResult res = kit_obj_reliter_next(it, &r);
    602     if (res != KIT_ITER_ITEM) break;
    603     if (kit_obj_section(f, r.section, &sec) != KIT_OK) continue;
    604     if (!j_match(opts, sec.name)) continue;
    605 
    606     if (r.section != cur_sec) {
    607       if (printed_header) driver_printf("\n");
    608       driver_printf(
    609           "RELOCATION RECORDS FOR [%.*s]:\n",
    610           KIT_SLICE_ARG(sec.name.len ? sec.name : KIT_SLICE_LIT("(anon)")));
    611       driver_printf("OFFSET           TYPE              VALUE\n");
    612       cur_sec = r.section;
    613       printed_header = 1;
    614     }
    615 
    616     if (r.addend) {
    617       driver_printf(
    618           "%016llx %-17s %.*s%c0x%llx\n", (unsigned long long)r.offset,
    619           r.kind_name.len ? r.kind_name.s : "?",
    620           KIT_SLICE_ARG(r.sym_name.len ? r.sym_name : KIT_SLICE_LIT("*ABS*")),
    621           r.addend < 0 ? '-' : '+',
    622           (unsigned long long)(r.addend < 0 ? -r.addend : r.addend));
    623     } else {
    624       driver_printf(
    625           "%016llx %-17s %.*s\n", (unsigned long long)r.offset,
    626           r.kind_name.len ? r.kind_name.s : "?",
    627           KIT_SLICE_ARG(r.sym_name.len ? r.sym_name : KIT_SLICE_LIT("*ABS*")));
    628     }
    629     emitted_any = 1;
    630   }
    631   kit_obj_reliter_free(it);
    632   if (emitted_any) driver_printf("\n");
    633 }
    634 
    635 /* Find a symbol whose value is exactly `value`. When `section_idx` is a real
    636  * index the match is scoped to that section; pass OBJDUMP_SEC_ANY to match by
    637  * address across the whole symbol table — used by the segment-fallback path,
    638  * where the disassembled bytes have no owning section. */
    639 #define OBJDUMP_SEC_ANY UINT32_MAX
    640 
    641 static KitSlice objdump_sym_at(KitObjFile* f, uint32_t section_idx,
    642                                uint64_t value) {
    643   KitObjSymIter* it = NULL;
    644   KitObjSymInfo sym;
    645   KitSlice best = KIT_SLICE_NULL;
    646 
    647   if (kit_obj_symiter_new(f, &it) != KIT_OK) return KIT_SLICE_NULL;
    648   for (;;) {
    649     KitIterResult r = kit_obj_symiter_next(it, &sym);
    650     if (r != KIT_ITER_ITEM) break;
    651     if (section_idx != OBJDUMP_SEC_ANY && sym.section != section_idx) continue;
    652     if (sym.value != value) continue;
    653     if (!sym.name.len) continue;
    654     if (sym.kind == KIT_SK_SECTION) continue;
    655     best = sym.name;
    656     if (sym.kind == KIT_SK_FUNC || sym.bind != KIT_SB_LOCAL) break;
    657   }
    658   kit_obj_symiter_free(it);
    659   return best;
    660 }
    661 
    662 /* Disassemble `len` bytes at `data`, treating `vaddr` as the address of the
    663  * first byte. Symbol labels are looked up via `sym_section` (a real section
    664  * index, or OBJDUMP_SEC_ANY for the segment-fallback path). */
    665 static void disasm_buffer(const KitDisasmContext* dctx, KitObjFile* f,
    666                           const uint8_t* data, size_t len, uint64_t vaddr,
    667                           uint32_t sym_section) {
    668   KitDisasmIter* dis = NULL;
    669   KitInsn insn;
    670 
    671   if (kit_disasm_iter_new(dctx, data, len, vaddr, f, &dis) != KIT_OK) return;
    672   for (;;) {
    673     KitIterResult r = kit_disasm_iter_next(dis, &insn);
    674     uint32_t b;
    675     KitSlice label;
    676     if (r != KIT_ITER_ITEM) break;
    677     label = objdump_sym_at(f, sym_section, insn.vaddr);
    678     if (label.len)
    679       driver_printf("%016llx <%.*s>:\n", (unsigned long long)insn.vaddr,
    680                     KIT_SLICE_ARG(label));
    681     driver_printf("%8llx:\t", (unsigned long long)insn.vaddr);
    682     for (b = 0; b < insn.nbytes; ++b) driver_printf("%02x ", insn.bytes[b]);
    683     for (b = insn.nbytes; b < 8; ++b) driver_printf("   ");
    684     driver_printf("\t%.*s", KIT_SLICE_ARG(insn.mnemonic));
    685     if (insn.operands.len) {
    686       driver_printf(" %.*s", KIT_SLICE_ARG(insn.operands));
    687     }
    688     if (insn.annotation.len) {
    689       driver_printf(" # %.*s", KIT_SLICE_ARG(insn.annotation));
    690     }
    691     driver_printf("\n");
    692   }
    693   kit_disasm_iter_free(dis);
    694 }
    695 
    696 /* Fallback for fully section-stripped images (objcopy --strip-sections,
    697  * packers): the section table is gone, but the code still lives in the
    698  * executable PT_LOAD segments. We disassemble each such segment's file
    699  * contents directly, using its vaddr as the base. `image` is the raw file
    700  * bytes; segment file_off/file_size index into it. Returns the number of
    701  * segments disassembled. Format-agnostic — driven entirely by the segment
    702  * iterator, no ELF/Mach-O special-casing. */
    703 static uint32_t dump_disasm_segments(const KitDisasmContext* dctx,
    704                                      KitObjFile* f, const ObjdumpOpts* opts,
    705                                      const KitSlice* image) {
    706   KitObjSegIter* sit = NULL;
    707   KitObjSegInfo seg;
    708   uint32_t emitted = 0;
    709 
    710   if (!image || kit_obj_segiter_new(f, &sit) != KIT_OK) return 0;
    711   while (kit_obj_segiter_next(sit, &seg) == KIT_ITER_ITEM) {
    712     if (!(seg.perms & KIT_SEG_X)) continue;
    713     if (!j_match(opts, seg.name)) continue;
    714     if (seg.file_size == 0) continue;
    715     if (seg.file_off > image->len || seg.file_size > image->len - seg.file_off)
    716       continue;
    717 
    718     driver_printf(
    719         "Disassembly of segment %.*s:\n\n",
    720         KIT_SLICE_ARG(seg.name.len ? seg.name : KIT_SLICE_LIT("LOAD")));
    721     disasm_buffer(dctx, f, (const uint8_t*)image->data + seg.file_off,
    722                   (size_t)seg.file_size, seg.vaddr, OBJDUMP_SEC_ANY);
    723     driver_printf("\n");
    724     emitted++;
    725   }
    726   kit_obj_segiter_free(sit);
    727   return emitted;
    728 }
    729 
    730 static void dump_disasm(const KitDisasmContext* dctx, KitObjFile* f,
    731                         const ObjdumpOpts* opts, const KitSlice* image) {
    732   uint32_t nsec = kit_obj_nsections(f);
    733   uint32_t i;
    734   uint32_t emitted = 0;
    735   KitDisasmContext file_dctx;
    736   KitTarget* file_target = NULL;
    737   KitTargetOptions topts;
    738 
    739   if (!dctx) return;
    740   file_dctx = *dctx;
    741   memset(&topts, 0, sizeof topts);
    742   topts.spec = kit_obj_target(f);
    743   if (kit_target_new(&dctx->context, &topts, &file_target) != KIT_OK) return;
    744   file_dctx.target = file_target;
    745 
    746   for (i = 0; i < nsec; ++i) {
    747     KitObjSecInfo sec;
    748     size_t len = 0;
    749     const uint8_t* data = NULL;
    750     int want;
    751 
    752     if (kit_obj_section(f, i, &sec) != KIT_OK) continue;
    753     want = opts->D ? 1 : ((sec.flags & KIT_SF_EXEC) != 0);
    754     if (!want) continue;
    755     if (!j_match(opts, sec.name)) continue;
    756 
    757     if (kit_obj_section_data(f, i, &data, &len) != KIT_OK) continue;
    758     if (!data || len == 0) continue;
    759 
    760     driver_printf(
    761         "Disassembly of section %.*s:\n\n",
    762         KIT_SLICE_ARG(sec.name.len ? sec.name : KIT_SLICE_LIT("(anon)")));
    763     /* sec.addr is the load vaddr for a linked image, 0 for a relocatable
    764      * object — so branch/call targets resolve correctly in both. */
    765     disasm_buffer(&file_dctx, f, data, len, sec.addr, i);
    766     driver_printf("\n");
    767     emitted++;
    768   }
    769 
    770   /* No disassemblable sections, but this is a linked image: the section table
    771    * was stripped. Fall back to the executable load segments. */
    772   if (emitted == 0 && kit_obj_kind(f) != KIT_OBJ_KIND_REL)
    773     dump_disasm_segments(&file_dctx, f, opts, image);
    774   kit_target_free(file_target);
    775 }
    776 
    777 /* `-f`: GNU objdump-style file header summary. Object files have no
    778  * meaningful entry point so start address is always 0. For a PE image we
    779  * also surface the Windows subsystem (via the raw-fields escape hatch). The
    780  * flags line summarizes whether the input has symbols and relocations so
    781  * it's clear at a glance whether further -t / -r work is going to be
    782  * productive. */
    783 static void dump_file_header(KitObjFile* f, const char* label) {
    784   KitTargetSpec target = kit_obj_target(f);
    785   KitObjFmt fmt = kit_obj_fmt(f);
    786   KitObjSymIter* sit = NULL;
    787   KitObjRelocIter* rit = NULL;
    788   uint32_t nsec = kit_obj_nsections(f);
    789   uint32_t nsym = 0;
    790   int has_relocs = 0;
    791   unsigned flags = 0;
    792   KitObjKind kind = kit_obj_kind(f);
    793   KitObjImageInfo info;
    794   int have_info = kit_obj_image_info(f, &info) == KIT_OK;
    795   const char* sep = "";
    796 
    797   if (kit_obj_symiter_new(f, &sit) == KIT_OK) {
    798     KitObjSymInfo s;
    799     while (kit_obj_symiter_next(sit, &s) == KIT_ITER_ITEM) nsym++;
    800     kit_obj_symiter_free(sit);
    801   }
    802   if (kit_obj_reliter_new(f, &rit) == KIT_OK) {
    803     KitObjReloc r;
    804     if (kit_obj_reliter_next(rit, &r) == KIT_ITER_ITEM) has_relocs = 1;
    805     kit_obj_reliter_free(rit);
    806   }
    807   /* GNU objdump's BFD flag bits: 0x01 HAS_RELOC, 0x02 EXEC_P, 0x10 HAS_SYMS,
    808    * 0x40 DYNAMIC, 0x100 D_PAGED. */
    809   if (has_relocs) flags |= 0x0001u;
    810   if (kind == KIT_OBJ_KIND_EXEC) flags |= 0x0002u;
    811   if (nsym) flags |= 0x0010u;
    812   if (kind == KIT_OBJ_KIND_DYN) flags |= 0x0040u;
    813   if (kind != KIT_OBJ_KIND_REL) flags |= 0x0100u;
    814 
    815   driver_printf("architecture: %.*s, flags 0x%08x:\n",
    816                 KIT_SLICE_ARG(kit_slice_cstr(arch_str(target.arch))), flags);
    817 #define OBJDUMP_FLAG(bit, name)         \
    818   do {                                  \
    819     if (flags & (bit)) {                \
    820       driver_printf("%s%s", sep, name); \
    821       sep = ", ";                       \
    822     }                                   \
    823   } while (0)
    824   OBJDUMP_FLAG(0x0001u, "HAS_RELOC");
    825   OBJDUMP_FLAG(0x0002u, "EXEC_P");
    826   OBJDUMP_FLAG(0x0010u, "HAS_SYMS");
    827   OBJDUMP_FLAG(0x0040u, "DYNAMIC");
    828   OBJDUMP_FLAG(0x0100u, "D_PAGED");
    829 #undef OBJDUMP_FLAG
    830   if (flags) driver_printf("\n");
    831   driver_printf("start address 0x%016llx\n",
    832                 have_info ? (unsigned long long)info.entry : 0ull);
    833   {
    834     char fmt_buf[FMT_STR_CAP];
    835     driver_printf(
    836         "format: %.*s, sections: %u, symbols: %u\n\n",
    837         KIT_SLICE_ARG(kit_slice_cstr(
    838             fmt_str(fmt, target.ptr_size, fmt_buf, sizeof fmt_buf))),
    839         nsec, nsym);
    840   }
    841   if (fmt == KIT_OBJ_COFF && kind != KIT_OBJ_KIND_REL) {
    842     PeRaw raw;
    843     if (pe_collect_raw(f, &raw))
    844       driver_printf(
    845           "subsystem: %u (%.*s)\n\n", (unsigned)raw.subsystem,
    846           KIT_SLICE_ARG(kit_slice_cstr(pe_subsystem_name(raw.subsystem))));
    847   }
    848   (void)label;
    849 }
    850 
    851 /* ---- DWARF structural dump (`--dwarf`) ----
    852  *
    853  * Pulls the raw .debug_info / .debug_abbrev / .debug_line / .debug_str
    854  * structure out via the kit_dwarf_*_iter API and formats it. The library
    855  * hands back numeric DWARF codes; the symbolic-name tables below live here
    856  * (display logic stays in the driver). Unknown codes fall back to hex. */
    857 
    858 static const char* dw_tag_name(uint32_t tag) {
    859   switch (tag) {
    860     case 0x01:
    861       return "DW_TAG_array_type";
    862     case 0x04:
    863       return "DW_TAG_enumeration_type";
    864     case 0x05:
    865       return "DW_TAG_formal_parameter";
    866     case 0x0b:
    867       return "DW_TAG_lexical_block";
    868     case 0x0d:
    869       return "DW_TAG_member";
    870     case 0x0f:
    871       return "DW_TAG_pointer_type";
    872     case 0x11:
    873       return "DW_TAG_compile_unit";
    874     case 0x13:
    875       return "DW_TAG_structure_type";
    876     case 0x15:
    877       return "DW_TAG_subroutine_type";
    878     case 0x16:
    879       return "DW_TAG_typedef";
    880     case 0x17:
    881       return "DW_TAG_union_type";
    882     case 0x18:
    883       return "DW_TAG_unspecified_parameters";
    884     case 0x1d:
    885       return "DW_TAG_inlined_subroutine";
    886     case 0x21:
    887       return "DW_TAG_subrange_type";
    888     case 0x24:
    889       return "DW_TAG_base_type";
    890     case 0x26:
    891       return "DW_TAG_const_type";
    892     case 0x28:
    893       return "DW_TAG_enumerator";
    894     case 0x2e:
    895       return "DW_TAG_subprogram";
    896     case 0x34:
    897       return "DW_TAG_variable";
    898     case 0x35:
    899       return "DW_TAG_volatile_type";
    900     case 0x37:
    901       return "DW_TAG_restrict_type";
    902     case 0x3b:
    903       return "DW_TAG_unspecified_type";
    904     default:
    905       return NULL;
    906   }
    907 }
    908 
    909 static const char* dw_at_name(uint32_t at) {
    910   switch (at) {
    911     case 0x01:
    912       return "DW_AT_sibling";
    913     case 0x02:
    914       return "DW_AT_location";
    915     case 0x03:
    916       return "DW_AT_name";
    917     case 0x0b:
    918       return "DW_AT_byte_size";
    919     case 0x0d:
    920       return "DW_AT_bit_size";
    921     case 0x10:
    922       return "DW_AT_stmt_list";
    923     case 0x11:
    924       return "DW_AT_low_pc";
    925     case 0x12:
    926       return "DW_AT_high_pc";
    927     case 0x13:
    928       return "DW_AT_language";
    929     case 0x1b:
    930       return "DW_AT_comp_dir";
    931     case 0x1c:
    932       return "DW_AT_const_value";
    933     case 0x25:
    934       return "DW_AT_producer";
    935     case 0x27:
    936       return "DW_AT_prototyped";
    937     case 0x2f:
    938       return "DW_AT_upper_bound";
    939     case 0x34:
    940       return "DW_AT_artificial";
    941     case 0x37:
    942       return "DW_AT_count";
    943     case 0x38:
    944       return "DW_AT_data_member_location";
    945     case 0x39:
    946       return "DW_AT_decl_column";
    947     case 0x3a:
    948       return "DW_AT_decl_file";
    949     case 0x3b:
    950       return "DW_AT_decl_line";
    951     case 0x3c:
    952       return "DW_AT_declaration";
    953     case 0x3e:
    954       return "DW_AT_encoding";
    955     case 0x3f:
    956       return "DW_AT_external";
    957     case 0x40:
    958       return "DW_AT_frame_base";
    959     case 0x49:
    960       return "DW_AT_type";
    961     case 0x6e:
    962       return "DW_AT_linkage_name";
    963     case 0x88:
    964       return "DW_AT_alignment";
    965     default:
    966       return NULL;
    967   }
    968 }
    969 
    970 static const char* dw_form_name(uint32_t form) {
    971   switch (form) {
    972     case 0x01:
    973       return "DW_FORM_addr";
    974     case 0x05:
    975       return "DW_FORM_data2";
    976     case 0x06:
    977       return "DW_FORM_data4";
    978     case 0x07:
    979       return "DW_FORM_data8";
    980     case 0x08:
    981       return "DW_FORM_string";
    982     case 0x09:
    983       return "DW_FORM_block";
    984     case 0x0b:
    985       return "DW_FORM_data1";
    986     case 0x0c:
    987       return "DW_FORM_flag";
    988     case 0x0d:
    989       return "DW_FORM_sdata";
    990     case 0x0e:
    991       return "DW_FORM_strp";
    992     case 0x0f:
    993       return "DW_FORM_udata";
    994     case 0x10:
    995       return "DW_FORM_ref_addr";
    996     case 0x11:
    997       return "DW_FORM_ref1";
    998     case 0x12:
    999       return "DW_FORM_ref2";
   1000     case 0x13:
   1001       return "DW_FORM_ref4";
   1002     case 0x14:
   1003       return "DW_FORM_ref8";
   1004     case 0x15:
   1005       return "DW_FORM_ref_udata";
   1006     case 0x17:
   1007       return "DW_FORM_sec_offset";
   1008     case 0x18:
   1009       return "DW_FORM_exprloc";
   1010     case 0x19:
   1011       return "DW_FORM_flag_present";
   1012     case 0x1a:
   1013       return "DW_FORM_strx";
   1014     case 0x1b:
   1015       return "DW_FORM_addrx";
   1016     case 0x1f:
   1017       return "DW_FORM_line_strp";
   1018     case 0x21:
   1019       return "DW_FORM_implicit_const";
   1020     case 0x25:
   1021       return "DW_FORM_strx1";
   1022     default:
   1023       return NULL;
   1024   }
   1025 }
   1026 
   1027 /* Print a symbolic DWARF code or, when unknown, its hex value. */
   1028 static void dw_emit_code(const char* name, uint32_t val) {
   1029   if (name)
   1030     driver_printf("%s", name);
   1031   else
   1032     driver_printf("0x%x", val);
   1033 }
   1034 
   1035 static void dw_emit_attr_value(const KitDwarfAttr* a) {
   1036   switch (a->form_class) {
   1037     case KIT_DWARF_FC_STRING:
   1038       driver_printf("\"%.*s\"", KIT_SLICE_ARG(a->str));
   1039       break;
   1040     case KIT_DWARF_FC_SDATA:
   1041       driver_printf("%lld", (long long)a->s);
   1042       break;
   1043     case KIT_DWARF_FC_FLAG:
   1044       driver_printf("%s", a->u ? "true" : "false");
   1045       break;
   1046     case KIT_DWARF_FC_BLOCK: {
   1047       uint32_t i;
   1048       driver_printf("%u byte block:", a->block_len);
   1049       for (i = 0; i < a->block_len; ++i) driver_printf(" %02x", a->block[i]);
   1050       break;
   1051     }
   1052     case KIT_DWARF_FC_ADDR:
   1053     case KIT_DWARF_FC_REF:
   1054     case KIT_DWARF_FC_UDATA:
   1055     default:
   1056       driver_printf("0x%llx", (unsigned long long)a->u);
   1057       break;
   1058   }
   1059 }
   1060 
   1061 static void dump_dwarf_die_attrs(KitDebugInfo* dbg, uint32_t die_offset,
   1062                                  uint32_t depth) {
   1063   KitDwarfAttrIter* ai = NULL;
   1064   KitDwarfAttr a;
   1065   uint32_t k;
   1066   if (kit_dwarf_attr_iter_new(dbg, die_offset, &ai) != KIT_OK) return;
   1067   while (kit_dwarf_attr_iter_next(ai, &a) == KIT_ITER_ITEM) {
   1068     for (k = 0; k <= depth + 1; ++k) driver_printf("  ");
   1069     dw_emit_code(dw_at_name(a.attr), a.attr);
   1070     driver_printf(" (");
   1071     dw_emit_code(dw_form_name(a.form), a.form);
   1072     driver_printf(") = ");
   1073     dw_emit_attr_value(&a);
   1074     driver_printf("\n");
   1075   }
   1076   kit_dwarf_attr_iter_free(ai);
   1077 }
   1078 
   1079 static void dump_dwarf_info(KitDebugInfo* dbg) {
   1080   KitDwarfCuIter* cui = NULL;
   1081   KitDwarfDieIter* dii = NULL;
   1082   KitDwarfCu cu;
   1083   KitDwarfDie die;
   1084   driver_printf(".debug_info contents:\n");
   1085   if (kit_dwarf_cu_iter_new(dbg, &cui) == KIT_OK) {
   1086     while (kit_dwarf_cu_iter_next(cui, &cu) == KIT_ITER_ITEM) {
   1087       driver_printf(
   1088           "  Compilation Unit @ offset 0x%x: version %u, abbrev_offset 0x%x, "
   1089           "addr_size %u, length 0x%x\n",
   1090           cu.offset, (unsigned)cu.version, cu.abbrev_offset,
   1091           (unsigned)cu.address_size, cu.length);
   1092     }
   1093     kit_dwarf_cu_iter_free(cui);
   1094   }
   1095   if (kit_dwarf_die_iter_new(dbg, &dii) != KIT_OK) return;
   1096   while (kit_dwarf_die_iter_next(dii, &die) == KIT_ITER_ITEM) {
   1097     uint32_t k;
   1098     for (k = 0; k <= die.depth; ++k) driver_printf("  ");
   1099     driver_printf("<0x%x> ", die.offset);
   1100     dw_emit_code(dw_tag_name(die.tag), die.tag);
   1101     driver_printf("\n");
   1102     dump_dwarf_die_attrs(dbg, die.offset, die.depth);
   1103   }
   1104   kit_dwarf_die_iter_free(dii);
   1105   driver_printf("\n");
   1106 }
   1107 
   1108 static void dump_dwarf_abbrev(KitDebugInfo* dbg) {
   1109   KitDwarfAbbrevIter* it = NULL;
   1110   KitDwarfAbbrev ab;
   1111   uint32_t cur_table = 0xffffffffu;
   1112   driver_printf(".debug_abbrev contents:\n");
   1113   if (kit_dwarf_abbrev_iter_new(dbg, &it) != KIT_OK) return;
   1114   while (kit_dwarf_abbrev_iter_next(it, &ab) == KIT_ITER_ITEM) {
   1115     KitDwarfAbbrevAttrIter* ait = NULL;
   1116     KitDwarfAbbrevAttr aa;
   1117     if (ab.table_offset != cur_table) {
   1118       cur_table = ab.table_offset;
   1119       driver_printf("  Abbrev table @ offset 0x%x:\n", cur_table);
   1120     }
   1121     driver_printf("    [%llu] ", (unsigned long long)ab.code);
   1122     dw_emit_code(dw_tag_name(ab.tag), ab.tag);
   1123     driver_printf(" %s\n",
   1124                   ab.has_children ? "[has children]" : "[no children]");
   1125     if (kit_dwarf_abbrev_attr_iter_new(dbg, ab.table_offset, ab.code, &ait) !=
   1126         KIT_OK)
   1127       continue;
   1128     while (kit_dwarf_abbrev_attr_iter_next(ait, &aa) == KIT_ITER_ITEM) {
   1129       driver_printf("        ");
   1130       dw_emit_code(dw_at_name(aa.attr), aa.attr);
   1131       driver_printf("  ");
   1132       dw_emit_code(dw_form_name(aa.form), aa.form);
   1133       driver_printf("\n");
   1134     }
   1135     kit_dwarf_abbrev_attr_iter_free(ait);
   1136   }
   1137   kit_dwarf_abbrev_iter_free(it);
   1138   driver_printf("\n");
   1139 }
   1140 
   1141 static void dump_dwarf_line(KitDebugInfo* dbg) {
   1142   KitDwarfCuIter* cui = NULL;
   1143   KitDwarfCu cu;
   1144   driver_printf(".debug_line contents:\n");
   1145   if (kit_dwarf_cu_iter_new(dbg, &cui) != KIT_OK) return;
   1146   while (kit_dwarf_cu_iter_next(cui, &cu) == KIT_ITER_ITEM) {
   1147     KitDwarfLineIter* li = NULL;
   1148     KitDwarfLineRow row;
   1149     if (kit_dwarf_line_iter_new(dbg, cu.offset, &li) != KIT_OK) continue;
   1150     driver_printf("  CU @ offset 0x%x:\n", cu.offset);
   1151     driver_printf("    %-18s %-6s %-6s %-4s %s\n", "Address", "File", "Line",
   1152                   "Col", "Flags");
   1153     while (kit_dwarf_line_iter_next(li, &row) == KIT_ITER_ITEM) {
   1154       driver_printf("    0x%016llx %-6u %-6u %-4u %s%s\n",
   1155                     (unsigned long long)row.address, row.file_index, row.line,
   1156                     row.column, row.is_stmt ? "stmt " : "",
   1157                     row.end_sequence ? "end_seq" : "");
   1158     }
   1159     kit_dwarf_line_iter_free(li);
   1160   }
   1161   kit_dwarf_cu_iter_free(cui);
   1162   driver_printf("\n");
   1163 }
   1164 
   1165 static void dump_dwarf_str(KitDebugInfo* dbg) {
   1166   KitDwarfStrIter* it = NULL;
   1167   KitDwarfStr s;
   1168   driver_printf(".debug_str contents:\n");
   1169   if (kit_dwarf_str_iter_new(dbg, &it) != KIT_OK) return;
   1170   while (kit_dwarf_str_iter_next(it, &s) == KIT_ITER_ITEM) {
   1171     driver_printf("  0x%x  \"%.*s\"\n", s.offset, KIT_SLICE_ARG(s.str));
   1172   }
   1173   kit_dwarf_str_iter_free(it);
   1174   driver_printf("\n");
   1175 }
   1176 
   1177 static void dump_dwarf(const KitContext* ctx, KitObjFile* f,
   1178                        const ObjdumpOpts* opts) {
   1179   KitDebugInfo* dbg = NULL;
   1180   if (kit_dwarf_open(ctx, f, &dbg) != KIT_OK || !dbg) {
   1181     driver_errf(OBJDUMP_TOOL, "no DWARF debug info found");
   1182     return;
   1183   }
   1184   if (opts->dwarf & OBJDUMP_DWARF_INFO) dump_dwarf_info(dbg);
   1185   if (opts->dwarf & OBJDUMP_DWARF_ABBREV) dump_dwarf_abbrev(dbg);
   1186   if (opts->dwarf & OBJDUMP_DWARF_LINE) dump_dwarf_line(dbg);
   1187   if (opts->dwarf & OBJDUMP_DWARF_STR) dump_dwarf_str(dbg);
   1188   kit_dwarf_free(dbg);
   1189 }
   1190 
   1191 /* Dynamic relocations (-R). Unlike section relocations these aren't grouped
   1192  * by section, so we print one flat table in GNU `objdump -R` style. */
   1193 static void dump_dynrelocs(KitObjFile* f) {
   1194   KitObjRelocIter* it = NULL;
   1195   KitObjReloc r;
   1196   int any = 0;
   1197 
   1198   if (kit_obj_dynreliter_new(f, &it) != KIT_OK) return;
   1199   for (;;) {
   1200     KitIterResult res = kit_obj_reliter_next(it, &r);
   1201     if (res != KIT_ITER_ITEM) break;
   1202     if (!any) {
   1203       driver_printf("DYNAMIC RELOCATION RECORDS\n");
   1204       driver_printf("OFFSET           TYPE              VALUE\n");
   1205       any = 1;
   1206     }
   1207     if (r.addend) {
   1208       driver_printf(
   1209           "%016llx %-17s %.*s%c0x%llx\n", (unsigned long long)r.offset,
   1210           r.kind_name.len ? r.kind_name.s : "?",
   1211           KIT_SLICE_ARG(r.sym_name.len ? r.sym_name : KIT_SLICE_LIT("*ABS*")),
   1212           r.addend < 0 ? '-' : '+',
   1213           (unsigned long long)(r.addend < 0 ? -r.addend : r.addend));
   1214     } else {
   1215       driver_printf(
   1216           "%016llx %-17s %.*s\n", (unsigned long long)r.offset,
   1217           r.kind_name.len ? r.kind_name.s : "?",
   1218           KIT_SLICE_ARG(r.sym_name.len ? r.sym_name : KIT_SLICE_LIT("*ABS*")));
   1219     }
   1220   }
   1221   kit_obj_reliter_free(it);
   1222   driver_printf(any ? "\n" : "DYNAMIC RELOCATION RECORDS (none)\n\n");
   1223 }
   1224 
   1225 static const char* seg_perms_str(uint32_t perms) {
   1226   static char b[4];
   1227   b[0] = (perms & KIT_SEG_R) ? 'r' : '-';
   1228   b[1] = (perms & KIT_SEG_W) ? 'w' : '-';
   1229   b[2] = (perms & KIT_SEG_X) ? 'x' : '-';
   1230   b[3] = '\0';
   1231   return b;
   1232 }
   1233 
   1234 /* align is a power of two; report it as 2**N like GNU objdump. */
   1235 static unsigned u32_log2(uint32_t v) {
   1236   unsigned n = 0;
   1237   while (v > 1) {
   1238     v >>= 1;
   1239     ++n;
   1240   }
   1241   return n;
   1242 }
   1243 
   1244 /* Private/program headers (-p): the linked-image view. PE images get the
   1245  * GNU objdump "PE32+ private headers" rendering (optional header + data
   1246  * directories + import tables); ELF / Mach-O get the entry/segments/dynamic
   1247  * view. Both are driven by the neutral kit_obj image API. Relocatable
   1248  * objects have no image and report so. */
   1249 static void dump_private(KitObjFile* f, const char* label) {
   1250   KitObjImageInfo info;
   1251   KitObjSegIter* sit = NULL;
   1252   KitObjDepIter* dit = NULL;
   1253   KitObjSegInfo seg;
   1254   KitObjDepInfo dep;
   1255   int have_info;
   1256 
   1257   if (kit_obj_fmt(f) == KIT_OBJ_COFF && kit_obj_kind(f) != KIT_OBJ_KIND_REL) {
   1258     dump_pe_private(f, label);
   1259     return;
   1260   }
   1261 
   1262   if (kit_obj_kind(f) == KIT_OBJ_KIND_REL) {
   1263     driver_printf(
   1264         "Private headers:\n"
   1265         "  relocatable object — no program or dynamic headers\n\n");
   1266     return;
   1267   }
   1268 
   1269   have_info = kit_obj_image_info(f, &info) == KIT_OK;
   1270   if (have_info) {
   1271     driver_printf("Image:\n");
   1272     driver_printf("  entry point  0x%016llx\n", (unsigned long long)info.entry);
   1273     driver_printf("  image base   0x%016llx\n",
   1274                   (unsigned long long)info.image_base);
   1275     if (info.interp.len)
   1276       driver_printf("  interpreter  %.*s\n", KIT_SLICE_ARG(info.interp));
   1277     driver_printf("\n");
   1278   }
   1279 
   1280   driver_printf("Program Header:\n");
   1281   if (kit_obj_segiter_new(f, &sit) == KIT_OK) {
   1282     int any = 0;
   1283     while (kit_obj_segiter_next(sit, &seg) == KIT_ITER_ITEM) {
   1284       any = 1;
   1285       driver_printf(
   1286           "  %-12.*s off 0x%016llx vaddr 0x%016llx align 2**%u\n"
   1287           "               filesz 0x%016llx memsz 0x%016llx flags %s\n",
   1288           KIT_SLICE_ARG(seg.name.len ? seg.name : KIT_SLICE_LIT("LOAD")),
   1289           (unsigned long long)seg.file_off, (unsigned long long)seg.vaddr,
   1290           u32_log2(seg.align), (unsigned long long)seg.file_size,
   1291           (unsigned long long)seg.vsize, seg_perms_str(seg.perms));
   1292     }
   1293     kit_obj_segiter_free(sit);
   1294     if (!any) driver_printf("  (none)\n");
   1295   }
   1296   driver_printf("\n");
   1297 
   1298   driver_printf("Dynamic Section:\n");
   1299   if (have_info && info.soname.len)
   1300     driver_printf("  SONAME       %.*s\n", KIT_SLICE_ARG(info.soname));
   1301   if (kit_obj_depiter_new(f, &dit) == KIT_OK) {
   1302     while (kit_obj_depiter_next(dit, &dep) == KIT_ITER_ITEM) {
   1303       uint32_t k;
   1304       driver_printf("  NEEDED       %.*s\n", KIT_SLICE_ARG(dep.name));
   1305       for (k = 0; k < dep.nimports; ++k)
   1306         driver_printf("                 %.*s\n", KIT_SLICE_ARG(dep.imports[k]));
   1307     }
   1308     kit_obj_depiter_free(dit);
   1309   }
   1310   {
   1311     KitObjRpathIter* rit = NULL;
   1312     KitSlice rpath;
   1313     if (kit_obj_rpathiter_new(f, &rit) == KIT_OK) {
   1314       while (kit_obj_rpathiter_next(rit, &rpath) == KIT_ITER_ITEM)
   1315         driver_printf("  RPATH        %.*s\n", KIT_SLICE_ARG(rpath));
   1316       kit_obj_rpathiter_free(rit);
   1317     }
   1318   }
   1319   driver_printf("\n");
   1320 }
   1321 
   1322 static void dump_obj(const KitContext* ctx, const KitDisasmContext* dctx,
   1323                      const char* label, KitObjFile* f, const ObjdumpOpts* opts,
   1324                      const KitSlice* image) {
   1325   KitTargetSpec target = kit_obj_target(f);
   1326   KitObjFmt fmt = kit_obj_fmt(f);
   1327   char fmt_buf[FMT_STR_CAP];
   1328 
   1329   driver_printf(
   1330       "%.*s:\tfile format %.*s-%.*s\n\n",
   1331       KIT_SLICE_ARG(kit_slice_cstr(label)),
   1332       KIT_SLICE_ARG(
   1333           kit_slice_cstr(fmt_str(fmt, target.ptr_size, fmt_buf, sizeof fmt_buf))),
   1334       KIT_SLICE_ARG(kit_slice_cstr(arch_str(target.arch))));
   1335 
   1336   if (opts->f) dump_file_header(f, label);
   1337   if (opts->h) dump_sections(f, opts);
   1338   if (opts->h) dump_groups(f, opts);
   1339   if (opts->t) dump_symbols(f, opts, 0);
   1340   if (opts->T) dump_symbols(f, opts, 1);
   1341   if (opts->p) dump_private(f, label);
   1342   if (opts->s) dump_hex(f, opts);
   1343   if (opts->d || opts->D) dump_disasm(dctx, f, opts, image);
   1344   if (opts->r) dump_relocs(f, opts);
   1345   if (opts->R) dump_dynrelocs(f);
   1346   if (opts->dwarf) dump_dwarf(ctx, f, opts);
   1347 }
   1348 
   1349 static int dump_archive(const char* path, const KitSlice* input,
   1350                         const KitContext* ctx, const KitDisasmContext* dctx,
   1351                         const ObjdumpOpts* opts) {
   1352   KitArIter* it = NULL;
   1353   KitArMember member;
   1354   char label[256];
   1355   int j;
   1356 
   1357   driver_printf("In archive %.*s:\n\n", KIT_SLICE_ARG(kit_slice_cstr(path)));
   1358 
   1359   if (kit_ar_iter_new(ctx, input, &it) != KIT_OK) return 1;
   1360   for (;;) {
   1361     KitIterResult r = kit_ar_iter_next(it, &member);
   1362     KitSlice min;
   1363     KitObjFile* f = NULL;
   1364     if (r != KIT_ITER_ITEM) break;
   1365 
   1366     /* Build "archive.a(member.o)" label. */
   1367     j = 0;
   1368     {
   1369       const char* p = path;
   1370       while (*p && j < 230) label[j++] = *p++;
   1371     }
   1372     label[j++] = '(';
   1373     {
   1374       size_t k = 0;
   1375       while (k < member.name.len && j < 252) label[j++] = member.name.s[k++];
   1376     }
   1377     label[j++] = ')';
   1378     label[j] = '\0';
   1379 
   1380     min.data = member.data;
   1381     min.len = member.size;
   1382 
   1383     if (kit_obj_open(ctx, member.name, &min, &f) != KIT_OK) {
   1384       driver_errf(OBJDUMP_TOOL, "failed to parse member: %.*s",
   1385                   KIT_SLICE_ARG(kit_slice_cstr(label)));
   1386       continue;
   1387     }
   1388     dump_obj(ctx, dctx, label, f, opts, &min);
   1389     kit_obj_free(f);
   1390   }
   1391   kit_ar_iter_free(it);
   1392 
   1393   return 0;
   1394 }
   1395 
   1396 static int parse_short_flags(const char* arg, ObjdumpOpts* o) {
   1397   const char* p;
   1398   for (p = arg + 1; *p; ++p) {
   1399     switch (*p) {
   1400       case 'f':
   1401         o->f = 1;
   1402         break;
   1403       case 'h':
   1404         o->h = 1;
   1405         break;
   1406       case 't':
   1407         o->t = 1;
   1408         break;
   1409       case 'd':
   1410         o->d = 1;
   1411         break;
   1412       case 'D':
   1413         o->D = 1;
   1414         break;
   1415       case 'r':
   1416         o->r = 1;
   1417         break;
   1418       case 's':
   1419         o->s = 1;
   1420         break;
   1421       case 'p':
   1422         o->p = 1;
   1423         break;
   1424       case 'T':
   1425         o->T = 1;
   1426         break;
   1427       case 'R':
   1428         o->R = 1;
   1429         break;
   1430       case 'x':
   1431         o->f = 1;
   1432         o->h = 1;
   1433         o->r = 1;
   1434         o->t = 1;
   1435         break;
   1436       default:
   1437         driver_errf(OBJDUMP_TOOL, "unknown flag: -%c", *p);
   1438         return -1;
   1439     }
   1440   }
   1441   return 0;
   1442 }
   1443 
   1444 static int parse_long_flag(const char* arg, ObjdumpOpts* o) {
   1445   if (driver_streq(arg, "--file-headers")) {
   1446     o->f = 1;
   1447     return 1;
   1448   }
   1449   if (driver_streq(arg, "--section-headers")) {
   1450     o->h = 1;
   1451     return 1;
   1452   }
   1453   if (driver_streq(arg, "--syms")) {
   1454     o->t = 1;
   1455     return 1;
   1456   }
   1457   if (driver_streq(arg, "--reloc")) {
   1458     o->r = 1;
   1459     return 1;
   1460   }
   1461   if (driver_streq(arg, "--full-contents")) {
   1462     o->s = 1;
   1463     return 1;
   1464   }
   1465   if (driver_streq(arg, "--disassemble")) {
   1466     o->d = 1;
   1467     return 1;
   1468   }
   1469   if (driver_streq(arg, "--all-headers")) {
   1470     o->f = 1;
   1471     o->h = 1;
   1472     o->r = 1;
   1473     o->t = 1;
   1474     return 1;
   1475   }
   1476   if (driver_streq(arg, "--private-headers")) {
   1477     o->p = 1;
   1478     return 1;
   1479   }
   1480   if (driver_streq(arg, "--dynamic-syms")) {
   1481     o->T = 1;
   1482     return 1;
   1483   }
   1484   if (driver_streq(arg, "--dynamic-reloc")) {
   1485     o->R = 1;
   1486     return 1;
   1487   }
   1488   return 0;
   1489 }
   1490 
   1491 /* Match one comma-separated --dwarf section selector against `tok` of
   1492  * length `n`. Returns the OBJDUMP_DWARF_* bit, or 0 if unrecognized. */
   1493 static unsigned dwarf_sel_bit(const char* tok, size_t n) {
   1494   if (n == 4 && memcmp(tok, "info", 4) == 0) return OBJDUMP_DWARF_INFO;
   1495   if (n == 6 && memcmp(tok, "abbrev", 6) == 0) return OBJDUMP_DWARF_ABBREV;
   1496   if (n == 4 && memcmp(tok, "line", 4) == 0) return OBJDUMP_DWARF_LINE;
   1497   if (n == 3 && memcmp(tok, "str", 3) == 0) return OBJDUMP_DWARF_STR;
   1498   return 0;
   1499 }
   1500 
   1501 /* Parse a `--dwarf` / `--dwarf=sec,sec` argument into o->dwarf. Returns 1
   1502  * if `arg` was a dwarf flag (handled), 0 if not, -1 on a bad selector. */
   1503 static int parse_dwarf_flag(const char* arg, ObjdumpOpts* o) {
   1504   const char *list, *p;
   1505   if (driver_streq(arg, "--dwarf")) {
   1506     o->dwarf = OBJDUMP_DWARF_ALL;
   1507     return 1;
   1508   }
   1509   if (strncmp(arg, "--dwarf=", 8) != 0) return 0;
   1510   list = arg + 8;
   1511   for (p = list; *p;) {
   1512     const char* start = p;
   1513     unsigned bit;
   1514     while (*p && *p != ',') ++p;
   1515     bit = dwarf_sel_bit(start, (size_t)(p - start));
   1516     if (!bit) return -1;
   1517     o->dwarf |= bit;
   1518     if (*p == ',') ++p;
   1519   }
   1520   if (!o->dwarf) return -1;
   1521   return 1;
   1522 }
   1523 
   1524 int driver_objdump(int argc, char** argv) {
   1525   DriverEnv env;
   1526   ObjdumpOpts opts = {0};
   1527   int i;
   1528   int rc = 0;
   1529   int saw_input = 0;
   1530   int saw_op = 0;
   1531   KitContext ctx;
   1532   KitDisasmContext dctx;
   1533   KitDisasmContext* dctx_p = NULL;
   1534 
   1535   /* For objdump, -h means "section headers" (GNU objdump convention),
   1536    * so we only honour --help / -help (and bare invocation) as help. */
   1537   if (argc < 2 || driver_argv_wants_help(argc, argv, 0)) {
   1538     driver_help_objdump();
   1539     return 0;
   1540   }
   1541 
   1542   driver_env_init(&env);
   1543   ctx = driver_env_to_context(&env);
   1544 
   1545   /* First pass: parse flags. */
   1546   for (i = 1; i < argc; ++i) {
   1547     const char* a = argv[i];
   1548     if (a[0] != '-' || a[1] == '\0') continue;
   1549     if (driver_streq(a, "-j")) {
   1550       if (i + 1 >= argc) {
   1551         driver_errf(OBJDUMP_TOOL, "%.*s",
   1552                     KIT_SLICE_ARG(KIT_SLICE_LIT("-j requires a section name")));
   1553         rc = 2;
   1554         goto done;
   1555       }
   1556       if (opts.nj >= MAX_J_FILTERS) {
   1557         driver_errf(OBJDUMP_TOOL, "%.*s",
   1558                     KIT_SLICE_ARG(KIT_SLICE_LIT("too many -j filters")));
   1559         rc = 2;
   1560         goto done;
   1561       }
   1562       opts.j[opts.nj++] = argv[++i];
   1563       continue;
   1564     }
   1565     {
   1566       int dr = parse_dwarf_flag(a, &opts);
   1567       if (dr < 0) {
   1568         driver_errf(OBJDUMP_TOOL, "%.*s",
   1569                     KIT_SLICE_ARG(KIT_SLICE_LIT(
   1570                         "--dwarf sections: info, abbrev, line, str")));
   1571         rc = 2;
   1572         goto done;
   1573       }
   1574       if (dr > 0) continue;
   1575     }
   1576     if (parse_long_flag(a, &opts)) continue;
   1577     if (parse_short_flags(a, &opts) != 0) {
   1578       objdump_usage();
   1579       rc = 2;
   1580       goto done;
   1581     }
   1582   }
   1583 
   1584   saw_op = opts.f || opts.h || opts.t || opts.d || opts.D || opts.r || opts.s ||
   1585            opts.p || opts.T || opts.R || opts.dwarf != 0;
   1586   if (!saw_op) { /* Default = -h -t (matches the prior behavior). */
   1587     opts.h = 1;
   1588     opts.t = 1;
   1589   }
   1590 
   1591   /* Disassembler context: a target + ctx value pair. Disasm consults
   1592    * the per-file object reader for annotation. */
   1593   if (opts.d || opts.D) {
   1594     dctx.target = NULL;
   1595     dctx.context = ctx;
   1596     dctx_p = &dctx;
   1597   }
   1598 
   1599   /* Second pass: process inputs. */
   1600   for (i = 1; i < argc && rc == 0; ++i) {
   1601     const char* a = argv[i];
   1602     KitFileData fd = {0};
   1603     KitSlice input;
   1604     KitBinFmt bin;
   1605 
   1606     if (a[0] == '-' && a[1] != '\0') {
   1607       if (driver_streq(a, "-j")) ++i;
   1608       continue;
   1609     }
   1610     saw_input = 1;
   1611 
   1612     if (ctx.file_io->read_all(ctx.file_io->user, a, &fd) != KIT_OK) {
   1613       driver_errf(OBJDUMP_TOOL, "failed to read: %.*s",
   1614                   KIT_SLICE_ARG(kit_slice_cstr(a)));
   1615       rc = 1;
   1616       break;
   1617     }
   1618 
   1619     input.data = fd.data;
   1620     input.len = fd.size;
   1621 
   1622     bin = kit_detect_fmt(input.data, input.len);
   1623     switch (bin) {
   1624       case KIT_BIN_AR:
   1625         rc = dump_archive(a, &input, &ctx, dctx_p, &opts);
   1626         break;
   1627       case KIT_BIN_ELF:
   1628       case KIT_BIN_COFF:
   1629       case KIT_BIN_PE:
   1630       case KIT_BIN_MACHO:
   1631       case KIT_BIN_WASM: {
   1632         KitObjFile* f = NULL;
   1633         /* PE executables / DLLs open through kit_obj_open like every other
   1634          * format (read_coff dispatches the DOS 'MZ' magic to the image
   1635          * reader); the whole dump flows through the neutral dump_obj path. */
   1636         if (kit_obj_open(&ctx, kit_slice_cstr(a), &input, &f) != KIT_OK) {
   1637           driver_errf(OBJDUMP_TOOL, "failed to parse: %.*s",
   1638                       KIT_SLICE_ARG(kit_slice_cstr(a)));
   1639           rc = 1;
   1640         } else {
   1641           dump_obj(&ctx, dctx_p, a, f, &opts, &input);
   1642           kit_obj_free(f);
   1643         }
   1644         break;
   1645       }
   1646       case KIT_BIN_UNKNOWN:
   1647       default:
   1648         driver_errf(OBJDUMP_TOOL, "unsupported file format: %.*s",
   1649                     KIT_SLICE_ARG(kit_slice_cstr(a)));
   1650         rc = 1;
   1651         break;
   1652     }
   1653 
   1654     ctx.file_io->release(ctx.file_io->user, &fd);
   1655   }
   1656 
   1657   if (rc == 0 && !saw_input) {
   1658     objdump_usage();
   1659     rc = 2;
   1660   }
   1661 
   1662 done:
   1663   driver_env_fini(&env);
   1664   return rc;
   1665 }