authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-05 01:55:36-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:26:56-04:00
log9df2ca0d3382170a2d616d110073e7be25793778
tree706a2759138d92278273568fa43b982c537ca604
parent224d7e7999fbe81c17abf7a154260b1a63675f63

objdump: more coff progress

- Linker member selection - Dump implib headers - Dump relocs - Add more info to table headers for easier grepping

2 files changed, 238 insertions(+), 64 deletions(-)

lib/compiler/objdump.zig+208-64
...@@ -10,10 +10,13 @@ const native_endian = builtin.cpu.arch.endian();...@@ -10,10 +10,13 @@ const native_endian = builtin.cpu.arch.endian();
10var stdout_buffer: [4000]u8 = undefined;10var stdout_buffer: [4000]u8 = undefined;
1111
12const Options = struct {12const Options = struct {
13 input_path: []const u8,13 exports: bool,
14 file_headers: bool,14 file_headers: bool,
15 imports: bool,
16 input_path: []const u8,
15 member_filters: []const []const u8 = &.{},17 member_filters: []const []const u8 = &.{},
16 member_headers: bool,18 member_headers: bool,
19 relocs: bool,
17 section_filters: []const []const u8 = &.{},20 section_filters: []const []const u8 = &.{},
18 section_headers: bool,21 section_headers: bool,
19 strings: bool,22 strings: bool,
...@@ -30,14 +33,16 @@ pub fn main(init: std.process.Init) !void {...@@ -30,14 +33,16 @@ pub fn main(init: std.process.Init) !void {
3033
31 var i: usize = 1;34 var i: usize = 1;
3235
33 var opt_input_path: ?[]const u8 = null;36 var opt_exports: ?bool = null;
34 var opt_file_headers: ?bool = null;37 var opt_file_headers: ?bool = null;
38 var opt_imports: ?bool = null;
39 var opt_input_path: ?[]const u8 = null;
35 var opt_linker_member: ?std.coff.ArchiveMemberHeader.Kind = null;40 var opt_linker_member: ?std.coff.ArchiveMemberHeader.Kind = null;
36 var opt_member_headers: ?bool = null;41 var opt_member_headers: ?bool = null;
42 var opt_relocs: ?bool = null;
37 var opt_section_headers: ?bool = null;43 var opt_section_headers: ?bool = null;
38 var opt_strings: ?bool = null;44 var opt_strings: ?bool = null;
39 var opt_symbols: ?bool = null;45 var opt_symbols: ?bool = null;
40 var opt_relocs: ?bool = null;
41 var section_filters: std.ArrayList([]const u8) = .empty;46 var section_filters: std.ArrayList([]const u8) = .empty;
42 var member_filters: std.ArrayList([]const u8) = .empty;47 var member_filters: std.ArrayList([]const u8) = .empty;
43 while (i < args.len) : (i += 1) {48 while (i < args.len) : (i += 1) {
...@@ -51,11 +56,17 @@ pub fn main(init: std.process.Init) !void {...@@ -51,11 +56,17 @@ pub fn main(init: std.process.Init) !void {
51 opt_section_headers = true;56 opt_section_headers = true;
52 opt_symbols = true;57 opt_symbols = true;
53 opt_relocs = true;58 opt_relocs = true;
59 } else if (mem.eql(u8, arg, "--exports")) {
60 opt_exports = true;
54 } else if (mem.eql(u8, arg, "--file-headers")) {61 } else if (mem.eql(u8, arg, "--file-headers")) {
55 opt_file_headers = true;62 opt_file_headers = true;
63 } else if (mem.eql(u8, arg, "--imports")) {
64 opt_imports = true;
56 } else if (mem.startsWith(u8, arg, "--linker-member")) {65 } else if (mem.startsWith(u8, arg, "--linker-member")) {
57 if (mem.eql(u8, arg["--linker-member".len..], "=1"))66 if (mem.eql(u8, arg["--linker-member".len..], "=1"))
58 opt_linker_member = .first_linker67 opt_linker_member = .first_linker
68 else if (mem.eql(u8, arg["--linker-member".len..], "=longnames"))
69 opt_linker_member = .longnames
59 else70 else
60 opt_linker_member = .second_linker;71 opt_linker_member = .second_linker;
61 } else if (mem.eql(u8, arg, "--member-headers")) {72 } else if (mem.eql(u8, arg, "--member-headers")) {
...@@ -84,9 +95,12 @@ pub fn main(init: std.process.Init) !void {...@@ -84,9 +95,12 @@ pub fn main(init: std.process.Init) !void {
8495
85 const opts: Options = .{96 const opts: Options = .{
86 .input_path = opt_input_path orelse fatal("missing input file path positional argument", .{}),97 .input_path = opt_input_path orelse fatal("missing input file path positional argument", .{}),
98 .exports = opt_exports orelse false,
87 .file_headers = opt_file_headers orelse false,99 .file_headers = opt_file_headers orelse false,
100 .imports = opt_imports orelse false,
88 .section_filters = section_filters.items,101 .section_filters = section_filters.items,
89 .section_headers = opt_section_headers orelse false,102 .section_headers = opt_section_headers orelse false,
103 .relocs = opt_relocs orelse false,
90 .strings = opt_strings orelse false,104 .strings = opt_strings orelse false,
91 .symbols = opt_symbols orelse false,105 .symbols = opt_symbols orelse false,
92 .member_filters = member_filters.items,106 .member_filters = member_filters.items,
...@@ -129,6 +143,7 @@ fn dump(gpa: std.mem.Allocator, opts: *const Options, fr: *Io.File.Reader, w: *I...@@ -129,6 +143,7 @@ fn dump(gpa: std.mem.Allocator, opts: *const Options, fr: *Io.File.Reader, w: *I
129 }143 }
130 coff: {144 coff: {
131 const ext = std.fs.path.extension(opts.input_path);145 const ext = std.fs.path.extension(opts.input_path);
146 const basename = std.fs.path.basename(opts.input_path);
132 if (std.mem.eql(u8, ext, ".exe") or std.mem.eql(u8, ext, ".dll")) {147 if (std.mem.eql(u8, ext, ".exe") or std.mem.eql(u8, ext, ".dll")) {
133 if (!mem.eql(u8, r.buffered()[0..2], "MZ")) break :coff;148 if (!mem.eql(u8, r.buffered()[0..2], "MZ")) break :coff;
134 try r.discardAll(std.coff.pe_pointer_offset);149 try r.discardAll(std.coff.pe_pointer_offset);
...@@ -141,16 +156,16 @@ fn dump(gpa: std.mem.Allocator, opts: *const Options, fr: *Io.File.Reader, w: *I...@@ -141,16 +156,16 @@ fn dump(gpa: std.mem.Allocator, opts: *const Options, fr: *Io.File.Reader, w: *I
141 return error.ParseFailure;156 return error.ParseFailure;
142 }157 }
143158
144 try w.print("{s}: PE/COFF image\n\n", .{std.fs.path.basename(opts.input_path)});159 try w.print("{s}: PE/COFF image\n\n", .{basename});
145 return coff.dumpObject(gpa, opts, true, fr, w);160 return coff.dumpObject(gpa, opts, true, basename, fr, w);
146 } else if (std.mem.eql(u8, ext, ".lib")) {161 } else if (std.mem.eql(u8, ext, ".lib")) {
147 r.fill(std.coff.archive_signature.len) catch break :coff;162 r.fill(std.coff.archive_signature.len) catch break :coff;
148 if (!mem.eql(u8, r.buffered()[0..std.coff.archive_signature.len], std.coff.archive_signature)) break :coff;163 if (!mem.eql(u8, r.buffered()[0..std.coff.archive_signature.len], std.coff.archive_signature)) break :coff;
149 try w.print("{s}: COFF archive\n\n", .{std.fs.path.basename(opts.input_path)});164 try w.print("{s}: COFF archive\n\n", .{basename});
150 return coff.dumpArchive(gpa, opts, fr, w);165 return coff.dumpArchive(gpa, opts, fr, w);
151 } else if (std.mem.eql(u8, ext, ".obj")) {166 } else if (std.mem.eql(u8, ext, ".obj")) {
152 try w.print("{s}: COFF object\n\n", .{std.fs.path.basename(opts.input_path)});167 try w.print("{s}: COFF object\n\n", .{basename});
153 return coff.dumpObject(gpa, opts, false, fr, w);168 return coff.dumpObject(gpa, opts, false, basename, fr, w);
154 }169 }
155 }170 }
156 return error.UnknownFile;171 return error.UnknownFile;
...@@ -242,7 +257,10 @@ const coff = struct {...@@ -242,7 +257,10 @@ const coff = struct {
242 if (!std.mem.eql(u8, &raw_header.end_of_header, std.coff.archive_end_of_header))257 if (!std.mem.eql(u8, &raw_header.end_of_header, std.coff.archive_end_of_header))
243 return failParse(opts, "malformed end-of-header field in member '{s}': {x}", .{ header.name, raw_header.end_of_header });258 return failParse(opts, "malformed end-of-header field in member '{s}': {x}", .{ header.name, raw_header.end_of_header });
244259
245 const dump_header = opts.member_headers and filterMatches(opts.member_filters, header.name);260 const dump_header =
261 (opts.member_headers and filterMatches(opts.member_filters, header.name)) or
262 (opts.linker_member == opt_expected_kind);
263
246 if (dump_header)264 if (dump_header)
247 try dumpArchiveHeader(w, &header, @intCast(pos));265 try dumpArchiveHeader(w, &header, @intCast(pos));
248266
...@@ -264,11 +282,12 @@ const coff = struct {...@@ -264,11 +282,12 @@ const coff = struct {
264 , .{ expected_kind, num_symbols });282 , .{ expected_kind, num_symbols });
265283
266 if (opts.linker_member == .first_linker) {284 if (opts.linker_member == .first_linker) {
267 try w.print(285 try w.writeAll(
268 \\Archives symbols ({d}):286 \\
287 \\Archive symbols:
269 \\& Member Symbol288 \\& Member Symbol
270 \\289 \\
271 , .{num_symbols});290 );
272291
273 const offsets = try r.readAlloc(gpa, num_symbols * 4);292 const offsets = try r.readAlloc(gpa, num_symbols * 4);
274 defer gpa.free(offsets);293 defer gpa.free(offsets);
...@@ -320,11 +339,12 @@ const coff = struct {...@@ -320,11 +339,12 @@ const coff = struct {
320 symbol_member_indices.addOneAssumeCapacity().* = (try r.takeInt(u16, .little)) - 1;339 symbol_member_indices.addOneAssumeCapacity().* = (try r.takeInt(u16, .little)) - 1;
321340
322 if (opts.linker_member == .second_linker) {341 if (opts.linker_member == .second_linker) {
323 try w.print(342 try w.writeAll(
324 \\Archive symbols ({d} members, {d} symbols):343 \\
344 \\Archive Symbols:
325 \\& Member Symbol345 \\& Member Symbol
326 \\346 \\
327 , .{ num_members, num_symbols });347 );
328348
329 pos = fr.logicalPos();349 pos = fr.logicalPos();
330 var symbol_i: u32 = 0;350 var symbol_i: u32 = 0;
...@@ -362,6 +382,21 @@ const coff = struct {...@@ -362,6 +382,21 @@ const coff = struct {
362 opt_longnames = try r.readAlloc(gpa, header.size);382 opt_longnames = try r.readAlloc(gpa, header.size);
363 if (dump_header)383 if (dump_header)
364 try w.print("{t: >16} type\n", .{expected_kind});384 try w.print("{t: >16} type\n", .{expected_kind});
385
386 if (opts.linker_member == .longnames) {
387 try w.print(
388 \\
389 \\Longnames (0x{x} bytes):
390 \\
391 , .{opt_longnames.?.len});
392
393 var lr = Io.Reader.fixed(opt_longnames.?);
394 while (try lr.takeDelimiter(0)) |str| {
395 try w.writeAll(str);
396 try w.writeByte('\n');
397 }
398 try w.writeByte('\n');
399 }
365 }400 }
366401
367 opt_expected_kind = null;402 opt_expected_kind = null;
...@@ -390,13 +425,50 @@ const coff = struct {...@@ -390,13 +425,50 @@ const coff = struct {
390 @enumFromInt(std.mem.readInt(u16, member_sig[0..2], .little));425 @enumFromInt(std.mem.readInt(u16, member_sig[0..2], .little));
391 const sig = std.mem.readInt(u16, member_sig[2..4], .little);426 const sig = std.mem.readInt(u16, member_sig[2..4], .little);
392427
393 const is_import = machine == std.coff.IMAGE.FILE.MACHINE.UNKNOWN and sig == 0xffff;428 const is_imp_lib = machine == std.coff.IMAGE.FILE.MACHINE.UNKNOWN and sig == 0xffff;
394429
395 if (opts.member_headers) {430 if (opts.member_headers or (opts.exports and is_imp_lib)) {
396 try dumpArchiveHeader(w, &header, member.offset);431 try dumpArchiveHeader(w, &header, member.offset);
397 if (is_import) {432 if (is_imp_lib) {
398 try w.writeAll(" Import header type\n");433 try w.writeAll("\nImport header:\n");
399 // TODO: Dump import header434
435 const imp_header = try r.takeStruct(std.coff.ImportHeader, .little);
436 try dumpHeader(w, std.coff.ImportHeader, &imp_header, struct {
437 pub fn sig1(_: *const std.coff.ImportHeader, _: *Io.Writer) !void {}
438 pub fn sig2(_: *const std.coff.ImportHeader, _: *Io.Writer) !void {}
439 pub fn types(h: *const std.coff.ImportHeader, cw: *Io.Writer) !void {
440 try cw.print(
441 \\{t: >16} import_type
442 \\{t: >16} name_type
443 \\
444 , .{ h.types.type, h.types.name_type });
445 }
446 });
447
448 const sym_name = (try r.takeDelimiter(0)).?;
449 const imp_dll = (try r.takeDelimiter(0)).?;
450 const imp_name = imp_name: switch (imp_header.types.name_type) {
451 .NAME_NOPREFIX,
452 .NAME_UNDECORATE,
453 => |tag| {
454 var imp_name = std.mem.trimStart(u8, sym_name, "?@_");
455 if (tag == .NAME_UNDECORATE)
456 imp_name = std.mem.sliceTo(imp_name, '@');
457 break :imp_name imp_name;
458 },
459 else => sym_name,
460 };
461
462 try w.print(
463 \\ symbol name | {s}
464 \\ import name | {s}
465 \\ dll | {s}
466 \\
467 , .{
468 sym_name,
469 imp_name,
470 imp_dll,
471 });
400 } else {472 } else {
401 try w.writeAll(" COFF object type\n");473 try w.writeAll(" COFF object type\n");
402 }474 }
...@@ -409,7 +481,7 @@ const coff = struct {...@@ -409,7 +481,7 @@ const coff = struct {
409 opts.symbols)481 opts.symbols)
410 {482 {
411 try w.print("{s}({s}): COFF object\n\n", .{ std.fs.path.basename(opts.input_path), header.name });483 try w.print("{s}({s}): COFF object\n\n", .{ std.fs.path.basename(opts.input_path), header.name });
412 try dumpObject(gpa, opts, false, fr, w);484 try dumpObject(gpa, opts, false, header.name, fr, w);
413 }485 }
414 }486 }
415 }487 }
...@@ -418,6 +490,7 @@ const coff = struct {...@@ -418,6 +490,7 @@ const coff = struct {
418 gpa: std.mem.Allocator,490 gpa: std.mem.Allocator,
419 opts: *const Options,491 opts: *const Options,
420 is_image: bool,492 is_image: bool,
493 obj_name: []const u8,
421 fr: *Io.File.Reader,494 fr: *Io.File.Reader,
422 w: *Io.Writer,495 w: *Io.Writer,
423 ) !void {496 ) !void {
...@@ -432,6 +505,11 @@ const coff = struct {...@@ -432,6 +505,11 @@ const coff = struct {
432 try w.writeByte('\n');505 try w.writeByte('\n');
433 }506 }
434507
508 switch (header.machine) {
509 _ => return failParse(opts, "unknown machine type: {x}", .{header.machine}),
510 else => {},
511 }
512
435 if (header.size_of_optional_header > 0) opt_header: {513 if (header.size_of_optional_header > 0) opt_header: {
436 if (!opts.file_headers) {514 if (!opts.file_headers) {
437 try fr.seekBy(header.size_of_optional_header);515 try fr.seekBy(header.size_of_optional_header);
...@@ -541,59 +619,65 @@ const coff = struct {...@@ -541,59 +619,65 @@ const coff = struct {
541 try w.writeByte('\n');619 try w.writeByte('\n');
542 }620 }
543621
544 var sections: std.ArrayList(std.coff.SectionHeader) = .empty;622 var sections: std.ArrayList(struct {
623 header: std.coff.SectionHeader,
624 name: []const u8,
625 }) = .empty;
545 defer sections.deinit(gpa);626 defer sections.deinit(gpa);
546627
547 const load_sections = opts.section_headers or opts.symbols;628 const load_sections =
629 opts.section_headers or
630 opts.symbols or
631 opts.relocs;
632
548 if (load_sections) {633 if (load_sections) {
549 if (opts.section_headers)634 if (opts.section_headers)
550 try w.writeAll(635 try w.print(
551 \\Section Table:636 \\Sections ({s}):
552 \\Num Name RVA Virt Size Data Size & Data & Relocs & Lines # Relocs # Lines Flags637 \\Num Name RVA Virt Size Data Size & Data & Relocs & Lines # Relocs # Lines Flags
553 \\638 \\
554 );639 , .{obj_name});
555640
556 try sections.resize(gpa, header.number_of_sections);641 try sections.resize(gpa, header.number_of_sections);
557 for (sections.items, 0..) |*section, section_i| {642 for (sections.items, 0..) |*section, section_i| {
558 section.* = r.takeStruct(std.coff.SectionHeader, .little) catch |err|643 section.header = r.takeStruct(std.coff.SectionHeader, .little) catch |err|
559 return failParse(opts, "unable to read section header {x}: {t}", .{ section_i, err });644 return failParse(opts, "unable to read section header {x}: {t}", .{ section_i, err });
645 section.name = headerName(&section.header.name, string_table) catch |err| switch (err) {
646 error.Overflow,
647 error.InvalidCharacter,
648 => return failParse(opts, "unable to parse section name offset '{s}': {t}", .{
649 section.name,
650 err,
651 }),
652 error.OutOfBounds => return failParse(opts, "section name offset '{s}' was out of bounds (>= {x})", .{
653 section.name,
654 string_table.len,
655 }),
656 };
560657
561 if (opts.section_headers) {658 if (opts.section_headers) {
562 const name = headerName(&section.name, string_table) catch |err| switch (err) {659 if (!filterMatches(opts.section_filters, section.name)) continue;
563 error.Overflow,660 const raw_name = std.mem.sliceTo(&section.header.name, 0);
564 error.InvalidCharacter,
565 => return failParse(opts, "unable to parse section name offset '{s}': {t}", .{
566 section.name,
567 err,
568 }),
569 error.OutOfBounds => return failParse(opts, "section name offset '{s}' was out of bounds (>= {x})", .{
570 section.name,
571 string_table.len,
572 }),
573 };
574
575 if (!filterMatches(opts.section_filters, name)) continue;
576 const raw_name = std.mem.sliceTo(&section.name, 0);
577 try w.print(661 try w.print(
578 "{x: >3} {s: <8} {x: >8} {x: >9} {x: >9} {x: >8} {x: >8} {x: >8} {x: >8} {x: >8} {x:0>8} | ",662 "{x: >3} {s: <8} {x: >8} {x: >9} {x: >9} {x: >8} {x: >8} {x: >8} {x: >8} {x: >8} {x:0>8} | ",
579 .{663 .{
580 section_i + 1,664 section_i + 1,
581 raw_name,665 raw_name,
582 section.virtual_address,666 section.header.virtual_address,
583 section.virtual_size,667 section.header.virtual_size,
584 section.size_of_raw_data,668 section.header.size_of_raw_data,
585 section.pointer_to_raw_data,669 section.header.pointer_to_raw_data,
586 section.pointer_to_relocations,670 section.header.pointer_to_relocations,
587 section.pointer_to_linenumbers,671 section.header.pointer_to_linenumbers,
588 section.number_of_relocations,672 section.header.number_of_relocations,
589 section.number_of_linenumbers,673 section.header.number_of_linenumbers,
590 @as(u32, @bitCast(section.flags)),674 @as(u32, @bitCast(section.header.flags)),
591 },675 },
592 );676 );
593677
594 try dumpFlags(w, "{s} ", std.coff.SectionHeader.Flags, &section.flags, 0);678 try dumpFlags(w, "{s} ", std.coff.SectionHeader.Flags, &section.header.flags, 1);
595 if (name.len > 8)679 if (section.name.len > 8)
596 try w.print("| {s}", .{name});680 try w.print("| {s}", .{section.name});
597681
598 try w.writeByte('\n');682 try w.writeByte('\n');
599 }683 }
...@@ -602,16 +686,21 @@ const coff = struct {...@@ -602,16 +686,21 @@ const coff = struct {
602 if (opts.section_headers) try w.writeByte('\n');686 if (opts.section_headers) try w.writeByte('\n');
603 }687 }
604688
689 var symbol_names: std.ArrayList([]const u8) = .empty;
690 defer symbol_names.deinit(gpa);
691 if (opts.relocs)
692 try symbol_names.ensureUnusedCapacity(gpa, header.number_of_symbols);
693
605 if (opts.symbols) {694 if (opts.symbols) {
606 if (header.pointer_to_symbol_table > 0) {695 if (header.pointer_to_symbol_table > 0) {
607 fr.seekTo(file_location + header.pointer_to_symbol_table) catch |err|696 fr.seekTo(file_location + header.pointer_to_symbol_table) catch |err|
608 return failParse(opts, "unable to seek to symbol table: {t}", .{err});697 return failParse(opts, "unable to seek to symbol table: {t}", .{err});
609698
610 try w.writeAll(699 try w.print(
611 \\Symbol Table:700 \\Symbols ({s}):
612 \\ Ord Value Sect Type Storage Name701 \\ Ord Value Sect Type Storage Name
613 \\702 \\
614 );703 , .{obj_name});
615704
616 const symbol_size = std.coff.Symbol.sizeOf();705 const symbol_size = std.coff.Symbol.sizeOf();
617 var symbol_i: u32 = 0;706 var symbol_i: u32 = 0;
...@@ -633,10 +722,17 @@ const coff = struct {...@@ -633,10 +722,17 @@ const coff = struct {
633 const name = std.mem.sliceTo(if (std.mem.eql(u8, symbol.name[0..4], "\x00\x00\x00\x00")) name: {722 const name = std.mem.sliceTo(if (std.mem.eql(u8, symbol.name[0..4], "\x00\x00\x00\x00")) name: {
634 const index = std.mem.readInt(u32, symbol.name[4..], .little);723 const index = std.mem.readInt(u32, symbol.name[4..], .little);
635 if (index >= string_table.len)724 if (index >= string_table.len)
636 return failParse(opts, "invalid name offset for symbol {x} ({x} >= {x})", .{ symbol_i, index, string_table.len });725 return failParse(opts, "invalid name offset for symbol {x} ({x} >= {x})", .{
726 symbol_i,
727 index,
728 string_table.len,
729 });
637 break :name string_table[index..];730 break :name string_table[index..];
638 } else &symbol.name, 0);731 } else &symbol.name, 0);
639732
733 if (opts.relocs)
734 symbol_names.appendNTimesAssumeCapacity(name, 1 + symbol.number_of_aux_symbols);
735
640 try w.print("{x: >4} {x:0>8} ", .{ symbol_i, symbol.value });736 try w.print("{x: >4} {x:0>8} ", .{ symbol_i, symbol.value });
641 try switch (symbol.section_number) {737 try switch (symbol.section_number) {
642 .UNDEFINED => w.writeAll("UNDEF"),738 .UNDEFINED => w.writeAll("UNDEF"),
...@@ -743,18 +839,18 @@ const coff = struct {...@@ -743,18 +839,18 @@ const coff = struct {
743 std.mem.byteSwapAllFields(std.coff.SectionDefinition, &section_def);839 std.mem.byteSwapAllFields(std.coff.SectionDefinition, &section_def);
744840
745 const section = &sections.items[section_i];841 const section = &sections.items[section_i];
746 if (section_def.number_of_relocations != section.number_of_relocations) {842 if (section_def.number_of_relocations != section.header.number_of_relocations) {
747 try w.print(843 try w.print(
748 " !! relocation count did not match section header: {d} vs {d}",844 " !! relocation count did not match section header: {d} vs {d}",
749 .{ section_def.number_of_relocations, section.number_of_relocations },845 .{ section_def.number_of_relocations, section.header.number_of_relocations },
750 );846 );
751 continue;847 continue;
752 }848 }
753849
754 if (section_def.number_of_linenumbers != section.number_of_linenumbers) {850 if (section_def.number_of_linenumbers != section.header.number_of_linenumbers) {
755 try w.print(851 try w.print(
756 " !! line number count did not match section header: {d} vs {d}",852 " !! line number count did not match section header: {d} vs {d}",
757 .{ section_def.number_of_linenumbers, section.number_of_linenumbers },853 .{ section_def.number_of_linenumbers, section.header.number_of_linenumbers },
758 );854 );
759 continue;855 continue;
760 }856 }
...@@ -786,6 +882,52 @@ const coff = struct {...@@ -786,6 +882,52 @@ const coff = struct {
786 try w.writeAll("No symbol table found\n");882 try w.writeAll("No symbol table found\n");
787 }883 }
788 }884 }
885
886 if (opts.relocs) {
887 const relocation_size = std.coff.Relocation.sizeOf();
888
889 for (sections.items, 0..) |section, section_i| {
890 if (section.header.pointer_to_relocations == 0) continue;
891
892 try w.print(
893 \\Relocs for section {x} '{s}' in {s}:
894 \\ Offset Type Symbol Name
895 \\
896 , .{ section_i + 1, section.name, obj_name });
897
898 fr.seekTo(file_location + section.header.pointer_to_relocations) catch |err|
899 return failParse(opts, "unable to seek to section {x} relocation table: {t}", .{ section_i + 1, err });
900
901 for (0..section.header.number_of_relocations) |reloc_i| {
902 var reloc: std.coff.Relocation = undefined;
903 @memcpy(std.mem.asBytes(&reloc)[0..relocation_size], try r.take(relocation_size));
904 if (native_endian != .little)
905 std.mem.byteSwapAllFields(std.coff.Relocation, &reloc);
906
907 try w.print("{x:0>8} ", .{reloc.virtual_address});
908 switch (header.machine) {
909 _ => unreachable,
910 inline else => |m| switch (m.RelocationType()) {
911 void => try w.writeAll("(unknown arch)"),
912 else => |RelocationType| try w.print(
913 "{t: <17} ",
914 .{@as(RelocationType, @enumFromInt(reloc.type))},
915 ),
916 },
917 }
918
919 if (reloc.symbol_table_index >= symbol_names.items.len)
920 return failParse(
921 opts,
922 "reloc {x} in section {x} has out-of-bounds symbol index {x}",
923 .{ reloc_i, section_i + 1, reloc.symbol_table_index },
924 );
925
926 try w.print("{x: >8} | {s}\n", .{ reloc.symbol_table_index, symbol_names.items[reloc.symbol_table_index] });
927 }
928 try w.writeByte('\n');
929 }
930 }
789 }931 }
790932
791 fn headerName(raw: *const [8]u8, string_table: []const u8) ![]const u8 {933 fn headerName(raw: *const [8]u8, string_table: []const u8) ![]const u8 {
...@@ -903,12 +1045,14 @@ const usage =...@@ -903,12 +1045,14 @@ const usage =
903 \\ -h, --help Print this help and exit1045 \\ -h, --help Print this help and exit
904 \\ --all-headers Alias for --file-headers --member-headers --section-headers --relocs --symbols1046 \\ --all-headers Alias for --file-headers --member-headers --section-headers --relocs --symbols
905 \\ --file-headers Display file-format specific headers1047 \\ --file-headers Display file-format specific headers
906 \\ --linker-member[=1|2] (Coff) Display contents of the specified linker member (default 2)1048 \\ --imports Display imported symbols
1049 \\ --exports Display exported symbols
1050 \\ --linker-member[=1|2|longnames] (Coff) Display contents of the specified linker member (default 2)
907 \\ --member-headers Display archive member headers1051 \\ --member-headers Display archive member headers
908 \\ --only-member=[name] Only consider archive members that contain [name]. Can be specified multiple times.1052 \\ --only-member=[name] Only consider archive members that contain [name]. Can be specified multiple times.
909 \\ --only-section=[name] Only consider sections that contain [name]. Can be specified multiple times.1053 \\ --only-section=[name] Only consider sections that contain [name]. Can be specified multiple times.
910 \\ --section-headers Display section headers1054 \\ --section-headers Display section headers
911 \\ --strings Display string table1055 \\ --strings Display string tables
912 \\ --symbols Display symbol tables1056 \\ --symbols Display symbol tables
913 \\ --relocs Display relocations1057 \\ --relocs Display relocations
914;1058;
lib/std/coff.zig+30
...@@ -1520,6 +1520,36 @@ pub const IMAGE = struct {...@@ -1520,6 +1520,36 @@ pub const IMAGE = struct {
1520 _,1520 _,
1521 /// AXP 64 (Same as Alpha 64)1521 /// AXP 64 (Same as Alpha 64)
1522 pub const AXP64: IMAGE.FILE.MACHINE = .ALPHA64;1522 pub const AXP64: IMAGE.FILE.MACHINE = .ALPHA64;
1523
1524 pub fn RelocationType(comptime machine: IMAGE.FILE.MACHINE) type {
1525 return switch (machine) {
1526 .AMD64,
1527 => REL.AMD64,
1528 .ARM,
1529 .ARMNT,
1530 => REL.ARM,
1531 .ARM64,
1532 .ARM64EC,
1533 .ARM64X,
1534 => REL.ARM64,
1535 .I386 => REL.I386,
1536 .IA64 => REL.IA64,
1537 .M32R => REL.M32R,
1538 .MIPS16,
1539 .MIPSFPU,
1540 .MIPSFPU16,
1541 => REL.MIPS,
1542 .POWERPC,
1543 .POWERPCFP,
1544 => REL.PPC,
1545 .SH3,
1546 .SH3DSP,
1547 .SH4,
1548 .SH5,
1549 => REL.SH,
1550 else => void,
1551 };
1552 }
1523 };1553 };
1524 };1554 };
15251555