authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-09-05 07:06:21+02:00
committergravatar for kcbanner@noreply.codeberg.orgkcbanner <kcbanner@noreply.codeberg.org> 2026-09-05 07:06:21+02:00
logab30a0b9a0c05c62450639de8653d6695a7ac006
treee5e4ce5776601602c2e9727b4b3b365b2752ae5e
parent1436d488b9d64af6d7087c0070d18df3d23604c5

Coff: Incremental progress (#36706)

Making progress on incremental support in the COFF linker: - Support reloc deletion by replacing them with .ABSOLUTE relocs (actually using the relocs from the free list will come later) - Fixup setting sizes during incremental updates - Fix incorrect modifications to the target_relocs linked list, add clarifying docs - Enable `x86_64-windows-selfhosted` for test-incremental Closes https://codeberg.org/ziglang/zig/issues/31773 Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36706

3 files changed, 60 insertions(+), 37 deletions(-)

lib/compiler/objdump.zig+1
......@@ -1780,4 +1780,5 @@ const usage =
17801780 \\ --strings Display string tables
17811781 \\ --symbols Display symbol tables
17821782 \\ --tls Display TLS information
1783 \\
17831784;
src/link/Coff.zig+52-29
......@@ -904,9 +904,11 @@ pub const Symbol = struct {
904904 weak_external_strat: WeakExternalStrat,
905905 _: u5 = 0,
906906 },
907 /// Relocations contained within this symbol
907 /// The first index of the contiguous range of location relocs for this symbol.
908 /// The list is terminated by a reloc with a different .target than this symbol.
909 /// These are relocations that have a .loc that points to this symbol.
908910 loc_relocs: Reloc.Index,
909 /// Relocations targeting this symbol
911 /// The tail of a linked list of relocations with a .target that points to this symbol.
910912 target_relocs: Reloc.Index,
911913 section_number: SectionNumber,
912914 gmi: Node.GlobalMapIndex,
......@@ -998,8 +1000,20 @@ pub const Symbol = struct {
9981000 };
9991001 }
10001002
1001 pub fn size(sym: *const Symbol) u32 {
1002 return if (sym.flags.extra_tag == .size) sym.extra.size else 0;
1003 pub fn size(sym: *const Symbol, coff: *Coff) u32 {
1004 var size_sym = sym;
1005 while (size_sym.flags.extra_tag == .next_alias_si)
1006 size_sym = size_sym.extra.next_alias_si.get(coff);
1007 if (size_sym.flags.extra_tag != .size) return 0;
1008 return size_sym.extra.size;
1009 }
1010
1011 pub fn setSize(sym: *Symbol, coff: *Coff, new_size: u32) void {
1012 var size_sym = sym;
1013 while (size_sym.flags.extra_tag == .next_alias_si)
1014 size_sym = size_sym.extra.next_alias_si.get(coff);
1015 assert(size_sym.flags.extra_tag == .size);
1016 size_sym.extra.size = new_size;
10031017 }
10041018
10051019 pub const SectionNumber = enum(i16) {
......@@ -1097,7 +1111,7 @@ pub const Symbol = struct {
10971111 assert(reloc.target == si);
10981112 if (reloc.sri.entry(coff, reloc.loc.get(coff).section_number)) |entry|
10991113 coff.targetStore(&entry.symbol_table_index, index);
1100 ri = reloc.next;
1114 ri = reloc.prev;
11011115 }
11021116 }
11031117
......@@ -1126,7 +1140,7 @@ pub const Symbol = struct {
11261140 const reloc = ri.get(coff);
11271141 assert(reloc.target == si);
11281142 try reloc.apply(coff);
1129 ri = reloc.next;
1143 ri = reloc.prev;
11301144 }
11311145 }
11321146
......@@ -1494,24 +1508,25 @@ pub const Reloc = extern struct {
14941508 }
14951509
14961510 pub fn delete(reloc: *Reloc, coff: *Coff) void {
1511 log.debug("deleteReloc({d})", .{reloc - coff.relocs.items.ptr});
14971512 if (reloc.sri != .none) {
1498 // TODO: Need to remove this from the COFF relocation table (maybe removeswap?)
1499 // TODO: If this was the last reloc causing something to be in the symbol table, we should remove
1500 // the symbol table entry (and unset sti). That will require flushSymbolTableIndex on the
1501 // swapped symbol if we exchange indices
1502 @panic("TODO implement symbol table reloc deletions");
1513 const loc_sym = reloc.loc.get(coff);
1514 const entry = reloc.sri.entry(coff, loc_sym.section_number).?;
1515
1516 // On every supported architecture, a reloc type of 0 is .ABSOLUTE, and is a no-op
1517 @memset(std.mem.asBytes(entry), 0);
15031518 }
15041519
15051520 switch (reloc.prev) {
1521 .none => {},
1522 else => |prev| prev.get(coff).next = reloc.next,
1523 }
1524 switch (reloc.next) {
15061525 .none => {
15071526 const target = reloc.target.get(coff);
15081527 assert(target.target_relocs.get(coff) == reloc);
1509 target.target_relocs = reloc.next;
1528 target.target_relocs = reloc.prev;
15101529 },
1511 else => |prev| prev.get(coff).next = reloc.next,
1512 }
1513 switch (reloc.next) {
1514 .none => {},
15151530 else => |next| next.get(coff).prev = reloc.prev,
15161531 }
15171532
......@@ -3264,7 +3279,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32) !void {
32643279 };
32653280
32663281 coff.targetStore(&entry.value, switch (sym.section_number) {
3267 .UNDEFINED => if (entry.storage_class == .WEAK_EXTERNAL) 0 else sym.size(),
3282 .UNDEFINED => if (entry.storage_class == .WEAK_EXTERNAL) 0 else sym.size(coff),
32683283 .ABSOLUTE,
32693284 .DEBUG,
32703285 => unreachable,
......@@ -3731,6 +3746,9 @@ fn addRelocAssumeCapacity(
37313746 } else .none;
37323747
37333748 const sri: Section.RelocationIndex = blk: {
3749 // TODO: Once the API for using free relocs exist, if this is about to consume a
3750 // free reloc, then we can use the existing (cleared) .sri on the reloc
3751
37343752 const section = loc_sn.section(coff);
37353753 const header = loc_sn.header(coff);
37363754 const old_num_relocations = coff.targetLoad(&header.number_of_relocations);
......@@ -3770,8 +3788,8 @@ fn addRelocAssumeCapacity(
37703788
37713789 coff.relocs.addOneAssumeCapacity().* = .{
37723790 .type = @"type",
3773 .prev = .none,
3774 .next = target.target_relocs,
3791 .prev = target.target_relocs,
3792 .next = .none,
37753793 .loc = loc_si,
37763794 .target = target_si,
37773795 .sri = sri,
......@@ -3784,7 +3802,7 @@ fn addRelocAssumeCapacity(
37843802 };
37853803 switch (target.target_relocs) {
37863804 .none => {},
3787 else => |target_ri| target_ri.get(coff).prev = ri,
3805 else => |target_ri| target_ri.get(coff).next = ri,
37883806 }
37893807 target.target_relocs = ri;
37903808}
......@@ -4894,7 +4912,7 @@ fn loadObject(
48944912 symbol.si = global_gop.value_ptr.si;
48954913 if (!global_gop.found_existing or symbol.si.get(coff).ni == .none) {
48964914 const sym = symbol.si.get(coff);
4897 sym.setExtra(.{ .size = @max(sym.size(), size) });
4915 sym.setExtra(.{ .size = @max(sym.size(coff), size) });
48984916 }
48994917 },
49004918 else => unreachable,
......@@ -5483,7 +5501,8 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde
54835501 error.WriteFailed => return nw.err.?,
54845502 else => |e| return e,
54855503 };
5486 si.get(coff).extra.size = @intCast(nw.interface.end);
5504
5505 si.get(coff).setSize(coff, @intCast(nw.interface.end));
54875506 try si.applyLocationRelocs(coff);
54885507 }
54895508
......@@ -5621,7 +5640,8 @@ fn updateFuncInner(
56215640 error.WriteFailed => return nw.err.?,
56225641 else => |e| return e,
56235642 };
5624 si.get(coff).extra.size = @intCast(nw.interface.end);
5643
5644 si.get(coff).setSize(coff, @intCast(nw.interface.end));
56255645 try si.applyLocationRelocs(coff);
56265646
56275647 // The NAV's node is done---now generate any UAVs or lazy code/data which the NAV needs.
......@@ -6214,7 +6234,8 @@ fn genUav(
62146234 error.WriteFailed => return nw.err.?,
62156235 else => |e| return e,
62166236 };
6217 si.get(coff).extra.size = @intCast(nw.interface.end);
6237
6238 si.get(coff).setSize(coff, @intCast(nw.interface.end));
62186239 try si.applyLocationRelocs(coff);
62196240}
62206241
......@@ -6238,18 +6259,19 @@ fn aliasGlobal(coff: *Coff, gmi: Node.GlobalMapIndex, alias_si: Symbol.Index) !v
62386259 const reloc = ri.get(coff);
62396260 assert(reloc.target == si);
62406261 reloc.target = alias_si;
6241 if (reloc.next == .none) {
6242 reloc.next = alias_sym.target_relocs;
6262 if (reloc.prev == .none) {
6263 reloc.prev = alias_sym.target_relocs;
62436264 if (alias_sym.target_relocs != .none)
6244 alias_sym.target_relocs.get(coff).prev = ri;
6265 alias_sym.target_relocs.get(coff).next = ri;
62456266 break;
62466267 }
6247 ri = reloc.next;
6268 ri = reloc.prev;
62486269 }
62496270
62506271 const prev_target_relocs = alias_sym.target_relocs;
62516272 if (sym.target_relocs != .none)
62526273 alias_sym.target_relocs = sym.target_relocs;
6274
62536275 sym.target_relocs = .none;
62546276 sym.gmi = alias_sym.gmi;
62556277 coff.globals.values()[gmi.unwrap().?].si = alias_si;
......@@ -6844,7 +6866,8 @@ fn genLazyInner(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
68446866 error.WriteFailed => return nw.err.?,
68456867 else => |e| return e,
68466868 };
6847 si.get(coff).extra.size = @intCast(nw.interface.end);
6869
6870 si.get(coff).setSize(coff, @intCast(nw.interface.end));
68486871 try si.applyLocationRelocs(coff);
68496872}
68506873
test/tests.zig+7-8
......@@ -2286,14 +2286,13 @@ const incremental_targets = &[_]IncrementalTarget{
22862286 },
22872287 .backend = .selfhosted,
22882288 },
2289 // https://codeberg.org/ziglang/zig/issues/31773
2290 // .{
2291 // .target = .{
2292 // .cpu_arch = .x86_64,
2293 // .os_tag = .windows,
2294 // },
2295 // .backend = .selfhosted,
2296 // },
2289 .{
2290 .target = .{
2291 .cpu_arch = .x86_64,
2292 .os_tag = .windows,
2293 },
2294 .backend = .selfhosted,
2295 },
22972296 .{
22982297 .target = .{
22992298 .cpu_arch = .wasm32,