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 {...@@ -535,15 +535,21 @@ const coff = struct {
535 opts.imports or535 opts.imports or
536 opts.tls;536 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: {
539 if (!opts.file_headers and !needs_data_dirs) {545 if (!opts.file_headers and !needs_data_dirs) {
540 try fr.seekBy(header.size_of_optional_header);546 try fr.seekBy(header.size_of_optional_header);
541 break :optional_header .{ &.{}, null };547 break :image_info null;
542 }548 }
543549
544 if (opts.file_headers) try w.writeAll("COFF Optional Header:\n");550 if (opts.file_headers) try w.writeAll("COFF Optional Header:\n");
545 const magic: std.coff.OptionalHeader.Magic = @enumFromInt(try r.peekInt(u16, .little));551 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) {
547 inline .PE32, .@"PE32+" => |v| num_data_dirs: {553 inline .PE32, .@"PE32+" => |v| num_data_dirs: {
548 const OptionalHeader = if (v == .PE32)554 const OptionalHeader = if (v == .PE32)
549 std.coff.OptionalHeader.PE32555 std.coff.OptionalHeader.PE32
...@@ -593,7 +599,10 @@ const coff = struct {...@@ -593,7 +599,10 @@ const coff = struct {
593 try w.writeByte('\n');599 try w.writeByte('\n');
594 }600 }
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 };
597 },606 },
598 else => return failParse(opts, "invalid optional header magic number: {x}", .{magic}),607 else => return failParse(opts, "invalid optional header magic number: {x}", .{magic}),
599 };608 };
...@@ -614,10 +623,14 @@ const coff = struct {...@@ -614,10 +623,14 @@ const coff = struct {
614 }623 }
615 if (opts.file_headers) try w.writeByte('\n');624 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 };
618 } else if (is_image) {631 } else if (is_image) {
619 return failParse(opts, "image did not contain an optional header", .{});632 return failParse(opts, "image did not contain an optional header", .{});
620 } else .{ &.{}, null };633 } else null;
621634
622 // Section names in images don't use the string table, as they must fit inline in the header635 // Section names in images don't use the string table, as they must fit inline in the header
623 const load_string_table = (opts.strings or !is_image) and header.pointer_to_symbol_table > 0;636 const load_string_table = (opts.strings or !is_image) and header.pointer_to_symbol_table > 0;
...@@ -1013,7 +1026,7 @@ const coff = struct {...@@ -1013,7 +1026,7 @@ const coff = struct {
1013 defer gpa.free(rva_index);1026 defer gpa.free(rva_index);
10141027
1015 if (opts.exports) {1028 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| {
1017 const export_dir = r.takeStruct(std.coff.ExportDirectoryTable, .little) catch |err|1030 const export_dir = r.takeStruct(std.coff.ExportDirectoryTable, .little) catch |err|
1018 return failParse(opts, "unable to read export directory: {t}", .{err});1031 return failParse(opts, "unable to read export directory: {t}", .{err});
10191032
...@@ -1056,7 +1069,7 @@ const coff = struct {...@@ -1056,7 +1069,7 @@ const coff = struct {
10561069
1057 // All the variable length fields should be contained within this directory.1070 // All the variable length fields should be contained within this directory.
1058 // Read it entirely to avoid needing to seek per-name when iterating.1071 // 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)];
1060 const dir_end_rva = dir.virtual_address + dir.size;1073 const dir_end_rva = dir.virtual_address + dir.size;
1061 const dir_loc = fr.logicalPos();1074 const dir_loc = fr.logicalPos();
1062 const dir_slice = try r.readAlloc(gpa, dir.size);1075 const dir_slice = try r.readAlloc(gpa, dir.size);
...@@ -1097,7 +1110,15 @@ const coff = struct {...@@ -1097,7 +1110,15 @@ const coff = struct {
1097 }1110 }
10981111
1099 if (opts.imports) {1112 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 )) |_| {
1101 const Entry = std.coff.ImportDirectoryEntry;1122 const Entry = std.coff.ImportDirectoryEntry;
1102 var directory_entries: std.ArrayList(Entry) = .empty;1123 var directory_entries: std.ArrayList(Entry) = .empty;
1103 defer directory_entries.deinit(gpa);1124 defer directory_entries.deinit(gpa);
...@@ -1114,14 +1135,20 @@ const coff = struct {...@@ -1114,14 +1135,20 @@ const coff = struct {
1114 }1135 }
11151136
1116 for (directory_entries.items) |entry| {1137 for (directory_entries.items) |entry| {
1117 const name_section = sectionContainingRva(rva_index, sections.items, entry.name_rva) orelse1138 const name_section = sectionContainingRva(
1139 rva_index,
1140 sections.items,
1141 entry.name_rva,
1142 ) orelse
1118 return failParse(1143 return failParse(
1119 opts,1144 opts,
1120 "import directory entry name rva 0x{x} was not found in any section",1145 "import directory entry name rva 0x{x} was not found in any section",
1121 .{entry.name_rva},1146 .{entry.name_rva},
1122 );1147 );
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;
1125 fr.seekTo(name_loc) catch |err|1152 fr.seekTo(name_loc) catch |err|
1126 return failParse(1153 return failParse(
1127 opts,1154 opts,
...@@ -1161,7 +1188,7 @@ const coff = struct {...@@ -1161,7 +1188,7 @@ const coff = struct {
1161 .{ ilt_loc, err },1188 .{ ilt_loc, err },
1162 );1189 );
11631190
1164 switch (magic.?) {1191 switch (image_info.?.magic) {
1165 _ => try w.writeAll("(unknown magic)"),1192 _ => try w.writeAll("(unknown magic)"),
1166 inline else => |m| {1193 inline else => |m| {
1167 const TableEntry = std.coff.ImportLookupTableEntry(m);1194 const TableEntry = std.coff.ImportLookupTableEntry(m);
...@@ -1230,8 +1257,79 @@ const coff = struct {...@@ -1230,8 +1257,79 @@ const coff = struct {
1230 }1257 }
12311258
1232 if (opts.tls) {1259 if (opts.tls) {
1233 if (try seekToDataDirectory(opts, fr, w, rva_index, sections.items, data_dirs, .TLS)) |_| {1260 if (try seekToDataDirectory(
1234 // TODO1261 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 }
1235 }1333 }
1236 }1334 }
1237 }1335 }
...@@ -1245,8 +1343,10 @@ const coff = struct {...@@ -1245,8 +1343,10 @@ const coff = struct {
1245 data_dirs: []const std.coff.ImageDataDirectory,1343 data_dirs: []const std.coff.ImageDataDirectory,
1246 entry: DIRECTORY_ENTRY,1344 entry: DIRECTORY_ENTRY,
1247 ) !?u16 {1345 ) !?u16 {
1248 if (@intFromEnum(entry) < data_dirs.len) {1346 if (@intFromEnum(entry) < data_dirs.len) blk: {
1249 const rva = data_dirs[@intFromEnum(entry)].virtual_address;1347 const rva = data_dirs[@intFromEnum(entry)].virtual_address;
1348 if (rva == 0) break :blk;
1349
1250 const section_index = sectionContainingRva(rva_index, sections, rva) orelse1350 const section_index = sectionContainingRva(rva_index, sections, rva) orelse
1251 return failParse(1351 return failParse(
1252 opts,1352 opts,
lib/std/coff.zig+30
...@@ -373,6 +373,36 @@ pub const DebugType = enum(u32) {...@@ -373,6 +373,36 @@ pub const DebugType = enum(u32) {
373 _,373 _,
374};374};
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
376pub const ImportDirectoryEntry = extern struct {406pub const ImportDirectoryEntry = extern struct {
377 /// The RVA of the import lookup table.407 /// The RVA of the import lookup table.
378 /// This table contains a name or ordinal for each import.408 /// This table contains a name or ordinal for each import.