authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-05 01:55:34-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:22:41-04:00
log0bfa6e41e9654d35d79ce4b617180a86eb5a2c3d
treec4ee01c0e351c8543f6f67773bf0f1d1e3229638
parent647fe54ef0843018bfc8416ae5f5f46e6bf2d78c

Coff: More progress on archives

- Move flushing symbol table entries to an idle task - Add progress nodes - Wire up --debug-link-snapshot

1 files changed, 225 insertions(+), 135 deletions(-)

src/link/Coff.zig+225-135
......@@ -39,6 +39,7 @@ strings: std.HashMapUnmanaged(
3939),
4040string_bytes: std.ArrayList(u8),
4141section_table: std.ArrayList(Section),
42tls_si: Symbol.Index,
4243pseudo_section_table: std.array_hash_map.Auto(String, Symbol.Index),
4344object_section_table: std.array_hash_map.Auto(String, Symbol.Index),
4445symbols: std.ArrayList(Symbol),
......@@ -56,6 +57,9 @@ pending_uavs: std.array_hash_map.Auto(Node.UavMapIndex, struct {
5657relocs: std.ArrayList(Reloc),
5758const_prog_node: std.Progress.Node,
5859synth_prog_node: std.Progress.Node,
60symbol_prog_node: std.Progress.Node,
61member_prog_node: std.Progress.Node,
62dump_snapshot: bool,
5963
6064pub const default_file_alignment: u16 = 0x200;
6165pub const default_size_of_stack_reserve: u32 = 0x1000000;
......@@ -158,7 +162,6 @@ pub const Node = union(enum) {
158162 section_table,
159163 // Archives and objects only
160164 symbol_table,
161 symbol_table_entry,
162165 // Archives and objects only
163166 string_table,
164167 // Archives and objects only
......@@ -314,8 +317,6 @@ pub const Node = union(enum) {
314317 optional_header,
315318 data_directories,
316319 section_table,
317 symbol_table,
318 string_table,
319320 };
320321 var mut_known: std.enums.EnumFieldStruct(Known, MappedFile.Node.Index, null) = undefined;
321322 const info = @typeInfo(Known).@"enum";
......@@ -464,20 +465,18 @@ pub const LongNamesTable = struct {
464465};
465466
466467pub const SymbolTable = struct {
468 ni: MappedFile.Node.Index,
469 strings_ni: MappedFile.Node.Index,
467470 strings: std.AutoArrayHashMapUnmanaged(String, StringIndex),
471 pending: std.AutoArrayHashMapUnmanaged(Symbol.Index, void),
468472
469 // Adding nodes to the symbol table has the result of accumulating padding
473 // Resizing the symbol table node has the result of accumulating padding
470474 // between the last symbol in the symbol table node and the start of the
471 // string table node, due to the growth factor in MappedFile.
475 // string table node, due to the shifting method when resizing the parent in MappedFile.
472476 // The spec requires the string table begin immediately after the last symbol,
473 // so we compact the symbol table node if needed.
477 // so we compact the symbol table node and move the string table back if needed.
474478 pending_shrink: bool,
475479
476 pub const Add = union(enum) {
477 section,
478 global,
479 };
480
481480 pub const StringIndex = enum(u32) {
482481 _,
483482 };
......@@ -1103,12 +1102,16 @@ fn create(
11031102 .entries = .empty,
11041103 },
11051104 .symbol_table = .{
1105 .ni = .none,
1106 .strings_ni = .none,
11061107 .strings = .empty,
1108 .pending = .empty,
11071109 .pending_shrink = false,
11081110 },
11091111 .strings = .empty,
11101112 .string_bytes = .empty,
11111113 .section_table = .empty,
1114 .tls_si = .null,
11121115 .pseudo_section_table = .empty,
11131116 .object_section_table = .empty,
11141117 .symbols = .empty,
......@@ -1124,6 +1127,9 @@ fn create(
11241127 .relocs = .empty,
11251128 .const_prog_node = .none,
11261129 .synth_prog_node = .none,
1130 .symbol_prog_node = .none,
1131 .member_prog_node = .none,
1132 .dump_snapshot = options.enable_link_snapshots,
11271133 };
11281134 errdefer coff.deinit();
11291135
......@@ -1155,6 +1161,7 @@ pub fn deinit(coff: *Coff) void {
11551161 coff.import_table.entries.deinit(gpa);
11561162 coff.export_table.entries.deinit(gpa);
11571163 coff.symbol_table.strings.deinit(gpa);
1164 coff.symbol_table.pending.deinit(gpa);
11581165 coff.strings.deinit(gpa);
11591166 coff.string_bytes.deinit(gpa);
11601167 coff.section_table.deinit(gpa);
......@@ -1222,14 +1229,21 @@ fn initHeaders(
12221229
12231230 var expected_nodes_len: usize = Node.known_count;
12241231 if (comp.zcu != null) {
1225 // Section nodes
1232 // Sections
12261233 expected_nodes_len += 3;
1227 // // Symbol table nodes
1228 // if (is_archive) expected_nodes_len += 6;
1229 // Pseudo-sections and import / export table nodes
1230 if (is_image) expected_nodes_len += 9;
1231 // TLS section nodes
1232 expected_nodes_len += @as(usize, @intFromBool(comp.config.any_non_single_threaded)) * 2;
1234
1235 if (is_image)
1236 // Pseudo-sections and import / export table
1237 expected_nodes_len += 9
1238 else
1239 // Symbol table
1240 expected_nodes_len += 2;
1241
1242 // TLS section
1243 if (comp.config.any_non_single_threaded) {
1244 if (!is_image) expected_nodes_len += 1;
1245 expected_nodes_len += 2;
1246 }
12331247 }
12341248 defer assert(coff.nodes.len == expected_nodes_len);
12351249
......@@ -1486,25 +1500,25 @@ fn initHeaders(
14861500 }));
14871501 coff.nodes.appendAssumeCapacity(.section_table);
14881502
1489 // TODO: These two nodes could be inside one movable node?
1490 const symbol_table_ni = Node.known.symbol_table;
1491 assert(symbol_table_ni == try coff.mf.addLastChildNode(gpa, zcu_coff_parent_ni, .{
1492 .alignment = .@"2",
1493 .fixed = true,
1494 .moved = true,
1495 }));
1496 coff.nodes.appendAssumeCapacity(.symbol_table);
1503 assert(coff.nodes.len == Node.known_count);
14971504
1498 const string_table_ni = Node.known.string_table;
1499 assert(string_table_ni == try coff.mf.addLastChildNode(gpa, zcu_coff_parent_ni, .{
1500 .alignment = .@"2",
1501 .size = if (!is_image) @sizeOf(u32) else 0,
1502 .fixed = true,
1503 .resized = true,
1504 }));
1505 coff.nodes.appendAssumeCapacity(.string_table);
1505 if (!is_image) {
1506 // TODO: These two nodes could be inside one movable node?
1507 coff.symbol_table.ni = try coff.mf.addLastChildNode(gpa, zcu_coff_parent_ni, .{
1508 .alignment = .@"2",
1509 .fixed = true,
1510 .moved = true,
1511 });
1512 coff.nodes.appendAssumeCapacity(.symbol_table);
15061513
1507 assert(coff.nodes.len == Node.known_count);
1514 coff.symbol_table.strings_ni = try coff.mf.addLastChildNode(gpa, zcu_coff_parent_ni, .{
1515 .alignment = .@"2",
1516 .size = @sizeOf(u32),
1517 .fixed = true,
1518 .resized = true,
1519 });
1520 coff.nodes.appendAssumeCapacity(.string_table);
1521 }
15081522
15091523 try coff.symbols.ensureTotalCapacity(gpa, Symbol.Index.known_count);
15101524 coff.symbols.addOneAssumeCapacity().* = .{
......@@ -1618,12 +1632,23 @@ fn initHeaders(
16181632 std.mem.byteSwapAllFields(std.coff.ExportDirectoryTable, export_directory_table);
16191633 }
16201634
1621 // While tls variables allocated at runtime are writable, the template itself is not
1622 if (comp.config.any_non_single_threaded) _ = try coff.objectSectionMapIndex(
1623 .@".tls$",
1624 if (is_image) coff.mf.flags.block_size else .@"1",
1625 .{ .read = true },
1626 );
1635 if (comp.config.any_non_single_threaded) {
1636 if (!is_image)
1637 coff.tls_si = try coff.addSection(".tls$", .{
1638 .CNT_INITIALIZED_DATA = true,
1639 .MEM_READ = true,
1640 .MEM_WRITE = true,
1641 });
1642
1643 // While tls variables allocated at runtime are writable, the template itself is not.
1644 // In images, this call triggers the creation of a .tls pseudo section in .rdata.
1645 // In objects / archives, this section is part of the above .tls$ section.
1646 _ = try coff.objectSectionMapIndex(
1647 .@".tls$",
1648 coff.mf.flags.block_size,
1649 .{ .read = true, .write = !is_image, .tls = true },
1650 );
1651 }
16271652}
16281653
16291654pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {
......@@ -1634,12 +1659,23 @@ pub fn startProgress(coff: *Coff, prog_node: std.Progress.Node) void {
16341659 for (&coff.lazy.values) |*lazy| count += lazy.map.count() - lazy.pending_index;
16351660 break :count count;
16361661 });
1662 if (!isImage(coff)) {
1663 prog_node.increaseEstimatedTotalItems(2);
1664 coff.symbol_prog_node = prog_node.start("Symbols", coff.symbol_table.pending.count());
1665 coff.member_prog_node = prog_node.start("Members", coff.pending_members.count());
1666 }
16371667 coff.mf.update_prog_node = prog_node.start("Relocations", coff.mf.updates.items.len);
16381668}
16391669
16401670pub fn endProgress(coff: *Coff) void {
16411671 coff.mf.update_prog_node.end();
16421672 coff.mf.update_prog_node = .none;
1673 if (!isImage(coff)) {
1674 coff.member_prog_node.end();
1675 coff.member_prog_node = .none;
1676 coff.symbol_prog_node.end();
1677 coff.symbol_prog_node = .none;
1678 }
16431679 coff.synth_prog_node.end();
16441680 coff.synth_prog_node = .none;
16451681 coff.const_prog_node.end();
......@@ -1665,7 +1701,6 @@ fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
16651701 .placeholder,
16661702
16671703 .symbol_table,
1668 .symbol_table_entry,
16691704 .string_table,
16701705 .relocation_table,
16711706 .relocation_table_entry,
......@@ -1858,7 +1893,7 @@ pub fn sectionTableSlice(coff: *Coff) []std.coff.SectionHeader {
18581893pub fn symbolTableEntryStoragePtr(coff: *Coff, index: u32) *[std.coff.Symbol.sizeOf()]u8 {
18591894 assert(!coff.isImage());
18601895 const offset = index * std.coff.Symbol.sizeOf();
1861 return @ptrCast(@alignCast(Node.known.symbol_table.slice(&coff.mf)[offset..][0..std.coff.Symbol.sizeOf()]));
1896 return @ptrCast(@alignCast(coff.symbol_table.ni.slice(&coff.mf)[offset..][0..std.coff.Symbol.sizeOf()]));
18621897}
18631898
18641899pub fn symbolTableEntryPtr(coff: *Coff, sti: SymbolTable.Index) ?*align(2) std.coff.Symbol {
......@@ -1876,7 +1911,7 @@ pub fn symbolTableSectionAuxEntryPtr(coff: *Coff, si: Symbol.Index) *align(2) st
18761911}
18771912
18781913pub fn symbolTableStringLenPtr(coff: *Coff) *align(2) u32 {
1879 return @ptrCast(@alignCast(Node.known.string_table.slice(&coff.mf)[0..@sizeOf(u32)]));
1914 return @ptrCast(@alignCast(coff.symbol_table.strings_ni.slice(&coff.mf)[0..@sizeOf(u32)]));
18801915}
18811916
18821917pub fn importDirectoryTableSlice(coff: *Coff) []std.coff.ImportDirectoryEntry {
......@@ -1971,6 +2006,18 @@ pub fn globalSymbol(coff: *Coff, opts: struct {
19712006 return sym_gop.value_ptr.*;
19722007}
19732008
2009pub fn pendingSymbolTableEntry(coff: *Coff, si: Symbol.Index) !void {
2010 assert(!coff.isImage());
2011 const sym = si.get(coff);
2012 assert(sym.ni != .none or sym.gmi != .none);
2013
2014 const gpa = coff.base.comp.gpa;
2015 const pending_gop = try coff.symbol_table.pending.getOrPut(gpa, si);
2016 if (!pending_gop.found_existing) {
2017 coff.symbol_prog_node.increaseEstimatedTotalItems(1);
2018 }
2019}
2020
19742021fn navSection(
19752022 coff: *Coff,
19762023 zcu: *Zcu,
......@@ -1979,7 +2026,7 @@ fn navSection(
19792026 const ip = &zcu.intern_pool;
19802027 const default: String, const attributes: ObjectSectionAttributes =
19812028 if (nav_resolved.@"threadlocal" and coff.base.comp.config.any_non_single_threaded) .{
1982 .@".tls$", .{ .read = true, .write = true },
2029 .@".tls$", .{ .read = true, .write = true, .tls = true },
19832030 } else if (ip.isFunctionType(nav_resolved.type)) .{
19842031 .@".text", .{ .read = true, .execute = true },
19852032 } else if (nav_resolved.@"const") .{
......@@ -2149,6 +2196,7 @@ fn addMemberAssumeCapacity(coff: *Coff, kind: Member.Kind, size: usize) !Member.
21492196 gpa,
21502197 coff.pending_members.capacity() + 1,
21512198 );
2199 coff.member_prog_node.increaseEstimatedTotalItems(1);
21522200 },
21532201 }
21542202
......@@ -2249,16 +2297,15 @@ fn ensureMemberSymbol(
22492297 }
22502298
22512299 coff.pending_members.putAssumeCapacity(mi, {});
2300 coff.member_prog_node.increaseEstimatedTotalItems(1);
22522301}
22532302
2254// TODO: -> flushSymbolTableEntry, and push all call sites onto a pending list instead?
2255fn updateSymbolTableEntry(coff: *Coff, si: Symbol.Index) !SymbolTable.Index {
2303fn flushSymbolTableEntry(coff: *Coff, si: Symbol.Index, pt: Zcu.PerThread) !void {
22562304 assert(!coff.isImage());
22572305 const gpa = coff.base.comp.gpa;
22582306
22592307 const sym = si.get(coff);
2260 const has_node = sym.ni != .none;
2261 assert(has_node or sym.gmi != .none);
2308 assert(sym.ni != .none or sym.gmi != .none);
22622309
22632310 const entry = coff.symbolTableEntryPtr(sym.sti) orelse entry: {
22642311 var buf: [15]u8 = undefined;
......@@ -2274,14 +2321,14 @@ fn updateSymbolTableEntry(coff: *Coff, si: Symbol.Index) !SymbolTable.Index {
22742321 else
22752322 .NULL,
22762323 };
2277 } else switch (coff.getNode(sym.ni)) {
2324 } else blk: switch (coff.getNode(sym.ni)) {
22782325 .image_section => .{
22792326 &sym.section_number.header(coff).name,
22802327 null,
22812328 1,
22822329 .NULL,
22832330 },
2284 .nav => |nmi| blk: {
2331 .nav => |nmi| {
22852332 const zcu = coff.base.comp.zcu.?;
22862333 const ip = &zcu.intern_pool;
22872334 const nav = ip.getNav(nmi.navIndex(coff));
......@@ -2292,14 +2339,25 @@ fn updateSymbolTableEntry(coff: *Coff, si: Symbol.Index) !SymbolTable.Index {
22922339 if (ip.isFunctionType(nav.resolved.?.type)) .FUNCTION else .NULL,
22932340 };
22942341 },
2295 .uav => |umi| blk: {
2342 .uav => |umi| {
22962343 var w = Io.Writer.fixed(&buf);
22972344 w.print("__anon_{x}", .{umi.uavValue(coff)}) catch unreachable;
22982345 break :blk .{ w.buffered(), null, 0, .NULL };
22992346 },
2347 inline .lazy_code, .lazy_const_data => |mi, tag| {
2348 const lazy_sym = mi.lazySymbol(coff);
2349 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{f}", .{
2350 @tagName(lazy_sym.kind),
2351 Type.fromInterned(lazy_sym.ty).fmt(pt),
2352 });
2353 defer gpa.free(name);
2354
2355 const string = try coff.getOrPutString(name);
2356 break :blk .{ string.toSlice(coff), string, 0, if (tag == .lazy_code) .FUNCTION else .NULL };
2357 },
23002358 else => {
2301 log.err("TODO implement symbol table init for {s}", .{@tagName(coff.getNode(sym.ni))});
2302 return .none;
2359 log.err("TODO implement symbol table init for {s} ({d})", .{ @tagName(coff.getNode(sym.ni)), si });
2360 unreachable;
23032361 },
23042362 };
23052363
......@@ -2307,11 +2365,11 @@ fn updateSymbolTableEntry(coff: *Coff, si: Symbol.Index) !SymbolTable.Index {
23072365 const string = opt_name_string orelse try coff.getOrPutString(name_slice);
23082366 const string_gop = try coff.symbol_table.strings.getOrPut(gpa, string);
23092367 if (!string_gop.found_existing) {
2310 const string_index = Node.known.string_table.location(&coff.mf).resolve(&coff.mf)[1];
2368 const string_index = coff.symbol_table.strings_ni.location(&coff.mf).resolve(&coff.mf)[1];
23112369 string_gop.value_ptr.* = @enumFromInt(string_index);
23122370
2313 try Node.known.string_table.resize(&coff.mf, gpa, string_index + name_slice.len + 1);
2314 const slice = Node.known.string_table.slice(&coff.mf);
2371 try coff.symbol_table.strings_ni.resize(&coff.mf, gpa, string_index + name_slice.len + 1);
2372 const slice = coff.symbol_table.strings_ni.slice(&coff.mf);
23152373 @memcpy(slice[string_index..][0..name_slice.len], name_slice);
23162374 slice[string_index + name_slice.len] = 0;
23172375 }
......@@ -2322,11 +2380,7 @@ fn updateSymbolTableEntry(coff: *Coff, si: Symbol.Index) !SymbolTable.Index {
23222380 const old_num_symbols = coff.targetLoad(&coff.headerPtr().number_of_symbols);
23232381 const new_num_symbols = old_num_symbols + 1 + num_aux_symbols;
23242382
2325 try Node.known.symbol_table.resize(&coff.mf, gpa, new_num_symbols * std.coff.Symbol.sizeOf());
2326
2327 const symbol_table_loc = Node.known.symbol_table.location(&coff.mf).resolve(&coff.mf);
2328 const string_table_loc = Node.known.string_table.location(&coff.mf).resolve(&coff.mf);
2329 coff.symbol_table.pending_shrink = string_table_loc[0] - (symbol_table_loc[0] + symbol_table_loc[1]) > 0;
2383 try coff.symbol_table.ni.resize(&coff.mf, gpa, new_num_symbols * std.coff.Symbol.sizeOf());
23302384
23312385 coff.targetStore(&coff.headerPtr().number_of_symbols, new_num_symbols);
23322386 sym.sti = .wrap(old_num_symbols);
......@@ -2373,8 +2427,6 @@ fn updateSymbolTableEntry(coff: *Coff, si: Symbol.Index) !SymbolTable.Index {
23732427 });
23742428
23752429 log.debug("updateSymbolTableEntry({d}) = {d}", .{ si, sym.sti });
2376
2377 return sym.sti;
23782430}
23792431
23802432fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags) !Symbol.Index {
......@@ -2384,6 +2436,7 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags
23842436 try coff.nodes.ensureUnusedCapacity(gpa, 1);
23852437 try coff.section_table.ensureUnusedCapacity(gpa, 1);
23862438 try coff.symbols.ensureUnusedCapacity(gpa, 1);
2439 if (!isImage(coff)) try coff.symbol_table.pending.ensureUnusedCapacity(gpa, 1);
23872440
23882441 const coff_header = coff.headerPtr();
23892442 const section_index = coff.targetLoad(&coff_header.number_of_sections);
......@@ -2457,7 +2510,7 @@ fn addSection(coff: *Coff, name: []const u8, flags: std.coff.SectionHeader.Flags
24572510 ),
24582511 }
24592512 } else {
2460 assert(try coff.updateSymbolTableEntry(si) != .none);
2513 try coff.pendingSymbolTableEntry(si);
24612514 }
24622515
24632516 return si;
......@@ -2472,7 +2525,9 @@ const ObjectSectionAttributes = packed struct {
24722525 nocache: bool = false,
24732526 discard: bool = false,
24742527 remove: bool = false,
2528 tls: bool = false,
24752529};
2530
24762531fn pseudoSectionMapIndex(
24772532 coff: *Coff,
24782533 name: String,
......@@ -2485,6 +2540,8 @@ fn pseudoSectionMapIndex(
24852540 if (!pseudo_section_gop.found_existing) {
24862541 const parent: Symbol.Index = if (attributes.execute)
24872542 .text
2543 else if (attributes.tls and coff.tls_si != .null)
2544 coff.tls_si
24882545 else if (attributes.write)
24892546 .data
24902547 else
......@@ -2556,35 +2613,6 @@ fn objectSectionMapIndex(
25562613 return osmi;
25572614}
25582615
2559fn ensureUnusedRelocCapacity(coff: *Coff, loc_si: Symbol.Index, len: usize) !void {
2560 const gpa = coff.base.comp.gpa;
2561
2562 try coff.relocs.ensureUnusedCapacity(gpa, len);
2563 if (isImage(coff)) return;
2564
2565 switch (loc_si.get(coff).section_number) {
2566 .UNDEFINED, .ABSOLUTE, .DEBUG => {},
2567 else => |sn| {
2568 const section = sn.section(coff);
2569 const header = sn.header(coff);
2570 const new_size = (len + coff.targetLoad(&header.number_of_relocations)) * std.coff.Relocation.sizeOf();
2571 if (section.relocation_table_ni == .none) {
2572 // The entry's length in the file is shorter than its @sizeOf
2573 try coff.nodes.ensureUnusedCapacity(gpa, 1);
2574 section.relocation_table_ni = try coff.mf.addLastChildNode(gpa, Node.known.zcu_member, .{
2575 .size = new_size,
2576 .alignment = .@"2",
2577 .moved = true,
2578 .resized = true,
2579 });
2580 coff.nodes.appendAssumeCapacity(.{ .relocation_table = sn });
2581 } else {
2582 try section.relocation_table_ni.resize(&coff.mf, gpa, new_size);
2583 }
2584 },
2585 }
2586}
2587
25882616pub fn addReloc(
25892617 coff: *Coff,
25902618 loc_si: Symbol.Index,
......@@ -2612,10 +2640,10 @@ pub fn addReloc(
26122640 // have a node. In that case, flushGlobal will create the symbol table entry.
26132641 const sti: SymbolTable.Index = if (target.sti != .none)
26142642 target.sti
2615 else if (target.ni != .none)
2616 try updateSymbolTableEntry(coff, target_si)
2617 else
2618 .none;
2643 else if (target.ni != .none) sti: {
2644 try coff.pendingSymbolTableEntry(target_si);
2645 break :sti .none;
2646 } else .none;
26192647
26202648 const section = loc_sn.section(coff);
26212649 const header = loc_sn.header(coff);
......@@ -2714,6 +2742,7 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde
27142742 .none => {
27152743 const sec_si = try coff.navSection(zcu, nav.resolved.?);
27162744 try coff.nodes.ensureUnusedCapacity(gpa, 1);
2745 if (!isImage(coff)) try coff.symbol_table.pending.ensureUnusedCapacity(gpa, 1);
27172746 const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{
27182747 .alignment = zcu.navAlignment(nav_index).toStdMem(),
27192748 .moved = true,
......@@ -2728,8 +2757,8 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde
27282757 const sym = si.get(coff);
27292758 assert(sym.loc_relocs == .none);
27302759 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
2731 if (sym.target_relocs != .none)
2732 _ = try coff.updateSymbolTableEntry(si);
2760 if (!isImage(coff) and sym.target_relocs != .none)
2761 try coff.pendingSymbolTableEntry(si);
27332762
27342763 break :ni sym.ni;
27352764 };
......@@ -2836,6 +2865,7 @@ fn updateFuncInner(
28362865 .none => {
28372866 const sec_si = try coff.navSection(zcu, nav.resolved.?);
28382867 try coff.nodes.ensureUnusedCapacity(gpa, 1);
2868 if (!isImage(coff)) try coff.symbol_table.pending.ensureUnusedCapacity(gpa, 1);
28392869 const mod = zcu.navFileScope(func.owner_nav).mod.?;
28402870 const target = &mod.resolved_target.result;
28412871 const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{
......@@ -2861,8 +2891,8 @@ fn updateFuncInner(
28612891 const sym = si.get(coff);
28622892 assert(sym.loc_relocs == .none);
28632893 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
2864 if (sym.target_relocs != .none)
2865 _ = try coff.updateSymbolTableEntry(si);
2894 if (!isImage(coff) and sym.target_relocs != .none)
2895 try coff.pendingSymbolTableEntry(si);
28662896 break :ni sym.ni;
28672897 };
28682898
......@@ -3015,8 +3045,9 @@ pub fn flush(
30153045 else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),
30163046 };
30173047
3018 coff.dumpStderr(tid) catch |err|
3019 return comp.link_diags.fail("dumping link snapshot failed: {t}", .{err});
3048 if (coff.dump_snapshot)
3049 coff.dumpStderr(tid) catch |err|
3050 return comp.link_diags.fail("dumping link snapshot failed: {t}", .{err});
30203051}
30213052
30223053pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
......@@ -3083,6 +3114,29 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
30833114 };
30843115 break :task;
30853116 };
3117 while (coff.symbol_table.pending.pop()) |pending_si| {
3118 const sym = pending_si.key.get(coff);
3119 const sub_prog_node = coff.idleProgNode(
3120 tid,
3121 coff.symbol_prog_node,
3122 if (sym.ni != .none)
3123 coff.getNode(sym.ni)
3124 else
3125 .{ .global = pending_si.key.get(coff).gmi },
3126 );
3127 defer sub_prog_node.end();
3128 coff.flushSymbolTableEntry(
3129 pending_si.key,
3130 .{ .zcu = comp.zcu.?, .tid = tid },
3131 ) catch |err| switch (err) {
3132 error.OutOfMemory => return error.OutOfMemory,
3133 else => |e| return comp.link_diags.fail(
3134 "linker failed to flush symbol table entry: {t}",
3135 .{e},
3136 ),
3137 };
3138 break :task;
3139 }
30863140 while (coff.mf.updates.pop()) |ni| {
30873141 const clean_moved = ni.cleanMoved(&coff.mf);
30883142 const clean_resized = ni.cleanResized(&coff.mf);
......@@ -3096,22 +3150,39 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
30963150 } else coff.mf.update_prog_node.completeOne();
30973151 }
30983152 while (coff.pending_members.pop()) |pending_mi| {
3099 // TODO: Prog node
3153 const sub_prog_node = coff.idleProgNode(
3154 tid,
3155 coff.symbol_prog_node,
3156 coff.getNode(pending_mi.key.get(coff).content_ni),
3157 );
3158 defer sub_prog_node.end();
31003159 try coff.flushMember(pending_mi.key);
31013160 break :task;
31023161 }
3162 // TODO: This and the next task ideally only run once, as it's wasteful otherwise
31033163 if (coff.export_table.pending_sort) {
3104 // TODO: Prog node
3105 coff.export_table.pending_sort = false;
3164 defer coff.export_table.pending_sort = false;
3165 const sub_prog_node = coff.idleProgNode(
3166 tid,
3167 coff.synth_prog_node,
3168 coff.getNode(coff.export_table.ni),
3169 );
3170 defer sub_prog_node.end();
3171
31063172 coff.flushExportsSort();
31073173 break :task;
31083174 }
3109 // TODO: This and the above task ideally run only once, as it's wasteful otherwise
31103175 if (coff.symbol_table.pending_shrink) {
3111 coff.symbol_table.pending_shrink = false;
3112 // TODO: Prog node
3176 defer coff.symbol_table.pending_shrink = false;
3177 const sub_prog_node = coff.idleProgNode(
3178 tid,
3179 coff.symbol_prog_node,
3180 coff.getNode(coff.symbol_table.ni),
3181 );
3182 defer sub_prog_node.end();
3183
31133184 const number_of_symbols = coff.targetLoad(&coff.headerPtr().number_of_symbols);
3114 Node.known.symbol_table.shrink(
3185 coff.symbol_table.ni.shrink(
31153186 &coff.mf,
31163187 comp.gpa,
31173188 number_of_symbols * std.coff.Symbol.sizeOf(),
......@@ -3119,7 +3190,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
31193190 ) catch |err| switch (err) {
31203191 error.OutOfMemory => return error.OutOfMemory,
31213192 else => |e| return comp.link_diags.fail(
3122 "linker failed to shrink symbol table: {t}",
3193 "linker failed to compact symbol table: {t}",
31233194 .{e},
31243195 ),
31253196 };
......@@ -3129,6 +3200,7 @@ pub fn idle(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
31293200 }
31303201 if (coff.pending_uavs.count() > 0) return true;
31313202 if (coff.globals.count() > coff.global_pending_index) return true;
3203 if (coff.symbol_table.pending.count() > 0) return true;
31323204 for (&coff.lazy.values) |lazy| if (lazy.map.count() > lazy.pending_index) return true;
31333205 if (coff.mf.updates.items.len > 0) return true;
31343206 if (coff.pending_members.count() > 0) return true;
......@@ -3159,6 +3231,7 @@ fn idleProgNode(
31593231 .tid = tid,
31603232 }),
31613233 }) catch &name,
3234 .archive_member => |mi| &mi.get(coff).headerPtr(coff).name,
31623235 }, 0);
31633236}
31643237
......@@ -3182,6 +3255,7 @@ fn flushUav(
31823255 .{ .read = true },
31833256 )).symbol(coff);
31843257 try coff.nodes.ensureUnusedCapacity(gpa, 1);
3258 if (!isImage(coff)) try coff.symbol_table.pending.ensureUnusedCapacity(gpa, 1);
31853259 const sym = si.get(coff);
31863260 const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{
31873261 .alignment = uav_align.toStdMem(),
......@@ -3200,8 +3274,8 @@ fn flushUav(
32003274 const sym = si.get(coff);
32013275 assert(sym.loc_relocs == .none);
32023276 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
3203 if (sym.target_relocs != .none)
3204 _ = try coff.updateSymbolTableEntry(si);
3277 if (!isImage(coff) and sym.target_relocs != .none)
3278 try coff.pendingSymbolTableEntry(si);
32053279
32063280 break :ni sym.ni;
32073281 };
......@@ -3232,7 +3306,8 @@ fn flushGlobal(coff: *Coff, pt: Zcu.PerThread, gmi: Node.GlobalMapIndex) !void {
32323306
32333307 if (!coff.isImage()) {
32343308 const si = gmi.symbol(coff);
3235 assert(try coff.updateSymbolTableEntry(si) != .none);
3309 try coff.pendingSymbolTableEntry(si);
3310
32363311 if (si.get(coff).ni != .none)
32373312 try coff.ensureMemberSymbol(
32383313 gn.name,
......@@ -3429,6 +3504,9 @@ fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
34293504 }
34303505 assert(sym.loc_relocs == .none);
34313506 sym.loc_relocs = @enumFromInt(coff.relocs.items.len);
3507 if (!isImage(coff) and sym.target_relocs != .none)
3508 try coff.pendingSymbolTableEntry(si);
3509
34323510 break :ni sym.ni;
34333511 };
34343512
......@@ -3464,15 +3542,20 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
34643542 .data_directories,
34653543 .section_table,
34663544 .placeholder,
3467 .symbol_table_entry, // TODO: Need to impl this for symbol table updates to work?
3468 .string_table,
3469 => if (!coff.isArchive()) unreachable,
3545 => if (coff.isImage()) unreachable,
34703546 .symbol_table => {
34713547 coff.targetStore(
34723548 &coff.headerPtr().pointer_to_symbol_table,
34733549 @intCast(ni.location(&coff.mf).resolve(&coff.mf)[0]),
34743550 );
34753551 },
3552 .string_table => {
3553 if (!coff.symbol_table.pending_shrink) {
3554 const symbol_table_loc, const symbol_table_size = coff.symbol_table.ni.location(&coff.mf).resolve(&coff.mf);
3555 const string_table_offset, _ = coff.symbol_table.strings_ni.location(&coff.mf).resolve(&coff.mf);
3556 coff.symbol_table.pending_shrink = string_table_offset - (symbol_table_loc + symbol_table_size) > 0;
3557 }
3558 },
34763559 .relocation_table => |sn| {
34773560 coff.targetStore(
34783561 &sn.header(coff).pointer_to_relocations,
......@@ -3621,7 +3704,7 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
36213704}
36223705
36233706fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
3624 _, const size = ni.location(&coff.mf).resolve(&coff.mf);
3707 const offset, const size = ni.location(&coff.mf).resolve(&coff.mf);
36253708 log.debug("flushResized({s}, 0x{x})", .{ @tagName(coff.getNode(ni)), size });
36263709
36273710 switch (coff.getNode(ni)) {
......@@ -3657,7 +3740,7 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
36573740 .archive_member => |mi| {
36583741 const content_ni = mi.get(coff).content_ni;
36593742 const next_ni = content_ni.next(&coff.mf);
3660 const offset, _ = content_ni.location(&coff.mf).resolve(&coff.mf);
3743 const content_offset, _ = content_ni.location(&coff.mf).resolve(&coff.mf);
36613744 const next_offset = switch (next_ni) {
36623745 .none => offset: {
36633746 assert(content_ni.parent(&coff.mf) == Node.known.file);
......@@ -3672,15 +3755,22 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
36723755 };
36733756
36743757 // Not inserting IMAGE_ARCHIVE_PAD `\n` byte here, because we are expanding to full size
3675 Member.storeHeaderDecimalStr(&mi.get(coff).headerPtr(coff).size, next_offset - offset);
3758 Member.storeHeaderDecimalStr(&mi.get(coff).headerPtr(coff).size, next_offset - content_offset);
36763759 },
36773760 .coff_header,
36783761 .optional_header,
36793762 .data_directories,
36803763 => unreachable,
36813764 .section_table => {},
3682 .symbol_table => assert(!coff.isImage()),
3683 .symbol_table_entry => unreachable,
3765 .symbol_table => {
3766 assert(!coff.isImage());
3767 if (!coff.symbol_table.pending_shrink) {
3768 const string_table_offset, _ = coff.symbol_table.strings_ni.location(&coff.mf).resolve(&coff.mf);
3769 coff.symbol_table.pending_shrink =
3770 size > coff.targetLoad(&coff.headerPtr().number_of_symbols) * std.coff.Symbol.sizeOf() or
3771 string_table_offset - (offset + size) > 0;
3772 }
3773 },
36843774 .string_table => {
36853775 assert(!coff.isImage());
36863776 coff.targetStore(coff.symbolTableStringLenPtr(), @intCast(size));
......@@ -3904,17 +3994,18 @@ fn updateExportsInner(
39043994 export_sym.rva = exported_sym.rva;
39053995 export_sym.size = exported_sym.size;
39063996 export_sym.section_number = exported_sym.section_number;
3907 export_si.applyTargetRelocs(coff);
3908 if (@"export".opts.name.eqlSlice("wWinMainCRTStartup", ip)) {
3909 coff.optionalHeaderStandardPtr().address_of_entry_point = exported_sym.rva;
3910 } else if (@"export".opts.name.eqlSlice("_tls_used", ip)) {
3911 const tls_directory = coff.dataDirectoryPtr(.TLS);
3912 tls_directory.* = .{ .virtual_address = exported_sym.rva, .size = exported_sym.size };
3913 if (coff.targetEndian() != native_endian)
3914 std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, tls_directory);
3915 }
3916
3917 if (coff.export_table.ni == .none) continue;
3997 defer export_si.applyTargetRelocs(coff);
3998
3999 if (isImage(coff)) {
4000 if (@"export".opts.name.eqlSlice("wWinMainCRTStartup", ip)) {
4001 coff.optionalHeaderStandardPtr().address_of_entry_point = exported_sym.rva;
4002 } else if (@"export".opts.name.eqlSlice("_tls_used", ip)) {
4003 const tls_directory = coff.dataDirectoryPtr(.TLS);
4004 tls_directory.* = .{ .virtual_address = exported_sym.rva, .size = exported_sym.size };
4005 if (coff.targetEndian() != native_endian)
4006 std.mem.byteSwapAllFields(std.coff.ImageDataDirectory, tls_directory);
4007 }
4008 } else continue;
39184009
39194010 const entries_ctx = ExportTable.Adapter{ .coff = coff };
39204011 const gop = try coff.export_table.entries.getOrPutAdapted(
......@@ -4002,7 +4093,6 @@ fn updateExportsInner(
40024093 gop.value_ptr.si = export_si;
40034094 const reloc = gop.value_ptr.*.export_address_table_ri.get(coff);
40044095 reloc.target = export_si;
4005 export_si.applyTargetRelocs(coff); // TODO: Potentially doing this twice, defer first one?
40064096 }
40074097 }
40084098}