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 =...@@ -1780,4 +1780,5 @@ const usage =
1780 \\ --strings Display string tables1780 \\ --strings Display string tables
1781 \\ --symbols Display symbol tables1781 \\ --symbols Display symbol tables
1782 \\ --tls Display TLS information1782 \\ --tls Display TLS information
1783 \\
1783;1784;
src/link/Coff.zig+52-29
...@@ -904,9 +904,11 @@ pub const Symbol = struct {...@@ -904,9 +904,11 @@ pub const Symbol = struct {
904 weak_external_strat: WeakExternalStrat,904 weak_external_strat: WeakExternalStrat,
905 _: u5 = 0,905 _: u5 = 0,
906 },906 },
907 /// Relocations contained within this symbol907 /// 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.
908 loc_relocs: Reloc.Index,910 loc_relocs: Reloc.Index,
909 /// Relocations targeting this symbol911 /// The tail of a linked list of relocations with a .target that points to this symbol.
910 target_relocs: Reloc.Index,912 target_relocs: Reloc.Index,
911 section_number: SectionNumber,913 section_number: SectionNumber,
912 gmi: Node.GlobalMapIndex,914 gmi: Node.GlobalMapIndex,
...@@ -998,8 +1000,20 @@ pub const Symbol = struct {...@@ -998,8 +1000,20 @@ pub const Symbol = struct {
998 };1000 };
999 }1001 }
10001002
1001 pub fn size(sym: *const Symbol) u32 {1003 pub fn size(sym: *const Symbol, coff: *Coff) u32 {
1002 return if (sym.flags.extra_tag == .size) sym.extra.size else 0;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;
1003 }1017 }
10041018
1005 pub const SectionNumber = enum(i16) {1019 pub const SectionNumber = enum(i16) {
...@@ -1097,7 +1111,7 @@ pub const Symbol = struct {...@@ -1097,7 +1111,7 @@ pub const Symbol = struct {
1097 assert(reloc.target == si);1111 assert(reloc.target == si);
1098 if (reloc.sri.entry(coff, reloc.loc.get(coff).section_number)) |entry|1112 if (reloc.sri.entry(coff, reloc.loc.get(coff).section_number)) |entry|
1099 coff.targetStore(&entry.symbol_table_index, index);1113 coff.targetStore(&entry.symbol_table_index, index);
1100 ri = reloc.next;1114 ri = reloc.prev;
1101 }1115 }
1102 }1116 }
11031117
...@@ -1126,7 +1140,7 @@ pub const Symbol = struct {...@@ -1126,7 +1140,7 @@ pub const Symbol = struct {
1126 const reloc = ri.get(coff);1140 const reloc = ri.get(coff);
1127 assert(reloc.target == si);1141 assert(reloc.target == si);
1128 try reloc.apply(coff);1142 try reloc.apply(coff);
1129 ri = reloc.next;1143 ri = reloc.prev;
1130 }1144 }
1131 }1145 }
11321146
...@@ -1494,24 +1508,25 @@ pub const Reloc = extern struct {...@@ -1494,24 +1508,25 @@ pub const Reloc = extern struct {
1494 }1508 }
14951509
1496 pub fn delete(reloc: *Reloc, coff: *Coff) void {1510 pub fn delete(reloc: *Reloc, coff: *Coff) void {
1511 log.debug("deleteReloc({d})", .{reloc - coff.relocs.items.ptr});
1497 if (reloc.sri != .none) {1512 if (reloc.sri != .none) {
1498 // TODO: Need to remove this from the COFF relocation table (maybe removeswap?)1513 const loc_sym = reloc.loc.get(coff);
1499 // TODO: If this was the last reloc causing something to be in the symbol table, we should remove1514 const entry = reloc.sri.entry(coff, loc_sym.section_number).?;
1500 // the symbol table entry (and unset sti). That will require flushSymbolTableIndex on the1515
1501 // swapped symbol if we exchange indices1516 // On every supported architecture, a reloc type of 0 is .ABSOLUTE, and is a no-op
1502 @panic("TODO implement symbol table reloc deletions");1517 @memset(std.mem.asBytes(entry), 0);
1503 }1518 }
15041519
1505 switch (reloc.prev) {1520 switch (reloc.prev) {
1521 .none => {},
1522 else => |prev| prev.get(coff).next = reloc.next,
1523 }
1524 switch (reloc.next) {
1506 .none => {1525 .none => {
1507 const target = reloc.target.get(coff);1526 const target = reloc.target.get(coff);
1508 assert(target.target_relocs.get(coff) == reloc);1527 assert(target.target_relocs.get(coff) == reloc);
1509 target.target_relocs = reloc.next;1528 target.target_relocs = reloc.prev;
1510 },1529 },
1511 else => |prev| prev.get(coff).next = reloc.next,
1512 }
1513 switch (reloc.next) {
1514 .none => {},
1515 else => |next| next.get(coff).prev = reloc.prev,1530 else => |next| next.get(coff).prev = reloc.prev,
1516 }1531 }
15171532
...@@ -3264,7 +3279,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32) !void {...@@ -3264,7 +3279,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32) !void {
3264 };3279 };
32653280
3266 coff.targetStore(&entry.value, switch (sym.section_number) {3281 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),
3268 .ABSOLUTE,3283 .ABSOLUTE,
3269 .DEBUG,3284 .DEBUG,
3270 => unreachable,3285 => unreachable,
...@@ -3731,6 +3746,9 @@ fn addRelocAssumeCapacity(...@@ -3731,6 +3746,9 @@ fn addRelocAssumeCapacity(
3731 } else .none;3746 } else .none;
37323747
3733 const sri: Section.RelocationIndex = blk: {3748 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
3734 const section = loc_sn.section(coff);3752 const section = loc_sn.section(coff);
3735 const header = loc_sn.header(coff);3753 const header = loc_sn.header(coff);
3736 const old_num_relocations = coff.targetLoad(&header.number_of_relocations);3754 const old_num_relocations = coff.targetLoad(&header.number_of_relocations);
...@@ -3770,8 +3788,8 @@ fn addRelocAssumeCapacity(...@@ -3770,8 +3788,8 @@ fn addRelocAssumeCapacity(
37703788
3771 coff.relocs.addOneAssumeCapacity().* = .{3789 coff.relocs.addOneAssumeCapacity().* = .{
3772 .type = @"type",3790 .type = @"type",
3773 .prev = .none,3791 .prev = target.target_relocs,
3774 .next = target.target_relocs,3792 .next = .none,
3775 .loc = loc_si,3793 .loc = loc_si,
3776 .target = target_si,3794 .target = target_si,
3777 .sri = sri,3795 .sri = sri,
...@@ -3784,7 +3802,7 @@ fn addRelocAssumeCapacity(...@@ -3784,7 +3802,7 @@ fn addRelocAssumeCapacity(
3784 };3802 };
3785 switch (target.target_relocs) {3803 switch (target.target_relocs) {
3786 .none => {},3804 .none => {},
3787 else => |target_ri| target_ri.get(coff).prev = ri,3805 else => |target_ri| target_ri.get(coff).next = ri,
3788 }3806 }
3789 target.target_relocs = ri;3807 target.target_relocs = ri;
3790}3808}
...@@ -4894,7 +4912,7 @@ fn loadObject(...@@ -4894,7 +4912,7 @@ fn loadObject(
4894 symbol.si = global_gop.value_ptr.si;4912 symbol.si = global_gop.value_ptr.si;
4895 if (!global_gop.found_existing or symbol.si.get(coff).ni == .none) {4913 if (!global_gop.found_existing or symbol.si.get(coff).ni == .none) {
4896 const sym = symbol.si.get(coff);4914 const sym = symbol.si.get(coff);
4897 sym.setExtra(.{ .size = @max(sym.size(), size) });4915 sym.setExtra(.{ .size = @max(sym.size(coff), size) });
4898 }4916 }
4899 },4917 },
4900 else => unreachable,4918 else => unreachable,
...@@ -5483,7 +5501,8 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde...@@ -5483,7 +5501,8 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde
5483 error.WriteFailed => return nw.err.?,5501 error.WriteFailed => return nw.err.?,
5484 else => |e| return e,5502 else => |e| return e,
5485 };5503 };
5486 si.get(coff).extra.size = @intCast(nw.interface.end);5504
5505 si.get(coff).setSize(coff, @intCast(nw.interface.end));
5487 try si.applyLocationRelocs(coff);5506 try si.applyLocationRelocs(coff);
5488 }5507 }
54895508
...@@ -5621,7 +5640,8 @@ fn updateFuncInner(...@@ -5621,7 +5640,8 @@ fn updateFuncInner(
5621 error.WriteFailed => return nw.err.?,5640 error.WriteFailed => return nw.err.?,
5622 else => |e| return e,5641 else => |e| return e,
5623 };5642 };
5624 si.get(coff).extra.size = @intCast(nw.interface.end);5643
5644 si.get(coff).setSize(coff, @intCast(nw.interface.end));
5625 try si.applyLocationRelocs(coff);5645 try si.applyLocationRelocs(coff);
56265646
5627 // The NAV's node is done---now generate any UAVs or lazy code/data which the NAV needs.5647 // The NAV's node is done---now generate any UAVs or lazy code/data which the NAV needs.
...@@ -6214,7 +6234,8 @@ fn genUav(...@@ -6214,7 +6234,8 @@ fn genUav(
6214 error.WriteFailed => return nw.err.?,6234 error.WriteFailed => return nw.err.?,
6215 else => |e| return e,6235 else => |e| return e,
6216 };6236 };
6217 si.get(coff).extra.size = @intCast(nw.interface.end);6237
6238 si.get(coff).setSize(coff, @intCast(nw.interface.end));
6218 try si.applyLocationRelocs(coff);6239 try si.applyLocationRelocs(coff);
6219}6240}
62206241
...@@ -6238,18 +6259,19 @@ fn aliasGlobal(coff: *Coff, gmi: Node.GlobalMapIndex, alias_si: Symbol.Index) !v...@@ -6238,18 +6259,19 @@ fn aliasGlobal(coff: *Coff, gmi: Node.GlobalMapIndex, alias_si: Symbol.Index) !v
6238 const reloc = ri.get(coff);6259 const reloc = ri.get(coff);
6239 assert(reloc.target == si);6260 assert(reloc.target == si);
6240 reloc.target = alias_si;6261 reloc.target = alias_si;
6241 if (reloc.next == .none) {6262 if (reloc.prev == .none) {
6242 reloc.next = alias_sym.target_relocs;6263 reloc.prev = alias_sym.target_relocs;
6243 if (alias_sym.target_relocs != .none)6264 if (alias_sym.target_relocs != .none)
6244 alias_sym.target_relocs.get(coff).prev = ri;6265 alias_sym.target_relocs.get(coff).next = ri;
6245 break;6266 break;
6246 }6267 }
6247 ri = reloc.next;6268 ri = reloc.prev;
6248 }6269 }
62496270
6250 const prev_target_relocs = alias_sym.target_relocs;6271 const prev_target_relocs = alias_sym.target_relocs;
6251 if (sym.target_relocs != .none)6272 if (sym.target_relocs != .none)
6252 alias_sym.target_relocs = sym.target_relocs;6273 alias_sym.target_relocs = sym.target_relocs;
6274
6253 sym.target_relocs = .none;6275 sym.target_relocs = .none;
6254 sym.gmi = alias_sym.gmi;6276 sym.gmi = alias_sym.gmi;
6255 coff.globals.values()[gmi.unwrap().?].si = alias_si;6277 coff.globals.values()[gmi.unwrap().?].si = alias_si;
...@@ -6844,7 +6866,8 @@ fn genLazyInner(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {...@@ -6844,7 +6866,8 @@ fn genLazyInner(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
6844 error.WriteFailed => return nw.err.?,6866 error.WriteFailed => return nw.err.?,
6845 else => |e| return e,6867 else => |e| return e,
6846 };6868 };
6847 si.get(coff).extra.size = @intCast(nw.interface.end);6869
6870 si.get(coff).setSize(coff, @intCast(nw.interface.end));
6848 try si.applyLocationRelocs(coff);6871 try si.applyLocationRelocs(coff);
6849}6872}
68506873
test/tests.zig+7-8
...@@ -2286,14 +2286,13 @@ const incremental_targets = &[_]IncrementalTarget{...@@ -2286,14 +2286,13 @@ const incremental_targets = &[_]IncrementalTarget{
2286 },2286 },
2287 .backend = .selfhosted,2287 .backend = .selfhosted,
2288 },2288 },
2289 // https://codeberg.org/ziglang/zig/issues/317732289 .{
2290 // .{2290 .target = .{
2291 // .target = .{2291 .cpu_arch = .x86_64,
2292 // .cpu_arch = .x86_64,2292 .os_tag = .windows,
2293 // .os_tag = .windows,2293 },
2294 // },2294 .backend = .selfhosted,
2295 // .backend = .selfhosted,2295 },
2296 // },
2297 .{2296 .{
2298 .target = .{2297 .target = .{
2299 .cpu_arch = .wasm32,2298 .cpu_arch = .wasm32,