authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-07-23 21:52:17-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-07-27 20:28:07+02:00
log39c5d3a205b8fb2bf21d49c71c8c30ce15d73254
treecb37fd1e6ed593c90d895a561d95005856940085
parent91a29d7074a61ba192fcefb351a10a26d60b85c8

Elf2: start implementing archives

Allows building static libraries with the new linker.

3 files changed, 549 insertions(+), 255 deletions(-)

lib/std/elf.zig+4-4
......@@ -3272,12 +3272,12 @@ pub const ar_hdr = extern struct {
32723272 ar_fmag: [2]u8,
32733273
32743274 pub fn date(self: ar_hdr) std.fmt.ParseIntError!u64 {
3275 const value = mem.trimEnd(u8, &self.ar_date, &[_]u8{0x20});
3275 const value = mem.trimEnd(u8, &self.ar_date, " ");
32763276 return std.fmt.parseInt(u64, value, 10);
32773277 }
32783278
32793279 pub fn size(self: ar_hdr) std.fmt.ParseIntError!u32 {
3280 const value = mem.trimEnd(u8, &self.ar_size, &[_]u8{0x20});
3280 const value = mem.trimEnd(u8, &self.ar_size, " ");
32813281 return std.fmt.parseInt(u32, value, 10);
32823282 }
32833283
......@@ -3311,7 +3311,7 @@ pub const ar_hdr = extern struct {
33113311 pub fn nameOffset(self: ar_hdr) std.fmt.ParseIntError!?u32 {
33123312 const value = &self.ar_name;
33133313 if (value[0] != '/') return null;
3314 const trimmed = mem.trimEnd(u8, value, &[_]u8{0x20});
3314 const trimmed = mem.trimEnd(u8, value, " ");
33153315 return try std.fmt.parseInt(u32, trimmed[1..], 10);
33163316 }
33173317};
......@@ -3319,7 +3319,7 @@ pub const ar_hdr = extern struct {
33193319fn genSpecialMemberName(comptime name: []const u8) *const [16]u8 {
33203320 assert(name.len <= 16);
33213321 const padding = 16 - name.len;
3322 return name ++ @as([padding]u8, @splat(0x20));
3322 return name ++ @as([padding]u8, @splat(' '));
33233323}
33243324
33253325// Archive files start with the ARMAG identifying string. Then follows a
src/link/Elf2.zig+452-210
......@@ -126,8 +126,14 @@ needed: std.array_hash_map.Auto(String(.dynstr), void),
126126inputs: std.ArrayList(struct {
127127 path: std.Build.Cache.Path,
128128 member: ?[]const u8,
129 file_symbol: Symbol.LocalIndex,
129 extra: union {
130 /// Active for static libraries.
131 node: MappedFile.Node.Index,
132 /// Active otherwise.
133 file_symbol: Symbol.LocalIndex,
134 },
130135}),
136input_pending_index: u32,
131137input_sections: std.ArrayList(InputSection),
132138input_section_pending_index: u32,
133139navs: std.array_hash_map.Auto(InternPool.Nav.Index, struct {
......@@ -181,7 +187,10 @@ input_prog_node: std.Progress.Node,
181187const Error = link.Error || error{MappedFileIo};
182188
183189const Node = union(enum) {
184 file,
190 archive,
191 /// This includes the archive magic and long file member.
192 archive_header,
193 elf,
185194 ehdr,
186195 shdr,
187196 segment: u32,
......@@ -189,6 +198,8 @@ const Node = union(enum) {
189198 ///
190199 /// The section '.dynamic' may contain relocations via `elf.dynamic_first_symbol_reloc`.
191200 section: Section.Index,
201 /// Only valid for static libraries, represents one non-zcu archive member.
202 input_member: InputIndex,
192203 /// May contain relocations.
193204 input_section: InputSection.Index,
194205 /// Value is the name of a global which has an entry in `elf.copied_globals`, so, a global for
......@@ -219,19 +230,23 @@ const Node = union(enum) {
219230 return elf.inputs.items[@backingInt(ii)].member;
220231 }
221232
233 pub fn node(ii: InputIndex, elf: *const Elf) MappedFile.Node.Index {
234 return elf.inputs.items[@backingInt(ii)].extra.node;
235 }
236
222237 pub fn fileSymbol(ii: InputIndex, elf: *const Elf) Symbol.LocalIndex {
223 return elf.inputs.items[@backingInt(ii)].file_symbol;
238 return elf.inputs.items[@backingInt(ii)].extra.file_symbol;
224239 }
225240
226241 pub fn localSymbolRange(ii: InputIndex, elf: *Elf) [2]Symbol.LocalIndex {
227242 if (@backingInt(ii) + 1 < elf.inputs.items.len) {
228 const next_ii: InputIndex = @fromBackingInt(@intCast(@backingInt(ii) + 1));
243 const next_ii: InputIndex = @fromBackingInt(@backingInt(ii) + 1);
229244 return .{ ii.fileSymbol(elf), next_ii.fileSymbol(elf) };
230245 } else {
231246 const local_symbols_len = switch (elf.shdrPtr(.symtab)) {
232247 inline else => |shdr| elf.targetLoad(&shdr.info),
233248 };
234 return .{ ii.fileSymbol(elf), @fromBackingInt(@intCast(local_symbols_len)) };
249 return .{ ii.fileSymbol(elf), @fromBackingInt(local_symbols_len) };
235250 }
236251 }
237252 };
......@@ -315,15 +330,16 @@ const Node = union(enum) {
315330 };
316331
317332 pub const Known = struct {
318 comptime file: MappedFile.Node.Index = .root,
319 comptime ehdr: MappedFile.Node.Index = @fromBackingInt(@intCast(1)),
320 comptime shdr: MappedFile.Node.Index = @fromBackingInt(@intCast(2)),
321 comptime rodata: MappedFile.Node.Index = @fromBackingInt(@intCast(3)),
322 comptime phdr: MappedFile.Node.Index = @fromBackingInt(@intCast(4)),
323 comptime text: MappedFile.Node.Index = @fromBackingInt(@intCast(5)),
324 comptime data: MappedFile.Node.Index = @fromBackingInt(@intCast(6)),
325 comptime data_rel_ro: MappedFile.Node.Index = @fromBackingInt(@intCast(7)),
326
333 archive: MappedFile.Node.Index,
334 archive_header: MappedFile.Node.Index,
335 elf: MappedFile.Node.Index,
336 ehdr: MappedFile.Node.Index,
337 shdr: MappedFile.Node.Index,
338 rodata: MappedFile.Node.Index,
339 phdr: MappedFile.Node.Index,
340 text: MappedFile.Node.Index,
341 data: MappedFile.Node.Index,
342 data_rel_ro: MappedFile.Node.Index,
327343 tls: MappedFile.Node.Index,
328344 };
329345
......@@ -333,11 +349,11 @@ const Node = union(enum) {
333349
334350 /// In this linker implementation, `link.File.AtomId` is a type-erased `MappedFile.Node.Index`.
335351 fn toAtom(ni: MappedFile.Node.Index) link.File.AtomId {
336 return @fromBackingInt(@intCast(@backingInt(ni)));
352 return @fromBackingInt(@backingInt(ni));
337353 }
338354 /// In this linker implementation, `link.File.AtomId` is a type-erased `MappedFile.Node.Index`.
339355 fn fromAtom(atom: link.File.AtomId) MappedFile.Node.Index {
340 return @fromBackingInt(@intCast(@backingInt(atom)));
356 return @fromBackingInt(@backingInt(atom));
341357 }
342358};
343359
......@@ -424,13 +440,13 @@ const Section = struct {
424440 fn unwrap(opt: RelaIndex.Optional) ?RelaIndex {
425441 return switch (opt) {
426442 .none => null,
427 _ => @fromBackingInt(@intCast(@backingInt(opt))),
443 _ => @fromBackingInt(@backingInt(opt)),
428444 };
429445 }
430446 };
431447
432448 fn toOptional(i: RelaIndex) RelaIndex.Optional {
433 return @fromBackingInt(@intCast(@backingInt(i)));
449 return @fromBackingInt(@backingInt(i));
434450 }
435451 };
436452
......@@ -465,8 +481,8 @@ const Section = struct {
465481
466482 pub fn fromSection(sec: std.elf.Section) Index {
467483 return switch (sec) {
468 std.elf.SHN_UNDEF...std.elf.SHN_LORESERVE - 1 => @fromBackingInt(@intCast(sec)),
469 std.elf.SHN_LORESERVE...std.elf.SHN_HIRESERVE => @fromBackingInt(@intCast(reserve(sec))),
484 std.elf.SHN_UNDEF...std.elf.SHN_LORESERVE - 1 => @fromBackingInt(sec),
485 std.elf.SHN_LORESERVE...std.elf.SHN_HIRESERVE => @fromBackingInt(reserve(sec)),
470486 };
471487 }
472488 pub fn toSection(s: Index) ?std.elf.Section {
......@@ -485,7 +501,7 @@ const Section = struct {
485501
486502 fn name(s: Index, elf: *Elf) String(.shstrtab) {
487503 return switch (elf.shdrPtr(s)) {
488 inline else => |shdr| @fromBackingInt(@intCast(elf.targetLoad(&shdr.name))),
504 inline else => |shdr| @fromBackingInt(elf.targetLoad(&shdr.name)),
489505 };
490506 }
491507
......@@ -928,21 +944,7 @@ const GotReloc = struct {
928944 }
929945 }
930946 fn applyInner(reloc: *const GotReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void {
931 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {
932 .file => unreachable,
933 .ehdr => unreachable,
934 .shdr => unreachable,
935 .segment => unreachable,
936 .copied_global => unreachable,
937 .section => |shndx| shndx.vaddr(elf),
938 .input_section => |isi| isi.ptrConst(elf).vaddr,
939 inline .nav,
940 .uav,
941 .lazy_code,
942 .lazy_const_data,
943 => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
944 };
945 const dest_vaddr = node_vaddr + reloc.offset;
947 const dest_vaddr = elf.getNodeVAddr(reloc.node) + reloc.offset;
946948 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];
947949
948950 const got_vaddr = elf.shndx.got.vaddr(elf);
......@@ -1131,12 +1133,12 @@ pub const MachineRelocType = union {
11311133
11321134 pub fn wrap(int: u32, elf: *const Elf) MachineRelocType {
11331135 return switch (elf.ehdrMachine()) {
1134 .AARCH64 => .{ .AARCH64 = @fromBackingInt(@intCast(int)) },
1135 .LOONGARCH => .{ .LARCH = @fromBackingInt(@intCast(int)) },
1136 .PPC64 => .{ .PPC64 = @fromBackingInt(@intCast(int)) },
1137 .RISCV => .{ .RISCV = @fromBackingInt(@intCast(int)) },
1138 .SPARCV9 => .{ .SPARC = @fromBackingInt(@intCast(int)) },
1139 .X86_64 => .{ .X86_64 = @fromBackingInt(@intCast(int)) },
1136 .AARCH64 => .{ .AARCH64 = @fromBackingInt(int) },
1137 .LOONGARCH => .{ .LARCH = @fromBackingInt(int) },
1138 .PPC64 => .{ .PPC64 = @fromBackingInt(int) },
1139 .RISCV => .{ .RISCV = @fromBackingInt(int) },
1140 .SPARCV9 => .{ .SPARC = @fromBackingInt(int) },
1141 .X86_64 => .{ .X86_64 = @fromBackingInt(int) },
11401142 };
11411143 }
11421144 pub fn unwrap(rt: MachineRelocType, elf: *const Elf) u32 {
......@@ -1646,21 +1648,7 @@ const SymbolReloc = struct {
16461648 }
16471649 }
16481650 fn applyInner(reloc: *const SymbolReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void {
1649 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {
1650 .file => unreachable,
1651 .ehdr => unreachable,
1652 .shdr => unreachable,
1653 .segment => unreachable,
1654 .copied_global => unreachable,
1655 .section => |shndx| shndx.vaddr(elf),
1656 .input_section => |isi| isi.ptrConst(elf).vaddr,
1657 inline .nav,
1658 .uav,
1659 .lazy_code,
1660 .lazy_const_data,
1661 => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
1662 };
1663 const dest_vaddr = node_vaddr + reloc.offset;
1651 const dest_vaddr = elf.getNodeVAddr(reloc.node) + reloc.offset;
16641652 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];
16651653
16661654 const addend: u64 = @bitCast(reloc.addend);
......@@ -1875,7 +1863,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
18751863
18761864 // `shdr.info` stores the index of the first global symbol. We will replace it with our
18771865 // new local symbol, and move the global symbol to a new index at the end of the symtab.
1878 const target_index: Symbol.Index = @fromBackingInt(@intCast(elf.targetLoad(&shdr.info)));
1866 const target_index: Symbol.Index = @fromBackingInt(elf.targetLoad(&shdr.info));
18791867
18801868 const old_size = elf.targetLoad(&shdr.size);
18811869 const new_size = old_size + ent_size;
......@@ -1897,7 +1885,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
18971885 // ...then the `elf.symtab` metadata...
18981886 new_index.ptr(elf).* = target_index.ptr(elf).*;
18991887 // ...then update the `elf.globals` tracking.
1900 const global_name: String(.strtab) = @fromBackingInt(@intCast(elf.targetLoad(&new_sym.name)));
1888 const global_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&new_sym.name));
19011889 elf.globalByName(global_name).?.symtab_index = new_index;
19021890
19031891 if (elf.ehdrType() == .REL and target_index.ptr(elf).first_target_reloc != .none) {
......@@ -1923,7 +1911,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
19231911 std.mem.byteSwapAllFields(class.ElfN().Sym, target_sym);
19241912 }
19251913
1926 return @fromBackingInt(@intCast(@backingInt(target_index)));
1914 return @fromBackingInt(@backingInt(target_index));
19271915 },
19281916 }
19291917}
......@@ -2371,7 +2359,7 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
23712359 inline else => |shdr, class| {
23722360 // `shdr.info` stores the index of the first global symbol. We are going to swap the
23732361 // demoted symbol with that first global symbol, then increment that start index.
2374 const dest_index: Symbol.Index = @fromBackingInt(@intCast(elf.targetLoad(&shdr.info)));
2362 const dest_index: Symbol.Index = @fromBackingInt(elf.targetLoad(&shdr.info));
23752363 const src_index = global_ptr.symtab_index;
23762364
23772365 // This global should currently be in the "global symbols" part of the symtab, since our
......@@ -2387,10 +2375,10 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
23872375 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));
23882376 const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));
23892377
2390 const this_name: String(.strtab) = @fromBackingInt(@intCast(elf.targetLoad(&src_sym_ptr.name)));
2378 const this_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&src_sym_ptr.name));
23912379 assert(elf.globalByName(this_name).? == global_ptr);
23922380
2393 const other_name: String(.strtab) = @fromBackingInt(@intCast(elf.targetLoad(&dest_sym_ptr.name)));
2381 const other_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&dest_sym_ptr.name));
23942382 const other_global_ptr = elf.globalByName(other_name).?;
23952383 assert(other_global_ptr.symtab_index == dest_index);
23962384
......@@ -2426,7 +2414,7 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
24262414 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));
24272415 const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class));
24282416
2429 const moved_name_dynstr: String(.dynstr) = @fromBackingInt(@intCast(elf.targetLoad(&src_dynsym_ptr.name)));
2417 const moved_name_dynstr: String(.dynstr) = @fromBackingInt(elf.targetLoad(&src_dynsym_ptr.name));
24302418 const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf));
24312419 const moved_global_ptr = elf.globalByName(moved_name).?;
24322420
......@@ -2505,7 +2493,7 @@ const Symbol = struct {
25052493 _,
25062494
25072495 fn index(li: LocalIndex) Index {
2508 return @fromBackingInt(@intCast(@backingInt(li)));
2496 return @fromBackingInt(@backingInt(li));
25092497 }
25102498 };
25112499
......@@ -2527,16 +2515,16 @@ const Symbol = struct {
25272515 global: String(.strtab),
25282516 } {
25292517 return switch (s.kind) {
2530 .local => .{ .local = @fromBackingInt(@intCast(s.raw)) },
2531 .global => .{ .global = @fromBackingInt(@intCast(s.raw)) },
2518 .local => .{ .local = @fromBackingInt(s.raw) },
2519 .global => .{ .global = @fromBackingInt(s.raw) },
25322520 };
25332521 }
25342522
25352523 fn toTypeErased(s: Symbol.Id) link.File.SymbolId {
2536 return @fromBackingInt(@intCast(@as(u32, @bitCast(s))));
2524 return @bitCast(s);
25372525 }
25382526 fn fromTypeErased(s: link.File.SymbolId) Symbol.Id {
2539 return @bitCast(@backingInt(s));
2527 return @bitCast(s);
25402528 }
25412529
25422530 fn index(s: Symbol.Id, elf: *const Elf) Symbol.Index {
......@@ -2648,24 +2636,10 @@ const Symbol = struct {
26482636 .yes_textrel => elf.textrel_count += 1,
26492637 .yes => {},
26502638 }
2651 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {
2652 .file => unreachable,
2653 .ehdr => unreachable,
2654 .shdr => unreachable,
2655 .segment => unreachable,
2656 .copied_global => unreachable,
2657 .section => |shndx| shndx.vaddr(elf),
2658 .input_section => |isi| isi.ptrConst(elf).vaddr,
2659 inline .nav,
2660 .uav,
2661 .lazy_code,
2662 .lazy_const_data,
2663 => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
2664 };
26652639 // There is capacity for a relocation because we just deleted one earlier.
26662640 reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
26672641 .type = .relative(elf),
2668 .offset = node_vaddr + reloc.offset,
2642 .offset = elf.getNodeVAddr(reloc.node) + reloc.offset,
26692643 .raw_sym_index = 0,
26702644 .addend = 0,
26712645 }).toOptional();
......@@ -2771,11 +2745,14 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
27712745
27722746pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {
27732747 const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) {
2774 .file,
2748 .archive,
2749 .archive_header,
2750 .elf,
27752751 .ehdr,
27762752 .shdr,
27772753 .segment,
27782754 .section,
2755 .input_member,
27792756 .input_section,
27802757 .copied_global,
27812758 => unreachable,
......@@ -3001,12 +2978,12 @@ fn String(section: StringSection) type {
30012978}
30022979fn string(elf: *Elf, comptime section: StringSection, key: []const u8) Error!String(section) {
30032980 const st: *StringTable = &@field(elf, @tagName(section));
3004 return @fromBackingInt(@intCast(try st.get(elf, section.shndx(elf), key)));
2981 return @fromBackingInt(try st.get(elf, section.shndx(elf), key));
30052982}
30062983/// Like `string`, but asserts that the string is already in `section`.
30072984fn stringExisting(elf: *Elf, comptime section: StringSection, key: []const u8) String(section) {
30082985 const st: *StringTable = &@field(elf, @tagName(section));
3009 return @fromBackingInt(@intCast(st.getExisting(elf, section.shndx(elf), key)));
2986 return @fromBackingInt(st.getExisting(elf, section.shndx(elf), key));
30102987}
30112988
30122989const StringTable = struct {
......@@ -3172,6 +3149,16 @@ fn create(
31723149 .options = options,
31733150 .mf = try .init(file, comp.gpa, io),
31743151 .ni = .{
3152 .archive = .root,
3153 .archive_header = .none,
3154 .elf = .root,
3155 .ehdr = .none,
3156 .shdr = .none,
3157 .rodata = .none,
3158 .phdr = .none,
3159 .text = .none,
3160 .data = .none,
3161 .data_rel_ro = .none,
31753162 .tls = .none,
31763163 },
31773164 .nodes = .empty,
......@@ -3218,6 +3205,7 @@ fn create(
32183205 .dynamic_first_symbol_reloc = .none,
32193206 .needed = .empty,
32203207 .inputs = .empty,
3208 .input_pending_index = 0,
32213209 .input_sections = .empty,
32223210 .input_section_pending_index = 0,
32233211 .navs = .empty,
......@@ -3293,6 +3281,7 @@ fn initHeaders(
32933281 const comp = elf.base.comp;
32943282 const gpa = comp.gpa;
32953283
3284 const is_archive = comp.config.output_mode == .Lib and comp.config.link_mode == .static;
32963285 const have_dynamic_section = switch (@"type") {
32973286 .REL => false,
32983287 .EXEC => comp.config.link_mode == .dynamic,
......@@ -3389,7 +3378,8 @@ fn initHeaders(
33893378 }, phnum };
33903379 };
33913380
3392 const expected_nodes_len = 3 + // `.file`, `.ehdr`, and `.shdr` nodes
3381 const expected_nodes_len = @as(usize, if (is_archive) 2 else 0) + // .archive, .archive_header
3382 3 + // `.file`, `.ehdr`, and `.shdr` nodes
33933383 (shnum - 1) + // -1 because the null shdr does not have a `.section` node
33943384 (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node
33953385
......@@ -3398,17 +3388,49 @@ fn initHeaders(
33983388 try elf.section_by_name.ensureUnusedCapacity(gpa, shnum);
33993389 try elf.phdrs.resize(gpa, phnum);
34003390 try elf.symtab.ensureTotalCapacity(gpa, 1);
3401 elf.nodes.appendAssumeCapacity(.file);
3391
3392 if (is_archive) {
3393 elf.nodes.appendAssumeCapacity(.archive);
3394 elf.ni.archive_header = try elf.mf.addOnlyChildNode(gpa, elf.ni.archive, .{
3395 .size = std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr) * 2,
3396 .alignment = .@"2",
3397 .fixed = true,
3398 .next_moved = true,
3399 .bubbles_moved = false,
3400 .enable_next_moved = true,
3401 });
3402 const archive_header_slice = elf.ni.archive_header.slice(&elf.mf);
3403 @memcpy(archive_header_slice[0..std.elf.ARMAG.len], std.elf.ARMAG);
3404 const strtab_ar_hdr: *std.elf.ar_hdr = @ptrCast(archive_header_slice[std.elf.ARMAG.len..]);
3405 strtab_ar_hdr.* = .{
3406 .ar_name = std.elf.STRNAME.*,
3407 .ar_date = @splat(' '),
3408 .ar_uid = @splat(' '),
3409 .ar_gid = @splat(' '),
3410 .ar_mode = @splat(' '),
3411 .ar_size = @splat(' '),
3412 .ar_fmag = std.elf.ARFMAG.*,
3413 };
3414
3415 elf.nodes.appendAssumeCapacity(.archive_header);
3416 elf.ni.elf = try elf.mf.addLastChildNode(gpa, elf.ni.archive, .{
3417 .alignment = elf.mf.flags.block_size.max(.@"2"),
3418 .next_moved = true,
3419 .bubbles_moved = false,
3420 .enable_next_moved = true,
3421 });
3422 }
3423 elf.nodes.appendAssumeCapacity(.elf);
34023424
34033425 const entsize: struct { ph: u32, sh: u32 } = switch (class) {
34043426 .NONE, _ => unreachable,
34053427 inline else => |ct_class| entsize: {
34063428 const ElfN = ct_class.ElfN();
3407 assert(elf.ni.ehdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.file, .{
3429 elf.ni.ehdr = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
34083430 .size = @sizeOf(ElfN.Ehdr),
34093431 .alignment = addr_align,
34103432 .fixed = true,
3411 }));
3433 });
34123434 elf.nodes.appendAssumeCapacity(.ehdr);
34133435
34143436 const ehdr: *ElfN.Ehdr = @ptrCast(@alignCast(elf.ni.ehdr.slice(&elf.mf)));
......@@ -3461,12 +3483,12 @@ fn initHeaders(
34613483 },
34623484 };
34633485
3464 assert(elf.ni.shdr == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
3486 elf.ni.shdr = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
34653487 .size = 1 * entsize.sh, // as above, only the null shdr initially
34663488 .alignment = elf.mf.flags.block_size,
34673489 .moved = true,
34683490 .resized = true,
3469 }));
3491 });
34703492 elf.nodes.appendAssumeCapacity(.shdr);
34713493
34723494 const page_align: std.mem.Alignment = .fromByteUnits(switch (machine) {
......@@ -3491,45 +3513,45 @@ fn initHeaders(
34913513 });
34923514
34933515 var ph_vaddr: u32 = if (@"type" != .REL) ph_vaddr: {
3494 assert(elf.ni.rodata == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
3516 elf.ni.rodata = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
34953517 .alignment = elf.mf.flags.block_size,
34963518 .moved = true,
34973519 .bubbles_moved = false,
3498 }));
3520 });
34993521 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.rodata });
35003522 elf.phdrs.items[phndx.rodata] = elf.ni.rodata;
35013523
3502 assert(elf.ni.phdr == try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{
3524 elf.ni.phdr = try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{
35033525 .size = @as(u64, phnum) * entsize.ph,
35043526 .alignment = addr_align,
35053527 .moved = true,
35063528 .resized = true,
35073529 .bubbles_moved = false,
3508 }));
3530 });
35093531 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.phdr });
35103532 elf.phdrs.items[phndx.phdr] = elf.ni.phdr;
35113533
3512 assert(elf.ni.text == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
3534 elf.ni.text = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
35133535 .alignment = elf.mf.flags.block_size,
35143536 .moved = true,
35153537 .bubbles_moved = false,
3516 }));
3538 });
35173539 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.text });
35183540 elf.phdrs.items[phndx.text] = elf.ni.text;
35193541
3520 assert(elf.ni.data == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
3542 elf.ni.data = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
35213543 .alignment = elf.mf.flags.block_size,
35223544 .moved = true,
35233545 .bubbles_moved = false,
3524 }));
3546 });
35253547 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data });
35263548 elf.phdrs.items[phndx.data] = elf.ni.data;
35273549
3528 assert(elf.ni.data_rel_ro == try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{
3550 elf.ni.data_rel_ro = try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{
35293551 .alignment = elf.mf.flags.block_size,
35303552 .moved = true,
35313553 .bubbles_moved = false,
3532 }));
3554 });
35333555 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro });
35343556 elf.phdrs.items[phndx.relro] = elf.ni.data_rel_ro;
35353557
......@@ -3706,7 +3728,7 @@ fn initHeaders(
37063728 .node = .none,
37073729 .first_target_reloc = .none,
37083730 };
3709 assert(.symtab == try elf.addSection(elf.ni.file, .{
3731 assert(.symtab == try elf.addSection(elf.ni.elf, .{
37103732 .type = .SYMTAB,
37113733 .size = @sizeOf(ElfN.Sym) * 1,
37123734 .addralign = addr_align,
......@@ -3729,7 +3751,7 @@ fn initHeaders(
37293751 ehdr.shstrndx = ehdr.shnum;
37303752 },
37313753 }
3732 assert(.shstrtab == try elf.addSection(elf.ni.file, .{
3754 assert(.shstrtab == try elf.addSection(elf.ni.elf, .{
37333755 .type = .STRTAB,
37343756 .size = 1,
37353757 .entsize = 1,
......@@ -3740,7 +3762,7 @@ fn initHeaders(
37403762 try Section.Index.symtab.rename(elf, ".symtab");
37413763 try Section.Index.shstrtab.rename(elf, ".shstrtab");
37423764
3743 assert(.strtab == try elf.addSection(elf.ni.file, .{
3765 assert(.strtab == try elf.addSection(elf.ni.elf, .{
37443766 .name = ".strtab",
37453767 .type = .STRTAB,
37463768 .size = 1,
......@@ -4210,6 +4232,8 @@ fn initHeaders(
42104232 break :str try elf.string(.dynstr, slice);
42114233 },
42124234 };
4235
4236 try elf.ensureElfNodeSize();
42134237}
42144238
42154239pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {
......@@ -4221,10 +4245,8 @@ pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {
42214245 break :count count;
42224246 });
42234247 elf.mf.update_prog_node = prog_node.start("Relocations", elf.mf.updates.items.len);
4224 elf.input_prog_node = prog_node.start(
4225 "Inputs",
4226 elf.input_sections.items.len - elf.input_section_pending_index,
4227 );
4248 elf.input_prog_node = prog_node.start("Inputs", (elf.inputs.items.len - elf.input_pending_index) +
4249 (elf.input_sections.items.len - elf.input_section_pending_index));
42284250}
42294251
42304252pub fn endProgress(elf: *Elf) void {
......@@ -4244,13 +4266,15 @@ fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node {
42444266/// Asserts that `ni` is a section, input section, copied global, NAV, UAV, or lazy code/data.
42454267fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {
42464268 return switch (elf.getNode(ni)) {
4247 .file => unreachable,
4248 .ehdr => unreachable,
4249 .shdr => unreachable,
4250 .segment => unreachable,
4251
4269 .archive,
4270 .archive_header,
4271 .elf,
4272 .ehdr,
4273 .shdr,
4274 .segment,
4275 .input_member,
4276 => unreachable,
42524277 .section => |shndx| shndx,
4253
42544278 .input_section,
42554279 .copied_global,
42564280 .nav,
......@@ -4260,21 +4284,44 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {
42604284 => elf.getNode(ni.parent(&elf.mf)).section,
42614285 };
42624286}
4287fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
4288 return switch (elf.getNode(ni)) {
4289 .archive,
4290 .archive_header,
4291 .elf,
4292 .ehdr,
4293 .shdr,
4294 .segment,
4295 .input_member,
4296 .copied_global,
4297 => unreachable,
4298 .section => |shndx| shndx.vaddr(elf),
4299 .input_section => |isi| isi.ptrConst(elf).vaddr,
4300 inline .nav,
4301 .uav,
4302 .lazy_code,
4303 .lazy_const_data,
4304 => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
4305 };
4306}
42634307fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
42644308 const parent_vaddr = switch (elf.getNode(ni.parent(&elf.mf))) {
4265 .file => return 0,
4309 .archive, .archive_header => unreachable,
4310 .elf => return 0,
42664311 .ehdr, .shdr => unreachable,
42674312 .segment => |phndx| switch (elf.phdrSlice()) {
42684313 inline else => |phdr| elf.targetLoad(&phdr[phndx].vaddr),
42694314 },
42704315 .section => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf),
4271 .input_section => unreachable,
4272 .copied_global => unreachable,
4316 .input_member, .input_section, .copied_global => unreachable,
42734317 inline .nav, .uav, .lazy_code, .lazy_const_data => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
42744318 };
42754319 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);
42764320 return parent_vaddr + offset;
42774321}
4322fn getNodeElfOffset(elf: *Elf, ni: MappedFile.Node.Index) u64 {
4323 return ni.fileLocation(&elf.mf, false).offset - elf.ni.elf.fileLocation(&elf.mf, false).offset;
4324}
42784325
42794326/// Deletes any existing relocations in the given node, and marks the start of the node's contiguous
42804327/// sequence of relocations, so that the caller may append the node's updated relocations.
......@@ -4283,12 +4330,16 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
42834330/// the special-case sections '.plt' and '.dynamic'.
42844331fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
42854332 const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) {
4286 .file => unreachable, // cannot contain relocs
4287 .ehdr => unreachable, // cannot contain relocs
4288 .shdr => unreachable, // cannot contain relocs
4289 .segment => unreachable, // cannot contain relocs
4333 .archive,
4334 .archive_header,
4335 .elf,
4336 .ehdr,
4337 .shdr,
4338 .segment,
4339 .input_member,
4340 .copied_global,
4341 => unreachable, // cannot contain relocs
42904342 .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported)
4291 .copied_global => unreachable, // cannot contain relocs
42924343 .input_section => |isi| .{
42934344 &elf.input_sections.items[@backingInt(isi)].first_symbol_reloc,
42944345 &elf.input_sections.items[@backingInt(isi)].first_got_reloc,
......@@ -4359,7 +4410,7 @@ fn flushMovedNodeRelocs(
43594410}
43604411
43614412fn identClass(elf: *const Elf) std.elf.CLASS {
4362 return @fromBackingInt(@intCast(elf.mf.memory_map.memory[std.elf.EI.CLASS]));
4413 return @fromBackingInt(elf.ni.elf.sliceConst(&elf.mf)[std.elf.EI.CLASS]);
43634414}
43644415
43654416/// Like `std.elf.ET`, but only includes the ELF machine architectures we support, so that we can
......@@ -4415,7 +4466,7 @@ fn targetPtrSize(elf: *const Elf) u8 {
44154466 return elf.identClass().size();
44164467}
44174468fn targetEndian(elf: *const Elf) std.lang.Endian {
4418 const ident_data: std.elf.DATA = @fromBackingInt(@intCast(elf.mf.memory_map.memory[std.elf.EI.DATA]));
4469 const ident_data: std.elf.DATA = @fromBackingInt(elf.ni.elf.sliceConst(&elf.mf)[std.elf.EI.DATA]);
44194470 return ident_data.endian();
44204471}
44214472fn targetTlsVariant(elf: *const Elf) union(enum) {
......@@ -4487,7 +4538,7 @@ fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.chi
44874538 return switch (@typeInfo(Child)) {
44884539 else => @compileError(@typeName(Child)),
44894540 .int => std.mem.toNative(Child, ptr.*, elf.targetEndian()),
4490 .@"enum" => |@"enum"| @fromBackingInt(@intCast(elf.targetLoad(@as(*align(alignment) const @"enum".tag_type, @ptrCast(ptr))))),
4541 .@"enum" => |@"enum"| @fromBackingInt(elf.targetLoad(@as(*align(alignment) const @"enum".tag_type, @ptrCast(ptr)))),
44914542 .@"struct" => |@"struct"| @bitCast(
44924543 elf.targetLoad(@as(*align(alignment) @"struct".backing_integer.?, @ptrCast(ptr))),
44934544 ),
......@@ -4563,6 +4614,16 @@ fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr {
45634614 }
45644615}
45654616
4617fn arHdrPtr(elf: *Elf, ni: MappedFile.Node.Index) *align(2) std.elf.ar_hdr {
4618 assert(elf.ni.elf != MappedFile.Node.Index.root);
4619 const file_offset = ni.fileLocation(&elf.mf, false).offset;
4620 return @ptrCast(@alignCast(elf.mf.memory_map.memory[@intCast(switch (elf.getNode(ni)) {
4621 else => unreachable,
4622 .archive_header => file_offset + std.elf.ARMAG.len,
4623 .elf, .input_member => file_offset - @sizeOf(std.elf.ar_hdr),
4624 })..][0..@sizeOf(std.elf.ar_hdr)]));
4625}
4626
45664627const SymPtr = union(std.elf.CLASS) {
45674628 NONE: noreturn,
45684629 @"32": *std.elf.Elf32.Sym,
......@@ -4657,7 +4718,7 @@ fn mapInputSection(elf: *Elf, opts: struct {
46574718 }
46584719 errdefer assert(elf.section_by_name.pop().?.key == name_shstrtab);
46594720 const parent_node: MappedFile.Node.Index = parent: {
4660 if (!opts.flags.ALLOC) break :parent elf.ni.file;
4721 if (!opts.flags.ALLOC) break :parent elf.ni.elf;
46614722 if (opts.flags.EXECINSTR) break :parent elf.ni.text;
46624723 if (opts.flags.TLS) break :parent elf.ni.tls;
46634724 if (opts.flags.WRITE) break :parent elf.ni.data;
......@@ -4878,7 +4939,7 @@ const LoadParseInputError = Error || Io.File.SeekError || Io.Reader.Error;
48784939/// indicates to the frontend that the input could be a GNU ld script instead.
48794940pub fn loadInput(elf: *Elf, input: link.Input) (link.Error || error{BadMagic})!void {
48804941 const diags = &elf.base.comp.link_diags;
4881 return elf.loadInputInner(input) catch |err| switch (err) {
4942 elf.loadInputInner(input) catch |err| switch (err) {
48824943 else => |e| return e,
48834944 error.MappedFileIo => return diags.fail(
48844945 "failed to write output file: {t}",
......@@ -4986,6 +5047,9 @@ fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (Load
49865047 const r = &fr.interface;
49875048
49885049 log.debug("loadArchive({f})", .{path.fmtEscapeString()});
5050
5051 if (elf.ehdrType() == .REL) return; // this input does not affect the output artifact
5052
49895053 {
49905054 const magic = r.take(std.elf.ARMAG.len) catch |err| switch (err) {
49915055 error.ReadFailed => |e| return e,
......@@ -5071,21 +5135,40 @@ fn loadObject(
50715135 .{},
50725136 ),
50735137 };
5138
5139 const input = try elf.inputs.addOne(gpa);
5140 input.* = .{
5141 .path = path,
5142 .member = if (member) |m| try gpa.dupe(u8, m) else null,
5143 .extra = undefined,
5144 };
5145 if (elf.ni.elf != MappedFile.Node.Index.root) {
5146 try elf.nodes.ensureUnusedCapacity(gpa, 1);
5147 input.extra = .{ .node = try elf.mf.addLastChildNode(gpa, .root, .{
5148 .size = fl.size + @sizeOf(std.elf.ar_hdr),
5149 .alignment = .@"2",
5150 .next_moved = true,
5151 .bubbles_moved = false,
5152 .enable_next_moved = true,
5153 }) };
5154 elf.nodes.appendAssumeCapacity(.{ .input_member = input_index });
5155 elf.input_prog_node.increaseEstimatedTotalItems(1);
5156
5157 // Since we are not emitting the archive symbol table (yet?) we do not need to parse
5158 // the symbols in this input.
5159 return;
5160 }
5161
5162 elf.input_pending_index += 1;
50745163 try elf.ensureUnusedSymbolCapacity(1, .all_local);
5075 try elf.inputs.ensureUnusedCapacity(gpa, 1);
5076 const file_symbol = elf.addLocalSymbolAssumeCapacity(.{
5164 input.extra = .{ .file_symbol = elf.addLocalSymbolAssumeCapacity(.{
50775165 .node = .none,
50785166 .name = try elf.string(.strtab, std.fs.path.stem(member orelse path.sub_path)),
50795167 .value = 0,
50805168 .size = 0,
50815169 .type = .FILE,
50825170 .shndx = .ABS,
5083 });
5084 elf.inputs.addOneAssumeCapacity().* = .{
5085 .path = path,
5086 .member = if (member) |m| try gpa.dupe(u8, m) else null,
5087 .file_symbol = file_symbol,
5088 };
5171 }) };
50895172 const target_endian = elf.targetEndian();
50905173 switch (elf.identClass()) {
50915174 .NONE, _ => unreachable,
......@@ -5479,6 +5562,9 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars
54795562
54805563 log.debug("loadDso({f})", .{path.fmtEscapeString()});
54815564 try elf.checkInputIdent(path, r);
5565
5566 if (elf.ehdrType() == .REL) return; // this input does not affect the output artifact
5567
54825568 const target_endian = elf.targetEndian();
54835569 switch (elf.identClass()) {
54845570 .NONE, _ => unreachable,
......@@ -5709,7 +5795,8 @@ fn checkInputIdent(
57095795 }
57105796
57115797 const ident = try r.peekStructPointer(std.elf.Ident);
5712 const target: *const std.elf.Ident = @ptrCast(elf.mf.memory_map.memory[0..@sizeOf(std.elf.Ident)]);
5798 const target: *const std.elf.Ident =
5799 @ptrCast(elf.ni.elf.sliceConst(&elf.mf)[0..@sizeOf(std.elf.Ident)]);
57135800
57145801 if (ident.class != target.class) return diags.failParse(
57155802 path,
......@@ -5825,13 +5912,11 @@ fn prelinkInner(elf: *Elf) Error!void {
58255912 const comp = elf.base.comp;
58265913 const gpa = comp.gpa;
58275914
5828 if (comp.zcu != null and !comp.config.use_llvm) {
5829 // We're use self-hosted codegen---add an input representing the Zig "object".
5915 if (comp.zcu != null and !comp.config.use_llvm and elf.ni.elf == MappedFile.Node.Index.root) {
5916 // We're using self-hosted codegen---add an input representing the Zig "object".
58305917 try elf.ensureUnusedSymbolCapacity(1, .all_local);
58315918 try elf.inputs.ensureUnusedCapacity(gpa, 1);
5832 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{
5833 std.fs.path.stem(elf.base.emit.sub_path),
5834 });
5919 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name});
58355920 defer gpa.free(zcu_name);
58365921 const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{
58375922 .node = .none,
......@@ -5844,9 +5929,12 @@ fn prelinkInner(elf: *Elf) Error!void {
58445929 elf.inputs.addOneAssumeCapacity().* = .{
58455930 .path = elf.base.emit,
58465931 .member = null,
5847 .file_symbol = zcu_file_symbol,
5932 .extra = .{ .file_symbol = zcu_file_symbol },
58485933 };
5934 elf.input_pending_index += 1;
58495935 }
5936
5937 try elf.ensureElfNodeSize();
58505938}
58515939
58525940fn prepareDynamic(elf: *Elf) Error!void {
......@@ -6036,12 +6124,12 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
60366124 },
60376125 };
60386126 assert(shndx < @backingInt(Section.Index.LORESERVE));
6039 break :shndx .{ @fromBackingInt(@intCast(shndx)), @as(u64, elf.targetLoad(&ehdr.shentsize)) * @as(u64, shnum) };
6127 break :shndx .{ @fromBackingInt(shndx), @as(u64, elf.targetLoad(&ehdr.shentsize)) * @as(u64, shnum) };
60406128 },
60416129 };
60426130 try elf.ensureNodeSize(elf.ni.shdr, new_shdr_size);
60436131 const ni = try elf.mf.addLastChildNode(gpa, switch (elf.ehdrType()) {
6044 .REL => elf.ni.file,
6132 .REL => elf.ni.elf,
60456133 .EXEC, .DYN => segment_ni,
60466134 }, .{
60476135 .size = opts.size,
......@@ -6064,7 +6152,6 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
60646152 else => .{ .shndx = .UNDEF },
60656153 } });
60666154 elf.nodes.appendAssumeCapacity(.{ .section = shndx });
6067 const offset = ni.fileLocation(&elf.mf, false).offset;
60686155 switch (elf.shdrPtr(shndx)) {
60696156 inline else => |shdr, class| {
60706157 shdr.* = .{
......@@ -6072,7 +6159,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
60726159 .type = opts.type,
60736160 .flags = .{ .shf = opts.flags },
60746161 .addr = @intCast(addr),
6075 .offset = @intCast(offset),
6162 .offset = @intCast(elf.getNodeElfOffset(ni)),
60766163 .size = @intCast(opts.size),
60776164 .link = opts.link,
60786165 .info = opts.info,
......@@ -6506,20 +6593,7 @@ fn addSymbolRelocAssumeCapacity(
65066593
65076594 // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to
65086595 // determine the vaddr of `node`.
6509 const node_vaddr: u64 = switch (elf.getNode(node)) {
6510 .file => unreachable,
6511 .ehdr => unreachable,
6512 .shdr => unreachable,
6513 .segment => unreachable,
6514 .copied_global => unreachable,
6515 .section => |shndx| shndx.vaddr(elf),
6516 .input_section => |isi| isi.ptrConst(elf).vaddr,
6517 inline .nav,
6518 .uav,
6519 .lazy_code,
6520 .lazy_const_data,
6521 => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
6522 };
6596 const node_vaddr = elf.getNodeVAddr(node);
65236597
65246598 // If this is `true`, we will try to create a copy relocation for the target symbol if it is
65256599 // not locally defined. If the relocation value is always computed from the target symbol's
......@@ -6658,20 +6732,23 @@ fn addGotRelocAssumeCapacity(
66586732) void {
66596733 assert(elf.ehdrType() != .REL);
66606734 switch (elf.getNode(node)) {
6735 .archive,
6736 .archive_header,
6737 .elf,
6738 .ehdr,
6739 .shdr,
6740 .segment,
6741 .input_member,
6742 .copied_global,
6743 => unreachable, // cannot contain relocs,
6744 .section,
6745 .uav,
6746 => unreachable, // cannot contain GOT relocs
66616747 .input_section,
66626748 .nav,
66636749 .lazy_code,
66646750 .lazy_const_data,
66656751 => {},
6666
6667 .section => unreachable, // cannot contain GOT relocs
6668 .uav => unreachable, // cannot contain GOT relocs
6669
6670 .file => unreachable, // cannot contain relocs
6671 .ehdr => unreachable, // cannot contain relocs
6672 .shdr => unreachable, // cannot contain relocs
6673 .segment => unreachable, // cannot contain relocs
6674 .copied_global => unreachable, // cannot contain relocs
66756752 }
66766753
66776754 const gop = elf.got.getOrPutAssumeCapacity(target);
......@@ -7055,6 +7132,17 @@ pub fn flush(
70557132 tid: Zcu.PerThread.Id,
70567133 prog_node: std.Progress.Node,
70577134) link.Error!void {
7135 elf.flushInner(arena, tid, prog_node) catch |err| switch (err) {
7136 error.MappedFileIo => return elf.base.comp.link_diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
7137 else => |e| return e,
7138 };
7139}
7140fn flushInner(
7141 elf: *Elf,
7142 arena: std.mem.Allocator,
7143 tid: Zcu.PerThread.Id,
7144 prog_node: std.Progress.Node,
7145) Error!void {
70587146 const comp = elf.base.comp;
70597147 const diags = &comp.link_diags;
70607148 _ = arena;
......@@ -7072,11 +7160,9 @@ pub fn flush(
70727160 if (any_undef) return error.AlreadyReported;
70737161 }
70747162
7075 elf.prepareDynamic() catch |err| switch (err) {
7076 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
7077 else => |e| return e,
7078 };
7163 try elf.prepareDynamic();
70797164
7165 try elf.ensureElfNodeSize();
70807166 while (try elf.idle(tid)) {}
70817167
70827168 // We've done the final `idle` loop, so everything is at its final place in the file. We have a
......@@ -7101,10 +7187,7 @@ pub fn flush(
71017187 .enabled => "_start",
71027188 .named => |named| named,
71037189 };
7104 const sym_name_strtab = elf.string(.strtab, sym_name_slice) catch |err| switch (err) {
7105 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
7106 else => |e| return e,
7107 };
7190 const sym_name_strtab = try elf.string(.strtab, sym_name_slice);
71087191 if (elf.globalByName(sym_name_strtab) == null) break :entry 0;
71097192 break :entry Symbol.Id.global(sym_name_strtab).value(elf);
71107193 };
......@@ -7112,10 +7195,11 @@ pub fn flush(
71127195 inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)),
71137196 }
71147197
7115 elf.mf.flush() catch |err| switch (err) {
7116 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
7117 else => |e| return e,
7118 };
7198 try elf.mf.flush();
7199
7200 if (elf.options.enable_link_snapshots)
7201 elf.dumpStderr(tid) catch |err|
7202 return comp.link_diags.fail("dumping link snapshot failed: {t}", .{err});
71197203}
71207204
71217205pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
......@@ -7128,8 +7212,19 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
71287212 }
71297213
71307214 task: {
7215 if (elf.input_pending_index < elf.inputs.items.len) {
7216 const ii: Node.InputIndex = @fromBackingInt(elf.input_pending_index);
7217 elf.input_pending_index += 1;
7218 const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(ii.node(elf)));
7219 defer sub_prog_node.end();
7220 elf.flushInput(ii) catch |err| switch (err) {
7221 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
7222 else => |e| return e,
7223 };
7224 break :task;
7225 }
71317226 if (elf.input_section_pending_index < elf.input_sections.items.len) {
7132 const isi: InputSection.Index = @fromBackingInt(@intCast(elf.input_section_pending_index));
7227 const isi: InputSection.Index = @fromBackingInt(elf.input_section_pending_index);
71337228 elf.input_section_pending_index += 1;
71347229 const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(isi.node(elf)));
71357230 defer sub_prog_node.end();
......@@ -7217,11 +7312,13 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
72177312 while (elf.mf.updates.pop()) |ni| {
72187313 const clean_moved = ni.cleanMoved(&elf.mf);
72197314 const clean_resized = ni.cleanResized(&elf.mf);
7220 if (clean_moved or clean_resized) {
7315 const clean_next_moved = ni.cleanNextMoved(&elf.mf);
7316 if (clean_moved or clean_resized or clean_next_moved) {
72217317 const sub_prog_node = elf.idleProgNode(tid, elf.mf.update_prog_node, elf.getNode(ni));
72227318 defer sub_prog_node.end();
72237319 if (clean_moved) try elf.flushMoved(ni);
72247320 if (clean_resized) try elf.flushResized(ni);
7321 if (clean_next_moved) try elf.flushNextMoved(ni);
72257322 break :task;
72267323 } else elf.mf.update_prog_node.completeOne();
72277324 }
......@@ -7242,6 +7339,10 @@ fn idleProgNode(
72427339 return prog_node.start(name: switch (node) {
72437340 else => |tag| @tagName(tag),
72447341 .section => |shndx| shndx.name(elf).slice(elf),
7342 .input_member => |ii| std.fmt.bufPrint(&name, "{f}{f}", .{
7343 ii.path(elf).fmtEscapeString(),
7344 fmtMemberString(ii.member(elf)),
7345 }) catch &name,
72457346 .input_section => |isi| {
72467347 const ii = isi.input(elf);
72477348 break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{
......@@ -7294,6 +7395,8 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) Error!void {
72947395 };
72957396 break;
72967397 }
7398
7399 try elf.ensureElfNodeSize();
72977400}
72987401
72997402fn genUav(
......@@ -7362,6 +7465,36 @@ fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) Error!void {
73627465 }
73637466}
73647467
7468fn flushInput(elf: *Elf, ii: Node.InputIndex) Error!void {
7469 const comp = elf.base.comp;
7470 const io = comp.io;
7471 const gpa = comp.gpa;
7472 const diags = &comp.link_diags;
7473 const path = ii.path(elf);
7474 const file = path.root_dir.handle.openFile(io, path.sub_path, .{}) catch |err| switch (err) {
7475 error.Canceled => |e| return e,
7476 else => |e| return diags.fail("failed to open input file \"{f}\": {t}", .{ path.fmtEscapeString(), e }),
7477 };
7478 defer file.close(io);
7479 var fr = file.reader(io, &.{});
7480 var nw: MappedFile.Node.Writer = undefined;
7481 ii.node(elf).writer(&elf.mf, gpa, &nw);
7482 defer nw.deinit();
7483 const size = nw.interface.buffer.len - @sizeOf(std.elf.ar_hdr);
7484 const n_bytes = nw.interface.sendFileAll(&fr, .limited(size)) catch |err| switch (err) {
7485 error.ReadFailed => return diags.fail("failed to read input \"{f}{f}\": {t}", .{
7486 path.fmtEscapeString(),
7487 fmtMemberString(ii.member(elf)),
7488 fr.err orelse (fr.seek_err orelse fr.size_err.?),
7489 }),
7490 error.WriteFailed => return nw.err.?,
7491 };
7492 if (n_bytes + 1 < size) return diags.fail("failed to read input \"{f}{f}\": unexpected eof", .{
7493 path.fmtEscapeString(),
7494 fmtMemberString(ii.member(elf)),
7495 });
7496}
7497
73657498fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void {
73667499 const file_loc = isi.fileLocation(elf);
73677500 if (file_loc.size == 0) return;
......@@ -7408,33 +7541,29 @@ fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void {
74087541 assert(isi.node(elf).hasMoved(&elf.mf));
74097542}
74107543
7411fn flushFileOffset(elf: *Elf, ni: MappedFile.Node.Index) void {
7544fn flushElfOffset(elf: *Elf, ni: MappedFile.Node.Index) void {
7545 const elf_offset = elf.getNodeElfOffset(ni);
74127546 switch (elf.getNode(ni)) {
74137547 else => unreachable,
7414 .ehdr => assert(ni.fileLocation(&elf.mf, false).offset == 0),
7548 .ehdr => assert(elf_offset == 0),
74157549 .shdr => switch (elf.ehdrPtr()) {
7416 inline else => |ehdr| elf.targetStore(
7417 &ehdr.shoff,
7418 @intCast(ni.fileLocation(&elf.mf, false).offset),
7419 ),
7550 inline else => |ehdr| elf.targetStore(&ehdr.shoff, @intCast(elf_offset)),
74207551 },
74217552 .segment => |phndx| {
74227553 switch (elf.phdrSlice()) {
74237554 inline else => |phdr, class| {
74247555 const ph = &phdr[phndx];
7425 elf.targetStore(&ph.offset, @intCast(ni.fileLocation(&elf.mf, false).offset));
7556 elf.targetStore(&ph.offset, @intCast(elf_offset));
74267557 if (elf.targetLoad(&ph.type) == .PHDR) {
74277558 @field(elf.ehdrPtr(), @tagName(class)).phoff = ph.offset;
74287559 }
74297560 },
74307561 }
74317562 var child_it = ni.children(&elf.mf);
7432 while (child_it.next()) |child_ni| elf.flushFileOffset(child_ni);
7563 while (child_it.next()) |child_ni| elf.flushElfOffset(child_ni);
74337564 },
74347565 .section => |shndx| switch (elf.shdrPtr(shndx)) {
7435 inline else => |shdr| elf.targetStore(&shdr.offset, @intCast(
7436 ni.fileLocation(&elf.mf, false).offset,
7437 )),
7566 inline else => |shdr| elf.targetStore(&shdr.offset, @intCast(elf_offset)),
74387567 },
74397568 }
74407569}
......@@ -7447,10 +7576,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
74477576 defer elf.mf.nodes_lock.unlock();
74487577
74497578 switch (elf.getNode(ni)) {
7450 .file => unreachable,
7451 .ehdr, .shdr => elf.flushFileOffset(ni),
7579 .archive, .archive_header => unreachable,
7580 .elf => {},
7581 .ehdr, .shdr => elf.flushElfOffset(ni),
74527582 .segment => |phndx| {
7453 elf.flushFileOffset(ni);
7583 elf.flushElfOffset(ni);
74547584 switch (elf.phdrSlice()) {
74557585 inline else => |phdr| {
74567586 const ph = &phdr[phndx];
......@@ -7471,7 +7601,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
74717601 }
74727602 },
74737603 .section => |shndx| {
7474 elf.flushFileOffset(ni);
7604 elf.flushElfOffset(ni);
74757605 const addr = elf.computeNodeVAddr(ni);
74767606 const old_addr: u64, const flags: std.elf.SHF = switch (elf.shdrPtr(shndx)) {
74777607 inline else => |shdr| .{
......@@ -7522,6 +7652,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
75227652 elf.flushMovedNodeRelocs(ni, addr, elf.dynamic_first_symbol_reloc, .none);
75237653 }
75247654 },
7655 .input_member => {},
75257656 .input_section => |isi| {
75267657 const old_section_addr = isi.ptr(elf).vaddr;
75277658 const new_section_addr = elf.computeNodeVAddr(ni);
......@@ -7530,7 +7661,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
75307661 // Update local symbols
75317662 const ii = isi.input(elf);
75327663 var lsi, const end_lsi = ii.localSymbolRange(elf);
7533 while (lsi != end_lsi) : (lsi = @fromBackingInt(@intCast(@backingInt(lsi) + 1))) {
7664 while (lsi != end_lsi) : (lsi = @fromBackingInt(@backingInt(lsi) + 1)) {
75347665 if (lsi.index().ptr(elf).node != ni) continue;
75357666 const visibility: std.elf.STV = switch (elf.symPtr(lsi.index())) {
75367667 inline else => |sym| elf.targetLoad(&sym.other).visibility,
......@@ -7617,7 +7748,17 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
76177748
76187749 _, const size = ni.location(&elf.mf).resolve(&elf.mf);
76197750 switch (elf.getNode(ni)) {
7620 .file => {},
7751 .archive => {
7752 var child_it = ni.reverseChildren(&elf.mf);
7753 if (child_it.next()) |last_ni| {
7754 if (child_it.next()) |prev_ni| if (prev_ni.hasNextMoved(&elf.mf)) return;
7755 const offset, _ = last_ni.location(&elf.mf).resolve(&elf.mf);
7756 _ = std.mem.print(&elf.arHdrPtr(last_ni).ar_size, "{d:<10}", .{
7757 size - offset,
7758 }) catch @panic("archive member too large");
7759 }
7760 },
7761 .archive_header, .elf => {},
76217762 .ehdr => unreachable,
76227763 .shdr => {},
76237764 .segment => |phndx| switch (elf.phdrSlice()) {
......@@ -7717,9 +7858,88 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
77177858 }
77187859 },
77197860 },
7720 .copied_global, .input_section, .nav, .uav, .lazy_code, .lazy_const_data => {},
7861 .input_member, .input_section, .copied_global, .nav, .uav, .lazy_code, .lazy_const_data => {},
7862 }
7863}
7864
7865fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {
7866 const trace = tracy.trace(@src());
7867 defer trace.end();
7868
7869 elf.mf.nodes_lock.lock();
7870 defer elf.mf.nodes_lock.unlock();
7871
7872 switch (elf.getNode(ni)) {
7873 .archive,
7874 .ehdr,
7875 .shdr,
7876 .segment,
7877 .section,
7878 .input_section,
7879 .copied_global,
7880 .nav,
7881 .uav,
7882 .lazy_code,
7883 .lazy_const_data,
7884 => unreachable,
7885 .archive_header, .elf, .input_member => |_, tag| {
7886 const member_offset, const update_size = member_offset: {
7887 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);
7888 break :member_offset switch (tag) {
7889 else => unreachable,
7890 .archive_header => .{ offset + std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr), true },
7891 .elf, .input_member => .{ offset, switch (ni.prev(&elf.mf)) {
7892 .none => unreachable,
7893 else => |prev_ni| !prev_ni.hasNextMoved(&elf.mf),
7894 } },
7895 };
7896 };
7897 const member_size = member_end: switch (ni.next(&elf.mf)) {
7898 else => |next_ni| {
7899 const next_offset, _ = next_ni.location(&elf.mf).resolve(&elf.mf);
7900 const next_member_size = next_member_end: switch (next_ni.next(&elf.mf)) {
7901 else => |next_next_ni| {
7902 const next_next_offset, _ = next_next_ni.location(&elf.mf).resolve(&elf.mf);
7903 break :next_member_end next_next_offset - @sizeOf(std.elf.ar_hdr);
7904 },
7905 .none => {
7906 _, const parent_size =
7907 ni.parent(&elf.mf).location(&elf.mf).resolve(&elf.mf);
7908 break :next_member_end parent_size;
7909 },
7910 } - next_offset;
7911 const ar_hdr = elf.arHdrPtr(next_ni);
7912 var name_buf: [16]u8 = undefined;
7913 _ = std.mem.print(&ar_hdr.ar_name, "{s:<16}", .{
7914 switch (elf.getNode(next_ni)) {
7915 else => unreachable,
7916 .elf => std.mem.print(&name_buf, "{s}_zcu.o/", .{elf.base.comp.root_name}),
7917 .input_member => |ii| std.mem.print(&name_buf, "{s}/", .{
7918 std.fs.path.basename(ii.path(elf).sub_path),
7919 }),
7920 } catch @panic("TODO: long archive member names"),
7921 }) catch @panic("TODO: long archive member names");
7922 ar_hdr.ar_date = "0 ".*;
7923 ar_hdr.ar_uid = "0 ".*;
7924 ar_hdr.ar_gid = "0 ".*;
7925 ar_hdr.ar_mode = "644 ".*;
7926 _ = std.mem.print(&ar_hdr.ar_size, "{d:<10}", .{next_member_size}) catch
7927 @panic("archive member too large");
7928 ar_hdr.ar_fmag = std.elf.ARFMAG.*;
7929 break :member_end next_offset - @sizeOf(std.elf.ar_hdr);
7930 },
7931 .none => {
7932 _, const parent_size = ni.parent(&elf.mf).location(&elf.mf).resolve(&elf.mf);
7933 break :member_end parent_size;
7934 },
7935 } - member_offset;
7936 if (update_size) _ = std.mem.print(&elf.arHdrPtr(ni).ar_size, "{d:<10}", .{
7937 member_size,
7938 }) catch @panic("archive member too large");
7939 },
77217940 }
77227941}
7942
77237943fn updateDynamicEntry(elf: *Elf, key: u32, new_val: u64) void {
77247944 switch (elf.shdrPtr(elf.shndx.dynamic)) {
77257945 inline else => |shdr, class| {
......@@ -7760,7 +7980,7 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
77607980 };
77617981
77627982 // Now that we know the index, we can set the relocation's offset.
7763 elf.shndx.rela_plt.relaSetOffset(elf, @fromBackingInt(@intCast(plt_index)), got_plt_section.vaddr(elf) + got_plt_offset);
7983 elf.shndx.rela_plt.relaSetOffset(elf, @fromBackingInt(plt_index), got_plt_section.vaddr(elf) + got_plt_offset);
77647984
77657985 if (plt_index < elf.plt.count()) {
77667986 // We reused a free entry, so we're already done!
......@@ -8100,7 +8320,10 @@ fn updateExportsInner(
81008320 },
81018321 .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT },
81028322 };
8323
8324 try elf.ensureElfNodeSize();
81038325 while (try elf.idle(pt.tid)) {}
8326
81048327 const value: u64 = Symbol.Id.local(exported_lsi).value(elf);
81058328 const size: u64, const shndx: Section.Index = switch (elf.symPtr(exported_lsi.index())) {
81068329 inline else => |exported_sym| .{
......@@ -8154,6 +8377,16 @@ pub fn deleteExport(elf: *Elf, exported: Zcu.Exported, name: InternPool.NullTerm
81548377 _ = name;
81558378}
81568379
8380fn dumpStderr(elf: *Elf, tid: Zcu.PerThread.Id) !void {
8381 const comp = elf.base.comp;
8382 const io = comp.io;
8383 var buffer: [512]u8 = undefined;
8384 const stderr = try io.lockStderr(&buffer, null);
8385 defer io.unlockStderr();
8386 const w = &stderr.file_writer.interface;
8387 _ = try elf.dump(w, tid);
8388}
8389
81578390pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult {
81588391 if (elf.options.enable_link_snapshots) {
81598392 try elf.printNode(tid, w, .root, 0);
......@@ -8231,13 +8464,14 @@ pub fn printNode(
82318464 {
82328465 const mf_node = &elf.mf.nodes.items[@backingInt(ni)];
82338466 const off, const size = mf_node.location().resolve(&elf.mf);
8234 try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x}{s}{s}{s}{s}\n", .{
8467 try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x}{s}{s}{s}{s}{s}\n", .{
82358468 @backingInt(ni),
82368469 off,
82378470 size,
82388471 mf_node.flags.alignment.toByteUnits(),
82398472 if (mf_node.flags.fixed) " fixed" else "",
82408473 if (mf_node.flags.moved) " moved" else "",
8474 if (mf_node.flags.next_moved) " next_moved" else "",
82418475 if (mf_node.flags.resized) " resized" else "",
82428476 if (mf_node.flags.has_content) " has_content" else "",
82438477 });
......@@ -8273,11 +8507,19 @@ pub fn printNode(
82738507 }
82748508}
82758509
8276fn ensureNodeSize(
8277 elf: *Elf,
8278 node: MappedFile.Node.Index,
8279 need_size: u64,
8280) Error!void {
8510/// Must be called deterministically after any call to `MappedFile.Node.Index.resize`
8511/// (of `elf.ni.elf` or one of its children) before any possible calls to `idle`.
8512fn ensureElfNodeSize(elf: *Elf) MappedFile.Error!void {
8513 if (elf.ni.elf == MappedFile.Node.Index.root) return;
8514 var child_it = elf.ni.elf.reverseChildren(&elf.mf);
8515 const last_end = if (child_it.next()) |last_ni| last_end: {
8516 const last_offset, const last_size = last_ni.location(&elf.mf).resolve(&elf.mf);
8517 break :last_end last_offset + last_size;
8518 } else 0;
8519 try elf.ensureNodeSize(elf.ni.elf, last_end + @sizeOf(std.elf.ar_hdr));
8520}
8521
8522fn ensureNodeSize(elf: *Elf, node: MappedFile.Node.Index, need_size: u64) MappedFile.Error!void {
82818523 _, const node_size = node.location(&elf.mf).resolve(&elf.mf);
82828524 if (need_size <= node_size) return;
82838525 const gpa = elf.base.comp.gpa;
src/link/MappedFile.zig+93-41
......@@ -23,6 +23,7 @@ nodes: std.ArrayList(Node),
2323free_ni: Node.Index,
2424large: std.ArrayList(u64),
2525updates: std.ArrayList(Node.Index),
26/// This progress node's estimated total items is increased once for each node appended to `updates`.
2627update_prog_node: std.Progress.Node,
2728writers: std.SinglyLinkedList,
2829io_err: ?IoError,
......@@ -61,7 +62,7 @@ pub const Error = Allocator.Error || Io.Cancelable || error{
6162 MappedFileIo,
6263};
6364
64pub fn init(file: Io.File, gpa: std.mem.Allocator, io: Io) (Allocator.Error || Io.Cancelable || IoError)!MappedFile {
65pub fn init(file: Io.File, gpa: Allocator, io: Io) (Allocator.Error || Io.Cancelable || IoError)!MappedFile {
6566 var mf: MappedFile = .{
6667 .io = io,
6768 .flags = undefined,
......@@ -105,7 +106,7 @@ pub fn init(file: Io.File, gpa: std.mem.Allocator, io: Io) (Allocator.Error || I
105106 return mf;
106107}
107108
108pub fn deinit(mf: *MappedFile, gpa: std.mem.Allocator) void {
109pub fn deinit(mf: *MappedFile, gpa: Allocator) void {
109110 mf.unmap();
110111 mf.nodes.deinit(gpa);
111112 mf.large.deinit(gpa);
......@@ -133,11 +134,15 @@ pub const Node = extern struct {
133134 moved: bool,
134135 /// Whether this node has been resized.
135136 resized: bool,
137 /// Whether the next sibling has moved or is a different node.
138 next_moved: bool,
136139 /// Whether this node might contain non-zero bytes.
137140 has_content: bool,
138 /// Whether a moved event on this node bubbles down to children.
141 /// Whether `moved` events on this node bubble down to children.
139142 bubbles_moved: bool,
140 unused: @Int(.unsigned, 32 - @bitSizeOf(std.mem.Alignment) - 6) = 0,
143 /// Whether `next_moved` events are reported in `updates`.
144 enable_next_moved: bool,
145 unused: @Int(.unsigned, 32 - @bitSizeOf(std.mem.Alignment) - 8) = 0,
141146 };
142147
143148 pub const Location = union(enum(u1)) {
......@@ -191,6 +196,22 @@ pub const Node = extern struct {
191196 pub fn next(ni: Node.Index, mf: *const MappedFile) Node.Index {
192197 return ni.get(mf).next;
193198 }
199 fn setNext(
200 prev_ni: Node.Index,
201 gpa: Allocator,
202 next_ni: Node.Index,
203 mf: *MappedFile,
204 ) Allocator.Error!void {
205 assert(prev_ni != .none);
206 const prev_next = &prev_ni.get(mf).next;
207 if (prev_next.* == next_ni) return;
208 prev_next.* = next_ni;
209 try prev_ni.nextMoved(gpa, mf);
210 }
211
212 pub fn prev(ni: Node.Index, mf: *const MappedFile) Node.Index {
213 return ni.get(mf).prev;
214 }
194215
195216 pub fn ChildIterator(comptime direction: enum { prev, next }) type {
196217 return struct {
......@@ -211,7 +232,7 @@ pub const Node = extern struct {
211232 return .{ .mf = mf, .ni = ni.get(mf).last };
212233 }
213234
214 pub fn childrenMoved(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) Allocator.Error!void {
235 pub fn childrenMoved(ni: Node.Index, gpa: Allocator, mf: *MappedFile) Allocator.Error!void {
215236 var child_ni = ni.get(mf).last;
216237 while (child_ni != .none) {
217238 try child_ni.moved(gpa, mf);
......@@ -229,11 +250,11 @@ pub const Node = extern struct {
229250 }
230251 return false;
231252 }
232 pub fn moved(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) Allocator.Error!void {
233 try mf.updates.ensureUnusedCapacity(gpa, 1);
253 pub fn moved(ni: Node.Index, gpa: Allocator, mf: *MappedFile) Allocator.Error!void {
254 try mf.updates.ensureUnusedCapacity(gpa, 2);
234255 ni.movedAssumeCapacity(mf);
235256 }
236 pub fn cleanMoved(ni: Node.Index, mf: *const MappedFile) bool {
257 pub fn cleanMoved(ni: Node.Index, mf: *MappedFile) bool {
237258 const node_moved = &ni.get(mf).flags.moved;
238259 defer node_moved.* = false;
239260 return node_moved.*;
......@@ -242,7 +263,11 @@ pub const Node = extern struct {
242263 if (ni.hasMoved(mf)) return;
243264 const node = ni.get(mf);
244265 node.flags.moved = true;
245 if (node.flags.resized) return;
266 switch (node.prev) {
267 .none => {},
268 else => |prev_ni| prev_ni.nextMovedAssumeCapacity(mf),
269 }
270 if (node.flags.resized or node.flags.next_moved) return;
246271 mf.updates.appendAssumeCapacity(ni);
247272 mf.update_prog_node.increaseEstimatedTotalItems(1);
248273 }
......@@ -250,11 +275,11 @@ pub const Node = extern struct {
250275 pub fn hasResized(ni: Node.Index, mf: *const MappedFile) bool {
251276 return ni.get(mf).flags.resized;
252277 }
253 pub fn resized(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) Allocator.Error!void {
278 pub fn resized(ni: Node.Index, gpa: Allocator, mf: *MappedFile) Allocator.Error!void {
254279 try mf.updates.ensureUnusedCapacity(gpa, 1);
255280 ni.resizedAssumeCapacity(mf);
256281 }
257 pub fn cleanResized(ni: Node.Index, mf: *const MappedFile) bool {
282 pub fn cleanResized(ni: Node.Index, mf: *MappedFile) bool {
258283 const node_resized = &ni.get(mf).flags.resized;
259284 defer node_resized.* = false;
260285 return node_resized.*;
......@@ -263,7 +288,28 @@ pub const Node = extern struct {
263288 const node = ni.get(mf);
264289 if (node.flags.resized) return;
265290 node.flags.resized = true;
266 if (node.flags.moved) return;
291 if (node.flags.moved or node.flags.next_moved) return;
292 mf.updates.appendAssumeCapacity(ni);
293 mf.update_prog_node.increaseEstimatedTotalItems(1);
294 }
295
296 pub fn hasNextMoved(ni: Node.Index, mf: *const MappedFile) bool {
297 return ni.get(mf).flags.next_moved;
298 }
299 pub fn nextMoved(ni: Node.Index, gpa: Allocator, mf: *MappedFile) Allocator.Error!void {
300 try mf.updates.ensureUnusedCapacity(gpa, 1);
301 ni.nextMovedAssumeCapacity(mf);
302 }
303 pub fn cleanNextMoved(ni: Node.Index, mf: *MappedFile) bool {
304 const node_next_moved = &ni.get(mf).flags.next_moved;
305 defer node_next_moved.* = false;
306 return node_next_moved.*;
307 }
308 pub fn nextMovedAssumeCapacity(ni: Node.Index, mf: *MappedFile) void {
309 const node = ni.get(mf);
310 if (!node.flags.enable_next_moved or node.flags.next_moved) return;
311 node.flags.next_moved = true;
312 if (node.flags.moved or node.flags.resized) return;
267313 mf.updates.appendAssumeCapacity(ni);
268314 mf.update_prog_node.increaseEstimatedTotalItems(1);
269315 }
......@@ -333,7 +379,7 @@ pub const Node = extern struct {
333379 return mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)];
334380 }
335381
336 pub fn resize(ni: Node.Index, mf: *MappedFile, gpa: std.mem.Allocator, size: u64) Error!void {
382 pub fn resize(ni: Node.Index, mf: *MappedFile, gpa: Allocator, size: u64) Error!void {
337383 mf.resizeNode(gpa, ni, size) catch |err| switch (err) {
338384 error.OutOfMemory,
339385 error.Canceled,
......@@ -360,7 +406,7 @@ pub const Node = extern struct {
360406 pub fn realign(
361407 ni: Node.Index,
362408 mf: *MappedFile,
363 gpa: std.mem.Allocator,
409 gpa: Allocator,
364410 new_alignment: std.mem.Alignment,
365411 opts: RealignNodeOptions,
366412 ) Error!void {
......@@ -384,7 +430,7 @@ pub const Node = extern struct {
384430 pub fn shrink(
385431 ni: Node.Index,
386432 mf: *MappedFile,
387 gpa: std.mem.Allocator,
433 gpa: Allocator,
388434 size: u64,
389435 shift_next: bool,
390436 ) Error!void {
......@@ -392,7 +438,7 @@ pub const Node = extern struct {
392438 mf.updateWriters();
393439 }
394440
395 pub fn writer(ni: Node.Index, mf: *MappedFile, gpa: std.mem.Allocator, w: *Writer) void {
441 pub fn writer(ni: Node.Index, mf: *MappedFile, gpa: Allocator, w: *Writer) void {
396442 w.* = .{
397443 .gpa = gpa,
398444 .mf = mf,
......@@ -419,7 +465,7 @@ pub const Node = extern struct {
419465 }
420466
421467 pub const Writer = struct {
422 gpa: std.mem.Allocator,
468 gpa: Allocator,
423469 mf: *MappedFile,
424470 writer_node: std.SinglyLinkedList.Node,
425471 ni: Node.Index,
......@@ -543,14 +589,13 @@ pub const Node = extern struct {
543589 }
544590};
545591
546fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
592fn addNode(mf: *MappedFile, gpa: Allocator, opts: struct {
547593 parent: Node.Index = .none,
548594 prev: Node.Index = .none,
549595 next: Node.Index = .none,
550596 offset: u64 = 0,
551597 add_node: AddNodeOptions,
552598}) (Allocator.Error || Io.Cancelable || IoError)!Node.Index {
553 if (opts.add_node.moved or opts.add_node.resized) try mf.updates.ensureUnusedCapacity(gpa, 1);
554599 mf.nodes_lock.assertUnlocked();
555600 const location_tag: Node.Location.Tag, const location_payload: Node.Location.Payload = location: {
556601 if (std.math.cast(u32, opts.offset)) |small_offset| break :location .{ .small, .{
......@@ -570,7 +615,7 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
570615 };
571616 switch (opts.prev) {
572617 .none => opts.parent.get(mf).first = free_ni,
573 else => |prev_ni| prev_ni.get(mf).next = free_ni,
618 else => |prev_ni| try prev_ni.setNext(gpa, free_ni, mf),
574619 }
575620 switch (opts.next) {
576621 .none => opts.parent.get(mf).last = free_ni,
......@@ -588,22 +633,27 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
588633 .fixed = opts.add_node.fixed,
589634 .moved = true,
590635 .resized = true,
636 .next_moved = true,
591637 .has_content = false,
592638 .bubbles_moved = opts.add_node.bubbles_moved,
639 .enable_next_moved = opts.add_node.enable_next_moved,
593640 },
594641 .location_payload = location_payload,
595642 };
596643
597644 {
645 defer {
646 free_node.flags.moved = false;
647 free_node.flags.resized = false;
648 free_node.flags.next_moved = false;
649 }
598650 try mf.realignNode(gpa, free_ni, opts.add_node.alignment, .{});
599651 try mf.resizeNode(gpa, free_ni, opts.add_node.size);
600 if (opts.add_node.moved or opts.add_node.resized) try mf.updates.ensureUnusedCapacity(gpa, 1);
601 free_node.flags.moved = false;
602 free_node.flags.resized = false;
603652 }
604 if (opts.add_node.moved) free_ni.movedAssumeCapacity(mf);
605 if (opts.add_node.resized) free_ni.resizedAssumeCapacity(mf);
606653 mf.updateWriters();
654 if (opts.add_node.moved) try free_ni.moved(gpa, mf);
655 if (opts.add_node.resized) try free_ni.resized(gpa, mf);
656 if (opts.add_node.next_moved) try free_ni.nextMoved(gpa, mf);
607657 return free_ni;
608658}
609659
......@@ -613,12 +663,14 @@ pub const AddNodeOptions = struct {
613663 fixed: bool = false,
614664 moved: bool = false,
615665 resized: bool = false,
666 next_moved: bool = false,
616667 bubbles_moved: bool = true,
668 enable_next_moved: bool = false,
617669};
618670
619671pub fn addOnlyChildNode(
620672 mf: *MappedFile,
621 gpa: std.mem.Allocator,
673 gpa: Allocator,
622674 parent_ni: Node.Index,
623675 opts: AddNodeOptions,
624676) Error!Node.Index {
......@@ -641,7 +693,7 @@ pub fn addOnlyChildNode(
641693
642694pub fn addFirstChildNode(
643695 mf: *MappedFile,
644 gpa: std.mem.Allocator,
696 gpa: Allocator,
645697 parent_ni: Node.Index,
646698 opts: AddNodeOptions,
647699) Error!Node.Index {
......@@ -664,7 +716,7 @@ pub fn addFirstChildNode(
664716
665717pub fn addLastChildNode(
666718 mf: *MappedFile,
667 gpa: std.mem.Allocator,
719 gpa: Allocator,
668720 parent_ni: Node.Index,
669721 opts: AddNodeOptions,
670722) Error!Node.Index {
......@@ -694,7 +746,7 @@ pub fn addLastChildNode(
694746
695747pub fn addNodeAfter(
696748 mf: *MappedFile,
697 gpa: std.mem.Allocator,
749 gpa: Allocator,
698750 prev_ni: Node.Index,
699751 opts: AddNodeOptions,
700752) Error!Node.Index {
......@@ -721,7 +773,7 @@ pub fn addNodeAfter(
721773
722774fn shrinkNode(
723775 mf: *MappedFile,
724 gpa: std.mem.Allocator,
776 gpa: Allocator,
725777 ni: Node.Index,
726778 size: u64,
727779 shift_next: bool,
......@@ -740,7 +792,7 @@ fn shrinkNode(
740792 }
741793
742794 try mf.large.ensureUnusedCapacity(gpa, 4);
743 try mf.updates.ensureUnusedCapacity(gpa, 2);
795 try mf.updates.ensureUnusedCapacity(gpa, 4);
744796
745797 ni.setLocationAssumeCapacity(mf, old_offset, size);
746798 if (!shift_next or node.next == .none) return;
......@@ -765,7 +817,7 @@ fn shrinkNode(
765817
766818fn resizeNode(
767819 mf: *MappedFile,
768 gpa: std.mem.Allocator,
820 gpa: Allocator,
769821 ni: Node.Index,
770822 requested_size: u64,
771823) (Allocator.Error || Io.Cancelable || IoError)!void {
......@@ -904,11 +956,11 @@ fn resizeNode(
904956 next_ni.get(mf).prev = node.prev;
905957 switch (node.prev) {
906958 .none => parent.first = next_ni,
907 else => |prev_ni| prev_ni.get(mf).next = next_ni,
959 else => |prev_ni| try prev_ni.setNext(gpa, next_ni, mf),
908960 }
909 last.next = ni;
961 try parent.last.setNext(gpa, ni, mf);
910962 node.prev = parent.last;
911 node.next = .none;
963 try ni.setNext(gpa, .none, mf);
912964 parent.last = ni;
913965 if (node.flags.has_content) {
914966 const parent_file_offset = node.parent.fileLocation(mf, false).offset;
......@@ -972,13 +1024,13 @@ fn resizeNode(
9721024 if (parent.last != first_floating_ni) {
9731025 first_floating.prev = parent.last;
9741026 parent.last = first_floating_ni;
975 last.next = first_floating_ni;
976 last_fixed.next = first_floating.next;
1027 try parent.last.setNext(gpa, first_floating_ni, mf);
1028 try last_fixed_ni.setNext(gpa, first_floating.next, mf);
9771029 switch (first_floating.next) {
9781030 .none => {},
9791031 else => |next_ni| next_ni.get(mf).prev = last_fixed_ni,
9801032 }
981 first_floating.next = .none;
1033 try first_floating_ni.setNext(gpa, .none, mf);
9821034 }
9831035 if (first_floating.flags.has_content) {
9841036 const parent_file_offset =
......@@ -1040,7 +1092,7 @@ fn resizeNode(
10401092
10411093fn realignNode(
10421094 mf: *MappedFile,
1043 gpa: std.mem.Allocator,
1095 gpa: Allocator,
10441096 ni: Node.Index,
10451097 new_alignment: std.mem.Alignment,
10461098 opts: Node.Index.RealignNodeOptions,
......@@ -1241,9 +1293,9 @@ fn copyFileRange(
12411293 return size - remaining_size;
12421294}
12431295
1244fn ensureCapacityForSetLocation(mf: *MappedFile, gpa: std.mem.Allocator) Allocator.Error!void {
1296fn ensureCapacityForSetLocation(mf: *MappedFile, gpa: Allocator) Allocator.Error!void {
12451297 try mf.large.ensureUnusedCapacity(gpa, 2);
1246 try mf.updates.ensureUnusedCapacity(gpa, 1);
1298 try mf.updates.ensureUnusedCapacity(gpa, 2);
12471299}
12481300
12491301pub fn ensureTotalCapacity(mf: *MappedFile, new_capacity: usize) Error!void {