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:57-04:00
log62892173839f4741e02b15dea117c9245bfe930b
tree1d8c4b0ef407840ccac3d0ff45e395bef455fadc
parent32377bb73cc70dfd141629eef0e5238b5debeb57

objdump: coff --exports


1 files changed, 294 insertions(+), 87 deletions(-)

lib/compiler/objdump.zig+294-87
...@@ -21,6 +21,7 @@ const Options = struct {...@@ -21,6 +21,7 @@ const Options = struct {
21 section_headers: bool,21 section_headers: bool,
22 strings: bool,22 strings: bool,
23 symbols: bool,23 symbols: bool,
24 tls: bool,
2425
25 // Coff-specific26 // Coff-specific
26 linker_member: ?std.coff.ArchiveMemberHeader.Kind,27 linker_member: ?std.coff.ArchiveMemberHeader.Kind,
...@@ -43,6 +44,7 @@ pub fn main(init: std.process.Init) !void {...@@ -43,6 +44,7 @@ pub fn main(init: std.process.Init) !void {
43 var opt_section_headers: ?bool = null;44 var opt_section_headers: ?bool = null;
44 var opt_strings: ?bool = null;45 var opt_strings: ?bool = null;
45 var opt_symbols: ?bool = null;46 var opt_symbols: ?bool = null;
47 var opt_tls: ?bool = null;
46 var section_filters: std.ArrayList([]const u8) = .empty;48 var section_filters: std.ArrayList([]const u8) = .empty;
47 var member_filters: std.ArrayList([]const u8) = .empty;49 var member_filters: std.ArrayList([]const u8) = .empty;
48 while (i < args.len) : (i += 1) {50 while (i < args.len) : (i += 1) {
...@@ -83,6 +85,8 @@ pub fn main(init: std.process.Init) !void {...@@ -83,6 +85,8 @@ pub fn main(init: std.process.Init) !void {
83 opt_strings = true;85 opt_strings = true;
84 } else if (mem.eql(u8, arg, "--symbols")) {86 } else if (mem.eql(u8, arg, "--symbols")) {
85 opt_symbols = true;87 opt_symbols = true;
88 } else if (mem.eql(u8, arg, "--tls")) {
89 opt_tls = true;
86 } else {90 } else {
87 fatal("unrecognized argument: {s}", .{arg});91 fatal("unrecognized argument: {s}", .{arg});
88 }92 }
...@@ -98,14 +102,15 @@ pub fn main(init: std.process.Init) !void {...@@ -98,14 +102,15 @@ pub fn main(init: std.process.Init) !void {
98 .exports = opt_exports orelse false,102 .exports = opt_exports orelse false,
99 .file_headers = opt_file_headers orelse false,103 .file_headers = opt_file_headers orelse false,
100 .imports = opt_imports orelse false,104 .imports = opt_imports orelse false,
105 .linker_member = opt_linker_member,
106 .member_filters = member_filters.items,
107 .member_headers = opt_member_headers orelse false,
101 .section_filters = section_filters.items,108 .section_filters = section_filters.items,
102 .section_headers = opt_section_headers orelse false,109 .section_headers = opt_section_headers orelse false,
103 .relocs = opt_relocs orelse false,110 .relocs = opt_relocs orelse false,
104 .strings = opt_strings orelse false,111 .strings = opt_strings orelse false,
105 .symbols = opt_symbols orelse false,112 .symbols = opt_symbols orelse false,
106 .member_filters = member_filters.items,113 .tls = opt_tls orelse false,
107 .member_headers = opt_member_headers orelse false,
108 .linker_member = opt_linker_member,
109 };114 };
110115
111 var file = std.Io.Dir.cwd().openFile(io, opts.input_path, .{}) catch |err|116 var file = std.Io.Dir.cwd().openFile(io, opts.input_path, .{}) catch |err|
...@@ -202,6 +207,21 @@ const wasm = struct {...@@ -202,6 +207,21 @@ const wasm = struct {
202};207};
203208
204const coff = struct {209const coff = struct {
210 const DIRECTORY_ENTRY = std.coff.IMAGE.DIRECTORY_ENTRY;
211
212 const Section = struct {
213 header: std.coff.SectionHeader,
214 name: []const u8,
215
216 fn rvaFileOffset(section: *const Section, rva: u32) !u32 {
217 if (rva < section.header.virtual_address or
218 rva >= section.header.virtual_address + section.header.size_of_raw_data)
219 return error.OutOfBounds;
220
221 return section.header.pointer_to_raw_data + (rva - section.header.virtual_address);
222 }
223 };
224
205 const ArchiveHeader = struct {225 const ArchiveHeader = struct {
206 name: []const u8,226 name: []const u8,
207 date: u40,227 date: u40,
...@@ -308,8 +328,6 @@ const coff = struct {...@@ -308,8 +328,6 @@ const coff = struct {
308 if (!std.mem.eql(u8, header.name, "/"))328 if (!std.mem.eql(u8, header.name, "/"))
309 return failParse(opts, "expected second linker member, found '{s}'", .{header.name});329 return failParse(opts, "expected second linker member, found '{s}'", .{header.name});
310330
311 // TODO: Figure out what endianness is actually used, there are no headers to say yet?
312
313 const num_members = try r.takeInt(u32, .little);331 const num_members = try r.takeInt(u32, .little);
314 pos = fr.logicalPos();332 pos = fr.logicalPos();
315 if (pos + num_members * @sizeOf(u32) > member_end)333 if (pos + num_members * @sizeOf(u32) > member_end)
...@@ -511,16 +529,22 @@ const coff = struct {...@@ -511,16 +529,22 @@ const coff = struct {
511 else => {},529 else => {},
512 }530 }
513531
514 if (header.size_of_optional_header > 0) opt_header: {532 var known_dirs: [DIRECTORY_ENTRY.len]std.coff.ImageDataDirectory = undefined;
515 if (!opts.file_headers) {533 const needs_data_dirs =
534 opts.exports or
535 opts.imports or
536 opts.tls;
537
538 const data_dirs = if (header.size_of_optional_header > 0) data_dirs: {
539 if (!opts.file_headers and !needs_data_dirs) {
516 try fr.seekBy(header.size_of_optional_header);540 try fr.seekBy(header.size_of_optional_header);
517 break :opt_header;541 break :data_dirs &.{};
518 }542 }
519543
520 try w.writeAll("COFF Optional Header:\n");544 if (opts.file_headers) try w.writeAll("COFF Optional Header:\n");
521 const magic: std.coff.OptionalHeader.Magic = @enumFromInt(try r.peekInt(u16, .little));545 const magic: std.coff.OptionalHeader.Magic = @enumFromInt(try r.peekInt(u16, .little));
522 const num_directory_entries = switch (magic) {546 const num_directory_entries = switch (magic) {
523 inline .PE32, .@"PE32+" => |v| data_dirs: {547 inline .PE32, .@"PE32+" => |v| num_data_dirs: {
524 const OptionalHeader = if (v == .PE32)548 const OptionalHeader = if (v == .PE32)
525 std.coff.OptionalHeader.PE32549 std.coff.OptionalHeader.PE32
526 else550 else
...@@ -529,63 +553,71 @@ const coff = struct {...@@ -529,63 +553,71 @@ const coff = struct {
529 const optional_header = r.takeStruct(OptionalHeader, .little) catch |err|553 const optional_header = r.takeStruct(OptionalHeader, .little) catch |err|
530 return failParse(opts, "unable to read optional header: {t}", .{err});554 return failParse(opts, "unable to read optional header: {t}", .{err});
531555
532 try dumpHeader(w, OptionalHeader, &optional_header, struct {556 if (opts.file_headers) {
533 pub fn base_of_code(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void {557 try dumpHeader(w, OptionalHeader, &optional_header, struct {
534 const base = @as(*const OptionalHeader, @ptrCast(@alignCast(h))).image_base;558 pub fn base_of_code(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void {
535 try dumpRvaField(cw, @src().fn_name, h.base_of_code, base);559 const base = @as(*const OptionalHeader, @ptrCast(@alignCast(h))).image_base;
536 }560 try dumpRvaField(cw, @src().fn_name, h.base_of_code, base);
561 }
537562
538 pub fn address_of_entry_point(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void {563 pub fn address_of_entry_point(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void {
539 const base = @as(*const OptionalHeader, @ptrCast(@alignCast(h))).image_base;564 const base = @as(*const OptionalHeader, @ptrCast(@alignCast(h))).image_base;
540 try dumpRvaField(cw, @src().fn_name, h.base_of_code, base);565 try dumpRvaField(cw, @src().fn_name, h.base_of_code, base);
541 }566 }
542567
543 pub fn major_linker_version(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void {568 pub fn major_linker_version(h: *const std.coff.OptionalHeader, cw: *Io.Writer) !void {
544 try dumpVersionField(cw, "linker_version", h.major_linker_version, h.minor_linker_version);569 try dumpVersionField(cw, "linker_version", h.major_linker_version, h.minor_linker_version);
545 }570 }
546 pub fn minor_linker_version(_: *const std.coff.OptionalHeader, _: *Io.Writer) !void {}571 pub fn minor_linker_version(_: *const std.coff.OptionalHeader, _: *Io.Writer) !void {}
547572
548 pub fn major_operating_system_version(h: *const OptionalHeader, cw: *Io.Writer) !void {573 pub fn major_operating_system_version(h: *const OptionalHeader, cw: *Io.Writer) !void {
549 try dumpVersionField(574 try dumpVersionField(
550 cw,575 cw,
551 "operating_system_version",576 "operating_system_version",
552 h.major_operating_system_version,577 h.major_operating_system_version,
553 h.minor_operating_system_version,578 h.minor_operating_system_version,
554 );579 );
555 }580 }
556 pub fn minor_operating_system_version(_: *const OptionalHeader, _: *Io.Writer) !void {}581 pub fn minor_operating_system_version(_: *const OptionalHeader, _: *Io.Writer) !void {}
557582
558 pub fn major_image_version(h: *const OptionalHeader, cw: *Io.Writer) !void {583 pub fn major_image_version(h: *const OptionalHeader, cw: *Io.Writer) !void {
559 try dumpVersionField(cw, "image_version", h.major_image_version, h.minor_image_version);584 try dumpVersionField(cw, "image_version", h.major_image_version, h.minor_image_version);
560 }585 }
561 pub fn minor_image_version(_: *const OptionalHeader, _: *Io.Writer) !void {}586 pub fn minor_image_version(_: *const OptionalHeader, _: *Io.Writer) !void {}
562587
563 pub fn major_subsystem_version(h: *const OptionalHeader, cw: *Io.Writer) !void {588 pub fn major_subsystem_version(h: *const OptionalHeader, cw: *Io.Writer) !void {
564 try dumpVersionField(cw, "subsystem_version", h.major_subsystem_version, h.minor_subsystem_version);589 try dumpVersionField(cw, "subsystem_version", h.major_subsystem_version, h.minor_subsystem_version);
565 }590 }
566 pub fn minor_subsystem_version(_: *const OptionalHeader, _: *Io.Writer) !void {}591 pub fn minor_subsystem_version(_: *const OptionalHeader, _: *Io.Writer) !void {}
567 });592 });
568 try w.writeByte('\n');593 try w.writeByte('\n');
594 }
569595
570 break :data_dirs optional_header.number_of_rva_and_sizes;596 break :num_data_dirs optional_header.number_of_rva_and_sizes;
571 },597 },
572 else => return failParse(opts, "invalid optional header magic number: {x}", .{magic}),598 else => return failParse(opts, "invalid optional header magic number: {x}", .{magic}),
573 };599 };
574600
575 try w.writeAll("Data Directories:\n");601 if (opts.file_headers) try w.writeAll("Data Directories:\n");
576 for (0..num_directory_entries) |dir_i| {602 for (0..num_directory_entries) |dir_i| {
577 const dir = r.takeStruct(std.coff.ImageDataDirectory, .little) catch |err|603 const dir = r.takeStruct(std.coff.ImageDataDirectory, .little) catch |err|
578 return failParse(opts, "unable to read data directory {x}: {t}", .{ dir_i, err });604 return failParse(opts, "unable to read data directory {x}: {t}", .{ dir_i, err });
579605
580 try w.print(606 if (dir_i < known_dirs.len)
581 "{x: >16} {x: >8} {t}\n",607 known_dirs[dir_i] = dir;
582 .{ dir.virtual_address, dir.size, @as(std.coff.IMAGE.DIRECTORY_ENTRY, @enumFromInt(dir_i)) },608
583 );609 if (opts.file_headers)
610 try w.print(
611 "{x: >16} {x: >8} {t}\n",
612 .{ dir.virtual_address, dir.size, @as(DIRECTORY_ENTRY, @enumFromInt(dir_i)) },
613 );
584 }614 }
585 try w.writeByte('\n');615 if (opts.file_headers) try w.writeByte('\n');
616
617 break :data_dirs known_dirs[0..@min(known_dirs.len, num_directory_entries)];
586 } else if (is_image) {618 } else if (is_image) {
587 return failParse(opts, "image did not contain an optional header", .{});619 return failParse(opts, "image did not contain an optional header", .{});
588 }620 } else &.{};
589621
590 // Section names in images don't use the string table, as they must fit inline in the header622 // Section names in images don't use the string table, as they must fit inline in the header
591 const load_string_table = (opts.strings or !is_image) and header.pointer_to_symbol_table > 0;623 const load_string_table = (opts.strings or !is_image) and header.pointer_to_symbol_table > 0;
...@@ -620,16 +652,15 @@ const coff = struct {...@@ -620,16 +652,15 @@ const coff = struct {
620 try w.writeByte('\n');652 try w.writeByte('\n');
621 }653 }
622654
623 var sections: std.ArrayList(struct {655 var sections: std.ArrayList(Section) = .empty;
624 header: std.coff.SectionHeader,
625 name: []const u8,
626 }) = .empty;
627 defer sections.deinit(gpa);656 defer sections.deinit(gpa);
657 var sections_with_data: u16 = 0;
628658
629 const load_sections =659 const load_sections =
630 opts.section_headers or660 opts.section_headers or
631 opts.symbols or661 opts.symbols or
632 opts.relocs;662 opts.relocs or
663 needs_data_dirs;
633664
634 if (load_sections) {665 if (load_sections) {
635 if (opts.section_headers)666 if (opts.section_headers)
...@@ -656,11 +687,12 @@ const coff = struct {...@@ -656,11 +687,12 @@ const coff = struct {
656 }),687 }),
657 };688 };
658689
690 sections_with_data += @intFromBool(section.header.size_of_raw_data > 0);
659 if (opts.section_headers) {691 if (opts.section_headers) {
660 if (!filterMatches(opts.section_filters, section.name)) continue;692 if (!filterMatches(opts.section_filters, section.name)) continue;
661 const raw_name = std.mem.sliceTo(&section.header.name, 0);693 const raw_name = std.mem.sliceTo(&section.header.name, 0);
662 try w.print(694 try w.print(
663 "{x: >3} {s: <8} {x: >8} {x: >9} {x: >9} {x: >8} {x: >8} {x: >8} {x: >8} {x: >8} {x:0>8} | ",695 "{x: >3} {s: <8} {x: >8} {x: >9} {x: >9} {x: >8} {x: >8} {x: >8} {x: >8} {x: >8} {x:0>8} |",
664 .{696 .{
665 section_i + 1,697 section_i + 1,
666 raw_name,698 raw_name,
...@@ -676,7 +708,7 @@ const coff = struct {...@@ -676,7 +708,7 @@ const coff = struct {
676 },708 },
677 );709 );
678710
679 try dumpFlags(w, "{s} ", std.coff.SectionHeader.Flags, &section.header.flags, 1);711 try dumpFlags(w, "{s}", std.coff.SectionHeader.Flags, &section.header.flags, 1);
680 if (section.name.len > 8)712 if (section.name.len > 8)
681 try w.print("| {s}", .{section.name});713 try w.print("| {s}", .{section.name});
682714
...@@ -790,10 +822,7 @@ const coff = struct {...@@ -790,10 +822,7 @@ const coff = struct {
790 (std.mem.eql(u8, name, ".bf") or std.mem.eql(u8, name, ".ef")))822 (std.mem.eql(u8, name, ".bf") or std.mem.eql(u8, name, ".ef")))
791 {823 {
792 try w.writeAll("TODO bf / ef aux symbol");824 try w.writeAll("TODO bf / ef aux symbol");
793 } else if (symbol.storage_class == .EXTERNAL and825 } else if (symbol.storage_class == .WEAK_EXTERNAL and symbol.section_number == .UNDEFINED) {
794 symbol.section_number == .UNDEFINED and
795 symbol.value == 0)
796 {
797 if (symbol.value != 0)826 if (symbol.value != 0)
798 return failParse(827 return failParse(
799 opts,828 opts,
...@@ -815,6 +844,10 @@ const coff = struct {...@@ -815,6 +844,10 @@ const coff = struct {
815844
816 // TODO845 // TODO
817846
847 try w.print(" Weak External [falls back to {x:0>8} via {t}]", .{
848 weak_external.tag_index,
849 weak_external.flag,
850 });
818 } else if (symbol.storage_class == .FILE) {851 } else if (symbol.storage_class == .FILE) {
819 if (!std.mem.eql(u8, name, ".file")) {852 if (!std.mem.eql(u8, name, ".file")) {
820 try w.print(" !! unexpected symbol name '{s}' for file symbol 0x{x}", .{ name, symbol_i });853 try w.print(" !! unexpected symbol name '{s}' for file symbol 0x{x}", .{ name, symbol_i });
...@@ -824,6 +857,7 @@ const coff = struct {...@@ -824,6 +857,7 @@ const coff = struct {
824 var file: std.coff.FileDefinition = undefined;857 var file: std.coff.FileDefinition = undefined;
825 @memcpy(std.mem.asBytes(&file)[0..symbol_size], aux_symbols[0..symbol_size]);858 @memcpy(std.mem.asBytes(&file)[0..symbol_size], aux_symbols[0..symbol_size]);
826859
860 // TODO
827 _ = file.getFileName();861 _ = file.getFileName();
828 } else if (symbol.storage_class == .STATIC and862 } else if (symbol.storage_class == .STATIC and
829 symbol.type == std.coff.SymType{863 symbol.type == std.coff.SymType{
...@@ -934,16 +968,205 @@ const coff = struct {...@@ -934,16 +968,205 @@ const coff = struct {
934 .{ reloc_i, section_i + 1, reloc.symbol_table_index },968 .{ reloc_i, section_i + 1, reloc.symbol_table_index },
935 );969 );
936970
937 try w.print("{x: >8} ", .{reloc.symbol_table_index});
938
939 const sym = &symbols.items[reloc.symbol_table_index];971 const sym = &symbols.items[reloc.symbol_table_index];
940 try sectionNumberString(sym.section_number, w);972 try w.print("{x: >8} {f} | {s}\n", .{
941973 reloc.symbol_table_index,
942 try w.print(" | {s}\n", .{sym.name});974 fmtSectionNumber(sym.section_number),
975 sym.name,
976 });
943 }977 }
944 try w.writeByte('\n');978 try w.writeByte('\n');
945 }979 }
946 }980 }
981
982 // Sections indices with raw data, sorted by RVA
983 const rva_index = if (needs_data_dirs) rva_index: {
984 const rva_index = try gpa.alloc(u16, sections_with_data);
985 var indices_i: u16 = 0;
986 for (sections.items, 0..) |*section, i| {
987 if (section.header.size_of_raw_data == 0) continue;
988 rva_index[indices_i] = @intCast(i);
989 indices_i += 1;
990 }
991
992 const Context = struct {
993 indices: []u16,
994 sections: []const Section,
995
996 pub fn lessThan(ctx: @This(), lhs: usize, rhs: usize) bool {
997 return ctx.sections[ctx.indices[lhs]].header.virtual_address <
998 ctx.sections[ctx.indices[rhs]].header.virtual_address;
999 }
1000
1001 pub fn swap(ctx: @This(), lhs: usize, rhs: usize) void {
1002 std.mem.swap(u16, &ctx.indices[lhs], &ctx.indices[rhs]);
1003 }
1004 };
1005
1006 std.sort.pdqContext(0, rva_index.len, Context{
1007 .indices = rva_index,
1008 .sections = sections.items,
1009 });
1010
1011 break :rva_index rva_index;
1012 } else &.{};
1013 defer gpa.free(rva_index);
1014
1015 if (opts.exports) {
1016 if (try seekToDataDirectory(opts, fr, w, rva_index, sections.items, data_dirs, .EXPORT)) |section_index| {
1017 const export_dir = r.takeStruct(std.coff.ExportDirectoryTable, .little) catch |err|
1018 return failParse(opts, "unable to read export directory: {t}", .{err});
1019
1020 try w.print("Export directory:\n", .{});
1021 try dumpHeader(w, std.coff.ExportDirectoryTable, &export_dir, struct {
1022 pub fn major_version(h: *const std.coff.ExportDirectoryTable, cw: *Io.Writer) !void {
1023 try dumpVersionField(cw, "version", h.major_version, h.minor_version);
1024 }
1025 pub fn minor_version(_: *const std.coff.ExportDirectoryTable, _: *Io.Writer) !void {}
1026 });
1027
1028 const section = sections.items[section_index];
1029 const name_loc = section.rvaFileOffset(export_dir.name_rva) catch
1030 return failParse(
1031 opts,
1032 "export name rva 0x{x} was not within the export section",
1033 .{export_dir.name_rva},
1034 );
1035
1036 const eat_loc = section.rvaFileOffset(export_dir.export_address_table_rva) catch
1037 return failParse(
1038 opts,
1039 "export address table rva 0x{x} was not within the export section",
1040 .{export_dir.export_address_table_rva},
1041 );
1042
1043 const name_pointer_loc = section.rvaFileOffset(export_dir.name_pointer_table_rva) catch
1044 return failParse(
1045 opts,
1046 "export name pointer table rva 0x{x} was not within the export section",
1047 .{export_dir.name_pointer_table_rva},
1048 );
1049
1050 const ord_loc = section.rvaFileOffset(export_dir.ordinal_table_rva) catch
1051 return failParse(
1052 opts,
1053 "export ordinal table rva 0x{x} was not within the export section",
1054 .{export_dir.ordinal_table_rva},
1055 );
1056
1057 // All the variable length fields should be contained within this directory.
1058 // Read it entirely to avoid needing to seek per-name when iterating.
1059 const dir = data_dirs[@intFromEnum(DIRECTORY_ENTRY.EXPORT)];
1060 const dir_end_rva = dir.virtual_address + dir.size;
1061 const dir_loc = fr.logicalPos();
1062 const dir_slice = try r.readAlloc(gpa, dir.size);
1063 defer gpa.free(dir_slice);
1064
1065 const dll_name = std.mem.sliceTo(dir_slice[name_loc - dir_loc ..], 0);
1066 try w.print(
1067 \\
1068 \\Exports from {s}:
1069 \\ Ord Hint RVA Name
1070 \\
1071 , .{dll_name});
1072
1073 const name_pointers = dir_slice[name_pointer_loc - dir_loc ..][0 .. export_dir.number_of_names * @sizeOf(u32)];
1074 const ords = dir_slice[ord_loc - dir_loc ..][0 .. export_dir.number_of_names * @sizeOf(u16)];
1075 const addrs = dir_slice[eat_loc - dir_loc ..][0 .. export_dir.number_of_entries * @sizeOf(u32)];
1076 const name_rva_to_offset = dir.virtual_address + @sizeOf(std.coff.ExportDirectoryTable);
1077 for (0..export_dir.number_of_names) |name_i| {
1078 const name_rva = std.mem.readInt(u32, name_pointers[name_i * @sizeOf(u32) ..][0..@sizeOf(u32)], .little);
1079 const ord = std.mem.readInt(u16, ords[name_i * @sizeOf(u16) ..][0..@sizeOf(u16)], .little);
1080 const addr = std.mem.readInt(u32, addrs[@as(u32, ord) * @sizeOf(u32) ..][0..@sizeOf(u32)], .little);
1081
1082 try w.print("{x: >4} {x: >4} ", .{ export_dir.ordinal_base + ord, name_i });
1083 const is_forwarder = addr >= dir.virtual_address and addr < dir_end_rva;
1084 if (is_forwarder) {
1085 try w.writeAll("forwards");
1086 } else {
1087 try w.print("{x: >8}", .{addr});
1088 }
1089
1090 const name = std.mem.sliceTo(dir_slice[name_rva - name_rva_to_offset ..], 0);
1091 try w.print(" | {s}", .{name});
1092 if (is_forwarder)
1093 try w.print(" -> {s}", .{std.mem.sliceTo(dir_slice[addr - name_rva_to_offset ..], 0)});
1094 try w.writeByte('\n');
1095 }
1096 }
1097 }
1098
1099 if (opts.imports) {
1100 if (try seekToDataDirectory(opts, fr, w, rva_index, sections.items, data_dirs, .IMPORT)) |_| {
1101 // TODO
1102 }
1103 }
1104
1105 if (opts.tls) {
1106 if (try seekToDataDirectory(opts, fr, w, rva_index, sections.items, data_dirs, .TLS)) |_| {
1107 // TODO
1108 }
1109 }
1110 }
1111
1112 fn seekToDataDirectory(
1113 opts: *const Options,
1114 fr: *Io.File.Reader,
1115 w: *Io.Writer,
1116 rva_index: []const u16,
1117 sections: []const Section,
1118 data_dirs: []const std.coff.ImageDataDirectory,
1119 entry: DIRECTORY_ENTRY,
1120 ) !?u16 {
1121 if (@intFromEnum(entry) < data_dirs.len) {
1122 const rva = data_dirs[@intFromEnum(entry)].virtual_address;
1123 const section_index = sectionContainingRva(rva_index, sections, rva) orelse
1124 return failParse(
1125 opts,
1126 "{t} directory rva 0x{x} was not found in any section",
1127 .{ entry, rva },
1128 );
1129
1130 const file_offset = sections[section_index].rvaFileOffset(rva) catch unreachable;
1131 fr.seekTo(file_offset) catch |err|
1132 return failParse(
1133 opts,
1134 "unable to seek to {t} directory at offset 0x{x}: {t}",
1135 .{ entry, file_offset, err },
1136 );
1137
1138 return section_index;
1139 }
1140
1141 try w.print("{t} directory was not present in optional header\n", .{entry});
1142 return null;
1143 }
1144
1145 fn sectionContainingRva(
1146 /// Indices into `sections` sorted by rva
1147 indices: []const u16,
1148 sections: []const Section,
1149 rva: u32,
1150 ) ?u16 {
1151 const Context = struct {
1152 rva: u32,
1153 sections: []const Section,
1154
1155 fn order(ctx: @This(), section_index: u16) std.math.Order {
1156 const h = &ctx.sections[section_index].header;
1157 const start = h.virtual_address;
1158 if (ctx.rva < start) return .lt;
1159 const end = h.virtual_address + h.size_of_raw_data;
1160 if (ctx.rva >= end) return .gt;
1161 return .eq;
1162 }
1163 };
1164
1165 const index = std.sort.binarySearch(u16, indices, Context{
1166 .rva = rva,
1167 .sections = sections,
1168 }, Context.order) orelse return null;
1169 return @intCast(index);
947 }1170 }
9481171
949 fn headerName(raw: *const [8]u8, string_table: []const u8) ![]const u8 {1172 fn headerName(raw: *const [8]u8, string_table: []const u8) ![]const u8 {
...@@ -956,21 +1179,6 @@ const coff = struct {...@@ -956,21 +1179,6 @@ const coff = struct {
956 } else std.mem.sliceTo(raw, 0);1179 } else std.mem.sliceTo(raw, 0);
957 }1180 }
9581181
959 fn fmtSymbolType(sym_type: std.coff.SymType) std.fmt.Alt(std.coff.SymType, symbolTypeString) {
960 return .{ .data = sym_type };
961 }
962
963 fn symbolTypeString(sym_type: std.coff.SymType, w: *std.Io.Writer) std.Io.Writer.Error!void {
964 try w.print("{t: >5}", .{sym_type.base_type});
965 if (try switch (sym_type.complex_type) {
966 .NULL => " ",
967 .POINTER => "* ",
968 .FUNCTION => "()",
969 .ARRAY => "[]",
970 else => null,
971 }) |suffix| try .printAll(suffix) else w.print("{x}", .{sym_type.complex_type});
972 }
973
974 fn fmtSectionNumber(section_number: std.coff.SectionNumber) std.fmt.Alt(std.coff.SectionNumber, sectionNumberString) {1182 fn fmtSectionNumber(section_number: std.coff.SectionNumber) std.fmt.Alt(std.coff.SectionNumber, sectionNumberString) {
975 return .{ .data = section_number };1183 return .{ .data = section_number };
976 }1184 }
...@@ -1003,8 +1211,6 @@ const coff = struct {...@@ -1003,8 +1211,6 @@ const coff = struct {
10031211
1004 fn dumpArchiveHeader(w: *Io.Writer, header: *const ArchiveHeader, pos: u32) !void {1212 fn dumpArchiveHeader(w: *Io.Writer, header: *const ArchiveHeader, pos: u32) !void {
1005 try w.print("Archive member at offset 0x{x}: '{s}'\n", .{ pos, header.name });1213 try w.print("Archive member at offset 0x{x}: '{s}'\n", .{ pos, header.name });
1006
1007 // TODO: Date formatter
1008 try dumpHeader(w, ArchiveHeader, header, struct {1214 try dumpHeader(w, ArchiveHeader, header, struct {
1009 pub fn name(_: *const ArchiveHeader, _: *Io.Writer) !void {}1215 pub fn name(_: *const ArchiveHeader, _: *Io.Writer) !void {}
1010 pub fn file_mode(h: *const ArchiveHeader, cw: *Io.Writer) !void {1216 pub fn file_mode(h: *const ArchiveHeader, cw: *Io.Writer) !void {
...@@ -1065,10 +1271,11 @@ const usage =...@@ -1065,10 +1271,11 @@ const usage =
1065 \\ --exports Display exported symbols1271 \\ --exports Display exported symbols
1066 \\ --linker-member[=1|2|longnames] (Coff) Display contents of the specified linker member (default 2)1272 \\ --linker-member[=1|2|longnames] (Coff) Display contents of the specified linker member (default 2)
1067 \\ --member-headers Display archive member headers1273 \\ --member-headers Display archive member headers
1068 \\ --only-member=[name] Only consider archive members that contain [name]. Can be specified multiple times.1274 \\ --only-member=[name] Only consider archive members names that contain [name]. Can be specified multiple times.
1069 \\ --only-section=[name] Only consider sections that contain [name]. Can be specified multiple times.1275 \\ --only-section=[name] Only consider section names that contain [name]. Can be specified multiple times.
1276 \\ --relocs Display relocations
1070 \\ --section-headers Display section headers1277 \\ --section-headers Display section headers
1071 \\ --strings Display string tables1278 \\ --strings Display string tables
1072 \\ --symbols Display symbol tables1279 \\ --symbols Display symbol tables
1073 \\ --relocs Display relocations1280 \\ --tls Display TLS information
1074;1281;