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 {...@@ -3272,12 +3272,12 @@ pub const ar_hdr = extern struct {
3272 ar_fmag: [2]u8,3272 ar_fmag: [2]u8,
32733273
3274 pub fn date(self: ar_hdr) std.fmt.ParseIntError!u64 {3274 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, " ");
3276 return std.fmt.parseInt(u64, value, 10);3276 return std.fmt.parseInt(u64, value, 10);
3277 }3277 }
32783278
3279 pub fn size(self: ar_hdr) std.fmt.ParseIntError!u32 {3279 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, " ");
3281 return std.fmt.parseInt(u32, value, 10);3281 return std.fmt.parseInt(u32, value, 10);
3282 }3282 }
32833283
...@@ -3311,7 +3311,7 @@ pub const ar_hdr = extern struct {...@@ -3311,7 +3311,7 @@ pub const ar_hdr = extern struct {
3311 pub fn nameOffset(self: ar_hdr) std.fmt.ParseIntError!?u32 {3311 pub fn nameOffset(self: ar_hdr) std.fmt.ParseIntError!?u32 {
3312 const value = &self.ar_name;3312 const value = &self.ar_name;
3313 if (value[0] != '/') return null;3313 if (value[0] != '/') return null;
3314 const trimmed = mem.trimEnd(u8, value, &[_]u8{0x20});3314 const trimmed = mem.trimEnd(u8, value, " ");
3315 return try std.fmt.parseInt(u32, trimmed[1..], 10);3315 return try std.fmt.parseInt(u32, trimmed[1..], 10);
3316 }3316 }
3317};3317};
...@@ -3319,7 +3319,7 @@ pub const ar_hdr = extern struct {...@@ -3319,7 +3319,7 @@ pub const ar_hdr = extern struct {
3319fn genSpecialMemberName(comptime name: []const u8) *const [16]u8 {3319fn genSpecialMemberName(comptime name: []const u8) *const [16]u8 {
3320 assert(name.len <= 16);3320 assert(name.len <= 16);
3321 const padding = 16 - name.len;3321 const padding = 16 - name.len;
3322 return name ++ @as([padding]u8, @splat(0x20));3322 return name ++ @as([padding]u8, @splat(' '));
3323}3323}
33243324
3325// Archive files start with the ARMAG identifying string. Then follows a3325// 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),...@@ -126,8 +126,14 @@ needed: std.array_hash_map.Auto(String(.dynstr), void),
126inputs: std.ArrayList(struct {126inputs: std.ArrayList(struct {
127 path: std.Build.Cache.Path,127 path: std.Build.Cache.Path,
128 member: ?[]const u8,128 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 },
130}),135}),
136input_pending_index: u32,
131input_sections: std.ArrayList(InputSection),137input_sections: std.ArrayList(InputSection),
132input_section_pending_index: u32,138input_section_pending_index: u32,
133navs: std.array_hash_map.Auto(InternPool.Nav.Index, struct {139navs: std.array_hash_map.Auto(InternPool.Nav.Index, struct {
...@@ -181,7 +187,10 @@ input_prog_node: std.Progress.Node,...@@ -181,7 +187,10 @@ input_prog_node: std.Progress.Node,
181const Error = link.Error || error{MappedFileIo};187const Error = link.Error || error{MappedFileIo};
182188
183const Node = union(enum) {189const Node = union(enum) {
184 file,190 archive,
191 /// This includes the archive magic and long file member.
192 archive_header,
193 elf,
185 ehdr,194 ehdr,
186 shdr,195 shdr,
187 segment: u32,196 segment: u32,
...@@ -189,6 +198,8 @@ const Node = union(enum) {...@@ -189,6 +198,8 @@ const Node = union(enum) {
189 ///198 ///
190 /// The section '.dynamic' may contain relocations via `elf.dynamic_first_symbol_reloc`.199 /// The section '.dynamic' may contain relocations via `elf.dynamic_first_symbol_reloc`.
191 section: Section.Index,200 section: Section.Index,
201 /// Only valid for static libraries, represents one non-zcu archive member.
202 input_member: InputIndex,
192 /// May contain relocations.203 /// May contain relocations.
193 input_section: InputSection.Index,204 input_section: InputSection.Index,
194 /// Value is the name of a global which has an entry in `elf.copied_globals`, so, a global for205 /// 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) {...@@ -219,19 +230,23 @@ const Node = union(enum) {
219 return elf.inputs.items[@backingInt(ii)].member;230 return elf.inputs.items[@backingInt(ii)].member;
220 }231 }
221232
233 pub fn node(ii: InputIndex, elf: *const Elf) MappedFile.Node.Index {
234 return elf.inputs.items[@backingInt(ii)].extra.node;
235 }
236
222 pub fn fileSymbol(ii: InputIndex, elf: *const Elf) Symbol.LocalIndex {237 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;
224 }239 }
225240
226 pub fn localSymbolRange(ii: InputIndex, elf: *Elf) [2]Symbol.LocalIndex {241 pub fn localSymbolRange(ii: InputIndex, elf: *Elf) [2]Symbol.LocalIndex {
227 if (@backingInt(ii) + 1 < elf.inputs.items.len) {242 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);
229 return .{ ii.fileSymbol(elf), next_ii.fileSymbol(elf) };244 return .{ ii.fileSymbol(elf), next_ii.fileSymbol(elf) };
230 } else {245 } else {
231 const local_symbols_len = switch (elf.shdrPtr(.symtab)) {246 const local_symbols_len = switch (elf.shdrPtr(.symtab)) {
232 inline else => |shdr| elf.targetLoad(&shdr.info),247 inline else => |shdr| elf.targetLoad(&shdr.info),
233 };248 };
234 return .{ ii.fileSymbol(elf), @fromBackingInt(@intCast(local_symbols_len)) };249 return .{ ii.fileSymbol(elf), @fromBackingInt(local_symbols_len) };
235 }250 }
236 }251 }
237 };252 };
...@@ -315,15 +330,16 @@ const Node = union(enum) {...@@ -315,15 +330,16 @@ const Node = union(enum) {
315 };330 };
316331
317 pub const Known = struct {332 pub const Known = struct {
318 comptime file: MappedFile.Node.Index = .root,333 archive: MappedFile.Node.Index,
319 comptime ehdr: MappedFile.Node.Index = @fromBackingInt(@intCast(1)),334 archive_header: MappedFile.Node.Index,
320 comptime shdr: MappedFile.Node.Index = @fromBackingInt(@intCast(2)),335 elf: MappedFile.Node.Index,
321 comptime rodata: MappedFile.Node.Index = @fromBackingInt(@intCast(3)),336 ehdr: MappedFile.Node.Index,
322 comptime phdr: MappedFile.Node.Index = @fromBackingInt(@intCast(4)),337 shdr: MappedFile.Node.Index,
323 comptime text: MappedFile.Node.Index = @fromBackingInt(@intCast(5)),338 rodata: MappedFile.Node.Index,
324 comptime data: MappedFile.Node.Index = @fromBackingInt(@intCast(6)),339 phdr: MappedFile.Node.Index,
325 comptime data_rel_ro: MappedFile.Node.Index = @fromBackingInt(@intCast(7)),340 text: MappedFile.Node.Index,
326341 data: MappedFile.Node.Index,
342 data_rel_ro: MappedFile.Node.Index,
327 tls: MappedFile.Node.Index,343 tls: MappedFile.Node.Index,
328 };344 };
329345
...@@ -333,11 +349,11 @@ const Node = union(enum) {...@@ -333,11 +349,11 @@ const Node = union(enum) {
333349
334 /// In this linker implementation, `link.File.AtomId` is a type-erased `MappedFile.Node.Index`.350 /// In this linker implementation, `link.File.AtomId` is a type-erased `MappedFile.Node.Index`.
335 fn toAtom(ni: MappedFile.Node.Index) link.File.AtomId {351 fn toAtom(ni: MappedFile.Node.Index) link.File.AtomId {
336 return @fromBackingInt(@intCast(@backingInt(ni)));352 return @fromBackingInt(@backingInt(ni));
337 }353 }
338 /// In this linker implementation, `link.File.AtomId` is a type-erased `MappedFile.Node.Index`.354 /// In this linker implementation, `link.File.AtomId` is a type-erased `MappedFile.Node.Index`.
339 fn fromAtom(atom: link.File.AtomId) MappedFile.Node.Index {355 fn fromAtom(atom: link.File.AtomId) MappedFile.Node.Index {
340 return @fromBackingInt(@intCast(@backingInt(atom)));356 return @fromBackingInt(@backingInt(atom));
341 }357 }
342};358};
343359
...@@ -424,13 +440,13 @@ const Section = struct {...@@ -424,13 +440,13 @@ const Section = struct {
424 fn unwrap(opt: RelaIndex.Optional) ?RelaIndex {440 fn unwrap(opt: RelaIndex.Optional) ?RelaIndex {
425 return switch (opt) {441 return switch (opt) {
426 .none => null,442 .none => null,
427 _ => @fromBackingInt(@intCast(@backingInt(opt))),443 _ => @fromBackingInt(@backingInt(opt)),
428 };444 };
429 }445 }
430 };446 };
431447
432 fn toOptional(i: RelaIndex) RelaIndex.Optional {448 fn toOptional(i: RelaIndex) RelaIndex.Optional {
433 return @fromBackingInt(@intCast(@backingInt(i)));449 return @fromBackingInt(@backingInt(i));
434 }450 }
435 };451 };
436452
...@@ -465,8 +481,8 @@ const Section = struct {...@@ -465,8 +481,8 @@ const Section = struct {
465481
466 pub fn fromSection(sec: std.elf.Section) Index {482 pub fn fromSection(sec: std.elf.Section) Index {
467 return switch (sec) {483 return switch (sec) {
468 std.elf.SHN_UNDEF...std.elf.SHN_LORESERVE - 1 => @fromBackingInt(@intCast(sec)),484 std.elf.SHN_UNDEF...std.elf.SHN_LORESERVE - 1 => @fromBackingInt(sec),
469 std.elf.SHN_LORESERVE...std.elf.SHN_HIRESERVE => @fromBackingInt(@intCast(reserve(sec))),485 std.elf.SHN_LORESERVE...std.elf.SHN_HIRESERVE => @fromBackingInt(reserve(sec)),
470 };486 };
471 }487 }
472 pub fn toSection(s: Index) ?std.elf.Section {488 pub fn toSection(s: Index) ?std.elf.Section {
...@@ -485,7 +501,7 @@ const Section = struct {...@@ -485,7 +501,7 @@ const Section = struct {
485501
486 fn name(s: Index, elf: *Elf) String(.shstrtab) {502 fn name(s: Index, elf: *Elf) String(.shstrtab) {
487 return switch (elf.shdrPtr(s)) {503 return switch (elf.shdrPtr(s)) {
488 inline else => |shdr| @fromBackingInt(@intCast(elf.targetLoad(&shdr.name))),504 inline else => |shdr| @fromBackingInt(elf.targetLoad(&shdr.name)),
489 };505 };
490 }506 }
491507
...@@ -928,21 +944,7 @@ const GotReloc = struct {...@@ -928,21 +944,7 @@ const GotReloc = struct {
928 }944 }
929 }945 }
930 fn applyInner(reloc: *const GotReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void {946 fn applyInner(reloc: *const GotReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void {
931 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {947 const dest_vaddr = elf.getNodeVAddr(reloc.node) + reloc.offset;
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;
946 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];948 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];
947949
948 const got_vaddr = elf.shndx.got.vaddr(elf);950 const got_vaddr = elf.shndx.got.vaddr(elf);
...@@ -1131,12 +1133,12 @@ pub const MachineRelocType = union {...@@ -1131,12 +1133,12 @@ pub const MachineRelocType = union {
11311133
1132 pub fn wrap(int: u32, elf: *const Elf) MachineRelocType {1134 pub fn wrap(int: u32, elf: *const Elf) MachineRelocType {
1133 return switch (elf.ehdrMachine()) {1135 return switch (elf.ehdrMachine()) {
1134 .AARCH64 => .{ .AARCH64 = @fromBackingInt(@intCast(int)) },1136 .AARCH64 => .{ .AARCH64 = @fromBackingInt(int) },
1135 .LOONGARCH => .{ .LARCH = @fromBackingInt(@intCast(int)) },1137 .LOONGARCH => .{ .LARCH = @fromBackingInt(int) },
1136 .PPC64 => .{ .PPC64 = @fromBackingInt(@intCast(int)) },1138 .PPC64 => .{ .PPC64 = @fromBackingInt(int) },
1137 .RISCV => .{ .RISCV = @fromBackingInt(@intCast(int)) },1139 .RISCV => .{ .RISCV = @fromBackingInt(int) },
1138 .SPARCV9 => .{ .SPARC = @fromBackingInt(@intCast(int)) },1140 .SPARCV9 => .{ .SPARC = @fromBackingInt(int) },
1139 .X86_64 => .{ .X86_64 = @fromBackingInt(@intCast(int)) },1141 .X86_64 => .{ .X86_64 = @fromBackingInt(int) },
1140 };1142 };
1141 }1143 }
1142 pub fn unwrap(rt: MachineRelocType, elf: *const Elf) u32 {1144 pub fn unwrap(rt: MachineRelocType, elf: *const Elf) u32 {
...@@ -1646,21 +1648,7 @@ const SymbolReloc = struct {...@@ -1646,21 +1648,7 @@ const SymbolReloc = struct {
1646 }1648 }
1647 }1649 }
1648 fn applyInner(reloc: *const SymbolReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void {1650 fn applyInner(reloc: *const SymbolReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void {
1649 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {1651 const dest_vaddr = elf.getNodeVAddr(reloc.node) + reloc.offset;
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;
1664 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];1652 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];
16651653
1666 const addend: u64 = @bitCast(reloc.addend);1654 const addend: u64 = @bitCast(reloc.addend);
...@@ -1875,7 +1863,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L...@@ -1875,7 +1863,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
18751863
1876 // `shdr.info` stores the index of the first global symbol. We will replace it with our1864 // `shdr.info` stores the index of the first global symbol. We will replace it with our
1877 // new local symbol, and move the global symbol to a new index at the end of the symtab.1865 // 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
1880 const old_size = elf.targetLoad(&shdr.size);1868 const old_size = elf.targetLoad(&shdr.size);
1881 const new_size = old_size + ent_size;1869 const new_size = old_size + ent_size;
...@@ -1897,7 +1885,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L...@@ -1897,7 +1885,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
1897 // ...then the `elf.symtab` metadata...1885 // ...then the `elf.symtab` metadata...
1898 new_index.ptr(elf).* = target_index.ptr(elf).*;1886 new_index.ptr(elf).* = target_index.ptr(elf).*;
1899 // ...then update the `elf.globals` tracking.1887 // ...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));
1901 elf.globalByName(global_name).?.symtab_index = new_index;1889 elf.globalByName(global_name).?.symtab_index = new_index;
19021890
1903 if (elf.ehdrType() == .REL and target_index.ptr(elf).first_target_reloc != .none) {1891 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...@@ -1923,7 +1911,7 @@ fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.L
1923 std.mem.byteSwapAllFields(class.ElfN().Sym, target_sym);1911 std.mem.byteSwapAllFields(class.ElfN().Sym, target_sym);
1924 }1912 }
19251913
1926 return @fromBackingInt(@intCast(@backingInt(target_index)));1914 return @fromBackingInt(@backingInt(target_index));
1927 },1915 },
1928 }1916 }
1929}1917}
...@@ -2371,7 +2359,7 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {...@@ -2371,7 +2359,7 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
2371 inline else => |shdr, class| {2359 inline else => |shdr, class| {
2372 // `shdr.info` stores the index of the first global symbol. We are going to swap the2360 // `shdr.info` stores the index of the first global symbol. We are going to swap the
2373 // demoted symbol with that first global symbol, then increment that start index.2361 // 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));
2375 const src_index = global_ptr.symtab_index;2363 const src_index = global_ptr.symtab_index;
23762364
2377 // This global should currently be in the "global symbols" part of the symtab, since our2365 // 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 {...@@ -2387,10 +2375,10 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
2387 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));2375 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));
2388 const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));2376 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));
2391 assert(elf.globalByName(this_name).? == global_ptr);2379 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));
2394 const other_global_ptr = elf.globalByName(other_name).?;2382 const other_global_ptr = elf.globalByName(other_name).?;
2395 assert(other_global_ptr.symtab_index == dest_index);2383 assert(other_global_ptr.symtab_index == dest_index);
23962384
...@@ -2426,7 +2414,7 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {...@@ -2426,7 +2414,7 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
2426 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));2414 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));
2427 const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class));2415 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));
2430 const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf));2418 const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf));
2431 const moved_global_ptr = elf.globalByName(moved_name).?;2419 const moved_global_ptr = elf.globalByName(moved_name).?;
24322420
...@@ -2505,7 +2493,7 @@ const Symbol = struct {...@@ -2505,7 +2493,7 @@ const Symbol = struct {
2505 _,2493 _,
25062494
2507 fn index(li: LocalIndex) Index {2495 fn index(li: LocalIndex) Index {
2508 return @fromBackingInt(@intCast(@backingInt(li)));2496 return @fromBackingInt(@backingInt(li));
2509 }2497 }
2510 };2498 };
25112499
...@@ -2527,16 +2515,16 @@ const Symbol = struct {...@@ -2527,16 +2515,16 @@ const Symbol = struct {
2527 global: String(.strtab),2515 global: String(.strtab),
2528 } {2516 } {
2529 return switch (s.kind) {2517 return switch (s.kind) {
2530 .local => .{ .local = @fromBackingInt(@intCast(s.raw)) },2518 .local => .{ .local = @fromBackingInt(s.raw) },
2531 .global => .{ .global = @fromBackingInt(@intCast(s.raw)) },2519 .global => .{ .global = @fromBackingInt(s.raw) },
2532 };2520 };
2533 }2521 }
25342522
2535 fn toTypeErased(s: Symbol.Id) link.File.SymbolId {2523 fn toTypeErased(s: Symbol.Id) link.File.SymbolId {
2536 return @fromBackingInt(@intCast(@as(u32, @bitCast(s))));2524 return @bitCast(s);
2537 }2525 }
2538 fn fromTypeErased(s: link.File.SymbolId) Symbol.Id {2526 fn fromTypeErased(s: link.File.SymbolId) Symbol.Id {
2539 return @bitCast(@backingInt(s));2527 return @bitCast(s);
2540 }2528 }
25412529
2542 fn index(s: Symbol.Id, elf: *const Elf) Symbol.Index {2530 fn index(s: Symbol.Id, elf: *const Elf) Symbol.Index {
...@@ -2648,24 +2636,10 @@ const Symbol = struct {...@@ -2648,24 +2636,10 @@ const Symbol = struct {
2648 .yes_textrel => elf.textrel_count += 1,2636 .yes_textrel => elf.textrel_count += 1,
2649 .yes => {},2637 .yes => {},
2650 }2638 }
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 };
2665 // There is capacity for a relocation because we just deleted one earlier.2639 // There is capacity for a relocation because we just deleted one earlier.
2666 reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{2640 reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
2667 .type = .relative(elf),2641 .type = .relative(elf),
2668 .offset = node_vaddr + reloc.offset,2642 .offset = elf.getNodeVAddr(reloc.node) + reloc.offset,
2669 .raw_sym_index = 0,2643 .raw_sym_index = 0,
2670 .addend = 0,2644 .addend = 0,
2671 }).toOptional();2645 }).toOptional();
...@@ -2771,11 +2745,14 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {...@@ -2771,11 +2745,14 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
27712745
2772pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {2746pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {
2773 const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) {2747 const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) {
2774 .file,2748 .archive,
2749 .archive_header,
2750 .elf,
2775 .ehdr,2751 .ehdr,
2776 .shdr,2752 .shdr,
2777 .segment,2753 .segment,
2778 .section,2754 .section,
2755 .input_member,
2779 .input_section,2756 .input_section,
2780 .copied_global,2757 .copied_global,
2781 => unreachable,2758 => unreachable,
...@@ -3001,12 +2978,12 @@ fn String(section: StringSection) type {...@@ -3001,12 +2978,12 @@ fn String(section: StringSection) type {
3001}2978}
3002fn string(elf: *Elf, comptime section: StringSection, key: []const u8) Error!String(section) {2979fn string(elf: *Elf, comptime section: StringSection, key: []const u8) Error!String(section) {
3003 const st: *StringTable = &@field(elf, @tagName(section));2980 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));
3005}2982}
3006/// Like `string`, but asserts that the string is already in `section`.2983/// Like `string`, but asserts that the string is already in `section`.
3007fn stringExisting(elf: *Elf, comptime section: StringSection, key: []const u8) String(section) {2984fn stringExisting(elf: *Elf, comptime section: StringSection, key: []const u8) String(section) {
3008 const st: *StringTable = &@field(elf, @tagName(section));2985 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));
3010}2987}
30112988
3012const StringTable = struct {2989const StringTable = struct {
...@@ -3172,6 +3149,16 @@ fn create(...@@ -3172,6 +3149,16 @@ fn create(
3172 .options = options,3149 .options = options,
3173 .mf = try .init(file, comp.gpa, io),3150 .mf = try .init(file, comp.gpa, io),
3174 .ni = .{3151 .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,
3175 .tls = .none,3162 .tls = .none,
3176 },3163 },
3177 .nodes = .empty,3164 .nodes = .empty,
...@@ -3218,6 +3205,7 @@ fn create(...@@ -3218,6 +3205,7 @@ fn create(
3218 .dynamic_first_symbol_reloc = .none,3205 .dynamic_first_symbol_reloc = .none,
3219 .needed = .empty,3206 .needed = .empty,
3220 .inputs = .empty,3207 .inputs = .empty,
3208 .input_pending_index = 0,
3221 .input_sections = .empty,3209 .input_sections = .empty,
3222 .input_section_pending_index = 0,3210 .input_section_pending_index = 0,
3223 .navs = .empty,3211 .navs = .empty,
...@@ -3293,6 +3281,7 @@ fn initHeaders(...@@ -3293,6 +3281,7 @@ fn initHeaders(
3293 const comp = elf.base.comp;3281 const comp = elf.base.comp;
3294 const gpa = comp.gpa;3282 const gpa = comp.gpa;
32953283
3284 const is_archive = comp.config.output_mode == .Lib and comp.config.link_mode == .static;
3296 const have_dynamic_section = switch (@"type") {3285 const have_dynamic_section = switch (@"type") {
3297 .REL => false,3286 .REL => false,
3298 .EXEC => comp.config.link_mode == .dynamic,3287 .EXEC => comp.config.link_mode == .dynamic,
...@@ -3389,7 +3378,8 @@ fn initHeaders(...@@ -3389,7 +3378,8 @@ fn initHeaders(
3389 }, phnum };3378 }, phnum };
3390 };3379 };
33913380
3392 const expected_nodes_len = 3 + // `.file`, `.ehdr`, and `.shdr` nodes3381 const expected_nodes_len = @as(usize, if (is_archive) 2 else 0) + // .archive, .archive_header
3382 3 + // `.file`, `.ehdr`, and `.shdr` nodes
3393 (shnum - 1) + // -1 because the null shdr does not have a `.section` node3383 (shnum - 1) + // -1 because the null shdr does not have a `.section` node
3394 (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node3384 (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node
33953385
...@@ -3398,17 +3388,49 @@ fn initHeaders(...@@ -3398,17 +3388,49 @@ fn initHeaders(
3398 try elf.section_by_name.ensureUnusedCapacity(gpa, shnum);3388 try elf.section_by_name.ensureUnusedCapacity(gpa, shnum);
3399 try elf.phdrs.resize(gpa, phnum);3389 try elf.phdrs.resize(gpa, phnum);
3400 try elf.symtab.ensureTotalCapacity(gpa, 1);3390 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
3403 const entsize: struct { ph: u32, sh: u32 } = switch (class) {3425 const entsize: struct { ph: u32, sh: u32 } = switch (class) {
3404 .NONE, _ => unreachable,3426 .NONE, _ => unreachable,
3405 inline else => |ct_class| entsize: {3427 inline else => |ct_class| entsize: {
3406 const ElfN = ct_class.ElfN();3428 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, .{
3408 .size = @sizeOf(ElfN.Ehdr),3430 .size = @sizeOf(ElfN.Ehdr),
3409 .alignment = addr_align,3431 .alignment = addr_align,
3410 .fixed = true,3432 .fixed = true,
3411 }));3433 });
3412 elf.nodes.appendAssumeCapacity(.ehdr);3434 elf.nodes.appendAssumeCapacity(.ehdr);
34133435
3414 const ehdr: *ElfN.Ehdr = @ptrCast(@alignCast(elf.ni.ehdr.slice(&elf.mf)));3436 const ehdr: *ElfN.Ehdr = @ptrCast(@alignCast(elf.ni.ehdr.slice(&elf.mf)));
...@@ -3461,12 +3483,12 @@ fn initHeaders(...@@ -3461,12 +3483,12 @@ fn initHeaders(
3461 },3483 },
3462 };3484 };
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, .{
3465 .size = 1 * entsize.sh, // as above, only the null shdr initially3487 .size = 1 * entsize.sh, // as above, only the null shdr initially
3466 .alignment = elf.mf.flags.block_size,3488 .alignment = elf.mf.flags.block_size,
3467 .moved = true,3489 .moved = true,
3468 .resized = true,3490 .resized = true,
3469 }));3491 });
3470 elf.nodes.appendAssumeCapacity(.shdr);3492 elf.nodes.appendAssumeCapacity(.shdr);
34713493
3472 const page_align: std.mem.Alignment = .fromByteUnits(switch (machine) {3494 const page_align: std.mem.Alignment = .fromByteUnits(switch (machine) {
...@@ -3491,45 +3513,45 @@ fn initHeaders(...@@ -3491,45 +3513,45 @@ fn initHeaders(
3491 });3513 });
34923514
3493 var ph_vaddr: u32 = if (@"type" != .REL) ph_vaddr: {3515 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, .{
3495 .alignment = elf.mf.flags.block_size,3517 .alignment = elf.mf.flags.block_size,
3496 .moved = true,3518 .moved = true,
3497 .bubbles_moved = false,3519 .bubbles_moved = false,
3498 }));3520 });
3499 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.rodata });3521 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.rodata });
3500 elf.phdrs.items[phndx.rodata] = elf.ni.rodata;3522 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, .{
3503 .size = @as(u64, phnum) * entsize.ph,3525 .size = @as(u64, phnum) * entsize.ph,
3504 .alignment = addr_align,3526 .alignment = addr_align,
3505 .moved = true,3527 .moved = true,
3506 .resized = true,3528 .resized = true,
3507 .bubbles_moved = false,3529 .bubbles_moved = false,
3508 }));3530 });
3509 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.phdr });3531 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.phdr });
3510 elf.phdrs.items[phndx.phdr] = elf.ni.phdr;3532 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, .{
3513 .alignment = elf.mf.flags.block_size,3535 .alignment = elf.mf.flags.block_size,
3514 .moved = true,3536 .moved = true,
3515 .bubbles_moved = false,3537 .bubbles_moved = false,
3516 }));3538 });
3517 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.text });3539 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.text });
3518 elf.phdrs.items[phndx.text] = elf.ni.text;3540 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, .{
3521 .alignment = elf.mf.flags.block_size,3543 .alignment = elf.mf.flags.block_size,
3522 .moved = true,3544 .moved = true,
3523 .bubbles_moved = false,3545 .bubbles_moved = false,
3524 }));3546 });
3525 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data });3547 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data });
3526 elf.phdrs.items[phndx.data] = elf.ni.data;3548 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, .{
3529 .alignment = elf.mf.flags.block_size,3551 .alignment = elf.mf.flags.block_size,
3530 .moved = true,3552 .moved = true,
3531 .bubbles_moved = false,3553 .bubbles_moved = false,
3532 }));3554 });
3533 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro });3555 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro });
3534 elf.phdrs.items[phndx.relro] = elf.ni.data_rel_ro;3556 elf.phdrs.items[phndx.relro] = elf.ni.data_rel_ro;
35353557
...@@ -3706,7 +3728,7 @@ fn initHeaders(...@@ -3706,7 +3728,7 @@ fn initHeaders(
3706 .node = .none,3728 .node = .none,
3707 .first_target_reloc = .none,3729 .first_target_reloc = .none,
3708 };3730 };
3709 assert(.symtab == try elf.addSection(elf.ni.file, .{3731 assert(.symtab == try elf.addSection(elf.ni.elf, .{
3710 .type = .SYMTAB,3732 .type = .SYMTAB,
3711 .size = @sizeOf(ElfN.Sym) * 1,3733 .size = @sizeOf(ElfN.Sym) * 1,
3712 .addralign = addr_align,3734 .addralign = addr_align,
...@@ -3729,7 +3751,7 @@ fn initHeaders(...@@ -3729,7 +3751,7 @@ fn initHeaders(
3729 ehdr.shstrndx = ehdr.shnum;3751 ehdr.shstrndx = ehdr.shnum;
3730 },3752 },
3731 }3753 }
3732 assert(.shstrtab == try elf.addSection(elf.ni.file, .{3754 assert(.shstrtab == try elf.addSection(elf.ni.elf, .{
3733 .type = .STRTAB,3755 .type = .STRTAB,
3734 .size = 1,3756 .size = 1,
3735 .entsize = 1,3757 .entsize = 1,
...@@ -3740,7 +3762,7 @@ fn initHeaders(...@@ -3740,7 +3762,7 @@ fn initHeaders(
3740 try Section.Index.symtab.rename(elf, ".symtab");3762 try Section.Index.symtab.rename(elf, ".symtab");
3741 try Section.Index.shstrtab.rename(elf, ".shstrtab");3763 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, .{
3744 .name = ".strtab",3766 .name = ".strtab",
3745 .type = .STRTAB,3767 .type = .STRTAB,
3746 .size = 1,3768 .size = 1,
...@@ -4210,6 +4232,8 @@ fn initHeaders(...@@ -4210,6 +4232,8 @@ fn initHeaders(
4210 break :str try elf.string(.dynstr, slice);4232 break :str try elf.string(.dynstr, slice);
4211 },4233 },
4212 };4234 };
4235
4236 try elf.ensureElfNodeSize();
4213}4237}
42144238
4215pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {4239pub 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 {...@@ -4221,10 +4245,8 @@ pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {
4221 break :count count;4245 break :count count;
4222 });4246 });
4223 elf.mf.update_prog_node = prog_node.start("Relocations", elf.mf.updates.items.len);4247 elf.mf.update_prog_node = prog_node.start("Relocations", elf.mf.updates.items.len);
4224 elf.input_prog_node = prog_node.start(4248 elf.input_prog_node = prog_node.start("Inputs", (elf.inputs.items.len - elf.input_pending_index) +
4225 "Inputs",4249 (elf.input_sections.items.len - elf.input_section_pending_index));
4226 elf.input_sections.items.len - elf.input_section_pending_index,
4227 );
4228}4250}
42294251
4230pub fn endProgress(elf: *Elf) void {4252pub fn endProgress(elf: *Elf) void {
...@@ -4244,13 +4266,15 @@ fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node {...@@ -4244,13 +4266,15 @@ fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node {
4244/// Asserts that `ni` is a section, input section, copied global, NAV, UAV, or lazy code/data.4266/// Asserts that `ni` is a section, input section, copied global, NAV, UAV, or lazy code/data.
4245fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {4267fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {
4246 return switch (elf.getNode(ni)) {4268 return switch (elf.getNode(ni)) {
4247 .file => unreachable,4269 .archive,
4248 .ehdr => unreachable,4270 .archive_header,
4249 .shdr => unreachable,4271 .elf,
4250 .segment => unreachable,4272 .ehdr,
42514273 .shdr,
4274 .segment,
4275 .input_member,
4276 => unreachable,
4252 .section => |shndx| shndx,4277 .section => |shndx| shndx,
4253
4254 .input_section,4278 .input_section,
4255 .copied_global,4279 .copied_global,
4256 .nav,4280 .nav,
...@@ -4260,21 +4284,44 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {...@@ -4260,21 +4284,44 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {
4260 => elf.getNode(ni.parent(&elf.mf)).section,4284 => elf.getNode(ni.parent(&elf.mf)).section,
4261 };4285 };
4262}4286}
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}
4263fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {4307fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
4264 const parent_vaddr = switch (elf.getNode(ni.parent(&elf.mf))) {4308 const parent_vaddr = switch (elf.getNode(ni.parent(&elf.mf))) {
4265 .file => return 0,4309 .archive, .archive_header => unreachable,
4310 .elf => return 0,
4266 .ehdr, .shdr => unreachable,4311 .ehdr, .shdr => unreachable,
4267 .segment => |phndx| switch (elf.phdrSlice()) {4312 .segment => |phndx| switch (elf.phdrSlice()) {
4268 inline else => |phdr| elf.targetLoad(&phdr[phndx].vaddr),4313 inline else => |phdr| elf.targetLoad(&phdr[phndx].vaddr),
4269 },4314 },
4270 .section => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf),4315 .section => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf),
4271 .input_section => unreachable,4316 .input_member, .input_section, .copied_global => unreachable,
4272 .copied_global => unreachable,
4273 inline .nav, .uav, .lazy_code, .lazy_const_data => |i| Symbol.Id.local(i.symbol(elf)).value(elf),4317 inline .nav, .uav, .lazy_code, .lazy_const_data => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
4274 };4318 };
4275 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);4319 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);
4276 return parent_vaddr + offset;4320 return parent_vaddr + offset;
4277}4321}
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
4279/// Deletes any existing relocations in the given node, and marks the start of the node's contiguous4326/// Deletes any existing relocations in the given node, and marks the start of the node's contiguous
4280/// sequence of relocations, so that the caller may append the node's updated relocations.4327/// 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 {...@@ -4283,12 +4330,16 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
4283/// the special-case sections '.plt' and '.dynamic'.4330/// the special-case sections '.plt' and '.dynamic'.
4284fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {4331fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
4285 const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) {4332 const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) {
4286 .file => unreachable, // cannot contain relocs4333 .archive,
4287 .ehdr => unreachable, // cannot contain relocs4334 .archive_header,
4288 .shdr => unreachable, // cannot contain relocs4335 .elf,
4289 .segment => unreachable, // cannot contain relocs4336 .ehdr,
4337 .shdr,
4338 .segment,
4339 .input_member,
4340 .copied_global,
4341 => unreachable, // cannot contain relocs
4290 .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported)4342 .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported)
4291 .copied_global => unreachable, // cannot contain relocs
4292 .input_section => |isi| .{4343 .input_section => |isi| .{
4293 &elf.input_sections.items[@backingInt(isi)].first_symbol_reloc,4344 &elf.input_sections.items[@backingInt(isi)].first_symbol_reloc,
4294 &elf.input_sections.items[@backingInt(isi)].first_got_reloc,4345 &elf.input_sections.items[@backingInt(isi)].first_got_reloc,
...@@ -4359,7 +4410,7 @@ fn flushMovedNodeRelocs(...@@ -4359,7 +4410,7 @@ fn flushMovedNodeRelocs(
4359}4410}
43604411
4361fn identClass(elf: *const Elf) std.elf.CLASS {4412fn 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]);
4363}4414}
43644415
4365/// Like `std.elf.ET`, but only includes the ELF machine architectures we support, so that we can4416/// 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 {...@@ -4415,7 +4466,7 @@ fn targetPtrSize(elf: *const Elf) u8 {
4415 return elf.identClass().size();4466 return elf.identClass().size();
4416}4467}
4417fn targetEndian(elf: *const Elf) std.lang.Endian {4468fn 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]);
4419 return ident_data.endian();4470 return ident_data.endian();
4420}4471}
4421fn targetTlsVariant(elf: *const Elf) union(enum) {4472fn targetTlsVariant(elf: *const Elf) union(enum) {
...@@ -4487,7 +4538,7 @@ fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.chi...@@ -4487,7 +4538,7 @@ fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.chi
4487 return switch (@typeInfo(Child)) {4538 return switch (@typeInfo(Child)) {
4488 else => @compileError(@typeName(Child)),4539 else => @compileError(@typeName(Child)),
4489 .int => std.mem.toNative(Child, ptr.*, elf.targetEndian()),4540 .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)))),
4491 .@"struct" => |@"struct"| @bitCast(4542 .@"struct" => |@"struct"| @bitCast(
4492 elf.targetLoad(@as(*align(alignment) @"struct".backing_integer.?, @ptrCast(ptr))),4543 elf.targetLoad(@as(*align(alignment) @"struct".backing_integer.?, @ptrCast(ptr))),
4493 ),4544 ),
...@@ -4563,6 +4614,16 @@ fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr {...@@ -4563,6 +4614,16 @@ fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr {
4563 }4614 }
4564}4615}
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
4566const SymPtr = union(std.elf.CLASS) {4627const SymPtr = union(std.elf.CLASS) {
4567 NONE: noreturn,4628 NONE: noreturn,
4568 @"32": *std.elf.Elf32.Sym,4629 @"32": *std.elf.Elf32.Sym,
...@@ -4657,7 +4718,7 @@ fn mapInputSection(elf: *Elf, opts: struct {...@@ -4657,7 +4718,7 @@ fn mapInputSection(elf: *Elf, opts: struct {
4657 }4718 }
4658 errdefer assert(elf.section_by_name.pop().?.key == name_shstrtab);4719 errdefer assert(elf.section_by_name.pop().?.key == name_shstrtab);
4659 const parent_node: MappedFile.Node.Index = parent: {4720 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;
4661 if (opts.flags.EXECINSTR) break :parent elf.ni.text;4722 if (opts.flags.EXECINSTR) break :parent elf.ni.text;
4662 if (opts.flags.TLS) break :parent elf.ni.tls;4723 if (opts.flags.TLS) break :parent elf.ni.tls;
4663 if (opts.flags.WRITE) break :parent elf.ni.data;4724 if (opts.flags.WRITE) break :parent elf.ni.data;
...@@ -4878,7 +4939,7 @@ const LoadParseInputError = Error || Io.File.SeekError || Io.Reader.Error;...@@ -4878,7 +4939,7 @@ const LoadParseInputError = Error || Io.File.SeekError || Io.Reader.Error;
4878/// indicates to the frontend that the input could be a GNU ld script instead.4939/// indicates to the frontend that the input could be a GNU ld script instead.
4879pub fn loadInput(elf: *Elf, input: link.Input) (link.Error || error{BadMagic})!void {4940pub fn loadInput(elf: *Elf, input: link.Input) (link.Error || error{BadMagic})!void {
4880 const diags = &elf.base.comp.link_diags;4941 const diags = &elf.base.comp.link_diags;
4881 return elf.loadInputInner(input) catch |err| switch (err) {4942 elf.loadInputInner(input) catch |err| switch (err) {
4882 else => |e| return e,4943 else => |e| return e,
4883 error.MappedFileIo => return diags.fail(4944 error.MappedFileIo => return diags.fail(
4884 "failed to write output file: {t}",4945 "failed to write output file: {t}",
...@@ -4986,6 +5047,9 @@ fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (Load...@@ -4986,6 +5047,9 @@ fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (Load
4986 const r = &fr.interface;5047 const r = &fr.interface;
49875048
4988 log.debug("loadArchive({f})", .{path.fmtEscapeString()});5049 log.debug("loadArchive({f})", .{path.fmtEscapeString()});
5050
5051 if (elf.ehdrType() == .REL) return; // this input does not affect the output artifact
5052
4989 {5053 {
4990 const magic = r.take(std.elf.ARMAG.len) catch |err| switch (err) {5054 const magic = r.take(std.elf.ARMAG.len) catch |err| switch (err) {
4991 error.ReadFailed => |e| return e,5055 error.ReadFailed => |e| return e,
...@@ -5071,21 +5135,40 @@ fn loadObject(...@@ -5071,21 +5135,40 @@ fn loadObject(
5071 .{},5135 .{},
5072 ),5136 ),
5073 };5137 };
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;
5074 try elf.ensureUnusedSymbolCapacity(1, .all_local);5163 try elf.ensureUnusedSymbolCapacity(1, .all_local);
5075 try elf.inputs.ensureUnusedCapacity(gpa, 1);5164 input.extra = .{ .file_symbol = elf.addLocalSymbolAssumeCapacity(.{
5076 const file_symbol = elf.addLocalSymbolAssumeCapacity(.{
5077 .node = .none,5165 .node = .none,
5078 .name = try elf.string(.strtab, std.fs.path.stem(member orelse path.sub_path)),5166 .name = try elf.string(.strtab, std.fs.path.stem(member orelse path.sub_path)),
5079 .value = 0,5167 .value = 0,
5080 .size = 0,5168 .size = 0,
5081 .type = .FILE,5169 .type = .FILE,
5082 .shndx = .ABS,5170 .shndx = .ABS,
5083 });5171 }) };
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 };
5089 const target_endian = elf.targetEndian();5172 const target_endian = elf.targetEndian();
5090 switch (elf.identClass()) {5173 switch (elf.identClass()) {
5091 .NONE, _ => unreachable,5174 .NONE, _ => unreachable,
...@@ -5479,6 +5562,9 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars...@@ -5479,6 +5562,9 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars
54795562
5480 log.debug("loadDso({f})", .{path.fmtEscapeString()});5563 log.debug("loadDso({f})", .{path.fmtEscapeString()});
5481 try elf.checkInputIdent(path, r);5564 try elf.checkInputIdent(path, r);
5565
5566 if (elf.ehdrType() == .REL) return; // this input does not affect the output artifact
5567
5482 const target_endian = elf.targetEndian();5568 const target_endian = elf.targetEndian();
5483 switch (elf.identClass()) {5569 switch (elf.identClass()) {
5484 .NONE, _ => unreachable,5570 .NONE, _ => unreachable,
...@@ -5709,7 +5795,8 @@ fn checkInputIdent(...@@ -5709,7 +5795,8 @@ fn checkInputIdent(
5709 }5795 }
57105796
5711 const ident = try r.peekStructPointer(std.elf.Ident);5797 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
5714 if (ident.class != target.class) return diags.failParse(5801 if (ident.class != target.class) return diags.failParse(
5715 path,5802 path,
...@@ -5825,13 +5912,11 @@ fn prelinkInner(elf: *Elf) Error!void {...@@ -5825,13 +5912,11 @@ fn prelinkInner(elf: *Elf) Error!void {
5825 const comp = elf.base.comp;5912 const comp = elf.base.comp;
5826 const gpa = comp.gpa;5913 const gpa = comp.gpa;
58275914
5828 if (comp.zcu != null and !comp.config.use_llvm) {5915 if (comp.zcu != null and !comp.config.use_llvm and elf.ni.elf == MappedFile.Node.Index.root) {
5829 // We're use self-hosted codegen---add an input representing the Zig "object".5916 // We're using self-hosted codegen---add an input representing the Zig "object".
5830 try elf.ensureUnusedSymbolCapacity(1, .all_local);5917 try elf.ensureUnusedSymbolCapacity(1, .all_local);
5831 try elf.inputs.ensureUnusedCapacity(gpa, 1);5918 try elf.inputs.ensureUnusedCapacity(gpa, 1);
5832 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{5919 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name});
5833 std.fs.path.stem(elf.base.emit.sub_path),
5834 });
5835 defer gpa.free(zcu_name);5920 defer gpa.free(zcu_name);
5836 const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{5921 const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{
5837 .node = .none,5922 .node = .none,
...@@ -5844,9 +5929,12 @@ fn prelinkInner(elf: *Elf) Error!void {...@@ -5844,9 +5929,12 @@ fn prelinkInner(elf: *Elf) Error!void {
5844 elf.inputs.addOneAssumeCapacity().* = .{5929 elf.inputs.addOneAssumeCapacity().* = .{
5845 .path = elf.base.emit,5930 .path = elf.base.emit,
5846 .member = null,5931 .member = null,
5847 .file_symbol = zcu_file_symbol,5932 .extra = .{ .file_symbol = zcu_file_symbol },
5848 };5933 };
5934 elf.input_pending_index += 1;
5849 }5935 }
5936
5937 try elf.ensureElfNodeSize();
5850}5938}
58515939
5852fn prepareDynamic(elf: *Elf) Error!void {5940fn prepareDynamic(elf: *Elf) Error!void {
...@@ -6036,12 +6124,12 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -6036,12 +6124,12 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
6036 },6124 },
6037 };6125 };
6038 assert(shndx < @backingInt(Section.Index.LORESERVE));6126 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) };
6040 },6128 },
6041 };6129 };
6042 try elf.ensureNodeSize(elf.ni.shdr, new_shdr_size);6130 try elf.ensureNodeSize(elf.ni.shdr, new_shdr_size);
6043 const ni = try elf.mf.addLastChildNode(gpa, switch (elf.ehdrType()) {6131 const ni = try elf.mf.addLastChildNode(gpa, switch (elf.ehdrType()) {
6044 .REL => elf.ni.file,6132 .REL => elf.ni.elf,
6045 .EXEC, .DYN => segment_ni,6133 .EXEC, .DYN => segment_ni,
6046 }, .{6134 }, .{
6047 .size = opts.size,6135 .size = opts.size,
...@@ -6064,7 +6152,6 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -6064,7 +6152,6 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
6064 else => .{ .shndx = .UNDEF },6152 else => .{ .shndx = .UNDEF },
6065 } });6153 } });
6066 elf.nodes.appendAssumeCapacity(.{ .section = shndx });6154 elf.nodes.appendAssumeCapacity(.{ .section = shndx });
6067 const offset = ni.fileLocation(&elf.mf, false).offset;
6068 switch (elf.shdrPtr(shndx)) {6155 switch (elf.shdrPtr(shndx)) {
6069 inline else => |shdr, class| {6156 inline else => |shdr, class| {
6070 shdr.* = .{6157 shdr.* = .{
...@@ -6072,7 +6159,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -6072,7 +6159,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
6072 .type = opts.type,6159 .type = opts.type,
6073 .flags = .{ .shf = opts.flags },6160 .flags = .{ .shf = opts.flags },
6074 .addr = @intCast(addr),6161 .addr = @intCast(addr),
6075 .offset = @intCast(offset),6162 .offset = @intCast(elf.getNodeElfOffset(ni)),
6076 .size = @intCast(opts.size),6163 .size = @intCast(opts.size),
6077 .link = opts.link,6164 .link = opts.link,
6078 .info = opts.info,6165 .info = opts.info,
...@@ -6506,20 +6593,7 @@ fn addSymbolRelocAssumeCapacity(...@@ -6506,20 +6593,7 @@ fn addSymbolRelocAssumeCapacity(
65066593
6507 // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to6594 // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to
6508 // determine the vaddr of `node`.6595 // determine the vaddr of `node`.
6509 const node_vaddr: u64 = switch (elf.getNode(node)) {6596 const node_vaddr = elf.getNodeVAddr(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 };
65236597
6524 // If this is `true`, we will try to create a copy relocation for the target symbol if it is6598 // If this is `true`, we will try to create a copy relocation for the target symbol if it is
6525 // not locally defined. If the relocation value is always computed from the target symbol's6599 // not locally defined. If the relocation value is always computed from the target symbol's
...@@ -6658,20 +6732,23 @@ fn addGotRelocAssumeCapacity(...@@ -6658,20 +6732,23 @@ fn addGotRelocAssumeCapacity(
6658) void {6732) void {
6659 assert(elf.ehdrType() != .REL);6733 assert(elf.ehdrType() != .REL);
6660 switch (elf.getNode(node)) {6734 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
6661 .input_section,6747 .input_section,
6662 .nav,6748 .nav,
6663 .lazy_code,6749 .lazy_code,
6664 .lazy_const_data,6750 .lazy_const_data,
6665 => {},6751 => {},
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
6675 }6752 }
66766753
6677 const gop = elf.got.getOrPutAssumeCapacity(target);6754 const gop = elf.got.getOrPutAssumeCapacity(target);
...@@ -7055,6 +7132,17 @@ pub fn flush(...@@ -7055,6 +7132,17 @@ pub fn flush(
7055 tid: Zcu.PerThread.Id,7132 tid: Zcu.PerThread.Id,
7056 prog_node: std.Progress.Node,7133 prog_node: std.Progress.Node,
7057) link.Error!void {7134) 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 {
7058 const comp = elf.base.comp;7146 const comp = elf.base.comp;
7059 const diags = &comp.link_diags;7147 const diags = &comp.link_diags;
7060 _ = arena;7148 _ = arena;
...@@ -7072,11 +7160,9 @@ pub fn flush(...@@ -7072,11 +7160,9 @@ pub fn flush(
7072 if (any_undef) return error.AlreadyReported;7160 if (any_undef) return error.AlreadyReported;
7073 }7161 }
70747162
7075 elf.prepareDynamic() catch |err| switch (err) {7163 try elf.prepareDynamic();
7076 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
7077 else => |e| return e,
7078 };
70797164
7165 try elf.ensureElfNodeSize();
7080 while (try elf.idle(tid)) {}7166 while (try elf.idle(tid)) {}
70817167
7082 // We've done the final `idle` loop, so everything is at its final place in the file. We have a7168 // 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(...@@ -7101,10 +7187,7 @@ pub fn flush(
7101 .enabled => "_start",7187 .enabled => "_start",
7102 .named => |named| named,7188 .named => |named| named,
7103 };7189 };
7104 const sym_name_strtab = elf.string(.strtab, sym_name_slice) catch |err| switch (err) {7190 const sym_name_strtab = try elf.string(.strtab, sym_name_slice);
7105 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
7106 else => |e| return e,
7107 };
7108 if (elf.globalByName(sym_name_strtab) == null) break :entry 0;7191 if (elf.globalByName(sym_name_strtab) == null) break :entry 0;
7109 break :entry Symbol.Id.global(sym_name_strtab).value(elf);7192 break :entry Symbol.Id.global(sym_name_strtab).value(elf);
7110 };7193 };
...@@ -7112,10 +7195,11 @@ pub fn flush(...@@ -7112,10 +7195,11 @@ pub fn flush(
7112 inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)),7195 inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)),
7113 }7196 }
71147197
7115 elf.mf.flush() catch |err| switch (err) {7198 try elf.mf.flush();
7116 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),7199
7117 else => |e| return e,7200 if (elf.options.enable_link_snapshots)
7118 };7201 elf.dumpStderr(tid) catch |err|
7202 return comp.link_diags.fail("dumping link snapshot failed: {t}", .{err});
7119}7203}
71207204
7121pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {7205pub 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 {...@@ -7128,8 +7212,19 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
7128 }7212 }
71297213
7130 task: {7214 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 }
7131 if (elf.input_section_pending_index < elf.input_sections.items.len) {7226 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);
7133 elf.input_section_pending_index += 1;7228 elf.input_section_pending_index += 1;
7134 const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(isi.node(elf)));7229 const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(isi.node(elf)));
7135 defer sub_prog_node.end();7230 defer sub_prog_node.end();
...@@ -7217,11 +7312,13 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {...@@ -7217,11 +7312,13 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
7217 while (elf.mf.updates.pop()) |ni| {7312 while (elf.mf.updates.pop()) |ni| {
7218 const clean_moved = ni.cleanMoved(&elf.mf);7313 const clean_moved = ni.cleanMoved(&elf.mf);
7219 const clean_resized = ni.cleanResized(&elf.mf);7314 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) {
7221 const sub_prog_node = elf.idleProgNode(tid, elf.mf.update_prog_node, elf.getNode(ni));7317 const sub_prog_node = elf.idleProgNode(tid, elf.mf.update_prog_node, elf.getNode(ni));
7222 defer sub_prog_node.end();7318 defer sub_prog_node.end();
7223 if (clean_moved) try elf.flushMoved(ni);7319 if (clean_moved) try elf.flushMoved(ni);
7224 if (clean_resized) try elf.flushResized(ni);7320 if (clean_resized) try elf.flushResized(ni);
7321 if (clean_next_moved) try elf.flushNextMoved(ni);
7225 break :task;7322 break :task;
7226 } else elf.mf.update_prog_node.completeOne();7323 } else elf.mf.update_prog_node.completeOne();
7227 }7324 }
...@@ -7242,6 +7339,10 @@ fn idleProgNode(...@@ -7242,6 +7339,10 @@ fn idleProgNode(
7242 return prog_node.start(name: switch (node) {7339 return prog_node.start(name: switch (node) {
7243 else => |tag| @tagName(tag),7340 else => |tag| @tagName(tag),
7244 .section => |shndx| shndx.name(elf).slice(elf),7341 .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,
7245 .input_section => |isi| {7346 .input_section => |isi| {
7246 const ii = isi.input(elf);7347 const ii = isi.input(elf);
7247 break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{7348 break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{
...@@ -7294,6 +7395,8 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) Error!void {...@@ -7294,6 +7395,8 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) Error!void {
7294 };7395 };
7295 break;7396 break;
7296 }7397 }
7398
7399 try elf.ensureElfNodeSize();
7297}7400}
72987401
7299fn genUav(7402fn genUav(
...@@ -7362,6 +7465,36 @@ fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) Error!void {...@@ -7362,6 +7465,36 @@ fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) Error!void {
7362 }7465 }
7363}7466}
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
7365fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void {7498fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void {
7366 const file_loc = isi.fileLocation(elf);7499 const file_loc = isi.fileLocation(elf);
7367 if (file_loc.size == 0) return;7500 if (file_loc.size == 0) return;
...@@ -7408,33 +7541,29 @@ fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void {...@@ -7408,33 +7541,29 @@ fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void {
7408 assert(isi.node(elf).hasMoved(&elf.mf));7541 assert(isi.node(elf).hasMoved(&elf.mf));
7409}7542}
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);
7412 switch (elf.getNode(ni)) {7546 switch (elf.getNode(ni)) {
7413 else => unreachable,7547 else => unreachable,
7414 .ehdr => assert(ni.fileLocation(&elf.mf, false).offset == 0),7548 .ehdr => assert(elf_offset == 0),
7415 .shdr => switch (elf.ehdrPtr()) {7549 .shdr => switch (elf.ehdrPtr()) {
7416 inline else => |ehdr| elf.targetStore(7550 inline else => |ehdr| elf.targetStore(&ehdr.shoff, @intCast(elf_offset)),
7417 &ehdr.shoff,
7418 @intCast(ni.fileLocation(&elf.mf, false).offset),
7419 ),
7420 },7551 },
7421 .segment => |phndx| {7552 .segment => |phndx| {
7422 switch (elf.phdrSlice()) {7553 switch (elf.phdrSlice()) {
7423 inline else => |phdr, class| {7554 inline else => |phdr, class| {
7424 const ph = &phdr[phndx];7555 const ph = &phdr[phndx];
7425 elf.targetStore(&ph.offset, @intCast(ni.fileLocation(&elf.mf, false).offset));7556 elf.targetStore(&ph.offset, @intCast(elf_offset));
7426 if (elf.targetLoad(&ph.type) == .PHDR) {7557 if (elf.targetLoad(&ph.type) == .PHDR) {
7427 @field(elf.ehdrPtr(), @tagName(class)).phoff = ph.offset;7558 @field(elf.ehdrPtr(), @tagName(class)).phoff = ph.offset;
7428 }7559 }
7429 },7560 },
7430 }7561 }
7431 var child_it = ni.children(&elf.mf);7562 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);
7433 },7564 },
7434 .section => |shndx| switch (elf.shdrPtr(shndx)) {7565 .section => |shndx| switch (elf.shdrPtr(shndx)) {
7435 inline else => |shdr| elf.targetStore(&shdr.offset, @intCast(7566 inline else => |shdr| elf.targetStore(&shdr.offset, @intCast(elf_offset)),
7436 ni.fileLocation(&elf.mf, false).offset,
7437 )),
7438 },7567 },
7439 }7568 }
7440}7569}
...@@ -7447,10 +7576,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -7447,10 +7576,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
7447 defer elf.mf.nodes_lock.unlock();7576 defer elf.mf.nodes_lock.unlock();
74487577
7449 switch (elf.getNode(ni)) {7578 switch (elf.getNode(ni)) {
7450 .file => unreachable,7579 .archive, .archive_header => unreachable,
7451 .ehdr, .shdr => elf.flushFileOffset(ni),7580 .elf => {},
7581 .ehdr, .shdr => elf.flushElfOffset(ni),
7452 .segment => |phndx| {7582 .segment => |phndx| {
7453 elf.flushFileOffset(ni);7583 elf.flushElfOffset(ni);
7454 switch (elf.phdrSlice()) {7584 switch (elf.phdrSlice()) {
7455 inline else => |phdr| {7585 inline else => |phdr| {
7456 const ph = &phdr[phndx];7586 const ph = &phdr[phndx];
...@@ -7471,7 +7601,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -7471,7 +7601,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
7471 }7601 }
7472 },7602 },
7473 .section => |shndx| {7603 .section => |shndx| {
7474 elf.flushFileOffset(ni);7604 elf.flushElfOffset(ni);
7475 const addr = elf.computeNodeVAddr(ni);7605 const addr = elf.computeNodeVAddr(ni);
7476 const old_addr: u64, const flags: std.elf.SHF = switch (elf.shdrPtr(shndx)) {7606 const old_addr: u64, const flags: std.elf.SHF = switch (elf.shdrPtr(shndx)) {
7477 inline else => |shdr| .{7607 inline else => |shdr| .{
...@@ -7522,6 +7652,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -7522,6 +7652,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
7522 elf.flushMovedNodeRelocs(ni, addr, elf.dynamic_first_symbol_reloc, .none);7652 elf.flushMovedNodeRelocs(ni, addr, elf.dynamic_first_symbol_reloc, .none);
7523 }7653 }
7524 },7654 },
7655 .input_member => {},
7525 .input_section => |isi| {7656 .input_section => |isi| {
7526 const old_section_addr = isi.ptr(elf).vaddr;7657 const old_section_addr = isi.ptr(elf).vaddr;
7527 const new_section_addr = elf.computeNodeVAddr(ni);7658 const new_section_addr = elf.computeNodeVAddr(ni);
...@@ -7530,7 +7661,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -7530,7 +7661,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
7530 // Update local symbols7661 // Update local symbols
7531 const ii = isi.input(elf);7662 const ii = isi.input(elf);
7532 var lsi, const end_lsi = ii.localSymbolRange(elf);7663 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)) {
7534 if (lsi.index().ptr(elf).node != ni) continue;7665 if (lsi.index().ptr(elf).node != ni) continue;
7535 const visibility: std.elf.STV = switch (elf.symPtr(lsi.index())) {7666 const visibility: std.elf.STV = switch (elf.symPtr(lsi.index())) {
7536 inline else => |sym| elf.targetLoad(&sym.other).visibility,7667 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...@@ -7617,7 +7748,17 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
76177748
7618 _, const size = ni.location(&elf.mf).resolve(&elf.mf);7749 _, const size = ni.location(&elf.mf).resolve(&elf.mf);
7619 switch (elf.getNode(ni)) {7750 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 => {},
7621 .ehdr => unreachable,7762 .ehdr => unreachable,
7622 .shdr => {},7763 .shdr => {},
7623 .segment => |phndx| switch (elf.phdrSlice()) {7764 .segment => |phndx| switch (elf.phdrSlice()) {
...@@ -7717,9 +7858,88 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo...@@ -7717,9 +7858,88 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
7717 }7858 }
7718 },7859 },
7719 },7860 },
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 },
7721 }7940 }
7722}7941}
7942
7723fn updateDynamicEntry(elf: *Elf, key: u32, new_val: u64) void {7943fn updateDynamicEntry(elf: *Elf, key: u32, new_val: u64) void {
7724 switch (elf.shdrPtr(elf.shndx.dynamic)) {7944 switch (elf.shdrPtr(elf.shndx.dynamic)) {
7725 inline else => |shdr, class| {7945 inline else => |shdr, class| {
...@@ -7760,7 +7980,7 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void...@@ -7760,7 +7980,7 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
7760 };7980 };
77617981
7762 // Now that we know the index, we can set the relocation's offset.7982 // 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
7765 if (plt_index < elf.plt.count()) {7985 if (plt_index < elf.plt.count()) {
7766 // We reused a free entry, so we're already done!7986 // We reused a free entry, so we're already done!
...@@ -8100,7 +8320,10 @@ fn updateExportsInner(...@@ -8100,7 +8320,10 @@ fn updateExportsInner(
8100 },8320 },
8101 .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT },8321 .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT },
8102 };8322 };
8323
8324 try elf.ensureElfNodeSize();
8103 while (try elf.idle(pt.tid)) {}8325 while (try elf.idle(pt.tid)) {}
8326
8104 const value: u64 = Symbol.Id.local(exported_lsi).value(elf);8327 const value: u64 = Symbol.Id.local(exported_lsi).value(elf);
8105 const size: u64, const shndx: Section.Index = switch (elf.symPtr(exported_lsi.index())) {8328 const size: u64, const shndx: Section.Index = switch (elf.symPtr(exported_lsi.index())) {
8106 inline else => |exported_sym| .{8329 inline else => |exported_sym| .{
...@@ -8154,6 +8377,16 @@ pub fn deleteExport(elf: *Elf, exported: Zcu.Exported, name: InternPool.NullTerm...@@ -8154,6 +8377,16 @@ pub fn deleteExport(elf: *Elf, exported: Zcu.Exported, name: InternPool.NullTerm
8154 _ = name;8377 _ = name;
8155}8378}
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
8157pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult {8390pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult {
8158 if (elf.options.enable_link_snapshots) {8391 if (elf.options.enable_link_snapshots) {
8159 try elf.printNode(tid, w, .root, 0);8392 try elf.printNode(tid, w, .root, 0);
...@@ -8231,13 +8464,14 @@ pub fn printNode(...@@ -8231,13 +8464,14 @@ pub fn printNode(
8231 {8464 {
8232 const mf_node = &elf.mf.nodes.items[@backingInt(ni)];8465 const mf_node = &elf.mf.nodes.items[@backingInt(ni)];
8233 const off, const size = mf_node.location().resolve(&elf.mf);8466 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", .{
8235 @backingInt(ni),8468 @backingInt(ni),
8236 off,8469 off,
8237 size,8470 size,
8238 mf_node.flags.alignment.toByteUnits(),8471 mf_node.flags.alignment.toByteUnits(),
8239 if (mf_node.flags.fixed) " fixed" else "",8472 if (mf_node.flags.fixed) " fixed" else "",
8240 if (mf_node.flags.moved) " moved" else "",8473 if (mf_node.flags.moved) " moved" else "",
8474 if (mf_node.flags.next_moved) " next_moved" else "",
8241 if (mf_node.flags.resized) " resized" else "",8475 if (mf_node.flags.resized) " resized" else "",
8242 if (mf_node.flags.has_content) " has_content" else "",8476 if (mf_node.flags.has_content) " has_content" else "",
8243 });8477 });
...@@ -8273,11 +8507,19 @@ pub fn printNode(...@@ -8273,11 +8507,19 @@ pub fn printNode(
8273 }8507 }
8274}8508}
82758509
8276fn ensureNodeSize(8510/// Must be called deterministically after any call to `MappedFile.Node.Index.resize`
8277 elf: *Elf,8511/// (of `elf.ni.elf` or one of its children) before any possible calls to `idle`.
8278 node: MappedFile.Node.Index,8512fn ensureElfNodeSize(elf: *Elf) MappedFile.Error!void {
8279 need_size: u64,8513 if (elf.ni.elf == MappedFile.Node.Index.root) return;
8280) Error!void {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 {
8281 _, const node_size = node.location(&elf.mf).resolve(&elf.mf);8523 _, const node_size = node.location(&elf.mf).resolve(&elf.mf);
8282 if (need_size <= node_size) return;8524 if (need_size <= node_size) return;
8283 const gpa = elf.base.comp.gpa;8525 const gpa = elf.base.comp.gpa;
src/link/MappedFile.zig+93-41
...@@ -23,6 +23,7 @@ nodes: std.ArrayList(Node),...@@ -23,6 +23,7 @@ nodes: std.ArrayList(Node),
23free_ni: Node.Index,23free_ni: Node.Index,
24large: std.ArrayList(u64),24large: std.ArrayList(u64),
25updates: std.ArrayList(Node.Index),25updates: std.ArrayList(Node.Index),
26/// This progress node's estimated total items is increased once for each node appended to `updates`.
26update_prog_node: std.Progress.Node,27update_prog_node: std.Progress.Node,
27writers: std.SinglyLinkedList,28writers: std.SinglyLinkedList,
28io_err: ?IoError,29io_err: ?IoError,
...@@ -61,7 +62,7 @@ pub const Error = Allocator.Error || Io.Cancelable || error{...@@ -61,7 +62,7 @@ pub const Error = Allocator.Error || Io.Cancelable || error{
61 MappedFileIo,62 MappedFileIo,
62};63};
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 {
65 var mf: MappedFile = .{66 var mf: MappedFile = .{
66 .io = io,67 .io = io,
67 .flags = undefined,68 .flags = undefined,
...@@ -105,7 +106,7 @@ pub fn init(file: Io.File, gpa: std.mem.Allocator, io: Io) (Allocator.Error || I...@@ -105,7 +106,7 @@ pub fn init(file: Io.File, gpa: std.mem.Allocator, io: Io) (Allocator.Error || I
105 return mf;106 return mf;
106}107}
107108
108pub fn deinit(mf: *MappedFile, gpa: std.mem.Allocator) void {109pub fn deinit(mf: *MappedFile, gpa: Allocator) void {
109 mf.unmap();110 mf.unmap();
110 mf.nodes.deinit(gpa);111 mf.nodes.deinit(gpa);
111 mf.large.deinit(gpa);112 mf.large.deinit(gpa);
...@@ -133,11 +134,15 @@ pub const Node = extern struct {...@@ -133,11 +134,15 @@ pub const Node = extern struct {
133 moved: bool,134 moved: bool,
134 /// Whether this node has been resized.135 /// Whether this node has been resized.
135 resized: bool,136 resized: bool,
137 /// Whether the next sibling has moved or is a different node.
138 next_moved: bool,
136 /// Whether this node might contain non-zero bytes.139 /// Whether this node might contain non-zero bytes.
137 has_content: bool,140 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.
139 bubbles_moved: bool,142 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,
141 };146 };
142147
143 pub const Location = union(enum(u1)) {148 pub const Location = union(enum(u1)) {
...@@ -191,6 +196,22 @@ pub const Node = extern struct {...@@ -191,6 +196,22 @@ pub const Node = extern struct {
191 pub fn next(ni: Node.Index, mf: *const MappedFile) Node.Index {196 pub fn next(ni: Node.Index, mf: *const MappedFile) Node.Index {
192 return ni.get(mf).next;197 return ni.get(mf).next;
193 }198 }
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
195 pub fn ChildIterator(comptime direction: enum { prev, next }) type {216 pub fn ChildIterator(comptime direction: enum { prev, next }) type {
196 return struct {217 return struct {
...@@ -211,7 +232,7 @@ pub const Node = extern struct {...@@ -211,7 +232,7 @@ pub const Node = extern struct {
211 return .{ .mf = mf, .ni = ni.get(mf).last };232 return .{ .mf = mf, .ni = ni.get(mf).last };
212 }233 }
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 {
215 var child_ni = ni.get(mf).last;236 var child_ni = ni.get(mf).last;
216 while (child_ni != .none) {237 while (child_ni != .none) {
217 try child_ni.moved(gpa, mf);238 try child_ni.moved(gpa, mf);
...@@ -229,11 +250,11 @@ pub const Node = extern struct {...@@ -229,11 +250,11 @@ pub const Node = extern struct {
229 }250 }
230 return false;251 return false;
231 }252 }
232 pub fn moved(ni: Node.Index, gpa: std.mem.Allocator, mf: *MappedFile) Allocator.Error!void {253 pub fn moved(ni: Node.Index, gpa: Allocator, mf: *MappedFile) Allocator.Error!void {
233 try mf.updates.ensureUnusedCapacity(gpa, 1);254 try mf.updates.ensureUnusedCapacity(gpa, 2);
234 ni.movedAssumeCapacity(mf);255 ni.movedAssumeCapacity(mf);
235 }256 }
236 pub fn cleanMoved(ni: Node.Index, mf: *const MappedFile) bool {257 pub fn cleanMoved(ni: Node.Index, mf: *MappedFile) bool {
237 const node_moved = &ni.get(mf).flags.moved;258 const node_moved = &ni.get(mf).flags.moved;
238 defer node_moved.* = false;259 defer node_moved.* = false;
239 return node_moved.*;260 return node_moved.*;
...@@ -242,7 +263,11 @@ pub const Node = extern struct {...@@ -242,7 +263,11 @@ pub const Node = extern struct {
242 if (ni.hasMoved(mf)) return;263 if (ni.hasMoved(mf)) return;
243 const node = ni.get(mf);264 const node = ni.get(mf);
244 node.flags.moved = true;265 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;
246 mf.updates.appendAssumeCapacity(ni);271 mf.updates.appendAssumeCapacity(ni);
247 mf.update_prog_node.increaseEstimatedTotalItems(1);272 mf.update_prog_node.increaseEstimatedTotalItems(1);
248 }273 }
...@@ -250,11 +275,11 @@ pub const Node = extern struct {...@@ -250,11 +275,11 @@ pub const Node = extern struct {
250 pub fn hasResized(ni: Node.Index, mf: *const MappedFile) bool {275 pub fn hasResized(ni: Node.Index, mf: *const MappedFile) bool {
251 return ni.get(mf).flags.resized;276 return ni.get(mf).flags.resized;
252 }277 }
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 {
254 try mf.updates.ensureUnusedCapacity(gpa, 1);279 try mf.updates.ensureUnusedCapacity(gpa, 1);
255 ni.resizedAssumeCapacity(mf);280 ni.resizedAssumeCapacity(mf);
256 }281 }
257 pub fn cleanResized(ni: Node.Index, mf: *const MappedFile) bool {282 pub fn cleanResized(ni: Node.Index, mf: *MappedFile) bool {
258 const node_resized = &ni.get(mf).flags.resized;283 const node_resized = &ni.get(mf).flags.resized;
259 defer node_resized.* = false;284 defer node_resized.* = false;
260 return node_resized.*;285 return node_resized.*;
...@@ -263,7 +288,28 @@ pub const Node = extern struct {...@@ -263,7 +288,28 @@ pub const Node = extern struct {
263 const node = ni.get(mf);288 const node = ni.get(mf);
264 if (node.flags.resized) return;289 if (node.flags.resized) return;
265 node.flags.resized = true;290 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;
267 mf.updates.appendAssumeCapacity(ni);313 mf.updates.appendAssumeCapacity(ni);
268 mf.update_prog_node.increaseEstimatedTotalItems(1);314 mf.update_prog_node.increaseEstimatedTotalItems(1);
269 }315 }
...@@ -333,7 +379,7 @@ pub const Node = extern struct {...@@ -333,7 +379,7 @@ pub const Node = extern struct {
333 return mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)];379 return mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)];
334 }380 }
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 {
337 mf.resizeNode(gpa, ni, size) catch |err| switch (err) {383 mf.resizeNode(gpa, ni, size) catch |err| switch (err) {
338 error.OutOfMemory,384 error.OutOfMemory,
339 error.Canceled,385 error.Canceled,
...@@ -360,7 +406,7 @@ pub const Node = extern struct {...@@ -360,7 +406,7 @@ pub const Node = extern struct {
360 pub fn realign(406 pub fn realign(
361 ni: Node.Index,407 ni: Node.Index,
362 mf: *MappedFile,408 mf: *MappedFile,
363 gpa: std.mem.Allocator,409 gpa: Allocator,
364 new_alignment: std.mem.Alignment,410 new_alignment: std.mem.Alignment,
365 opts: RealignNodeOptions,411 opts: RealignNodeOptions,
366 ) Error!void {412 ) Error!void {
...@@ -384,7 +430,7 @@ pub const Node = extern struct {...@@ -384,7 +430,7 @@ pub const Node = extern struct {
384 pub fn shrink(430 pub fn shrink(
385 ni: Node.Index,431 ni: Node.Index,
386 mf: *MappedFile,432 mf: *MappedFile,
387 gpa: std.mem.Allocator,433 gpa: Allocator,
388 size: u64,434 size: u64,
389 shift_next: bool,435 shift_next: bool,
390 ) Error!void {436 ) Error!void {
...@@ -392,7 +438,7 @@ pub const Node = extern struct {...@@ -392,7 +438,7 @@ pub const Node = extern struct {
392 mf.updateWriters();438 mf.updateWriters();
393 }439 }
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 {
396 w.* = .{442 w.* = .{
397 .gpa = gpa,443 .gpa = gpa,
398 .mf = mf,444 .mf = mf,
...@@ -419,7 +465,7 @@ pub const Node = extern struct {...@@ -419,7 +465,7 @@ pub const Node = extern struct {
419 }465 }
420466
421 pub const Writer = struct {467 pub const Writer = struct {
422 gpa: std.mem.Allocator,468 gpa: Allocator,
423 mf: *MappedFile,469 mf: *MappedFile,
424 writer_node: std.SinglyLinkedList.Node,470 writer_node: std.SinglyLinkedList.Node,
425 ni: Node.Index,471 ni: Node.Index,
...@@ -543,14 +589,13 @@ pub const Node = extern struct {...@@ -543,14 +589,13 @@ pub const Node = extern struct {
543 }589 }
544};590};
545591
546fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {592fn addNode(mf: *MappedFile, gpa: Allocator, opts: struct {
547 parent: Node.Index = .none,593 parent: Node.Index = .none,
548 prev: Node.Index = .none,594 prev: Node.Index = .none,
549 next: Node.Index = .none,595 next: Node.Index = .none,
550 offset: u64 = 0,596 offset: u64 = 0,
551 add_node: AddNodeOptions,597 add_node: AddNodeOptions,
552}) (Allocator.Error || Io.Cancelable || IoError)!Node.Index {598}) (Allocator.Error || Io.Cancelable || IoError)!Node.Index {
553 if (opts.add_node.moved or opts.add_node.resized) try mf.updates.ensureUnusedCapacity(gpa, 1);
554 mf.nodes_lock.assertUnlocked();599 mf.nodes_lock.assertUnlocked();
555 const location_tag: Node.Location.Tag, const location_payload: Node.Location.Payload = location: {600 const location_tag: Node.Location.Tag, const location_payload: Node.Location.Payload = location: {
556 if (std.math.cast(u32, opts.offset)) |small_offset| break :location .{ .small, .{601 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 {...@@ -570,7 +615,7 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
570 };615 };
571 switch (opts.prev) {616 switch (opts.prev) {
572 .none => opts.parent.get(mf).first = free_ni,617 .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),
574 }619 }
575 switch (opts.next) {620 switch (opts.next) {
576 .none => opts.parent.get(mf).last = free_ni,621 .none => opts.parent.get(mf).last = free_ni,
...@@ -588,22 +633,27 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {...@@ -588,22 +633,27 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
588 .fixed = opts.add_node.fixed,633 .fixed = opts.add_node.fixed,
589 .moved = true,634 .moved = true,
590 .resized = true,635 .resized = true,
636 .next_moved = true,
591 .has_content = false,637 .has_content = false,
592 .bubbles_moved = opts.add_node.bubbles_moved,638 .bubbles_moved = opts.add_node.bubbles_moved,
639 .enable_next_moved = opts.add_node.enable_next_moved,
593 },640 },
594 .location_payload = location_payload,641 .location_payload = location_payload,
595 };642 };
596643
597 {644 {
645 defer {
646 free_node.flags.moved = false;
647 free_node.flags.resized = false;
648 free_node.flags.next_moved = false;
649 }
598 try mf.realignNode(gpa, free_ni, opts.add_node.alignment, .{});650 try mf.realignNode(gpa, free_ni, opts.add_node.alignment, .{});
599 try mf.resizeNode(gpa, free_ni, opts.add_node.size);651 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;
603 }652 }
604 if (opts.add_node.moved) free_ni.movedAssumeCapacity(mf);
605 if (opts.add_node.resized) free_ni.resizedAssumeCapacity(mf);
606 mf.updateWriters();653 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);
607 return free_ni;657 return free_ni;
608}658}
609659
...@@ -613,12 +663,14 @@ pub const AddNodeOptions = struct {...@@ -613,12 +663,14 @@ pub const AddNodeOptions = struct {
613 fixed: bool = false,663 fixed: bool = false,
614 moved: bool = false,664 moved: bool = false,
615 resized: bool = false,665 resized: bool = false,
666 next_moved: bool = false,
616 bubbles_moved: bool = true,667 bubbles_moved: bool = true,
668 enable_next_moved: bool = false,
617};669};
618670
619pub fn addOnlyChildNode(671pub fn addOnlyChildNode(
620 mf: *MappedFile,672 mf: *MappedFile,
621 gpa: std.mem.Allocator,673 gpa: Allocator,
622 parent_ni: Node.Index,674 parent_ni: Node.Index,
623 opts: AddNodeOptions,675 opts: AddNodeOptions,
624) Error!Node.Index {676) Error!Node.Index {
...@@ -641,7 +693,7 @@ pub fn addOnlyChildNode(...@@ -641,7 +693,7 @@ pub fn addOnlyChildNode(
641693
642pub fn addFirstChildNode(694pub fn addFirstChildNode(
643 mf: *MappedFile,695 mf: *MappedFile,
644 gpa: std.mem.Allocator,696 gpa: Allocator,
645 parent_ni: Node.Index,697 parent_ni: Node.Index,
646 opts: AddNodeOptions,698 opts: AddNodeOptions,
647) Error!Node.Index {699) Error!Node.Index {
...@@ -664,7 +716,7 @@ pub fn addFirstChildNode(...@@ -664,7 +716,7 @@ pub fn addFirstChildNode(
664716
665pub fn addLastChildNode(717pub fn addLastChildNode(
666 mf: *MappedFile,718 mf: *MappedFile,
667 gpa: std.mem.Allocator,719 gpa: Allocator,
668 parent_ni: Node.Index,720 parent_ni: Node.Index,
669 opts: AddNodeOptions,721 opts: AddNodeOptions,
670) Error!Node.Index {722) Error!Node.Index {
...@@ -694,7 +746,7 @@ pub fn addLastChildNode(...@@ -694,7 +746,7 @@ pub fn addLastChildNode(
694746
695pub fn addNodeAfter(747pub fn addNodeAfter(
696 mf: *MappedFile,748 mf: *MappedFile,
697 gpa: std.mem.Allocator,749 gpa: Allocator,
698 prev_ni: Node.Index,750 prev_ni: Node.Index,
699 opts: AddNodeOptions,751 opts: AddNodeOptions,
700) Error!Node.Index {752) Error!Node.Index {
...@@ -721,7 +773,7 @@ pub fn addNodeAfter(...@@ -721,7 +773,7 @@ pub fn addNodeAfter(
721773
722fn shrinkNode(774fn shrinkNode(
723 mf: *MappedFile,775 mf: *MappedFile,
724 gpa: std.mem.Allocator,776 gpa: Allocator,
725 ni: Node.Index,777 ni: Node.Index,
726 size: u64,778 size: u64,
727 shift_next: bool,779 shift_next: bool,
...@@ -740,7 +792,7 @@ fn shrinkNode(...@@ -740,7 +792,7 @@ fn shrinkNode(
740 }792 }
741793
742 try mf.large.ensureUnusedCapacity(gpa, 4);794 try mf.large.ensureUnusedCapacity(gpa, 4);
743 try mf.updates.ensureUnusedCapacity(gpa, 2);795 try mf.updates.ensureUnusedCapacity(gpa, 4);
744796
745 ni.setLocationAssumeCapacity(mf, old_offset, size);797 ni.setLocationAssumeCapacity(mf, old_offset, size);
746 if (!shift_next or node.next == .none) return;798 if (!shift_next or node.next == .none) return;
...@@ -765,7 +817,7 @@ fn shrinkNode(...@@ -765,7 +817,7 @@ fn shrinkNode(
765817
766fn resizeNode(818fn resizeNode(
767 mf: *MappedFile,819 mf: *MappedFile,
768 gpa: std.mem.Allocator,820 gpa: Allocator,
769 ni: Node.Index,821 ni: Node.Index,
770 requested_size: u64,822 requested_size: u64,
771) (Allocator.Error || Io.Cancelable || IoError)!void {823) (Allocator.Error || Io.Cancelable || IoError)!void {
...@@ -904,11 +956,11 @@ fn resizeNode(...@@ -904,11 +956,11 @@ fn resizeNode(
904 next_ni.get(mf).prev = node.prev;956 next_ni.get(mf).prev = node.prev;
905 switch (node.prev) {957 switch (node.prev) {
906 .none => parent.first = next_ni,958 .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),
908 }960 }
909 last.next = ni;961 try parent.last.setNext(gpa, ni, mf);
910 node.prev = parent.last;962 node.prev = parent.last;
911 node.next = .none;963 try ni.setNext(gpa, .none, mf);
912 parent.last = ni;964 parent.last = ni;
913 if (node.flags.has_content) {965 if (node.flags.has_content) {
914 const parent_file_offset = node.parent.fileLocation(mf, false).offset;966 const parent_file_offset = node.parent.fileLocation(mf, false).offset;
...@@ -972,13 +1024,13 @@ fn resizeNode(...@@ -972,13 +1024,13 @@ fn resizeNode(
972 if (parent.last != first_floating_ni) {1024 if (parent.last != first_floating_ni) {
973 first_floating.prev = parent.last;1025 first_floating.prev = parent.last;
974 parent.last = first_floating_ni;1026 parent.last = first_floating_ni;
975 last.next = first_floating_ni;1027 try parent.last.setNext(gpa, first_floating_ni, mf);
976 last_fixed.next = first_floating.next;1028 try last_fixed_ni.setNext(gpa, first_floating.next, mf);
977 switch (first_floating.next) {1029 switch (first_floating.next) {
978 .none => {},1030 .none => {},
979 else => |next_ni| next_ni.get(mf).prev = last_fixed_ni,1031 else => |next_ni| next_ni.get(mf).prev = last_fixed_ni,
980 }1032 }
981 first_floating.next = .none;1033 try first_floating_ni.setNext(gpa, .none, mf);
982 }1034 }
983 if (first_floating.flags.has_content) {1035 if (first_floating.flags.has_content) {
984 const parent_file_offset =1036 const parent_file_offset =
...@@ -1040,7 +1092,7 @@ fn resizeNode(...@@ -1040,7 +1092,7 @@ fn resizeNode(
10401092
1041fn realignNode(1093fn realignNode(
1042 mf: *MappedFile,1094 mf: *MappedFile,
1043 gpa: std.mem.Allocator,1095 gpa: Allocator,
1044 ni: Node.Index,1096 ni: Node.Index,
1045 new_alignment: std.mem.Alignment,1097 new_alignment: std.mem.Alignment,
1046 opts: Node.Index.RealignNodeOptions,1098 opts: Node.Index.RealignNodeOptions,
...@@ -1241,9 +1293,9 @@ fn copyFileRange(...@@ -1241,9 +1293,9 @@ fn copyFileRange(
1241 return size - remaining_size;1293 return size - remaining_size;
1242}1294}
12431295
1244fn ensureCapacityForSetLocation(mf: *MappedFile, gpa: std.mem.Allocator) Allocator.Error!void {1296fn ensureCapacityForSetLocation(mf: *MappedFile, gpa: Allocator) Allocator.Error!void {
1245 try mf.large.ensureUnusedCapacity(gpa, 2);1297 try mf.large.ensureUnusedCapacity(gpa, 2);
1246 try mf.updates.ensureUnusedCapacity(gpa, 1);1298 try mf.updates.ensureUnusedCapacity(gpa, 2);
1247}1299}
12481300
1249pub fn ensureTotalCapacity(mf: *MappedFile, new_capacity: usize) Error!void {1301pub fn ensureTotalCapacity(mf: *MappedFile, new_capacity: usize) Error!void {