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