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
logb2eee06bd67e720014809ade89b0b054865b0040
tree5a062970ac062de6c15c6553fe723aac7bd92e69
parent97bf880486c6bd0811f675c61d7675632fd1722d

objdump: coff --tls


2 files changed, 145 insertions(+), 15 deletions(-)

lib/compiler/objdump.zig+115-15
......@@ -535,15 +535,21 @@ const coff = struct {
535535 opts.imports or
536536 opts.tls;
537537
538 const data_dirs, const magic = if (header.size_of_optional_header > 0) optional_header: {
538 const ImageInfo = struct {
539 data_dirs: []const std.coff.ImageDataDirectory,
540 magic: std.coff.OptionalHeader.Magic,
541 image_base: u64,
542 };
543
544 const image_info: ?ImageInfo = if (header.size_of_optional_header > 0) image_info: {
539545 if (!opts.file_headers and !needs_data_dirs) {
540546 try fr.seekBy(header.size_of_optional_header);
541 break :optional_header .{ &.{}, null };
547 break :image_info null;
542548 }
543549
544550 if (opts.file_headers) try w.writeAll("COFF Optional Header:\n");
545551 const magic: std.coff.OptionalHeader.Magic = @enumFromInt(try r.peekInt(u16, .little));
546 const num_directory_entries = switch (magic) {
552 const num_directory_entries, const image_base = switch (magic) {
547553 inline .PE32, .@"PE32+" => |v| num_data_dirs: {
548554 const OptionalHeader = if (v == .PE32)
549555 std.coff.OptionalHeader.PE32
......@@ -593,7 +599,10 @@ const coff = struct {
593599 try w.writeByte('\n');
594600 }
595601
596 break :num_data_dirs optional_header.number_of_rva_and_sizes;
602 break :num_data_dirs .{
603 optional_header.number_of_rva_and_sizes,
604 optional_header.image_base,
605 };
597606 },
598607 else => return failParse(opts, "invalid optional header magic number: {x}", .{magic}),
599608 };
......@@ -614,10 +623,14 @@ const coff = struct {
614623 }
615624 if (opts.file_headers) try w.writeByte('\n');
616625
617 break :optional_header .{ known_dirs[0..@min(known_dirs.len, num_directory_entries)], magic };
626 break :image_info .{
627 .data_dirs = known_dirs[0..@min(known_dirs.len, num_directory_entries)],
628 .magic = magic,
629 .image_base = image_base,
630 };
618631 } else if (is_image) {
619632 return failParse(opts, "image did not contain an optional header", .{});
620 } else .{ &.{}, null };
633 } else null;
621634
622635 // Section names in images don't use the string table, as they must fit inline in the header
623636 const load_string_table = (opts.strings or !is_image) and header.pointer_to_symbol_table > 0;
......@@ -1013,7 +1026,7 @@ const coff = struct {
10131026 defer gpa.free(rva_index);
10141027
10151028 if (opts.exports) {
1016 if (try seekToDataDirectory(opts, fr, w, rva_index, sections.items, data_dirs, .EXPORT)) |section_index| {
1029 if (try seekToDataDirectory(opts, fr, w, rva_index, sections.items, image_info.?.data_dirs, .EXPORT)) |section_index| {
10171030 const export_dir = r.takeStruct(std.coff.ExportDirectoryTable, .little) catch |err|
10181031 return failParse(opts, "unable to read export directory: {t}", .{err});
10191032
......@@ -1056,7 +1069,7 @@ const coff = struct {
10561069
10571070 // All the variable length fields should be contained within this directory.
10581071 // Read it entirely to avoid needing to seek per-name when iterating.
1059 const dir = data_dirs[@intFromEnum(DIRECTORY_ENTRY.EXPORT)];
1072 const dir = image_info.?.data_dirs[@intFromEnum(DIRECTORY_ENTRY.EXPORT)];
10601073 const dir_end_rva = dir.virtual_address + dir.size;
10611074 const dir_loc = fr.logicalPos();
10621075 const dir_slice = try r.readAlloc(gpa, dir.size);
......@@ -1097,7 +1110,15 @@ const coff = struct {
10971110 }
10981111
10991112 if (opts.imports) {
1100 if (try seekToDataDirectory(opts, fr, w, rva_index, sections.items, data_dirs, .IMPORT)) |_| {
1113 if (try seekToDataDirectory(
1114 opts,
1115 fr,
1116 w,
1117 rva_index,
1118 sections.items,
1119 image_info.?.data_dirs,
1120 .IMPORT,
1121 )) |_| {
11011122 const Entry = std.coff.ImportDirectoryEntry;
11021123 var directory_entries: std.ArrayList(Entry) = .empty;
11031124 defer directory_entries.deinit(gpa);
......@@ -1114,14 +1135,20 @@ const coff = struct {
11141135 }
11151136
11161137 for (directory_entries.items) |entry| {
1117 const name_section = sectionContainingRva(rva_index, sections.items, entry.name_rva) orelse
1138 const name_section = sectionContainingRva(
1139 rva_index,
1140 sections.items,
1141 entry.name_rva,
1142 ) orelse
11181143 return failParse(
11191144 opts,
11201145 "import directory entry name rva 0x{x} was not found in any section",
11211146 .{entry.name_rva},
11221147 );
11231148
1124 const name_loc = sections.items[name_section].rvaFileOffset(entry.name_rva) catch unreachable;
1149 const name_loc = sections.items[name_section].rvaFileOffset(
1150 entry.name_rva,
1151 ) catch unreachable;
11251152 fr.seekTo(name_loc) catch |err|
11261153 return failParse(
11271154 opts,
......@@ -1161,7 +1188,7 @@ const coff = struct {
11611188 .{ ilt_loc, err },
11621189 );
11631190
1164 switch (magic.?) {
1191 switch (image_info.?.magic) {
11651192 _ => try w.writeAll("(unknown magic)"),
11661193 inline else => |m| {
11671194 const TableEntry = std.coff.ImportLookupTableEntry(m);
......@@ -1230,8 +1257,79 @@ const coff = struct {
12301257 }
12311258
12321259 if (opts.tls) {
1233 if (try seekToDataDirectory(opts, fr, w, rva_index, sections.items, data_dirs, .TLS)) |_| {
1234 // TODO
1260 if (try seekToDataDirectory(
1261 opts,
1262 fr,
1263 w,
1264 rva_index,
1265 sections.items,
1266 image_info.?.data_dirs,
1267 .TLS,
1268 )) |_| {
1269 switch (image_info.?.magic) {
1270 _ => try w.writeAll("(unknown magic)"),
1271 inline else => |m| {
1272 const TlsDirectoryEntry = std.coff.TlsDirectoryEntry(m);
1273 const tls_entry = r.takeStruct(TlsDirectoryEntry, .little) catch |err|
1274 return failParse(opts, "unable to read tls directory: {t}", .{err});
1275
1276 try w.writeAll("TLS Directory:\n");
1277 try dumpHeader(w, TlsDirectoryEntry, &tls_entry, struct {});
1278
1279 try w.writeAll(" | ");
1280 if (tls_entry.characteristics.alignment == .NONE) {
1281 try w.writeAll("Alignment not specified");
1282 } else {
1283 try w.print(
1284 "Alignment: {d}",
1285 .{tls_entry.characteristics.alignment.toByteUnits().?},
1286 );
1287 }
1288
1289 try w.writeAll(
1290 \\
1291 \\
1292 \\TLS Callbacks:
1293 \\ Address
1294 \\
1295 );
1296
1297 const callbacks_rva: u32 = @intCast(tls_entry.callbacks_va - image_info.?.image_base);
1298 const section_index = sectionContainingRva(
1299 rva_index,
1300 sections.items,
1301 callbacks_rva,
1302 ) orelse
1303 return failParse(
1304 opts,
1305 "tls callbacks rva 0x{x} was not found in any section",
1306 .{callbacks_rva},
1307 );
1308
1309 const callbacks_loc = sections.items[section_index]
1310 .rvaFileOffset(callbacks_rva) catch unreachable;
1311
1312 fr.seekTo(callbacks_loc) catch |err|
1313 return failParse(
1314 opts,
1315 "unable to seek to tls callbacks array at offset 0x{x}: {t}",
1316 .{ callbacks_loc, err },
1317 );
1318
1319 while (true) {
1320 const callback_va = r.takeInt(@FieldType(TlsDirectoryEntry, "callbacks_va"), .little) catch |err|
1321 return failParse(
1322 opts,
1323 "unable to read tls callbacks array: {t}",
1324 .{err},
1325 );
1326
1327 try w.print("{x: >16} \n", .{callback_va});
1328 if (callback_va == 0) break;
1329 }
1330 try w.writeByte('\n');
1331 },
1332 }
12351333 }
12361334 }
12371335 }
......@@ -1245,8 +1343,10 @@ const coff = struct {
12451343 data_dirs: []const std.coff.ImageDataDirectory,
12461344 entry: DIRECTORY_ENTRY,
12471345 ) !?u16 {
1248 if (@intFromEnum(entry) < data_dirs.len) {
1346 if (@intFromEnum(entry) < data_dirs.len) blk: {
12491347 const rva = data_dirs[@intFromEnum(entry)].virtual_address;
1348 if (rva == 0) break :blk;
1349
12501350 const section_index = sectionContainingRva(rva_index, sections, rva) orelse
12511351 return failParse(
12521352 opts,
lib/std/coff.zig+30
......@@ -373,6 +373,36 @@ pub const DebugType = enum(u32) {
373373 _,
374374};
375375
376pub fn TlsDirectoryEntry(comptime magic: std.coff.OptionalHeader.Magic) type {
377 return switch (magic) {
378 _ => comptime unreachable,
379 .PE32 => extern struct {
380 raw_data_start_va: u32,
381 raw_data_end_va: u32,
382 tls_index_va: u32,
383 callbacks_va: u32,
384 size_of_zero_fill: u32,
385 characteristics: packed struct(u32) {
386 _reserved_0: u19,
387 alignment: SectionHeader.Flags.Align,
388 _reserved_1: u9,
389 },
390 },
391 .@"PE32+" => extern struct {
392 raw_data_start_va: u64,
393 raw_data_end_va: u64,
394 tls_index_va: u64,
395 callbacks_va: u64,
396 size_of_zero_fill: u32,
397 characteristics: packed struct(u32) {
398 _reserved_0: u19,
399 alignment: SectionHeader.Flags.Align,
400 _reserved_1: u9,
401 },
402 },
403 };
404}
405
376406pub const ImportDirectoryEntry = extern struct {
377407 /// The RVA of the import lookup table.
378408 /// This table contains a name or ordinal for each import.