authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-05 01:55:35-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:26:56-04:00
log769b5eaf9415f25cd68d6cd525614373f162ea90
tree0c02d44837b5515a94e29abcb4f7d585c32c32d4
parent303521cd14e17099be7aade312e83ea539c67b42

Coff: fixes to special symbols, more debug output

- Change flushSpecialSymbol into a state machine, since referencing entry points may pull in tls from the crt - Output symbol / section table with --debug-link-snapshot

1 files changed, 184 insertions(+), 104 deletions(-)

src/link/Coff.zig+184-104
...@@ -49,7 +49,7 @@ input_sections: std.ArrayList(Node.InputSection),...@@ -49,7 +49,7 @@ input_sections: std.ArrayList(Node.InputSection),
49input_section_pending_index: u32,49input_section_pending_index: u32,
50inputs_complete: bool,50inputs_complete: bool,
51exports_complete: bool,51exports_complete: bool,
52special_symbols_complete: bool,52pending_special_symbol: SpecialSymbol,
53strings: std.HashMapUnmanaged(53strings: std.HashMapUnmanaged(
54 u32,54 u32,
55 void,55 void,
...@@ -790,6 +790,7 @@ pub const ImportTable = struct {...@@ -790,6 +790,7 @@ pub const ImportTable = struct {
790};790};
791791
792pub const String = enum(u32) {792pub const String = enum(u32) {
793 // TODO: Re-order
793 @".data" = 0,794 @".data" = 0,
794 @".idata" = 6,795 @".idata" = 6,
795 @".rdata" = 13,796 @".rdata" = 13,
...@@ -802,6 +803,7 @@ pub const String = enum(u32) {...@@ -802,6 +803,7 @@ pub const String = enum(u32) {
802 @".dtors$ZZZ" = 64,803 @".dtors$ZZZ" = 64,
803 @".bss" = 75,804 @".bss" = 75,
804 @".fptable" = 80,805 @".fptable" = 80,
806 @".tls" = 89,
805 _,807 _,
806808
807 pub const Optional = enum(u32) {809 pub const Optional = enum(u32) {
...@@ -817,6 +819,7 @@ pub const String = enum(u32) {...@@ -817,6 +819,7 @@ pub const String = enum(u32) {
817 @".dtors$ZZZ" = @intFromEnum(String.@".dtors$ZZZ"),819 @".dtors$ZZZ" = @intFromEnum(String.@".dtors$ZZZ"),
818 @".bss" = @intFromEnum(String.@".bss"),820 @".bss" = @intFromEnum(String.@".bss"),
819 @".fptable" = @intFromEnum(String.@".fptable"),821 @".fptable" = @intFromEnum(String.@".fptable"),
822 @".tls" = @intFromEnum(String.@".tls"),
820 none = std.math.maxInt(u32),823 none = std.math.maxInt(u32),
821 _,824 _,
822825
...@@ -892,6 +895,12 @@ pub const WeakExternalStrat = enum(u2) {...@@ -892,6 +895,12 @@ pub const WeakExternalStrat = enum(u2) {
892 }895 }
893};896};
894897
898const SpecialSymbol = enum {
899 entry,
900 tls,
901 none,
902};
903
895pub const Symbol = struct {904pub const Symbol = struct {
896 ni: MappedFile.Node.Index,905 ni: MappedFile.Node.Index,
897 rva: u32,906 rva: u32,
...@@ -1538,7 +1547,7 @@ fn create(...@@ -1538,7 +1547,7 @@ fn create(
1538 .input_section_pending_index = 0,1547 .input_section_pending_index = 0,
1539 .inputs_complete = false,1548 .inputs_complete = false,
1540 .exports_complete = false,1549 .exports_complete = false,
1541 .special_symbols_complete = false,1550 .pending_special_symbol = .entry,
1542 .strings = .empty,1551 .strings = .empty,
1543 .string_bytes = .empty,1552 .string_bytes = .empty,
1544 .section_table = .empty,1553 .section_table = .empty,
...@@ -1718,7 +1727,7 @@ fn initHeaders(...@@ -1718,7 +1727,7 @@ fn initHeaders(
1718 // TLS section1727 // TLS section
1719 if (comp.config.any_non_single_threaded) {1728 if (comp.config.any_non_single_threaded) {
1720 if (!is_image) expected_nodes_len += 1;1729 if (!is_image) expected_nodes_len += 1;
1721 expected_nodes_len += 2;1730 expected_nodes_len += 1;
1722 }1731 }
1723 }1732 }
1724 defer assert(coff.nodes.len == expected_nodes_len);1733 defer assert(coff.nodes.len == expected_nodes_len);
...@@ -2130,10 +2139,11 @@ fn initHeaders(...@@ -2130,10 +2139,11 @@ fn initHeaders(
2130 });2139 });
21312140
2132 // While tls variables allocated at runtime are writable, the template itself is not.2141 // While tls variables allocated at runtime are writable, the template itself is not.
2133 // In images, this call triggers the creation of a .tls pseudo section in .rdata.2142 // In images, the template is in a .tls pseudo section in .rdata.
2134 // In objects / archives, this section is part of the above .tls$ section.2143 // In objects / archives, this section is part of the above .tls$ section. The suffix
2135 _ = try coff.objectSectionMapIndex(2144 // is maintained so merging can occur with other input tls symbols when linked later.
2136 .@".tls$",2145 _ = try coff.pseudoSectionMapIndex(
2146 if (is_image) .@".tls" else .@".tls$",
2137 coff.mf.flags.block_size,2147 coff.mf.flags.block_size,
2138 .{ .read = true, .write = !is_image, .initialized = true },2148 .{ .read = true, .write = !is_image, .initialized = true },
2139 );2149 );
...@@ -5622,15 +5632,15 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -5622,15 +5632,15 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
5622 }) coff.late_globals_pending_index += 1;5632 }) coff.late_globals_pending_index += 1;
5623 break :task;5633 break :task;
5624 }5634 }
5625 if (coff.exports_complete and !coff.special_symbols_complete) {5635 if (coff.exports_complete and coff.pending_special_symbol != .none) {
5626 coff.special_symbols_complete = true;5636 coff.pending_special_symbol = coff.flushSpecialSymbol(coff.pending_special_symbol) catch |err|
5627 coff.flushSpecialSymbols() catch |err| switch (err) {5637 switch (err) {
5628 error.OutOfMemory => |e| return e,5638 error.OutOfMemory => |e| return e,
5629 else => |e| return comp.link_diags.fail(5639 else => |e| return comp.link_diags.fail(
5630 "linker failed to flush special symbols: {t}",5640 "linker failed to flush special symbols: {t}",
5631 .{e},5641 .{e},
5632 ),5642 ),
5633 };5643 };
5634 break :task;5644 break :task;
5635 }5645 }
5636 var lazy_it = coff.lazy.iterator();5646 var lazy_it = coff.lazy.iterator();
...@@ -5773,7 +5783,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -5773,7 +5783,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
5773 if (coff.inputs_complete and coff.globals.count() > coff.global_pending_index) return true;5783 if (coff.inputs_complete and coff.globals.count() > coff.global_pending_index) return true;
5774 assert(!coff.exports_complete or coff.inputs_complete);5784 assert(!coff.exports_complete or coff.inputs_complete);
5775 if (coff.exports_complete and coff.late_globals.items.len > coff.late_globals_pending_index) return true;5785 if (coff.exports_complete and coff.late_globals.items.len > coff.late_globals_pending_index) return true;
5776 if (coff.exports_complete and !coff.special_symbols_complete) return true;5786 if (coff.exports_complete and coff.pending_special_symbol != .none) return true;
5777 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;5787 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
5778 if (coff.symbol_table.pending.count() > 0) return true;5788 if (coff.symbol_table.pending.count() > 0) return true;
5779 if (coff.input_sections.items.len > coff.input_section_pending_index) return true;5789 if (coff.input_sections.items.len > coff.input_section_pending_index) return true;
...@@ -6318,103 +6328,116 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -6318,103 +6328,116 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
6318 return true;6328 return true;
6319}6329}
63206330
6321fn flushSpecialSymbols(coff: *Coff) !void {6331fn flushSpecialSymbol(coff: *Coff, pending: SpecialSymbol) !SpecialSymbol {
6322 const comp = coff.base.comp;6332 const comp = coff.base.comp;
6323 const gpa = comp.gpa;6333 const gpa = comp.gpa;
6324 const machine = coff.targetLoad(&coff.headerPtr().machine);6334 const machine = coff.targetLoad(&coff.headerPtr().machine);
63256335
6326 if (coff.isImage()) {6336 if (!coff.isImage()) return .none;
6327 // TODO: Use explicitly specified entry if set, add err if not found6337 return next: switch (pending) {
6328 const entries: []const struct { ?[]const u8, []const u8 } = if (coff.isExe())6338 .entry => {
6329 if (comp.config.link_libc) switch (coff.optionalHeaderField(.subsystem)) {6339 // TODO: Use explicitly specified entry if set, add err if not found
6330 .WINDOWS_CUI => &.{6340 const entries: []const struct { ?[]const u8, []const u8 } = if (coff.isExe())
6331 .{ "main", "mainCRTStartup" },6341 if (comp.config.link_libc) switch (coff.optionalHeaderField(.subsystem)) {
6332 .{ "wmain", "wmainCRTStartup" },6342 .WINDOWS_CUI => &.{
6333 },6343 .{ "main", "mainCRTStartup" },
6334 .WINDOWS_GUI => &.{6344 .{ "wmain", "wmainCRTStartup" },
6335 .{ "WinMain", "WinMainCRTStartup" },6345 },
6336 .{ "wWinMain", "wWinMainCRTStartup" },6346 .WINDOWS_GUI => &.{
6337 },6347 .{ "WinMain", "WinMainCRTStartup" },
6338 else => unreachable,6348 .{ "wWinMain", "wWinMainCRTStartup" },
6339 } else &.{6349 },
6340 .{ "wWinMainCRTStartup", "wWinMainCRTStartup" },6350 else => unreachable,
6341 }6351 } else &.{
6342 else6352 .{ "wWinMainCRTStartup", "wWinMainCRTStartup" },
6343 &.{.{ null, "_DllMainCRTStartup" }};6353 }
6354 else
6355 &.{.{ null, "_DllMainCRTStartup" }};
63446356
6345 const entry_si = for (entries) |entry| {6357 const entry_si = for (entries) |entry| {
6346 if (entry[0]) |required_name|6358 if (entry[0]) |required_name|
6347 if (coff.getDefinedGlobal(required_name) == .null) continue;6359 if (coff.getDefinedGlobal(required_name) == .null) continue;
63486360
6349 break try coff.globalSymbol(.{ .name = entry[1], .type = .code });6361 break try coff.globalSymbol(.{ .name = entry[1], .type = .code });
6350 } else .null;6362 } else .null;
63516363
6352 if (entry_si != .null) {6364 if (entry_si != .null) {
6353 log.debug(6365 log.debug(
6354 "entry({s}, {d})",6366 "entry({s}, {d})",
6355 .{ entry_si.get(coff).gmi.globalName(coff).name.toSlice(coff), entry_si },6367 .{ entry_si.get(coff).gmi.globalName(coff).name.toSlice(coff), entry_si },
6356 );6368 );
63576369
6358 try coff.symbols.ensureTotalCapacity(gpa, 1);6370 try coff.symbols.ensureUnusedCapacity(gpa, 1);
6359 const optional_hdr_si = coff.addSymbolAssumeCapacity();6371 const optional_hdr_si = coff.addSymbolAssumeCapacity();
6360 const optional_hdr_sym = optional_hdr_si.get(coff);6372 const optional_hdr_sym = optional_hdr_si.get(coff);
6361 optional_hdr_sym.ni = Node.known.optional_header;6373 optional_hdr_sym.ni = Node.known.optional_header;
6362 assert(optional_hdr_sym.loc_relocs == .none);6374 assert(optional_hdr_sym.loc_relocs == .none);
6363 optional_hdr_sym.loc_relocs = @enumFromInt(coff.relocs.items.len);6375 optional_hdr_sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
6364
6365 const optional_hdr = coff.optionalHeaderStandardPtr();
6366 optional_hdr.address_of_entry_point = std.mem.nativeTo(
6367 u32,
6368 entry_si.get(coff).rva,
6369 coff.targetEndian(),
6370 );
63716376
6372 try coff.addReloc(6377 const optional_hdr = coff.optionalHeaderStandardPtr();
6373 optional_hdr_si,6378 optional_hdr.address_of_entry_point = std.mem.nativeTo(
6374 @intFromPtr(&optional_hdr.address_of_entry_point) - @intFromPtr(optional_hdr),6379 u32,
6375 entry_si,6380 entry_si.get(coff).rva,
6376 .{ .known = 0 },6381 coff.targetEndian(),
6377 switch (machine) {6382 );
6378 else => |tag| @panic(@tagName(tag)),
6379 .AMD64 => .{ .AMD64 = .ADDR32NB },
6380 .I386 => .{ .I386 = .DIR32NB },
6381 },
6382 );
6383 }
6384 }
63856383
6386 if (coff.getDefinedGlobal("_tls_used").unwrap()) |tls_used_si| {6384 try coff.addReloc(
6387 const tls_directory = coff.dataDirectoryPtr(.TLS);6385 optional_hdr_si,
6388 tls_directory.* = .{6386 @intFromPtr(&optional_hdr.address_of_entry_point) - @intFromPtr(optional_hdr),
6389 .virtual_address = tls_used_si.get(coff).rva,6387 entry_si,
6390 .size = switch (coff.targetLoad(&coff.optionalHeaderStandardPtr().magic)) {6388 .{ .known = 0 },
6391 _ => unreachable,6389 switch (machine) {
6392 .PE32 => 24,6390 else => |tag| @panic(@tagName(tag)),
6393 .@"PE32+" => 40,6391 .AMD64 => .{ .AMD64 = .ADDR32NB },
6394 },6392 .I386 => .{ .I386 = .DIR32NB },
6395 };6393 },
6396 if (coff.targetEndian() != native_endian)6394 );
6397 std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, tls_directory);6395 }
63986396
6399 try coff.symbols.ensureTotalCapacity(gpa, 1);6397 // Referencing the startup functions may trigger loading the object containing them,
6400 const data_dir_si = coff.addSymbolAssumeCapacity();6398 // we need to wait until that is done before looking for further symbols.
6401 const data_dir_sym = data_dir_si.get(coff);6399 break :next .tls;
6402 data_dir_sym.ni = Node.known.data_directories;6400 },
6403 assert(data_dir_sym.loc_relocs == .none);6401 .tls => {
6404 data_dir_sym.loc_relocs = @enumFromInt(coff.relocs.items.len);6402 if (coff.getDefinedGlobal("_tls_used").unwrap()) |tls_used_si| {
64056403 log.debug("tlsDir({d})", .{tls_used_si});
6406 try coff.addReloc(6404
6407 data_dir_si,6405 const tls_directory = coff.dataDirectoryPtr(.TLS);
6408 @intFromPtr(&tls_directory.virtual_address) - @intFromPtr(coff.dataDirectorySlice().ptr),6406 tls_directory.* = .{
6409 tls_used_si,6407 .virtual_address = tls_used_si.get(coff).rva,
6410 .{ .known = 0 },6408 .size = switch (coff.targetLoad(&coff.optionalHeaderStandardPtr().magic)) {
6411 switch (machine) {6409 _ => unreachable,
6412 else => |tag| @panic(@tagName(tag)),6410 .PE32 => 24,
6413 .AMD64 => .{ .AMD64 = .ADDR32NB },6411 .@"PE32+" => 40,
6414 .I386 => .{ .I386 = .DIR32NB },6412 },
6415 },6413 };
6416 );6414 if (coff.targetEndian() != native_endian)
6417 }6415 std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, tls_directory);
6416
6417 try coff.symbols.ensureUnusedCapacity(gpa, 1);
6418 const data_dir_si = coff.addSymbolAssumeCapacity();
6419 const data_dir_sym = data_dir_si.get(coff);
6420 data_dir_sym.ni = Node.known.data_directories;
6421 assert(data_dir_sym.loc_relocs == .none);
6422 data_dir_sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
6423
6424 try coff.addReloc(
6425 data_dir_si,
6426 @intFromPtr(&tls_directory.virtual_address) - @intFromPtr(coff.dataDirectorySlice().ptr),
6427 tls_used_si,
6428 .{ .known = 0 },
6429 switch (machine) {
6430 else => |tag| @panic(@tagName(tag)),
6431 .AMD64 => .{ .AMD64 = .ADDR32NB },
6432 .I386 => .{ .I386 = .DIR32NB },
6433 },
6434 );
6435 }
6436
6437 break :next .none;
6438 },
6439 .none => unreachable,
6440 };
6418}6441}
64196442
6420fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {6443fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
...@@ -7137,11 +7160,68 @@ fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) !void {...@@ -7137,11 +7160,68 @@ fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) !void {
7137pub fn dump(coff: *Coff, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult {7160pub fn dump(coff: *Coff, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult {
7138 if (coff.dump_snapshot) {7161 if (coff.dump_snapshot) {
7139 try coff.printNode(tid, w, .root, 0);7162 try coff.printNode(tid, w, .root, 0);
7163 try w.writeAll("Section table:\n");
7164 for (coff.section_table.keys(), coff.section_table.values()) |name, sec|
7165 try coff.printSection(w, name, sec.si);
7166 try w.writeAll("Symbol table:\n");
7167 for (1..coff.symbols.items.len) |si|
7168 try coff.printSymbol(w, @enumFromInt(si));
7169
7140 return .enabled;7170 return .enabled;
7141 }7171 }
7142 return .disabled;7172 return .disabled;
7143}7173}
71447174
7175fn printSection(coff: *Coff, w: *Io.Writer, name: String, si: Symbol.Index) !void {
7176 const sym = si.get(coff);
7177 try w.print("{d:0>6}@{d:0>2} {x:08} n{d:0>8} | {s}\n", .{
7178 si,
7179 sym.section_number,
7180 if (sym.flags.value_tag == .size) sym.value.size else 0,
7181 sym.ni,
7182 name.toSlice(coff),
7183 });
7184}
7185
7186fn printSymbol(coff: *Coff, w: *Io.Writer, si: Symbol.Index) !void {
7187 const sym = si.get(coff);
7188 const node = coff.getNode(sym.ni);
7189 try w.print("{d:0>6}@{d:0>2} {x:08} {s} n{d:0>8}+{x:08}:{t: <26} | {x:08} | {f}\n", .{
7190 si,
7191 sym.section_number,
7192 if (sym.flags.value_tag == .size)
7193 @as(u64, sym.value.size)
7194 else if (sym.ni != .none)
7195 sym.ni.location(&coff.mf).resolve(&coff.mf)[1]
7196 else
7197 0,
7198 switch (sym.flags.type) {
7199 .unknown => "u",
7200 .code => "c",
7201 .data => "d",
7202 },
7203 sym.ni,
7204 if (sym.flags.value_tag == .node_offset) sym.value.node_offset else 0,
7205 node,
7206 sym.rva,
7207 fmtGlobalName(coff, sym.gmi),
7208 });
7209}
7210
7211const FmtGlobalName = struct { coff: *Coff, gmi: Node.GlobalMapIndex };
7212
7213fn fmtGlobalName(coff: *Coff, gmi: Node.GlobalMapIndex) std.fmt.Alt(FmtGlobalName, globalNameEscape) {
7214 return .{ .data = .{ .coff = coff, .gmi = gmi } };
7215}
7216
7217fn globalNameEscape(data: FmtGlobalName, w: *std.Io.Writer) std.Io.Writer.Error!void {
7218 if (data.gmi == .none) return;
7219 const gn = data.gmi.globalName(data.coff);
7220 try w.writeAll(gn.name.toSlice(data.coff));
7221 if (gn.lib_name.unwrap()) |lib_name|
7222 try w.print("({s})", .{lib_name.toSlice(data.coff)});
7223}
7224
7145pub fn printNode(7225pub fn printNode(
7146 coff: *Coff,7226 coff: *Coff,
7147 tid: Zcu.PerThread.Id,7227 tid: Zcu.PerThread.Id,