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
logdc8dd5c08a4715c39de2e6ca8f4d4c6c4aa1f96b
treee1a0bce3cc27e9585a40917c61fc63cba7f765e1
parent769b5eaf9415f25cd68d6cd525614373f162ea90

Coff: more debug output

- Track non-global input symbol names, for use in debug output and error messages - Output COMDAT section names where possible

1 files changed, 108 insertions(+), 34 deletions(-)

src/link/Coff.zig+108-34
...@@ -44,7 +44,7 @@ pending_default_libs: std.ArrayList(struct {...@@ -44,7 +44,7 @@ pending_default_libs: std.ArrayList(struct {
44}),44}),
45alternate_names: std.AutoArrayHashMapUnmanaged(String, String),45alternate_names: std.AutoArrayHashMapUnmanaged(String, String),
46input_objects: std.ArrayList(InputObject),46input_objects: std.ArrayList(InputObject),
47input_symbols: std.ArrayList(Symbol.Index),47input_symbols: std.ArrayList(struct { si: Symbol.Index, name: String }),
48input_sections: std.ArrayList(Node.InputSection),48input_sections: std.ArrayList(Node.InputSection),
49input_section_pending_index: u32,49input_section_pending_index: u32,
50inputs_complete: bool,50inputs_complete: bool,
...@@ -300,6 +300,7 @@ pub const Node = union(enum) {...@@ -300,6 +300,7 @@ pub const Node = union(enum) {
300 const InputSection = struct {300 const InputSection = struct {
301 ioi: InputObject.Index,301 ioi: InputObject.Index,
302 si: Symbol.Index,302 si: Symbol.Index,
303 comdat_si: Symbol.Index,
303 file_location: MappedFile.Node.FileLocation,304 file_location: MappedFile.Node.FileLocation,
304 first_li: Node.InputSection.LocalIndex,305 first_li: Node.InputSection.LocalIndex,
305 crc: u32,306 crc: u32,
...@@ -918,9 +919,13 @@ pub const Symbol = struct {...@@ -918,9 +919,13 @@ pub const Symbol = struct {
918 /// Relocations targeting this symbol919 /// Relocations targeting this symbol
919 target_relocs: Reloc.Index,920 target_relocs: Reloc.Index,
920 section_number: SectionNumber,921 section_number: SectionNumber,
921 /// Only used when outputting objects
922 sti: SymbolTable.Index,
923 gmi: Node.GlobalMapIndex,922 gmi: Node.GlobalMapIndex,
923 extra: union {
924 /// Only valid when outputting objects
925 sti: SymbolTable.Index,
926 /// Only valid when .ni == .input_section and .value_tag == .node_offset
927 isli: Node.InputSection.LocalIndex,
928 },
924929
925 pub const DllStorageClass = enum(u2) {930 pub const DllStorageClass = enum(u2) {
926 default,931 default,
...@@ -1062,7 +1067,7 @@ pub const Symbol = struct {...@@ -1062,7 +1067,7 @@ pub const Symbol = struct {
10621067
1063 pub fn flushSymbolTableIndex(si: Symbol.Index, coff: *Coff) void {1068 pub fn flushSymbolTableIndex(si: Symbol.Index, coff: *Coff) void {
1064 const sym = si.get(coff);1069 const sym = si.get(coff);
1065 const index = sym.sti.unwrap() orelse return;1070 const index = sym.extra.sti.unwrap() orelse return;
1066 var ri = sym.target_relocs;1071 var ri = sym.target_relocs;
1067 while (ri != .none) {1072 while (ri != .none) {
1068 const reloc = ri.get(coff);1073 const reloc = ri.get(coff);
...@@ -2496,7 +2501,7 @@ pub fn symbolTableEntryPtr(coff: *Coff, sti: SymbolTable.Index) ?*align(2) std.c...@@ -2496,7 +2501,7 @@ pub fn symbolTableEntryPtr(coff: *Coff, sti: SymbolTable.Index) ?*align(2) std.c
2496}2501}
24972502
2498pub fn symbolTableSectionAuxEntryPtr(coff: *Coff, si: Symbol.Index) *align(2) std.coff.SectionDefinition {2503pub fn symbolTableSectionAuxEntryPtr(coff: *Coff, si: Symbol.Index) *align(2) std.coff.SectionDefinition {
2499 const sti = si.get(coff).sti;2504 const sti = si.get(coff).extra.sti;
2500 const entry = symbolTableEntryPtr(coff, sti).?;2505 const entry = symbolTableEntryPtr(coff, sti).?;
2501 assert(entry.storage_class == .STATIC and entry.number_of_aux_symbols == 1);2506 assert(entry.storage_class == .STATIC and entry.number_of_aux_symbols == 1);
2502 return @ptrCast(@alignCast(symbolTableEntryStoragePtr(coff, sti.unwrap().? + 1)));2507 return @ptrCast(@alignCast(symbolTableEntryStoragePtr(coff, sti.unwrap().? + 1)));
...@@ -2546,8 +2551,8 @@ fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index {...@@ -2546,8 +2551,8 @@ fn addSymbolAssumeCapacity(coff: *Coff) Symbol.Index {
2546 .loc_relocs = .none,2551 .loc_relocs = .none,
2547 .target_relocs = .none,2552 .target_relocs = .none,
2548 .section_number = .UNDEFINED,2553 .section_number = .UNDEFINED,
2549 .sti = .none,
2550 .gmi = .none,2554 .gmi = .none,
2555 .extra = .{ .sti = .none },
2551 };2556 };
2552 return @enumFromInt(coff.symbols.items.len);2557 return @enumFromInt(coff.symbols.items.len);
2553}2558}
...@@ -2973,7 +2978,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void...@@ -2973,7 +2978,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
2973 const sym = si.get(coff);2978 const sym = si.get(coff);
2974 assert(sym.ni != .none or sym.gmi != .none);2979 assert(sym.ni != .none or sym.gmi != .none);
29752980
2976 const entry = coff.symbolTableEntryPtr(sym.sti) orelse entry: {2981 const entry = coff.symbolTableEntryPtr(sym.extra.sti) orelse entry: {
2977 var buf: [15]u8 = undefined;2982 var buf: [15]u8 = undefined;
2978 const symbol_name, const num_aux_symbols: u8, const complex_type: std.coff.ComplexType =2983 const symbol_name, const num_aux_symbols: u8, const complex_type: std.coff.ComplexType =
2979 if (sym.gmi != .none) blk: {2984 if (sym.gmi != .none) blk: {
...@@ -3038,10 +3043,10 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void...@@ -3038,10 +3043,10 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
3038 try coff.symbol_table.ni.resize(&coff.mf, gpa, new_num_symbols * std.coff.Symbol.sizeOf());3043 try coff.symbol_table.ni.resize(&coff.mf, gpa, new_num_symbols * std.coff.Symbol.sizeOf());
30393044
3040 coff.targetStore(&coff.headerPtr().number_of_symbols, new_num_symbols);3045 coff.targetStore(&coff.headerPtr().number_of_symbols, new_num_symbols);
3041 sym.sti = .wrap(old_num_symbols);3046 sym.extra = .{ .sti = .wrap(old_num_symbols) };
3042 si.flushSymbolTableIndex(coff);3047 si.flushSymbolTableIndex(coff);
30433048
3044 const entry = coff.symbolTableEntryPtr(sym.sti).?;3049 const entry = coff.symbolTableEntryPtr(sym.extra.sti).?;
3045 symbol_name.store(coff, &entry.name);3050 symbol_name.store(coff, &entry.name);
30463051
3047 entry.section_number = @enumFromInt(@intFromEnum(sym.section_number));3052 entry.section_number = @enumFromInt(@intFromEnum(sym.section_number));
...@@ -3071,7 +3076,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void...@@ -3071,7 +3076,7 @@ fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void
3071 },3076 },
3072 });3077 });
30733078
3074 log.debug("flushSymbolTableEntry({d}) = {d}", .{ si, sym.sti });3079 log.debug("flushSymbolTableEntry({d}) = {d}", .{ si, sym.extra.sti });
3075}3080}
30763081
3077fn flushInputMember(coff: *Coff, iami: InputArchive.Member.Index) !void {3082fn flushInputMember(coff: *Coff, iami: InputArchive.Member.Index) !void {
...@@ -3467,8 +3472,8 @@ pub fn addReloc(...@@ -3467,8 +3472,8 @@ pub fn addReloc(
3467 else => |loc_sn| sri: {3472 else => |loc_sn| sri: {
3468 // The target may not have a node yet, or it could be an extern that will never3473 // The target may not have a node yet, or it could be an extern that will never
3469 // have a node. In that case, flushGlobal will create the symbol table entry.3474 // have a node. In that case, flushGlobal will create the symbol table entry.
3470 const sti: SymbolTable.Index = if (target.sti != .none)3475 const sti: SymbolTable.Index = if (target.extra.sti != .none)
3471 target.sti3476 target.extra.sti
3472 else if (target.ni != .none) sti: {3477 else if (target.ni != .none) sti: {
3473 try coff.pendingSymbolTableEntry(target_si);3478 try coff.pendingSymbolTableEntry(target_si);
3474 break :sti .none;3479 break :sti .none;
...@@ -4381,6 +4386,10 @@ fn loadObject(...@@ -4381,6 +4386,10 @@ fn loadObject(
4381 },4386 },
4382 .first_li = @enumFromInt(coff.input_symbols.items.len),4387 .first_li = @enumFromInt(coff.input_symbols.items.len),
4383 .crc = section.comdat_crc,4388 .crc = section.comdat_crc,
4389 .comdat_si = if (section.comdat_psi.unwrap()) |psi|
4390 pending_symbols.values()[psi].si
4391 else
4392 .null,
4384 };4393 };
43854394
4386 log.debug(4395 log.debug(
...@@ -4489,6 +4498,9 @@ fn loadObject(...@@ -4489,6 +4498,9 @@ fn loadObject(
4489 .weak_external_aux,4498 .weak_external_aux,
4490 => unreachable,4499 => unreachable,
4491 }4500 }
4501
4502 if (section.comdat_psi.unwrap() == @as(u32, @intCast(i)))
4503 coff.getNode(section.si.get(coff).ni).input_section.inputSection(coff).comdat_si = symbol.si;
4492 }4504 }
44934505
4494 if (symbol.weak_external_psi.unwrap()) |weak_external_i| {4506 if (symbol.weak_external_psi.unwrap()) |weak_external_i| {
...@@ -4610,7 +4622,11 @@ fn loadObject(...@@ -4610,7 +4622,11 @@ fn loadObject(
46104622
4611 if (include_section) {4623 if (include_section) {
4612 assert(coff.getNode(symbol.si.get(coff).ni) == .input_section);4624 assert(coff.getNode(symbol.si.get(coff).ni) == .input_section);
4613 coff.input_symbols.addOneAssumeCapacity().* = symbol.si;4625 symbol.si.get(coff).extra = .{ .isli = @enumFromInt(coff.input_symbols.items.len) };
4626 coff.input_symbols.addOneAssumeCapacity().* = .{
4627 .si = symbol.si,
4628 .name = symbol.name,
4629 };
4614 }4630 }
4615 }4631 }
4616}4632}
...@@ -5450,11 +5466,30 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {...@@ -5450,11 +5466,30 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
5450 .input_section => |isi| {5466 .input_section => |isi| {
5451 const other_ioi = isi.input(coff);5467 const other_ioi = isi.input(coff);
5452 if (loc_sym.gmi == .none) {5468 if (loc_sym.gmi == .none) {
5453 // TODO: We could report non-global names here if we intern them in loadObject5469 const section = isi.inputSection(coff);
5454 err.addNote("referenced by input '{f}{f}'", .{5470 const section_name = coff.getNode(loc_sym.ni.parent(&coff.mf))
5455 other_ioi.path(coff).fmtEscapeString(),5471 .object_section.name(coff).toSlice(coff);
5456 fmtMemberNameString(other_ioi.memberName(coff)),5472
5457 });5473 if (section.comdat_si != .null) {
5474 const comdat_sym = section.comdat_si.get(coff);
5475 const comdat_name = if (comdat_sym.gmi != .none)
5476 comdat_sym.gmi.globalName(coff).name.toSlice(coff)
5477 else
5478 coff.input_symbols.items[@intFromEnum(comdat_sym.extra.isli)].name.toSlice(coff);
5479
5480 err.addNote("referenced by input COMDAT section '{s}={s}' '{f}{f}'", .{
5481 section_name,
5482 comdat_name,
5483 other_ioi.path(coff).fmtEscapeString(),
5484 fmtMemberNameString(other_ioi.memberName(coff)),
5485 });
5486 } else {
5487 err.addNote("referenced by input section '{s}' '{f}{f}'", .{
5488 section_name,
5489 other_ioi.path(coff).fmtEscapeString(),
5490 fmtMemberNameString(other_ioi.memberName(coff)),
5491 });
5492 }
5458 } else {5493 } else {
5459 err.addNote("referenced by input symbol '{s}' from '{f}{f}'", .{5494 err.addNote("referenced by input symbol '{s}' from '{f}{f}'", .{
5460 loc_sym.gmi.globalName(coff).name.toSlice(coff),5495 loc_sym.gmi.globalName(coff).name.toSlice(coff),
...@@ -6558,9 +6593,9 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -6558,9 +6593,9 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
6558 },6593 },
6559 .input_section => |isi| {6594 .input_section => |isi| {
6560 isi.symbol(coff).flushMoved(coff);6595 isi.symbol(coff).flushMoved(coff);
6561 for (coff.input_symbols.items[@intFromEnum(isi.firstSymbol(coff))..]) |si| {6596 for (coff.input_symbols.items[@intFromEnum(isi.firstSymbol(coff))..]) |input_symbol| {
6562 if (si.get(coff).ni != ni) break;6597 if (input_symbol.si.get(coff).ni != ni) break;
6563 si.flushMoved(coff);6598 input_symbol.si.flushMoved(coff);
6564 }6599 }
6565 },6600 },
6566 .import_directory_table => coff.targetStore(6601 .import_directory_table => coff.targetStore(
...@@ -7165,7 +7200,7 @@ pub fn dump(coff: *Coff, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpRe...@@ -7165,7 +7200,7 @@ pub fn dump(coff: *Coff, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpRe
7165 try coff.printSection(w, name, sec.si);7200 try coff.printSection(w, name, sec.si);
7166 try w.writeAll("Symbol table:\n");7201 try w.writeAll("Symbol table:\n");
7167 for (1..coff.symbols.items.len) |si|7202 for (1..coff.symbols.items.len) |si|
7168 try coff.printSymbol(w, @enumFromInt(si));7203 try coff.printSymbol(w, tid, @enumFromInt(si));
71697204
7170 return .enabled;7205 return .enabled;
7171 }7206 }
...@@ -7183,10 +7218,15 @@ fn printSection(coff: *Coff, w: *Io.Writer, name: String, si: Symbol.Index) !voi...@@ -7183,10 +7218,15 @@ fn printSection(coff: *Coff, w: *Io.Writer, name: String, si: Symbol.Index) !voi
7183 });7218 });
7184}7219}
71857220
7186fn printSymbol(coff: *Coff, w: *Io.Writer, si: Symbol.Index) !void {7221fn printSymbol(
7222 coff: *Coff,
7223 w: *Io.Writer,
7224 tid: Zcu.PerThread.Id,
7225 si: Symbol.Index,
7226) !void {
7187 const sym = si.get(coff);7227 const sym = si.get(coff);
7188 const node = coff.getNode(sym.ni);7228 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", .{7229 try w.print("{d:0>6}@{d:0>2} {x:08} {s} {s} n{d:0>8}+{x:08}:{t: <26} | {x:08} ", .{
7190 si,7230 si,
7191 sym.section_number,7231 sym.section_number,
7192 if (sym.flags.value_tag == .size)7232 if (sym.flags.value_tag == .size)
...@@ -7195,6 +7235,12 @@ fn printSymbol(coff: *Coff, w: *Io.Writer, si: Symbol.Index) !void {...@@ -7195,6 +7235,12 @@ fn printSymbol(coff: *Coff, w: *Io.Writer, si: Symbol.Index) !void {
7195 sym.ni.location(&coff.mf).resolve(&coff.mf)[1]7235 sym.ni.location(&coff.mf).resolve(&coff.mf)[1]
7196 else7236 else
7197 0,7237 0,
7238 switch (sym.flags.value_tag) {
7239 .alias_name => "an",
7240 .alias_si => "as",
7241 .node_offset => "no",
7242 .size => "sz",
7243 },
7198 switch (sym.flags.type) {7244 switch (sym.flags.type) {
7199 .unknown => "u",7245 .unknown => "u",
7200 .code => "c",7246 .code => "c",
...@@ -7204,8 +7250,15 @@ fn printSymbol(coff: *Coff, w: *Io.Writer, si: Symbol.Index) !void {...@@ -7204,8 +7250,15 @@ fn printSymbol(coff: *Coff, w: *Io.Writer, si: Symbol.Index) !void {
7204 if (sym.flags.value_tag == .node_offset) sym.value.node_offset else 0,7250 if (sym.flags.value_tag == .node_offset) sym.value.node_offset else 0,
7205 node,7251 node,
7206 sym.rva,7252 sym.rva,
7207 fmtGlobalName(coff, sym.gmi),
7208 });7253 });
7254
7255 if (sym.gmi != .none) {
7256 try w.print("G {f}\n", .{fmtGlobalName(coff, sym.gmi)});
7257 } else {
7258 try w.writeAll("| ");
7259 try coff.printNodeName(w, tid, node);
7260 try w.writeByte('\n');
7261 }
7209}7262}
72107263
7211const FmtGlobalName = struct { coff: *Coff, gmi: Node.GlobalMapIndex };7264const FmtGlobalName = struct { coff: *Coff, gmi: Node.GlobalMapIndex };
...@@ -7222,16 +7275,12 @@ fn globalNameEscape(data: FmtGlobalName, w: *std.Io.Writer) std.Io.Writer.Error!...@@ -7222,16 +7275,12 @@ fn globalNameEscape(data: FmtGlobalName, w: *std.Io.Writer) std.Io.Writer.Error!
7222 try w.print("({s})", .{lib_name.toSlice(data.coff)});7275 try w.print("({s})", .{lib_name.toSlice(data.coff)});
7223}7276}
72247277
7225pub fn printNode(7278fn printNodeName(
7226 coff: *Coff,7279 coff: *Coff,
7280 w: *std.Io.Writer,
7227 tid: Zcu.PerThread.Id,7281 tid: Zcu.PerThread.Id,
7228 w: *Io.Writer,7282 node: Node,
7229 ni: MappedFile.Node.Index,
7230 indent: usize,
7231) !void {7283) !void {
7232 const node = coff.getNode(ni);
7233 try w.splatByteAll(' ', indent);
7234 try w.writeAll(@tagName(node));
7235 switch (node) {7284 switch (node) {
7236 else => {},7285 else => {},
7237 .image_section => |si| try w.print("({s})", .{7286 .image_section => |si| try w.print("({s})", .{
...@@ -7239,11 +7288,23 @@ pub fn printNode(...@@ -7239,11 +7288,23 @@ pub fn printNode(
7239 }),7288 }),
7240 .input_section => |isi| {7289 .input_section => |isi| {
7241 const ioi = isi.input(coff);7290 const ioi = isi.input(coff);
7242 try w.print("({f}{f}, {s})", .{7291 const is = isi.inputSection(coff);
7292 // TODO: Use only filename from these paths, they are long
7293 try w.print("({f}{f}, {s}", .{
7243 ioi.path(coff).fmtEscapeString(),7294 ioi.path(coff).fmtEscapeString(),
7244 fmtMemberNameString(ioi.memberName(coff)),7295 fmtMemberNameString(ioi.memberName(coff)),
7245 coff.getNode(isi.symbol(coff).node(coff).parent(&coff.mf)).object_section.name(coff).toSlice(coff),7296 coff.getNode(is.si.node(coff).parent(&coff.mf)).object_section.name(coff).toSlice(coff),
7246 });7297 });
7298 if (is.comdat_si != .null) {
7299 const comdat_sym = is.comdat_si.get(coff);
7300 const comdat_name = if (comdat_sym.gmi != .none)
7301 comdat_sym.gmi.globalName(coff).name.toSlice(coff)
7302 else
7303 coff.input_symbols.items[@intFromEnum(comdat_sym.extra.isli)].name.toSlice(coff);
7304
7305 try w.print("={s}", .{comdat_name});
7306 }
7307 try w.writeAll(")");
7247 },7308 },
7248 .import_lookup_table,7309 .import_lookup_table,
7249 .import_address_table,7310 .import_address_table,
...@@ -7284,6 +7345,19 @@ pub fn printNode(...@@ -7284,6 +7345,19 @@ pub fn printNode(
7284 }),7345 }),
7285 }),7346 }),
7286 }7347 }
7348}
7349
7350pub fn printNode(
7351 coff: *Coff,
7352 tid: Zcu.PerThread.Id,
7353 w: *Io.Writer,
7354 ni: MappedFile.Node.Index,
7355 indent: usize,
7356) !void {
7357 const node = coff.getNode(ni);
7358 try w.splatByteAll(' ', indent);
7359 try w.writeAll(@tagName(node));
7360 try coff.printNodeName(w, tid, node);
7287 {7361 {
7288 const mf_node = &coff.mf.nodes.items[@intFromEnum(ni)];7362 const mf_node = &coff.mf.nodes.items[@intFromEnum(ni)];
7289 const off, const size = mf_node.location().resolve(&coff.mf);7363 const off, const size = mf_node.location().resolve(&coff.mf);