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
log97bf880486c6bd0811f675c61d7675632fd1722d
tree8dc4c051c365bfd0d82248f06dd359cde3004fdb
parent62892173839f4741e02b15dea117c9245bfe930b

objdump: coff --imports


3 files changed, 153 insertions(+), 77 deletions(-)

lib/compiler/objdump.zig+132-5
......@@ -535,10 +535,10 @@ const coff = struct {
535535 opts.imports or
536536 opts.tls;
537537
538 const data_dirs = if (header.size_of_optional_header > 0) data_dirs: {
538 const data_dirs, const magic = if (header.size_of_optional_header > 0) optional_header: {
539539 if (!opts.file_headers and !needs_data_dirs) {
540540 try fr.seekBy(header.size_of_optional_header);
541 break :data_dirs &.{};
541 break :optional_header .{ &.{}, null };
542542 }
543543
544544 if (opts.file_headers) try w.writeAll("COFF Optional Header:\n");
......@@ -614,10 +614,10 @@ const coff = struct {
614614 }
615615 if (opts.file_headers) try w.writeByte('\n');
616616
617 break :data_dirs known_dirs[0..@min(known_dirs.len, num_directory_entries)];
617 break :optional_header .{ known_dirs[0..@min(known_dirs.len, num_directory_entries)], magic };
618618 } else if (is_image) {
619619 return failParse(opts, "image did not contain an optional header", .{});
620 } else &.{};
620 } else .{ &.{}, null };
621621
622622 // Section names in images don't use the string table, as they must fit inline in the header
623623 const load_string_table = (opts.strings or !is_image) and header.pointer_to_symbol_table > 0;
......@@ -1098,7 +1098,134 @@ const coff = struct {
10981098
10991099 if (opts.imports) {
11001100 if (try seekToDataDirectory(opts, fr, w, rva_index, sections.items, data_dirs, .IMPORT)) |_| {
1101 // TODO
1101 const Entry = std.coff.ImportDirectoryEntry;
1102 var directory_entries: std.ArrayList(Entry) = .empty;
1103 defer directory_entries.deinit(gpa);
1104 while (true) {
1105 const entry = r.takeStruct(Entry, .little) catch |err|
1106 return failParse(
1107 opts,
1108 "unable to read import directory entry {x}: {t}",
1109 .{ directory_entries.items.len, err },
1110 );
1111
1112 if (std.mem.allEqual(u8, std.mem.asBytes(&entry), 0)) break;
1113 (try directory_entries.addOne(gpa)).* = entry;
1114 }
1115
1116 for (directory_entries.items) |entry| {
1117 const name_section = sectionContainingRva(rva_index, sections.items, entry.name_rva) orelse
1118 return failParse(
1119 opts,
1120 "import directory entry name rva 0x{x} was not found in any section",
1121 .{entry.name_rva},
1122 );
1123
1124 const name_loc = sections.items[name_section].rvaFileOffset(entry.name_rva) catch unreachable;
1125 fr.seekTo(name_loc) catch |err|
1126 return failParse(
1127 opts,
1128 "unable to seek to import directory entry name at 0x{x}: {t}",
1129 .{ name_loc, err },
1130 );
1131
1132 const dll_name = (try r.takeDelimiter(0)).?;
1133
1134 try w.print("Import table entry for {s}:\n", .{dll_name});
1135 try dumpHeader(w, Entry, &entry, struct {});
1136
1137 try w.print(
1138 \\
1139 \\ Ord Hint Name
1140 \\
1141 , .{});
1142
1143 const ilt_section = sectionContainingRva(
1144 rva_index,
1145 sections.items,
1146 entry.import_lookup_table_rva,
1147 ) orelse
1148 return failParse(
1149 opts,
1150 "import directory entry ilt rva 0x{x} was not found in any section",
1151 .{entry.import_lookup_table_rva},
1152 );
1153
1154 const ilt_loc = sections.items[ilt_section].rvaFileOffset(
1155 entry.import_lookup_table_rva,
1156 ) catch unreachable;
1157 fr.seekTo(ilt_loc) catch |err|
1158 return failParse(
1159 opts,
1160 "unable to seek to import directory ilt at 0x{x}: {t}",
1161 .{ ilt_loc, err },
1162 );
1163
1164 switch (magic.?) {
1165 _ => try w.writeAll("(unknown magic)"),
1166 inline else => |m| {
1167 const TableEntry = std.coff.ImportLookupTableEntry(m);
1168 const null_entry: TableEntry = @bitCast(@as(@typeInfo(TableEntry).@"struct".backing_integer.?, 0));
1169
1170 var ilt_entries: std.ArrayList(TableEntry) = .empty;
1171 defer ilt_entries.deinit(gpa);
1172 while (true) {
1173 const table_entry = r.takeStruct(TableEntry, .little) catch |err|
1174 return failParse(
1175 opts,
1176 "unable to read ilt entry {s}:{x}: {t}",
1177 .{ dll_name, ilt_entries.items.len, err },
1178 );
1179 if (table_entry == null_entry) break;
1180 (try ilt_entries.addOne(gpa)).* = table_entry;
1181 }
1182
1183 for (ilt_entries.items, 0..) |ilt_entry, ilt_entry_i| {
1184 if (ilt_entry.is_ordinal) {
1185 try w.print("{x: >4}", .{ilt_entry.payload.ordinal.ordinal});
1186 } else {
1187 const hint_section = sectionContainingRva(
1188 rva_index,
1189 sections.items,
1190 ilt_entry.payload.hint_name_rva,
1191 ) orelse
1192 return failParse(
1193 opts,
1194 "import directory ilt entry 0x{x}'s hint rva 0x{x} was not found in any section",
1195 .{ ilt_entry_i, ilt_entry.payload.hint_name_rva },
1196 );
1197
1198 const hint_loc = sections.items[hint_section].rvaFileOffset(
1199 ilt_entry.payload.hint_name_rva,
1200 ) catch unreachable;
1201 fr.seekTo(hint_loc) catch |err|
1202 return failParse(
1203 opts,
1204 "unable to seek to ilt entry 0x{x}'s hint at 0x{x}: {t}",
1205 .{ ilt_entry_i, hint_loc, err },
1206 );
1207
1208 const hint = r.takeInt(u16, .little) catch |err|
1209 return failParse(
1210 opts,
1211 "unable to read import directory ilt entry 0x{x}'s hint: {t}",
1212 .{ ilt_entry_i, err },
1213 );
1214
1215 const name = r.takeDelimiter(0) catch |err|
1216 return failParse(
1217 opts,
1218 "unable to read import directory ilt entry 0x{x}'s name: {t}",
1219 .{ ilt_entry_i, err },
1220 );
1221
1222 try w.print(" {x: >4} | {s}\n", .{ hint, name.? });
1223 }
1224 }
1225 try w.writeByte('\n');
1226 },
1227 }
1228 }
11021229 }
11031230 }
11041231
lib/std/coff.zig+19-47
......@@ -395,56 +395,28 @@ pub const ImportDirectoryEntry = extern struct {
395395 import_address_table_rva: u32,
396396};
397397
398pub const ImportLookupEntry32 = struct {
399 pub const ByName = packed struct(u32) {
400 name_table_rva: u31,
401 flag: u1 = 0,
398pub fn ImportLookupTableEntry(comptime magic: std.coff.OptionalHeader.Magic) type {
399 const Payload = packed union(u31) {
400 ordinal: packed struct(u31) {
401 ordinal: u16,
402 _: u15 = 0,
403 },
404 hint_name_rva: u31,
402405 };
403406
404 pub const ByOrdinal = packed struct(u32) {
405 ordinal_number: u16,
406 unused: u15 = 0,
407 flag: u1 = 1,
407 return switch (magic) {
408 _ => comptime unreachable,
409 .PE32 => packed struct(u32) {
410 payload: Payload,
411 is_ordinal: bool,
412 },
413 .@"PE32+" => packed struct(u64) {
414 payload: Payload,
415 _: u32 = 0,
416 is_ordinal: bool,
417 },
408418 };
409
410 const mask = 0x80000000;
411
412 pub fn getImportByName(raw: u32) ?ByName {
413 if (mask & raw != 0) return null;
414 return @as(ByName, @bitCast(raw));
415 }
416
417 pub fn getImportByOrdinal(raw: u32) ?ByOrdinal {
418 if (mask & raw == 0) return null;
419 return @as(ByOrdinal, @bitCast(raw));
420 }
421};
422
423pub const ImportLookupEntry64 = struct {
424 pub const ByName = packed struct(u64) {
425 name_table_rva: u31,
426 unused: u32 = 0,
427 flag: u1 = 0,
428 };
429
430 pub const ByOrdinal = packed struct(u64) {
431 ordinal_number: u16,
432 unused: u47 = 0,
433 flag: u1 = 1,
434 };
435
436 const mask = 0x8000000000000000;
437
438 pub fn getImportByName(raw: u64) ?ByName {
439 if (mask & raw != 0) return null;
440 return @as(ByName, @bitCast(raw));
441 }
442
443 pub fn getImportByOrdinal(raw: u64) ?ByOrdinal {
444 if (mask & raw == 0) return null;
445 return @as(ByOrdinal, @bitCast(raw));
446 }
447};
419}
448420
449421/// Every name ends with a NULL byte. IF the NULL byte does not fall on
450422/// 2byte boundary, the entry structure is padded to ensure 2byte alignment.
src/link/Coff.zig+2-25
......@@ -730,29 +730,6 @@ pub const ImportTable = struct {
730730 hint_name_len: u32,
731731 };
732732
733 pub fn TableEntry(comptime magic: std.coff.OptionalHeader.Magic) type {
734 const Payload = packed union(u31) {
735 ordinal: packed struct(u31) {
736 ordinal: u16,
737 _: u15 = 0,
738 },
739 hint_name_rva: u31,
740 };
741
742 return switch (magic) {
743 _ => comptime unreachable,
744 .PE32 => packed struct(u32) {
745 payload: Payload,
746 is_ordinal: bool,
747 },
748 .@"PE32+" => packed struct(u64) {
749 payload: Payload,
750 _: u32 = 0,
751 is_ordinal: bool,
752 },
753 };
754 }
755
756733 const Adapter = struct {
757734 coff: *Coff,
758735
......@@ -6316,7 +6293,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
63166293 switch (addr_info.magic) {
63176294 _ => unreachable,
63186295 inline .PE32, .@"PE32+" => |ct_magic| {
6319 const Entry = ImportTable.TableEntry(ct_magic);
6296 const Entry = std.coff.ImportLookupTableEntry(ct_magic);
63206297 const import_lookup_table: []Entry = @ptrCast(@alignCast(import_lookup_slice));
63216298 const import_address_table: []Entry = @ptrCast(@alignCast(import_address_slice));
63226299 const import_hint_name_rvas: [2]Entry = .{
......@@ -6675,7 +6652,7 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
66756652 switch (magic) {
66766653 _ => unreachable,
66776654 inline .PE32, .@"PE32+" => |ct_magic| {
6678 const Entry = ImportTable.TableEntry(ct_magic);
6655 const Entry = std.coff.ImportLookupTableEntry(ct_magic);
66796656 const import_lookup_table: []Entry = @ptrCast(@alignCast(import_lookup_slice));
66806657 const import_address_table: []Entry = @ptrCast(@alignCast(import_address_slice));
66816658