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 }