authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-25 08:30:25+02:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-25 08:30:25+02:00
log841dd0eb874cf0febf9582031f86703f5cde00cb
tree492bd49abf34c42e0c2ce736d0265651e12fa321
parent40ad0920b37564a4c119fa6fa385b76a9c2b0c21
parentedf18aa383cc5553a51e57954cf7e076c4b745db

Merge pull request 'MappedFile: rework node operations' (#36618) from mlugg/mappedfile-stuff into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36618

6 files changed, 2506 insertions(+), 1548 deletions(-)

lib/compiler/test_runner.zig+16-7
...@@ -185,7 +185,6 @@ fn mainServer(init: std.process.Init.Minimal) !void {...@@ -185,7 +185,6 @@ fn mainServer(init: std.process.Init.Minimal) !void {
185 .environ = init.environ,185 .environ = init.environ,
186 });186 });
187 defer io_instance.deinit();187 defer io_instance.deinit();
188 const io = io_instance.io();
189188
190 const mode: fuzz_abi.LimitKind = @fromBackingInt(@intCast(try server.receiveBody_u8()));189 const mode: fuzz_abi.LimitKind = @fromBackingInt(@intCast(try server.receiveBody_u8()));
191 const amount_or_instance = try server.receiveBody_u64();190 const amount_or_instance = try server.receiveBody_u64();
...@@ -208,7 +207,7 @@ fn mainServer(init: std.process.Init.Minimal) !void {...@@ -208,7 +207,7 @@ fn mainServer(init: std.process.Init.Minimal) !void {
208 .indexes = test_indexes,207 .indexes = test_indexes,
209 .server = &server,208 .server = &server,
210 .gpa = gpa,209 .gpa = gpa,
211 .io = io,210 .threaded_io = &io_instance,
212 .input_poller = undefined,211 .input_poller = undefined,
213 };212 };
214213
...@@ -422,7 +421,7 @@ var fuzz_runner: if (builtin.fuzz) struct {...@@ -422,7 +421,7 @@ var fuzz_runner: if (builtin.fuzz) struct {
422 indexes: []u32,421 indexes: []u32,
423 server: *std.zig.Server,422 server: *std.zig.Server,
424 gpa: std.mem.Allocator,423 gpa: std.mem.Allocator,
425 io: Io,424 threaded_io: *Io.Threaded,
426 input_poller: Io.Future(Io.Cancelable!void),425 input_poller: Io.Future(Io.Cancelable!void),
427426
428 comptime {427 comptime {
...@@ -443,6 +442,12 @@ var fuzz_runner: if (builtin.fuzz) struct {...@@ -443,6 +442,12 @@ var fuzz_runner: if (builtin.fuzz) struct {
443 defer if (testing.allocator_instance.deinit() != 0) std.process.exit(1);442 defer if (testing.allocator_instance.deinit() != 0) std.process.exit(1);
444 is_fuzz_test = false;443 is_fuzz_test = false;
445444
445 testing.io_instance = .init(testing.allocator, .{
446 .argv0 = fuzz_runner.threaded_io.argv0,
447 .environ = fuzz_runner.threaded_io.environ.process_environ,
448 });
449 defer testing.io_instance.deinit();
450
446 builtin.test_functions[fuzz_runner.indexes[i]].func() catch |err| switch (err) {451 builtin.test_functions[fuzz_runner.indexes[i]].func() catch |err| switch (err) {
447 error.SkipZigTest => return,452 error.SkipZigTest => return,
448 else => {453 else => {
...@@ -473,7 +478,8 @@ var fuzz_runner: if (builtin.fuzz) struct {...@@ -473,7 +478,8 @@ var fuzz_runner: if (builtin.fuzz) struct {
473478
474 export fn runner_start_input_poller() void {479 export fn runner_start_input_poller() void {
475 @disableInstrumentation();480 @disableInstrumentation();
476 const future = fuzz_runner.io.concurrent(inputPoller, .{}) catch |e| switch (e) {481 const io = fuzz_runner.threaded_io.io();
482 const future = io.concurrent(inputPoller, .{}) catch |e| switch (e) {
477 error.ConcurrencyUnavailable => @panic("failed to spawn concurrent fuzz input poller"),483 error.ConcurrencyUnavailable => @panic("failed to spawn concurrent fuzz input poller"),
478 };484 };
479 fuzz_runner.input_poller = future;485 fuzz_runner.input_poller = future;
...@@ -481,17 +487,20 @@ var fuzz_runner: if (builtin.fuzz) struct {...@@ -481,17 +487,20 @@ var fuzz_runner: if (builtin.fuzz) struct {
481487
482 export fn runner_stop_input_poller() void {488 export fn runner_stop_input_poller() void {
483 @disableInstrumentation();489 @disableInstrumentation();
484 assert(fuzz_runner.input_poller.cancel(fuzz_runner.io) == error.Canceled);490 const io = fuzz_runner.threaded_io.io();
491 assert(fuzz_runner.input_poller.cancel(io) == error.Canceled);
485 }492 }
486493
487 export fn runner_futex_wait(ptr: *const u32, expected: u32) bool {494 export fn runner_futex_wait(ptr: *const u32, expected: u32) bool {
488 @disableInstrumentation();495 @disableInstrumentation();
489 return fuzz_runner.io.futexWait(u32, ptr, expected) == error.Canceled;496 const io = fuzz_runner.threaded_io.io();
497 return io.futexWait(u32, ptr, expected) == error.Canceled;
490 }498 }
491499
492 export fn runner_futex_wake(ptr: *const u32, waiters: u32) void {500 export fn runner_futex_wake(ptr: *const u32, waiters: u32) void {
493 @disableInstrumentation();501 @disableInstrumentation();
494 fuzz_runner.io.futexWake(u32, ptr, waiters);502 const io = fuzz_runner.threaded_io.io();
503 io.futexWake(u32, ptr, waiters);
495 }504 }
496505
497 fn inputPoller() Io.Cancelable!void {506 fn inputPoller() Io.Cancelable!void {
src/InternPool.zig-11
...@@ -5987,17 +5987,6 @@ pub const Alignment = enum(u6) {...@@ -5987,17 +5987,6 @@ pub const Alignment = enum(u6) {
5987 return n + 1;5987 return n + 1;
5988 }5988 }
59895989
5990 pub fn toStdMem(a: Alignment) std.mem.Alignment {
5991 assert(a != .none);
5992 return @fromBackingInt(@intCast(@backingInt(a)));
5993 }
5994
5995 pub fn fromStdMem(a: std.mem.Alignment) Alignment {
5996 const r: Alignment = @fromBackingInt(@intCast(@backingInt(a)));
5997 assert(r != .none);
5998 return r;
5999 }
6000
6001 pub fn toLlvm(a: Alignment) std.zig.llvm.Builder.Alignment {5990 pub fn toLlvm(a: Alignment) std.zig.llvm.Builder.Alignment {
6002 return @fromBackingInt(@intCast(@backingInt(a)));5991 return @fromBackingInt(@intCast(@backingInt(a)));
6003 }5992 }
src/link/Coff.zig+291-328
...@@ -21,6 +21,7 @@ const Zcu = @import("../Zcu.zig");...@@ -21,6 +21,7 @@ const Zcu = @import("../Zcu.zig");
21const ModuleDefinition = @import("../libs/mingw/def.zig").ModuleDefinition;21const ModuleDefinition = @import("../libs/mingw/def.zig").ModuleDefinition;
22const implib = @import("../libs/mingw/implib.zig");22const implib = @import("../libs/mingw/implib.zig");
23const Path = std.Build.Cache.Path;23const Path = std.Build.Cache.Path;
24const Alignment = MappedFile.Alignment;
2425
25base: link.File,26base: link.File,
26options: link.File.OpenOptions,27options: link.File.OpenOptions,
...@@ -532,10 +533,10 @@ pub const Member = struct {...@@ -532,10 +533,10 @@ pub const Member = struct {
532 errdefer _ = coff.export_table.entries.pop();533 errdefer _ = coff.export_table.entries.pop();
533534
534 _, const old_size = Node.known.longnames_member.location(&coff.mf).resolve(&coff.mf);535 _, const old_size = Node.known.longnames_member.location(&coff.mf).resolve(&coff.mf);
535 const new_size = old_size + name.len + 1;536 const new_size = Alignment.@"4".forward(old_size + name.len + 1);
536 assert(new_size < comptime try std.math.powi(u64, 10, max_name_len - 1));537 assert(new_size < comptime try std.math.powi(u64, 10, max_name_len - 1));
537538
538 try Node.known.longnames_member.resize(&coff.mf, gpa, new_size);539 try Node.known.longnames_member.resizeLeaf(&coff.mf, gpa, new_size);
539 const name_table_slice = Node.known.longnames_member.slice(&coff.mf);540 const name_table_slice = Node.known.longnames_member.slice(&coff.mf);
540 const name_slice = name_table_slice[@intCast(old_size)..][0 .. name.len + 1];541 const name_slice = name_table_slice[@intCast(old_size)..][0 .. name.len + 1];
541 @memcpy(name_slice[0..name.len], name);542 @memcpy(name_slice[0..name.len], name);
...@@ -602,7 +603,7 @@ pub const Member = struct {...@@ -602,7 +603,7 @@ pub const Member = struct {
602};603};
603604
604pub const LongNamesTable = struct {605pub const LongNamesTable = struct {
605 ni: MappedFile.Node.Index = .none,606 ni: MappedFile.Node.Index.Optional = .none,
606 entries: std.array_hash_map.Auto(void, Entry),607 entries: std.array_hash_map.Auto(void, Entry),
607608
608 pub const Entry = struct {609 pub const Entry = struct {
...@@ -832,7 +833,7 @@ pub const String = enum(u32) {...@@ -832,7 +833,7 @@ pub const String = enum(u32) {
832833
833pub const Section = struct {834pub const Section = struct {
834 si: Symbol.Index,835 si: Symbol.Index,
835 relocation_table_ni: MappedFile.Node.Index,836 relocation_table_ni: MappedFile.Node.Index.Optional,
836837
837 pub const RelocationIndex = enum(u16) {838 pub const RelocationIndex = enum(u16) {
838 none,839 none,
...@@ -855,7 +856,7 @@ pub const Section = struct {...@@ -855,7 +856,7 @@ pub const Section = struct {
855 sn: Symbol.SectionNumber,856 sn: Symbol.SectionNumber,
856 ) ?*align(2) std.coff.Relocation {857 ) ?*align(2) std.coff.Relocation {
857 if (sri == .none) return null;858 if (sri == .none) return null;
858 const table_slice = sn.section(coff).relocation_table_ni.slice(&coff.mf);859 const table_slice = sn.section(coff).relocation_table_ni.unwrap().?.slice(&coff.mf);
859 return @ptrCast(@alignCast(&table_slice[@as(u32, sri.unwrap().?) * std.coff.Relocation.sizeOf()]));860 return @ptrCast(@alignCast(&table_slice[@as(u32, sri.unwrap().?) * std.coff.Relocation.sizeOf()]));
860 }861 }
861 };862 };
...@@ -891,7 +892,7 @@ const SpecialSymbol = enum {...@@ -891,7 +892,7 @@ const SpecialSymbol = enum {
891};892};
892893
893pub const Symbol = struct {894pub const Symbol = struct {
894 ni: MappedFile.Node.Index,895 ni: MappedFile.Node.Index.Optional,
895 rva: u32,896 rva: u32,
896 value: std.meta.BareUnion(Symbol.Value),897 value: std.meta.BareUnion(Symbol.Value),
897 extra: std.meta.BareUnion(Symbol.Extra),898 extra: std.meta.BareUnion(Symbol.Extra),
...@@ -986,7 +987,7 @@ pub const Symbol = struct {...@@ -986,7 +987,7 @@ pub const Symbol = struct {
986 pub fn nodeOffset(sym: *const Symbol, coff: *Coff) u32 {987 pub fn nodeOffset(sym: *const Symbol, coff: *Coff) u32 {
987 return switch (sym.flags.value_tag) {988 return switch (sym.flags.value_tag) {
988 .node_offset => offset: {989 .node_offset => offset: {
989 assert(switch (coff.getNode(sym.ni)) {990 assert(switch (coff.getNode(sym.ni.unwrap().?)) {
990 // Separate nodes are not created for these entries per-symbol991 // Separate nodes are not created for these entries per-symbol
991 .input_section, .import_address_table => true,992 .input_section, .import_address_table => true,
992 else => false,993 else => false,
...@@ -1052,9 +1053,7 @@ pub const Symbol = struct {...@@ -1052,9 +1053,7 @@ pub const Symbol = struct {
1052 }1053 }
10531054
1054 pub fn node(si: Symbol.Index, coff: *Coff) MappedFile.Node.Index {1055 pub fn node(si: Symbol.Index, coff: *Coff) MappedFile.Node.Index {
1055 const ni = si.get(coff).ni;1056 return si.get(coff).ni.unwrap().?;
1056 assert(ni != .none);
1057 return ni;
1058 }1057 }
10591058
1060 pub fn sti(si: Symbol.Index, coff: *Coff) SymbolTable.Index {1059 pub fn sti(si: Symbol.Index, coff: *Coff) SymbolTable.Index {
...@@ -1075,7 +1074,7 @@ pub const Symbol = struct {...@@ -1075,7 +1074,7 @@ pub const Symbol = struct {
10751074
1076 pub fn flushMoved(si: Symbol.Index, coff: *Coff) !void {1075 pub fn flushMoved(si: Symbol.Index, coff: *Coff) !void {
1077 const sym = si.get(coff);1076 const sym = si.get(coff);
1078 sym.rva = coff.computeNodeRva(sym.ni) + sym.nodeOffset(coff);1077 sym.rva = coff.computeNodeRva(sym.ni.unwrap().?) + sym.nodeOffset(coff);
1079 try si.applyLocationRelocs(coff);1078 try si.applyLocationRelocs(coff);
1080 try si.applyTargetRelocs(coff, .none);1079 try si.applyTargetRelocs(coff, .none);
10811080
...@@ -1199,12 +1198,11 @@ pub const Reloc = extern struct {...@@ -1199,12 +1198,11 @@ pub const Reloc = extern struct {
11991198
1200 pub fn apply(reloc: *Reloc, coff: *Coff) !void {1199 pub fn apply(reloc: *Reloc, coff: *Coff) !void {
1201 const loc_sym = reloc.loc.get(coff);1200 const loc_sym = reloc.loc.get(coff);
1202 switch (loc_sym.ni) {
1203 .none => return,
1204 else => |ni| if (ni.hasMoved(&coff.mf)) return,
1205 }
12061201
1207 const loc_slice = loc_sym.ni.slice(&coff.mf)[@intCast(reloc.offset)..];1202 const loc_sym_ni = loc_sym.ni.unwrap() orelse return;
1203 if (loc_sym_ni.hasMoved(&coff.mf)) return;
1204
1205 const loc_slice = loc_sym_ni.slice(&coff.mf)[@intCast(reloc.offset)..];
1208 const target_endian = coff.targetEndian();1206 const target_endian = coff.targetEndian();
1209 const target_machine = coff.targetLoad(&coff.headerPtr().machine);1207 const target_machine = coff.targetLoad(&coff.headerPtr().machine);
12101208
...@@ -1331,9 +1329,12 @@ pub const Reloc = extern struct {...@@ -1331,9 +1329,12 @@ pub const Reloc = extern struct {
1331 }1329 }
13321330
1333 const target_sym = reloc.target.get(coff);1331 const target_sym = reloc.target.get(coff);
1334 const is_abs = switch (target_sym.ni) {1332 const is_abs = if (target_sym.ni.unwrap()) |ni| is_abs: {
1335 .none => if (target_sym.section_number == .ABSOLUTE) true else return,1333 if (ni.hasMoved(&coff.mf)) return;
1336 else => |ni| if (ni.hasMoved(&coff.mf)) return else false,1334 break :is_abs false;
1335 } else is_abs: {
1336 if (target_sym.section_number != .ABSOLUTE) return;
1337 break :is_abs true;
1337 };1338 };
13381339
1339 const target_rva = target_sym.rva +% @as(u64, @bitCast(reloc.addend));1340 const target_rva = target_sym.rva +% @as(u64, @bitCast(reloc.addend));
...@@ -1573,7 +1574,7 @@ fn create(...@@ -1573,7 +1574,7 @@ fn create(
1573 33...64 => .@"PE32+",1574 33...64 => .@"PE32+",
1574 else => return error.UnsupportedCOFFArchitecture,1575 else => return error.UnsupportedCOFFArchitecture,
1575 };1576 };
1576 const section_align: std.mem.Alignment = switch (machine) {1577 const section_align: Alignment = switch (machine) {
1577 .AMD64, .I386 => @fromBackingInt(@intCast(12)),1578 .AMD64, .I386 => @fromBackingInt(@intCast(12)),
1578 .SH3, .SH3DSP, .SH4, .SH5 => @fromBackingInt(@intCast(12)),1579 .SH3, .SH3DSP, .SH4, .SH5 => @fromBackingInt(@intCast(12)),
1579 .MIPS16, .MIPSFPU, .MIPSFPU16, .WCEMIPSV2 => @fromBackingInt(@intCast(12)),1580 .MIPS16, .MIPSFPU, .MIPSFPU16, .WCEMIPSV2 => @fromBackingInt(@intCast(12)),
...@@ -1617,22 +1618,22 @@ fn create(...@@ -1617,22 +1618,22 @@ fn create(
1617 .entries = .empty,1618 .entries = .empty,
1618 },1619 },
1619 .import_table = .{1620 .import_table = .{
1620 .ni = .none,1621 .ni = undefined,
1621 .entries = .empty,1622 .entries = .empty,
1622 .iat_symbol_indices = .empty,1623 .iat_symbol_indices = .empty,
1623 },1624 },
1624 .export_table = .{1625 .export_table = .{
1625 .ni = .none,1626 .ni = undefined,
1626 .export_directory_table_ni = .none,1627 .export_directory_table_ni = undefined,
1627 .export_address_table_si = .null,1628 .export_address_table_si = .null,
1628 .name_pointer_table_ni = .none,1629 .name_pointer_table_ni = undefined,
1629 .ordinal_table_ni = .none,1630 .ordinal_table_ni = undefined,
1630 .name_table_ni = .none,1631 .name_table_ni = undefined,
1631 .entries = .empty,1632 .entries = .empty,
1632 },1633 },
1633 .symbol_table = .{1634 .symbol_table = .{
1634 .ni = .none,1635 .ni = undefined,
1635 .strings_ni = .none,1636 .strings_ni = undefined,
1636 .strings = .empty,1637 .strings = .empty,
1637 .symbols = .empty,1638 .symbols = .empty,
1638 .pending_symbol_index = 0,1639 .pending_symbol_index = 0,
...@@ -1794,13 +1795,13 @@ fn initHeaders(...@@ -1794,13 +1795,13 @@ fn initHeaders(
1794 minor_subsystem_version: u16,1795 minor_subsystem_version: u16,
1795 magic: std.coff.OptionalHeader.Magic,1796 magic: std.coff.OptionalHeader.Magic,
1796 subsystem: std.coff.Subsystem,1797 subsystem: std.coff.Subsystem,
1797 section_align: std.mem.Alignment,1798 section_align: Alignment,
1798 file_name: []const u8,1799 file_name: []const u8,
1799) !void {1800) !void {
1800 const comp = coff.base.comp;1801 const comp = coff.base.comp;
1801 const gpa = comp.gpa;1802 const gpa = comp.gpa;
1802 const target_endian = coff.targetEndian();1803 const target_endian = coff.targetEndian();
1803 const file_align: std.mem.Alignment = comptime .fromByteUnits(default_file_alignment);1804 const file_align: Alignment = comptime .fromByteUnits(default_file_alignment);
1804 const is_image = coff.isImage();1805 const is_image = coff.isImage();
1805 const is_archive = coff.isArchive();1806 const is_archive = coff.isArchive();
1806 const target = &comp.root_mod.resolved_target.result;1807 const target = &comp.root_mod.resolved_target.result;
...@@ -1839,34 +1840,20 @@ fn initHeaders(...@@ -1839,34 +1840,20 @@ fn initHeaders(
1839 coff.nodes.appendAssumeCapacity(.file);1840 coff.nodes.appendAssumeCapacity(.file);
18401841
1841 const header_ni = Node.known.header;1842 const header_ni = Node.known.header;
1842 assert(header_ni == try coff.mf.addOnlyChildNode(gpa, Node.known.file, .{1843 assert(header_ni == try Node.known.file.addOnlyHeaderChild(&coff.mf, gpa, .{
1843 .alignment = coff.mf.flags.block_size,1844 .alignment = coff.mf.flags.block_size,
1844 .fixed = true,
1845 }));1845 }));
1846 coff.nodes.appendAssumeCapacity(.header);1846 coff.nodes.appendAssumeCapacity(.header);
18471847
1848 const signature_ni = Node.known.signature;1848 const coff_parent_ni: MappedFile.Node.Index = if (is_archive) parent: {
1849 assert(signature_ni == try coff.mf.addLastChildNode(gpa, if (is_image or !is_archive) header_ni else Node.known.file, .{1849 assert(try Node.known.file.addHeaderChildAfter(&coff.mf, gpa, .wrap(header_ni), .{
1850 .size = if (is_image)1850 .size = std.coff.archive_signature.len,
1851 msdos_stub.len + std.coff.pe_signature.len1851 .alignment = .@"4",
1852 else if (is_archive)1852 }) == Node.known.signature);
1853 std.coff.archive_signature.len1853 coff.nodes.appendAssumeCapacity(.signature);
1854 else1854 const signature_slice = Node.known.signature.slice(&coff.mf);
1855 0,
1856 .alignment = .@"4",
1857 .fixed = true,
1858 }));
1859 coff.nodes.appendAssumeCapacity(.signature);
1860
1861 const signature_slice = signature_ni.slice(&coff.mf);
1862 if (is_image) {
1863 @memcpy(signature_slice[0..msdos_stub.len], &msdos_stub);
1864 @memcpy(signature_slice[signature_slice.len - std.coff.pe_signature.len ..], std.coff.pe_signature);
1865 } else if (is_archive) {
1866 @memcpy(signature_slice, std.coff.archive_signature);1855 @memcpy(signature_slice, std.coff.archive_signature);
1867 }
18681856
1869 const opt_coff_parent_ni = if (is_archive) parent: {
1870 const initial_member_count = Member.Index.known_count + @intFromBool(comp.zcu != null);1857 const initial_member_count = Member.Index.known_count + @intFromBool(comp.zcu != null);
1871 try coff.members.ensureTotalCapacity(gpa, initial_member_count);1858 try coff.members.ensureTotalCapacity(gpa, initial_member_count);
18721859
...@@ -1892,46 +1879,54 @@ fn initHeaders(...@@ -1892,46 +1879,54 @@ fn initHeaders(
1892 const zcu_member = zcu_mi.get(coff);1879 const zcu_member = zcu_mi.get(coff);
1893 try zcu_member.initHeader(coff, zcu.main_mod.fully_qualified_name, timestamp);1880 try zcu_member.initHeader(coff, zcu.main_mod.fully_qualified_name, timestamp);
18941881
1882 assert(try zcu_member.content_ni.addOnlyHeaderChild(&coff.mf, gpa, .{
1883 .size = @sizeOf(std.coff.Header),
1884 .alignment = .@"4",
1885 }) == Node.known.coff_header);
1886 coff.nodes.appendAssumeCapacity(.coff_header);
1887
1895 break :parent zcu_member.content_ni;1888 break :parent zcu_member.content_ni;
1896 }1889 }
18971890
1891 // If we're not generating any code, no more known nodes are used
1892
1898 // These placeholder nodes are placed before the first member - if there are1893 // These placeholder nodes are placed before the first member - if there are
1899 // no other members then the last linker member (longnames) needs to expand1894 // no other members then the last linker member (longnames) needs to expand
1900 // to fill the padding at the end of the file.1895 // to fill the padding at the end of the file.
1901 assert(Node.known.zcu_member_header == try coff.mf.addNodeAfter(gpa, Node.known.header, .{}));1896 while (coff.nodes.len < Node.known_count) {
1902 assert(Node.known.zcu_member == try coff.mf.addNodeAfter(gpa, Node.known.header, .{}));1897 _ = try Node.known.header.addHeaderChildAfter(&coff.mf, gpa, .none, .{});
1903 coff.nodes.appendAssumeCapacity(.placeholder);1898 coff.nodes.appendAssumeCapacity(.placeholder);
1904 coff.nodes.appendAssumeCapacity(.placeholder);1899 }
19051900
1906 break :parent null;1901 return;
1907 } else parent: {1902 } else parent: {
1903 assert(try header_ni.addOnlyHeaderChild(&coff.mf, gpa, .{
1904 .size = if (is_image) msdos_stub.len + std.coff.pe_signature.len else 0,
1905 .alignment = .@"4",
1906 }) == Node.known.signature);
1907 coff.nodes.appendAssumeCapacity(.signature);
1908 if (is_image) {
1909 const signature_slice = Node.known.signature.slice(&coff.mf);
1910 @memcpy(signature_slice[0..msdos_stub.len], &msdos_stub);
1911 @memcpy(signature_slice[signature_slice.len - std.coff.pe_signature.len ..], std.coff.pe_signature);
1912 }
1913
1908 // TODO: Not ideal to have this many placeholder nodes - use two distinct `Node.known` types?1914 // TODO: Not ideal to have this many placeholder nodes - use two distinct `Node.known` types?
1909 while (true) {1915 while (true) {
1910 const placeholder_ni = try coff.mf.addLastChildNode(gpa, Node.known.file, .{});1916 const placeholder_ni = try Node.known.file.addHeaderChildAfter(&coff.mf, gpa, .none, .{});
1911 coff.nodes.appendAssumeCapacity(.placeholder);1917 coff.nodes.appendAssumeCapacity(.placeholder);
1912 if (placeholder_ni == Node.known.zcu_member) break;1918 if (placeholder_ni == Node.known.zcu_member) break;
1913 }1919 }
19141920
1915 break :parent Node.known.header;1921 assert(try header_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(Node.known.signature), .{
1916 };1922 .size = @sizeOf(std.coff.Header),
1923 .alignment = .@"4",
1924 }) == Node.known.coff_header);
1925 coff.nodes.appendAssumeCapacity(.coff_header);
19171926
1918 const coff_parent_ni = opt_coff_parent_ni orelse {1927 break :parent header_ni;
1919 // If we're not generating any code, no more known nodes are used
1920 while (coff.nodes.len < Node.known_count) {
1921 _ = try coff.mf.addNodeAfter(gpa, Node.known.header, .{});
1922 coff.nodes.appendAssumeCapacity(.placeholder);
1923 }
1924
1925 return;
1926 };1928 };
19271929
1928 const coff_header_ni = Node.known.coff_header;
1929 assert(coff_header_ni == try coff.mf.addLastChildNode(gpa, coff_parent_ni, .{
1930 .size = @sizeOf(std.coff.Header),
1931 .alignment = .@"4",
1932 .fixed = true,
1933 }));
1934 coff.nodes.appendAssumeCapacity(.coff_header);
1935 {1930 {
1936 const coff_header = coff.headerPtr();1931 const coff_header = coff.headerPtr();
1937 coff_header.* = .{1932 coff_header.* = .{
...@@ -1954,10 +1949,9 @@ fn initHeaders(...@@ -1954,10 +1949,9 @@ fn initHeaders(
1954 }1949 }
19551950
1956 const optional_header_ni = Node.known.optional_header;1951 const optional_header_ni = Node.known.optional_header;
1957 assert(optional_header_ni == try coff.mf.addLastChildNode(gpa, coff_parent_ni, .{1952 assert(optional_header_ni == try coff_parent_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(Node.known.coff_header), .{
1958 .size = optional_header_size,1953 .size = optional_header_size,
1959 .alignment = .@"4",1954 .alignment = .@"4",
1960 .fixed = true,
1961 }));1955 }));
1962 coff.nodes.appendAssumeCapacity(.optional_header);1956 coff.nodes.appendAssumeCapacity(.optional_header);
1963 if (is_image) {1957 if (is_image) {
...@@ -2066,10 +2060,9 @@ fn initHeaders(...@@ -2066,10 +2060,9 @@ fn initHeaders(
2066 }2060 }
20672061
2068 const data_directories_ni = Node.known.data_directories;2062 const data_directories_ni = Node.known.data_directories;
2069 assert(data_directories_ni == try coff.mf.addLastChildNode(gpa, coff_parent_ni, .{2063 assert(data_directories_ni == try coff_parent_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(optional_header_ni), .{
2070 .size = data_directories_size,2064 .size = data_directories_size,
2071 .alignment = .@"4",2065 .alignment = .@"4",
2072 .fixed = true,
2073 }));2066 }));
2074 coff.nodes.appendAssumeCapacity(.data_directories);2067 coff.nodes.appendAssumeCapacity(.data_directories);
2075 if (is_image) {2068 if (is_image) {
...@@ -2082,9 +2075,8 @@ fn initHeaders(...@@ -2082,9 +2075,8 @@ fn initHeaders(
2082 }2075 }
20832076
2084 const section_table_ni = Node.known.section_table;2077 const section_table_ni = Node.known.section_table;
2085 assert(section_table_ni == try coff.mf.addLastChildNode(gpa, coff_parent_ni, .{2078 assert(section_table_ni == try coff_parent_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(data_directories_ni), .{
2086 .alignment = .@"4",2079 .alignment = .@"4",
2087 .fixed = true,
2088 }));2080 }));
2089 coff.nodes.appendAssumeCapacity(.section_table);2081 coff.nodes.appendAssumeCapacity(.section_table);
20902082
...@@ -2092,16 +2084,14 @@ fn initHeaders(...@@ -2092,16 +2084,14 @@ fn initHeaders(
20922084
2093 if (!is_image) {2085 if (!is_image) {
2094 // TODO: These two nodes could be inside one movable node?2086 // TODO: These two nodes could be inside one movable node?
2095 coff.symbol_table.ni = try coff.mf.addLastChildNode(gpa, coff_parent_ni, .{2087 coff.symbol_table.ni = try coff_parent_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(section_table_ni), .{
2096 .alignment = .@"2",2088 .alignment = .@"2",
2097 .fixed = true,
2098 .moved = true,2089 .moved = true,
2099 });2090 });
2100 coff.nodes.appendAssumeCapacity(.symbol_table);2091 coff.nodes.appendAssumeCapacity(.symbol_table);
21012092
2102 coff.symbol_table.strings_ni = try coff.mf.addLastChildNode(gpa, coff_parent_ni, .{2093 coff.symbol_table.strings_ni = try coff_parent_ni.addHeaderChildAfter(&coff.mf, gpa, .wrap(coff.symbol_table.ni), .{
2103 .size = @sizeOf(u32),2094 .size = @sizeOf(u32),
2104 .fixed = true,
2105 .resized = true,2095 .resized = true,
2106 });2096 });
2107 coff.nodes.appendAssumeCapacity(.string_table);2097 coff.nodes.appendAssumeCapacity(.string_table);
...@@ -2148,15 +2138,14 @@ fn initHeaders(...@@ -2148,15 +2138,14 @@ fn initHeaders(
2148 }2138 }
21492139
2150 // TODO: Lazily initialize this instead, avoid the extra logic for this in flushMoved / flushResized2140 // TODO: Lazily initialize this instead, avoid the extra logic for this in flushMoved / flushResized
2151 coff.import_table.ni = try coff.mf.addLastChildNode(2141 const import_table_parent_ni = (try coff.objectSectionMapIndex(
2152 gpa,2142 .@".idata",
2153 (try coff.objectSectionMapIndex(2143 coff.mf.flags.block_size,
2154 .@".idata",2144 .{ .read = true, .initialized = true },
2155 coff.mf.flags.block_size,2145 )).symbol(coff).node(coff);
2156 .{ .read = true, .initialized = true },2146 coff.import_table.ni = try import_table_parent_ni.addFloatingChild(&coff.mf, gpa, .{
2157 )).symbol(coff).node(coff),2147 .alignment = .@"4",
2158 .{ .alignment = .@"4" },2148 });
2159 );
2160 coff.nodes.appendAssumeCapacity(.import_directory_table);2149 coff.nodes.appendAssumeCapacity(.import_directory_table);
21612150
2162 coff.export_table.ni = (try coff.pseudoSectionMapIndex(2151 coff.export_table.ni = (try coff.pseudoSectionMapIndex(
...@@ -2165,15 +2154,10 @@ fn initHeaders(...@@ -2165,15 +2154,10 @@ fn initHeaders(
2165 .{ .read = true, .initialized = true },2154 .{ .read = true, .initialized = true },
2166 )).symbol(coff).node(coff);2155 )).symbol(coff).node(coff);
21672156
2168 coff.export_table.export_directory_table_ni = try coff.mf.addLastChildNode(2157 coff.export_table.export_directory_table_ni = try coff.export_table.ni.addHeaderChildAfter(&coff.mf, gpa, coff.export_table.ni.last(&coff.mf), .{
2169 gpa,2158 .size = @sizeOf(std.coff.ExportDirectoryTable) + file_name.len + 1,
2170 coff.export_table.ni,2159 .moved = true,
2171 .{2160 });
2172 .size = @sizeOf(std.coff.ExportDirectoryTable) + file_name.len + 1,
2173 .moved = true,
2174 .fixed = true,
2175 },
2176 );
2177 coff.nodes.appendAssumeCapacity(.export_directory_table);2161 coff.nodes.appendAssumeCapacity(.export_directory_table);
21782162
2179 const name_index = @sizeOf(std.coff.ExportDirectoryTable);2163 const name_index = @sizeOf(std.coff.ExportDirectoryTable);
...@@ -2181,7 +2165,7 @@ fn initHeaders(...@@ -2181,7 +2165,7 @@ fn initHeaders(
2181 @memcpy(table_slice[name_index..][0..file_name.len], file_name[0..file_name.len]);2165 @memcpy(table_slice[name_index..][0..file_name.len], file_name[0..file_name.len]);
2182 @memset(table_slice[name_index + file_name.len ..], 0);2166 @memset(table_slice[name_index + file_name.len ..], 0);
21832167
2184 const export_address_table_ni = try coff.mf.addLastChildNode(gpa, coff.export_table.ni, .{2168 const export_address_table_ni = try coff.export_table.ni.addFloatingChild(&coff.mf, gpa, .{
2185 .alignment = .of(std.coff.ExportAddressTableEntry),2169 .alignment = .of(std.coff.ExportAddressTableEntry),
2186 .moved = true,2170 .moved = true,
2187 });2171 });
...@@ -2191,25 +2175,25 @@ fn initHeaders(...@@ -2191,25 +2175,25 @@ fn initHeaders(
2191 coff.export_table.export_address_table_si = coff.addSymbolAssumeCapacity();2175 coff.export_table.export_address_table_si = coff.addSymbolAssumeCapacity();
21922176
2193 const export_address_table_sym = coff.export_table.export_address_table_si.get(coff);2177 const export_address_table_sym = coff.export_table.export_address_table_si.get(coff);
2194 export_address_table_sym.ni = export_address_table_ni;2178 export_address_table_sym.ni = .wrap(export_address_table_ni);
2195 assert(export_address_table_sym.loc_relocs == .none);2179 assert(export_address_table_sym.loc_relocs == .none);
2196 export_address_table_sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));2180 export_address_table_sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
2197 export_address_table_sym.section_number =2181 export_address_table_sym.section_number =
2198 coff.getNode(coff.export_table.ni).pseudo_section.symbol(coff).get(coff).section_number;2182 coff.getNode(coff.export_table.ni).pseudo_section.symbol(coff).get(coff).section_number;
21992183
2200 coff.export_table.name_pointer_table_ni = try coff.mf.addLastChildNode(gpa, coff.export_table.ni, .{2184 coff.export_table.name_pointer_table_ni = try coff.export_table.ni.addFloatingChild(&coff.mf, gpa, .{
2201 .alignment = .of(std.coff.ExportNamePointerTableEntry),2185 .alignment = .of(std.coff.ExportNamePointerTableEntry),
2202 .moved = true,2186 .moved = true,
2203 });2187 });
2204 coff.nodes.appendAssumeCapacity(.export_name_pointer_table);2188 coff.nodes.appendAssumeCapacity(.export_name_pointer_table);
22052189
2206 coff.export_table.ordinal_table_ni = try coff.mf.addLastChildNode(gpa, coff.export_table.ni, .{2190 coff.export_table.ordinal_table_ni = try coff.export_table.ni.addFloatingChild(&coff.mf, gpa, .{
2207 .alignment = .of(std.coff.ExportOrdinalTableEntry),2191 .alignment = .of(std.coff.ExportOrdinalTableEntry),
2208 .moved = true,2192 .moved = true,
2209 });2193 });
2210 coff.nodes.appendAssumeCapacity(.export_ordinal_table);2194 coff.nodes.appendAssumeCapacity(.export_ordinal_table);
22112195
2212 coff.export_table.name_table_ni = try coff.mf.addLastChildNode(gpa, coff.export_table.ni, .{2196 coff.export_table.name_table_ni = try coff.export_table.ni.addFloatingChild(&coff.mf, gpa, .{
2213 .alignment = .of(u8),2197 .alignment = .of(u8),
2214 .moved = true,2198 .moved = true,
2215 });2199 });
...@@ -2260,7 +2244,7 @@ pub fn initBuiltins(coff: *Coff) !void {...@@ -2260,7 +2244,7 @@ pub fn initBuiltins(coff: *Coff) !void {
2260 if (coff.isImage()) {2244 if (coff.isImage()) {
2261 const si = try coff.globalSymbol(.{ .name = "__ImageBase", .type = .data });2245 const si = try coff.globalSymbol(.{ .name = "__ImageBase", .type = .data });
2262 const sym = si.get(coff);2246 const sym = si.get(coff);
2263 sym.ni = Node.known.header;2247 sym.ni = .wrap(Node.known.header);
2264 }2248 }
22652249
2266 defer coff.flushSectionMerges() catch unreachable;2250 defer coff.flushSectionMerges() catch unreachable;
...@@ -2302,14 +2286,13 @@ pub fn initBuiltins(coff: *Coff) !void {...@@ -2302,14 +2286,13 @@ pub fn initBuiltins(coff: *Coff) !void {
2302 const list_len_si = try coff.globalSymbol(.{ .name = list.global, .type = .data });2286 const list_len_si = try coff.globalSymbol(.{ .name = list.global, .type = .data });
2303 const list_len_sym = list_len_si.get(coff);2287 const list_len_sym = list_len_si.get(coff);
2304 list_len_sym.setExtra(.{ .size = addr_info.size });2288 list_len_sym.setExtra(.{ .size = addr_info.size });
2305 list_len_sym.ni = try coff.mf.addFirstChildNode(gpa, start_sym.ni, .{2289 list_len_sym.ni = .wrap(try start_sym.ni.unwrap().?.addHeaderChildAfter(&coff.mf, gpa, .none, .{
2306 .size = addr_info.size,2290 .size = addr_info.size,
2307 .fixed = true,2291 }));
2308 });
2309 coff.nodes.appendAssumeCapacity(.{ .builtin = list_len_si });2292 coff.nodes.appendAssumeCapacity(.{ .builtin = list_len_si });
2310 list_len_sym.section_number = start_sym.section_number;2293 list_len_sym.section_number = start_sym.section_number;
23112294
2312 const start_slice = list_len_sym.ni.slice(&coff.mf);2295 const start_slice = list_len_sym.ni.unwrap().?.slice(&coff.mf);
2313 switch (addr_info.magic) {2296 switch (addr_info.magic) {
2314 _ => unreachable,2297 _ => unreachable,
2315 inline .PE32, .@"PE32+" => |t| {2298 inline .PE32, .@"PE32+" => |t| {
...@@ -2324,14 +2307,13 @@ pub fn initBuiltins(coff: *Coff) !void {...@@ -2324,14 +2307,13 @@ pub fn initBuiltins(coff: *Coff) !void {
2324 const list_end_si = coff.addSymbolAssumeCapacity();2307 const list_end_si = coff.addSymbolAssumeCapacity();
2325 const list_end_sym = list_end_si.get(coff);2308 const list_end_sym = list_end_si.get(coff);
2326 list_end_sym.setExtra(.{ .size = addr_info.size });2309 list_end_sym.setExtra(.{ .size = addr_info.size });
2327 list_end_sym.ni = try coff.mf.addFirstChildNode(gpa, end_sym.ni, .{2310 list_end_sym.ni = .wrap(try end_sym.ni.unwrap().?.addHeaderChildAfter(&coff.mf, gpa, .none, .{
2328 .size = addr_info.size,2311 .size = addr_info.size,
2329 .fixed = true,2312 }));
2330 });
2331 coff.nodes.appendAssumeCapacity(.{ .builtin = list_end_si });2313 coff.nodes.appendAssumeCapacity(.{ .builtin = list_end_si });
2332 list_end_sym.section_number = start_sym.section_number;2314 list_end_sym.section_number = start_sym.section_number;
23332315
2334 @memset(list_end_sym.ni.slice(&coff.mf), 0);2316 @memset(list_end_sym.ni.unwrap().?.slice(&coff.mf), 0);
23352317
2336 try list_len_si.flushMoved(coff);2318 try list_len_si.flushMoved(coff);
2337 try list_end_si.flushMoved(coff);2319 try list_end_si.flushMoved(coff);
...@@ -2387,7 +2369,7 @@ fn getNode(coff: *const Coff, ni: MappedFile.Node.Index) Node {...@@ -2387,7 +2369,7 @@ fn getNode(coff: *const Coff, ni: MappedFile.Node.Index) Node {
2387}2369}
2388fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {2370fn computeNodeRva(coff: *Coff, ni: MappedFile.Node.Index) u32 {
2389 const parent_rva = parent_rva: {2371 const parent_rva = parent_rva: {
2390 const parent_si = switch (coff.getNode(ni.parent(&coff.mf))) {2372 const parent_si = switch (coff.getNode(ni.parent(&coff.mf).unwrap().?)) {
2391 .file,2373 .file,
2392 .header,2374 .header,
2393 .signature,2375 .signature,
...@@ -2452,11 +2434,11 @@ fn computeSymbolSectionOffset(...@@ -2452,11 +2434,11 @@ fn computeSymbolSectionOffset(
2452 relative_to: enum { image, pseudo },2434 relative_to: enum { image, pseudo },
2453) u32 {2435) u32 {
2454 var section_offset: u32 = sym.nodeOffset(coff);2436 var section_offset: u32 = sym.nodeOffset(coff);
2455 var parent_ni = sym.ni;2437 var parent_ni = sym.ni.unwrap().?;
2456 while (true) {2438 while (true) {
2457 const offset, _ = parent_ni.location(&coff.mf).resolve(&coff.mf);2439 const offset, _ = parent_ni.location(&coff.mf).resolve(&coff.mf);
2458 section_offset += @intCast(offset);2440 section_offset += @intCast(offset);
2459 parent_ni = parent_ni.parent(&coff.mf);2441 parent_ni = parent_ni.parent(&coff.mf).unwrap().?;
2460 switch (coff.getNode(parent_ni)) {2442 switch (coff.getNode(parent_ni)) {
2461 else => unreachable,2443 else => unreachable,
2462 .image_section => break,2444 .image_section => break,
...@@ -2475,7 +2457,7 @@ pub inline fn targetEndian(_: *const Coff) std.lang.Endian {...@@ -2475,7 +2457,7 @@ pub inline fn targetEndian(_: *const Coff) std.lang.Endian {
24752457
2476fn targetAddrInfo(coff: *Coff) struct {2458fn targetAddrInfo(coff: *Coff) struct {
2477 size: u8,2459 size: u8,
2478 alignment: std.mem.Alignment,2460 alignment: Alignment,
2479 magic: std.coff.OptionalHeader.Magic,2461 magic: std.coff.OptionalHeader.Magic,
2480} {2462} {
2481 const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic);2463 const magic = coff.targetLoad(&coff.optionalHeaderStandardPtr().magic);
...@@ -2741,7 +2723,7 @@ fn getOrPutSymbolName(coff: *Coff, name: []const u8, opt_string: ?String) !Symbo...@@ -2741,7 +2723,7 @@ fn getOrPutSymbolName(coff: *Coff, name: []const u8, opt_string: ?String) !Symbo
2741 const string_index = coff.symbol_table.strings_ni.location(&coff.mf).resolve(&coff.mf)[1];2723 const string_index = coff.symbol_table.strings_ni.location(&coff.mf).resolve(&coff.mf)[1];
2742 string_gop.value_ptr.* = @fromBackingInt(@intCast(string_index));2724 string_gop.value_ptr.* = @fromBackingInt(@intCast(string_index));
27432725
2744 try coff.symbol_table.strings_ni.resize(&coff.mf, gpa, string_index + name.len + 1);2726 try coff.symbol_table.strings_ni.resizeLeaf(&coff.mf, gpa, string_index + name.len + 1);
2745 const slice = coff.symbol_table.strings_ni.slice(&coff.mf);2727 const slice = coff.symbol_table.strings_ni.slice(&coff.mf);
2746 @memcpy(slice[@intCast(string_index)..][0..name.len], name);2728 @memcpy(slice[@intCast(string_index)..][0..name.len], name);
2747 slice[@intCast(string_index + name.len)] = 0;2729 slice[@intCast(string_index + name.len)] = 0;
...@@ -2875,9 +2857,9 @@ fn navSection(...@@ -2875,9 +2857,9 @@ fn navSection(
2875 switch (nav_resolved.@"linksection") {2857 switch (nav_resolved.@"linksection") {
2876 .none => coff.mf.flags.block_size,2858 .none => coff.mf.flags.block_size,
2877 else => switch (nav_resolved.@"align") {2859 else => switch (nav_resolved.@"align") {
2878 .none => Type.fromInterned(ip.typeOf(nav_resolved.value)).abiAlignment(zcu),2860 .none => .fromIp(Type.fromInterned(ip.typeOf(nav_resolved.value)).abiAlignment(zcu)),
2879 else => |alignment| alignment,2861 else => |a| .fromIp(a),
2880 }.toStdMem(),2862 },
2881 },2863 },
2882 attributes,2864 attributes,
2883 )).symbol(coff);2865 )).symbol(coff);
...@@ -2966,24 +2948,22 @@ fn addMemberAssumeCapacity(coff: *Coff, kind: std.coff.ArchiveMemberHeader.Kind,...@@ -2966,24 +2948,22 @@ fn addMemberAssumeCapacity(coff: *Coff, kind: std.coff.ArchiveMemberHeader.Kind,
2966 const comp = coff.base.comp;2948 const comp = coff.base.comp;
2967 const gpa = comp.gpa;2949 const gpa = comp.gpa;
29682950
2969 // TODO: These two nodes could to be inside a movable node if kind == .coff|.import2951 const header_ni = try Node.known.file.addHeaderChildAfter(&coff.mf, gpa, Node.known.file.last(&coff.mf), .{
2970 const header_ni = try coff.mf.addLastChildNode(gpa, Node.known.file, .{
2971 .size = @sizeOf(std.coff.ArchiveMemberHeader),2952 .size = @sizeOf(std.coff.ArchiveMemberHeader),
2972 .alignment = .@"2",2953 .alignment = .@"2",
2973 .fixed = true,
2974 .moved = true,2954 .moved = true,
2975 });2955 });
29762956
2977 const content_ni = try coff.mf.addLastChildNode(gpa, Node.known.file, .{2957 // The actual alignment required by the spec is 2, but to allow aligned access to
2978 // The actual alignment required by the spec is 2, but to allow aligned access to2958 // the various COFF data structures in-place during linking we overalign
2979 // the various COFF data structures in-place during linking we overalign2959 const content_align: Alignment = switch (kind) {
2980 .alignment = switch (kind) {2960 .first_linker, .second_linker, .longnames, .coff => .@"4",
2981 .coff => .@"4",2961 else => .@"2",
2982 else => .@"2",2962 };
2983 },2963 const content_ni = try Node.known.file.addHeaderChildAfter(&coff.mf, gpa, .wrap(header_ni), .{
2984 .size = size,2964 .alignment = content_align,
2965 .size = content_align.forward(size),
2985 .resized = size > 0,2966 .resized = size > 0,
2986 .fixed = true,
2987 });2967 });
29882968
2989 const mi: Member.Index = @fromBackingInt(@intCast(coff.members.items.len));2969 const mi: Member.Index = @fromBackingInt(@intCast(coff.members.items.len));
...@@ -3009,7 +2989,7 @@ fn addMemberAssumeCapacity(coff: *Coff, kind: std.coff.ArchiveMemberHeader.Kind,...@@ -3009,7 +2989,7 @@ fn addMemberAssumeCapacity(coff: *Coff, kind: std.coff.ArchiveMemberHeader.Kind,
3009 const old_size = Node.known.second_linker_member.location(&coff.mf).resolve(&coff.mf)[1];2989 const old_size = Node.known.second_linker_member.location(&coff.mf).resolve(&coff.mf)[1];
3010 const old_header_size = new_num_members * @sizeOf(u32);2990 const old_header_size = new_num_members * @sizeOf(u32);
3011 const trailing_size: usize = @intCast(old_size - old_header_size);2991 const trailing_size: usize = @intCast(old_size - old_header_size);
3012 try Node.known.second_linker_member.resize(&coff.mf, gpa, old_size + @sizeOf(u32));2992 try Node.known.second_linker_member.resizeLeaf(&coff.mf, gpa, old_size + @sizeOf(u32));
30132993
3014 const slice = Node.known.second_linker_member.slice(&coff.mf);2994 const slice = Node.known.second_linker_member.slice(&coff.mf);
3015 @memmove(2995 @memmove(
...@@ -3047,7 +3027,7 @@ fn appendMemberSymbolString(...@@ -3047,7 +3027,7 @@ fn appendMemberSymbolString(
3047 name: []const u8,3027 name: []const u8,
3048 offset: u64,3028 offset: u64,
3049) !void {3029) !void {
3050 try strings_ni.resize(&coff.mf, coff.base.comp.gpa, new_size);3030 try strings_ni.resizeLeaf(&coff.mf, coff.base.comp.gpa, new_size);
3051 const name_slice = strings_ni.slice(&coff.mf)[offset..][0 .. name.len + 1];3031 const name_slice = strings_ni.slice(&coff.mf)[offset..][0 .. name.len + 1];
3052 @memcpy(name_slice[0..name.len], name);3032 @memcpy(name_slice[0..name.len], name);
3053 name_slice[name.len] = 0;3033 name_slice[name.len] = 0;
...@@ -3080,7 +3060,7 @@ fn ensureMemberSymbol(coff: *Coff, mi: Member.Index, name: String) !void {...@@ -3080,7 +3060,7 @@ fn ensureMemberSymbol(coff: *Coff, mi: Member.Index, name: String) !void {
3080 {3060 {
3081 const old_header_size: usize = @intCast(@sizeOf(u32) + @backingInt(mfli) * @sizeOf(u32));3061 const old_header_size: usize = @intCast(@sizeOf(u32) + @backingInt(mfli) * @sizeOf(u32));
3082 const new_header_size: usize = @intCast(old_header_size + @sizeOf(u32));3062 const new_header_size: usize = @intCast(old_header_size + @sizeOf(u32));
3083 try Node.known.first_linker_member.resize(&coff.mf, gpa, new_header_size + new_string_table_size);3063 try Node.known.first_linker_member.resizeLeaf(&coff.mf, gpa, Alignment.@"4".forward(new_header_size + new_string_table_size));
30843064
3085 const slice = Node.known.first_linker_member.slice(&coff.mf);3065 const slice = Node.known.first_linker_member.slice(&coff.mf);
3086 @memmove(slice[new_header_size..][0..coff.lib_string_len], slice[old_header_size..][0..coff.lib_string_len]);3066 @memmove(slice[new_header_size..][0..coff.lib_string_len], slice[old_header_size..][0..coff.lib_string_len]);
...@@ -3094,7 +3074,7 @@ fn ensureMemberSymbol(coff: *Coff, mi: Member.Index, name: String) !void {...@@ -3094,7 +3074,7 @@ fn ensureMemberSymbol(coff: *Coff, mi: Member.Index, name: String) !void {
3094 const num_members = coff.targetLoad(coff.secondLinkerMemberNumMembersPtr());3074 const num_members = coff.targetLoad(coff.secondLinkerMemberNumMembersPtr());
3095 const old_header_size = 2 * @sizeOf(u32) + num_members * @sizeOf(u32) + @backingInt(mfli) * @sizeOf(u16);3075 const old_header_size = 2 * @sizeOf(u32) + num_members * @sizeOf(u32) + @backingInt(mfli) * @sizeOf(u16);
3096 const new_header_size = old_header_size + @sizeOf(u16);3076 const new_header_size = old_header_size + @sizeOf(u16);
3097 try Node.known.second_linker_member.resize(&coff.mf, gpa, new_header_size + new_string_table_size);3077 try Node.known.second_linker_member.resizeLeaf(&coff.mf, gpa, Alignment.@"4".forward(new_header_size + new_string_table_size));
30983078
3099 const old_needs_sort = coff.pending_members.get(Member.Index.second) != null;3079 const old_needs_sort = coff.pending_members.get(Member.Index.second) != null;
3100 const needs_sort = old_needs_sort or (if (coff.lib_string_table.items.len > 0)3080 const needs_sort = old_needs_sort or (if (coff.lib_string_table.items.len > 0)
...@@ -3151,7 +3131,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32, pt: Zcu.PerThread) !void {...@@ -3151,7 +3131,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32, pt: Zcu.PerThread) !void {
3151 else3131 else
3152 .NULL,3132 .NULL,
3153 };3133 };
3154 } else blk: switch (coff.getNode(sym.ni)) {3134 } else blk: switch (coff.getNode(sym.ni.unwrap().?)) {
3155 .image_section => .{3135 .image_section => .{
3156 try coff.getOrPutSymbolName(&sym.section_number.header(coff).name, null),3136 try coff.getOrPutSymbolName(&sym.section_number.header(coff).name, null),
3157 1,3137 1,
...@@ -3192,7 +3172,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32, pt: Zcu.PerThread) !void {...@@ -3192,7 +3172,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32, pt: Zcu.PerThread) !void {
3192 };3172 };
3193 },3173 },
3194 else => {3174 else => {
3195 log.err("TODO implement symbol table init for {s} ({d})", .{ @tagName(coff.getNode(sym.ni)), si });3175 log.err("TODO implement symbol table init for {s} ({d})", .{ @tagName(coff.getNode(sym.ni.unwrap().?)), si });
3196 unreachable;3176 unreachable;
3197 },3177 },
3198 };3178 };
...@@ -3201,7 +3181,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32, pt: Zcu.PerThread) !void {...@@ -3201,7 +3181,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32, pt: Zcu.PerThread) !void {
3201 const new_num_symbols = old_num_symbols + 1 + num_aux_symbols;3181 const new_num_symbols = old_num_symbols + 1 + num_aux_symbols;
3202 coff.targetStore(&coff.headerPtr().number_of_symbols, new_num_symbols);3182 coff.targetStore(&coff.headerPtr().number_of_symbols, new_num_symbols);
32033183
3204 try coff.symbol_table.ni.resize(&coff.mf, gpa, new_num_symbols * std.coff.Symbol.sizeOf());3184 try coff.symbol_table.ni.resizeLeaf(&coff.mf, gpa, new_num_symbols * std.coff.Symbol.sizeOf());
32053185
3206 sti.* = .wrap(old_num_symbols);3186 sti.* = .wrap(old_num_symbols);
3207 si.flushSymbolTableIndex(coff);3187 si.flushSymbolTableIndex(coff);
...@@ -3255,13 +3235,13 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32, pt: Zcu.PerThread) !void {...@@ -3255,13 +3235,13 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32, pt: Zcu.PerThread) !void {
3255 std.mem.byteSwapAllFieldsAligned(std.coff.WeakExternalDefinition, .@"2", aux_ptr);3235 std.mem.byteSwapAllFieldsAligned(std.coff.WeakExternalDefinition, .@"2", aux_ptr);
32563236
3257 break :aux_init;3237 break :aux_init;
3258 } else switch (coff.getNode(sym.ni)) {3238 } else switch (coff.getNode(sym.ni.unwrap().?)) {
3259 .image_section => |sec_si| {3239 .image_section => |sec_si| {
3260 assert(si == sec_si);3240 assert(si == sec_si);
3261 const header = sym.section_number.header(coff);3241 const header = sym.section_number.header(coff);
3262 const aux_ptr = coff.symbolTableSectionAuxEntryPtr(sti.*).?;3242 const aux_ptr = coff.symbolTableSectionAuxEntryPtr(sti.*).?;
3263 aux_ptr.* = .{3243 aux_ptr.* = .{
3264 .length = @intCast(sym.ni.location(&coff.mf).resolve(&coff.mf)[1]),3244 .length = @intCast(sym.ni.unwrap().?.location(&coff.mf).resolve(&coff.mf)[1]),
3265 .number_of_relocations = header.number_of_relocations,3245 .number_of_relocations = header.number_of_relocations,
3266 .number_of_linenumbers = header.number_of_linenumbers,3246 .number_of_linenumbers = header.number_of_linenumbers,
3267 .checksum = 0,3247 .checksum = 0,
...@@ -3288,7 +3268,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32, pt: Zcu.PerThread) !void {...@@ -3288,7 +3268,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32, pt: Zcu.PerThread) !void {
3288 .ABSOLUTE,3268 .ABSOLUTE,
3289 .DEBUG,3269 .DEBUG,
3290 => unreachable,3270 => unreachable,
3291 else => switch (coff.getNode(sym.ni)) {3271 else => switch (coff.getNode(sym.ni.unwrap().?)) {
3292 .image_section => 0,3272 .image_section => 0,
3293 else => coff.computeSymbolSectionOffset(sym, .image),3273 else => coff.computeSymbolSectionOffset(sym, .image),
3294 },3274 },
...@@ -3364,13 +3344,13 @@ fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !S...@@ -3364,13 +3344,13 @@ fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !S
3364 const section_index = coff.targetLoad(&coff_header.number_of_sections);3344 const section_index = coff.targetLoad(&coff_header.number_of_sections);
3365 const section_table_len = section_index + 1;3345 const section_table_len = section_index + 1;
3366 coff.targetStore(&coff_header.number_of_sections, section_table_len);3346 coff.targetStore(&coff_header.number_of_sections, section_table_len);
3367 try Node.known.section_table.resize(3347 try Node.known.section_table.resizeLeaf(
3368 &coff.mf,3348 &coff.mf,
3369 gpa,3349 gpa,
3370 @sizeOf(std.coff.SectionHeader) * section_table_len,3350 @sizeOf(std.coff.SectionHeader) * section_table_len,
3371 );3351 );
33723352
3373 const ni = try coff.mf.addLastChildNode(gpa, coff.sectionParent(), .{3353 const ni = try coff.sectionParent().addFloatingChild(&coff.mf, gpa, .{
3374 .alignment = coff.mf.flags.block_size,3354 .alignment = coff.mf.flags.block_size,
3375 .moved = true,3355 .moved = true,
3376 .bubbles_moved = false,3356 .bubbles_moved = false,
...@@ -3397,7 +3377,7 @@ fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !S...@@ -3397,7 +3377,7 @@ fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !S
33973377
3398 {3378 {
3399 const sym = si.get(coff);3379 const sym = si.get(coff);
3400 sym.ni = ni;3380 sym.ni = .wrap(ni);
3401 sym.rva = rva;3381 sym.rva = rva;
3402 sym.section_number = @fromBackingInt(@intCast(section_table_len));3382 sym.section_number = @fromBackingInt(@intCast(section_table_len));
3403 }3383 }
...@@ -3481,7 +3461,7 @@ const ObjectSectionAttributes = packed struct {...@@ -3481,7 +3461,7 @@ const ObjectSectionAttributes = packed struct {
3481fn pseudoSectionMapIndex(3461fn pseudoSectionMapIndex(
3482 coff: *Coff,3462 coff: *Coff,
3483 name: String,3463 name: String,
3484 alignment: std.mem.Alignment,3464 alignment: Alignment,
3485 attributes: ObjectSectionAttributes,3465 attributes: ObjectSectionAttributes,
3486) !Node.PseudoSectionMapIndex {3466) !Node.PseudoSectionMapIndex {
3487 const gpa = coff.base.comp.gpa;3467 const gpa = coff.base.comp.gpa;
...@@ -3506,11 +3486,11 @@ fn pseudoSectionMapIndex(...@@ -3506,11 +3486,11 @@ fn pseudoSectionMapIndex(
35063486
3507 try coff.nodes.ensureUnusedCapacity(gpa, 1);3487 try coff.nodes.ensureUnusedCapacity(gpa, 1);
3508 try coff.symbols.ensureUnusedCapacity(gpa, 1);3488 try coff.symbols.ensureUnusedCapacity(gpa, 1);
3509 const ni = try coff.mf.addLastChildNode(gpa, parent.node(coff), .{ .alignment = alignment });3489 const ni = try parent.node(coff).addFloatingChild(&coff.mf, gpa, .{ .alignment = alignment });
3510 const si = coff.addSymbolAssumeCapacity();3490 const si = coff.addSymbolAssumeCapacity();
3511 pseudo_section_gop.value_ptr.* = si;3491 pseudo_section_gop.value_ptr.* = si;
3512 const sym = si.get(coff);3492 const sym = si.get(coff);
3513 sym.ni = ni;3493 sym.ni = .wrap(ni);
3514 sym.rva = coff.computeNodeRva(ni);3494 sym.rva = coff.computeNodeRva(ni);
3515 sym.section_number = parent.get(coff).section_number;3495 sym.section_number = parent.get(coff).section_number;
3516 assert(sym.loc_relocs == .none);3496 assert(sym.loc_relocs == .none);
...@@ -3543,7 +3523,7 @@ fn objectSectionParentName(coff: *Coff, name: []const u8) []const u8 {...@@ -3543,7 +3523,7 @@ fn objectSectionParentName(coff: *Coff, name: []const u8) []const u8 {
3543fn objectSectionMapIndex(3523fn objectSectionMapIndex(
3544 coff: *Coff,3524 coff: *Coff,
3545 name: String,3525 name: String,
3546 alignment: std.mem.Alignment,3526 alignment: Alignment,
3547 attributes: ObjectSectionAttributes,3527 attributes: ObjectSectionAttributes,
3548) !Node.ObjectSectionMapIndex {3528) !Node.ObjectSectionMapIndex {
3549 const gpa = coff.base.comp.gpa;3529 const gpa = coff.base.comp.gpa;
...@@ -3565,31 +3545,28 @@ fn objectSectionMapIndex(...@@ -3565,31 +3545,28 @@ fn objectSectionMapIndex(
3565 try coff.nodes.ensureUnusedCapacity(gpa, 1);3545 try coff.nodes.ensureUnusedCapacity(gpa, 1);
3566 try coff.symbols.ensureUnusedCapacity(gpa, 1);3546 try coff.symbols.ensureUnusedCapacity(gpa, 1);
3567 const parent_ni = parent.node(coff);3547 const parent_ni = parent.node(coff);
3568 var prev_ni: MappedFile.Node.Index = .none;3548 var prev_oni: MappedFile.Node.Index.Optional = .none;
3569 var next_it = parent_ni.children(&coff.mf);3549 {
3570 while (next_it.next()) |next_ni| switch (std.mem.order(3550 var child_oni = parent_ni.first(&coff.mf);
3571 u8,3551 while (child_oni.unwrap()) |child_ni| : (child_oni = child_ni.next(&coff.mf)) {
3572 name_slice,3552 switch (std.mem.order(
3573 coff.getNode(next_ni).object_section.name(coff).toSlice(coff),3553 u8,
3574 )) {3554 name_slice,
3575 .lt => break,3555 coff.getNode(child_ni).object_section.name(coff).toSlice(coff),
3576 .eq => unreachable,3556 )) {
3577 .gt => prev_ni = next_ni,3557 .lt => break,
3578 };3558 .eq => unreachable,
3579 const ni = switch (prev_ni) {3559 .gt => prev_oni = .wrap(child_ni),
3580 .none => try coff.mf.addFirstChildNode(gpa, parent_ni, .{3560 }
3581 .alignment = alignment,3561 }
3582 .fixed = true,3562 }
3583 }),3563 const ni = try parent_ni.addHeaderChildAfter(&coff.mf, gpa, prev_oni, .{
3584 else => try coff.mf.addNodeAfter(gpa, prev_ni, .{3564 .alignment = alignment,
3585 .alignment = alignment,3565 });
3586 .fixed = true,
3587 }),
3588 };
3589 const si = coff.addSymbolAssumeCapacity();3566 const si = coff.addSymbolAssumeCapacity();
3590 object_section_gop.value_ptr.* = si;3567 object_section_gop.value_ptr.* = si;
3591 const sym = si.get(coff);3568 const sym = si.get(coff);
3592 sym.ni = ni;3569 sym.ni = .wrap(ni);
3593 sym.rva = coff.computeNodeRva(ni);3570 sym.rva = coff.computeNodeRva(ni);
3594 sym.section_number = parent.get(coff).section_number;3571 sym.section_number = parent.get(coff).section_number;
3595 assert(sym.loc_relocs == .none);3572 assert(sym.loc_relocs == .none);
...@@ -3598,17 +3575,17 @@ fn objectSectionMapIndex(...@@ -3598,17 +3575,17 @@ fn objectSectionMapIndex(
3598 break :sym sym;3575 break :sym sym;
3599 } else object_section_gop.value_ptr.get(coff);3576 } else object_section_gop.value_ptr.get(coff);
36003577
3601 const parent_ni = sym.ni.parent(&coff.mf);3578 const parent_ni = sym.ni.unwrap().?.parent(&coff.mf).unwrap().?;
3602 const parent_alignment = parent_ni.alignment(&coff.mf);3579 const parent_alignment = parent_ni.alignment(&coff.mf);
3603 if (alignment.compare(.gt, parent_alignment)) {3580 if (alignment.compare(.gt, parent_alignment)) {
3604 log.debug("realignParent({s}, {d}) {d}->{d}", .{ name.toSlice(coff), parent_ni, parent_alignment, alignment });3581 log.debug("realignParent({s}, {d}) {d}->{d}", .{ name.toSlice(coff), parent_ni, parent_alignment, alignment });
3605 try parent_ni.realign(&coff.mf, gpa, alignment, .{ .try_backwards = true });3582 try parent_ni.realign(&coff.mf, gpa, alignment);
3606 }3583 }
36073584
3608 const old_alignment = sym.ni.alignment(&coff.mf);3585 const old_alignment = sym.ni.unwrap().?.alignment(&coff.mf);
3609 if (alignment.compare(.gt, old_alignment)) {3586 if (alignment.compare(.gt, old_alignment)) {
3610 log.debug("realignObject({s}) {d}->{d}", .{ name.toSlice(coff), old_alignment, alignment });3587 log.debug("realignObject({s}) {d}->{d}", .{ name.toSlice(coff), old_alignment, alignment });
3611 try sym.ni.realign(&coff.mf, gpa, alignment, .{ .try_backwards = true });3588 try sym.ni.unwrap().?.realign(&coff.mf, gpa, alignment);
3612 }3589 }
36133590
3614 try coff.verifyParentSectionAttributes(3591 try coff.verifyParentSectionAttributes(
...@@ -3764,20 +3741,16 @@ fn addRelocAssumeCapacity(...@@ -3764,20 +3741,16 @@ fn addRelocAssumeCapacity(
3764 if (coff.symbolTableSectionAuxEntryPtr(loc_sn.symbol(coff).sti(coff))) |aux_ptr|3741 if (coff.symbolTableSectionAuxEntryPtr(loc_sn.symbol(coff).sti(coff))) |aux_ptr|
3765 coff.targetStore(&aux_ptr.number_of_relocations, new_num_relocations);3742 coff.targetStore(&aux_ptr.number_of_relocations, new_num_relocations);
37663743
3767 if (section.relocation_table_ni == .none) {3744 if (section.relocation_table_ni.unwrap()) |relocation_table_ni| {
3768 section.relocation_table_ni = try coff.mf.addLastChildNode(3745 try relocation_table_ni.resizeLeaf(&coff.mf, gpa, new_size);
3769 gpa,
3770 coff.sectionParent(),
3771 .{
3772 .size = new_size,
3773 .alignment = .@"2",
3774 .moved = true,
3775 .resized = true,
3776 },
3777 );
3778 coff.nodes.appendAssumeCapacity(.{ .relocation_table = loc_sn });
3779 } else {3746 } else {
3780 try section.relocation_table_ni.resize(&coff.mf, gpa, new_size);3747 section.relocation_table_ni = .wrap(try coff.sectionParent().addFloatingChild(&coff.mf, gpa, .{
3748 .size = new_size,
3749 .alignment = .@"2",
3750 .moved = true,
3751 .resized = true,
3752 }));
3753 coff.nodes.appendAssumeCapacity(.{ .relocation_table = loc_sn });
3781 }3754 }
37823755
3783 // TODO: These need to allocate from a free list, once deleting relocs from the table is supported3756 // TODO: These need to allocate from a free list, once deleting relocs from the table is supported
...@@ -4581,7 +4554,7 @@ fn loadObject(...@@ -4581,7 +4554,7 @@ fn loadObject(
4581 },4554 },
4582 .SAME_SIZE => {4555 .SAME_SIZE => {
4583 // TODO: Verify that this node isn't resized after creation4556 // TODO: Verify that this node isn't resized after creation
4584 _, const size = si.get(coff).ni.location(&coff.mf).resolve(&coff.mf);4557 _, const size = si.get(coff).ni.unwrap().?.location(&coff.mf).resolve(&coff.mf);
4585 if (size == section.header.size_of_raw_data) {4558 if (size == section.header.size_of_raw_data) {
4586 symbol.si = si;4559 symbol.si = si;
4587 break :comdat .skip;4560 break :comdat .skip;
...@@ -4598,9 +4571,9 @@ fn loadObject(...@@ -4598,9 +4571,9 @@ fn loadObject(
4598 },4571 },
4599 .EXACT_MATCH => {4572 .EXACT_MATCH => {
4600 const sym = si.get(coff);4573 const sym = si.get(coff);
4601 const existing_crc = switch (coff.getNode(sym.ni)) {4574 const existing_crc = switch (coff.getNode(sym.ni.unwrap().?)) {
4602 .input_section => |isi| isi.inputSection(coff).crc,4575 .input_section => |isi| isi.inputSection(coff).crc,
4603 else => Crc32.hash(sym.ni.sliceConst(&coff.mf)),4576 else => Crc32.hash(sym.ni.unwrap().?.sliceConst(&coff.mf)),
4604 };4577 };
46054578
4606 if (existing_crc == section.comdat_crc) {4579 if (existing_crc == section.comdat_crc) {
...@@ -4666,7 +4639,7 @@ fn loadObject(...@@ -4666,7 +4639,7 @@ fn loadObject(
46664639
4667 section.parent_si = (try coff.objectSectionMapIndex(4640 section.parent_si = (try coff.objectSectionMapIndex(
4668 section.name,4641 section.name,
4669 section.header.flags.ALIGN.alignment() orelse .@"1",4642 .fromByteUnits(section.header.flags.ALIGN.toByteUnits() orelse 1),
4670 .fromFlags(section.header.flags),4643 .fromFlags(section.header.flags),
4671 )).symbol(coff);4644 )).symbol(coff);
4672 }4645 }
...@@ -4679,9 +4652,10 @@ fn loadObject(...@@ -4679,9 +4652,10 @@ fn loadObject(
4679 for (sections) |*section| {4652 for (sections) |*section| {
4680 if (section.parent_si == .null) continue;4653 if (section.parent_si == .null) continue;
46814654
4682 const ni = try coff.mf.addLastChildNode(gpa, section.parent_si.node(coff), .{4655 const alignment: Alignment = .fromByteUnits(section.header.flags.ALIGN.toByteUnits() orelse 1);
4683 .size = section.header.size_of_raw_data,4656 const ni = try section.parent_si.node(coff).addFloatingChild(&coff.mf, gpa, .{
4684 .alignment = section.header.flags.ALIGN.alignment() orelse .@"1",4657 .size = alignment.forward(section.header.size_of_raw_data),
4658 .alignment = alignment,
4685 .moved = true,4659 .moved = true,
4686 });4660 });
4687 coff.nodes.appendAssumeCapacity(.{ .input_section = @fromBackingInt(@intCast(coff.input_sections.items.len)) });4661 coff.nodes.appendAssumeCapacity(.{ .input_section = @fromBackingInt(@intCast(coff.input_sections.items.len)) });
...@@ -4691,7 +4665,7 @@ fn loadObject(...@@ -4691,7 +4665,7 @@ fn loadObject(
4691 pending_symbols.values()[psi].si = section.si;4665 pending_symbols.values()[psi].si = section.si;
46924666
4693 const sym = section.si.get(coff);4667 const sym = section.si.get(coff);
4694 sym.ni = ni;4668 sym.ni = .wrap(ni);
4695 sym.section_number = section.parent_si.get(coff).section_number;4669 sym.section_number = section.parent_si.get(coff).section_number;
46964670
4697 coff.input_sections.addOneAssumeCapacity().* = .{4671 coff.input_sections.addOneAssumeCapacity().* = .{
...@@ -4852,7 +4826,7 @@ fn loadObject(...@@ -4852,7 +4826,7 @@ fn loadObject(
4852 }4826 }
48534827
4854 if (section.comdat_psi.unwrap() == @as(u32, @intCast(i)))4828 if (section.comdat_psi.unwrap() == @as(u32, @intCast(i)))
4855 coff.getNode(section.si.get(coff).ni).input_section.inputSection(coff).comdat_si = symbol.si;4829 coff.getNode(section.si.get(coff).ni.unwrap().?).input_section.inputSection(coff).comdat_si = symbol.si;
4856 }4830 }
48574831
4858 if (symbol.weak_external_psi.unwrap()) |weak_external_i| {4832 if (symbol.weak_external_psi.unwrap()) |weak_external_i| {
...@@ -4967,14 +4941,14 @@ fn loadObject(...@@ -4967,14 +4941,14 @@ fn loadObject(
4967 const section = &sections[symbol.section_number.toIndex()];4941 const section = &sections[symbol.section_number.toIndex()];
4968 include_section = section.comdat_result == .include;4942 include_section = section.comdat_result == .include;
4969 if (include_section) {4943 if (include_section) {
4970 const isi = coff.getNode(section.si.get(coff).ni).input_section;4944 const isi = coff.getNode(section.si.get(coff).ni.unwrap().?).input_section;
4971 isi.inputSection(coff).first_li = @fromBackingInt(@intCast(coff.input_symbols.items.len));4945 isi.inputSection(coff).first_li = @fromBackingInt(@intCast(coff.input_symbols.items.len));
4972 }4946 }
4973 }4947 }
4974 }4948 }
49754949
4976 if (include_section) {4950 if (include_section) {
4977 assert(coff.getNode(symbol.si.get(coff).ni) == .input_section);4951 assert(coff.getNode(symbol.si.get(coff).ni.unwrap().?) == .input_section);
4978 symbol.si.get(coff).setExtra(.{ .isli = @fromBackingInt(@intCast(coff.input_symbols.items.len)) });4952 symbol.si.get(coff).setExtra(.{ .isli = @fromBackingInt(@intCast(coff.input_symbols.items.len)) });
4979 coff.input_symbols.addOneAssumeCapacity().* = .{4953 coff.input_symbols.addOneAssumeCapacity().* = .{
4980 .si = symbol.si,4954 .si = symbol.si,
...@@ -5002,7 +4976,7 @@ fn failMultipleDefinitions(...@@ -5002,7 +4976,7 @@ fn failMultipleDefinitions(
5002 var err = try coff.base.comp.link_diags.addErrorWithNotes(num_notes);4976 var err = try coff.base.comp.link_diags.addErrorWithNotes(num_notes);
5003 try err.addMsg("multiple definitions of '{s}'", .{name.toSlice(coff)});4977 try err.addMsg("multiple definitions of '{s}'", .{name.toSlice(coff)});
50044978
5005 switch (coff.getNode(existing_si.get(coff).ni)) {4979 switch (coff.getNode(existing_si.get(coff).ni.unwrap().?)) {
5006 .input_section => |isi| {4980 .input_section => |isi| {
5007 const other_ioi = isi.input(coff);4981 const other_ioi = isi.input(coff);
5008 err.addNote("first seen in input '{f}{f}'", .{4982 err.addNote("first seen in input '{f}{f}'", .{
...@@ -5473,13 +5447,13 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde...@@ -5473,13 +5447,13 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde
5473 const sec_si = try coff.navSection(zcu, nav.resolved.?);5447 const sec_si = try coff.navSection(zcu, nav.resolved.?);
5474 try coff.nodes.ensureUnusedCapacity(gpa, 1);5448 try coff.nodes.ensureUnusedCapacity(gpa, 1);
5475 if (!isImage(coff)) try coff.symbol_table.symbols.ensureUnusedCapacity(gpa, 1);5449 if (!isImage(coff)) try coff.symbol_table.symbols.ensureUnusedCapacity(gpa, 1);
5476 const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{5450 const ni = try sec_si.node(coff).addFloatingChild(&coff.mf, gpa, .{
5477 .alignment = zcu.navAlignment(nav_index).toStdMem(),5451 .alignment = .fromIp(zcu.navAlignment(nav_index)),
5478 .moved = true,5452 .moved = true,
5479 });5453 });
5480 coff.nodes.appendAssumeCapacity(.{ .nav = nmi });5454 coff.nodes.appendAssumeCapacity(.{ .nav = nmi });
5481 const sym = si.get(coff);5455 const sym = si.get(coff);
5482 sym.ni = ni;5456 sym.ni = .wrap(ni);
5483 sym.section_number = sec_si.get(coff).section_number;5457 sym.section_number = sec_si.get(coff).section_number;
5484 },5458 },
5485 else => si.deleteLocationRelocs(coff),5459 else => si.deleteLocationRelocs(coff),
...@@ -5490,7 +5464,7 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde...@@ -5490,7 +5464,7 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde
5490 if (!isImage(coff) and sym.target_relocs != .none)5464 if (!isImage(coff) and sym.target_relocs != .none)
5491 try coff.pendingSymbolTableEntry(si);5465 try coff.pendingSymbolTableEntry(si);
54925466
5493 break :ni sym.ni;5467 break :ni sym.ni.unwrap().?;
5494 };5468 };
54955469
5496 {5470 {
...@@ -5512,21 +5486,7 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde...@@ -5512,21 +5486,7 @@ fn updateNavInner(coff: *Coff, pt: Zcu.PerThread, nav_index: InternPool.Nav.Inde
5512 }5486 }
55135487
5514 if (nav.resolved.?.@"linksection".unwrap()) |_| {5488 if (nav.resolved.?.@"linksection".unwrap()) |_| {
5515 try ni.resize(&coff.mf, gpa, si.get(coff).extra.size);5489 try ni.resizeLeaf(&coff.mf, gpa, si.get(coff).extra.size);
5516 var parent_ni = ni;
5517 while (true) {
5518 parent_ni = parent_ni.parent(&coff.mf);
5519 switch (coff.getNode(parent_ni)) {
5520 else => unreachable,
5521 .image_section, .pseudo_section => break,
5522 .object_section => {
5523 var child_it = parent_ni.reverseChildren(&coff.mf);
5524 const last_offset, const last_size =
5525 child_it.next().?.location(&coff.mf).resolve(&coff.mf);
5526 try parent_ni.resize(&coff.mf, gpa, last_offset + last_size);
5527 },
5528 }
5529 }
5530 }5490 }
5531}5491}
55325492
...@@ -5542,10 +5502,11 @@ pub fn lowerUav(...@@ -5542,10 +5502,11 @@ pub fn lowerUav(
5542 try coff.pending_uavs.ensureUnusedCapacity(gpa, 1);5502 try coff.pending_uavs.ensureUnusedCapacity(gpa, 1);
5543 const umi = try coff.uavMapIndex(uav_val);5503 const umi = try coff.uavMapIndex(uav_val);
5544 const si = umi.symbol(coff);5504 const si = umi.symbol(coff);
5545 if (switch (si.get(coff).ni) {5505 const need_update: bool = update: {
5546 .none => true,5506 const existing_ni = si.get(coff).ni.unwrap() orelse break :update true;
5547 else => |ni| uav_align.toStdMem().order(ni.alignment(&coff.mf)).compare(.gt),5507 break :update Alignment.compare(.fromIp(uav_align), .gt, existing_ni.alignment(&coff.mf));
5548 }) {5508 };
5509 if (need_update) {
5549 const gop = coff.pending_uavs.getOrPutAssumeCapacity(umi);5510 const gop = coff.pending_uavs.getOrPutAssumeCapacity(umi);
5550 if (gop.found_existing) {5511 if (gop.found_existing) {
5551 gop.value_ptr.alignment = gop.value_ptr.alignment.max(uav_align);5512 gop.value_ptr.alignment = gop.value_ptr.alignment.max(uav_align);
...@@ -5597,22 +5558,22 @@ fn updateFuncInner(...@@ -5597,22 +5558,22 @@ fn updateFuncInner(
5597 if (!isImage(coff)) try coff.symbol_table.symbols.ensureUnusedCapacity(gpa, 1);5558 if (!isImage(coff)) try coff.symbol_table.symbols.ensureUnusedCapacity(gpa, 1);
5598 const mod = zcu.navFileScope(func.owner_nav).mod.?;5559 const mod = zcu.navFileScope(func.owner_nav).mod.?;
5599 const target = &mod.resolved_target.result;5560 const target = &mod.resolved_target.result;
5600 const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{5561 const ni = try sec_si.node(coff).addFloatingChild(&coff.mf, gpa, .{
5601 .alignment = switch (nav.resolved.?.@"align") {5562 .alignment = switch (nav.resolved.?.@"align") {
5602 .none => switch (mod.optimize_mode) {5563 .none => switch (mod.optimize_mode) {
5603 .debug,5564 .debug,
5604 .safe,5565 .safe,
5605 .fast,5566 .fast,
5606 => target_util.defaultFunctionAlignment(target),5567 => .fromIp(target_util.defaultFunctionAlignment(target)),
5607 .small => target_util.minFunctionAlignment(target),5568 .small => .fromIp(target_util.minFunctionAlignment(target)),
5608 },5569 },
5609 else => |a| a.maxStrict(target_util.minFunctionAlignment(target)),5570 else => |a| .fromIp(a.maxStrict(target_util.minFunctionAlignment(target))),
5610 }.toStdMem(),5571 },
5611 .moved = true,5572 .moved = true,
5612 });5573 });
5613 coff.nodes.appendAssumeCapacity(.{ .nav = nmi });5574 coff.nodes.appendAssumeCapacity(.{ .nav = nmi });
5614 const sym = si.get(coff);5575 const sym = si.get(coff);
5615 sym.ni = ni;5576 sym.ni = .wrap(ni);
5616 sym.section_number = sec_si.get(coff).section_number;5577 sym.section_number = sec_si.get(coff).section_number;
5617 },5578 },
5618 else => si.deleteLocationRelocs(coff),5579 else => si.deleteLocationRelocs(coff),
...@@ -5622,7 +5583,7 @@ fn updateFuncInner(...@@ -5622,7 +5583,7 @@ fn updateFuncInner(
5622 sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));5583 sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
5623 if (!isImage(coff) and sym.target_relocs != .none)5584 if (!isImage(coff) and sym.target_relocs != .none)
5624 try coff.pendingSymbolTableEntry(si);5585 try coff.pendingSymbolTableEntry(si);
5625 break :ni sym.ni;5586 break :ni sym.ni.unwrap().?;
5626 };5587 };
56275588
5628 var nw: MappedFile.Node.Writer = undefined;5589 var nw: MappedFile.Node.Writer = undefined;
...@@ -5662,7 +5623,6 @@ fn flushImplib(...@@ -5662,7 +5623,6 @@ fn flushImplib(
5662 implib_file: []const u8,5623 implib_file: []const u8,
5663) !void {5624) !void {
5664 // Emitting implibs is only valid for images5625 // Emitting implibs is only valid for images
5665 assert(coff.export_table.ni != .none);
56665626
5667 const comp = coff.base.comp;5627 const comp = coff.base.comp;
5668 const gpa = comp.gpa;5628 const gpa = comp.gpa;
...@@ -5797,7 +5757,7 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {...@@ -5797,7 +5757,7 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
5797 const loc_sym = loc_si.get(coff);5757 const loc_sym = loc_si.get(coff);
57985758
5799 // TODO: Make this a helper for anything that needs to report "referenced by" notes5759 // TODO: Make this a helper for anything that needs to report "referenced by" notes
5800 switch (coff.getNode(loc_sym.ni)) {5760 switch (coff.getNode(loc_sym.ni.unwrap().?)) {
5801 .data_directories => {5761 .data_directories => {
5802 const dir: std.coff.IMAGE.DIRECTORY_ENTRY =5762 const dir: std.coff.IMAGE.DIRECTORY_ENTRY =
5803 @fromBackingInt(@intCast(reloc.offset / @sizeOf(std.coff.ImageDataDirectory)));5763 @fromBackingInt(@intCast(reloc.offset / @sizeOf(std.coff.ImageDataDirectory)));
...@@ -5808,7 +5768,7 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {...@@ -5808,7 +5768,7 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
5808 const other_ioi = isi.input(coff);5768 const other_ioi = isi.input(coff);
5809 if (loc_sym.gmi == .none) {5769 if (loc_sym.gmi == .none) {
5810 const section = isi.inputSection(coff);5770 const section = isi.inputSection(coff);
5811 const section_name = coff.getNode(loc_sym.ni.parent(&coff.mf))5771 const section_name = coff.getNode(loc_sym.ni.unwrap().?.parent(&coff.mf).unwrap().?)
5812 .object_section.name(coff).toSlice(coff);5772 .object_section.name(coff).toSlice(coff);
58135773
5814 if (section.comdat_si != .null) {5774 if (section.comdat_si != .null) {
...@@ -5902,15 +5862,17 @@ pub fn flush(...@@ -5902,15 +5862,17 @@ pub fn flush(
5902 coff.symbol_table.pending_shrink = false;5862 coff.symbol_table.pending_shrink = false;
59035863
5904 const number_of_symbols = coff.targetLoad(&coff.headerPtr().number_of_symbols);5864 const number_of_symbols = coff.targetLoad(&coff.headerPtr().number_of_symbols);
5905 coff.symbol_table.ni.shrink(5865 coff.symbol_table.ni.resizeLeaf(
5906 &coff.mf,5866 &coff.mf,
5907 comp.gpa,5867 comp.gpa,
5908 number_of_symbols * std.coff.Symbol.sizeOf(),5868 number_of_symbols * std.coff.Symbol.sizeOf(),
5909 true,5869 ) catch |err| switch (err) {
5910 ) catch |err| return comp.link_diags.fail(5870 else => |e| return e,
5911 "linker failed to compact symbol table: {t}",5871 error.MappedFileIo => return comp.link_diags.fail(
5912 .{err},5872 "linker failed to compact symbol table: {t}",
5913 );5873 .{coff.mf.io_err.?},
5874 ),
5875 };
5914 }5876 }
5915 while (try coff.idle(tid)) {}5877 while (try coff.idle(tid)) {}
59165878
...@@ -6055,8 +6017,8 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool {...@@ -6055,8 +6017,8 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
6055 const sub_prog_node = coff.idleProgNode(6017 const sub_prog_node = coff.idleProgNode(
6056 tid,6018 tid,
6057 coff.symbol_prog_node,6019 coff.symbol_prog_node,
6058 if (sym.ni != .none)6020 if (sym.ni.unwrap()) |sym_ni|
6059 coff.getNode(sym.ni)6021 coff.getNode(sym_ni)
6060 else6022 else
6061 .{ .import_thunk = sym.gmi },6023 .{ .import_thunk = sym.gmi },
6062 );6024 );
...@@ -6173,7 +6135,7 @@ fn idleProgNode(...@@ -6173,7 +6135,7 @@ fn idleProgNode(
6173 break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{6135 break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{
6174 ioi.path(coff).fmtEscapeString(),6136 ioi.path(coff).fmtEscapeString(),
6175 fmtMemberNameString(ioi.memberName(coff)),6137 fmtMemberNameString(ioi.memberName(coff)),
6176 coff.getNode(isi.symbol(coff).node(coff).parent(&coff.mf)).object_section.name(coff).toSlice(coff),6138 coff.getNode(isi.symbol(coff).node(coff).parent(&coff.mf).unwrap().?).object_section.name(coff).toSlice(coff),
6177 }) catch &name;6139 }) catch &name;
6178 },6140 },
6179 .import_thunk => |gmi| gmi.name(coff).toSlice(coff),6141 .import_thunk => |gmi| gmi.name(coff).toSlice(coff),
...@@ -6213,17 +6175,22 @@ fn flushUav(...@@ -6213,17 +6175,22 @@ fn flushUav(
6213 try coff.nodes.ensureUnusedCapacity(gpa, 1);6175 try coff.nodes.ensureUnusedCapacity(gpa, 1);
6214 if (!isImage(coff)) try coff.symbol_table.symbols.ensureUnusedCapacity(gpa, 1);6176 if (!isImage(coff)) try coff.symbol_table.symbols.ensureUnusedCapacity(gpa, 1);
6215 const sym = si.get(coff);6177 const sym = si.get(coff);
6216 const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{6178 const ni = try sec_si.node(coff).addFloatingChild(&coff.mf, gpa, .{
6217 .alignment = uav_align.toStdMem(),6179 .alignment = .fromIp(uav_align),
6218 .moved = true,6180 .moved = true,
6219 });6181 });
6220 coff.nodes.appendAssumeCapacity(.{ .uav = umi });6182 coff.nodes.appendAssumeCapacity(.{ .uav = umi });
6221 sym.ni = ni;6183 sym.ni = .wrap(ni);
6222 sym.section_number = sec_si.get(coff).section_number;6184 sym.section_number = sec_si.get(coff).section_number;
6223 },6185 },
6224 else => {6186 else => {
6225 if (si.get(coff).ni.alignment(&coff.mf).order(uav_align.toStdMem()).compare(.gte))6187 if (Alignment.compare(
6188 si.get(coff).ni.unwrap().?.alignment(&coff.mf),
6189 .gte,
6190 .fromIp(uav_align),
6191 )) {
6226 return;6192 return;
6193 }
6227 si.deleteLocationRelocs(coff);6194 si.deleteLocationRelocs(coff);
6228 },6195 },
6229 }6196 }
...@@ -6233,7 +6200,7 @@ fn flushUav(...@@ -6233,7 +6200,7 @@ fn flushUav(
6233 if (!isImage(coff) and sym.target_relocs != .none)6200 if (!isImage(coff) and sym.target_relocs != .none)
6234 try coff.pendingSymbolTableEntry(si);6201 try coff.pendingSymbolTableEntry(si);
62356202
6236 break :ni sym.ni;6203 break :ni sym.ni.unwrap().?;
6237 };6204 };
62386205
6239 var nw: MappedFile.Node.Writer = undefined;6206 var nw: MappedFile.Node.Writer = undefined;
...@@ -6497,23 +6464,23 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -6497,23 +6464,23 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
6497 lib_name,6464 lib_name,
6498 ImportTable.Adapter{ .coff = coff },6465 ImportTable.Adapter{ .coff = coff },
6499 );6466 );
6500 const import_hint_name_align: std.mem.Alignment = .@"2";6467 const import_hint_name_align: Alignment = .@"2";
6501 if (!gop.found_existing) {6468 if (!gop.found_existing) {
6502 errdefer _ = coff.import_table.entries.pop();6469 errdefer _ = coff.import_table.entries.pop();
6503 try coff.import_table.ni.resize(6470 try coff.import_table.ni.resizeLeaf(
6504 &coff.mf,6471 &coff.mf,
6505 gpa,6472 gpa,
6506 @sizeOf(std.coff.ImportDirectoryEntry) * (gop.index + 2),6473 @sizeOf(std.coff.ImportDirectoryEntry) * (gop.index + 2),
6507 );6474 );
6508 const import_hint_name_table_len =6475 const import_hint_name_table_len =
6509 import_hint_name_align.forward(lib_name.len + ".dll".len + 1);6476 import_hint_name_align.forward(lib_name.len + ".dll".len + 1);
6510 const idata_section_ni = coff.import_table.ni.parent(&coff.mf);6477 const idata_section_ni = coff.import_table.ni.parent(&coff.mf).unwrap().?;
6511 const import_lookup_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{6478 const import_lookup_table_ni = try idata_section_ni.addFloatingChild(&coff.mf, gpa, .{
6512 .size = addr_info.size * 2,6479 .size = addr_info.size * 2,
6513 .alignment = addr_info.alignment,6480 .alignment = addr_info.alignment,
6514 .moved = true,6481 .moved = true,
6515 });6482 });
6516 const import_address_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{6483 const import_address_table_ni = try idata_section_ni.addFloatingChild(&coff.mf, gpa, .{
6517 .size = addr_info.size * 2,6484 .size = addr_info.size * 2,
6518 .alignment = addr_info.alignment,6485 .alignment = addr_info.alignment,
6519 .moved = true,6486 .moved = true,
...@@ -6521,13 +6488,13 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -6521,13 +6488,13 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
6521 const import_address_table_si = coff.addSymbolAssumeCapacity();6488 const import_address_table_si = coff.addSymbolAssumeCapacity();
6522 {6489 {
6523 const import_address_table_sym = import_address_table_si.get(coff);6490 const import_address_table_sym = import_address_table_si.get(coff);
6524 import_address_table_sym.ni = import_address_table_ni;6491 import_address_table_sym.ni = .wrap(import_address_table_ni);
6525 assert(import_address_table_sym.loc_relocs == .none);6492 assert(import_address_table_sym.loc_relocs == .none);
6526 import_address_table_sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));6493 import_address_table_sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
6527 import_address_table_sym.section_number =6494 import_address_table_sym.section_number =
6528 coff.getNode(idata_section_ni).object_section.symbol(coff).get(coff).section_number;6495 coff.getNode(idata_section_ni).object_section.symbol(coff).get(coff).section_number;
6529 }6496 }
6530 const import_hint_name_table_ni = try coff.mf.addLastChildNode(gpa, idata_section_ni, .{6497 const import_hint_name_table_ni = try idata_section_ni.addFloatingChild(&coff.mf, gpa, .{
6531 .size = import_hint_name_table_len,6498 .size = import_hint_name_table_len,
6532 .alignment = import_hint_name_align,6499 .alignment = import_hint_name_align,
6533 .moved = true,6500 .moved = true,
...@@ -6583,9 +6550,9 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -6583,9 +6550,9 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
6583 gop.value_ptr.len = import_symbol_index + 1;6550 gop.value_ptr.len = import_symbol_index + 1;
6584 const new_symbol_table_size = addr_info.size * (import_symbol_index + 2);6551 const new_symbol_table_size = addr_info.size * (import_symbol_index + 2);
65856552
6586 try gop.value_ptr.import_lookup_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);6553 try gop.value_ptr.import_lookup_table_ni.resizeLeaf(&coff.mf, gpa, new_symbol_table_size);
6587 const import_address_table_ni = gop.value_ptr.import_address_table_si.node(coff);6554 const import_address_table_ni = gop.value_ptr.import_address_table_si.node(coff);
6588 try import_address_table_ni.resize(&coff.mf, gpa, new_symbol_table_size);6555 try import_address_table_ni.resizeLeaf(&coff.mf, gpa, new_symbol_table_size);
65896556
6590 const opt_imp_name = import.name.toSlice(coff);6557 const opt_imp_name = import.name.toSlice(coff);
6591 const opt_import_hint_name_index = if (opt_imp_name) |imp_name| blk: {6558 const opt_import_hint_name_index = if (opt_imp_name) |imp_name| blk: {
...@@ -6593,7 +6560,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -6593,7 +6560,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
6593 gop.value_ptr.hint_name_len = @intCast(6560 gop.value_ptr.hint_name_len = @intCast(
6594 import_hint_name_align.forward(import_hint_name_index + 2 + imp_name.len + 1),6561 import_hint_name_align.forward(import_hint_name_index + 2 + imp_name.len + 1),
6595 );6562 );
6596 try gop.value_ptr.import_hint_name_table_ni.resize(&coff.mf, gpa, gop.value_ptr.hint_name_len);6563 try gop.value_ptr.import_hint_name_table_ni.resizeLeaf(&coff.mf, gpa, gop.value_ptr.hint_name_len);
6597 break :blk import_hint_name_index;6564 break :blk import_hint_name_index;
6598 } else null;6565 } else null;
65996566
...@@ -6648,13 +6615,13 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -6648,13 +6615,13 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
6648 sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));6615 sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
66496616
6650 const target = &comp.root_mod.resolved_target.result;6617 const target = &comp.root_mod.resolved_target.result;
6651 const alignment = switch (comp.root_mod.optimize_mode) {6618 const alignment: Alignment = switch (comp.root_mod.optimize_mode) {
6652 .debug,6619 .debug,
6653 .safe,6620 .safe,
6654 .fast,6621 .fast,
6655 => target_util.defaultFunctionAlignment(target),6622 => .fromIp(target_util.defaultFunctionAlignment(target)),
6656 .small => target_util.minFunctionAlignment(target),6623 .small => .fromIp(target_util.minFunctionAlignment(target)),
6657 }.toStdMem();6624 };
6658 const parent_si = (try coff.pseudoSectionMapIndex(6625 const parent_si = (try coff.pseudoSectionMapIndex(
6659 .@".thunks",6626 .@".thunks",
6660 alignment,6627 alignment,
...@@ -6668,12 +6635,12 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {...@@ -6668,12 +6635,12 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
6668 else => |tag| @panic(@tagName(tag)),6635 else => |tag| @panic(@tagName(tag)),
6669 .AMD64 => {6636 .AMD64 => {
6670 const init = [_]u8{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 };6637 const init = [_]u8{ 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 };
6671 const ni = try coff.mf.addLastChildNode(gpa, parent_sym.ni, .{6638 const ni = try parent_sym.ni.unwrap().?.addFloatingChild(&coff.mf, gpa, .{
6672 .alignment = alignment,6639 .alignment = alignment,
6673 .size = init.len,6640 .size = alignment.forward(init.len),
6674 });6641 });
6675 @memcpy(ni.slice(&coff.mf)[0..init.len], &init);6642 @memcpy(ni.slice(&coff.mf)[0..init.len], &init);
6676 sym.ni = ni;6643 sym.ni = .wrap(ni);
6677 sym.extra.size = init.len;6644 sym.extra.size = init.len;
6678 try coff.addReloc(6645 try coff.addReloc(
6679 si,6646 si,
...@@ -6736,7 +6703,7 @@ fn flushSpecialSymbol(coff: *Coff, pending: SpecialSymbol) !SpecialSymbol {...@@ -6736,7 +6703,7 @@ fn flushSpecialSymbol(coff: *Coff, pending: SpecialSymbol) !SpecialSymbol {
6736 try coff.symbols.ensureUnusedCapacity(gpa, 1);6703 try coff.symbols.ensureUnusedCapacity(gpa, 1);
6737 const optional_hdr_si = coff.addSymbolAssumeCapacity();6704 const optional_hdr_si = coff.addSymbolAssumeCapacity();
6738 const optional_hdr_sym = optional_hdr_si.get(coff);6705 const optional_hdr_sym = optional_hdr_si.get(coff);
6739 optional_hdr_sym.ni = Node.known.optional_header;6706 optional_hdr_sym.ni = .wrap(Node.known.optional_header);
6740 assert(optional_hdr_sym.loc_relocs == .none);6707 assert(optional_hdr_sym.loc_relocs == .none);
6741 optional_hdr_sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));6708 optional_hdr_sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
67426709
...@@ -6783,7 +6750,7 @@ fn flushSpecialSymbol(coff: *Coff, pending: SpecialSymbol) !SpecialSymbol {...@@ -6783,7 +6750,7 @@ fn flushSpecialSymbol(coff: *Coff, pending: SpecialSymbol) !SpecialSymbol {
6783 try coff.symbols.ensureUnusedCapacity(gpa, 1);6750 try coff.symbols.ensureUnusedCapacity(gpa, 1);
6784 const data_dir_si = coff.addSymbolAssumeCapacity();6751 const data_dir_si = coff.addSymbolAssumeCapacity();
6785 const data_dir_sym = data_dir_si.get(coff);6752 const data_dir_sym = data_dir_si.get(coff);
6786 data_dir_sym.ni = Node.known.data_directories;6753 data_dir_sym.ni = .wrap(Node.known.data_directories);
6787 assert(data_dir_sym.loc_relocs == .none);6754 assert(data_dir_sym.loc_relocs == .none);
6788 data_dir_sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));6755 data_dir_sym.loc_relocs = @fromBackingInt(@intCast(coff.relocs.items.len));
67896756
...@@ -6821,12 +6788,12 @@ fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {...@@ -6821,12 +6788,12 @@ fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
6821 .code => .text,6788 .code => .text,
6822 .const_data => .rdata,6789 .const_data => .rdata,
6823 };6790 };
6824 const ni = try coff.mf.addLastChildNode(gpa, sec_si.node(coff), .{ .moved = true });6791 const ni = try sec_si.node(coff).addFloatingChild(&coff.mf, gpa, .{ .moved = true });
6825 coff.nodes.appendAssumeCapacity(switch (lazy.kind) {6792 coff.nodes.appendAssumeCapacity(switch (lazy.kind) {
6826 .code => .{ .lazy_code = @fromBackingInt(@intCast(lmr.index)) },6793 .code => .{ .lazy_code = @fromBackingInt(@intCast(lmr.index)) },
6827 .const_data => .{ .lazy_const_data = @fromBackingInt(@intCast(lmr.index)) },6794 .const_data => .{ .lazy_const_data = @fromBackingInt(@intCast(lmr.index)) },
6828 });6795 });
6829 sym.ni = ni;6796 sym.ni = .wrap(ni);
6830 sym.section_number = sec_si.get(coff).section_number;6797 sym.section_number = sec_si.get(coff).section_number;
6831 },6798 },
6832 else => si.deleteLocationRelocs(coff),6799 else => si.deleteLocationRelocs(coff),
...@@ -6836,7 +6803,7 @@ fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {...@@ -6836,7 +6803,7 @@ fn flushLazy(coff: *Coff, pt: Zcu.PerThread, lmr: Node.LazyMapRef) !void {
6836 if (!isImage(coff) and sym.target_relocs != .none)6803 if (!isImage(coff) and sym.target_relocs != .none)
6837 try coff.pendingSymbolTableEntry(si);6804 try coff.pendingSymbolTableEntry(si);
68386805
6839 break :ni sym.ni;6806 break :ni sym.ni.unwrap().?;
6840 };6807 };
68416808
6842 var required_alignment: InternPool.Alignment = .none;6809 var required_alignment: InternPool.Alignment = .none;
...@@ -6914,7 +6881,7 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -6914,7 +6881,7 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
6914 const flags = coff.targetLoad(&sym.section_number.header(coff).flags);6881 const flags = coff.targetLoad(&sym.section_number.header(coff).flags);
6915 if (!flags.CNT_UNINITIALIZED_DATA) {6882 if (!flags.CNT_UNINITIALIZED_DATA) {
6916 const file_offset = if (isArchive(coff))6883 const file_offset = if (isArchive(coff))
6917 sym.ni.location(&coff.mf).resolve(&coff.mf)[0]6884 sym.ni.unwrap().?.location(&coff.mf).resolve(&coff.mf)[0]
6918 else6885 else
6919 ni.fileLocation(&coff.mf, false).offset;6886 ni.fileLocation(&coff.mf, false).offset;
69206887
...@@ -6927,7 +6894,7 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -6927,7 +6894,7 @@ fn flushMoved(coff: *Coff, ni: MappedFile.Node.Index) !void {
6927 .input_section => |isi| {6894 .input_section => |isi| {
6928 try isi.symbol(coff).flushMoved(coff);6895 try isi.symbol(coff).flushMoved(coff);
6929 for (coff.input_symbols.items[@backingInt(isi.firstSymbol(coff))..]) |input_symbol| {6896 for (coff.input_symbols.items[@backingInt(isi.firstSymbol(coff))..]) |input_symbol| {
6930 if (input_symbol.si.get(coff).ni != ni) break;6897 if (input_symbol.si.get(coff).ni != ni.toOptional()) break;
6931 try input_symbol.si.flushMoved(coff);6898 try input_symbol.si.flushMoved(coff);
6932 }6899 }
6933 },6900 },
...@@ -7062,7 +7029,7 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -7062,7 +7029,7 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
7062 if (coff.isArchive() and coff.members.items.len > 0) {7029 if (coff.isArchive() and coff.members.items.len > 0) {
7063 const last_member = coff.members.items[coff.members.items.len - 1];7030 const last_member = coff.members.items[coff.members.items.len - 1];
7064 // See .archive_member branch for reasoning7031 // See .archive_member branch for reasoning
7065 assert(Node.known.file.reverseChildren(&coff.mf).ni == last_member.content_ni);7032 assert(Node.known.file.last(&coff.mf).unwrap().? == last_member.content_ni);
7066 try coff.flushResized(last_member.content_ni);7033 try coff.flushResized(last_member.content_ni);
7067 }7034 }
7068 },7035 },
...@@ -7090,19 +7057,15 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {...@@ -7090,19 +7057,15 @@ fn flushResized(coff: *Coff, ni: MappedFile.Node.Index) !void {
7090 => unreachable,7057 => unreachable,
7091 .archive_member => |mi| {7058 .archive_member => |mi| {
7092 const content_ni = mi.get(coff).content_ni;7059 const content_ni = mi.get(coff).content_ni;
7093 const next_ni = content_ni.next(&coff.mf);
7094 const content_offset, _ = content_ni.location(&coff.mf).resolve(&coff.mf);7060 const content_offset, _ = content_ni.location(&coff.mf).resolve(&coff.mf);
7095 const next_offset = switch (next_ni) {7061 const next_offset = if (content_ni.next(&coff.mf).unwrap()) |next_ni| offset: {
7096 .none => offset: {7062 assert(coff.getNode(next_ni) == .archive_member_header);
7097 assert(content_ni.parent(&coff.mf) == Node.known.file);7063 break :offset next_ni.location(&coff.mf).resolve(&coff.mf)[0];
7098 // This must take into account the final file size. If there are trailing7064 } else offset: {
7099 // bytes, they will be expected to contain another valid member header7065 assert(content_ni.parent(&coff.mf) == Node.known.file.toOptional());
7100 break :offset coff.mf.memory_map.memory.len;7066 // This must take into account the final file size. If there are trailing
7101 },7067 // bytes, they will be expected to contain another valid member header
7102 else => offset: {7068 break :offset coff.mf.memory_map.memory.len;
7103 assert(coff.getNode(next_ni) == .archive_member_header);
7104 break :offset next_ni.location(&coff.mf).resolve(&coff.mf)[0];
7105 },
7106 };7069 };
71077070
7108 // Not inserting IMAGE_ARCHIVE_PAD `\n` byte here, because we are expanding to full size7071 // Not inserting IMAGE_ARCHIVE_PAD `\n` byte here, because we are expanding to full size
...@@ -7356,7 +7319,7 @@ fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void {...@@ -7356,7 +7319,7 @@ fn virtualSlide(coff: *Coff, start_section_index: usize, start_rva: u32) !void {
7356 const section_sym = section.si.get(coff);7319 const section_sym = section.si.get(coff);
7357 section_sym.rva = rva;7320 section_sym.rva = rva;
7358 coff.targetStore(&header.virtual_address, rva);7321 coff.targetStore(&header.virtual_address, rva);
7359 try section_sym.ni.childrenMoved(coff.base.comp.gpa, &coff.mf);7322 try section_sym.ni.unwrap().?.childrenMoved(coff.base.comp.gpa, &coff.mf);
7360 rva += coff.targetLoad(&header.virtual_size);7323 rva += coff.targetLoad(&header.virtual_size);
7361 }7324 }
7362 switch (coff.optionalHeaderPtr()) {7325 switch (coff.optionalHeaderPtr()) {
...@@ -7430,7 +7393,7 @@ fn updateExportInner(...@@ -7430,7 +7393,7 @@ fn updateExportInner(
7430 // TODO: add an errMsg if this conflicts with an existing symbol7393 // TODO: add an errMsg if this conflicts with an existing symbol
7431 const export_si = try coff.globalSymbol(.{ .name = name });7394 const export_si = try coff.globalSymbol(.{ .name = name });
7432 const export_sym = export_si.get(coff);7395 const export_sym = export_si.get(coff);
7433 export_sym.ni = exported_ni;7396 export_sym.ni = .wrap(exported_ni);
7434 export_sym.rva = exported_sym.rva;7397 export_sym.rva = exported_sym.rva;
7435 export_sym.section_number = exported_sym.section_number;7398 export_sym.section_number = exported_sym.section_number;
7436 if (@"export".opts.linkage == .weak and !coff.isImage()) {7399 if (@"export".opts.linkage == .weak and !coff.isImage()) {
...@@ -7481,7 +7444,7 @@ fn updateExportInner(...@@ -7481,7 +7444,7 @@ fn updateExportInner(
7481 if (new_name_table_size > std.math.maxInt(@FieldType(ExportTable.Entry, "name_index")))7444 if (new_name_table_size > std.math.maxInt(@FieldType(ExportTable.Entry, "name_index")))
7482 return coff.base.comp.link_diags.fail("exports name table limit reached", .{});7445 return coff.base.comp.link_diags.fail("exports name table limit reached", .{});
74837446
7484 try coff.export_table.name_table_ni.resize(&coff.mf, gpa, new_name_table_size);7447 try coff.export_table.name_table_ni.resizeLeaf(&coff.mf, gpa, new_name_table_size);
74857448
7486 const name_table_slice = coff.export_table.name_table_ni.slice(&coff.mf);7449 const name_table_slice = coff.export_table.name_table_ni.slice(&coff.mf);
7487 @memcpy(name_table_slice[name_index..][0 .. name.len + 1], name[0 .. name.len + 1]);7450 @memcpy(name_table_slice[name_index..][0 .. name.len + 1], name[0 .. name.len + 1]);
...@@ -7504,19 +7467,19 @@ fn updateExportInner(...@@ -7504,19 +7467,19 @@ fn updateExportInner(
75047467
7505 // TODO: These should all be resized ahead of time to fit all exports7468 // TODO: These should all be resized ahead of time to fit all exports
7506 // after https://github.com/ziglang/zig/issues/236167469 // after https://github.com/ziglang/zig/issues/23616
7507 try coff.export_table.export_address_table_si.node(coff).resize(7470 try coff.export_table.export_address_table_si.node(coff).resizeLeaf(
7508 &coff.mf,7471 &coff.mf,
7509 gpa,7472 gpa,
7510 export_count * @sizeOf(std.coff.ExportAddressTableEntry),7473 export_count * @sizeOf(std.coff.ExportAddressTableEntry),
7511 );7474 );
75127475
7513 try coff.export_table.name_pointer_table_ni.resize(7476 try coff.export_table.name_pointer_table_ni.resizeLeaf(
7514 &coff.mf,7477 &coff.mf,
7515 gpa,7478 gpa,
7516 export_count * @sizeOf(std.coff.ExportNamePointerTableEntry),7479 export_count * @sizeOf(std.coff.ExportNamePointerTableEntry),
7517 );7480 );
75187481
7519 try coff.export_table.ordinal_table_ni.resize(7482 try coff.export_table.ordinal_table_ni.resizeLeaf(
7520 &coff.mf,7483 &coff.mf,
7521 gpa,7484 gpa,
7522 export_count * @sizeOf(std.coff.ExportOrdinalTableEntry),7485 export_count * @sizeOf(std.coff.ExportOrdinalTableEntry),
...@@ -7599,14 +7562,13 @@ fn printSymbol(...@@ -7599,14 +7562,13 @@ fn printSymbol(
7599 si: Symbol.Index,7562 si: Symbol.Index,
7600) !void {7563) !void {
7601 const sym = si.get(coff);7564 const sym = si.get(coff);
7602 const node = coff.getNode(sym.ni);7565 try w.print("{d:0>6}@{d:0>2} {x:08} {s} {s} {s} n{d:0>8}+{x:08}:{s: <26} | {x:08} ", .{
7603 try w.print("{d:0>6}@{d:0>2} {x:08} {s} {s} {s} n{d:0>8}+{x:08}:{t: <26} | {x:08} ", .{
7604 si,7566 si,
7605 sym.section_number,7567 sym.section_number,
7606 if (sym.flags.extra_tag == .size)7568 if (sym.flags.extra_tag == .size)
7607 @as(u64, sym.extra.size)7569 @as(u64, sym.extra.size)
7608 else if (sym.ni != .none)7570 else if (sym.ni.unwrap()) |ni|
7609 sym.ni.location(&coff.mf).resolve(&coff.mf)[1]7571 ni.location(&coff.mf).resolve(&coff.mf)[1]
7610 else7572 else
7611 0,7573 0,
7612 switch (sym.flags.value_tag) {7574 switch (sym.flags.value_tag) {
...@@ -7627,7 +7589,7 @@ fn printSymbol(...@@ -7627,7 +7589,7 @@ fn printSymbol(
7627 },7589 },
7628 sym.ni,7590 sym.ni,
7629 if (sym.flags.value_tag == .node_offset) sym.value.node_offset else 0,7591 if (sym.flags.value_tag == .node_offset) sym.value.node_offset else 0,
7630 node,7592 if (sym.ni.unwrap()) |ni| @tagName(coff.getNode(ni)) else "",
7631 sym.rva,7593 sym.rva,
7632 });7594 });
76337595
...@@ -7635,7 +7597,7 @@ fn printSymbol(...@@ -7635,7 +7597,7 @@ fn printSymbol(
7635 try w.print("G {f}\n", .{fmtGlobalName(coff, sym.gmi)});7597 try w.print("G {f}\n", .{fmtGlobalName(coff, sym.gmi)});
7636 } else {7598 } else {
7637 try w.writeAll("| ");7599 try w.writeAll("| ");
7638 try coff.printNodeName(w, tid, node);7600 try coff.printNodeName(w, tid, coff.getNode(sym.ni.unwrap().?));
7639 if (sym.flags.extra_tag == .isli)7601 if (sym.flags.extra_tag == .isli)
7640 try w.print(" | {s}", .{sym.extra.isli.name(coff).toSlice(coff)});7602 try w.print(" | {s}", .{sym.extra.isli.name(coff).toSlice(coff)});
7641 try w.writeByte('\n');7603 try w.writeByte('\n');
...@@ -7672,7 +7634,7 @@ fn printNodeName(...@@ -7672,7 +7634,7 @@ fn printNodeName(
7672 try w.print("({f}{f}, {s}", .{7634 try w.print("({f}{f}, {s}", .{
7673 ioi.path(coff).fmtEscapeString(),7635 ioi.path(coff).fmtEscapeString(),
7674 fmtMemberNameString(ioi.memberName(coff)),7636 fmtMemberNameString(ioi.memberName(coff)),
7675 coff.getNode(is.si.node(coff).parent(&coff.mf)).object_section.name(coff).toSlice(coff),7637 coff.getNode(is.si.node(coff).parent(&coff.mf).unwrap().?).object_section.name(coff).toSlice(coff),
7676 });7638 });
7677 if (is.comdat_si != .null) {7639 if (is.comdat_si != .null) {
7678 const comdat_sym = is.comdat_si.get(coff);7640 const comdat_sym = is.comdat_si.get(coff);
...@@ -7748,41 +7710,42 @@ pub fn printNode(...@@ -7748,41 +7710,42 @@ pub fn printNode(
7748 {7710 {
7749 const mf_node = &coff.mf.nodes.items[@backingInt(ni)];7711 const mf_node = &coff.mf.nodes.items[@backingInt(ni)];
7750 const off, const size = mf_node.location().resolve(&coff.mf);7712 const off, const size = mf_node.location().resolve(&coff.mf);
7751 try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x}{s}{s}{s}{s}\n", .{7713 try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x} {t}{s}{s}{s}\n", .{
7752 @backingInt(ni),7714 @backingInt(ni),
7753 off,7715 off,
7754 size,7716 size,
7755 mf_node.flags.alignment.toByteUnits(),7717 mf_node.flags.alignment.toByteUnits(),
7756 if (mf_node.flags.fixed) " fixed" else "",7718 mf_node.flags.position,
7757 if (mf_node.flags.moved) " moved" else "",7719 if (mf_node.flags.moved) " moved" else "",
7758 if (mf_node.flags.resized) " resized" else "",7720 if (mf_node.flags.resized) " resized" else "",
7759 if (mf_node.flags.has_content) " has_content" else "",7721 if (mf_node.flags.has_content) " has_content" else "",
7760 });7722 });
7761 }7723 }
7762 var leaf = true;7724 if (ni.first(&coff.mf).unwrap()) |first_ni| {
7763 var child_it = ni.children(&coff.mf);7725 // non-leaf, just print children
7764 while (child_it.next()) |child_ni| {7726 var child_ni = first_ni;
7765 leaf = false;7727 while (true) {
7766 try coff.printNode(tid, w, child_ni, indent + 1);7728 try coff.printNode(tid, w, child_ni, indent + 1);
7767 }7729 child_ni = child_ni.next(&coff.mf).unwrap() orelse break;
7768 if (leaf) {
7769 const file_loc = ni.fileLocation(&coff.mf, false);
7770 if (file_loc.size == 0) return;
7771 var address = file_loc.offset;
7772 const line_len = 0x10;
7773 var line_it = std.mem.window(
7774 u8,
7775 coff.mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)],
7776 line_len,
7777 line_len,
7778 );
7779 while (line_it.next()) |line_bytes| : (address += line_len) {
7780 try w.splatByteAll(' ', indent + 1);
7781 try w.print("{x:0>8} ", .{address});
7782 for (line_bytes) |byte| try w.print("{x:0>2} ", .{byte});
7783 try w.splatByteAll(' ', 3 * (line_len - line_bytes.len) + 1);
7784 for (line_bytes) |byte| try w.writeByte(if (std.ascii.isPrint(byte)) byte else '.');
7785 try w.writeByte('\n');
7786 }7730 }
7731 return;
7732 }
7733 const file_loc = ni.fileLocation(&coff.mf, false);
7734 if (file_loc.size == 0) return;
7735 var address = file_loc.offset;
7736 const line_len = 0x10;
7737 var line_it = std.mem.window(
7738 u8,
7739 coff.mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)],
7740 line_len,
7741 line_len,
7742 );
7743 while (line_it.next()) |line_bytes| : (address += line_len) {
7744 try w.splatByteAll(' ', indent + 1);
7745 try w.print("{x:0>8} ", .{address});
7746 for (line_bytes) |byte| try w.print("{x:0>2} ", .{byte});
7747 try w.splatByteAll(' ', 3 * (line_len - line_bytes.len) + 1);
7748 for (line_bytes) |byte| try w.writeByte(if (std.ascii.isPrint(byte)) byte else '.');
7749 try w.writeByte('\n');
7787 }7750 }
7788}7751}
src/link/Elf2.zig+292-285
...@@ -18,14 +18,16 @@ const tracy = @import("../tracy.zig");...@@ -18,14 +18,16 @@ const tracy = @import("../tracy.zig");
18const Type = @import("../Type.zig");18const Type = @import("../Type.zig");
19const Value = @import("../Value.zig");19const Value = @import("../Value.zig");
20const Zcu = @import("../Zcu.zig");20const Zcu = @import("../Zcu.zig");
21const Alignment = MappedFile.Alignment;
2122
22base: link.File,23base: link.File,
23options: link.File.OpenOptions,24options: link.File.OpenOptions,
24mf: MappedFile,25mf: MappedFile,
25ni: Node.Known,26ni: Node.Known,
26nodes: std.MultiArrayList(Node),27nodes: std.MultiArrayList(Node),
28/// Does not contain an item for `SHN_UNDEF`.
27shdrs: std.ArrayList(Section),29shdrs: std.ArrayList(Section),
28phdrs: std.ArrayList(MappedFile.Node.Index),30phdrs: std.ArrayList(MappedFile.Node.Index.Optional),
29shndx: struct {31shndx: struct {
30 got: Section.Index,32 got: Section.Index,
31 /// Always `.UNDEF` on some targets (e.g. SPARC).33 /// Always `.UNDEF` on some targets (e.g. SPARC).
...@@ -99,7 +101,7 @@ dso_globals: std.array_hash_map.Auto(String(.strtab), struct {...@@ -99,7 +101,7 @@ dso_globals: std.array_hash_map.Auto(String(.strtab), struct {
99 /// the section containing the symbol, and the symbol's offset within the section. I know this101 /// the section containing the symbol, and the symbol's offset within the section. I know this
100 /// sounds like a terrible hack, but it is *genuinely* how you're supposed to do this. Copy102 /// sounds like a terrible hack, but it is *genuinely* how you're supposed to do this. Copy
101 /// relocations suck.103 /// relocations suck.
102 alignment: std.mem.Alignment,104 alignment: Alignment,
103}),105}),
104shstrtab: StringTable,106shstrtab: StringTable,
105strtab: StringTable,107strtab: StringTable,
...@@ -175,7 +177,7 @@ symbol_relocs: std.ArrayList(SymbolReloc),...@@ -175,7 +177,7 @@ symbol_relocs: std.ArrayList(SymbolReloc),
175got_relocs: std.ArrayList(GotReloc),177got_relocs: std.ArrayList(GotReloc),
176/// Set of relocations which must be re-applied if the size of the TLS segment changes.178/// Set of relocations which must be re-applied if the size of the TLS segment changes.
177tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),179tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),
178/// Index matches the index into `shdrs`.180/// Index matches the index into `shdrs`. Like `shdrs`, this map excludes `SHN_UNDEF`.
179section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),181section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),
180/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation182/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation
181/// entries which target that symbol must be updated to reference the correct symbol index.183/// entries which target that symbol must be updated to reference the correct symbol index.
...@@ -201,6 +203,8 @@ const Node = union(enum) {...@@ -201,6 +203,8 @@ const Node = union(enum) {
201 archive,203 archive,
202 /// This includes the archive magic and long file member.204 /// This includes the archive magic and long file member.
203 archive_header,205 archive_header,
206 /// This is a footer of the `.elf` node, and contains the next archive entry's file header.
207 archive_elf_footer,
204 elf,208 elf,
205 ehdr,209 ehdr,
206 shdr,210 shdr,
...@@ -339,8 +343,6 @@ const Node = union(enum) {...@@ -339,8 +343,6 @@ const Node = union(enum) {
339 };343 };
340344
341 pub const Known = struct {345 pub const Known = struct {
342 archive: MappedFile.Node.Index,
343 archive_header: MappedFile.Node.Index,
344 elf: MappedFile.Node.Index,346 elf: MappedFile.Node.Index,
345 ehdr: MappedFile.Node.Index,347 ehdr: MappedFile.Node.Index,
346 shdr: MappedFile.Node.Index,348 shdr: MappedFile.Node.Index,
...@@ -349,7 +351,7 @@ const Node = union(enum) {...@@ -349,7 +351,7 @@ const Node = union(enum) {
349 text: MappedFile.Node.Index,351 text: MappedFile.Node.Index,
350 data: MappedFile.Node.Index,352 data: MappedFile.Node.Index,
351 data_rel_ro: MappedFile.Node.Index,353 data_rel_ro: MappedFile.Node.Index,
352 tls: MappedFile.Node.Index,354 tls: MappedFile.Node.Index.Optional,
353 };355 };
354356
355 comptime {357 comptime {
...@@ -505,7 +507,7 @@ const Section = struct {...@@ -505,7 +507,7 @@ const Section = struct {
505 }507 }
506508
507 fn get(s: Index, elf: *Elf) *Section {509 fn get(s: Index, elf: *Elf) *Section {
508 return &elf.shdrs.items[@backingInt(s)];510 return &elf.shdrs.items[@backingInt(s) - 1]; // overflow means you tried to get the `.UNDEF` section
509 }511 }
510512
511 fn name(s: Index, elf: *Elf) String(.shstrtab) {513 fn name(s: Index, elf: *Elf) String(.shstrtab) {
...@@ -539,7 +541,7 @@ const Section = struct {...@@ -539,7 +541,7 @@ const Section = struct {
539 }541 }
540 }542 }
541543
542 fn ensureAligned(shndx: Index, elf: *Elf, min_align: std.mem.Alignment) Error!void {544 fn ensureAligned(shndx: Index, elf: *Elf, min_align: Alignment) Error!void {
543 switch (elf.shdrPtr(shndx)) {545 switch (elf.shdrPtr(shndx)) {
544 inline else => |shdr| {546 inline else => |shdr| {
545 if (elf.targetLoad(&shdr.addralign) >= min_align.toByteUnits()) {547 if (elf.targetLoad(&shdr.addralign) >= min_align.toByteUnits()) {
...@@ -550,9 +552,9 @@ const Section = struct {...@@ -550,9 +552,9 @@ const Section = struct {
550 }552 }
551 const ni = shndx.get(elf).ni;553 const ni = shndx.get(elf).ni;
552 if (min_align.compare(.gt, ni.alignment(&elf.mf))) {554 if (min_align.compare(.gt, ni.alignment(&elf.mf))) {
553 try ni.realign(&elf.mf, elf.base.comp.gpa, min_align, .{});555 try ni.realign(&elf.mf, elf.base.comp.gpa, min_align);
554 }556 }
555 switch (elf.getNode(ni.parent(&elf.mf))) {557 switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) {
556 .elf => {},558 .elf => {},
557 .segment => |phndx| try elf.ensureSegmentAligned(phndx, min_align),559 .segment => |phndx| try elf.ensureSegmentAligned(phndx, min_align),
558 else => unreachable,560 else => unreachable,
...@@ -583,7 +585,7 @@ const Section = struct {...@@ -583,7 +585,7 @@ const Section = struct {
583 break :need_size cur_size + need_additional * ent_size;585 break :need_size cur_size + need_additional * ent_size;
584 },586 },
585 };587 };
586 try elf.ensureNodeSize(node, need_size);588 try node.ensureMinimumSize(&elf.mf, elf.base.comp.gpa, need_size);
587 }589 }
588590
589 /// Asserts that `rela_shndx` is a `SHT_RELA` section and deletes the `ElfN.Rela` entry at591 /// Asserts that `rela_shndx` is a `SHT_RELA` section and deletes the `ElfN.Rela` entry at
...@@ -818,7 +820,7 @@ const GotReloc = struct {...@@ -818,7 +820,7 @@ const GotReloc = struct {
818 /// * A section820 /// * A section
819 /// * A NAV, UAV, or lazy code/data821 /// * A NAV, UAV, or lazy code/data
820 /// * `.none`, if this relocation was deleted (in which case it should be ignored)822 /// * `.none`, if this relocation was deleted (in which case it should be ignored)
821 node: MappedFile.Node.Index,823 node: MappedFile.Node.Index.Optional,
822 /// The offset of the relocation inside of `node`.824 /// The offset of the relocation inside of `node`.
823 offset: u64,825 offset: u64,
824 target: GotKey,826 target: GotKey,
...@@ -942,8 +944,10 @@ const GotReloc = struct {...@@ -942,8 +944,10 @@ const GotReloc = struct {
942944
943 fn apply(reloc: *GotReloc, elf: *Elf) void {945 fn apply(reloc: *GotReloc, elf: *Elf) void {
944 assert(elf.ehdrType() != .REL);946 assert(elf.ehdrType() != .REL);
945 if (reloc.node == .none) return; // deleted947 const node = reloc.node.unwrap() orelse {
946 if (reloc.node.hasMoved(&elf.mf) or elf.shndx.got.get(elf).ni.hasMoved(&elf.mf)) {948 return; // deleted
949 };
950 if (node.hasMoved(&elf.mf) or elf.shndx.got.get(elf).ni.hasMoved(&elf.mf)) {
947 // There's no point applying the relocation now, because it will be re-applied by951 // There's no point applying the relocation now, because it will be re-applied by
948 // `flushMoved` at some point anyway.952 // `flushMoved` at some point anyway.
949 return;953 return;
...@@ -968,8 +972,9 @@ const GotReloc = struct {...@@ -968,8 +972,9 @@ const GotReloc = struct {
968 }972 }
969 }973 }
970 fn applyInner(reloc: *const GotReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void {974 fn applyInner(reloc: *const GotReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void {
971 const dest_vaddr = elf.getNodeVAddr(reloc.node) + reloc.offset;975 const node = reloc.node.unwrap().?;
972 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];976 const dest_vaddr = elf.getNodeVAddr(node) + reloc.offset;
977 const dest_slice = node.slice(&elf.mf)[@intCast(reloc.offset)..];
973978
974 const got_vaddr = elf.shndx.got.vaddr(elf);979 const got_vaddr = elf.shndx.got.vaddr(elf);
975 const got_index: u64 = elf.got.getIndex(reloc.target).?;980 const got_index: u64 = elf.got.getIndex(reloc.target).?;
...@@ -1587,7 +1592,7 @@ const SymbolReloc = struct {...@@ -1587,7 +1592,7 @@ const SymbolReloc = struct {
1587 }1592 }
1588 },1593 },
1589 .sparc_le_hix22 => {1594 .sparc_le_hix22 => {
1590 const tls_phndx = elf.getNode(elf.ni.tls).segment;1595 const tls_phndx = elf.getNode(elf.ni.tls.unwrap().?).segment;
1591 const tls_size: u64 = switch (elf.phdrSlice()) {1596 const tls_size: u64 = switch (elf.phdrSlice()) {
1592 inline else => |phdr| tls_size: {1597 inline else => |phdr| tls_size: {
1593 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);1598 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
...@@ -1646,7 +1651,6 @@ const SymbolReloc = struct {...@@ -1646,7 +1651,6 @@ const SymbolReloc = struct {
16461651
1647 fn apply(reloc: *SymbolReloc, elf: *Elf) void {1652 fn apply(reloc: *SymbolReloc, elf: *Elf) void {
1648 assert(elf.ehdrType() != .REL);1653 assert(elf.ehdrType() != .REL);
1649 assert(reloc.node != .none);
1650 if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {1654 if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {
1651 // There's no point applying the relocation now, because it will be re-applied by1655 // There's no point applying the relocation now, because it will be re-applied by
1652 // `flushMoved` at some point anyway.1656 // `flushMoved` at some point anyway.
...@@ -1692,7 +1696,7 @@ const SymbolReloc = struct {...@@ -1692,7 +1696,7 @@ const SymbolReloc = struct {
1692 .I_original => |tls| tls.tcb_size +% reloc.target.value(elf) +% addend,1696 .I_original => |tls| tls.tcb_size +% reloc.target.value(elf) +% addend,
1693 .I_modified => |tls| 0 -% tls.tp_off +% reloc.target.value(elf) +% addend,1697 .I_modified => |tls| 0 -% tls.tp_off +% reloc.target.value(elf) +% addend,
1694 .II => {1698 .II => {
1695 const tls_phndx = elf.getNode(elf.ni.tls).segment;1699 const tls_phndx = elf.getNode(elf.ni.tls.unwrap().?).segment;
1696 const tls_size: u64 = switch (elf.phdrSlice()) {1700 const tls_size: u64 = switch (elf.phdrSlice()) {
1697 inline else => |phdr| tls_size: {1701 inline else => |phdr| tls_size: {
1698 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);1702 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
...@@ -1785,6 +1789,8 @@ const SymbolReloc = struct {...@@ -1785,6 +1789,8 @@ const SymbolReloc = struct {
1785};1789};
17861790
1787fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {1791fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {
1792 const gpa = elf.base.comp.gpa;
1793
1788 const min_buckets = max_dynsym_count / 2;1794 const min_buckets = max_dynsym_count / 2;
17891795
1790 const cur_dynsym_count: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {1796 const cur_dynsym_count: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
...@@ -1805,7 +1811,7 @@ fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {...@@ -1805,7 +1811,7 @@ fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {
1805 // We don't need to add any buckets, but we still need to make sure the section is large1811 // We don't need to add any buckets, but we still need to make sure the section is large
1806 // enough to fit `max_dynsym_count` chains.1812 // enough to fit `max_dynsym_count` chains.
1807 const need_size = @sizeOf(info.Header()) + (nbucket + max_dynsym_count) * 4;1813 const need_size = @sizeOf(info.Header()) + (nbucket + max_dynsym_count) * 4;
1808 try elf.ensureNodeSize(elf.shndx.hash.get(elf).ni, need_size);1814 try elf.shndx.hash.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_size);
1809 return;1815 return;
1810 }1816 }
1811 // We need more buckets, so we'll have to rebuild the hash table.1817 // We need more buckets, so we'll have to rebuild the hash table.
...@@ -1817,7 +1823,7 @@ fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {...@@ -1817,7 +1823,7 @@ fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {
18171823
1818 {1824 {
1819 const need_size = @sizeOf(info.Header()) + (new_nbucket + max_dynsym_count) * 4;1825 const need_size = @sizeOf(info.Header()) + (new_nbucket + max_dynsym_count) * 4;
1820 try elf.ensureNodeSize(elf.shndx.hash.get(elf).ni, need_size);1826 try elf.shndx.hash.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_size);
1821 }1827 }
18221828
1823 elf.mf.nodes_lock.lock();1829 elf.mf.nodes_lock.lock();
...@@ -1963,7 +1969,7 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe...@@ -1963,7 +1969,7 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe
1963 const need_node_size: u64 = switch (elf.shdrPtr(.symtab)) {1969 const need_node_size: u64 = switch (elf.shdrPtr(.symtab)) {
1964 inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym),1970 inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym),
1965 };1971 };
1966 try elf.ensureNodeSize(Section.Index.symtab.get(elf).ni, need_node_size);1972 try Section.Index.symtab.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_node_size);
1967 }1973 }
19681974
1969 switch (kind) {1975 switch (kind) {
...@@ -1986,7 +1992,7 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe...@@ -1986,7 +1992,7 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe
1986 const dynsym_cur_len: u32 = @intCast(@divExact(dynsym_cur_size, dynsym_ent_size));1992 const dynsym_cur_len: u32 = @intCast(@divExact(dynsym_cur_size, dynsym_ent_size));
19871993
1988 const dynsym_need_size: u64 = (dynsym_cur_len + len) * dynsym_ent_size;1994 const dynsym_need_size: u64 = (dynsym_cur_len + len) * dynsym_ent_size;
1989 try elf.ensureNodeSize(elf.shndx.dynsym.get(elf).ni, dynsym_need_size);1995 try elf.shndx.dynsym.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, dynsym_need_size);
19901996
1991 try elf.ensureDynsymHashCapacity(dynsym_cur_len + len);1997 try elf.ensureDynsymHashCapacity(dynsym_cur_len + len);
19921998
...@@ -2008,19 +2014,19 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void {...@@ -2008,19 +2014,19 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void {
2008 // Ensure the `.plt` section's node is big enough:2014 // Ensure the `.plt` section's node is big enough:
2009 {2015 {
2010 const need_size: usize = plt.entry_size * (1 + need_plt_count);2016 const need_size: usize = plt.entry_size * (1 + need_plt_count);
2011 try elf.ensureNodeSize(elf.shndx.plt.get(elf).ni, need_size);2017 try elf.shndx.plt.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_size);
2012 }2018 }
20132019
2014 // If there is a `.got.plt` section, ensure its node is big enough2020 // If there is a `.got.plt` section, ensure its node is big enough
2015 if (plt.got_plt) |got_plt| {2021 if (plt.got_plt) |got_plt| {
2016 const need_size: usize = elf.targetPtrSize() * (got_plt.header_entries + need_plt_count);2022 const need_size: usize = elf.targetPtrSize() * (got_plt.header_entries + need_plt_count);
2017 try elf.ensureNodeSize(elf.shndx.got_plt.get(elf).ni, need_size);2023 try elf.shndx.got_plt.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_size);
2018 }2024 }
20192025
2020 // If there is a `.plt.sec` section, ensure its node is big enough2026 // If there is a `.plt.sec` section, ensure its node is big enough
2021 if (plt.plt_sec) |plt_sec| {2027 if (plt.plt_sec) |plt_sec| {
2022 const need_size: usize = plt_sec.entry_size * need_plt_count;2028 const need_size: usize = plt_sec.entry_size * need_plt_count;
2023 try elf.ensureNodeSize(elf.shndx.plt_sec.get(elf).ni, need_size);2029 try elf.shndx.plt_sec.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_size);
2024 }2030 }
2025}2031}
2026/// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at2032/// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at
...@@ -2044,7 +2050,7 @@ fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {...@@ -2044,7 +2050,7 @@ fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {
2044}2050}
20452051
2046const AddLocalSymbolOptions = struct {2052const AddLocalSymbolOptions = struct {
2047 node: MappedFile.Node.Index,2053 node: MappedFile.Node.Index.Optional,
2048 name: String(.strtab),2054 name: String(.strtab),
2049 value: u64,2055 value: u64,
2050 size: u64,2056 size: u64,
...@@ -2126,7 +2132,7 @@ const AddGlobalSymbolOptions = struct {...@@ -2126,7 +2132,7 @@ const AddGlobalSymbolOptions = struct {
2126 }2132 }
2127 };2133 };
21282134
2129 node: MappedFile.Node.Index,2135 node: MappedFile.Node.Index.Optional,
2130 name: Name,2136 name: Name,
2131 lib_name: ?[]const u8 = null,2137 lib_name: ?[]const u8 = null,
2132 value: u64,2138 value: u64,
...@@ -2294,8 +2300,8 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -2294,8 +2300,8 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
2294 }2300 }
22952301
2296 const old_head: String(.strtab) = old_head: {2302 const old_head: String(.strtab) = old_head: {
2297 if (opts.node == .none) break :old_head .empty;2303 const node = opts.node.unwrap() orelse break :old_head .empty;
2298 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(opts.node);2304 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(node);
2299 const old_head: String(.strtab) = if (gop.found_existing) gop.value_ptr.* else .empty;2305 const old_head: String(.strtab) = if (gop.found_existing) gop.value_ptr.* else .empty;
2300 gop.value_ptr.* = opts.name.strtab;2306 gop.value_ptr.* = opts.name.strtab;
2301 break :old_head old_head;2307 break :old_head old_head;
...@@ -2363,7 +2369,7 @@ fn setGlobalSymbolValue(...@@ -2363,7 +2369,7 @@ fn setGlobalSymbolValue(
2363 global_name: String(.strtab),2369 global_name: String(.strtab),
2364 global_ptr: *Symbol.Global,2370 global_ptr: *Symbol.Global,
2365 new: struct {2371 new: struct {
2366 node: MappedFile.Node.Index,2372 node: MappedFile.Node.Index.Optional,
2367 value: u64,2373 value: u64,
2368 size: u64,2374 size: u64,
2369 type: std.elf.STT,2375 type: std.elf.STT,
...@@ -2371,18 +2377,17 @@ fn setGlobalSymbolValue(...@@ -2371,18 +2377,17 @@ fn setGlobalSymbolValue(
2371 },2377 },
2372) void {2378) void {
2373 assert(new.shndx != .UNDEF);2379 assert(new.shndx != .UNDEF);
2374 const old_node = global_ptr.symtab_index.ptr(elf).node;2380 if (global_ptr.symtab_index.ptr(elf).node.unwrap()) |old_node| {
2375 if (old_node != .none) {
2376 if (global_ptr.next_in_node != .empty) {2381 if (global_ptr.next_in_node != .empty) {
2377 const next = elf.globalByName(global_ptr.next_in_node).?;2382 const next = elf.globalByName(global_ptr.next_in_node).?;
2378 assert(next.prev_in_node == global_name);2383 assert(next.prev_in_node == global_name);
2379 assert(next.symtab_index.ptr(elf).node == old_node);2384 assert(next.symtab_index.ptr(elf).node.unwrap().? == old_node);
2380 next.prev_in_node = global_ptr.prev_in_node;2385 next.prev_in_node = global_ptr.prev_in_node;
2381 }2386 }
2382 if (global_ptr.prev_in_node != .empty) {2387 if (global_ptr.prev_in_node != .empty) {
2383 const prev = elf.globalByName(global_ptr.prev_in_node).?;2388 const prev = elf.globalByName(global_ptr.prev_in_node).?;
2384 assert(prev.next_in_node == global_name);2389 assert(prev.next_in_node == global_name);
2385 assert(prev.symtab_index.ptr(elf).node == old_node);2390 assert(prev.symtab_index.ptr(elf).node.unwrap().? == old_node);
2386 prev.next_in_node = global_ptr.next_in_node;2391 prev.next_in_node = global_ptr.next_in_node;
2387 } else {2392 } else {
2388 // We're the start of the linked list, so we need to change the head.2393 // We're the start of the linked list, so we need to change the head.
...@@ -2417,8 +2422,8 @@ fn setGlobalSymbolValue(...@@ -2417,8 +2422,8 @@ fn setGlobalSymbolValue(
2417 global_ptr.symtab_index.ptr(elf).node = new.node;2422 global_ptr.symtab_index.ptr(elf).node = new.node;
24182423
2419 const old_head: String(.strtab) = old_head: {2424 const old_head: String(.strtab) = old_head: {
2420 if (new.node == .none) break :old_head .empty;2425 const new_node = new.node.unwrap() orelse break :old_head .empty;
2421 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(new.node);2426 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(new_node);
2422 const old_head: String(.strtab) = if (gop.found_existing) gop.value_ptr.* else .empty;2427 const old_head: String(.strtab) = if (gop.found_existing) gop.value_ptr.* else .empty;
2423 gop.value_ptr.* = global_name;2428 gop.value_ptr.* = global_name;
2424 break :old_head old_head;2429 break :old_head old_head;
...@@ -2644,7 +2649,7 @@ const Symbol = struct {...@@ -2644,7 +2649,7 @@ const Symbol = struct {
2644 /// * A section (the symbol's value is some vaddr in that section)2649 /// * A section (the symbol's value is some vaddr in that section)
2645 /// * An input section (the symbol's value is some vaddr in that input section)2650 /// * An input section (the symbol's value is some vaddr in that input section)
2646 /// * A NAV, UAV, or lazy code/data (the symbol's value is exactly the vaddr of that node)2651 /// * A NAV, UAV, or lazy code/data (the symbol's value is exactly the vaddr of that node)
2647 node: MappedFile.Node.Index,2652 node: MappedFile.Node.Index.Optional,
26482653
2649 /// The head of a linked list of relocations targeting this symbol.2654 /// The head of a linked list of relocations targeting this symbol.
2650 first_target_reloc: SymbolReloc.Index,2655 first_target_reloc: SymbolReloc.Index,
...@@ -2852,8 +2857,7 @@ const Symbol = struct {...@@ -2852,8 +2857,7 @@ const Symbol = struct {
2852 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at2857 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at
2853 /// some point due to a call to `flushMoved`.2858 /// some point due to a call to `flushMoved`.
2854 fn hasMoved(s: Symbol.Id, elf: *Elf) bool {2859 fn hasMoved(s: Symbol.Id, elf: *Elf) bool {
2855 const node = s.index(elf).ptr(elf).node;2860 if (s.index(elf).ptr(elf).node.unwrap()) |node| {
2856 if (node != .none) {
2857 return node.hasMoved(&elf.mf);2861 return node.hasMoved(&elf.mf);
2858 }2862 }
2859 switch (s.unwrap()) {2863 switch (s.unwrap()) {
...@@ -2950,6 +2954,7 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {...@@ -2950,6 +2954,7 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {
2950 const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) {2954 const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) {
2951 .archive,2955 .archive,
2952 .archive_header,2956 .archive_header,
2957 .archive_elf_footer,
2953 .elf,2958 .elf,
2954 .ehdr,2959 .ehdr,
2955 .shdr,2960 .shdr,
...@@ -2989,7 +2994,7 @@ fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.Symbol...@@ -2989,7 +2994,7 @@ fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.Symbol
2989 .code => .{ .text, .FUNC },2994 .code => .{ .text, .FUNC },
2990 .const_data => .{ .rodata, .OBJECT },2995 .const_data => .{ .rodata, .OBJECT },
2991 };2996 };
2992 const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{});2997 const node = try shndx.get(elf).ni.addFloatingChild(&elf.mf, gpa, .{});
2993 var name_buf: [64]u8 = undefined;2998 var name_buf: [64]u8 = undefined;
2994 const name = std.fmt.bufPrint(2999 const name = std.fmt.bufPrint(
2995 &name_buf,3000 &name_buf,
...@@ -2998,7 +3003,7 @@ fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.Symbol...@@ -2998,7 +3003,7 @@ fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.Symbol
2998 ) catch unreachable;3003 ) catch unreachable;
2999 gop.value_ptr.* = .{3004 gop.value_ptr.* = .{
3000 .lsi = elf.addLocalSymbolAssumeCapacity(.{3005 .lsi = elf.addLocalSymbolAssumeCapacity(.{
3001 .node = node,3006 .node = .wrap(node),
3002 .name = try elf.string(.strtab, name),3007 .name = try elf.string(.strtab, name),
3003 .value = 0,3008 .value = 0,
3004 .size = 0,3009 .size = 0,
...@@ -3248,7 +3253,7 @@ const StringTable = struct {...@@ -3248,7 +3253,7 @@ const StringTable = struct {
3248 break :size .{ old_size, new_size };3253 break :size .{ old_size, new_size };
3249 },3254 },
3250 };3255 };
3251 try elf.ensureNodeSize(ni, new_size);3256 try ni.ensureMinimumSize(&elf.mf, gpa, new_size);
3252 const slice = ni.slice(&elf.mf)[old_size..];3257 const slice = ni.slice(&elf.mf)[old_size..];
3253 @memcpy(slice[0..key.len], key);3258 @memcpy(slice[0..key.len], key);
3254 slice[key.len] = 0;3259 slice[key.len] = 0;
...@@ -3349,16 +3354,14 @@ fn create(...@@ -3349,16 +3354,14 @@ fn create(
3349 .options = options,3354 .options = options,
3350 .mf = try .init(file, comp.gpa, io),3355 .mf = try .init(file, comp.gpa, io),
3351 .ni = .{3356 .ni = .{
3352 .archive = .root,3357 .elf = undefined,
3353 .archive_header = .none,3358 .ehdr = undefined,
3354 .elf = .root,3359 .shdr = undefined,
3355 .ehdr = .none,3360 .rodata = undefined,
3356 .shdr = .none,3361 .phdr = undefined,
3357 .rodata = .none,3362 .text = undefined,
3358 .phdr = .none,3363 .data = undefined,
3359 .text = .none,3364 .data_rel_ro = undefined,
3360 .data = .none,
3361 .data_rel_ro = .none,
3362 .tls = .none,3365 .tls = .none,
3363 },3366 },
3364 .nodes = .empty,3367 .nodes = .empty,
...@@ -3489,7 +3492,7 @@ fn initHeaders(...@@ -3489,7 +3492,7 @@ fn initHeaders(
3489 .EXEC => comp.config.link_mode == .dynamic,3492 .EXEC => comp.config.link_mode == .dynamic,
3490 .DYN => true,3493 .DYN => true,
3491 };3494 };
3492 const addr_align: std.mem.Alignment = switch (class) {3495 const addr_align: Alignment = switch (class) {
3493 .NONE, _ => unreachable,3496 .NONE, _ => unreachable,
3494 .@"32" => .@"4",3497 .@"32" => .@"4",
3495 .@"64" => .@"8",3498 .@"64" => .@"8",
...@@ -3503,7 +3506,7 @@ fn initHeaders(...@@ -3503,7 +3506,7 @@ fn initHeaders(
3503 //3506 //
3504 // It can be handy to temporarily set this to `.@"1"` when working on the linker, because it3507 // It can be handy to temporarily set this to `.@"1"` when working on the linker, because it
3505 // prevents alignment bugs from being hidden by your filesystem's block alignment.3508 // prevents alignment bugs from being hidden by your filesystem's block alignment.
3506 const node_block_align: std.mem.Alignment = elf.mf.flags.block_size;3509 const node_block_align: Alignment = elf.mf.flags.block_size;
35073510
3508 const plt: PltInfo = .fromMachine(machine);3511 const plt: PltInfo = .fromMachine(machine);
35093512
...@@ -3599,28 +3602,31 @@ fn initHeaders(...@@ -3599,28 +3602,31 @@ fn initHeaders(
3599 }, phnum };3602 }, phnum };
3600 };3603 };
36013604
3602 const expected_nodes_len = @as(usize, if (is_archive) 2 else 0) + // .archive, .archive_header3605 const expected_nodes_len = @as(usize, if (is_archive) 3 else 0) + // .archive, .archive_header, .archive_elf_footer
3603 3 + // `.file`, `.ehdr`, and `.shdr` nodes3606 3 + // `.elf`, `.ehdr`, and `.shdr` nodes
3604 (shnum - 1) + // -1 because the null shdr does not have a `.section` node3607 (shnum - 1) + // -1 because the SHN_UNDEF shdr does not have a `.section` node
3605 (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node3608 (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node
36063609
3607 try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);3610 try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
3608 try elf.shdrs.ensureTotalCapacity(gpa, shnum);3611 try elf.shdrs.ensureTotalCapacity(gpa, shnum - 1); // -1 to exclude SHN_UNDEF
3609 try elf.section_by_name.ensureUnusedCapacity(gpa, shnum);3612 try elf.section_by_name.ensureUnusedCapacity(gpa, shnum - 1); // -1 to exclude SHN_UNDEF
3610 try elf.phdrs.resize(gpa, phnum);3613 try elf.phdrs.resize(gpa, phnum);
3611 try elf.symtab.ensureTotalCapacity(gpa, 1);3614 try elf.symtab.ensureTotalCapacity(gpa, 1);
36123615
3613 if (is_archive) {3616 if (is_archive) {
3614 elf.nodes.appendAssumeCapacity(.archive);3617 elf.nodes.appendAssumeCapacity(.archive);
3615 elf.ni.archive_header = try elf.mf.addOnlyChildNode(gpa, elf.ni.archive, .{3618
3619 const archive_ni: MappedFile.Node.Index = .root;
3620
3621 const archive_header_ni = try archive_ni.addOnlyHeaderChild(&elf.mf, gpa, .{
3616 .size = std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr) * 2,3622 .size = std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr) * 2,
3617 .alignment = .@"2",3623 .alignment = .@"2",
3618 .fixed = true,
3619 .next_moved = true,3624 .next_moved = true,
3620 .bubbles_moved = false,3625 .bubbles_moved = false,
3621 .enable_next_moved = true,3626 .enable_next_moved = true,
3622 });3627 });
3623 const archive_header_slice = elf.ni.archive_header.slice(&elf.mf);3628 elf.nodes.appendAssumeCapacity(.archive_header);
3629 const archive_header_slice = archive_header_ni.slice(&elf.mf);
3624 @memcpy(archive_header_slice[0..std.elf.ARMAG.len], std.elf.ARMAG);3630 @memcpy(archive_header_slice[0..std.elf.ARMAG.len], std.elf.ARMAG);
3625 const strtab_ar_hdr: *std.elf.ar_hdr = @ptrCast(archive_header_slice[std.elf.ARMAG.len..]);3631 const strtab_ar_hdr: *std.elf.ar_hdr = @ptrCast(archive_header_slice[std.elf.ARMAG.len..]);
3626 strtab_ar_hdr.* = .{3632 strtab_ar_hdr.* = .{
...@@ -3633,15 +3639,23 @@ fn initHeaders(...@@ -3633,15 +3639,23 @@ fn initHeaders(
3633 .ar_fmag = std.elf.ARFMAG.*,3639 .ar_fmag = std.elf.ARFMAG.*,
3634 };3640 };
36353641
3636 elf.nodes.appendAssumeCapacity(.archive_header);3642 elf.ni.elf = try archive_ni.addFloatingChild(&elf.mf, gpa, .{
3637 elf.ni.elf = try elf.mf.addLastChildNode(gpa, elf.ni.archive, .{
3638 .alignment = node_block_align.max(.@"2"),3643 .alignment = node_block_align.max(.@"2"),
3639 .next_moved = true,3644 .next_moved = true,
3640 .bubbles_moved = false,3645 .bubbles_moved = false,
3641 .enable_next_moved = true,3646 .enable_next_moved = true,
3642 });3647 });
3648 elf.nodes.appendAssumeCapacity(.elf);
3649
3650 _ = try elf.ni.elf.addOnlyFooterChild(&elf.mf, gpa, .{
3651 .alignment = .@"2",
3652 .size = @sizeOf(std.elf.ar_hdr),
3653 });
3654 elf.nodes.appendAssumeCapacity(.archive_elf_footer);
3655 } else {
3656 elf.ni.elf = .root;
3657 elf.nodes.appendAssumeCapacity(.elf);
3643 }3658 }
3644 elf.nodes.appendAssumeCapacity(.elf);
36453659
3646 const entsize: struct { ph: u32, sh: u32 } = switch (class) {3660 const entsize: struct { ph: u32, sh: u32 } = switch (class) {
3647 .NONE, _ => unreachable,3661 .NONE, _ => unreachable,
...@@ -3655,19 +3669,18 @@ fn initHeaders(...@@ -3655,19 +3669,18 @@ fn initHeaders(
3655 // the rodata segment. Although to my knowledge neither ELF nor any ELF-based OS strictly3669 // the rodata segment. Although to my knowledge neither ELF nor any ELF-based OS strictly
3656 // requires this, it is highly conventional and therefore sometimes relied upon.3670 // requires this, it is highly conventional and therefore sometimes relied upon.
3657 if (@"type" != .REL) {3671 if (@"type" != .REL) {
3658 elf.ni.rodata = try elf.mf.addOnlyChildNode(gpa, elf.ni.elf, .{3672 // This node will contain the ehdr, which must be at the start of the ELF file, so this
3673 // node must itself be a header of the `.elf` node.
3674 elf.ni.rodata = try elf.ni.elf.addOnlyHeaderChild(&elf.mf, gpa, .{
3659 // Must be at least `addr_align` for `elf.ni.phdr` to be placed inside this node3675 // Must be at least `addr_align` for `elf.ni.phdr` to be placed inside this node
3660 .alignment = node_block_align.max(addr_align),3676 .alignment = node_block_align.max(addr_align),
3661 // This node will contain the ehdr, which must be at the start of the ELF file, so this
3662 // node must itself be fixed.
3663 .fixed = true,
3664 .moved = true,3677 .moved = true,
3665 .bubbles_moved = false,3678 .bubbles_moved = false,
3666 });3679 });
3667 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.rodata });3680 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.rodata });
3668 elf.phdrs.items[phndx.rodata] = elf.ni.rodata;3681 elf.phdrs.items[phndx.rodata] = .wrap(elf.ni.rodata);
36693682
3670 elf.ni.phdr = try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{3683 elf.ni.phdr = try elf.ni.rodata.addFloatingChild(&elf.mf, gpa, .{
3671 .size = @as(u64, phnum) * entsize.ph,3684 .size = @as(u64, phnum) * entsize.ph,
3672 .alignment = addr_align, // keep in sync with `elf.ni.rodata` alignment above3685 .alignment = addr_align, // keep in sync with `elf.ni.rodata` alignment above
3673 .moved = true,3686 .moved = true,
...@@ -3675,36 +3688,36 @@ fn initHeaders(...@@ -3675,36 +3688,36 @@ fn initHeaders(
3675 .bubbles_moved = false,3688 .bubbles_moved = false,
3676 });3689 });
3677 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.phdr });3690 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.phdr });
3678 elf.phdrs.items[phndx.phdr] = elf.ni.phdr;3691 elf.phdrs.items[phndx.phdr] = .wrap(elf.ni.phdr);
36793692
3680 elf.ni.text = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{3693 elf.ni.text = try elf.ni.elf.addFloatingChild(&elf.mf, gpa, .{
3681 .alignment = node_block_align,3694 .alignment = node_block_align,
3682 .moved = true,3695 .moved = true,
3683 .bubbles_moved = false,3696 .bubbles_moved = false,
3684 });3697 });
3685 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.text });3698 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.text });
3686 elf.phdrs.items[phndx.text] = elf.ni.text;3699 elf.phdrs.items[phndx.text] = .wrap(elf.ni.text);
36873700
3688 elf.ni.data = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{3701 elf.ni.data = try elf.ni.elf.addFloatingChild(&elf.mf, gpa, .{
3689 // Must be at least `addr_align` for `elf.ni.data_rel_ro` to be placed inside this node3702 // Must be at least `addr_align` for `elf.ni.data_rel_ro` to be placed inside this node
3690 .alignment = node_block_align.max(addr_align),3703 .alignment = node_block_align.max(addr_align),
3691 .moved = true,3704 .moved = true,
3692 .bubbles_moved = false,3705 .bubbles_moved = false,
3693 });3706 });
3694 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data });3707 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data });
3695 elf.phdrs.items[phndx.data] = elf.ni.data;3708 elf.phdrs.items[phndx.data] = .wrap(elf.ni.data);
36963709
3697 if (plt.got_plt == null) {3710 if (plt.got_plt == null) {
3698 const plt_ni = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{3711 const plt_ni = try elf.ni.elf.addFloatingChild(&elf.mf, gpa, .{
3699 .alignment = node_block_align,3712 .alignment = node_block_align,
3700 .moved = true,3713 .moved = true,
3701 .bubbles_moved = false,3714 .bubbles_moved = false,
3702 });3715 });
3703 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.plt });3716 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.plt });
3704 elf.phdrs.items[phndx.plt] = plt_ni;3717 elf.phdrs.items[phndx.plt] = .wrap(plt_ni);
3705 }3718 }
37063719
3707 elf.ni.data_rel_ro = try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{3720 elf.ni.data_rel_ro = try elf.ni.data.addFloatingChild(&elf.mf, gpa, .{
3708 // Must be at least `addr_align` for the `PT_DYNAMIC` node to be placed inside this one3721 // Must be at least `addr_align` for the `PT_DYNAMIC` node to be placed inside this one
3709 // later (if `have_dynamic_section`). Keep in sync with `elf.ni.data` alignment above.3722 // later (if `have_dynamic_section`). Keep in sync with `elf.ni.data` alignment above.
3710 .alignment = node_block_align.max(addr_align),3723 .alignment = node_block_align.max(addr_align),
...@@ -3712,19 +3725,27 @@ fn initHeaders(...@@ -3712,19 +3725,27 @@ fn initHeaders(
3712 .bubbles_moved = false,3725 .bubbles_moved = false,
3713 });3726 });
3714 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro });3727 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro });
3715 elf.phdrs.items[phndx.relro] = elf.ni.data_rel_ro;3728 elf.phdrs.items[phndx.relro] = .wrap(elf.ni.data_rel_ro);
37163729
3717 if (comp.config.any_non_single_threaded) {3730 if (comp.config.any_non_single_threaded) {
3718 elf.ni.tls = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{3731 elf.ni.tls = .wrap(try elf.ni.rodata.addFloatingChild(&elf.mf, gpa, .{
3719 .alignment = node_block_align,3732 .alignment = node_block_align,
3720 .moved = true,3733 .moved = true,
3721 .bubbles_moved = false,3734 .bubbles_moved = false,
3722 });3735 }));
3723 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.tls });3736 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.tls });
3724 elf.phdrs.items[phndx.tls] = elf.ni.tls;3737 elf.phdrs.items[phndx.tls] = elf.ni.tls;
3725 }3738 }
37263739
3727 elf.phdrs.items[phndx.gnu_stack] = .none;3740 elf.phdrs.items[phndx.gnu_stack] = .none;
3741 } else {
3742 elf.ni.rodata = elf.ni.elf;
3743 elf.ni.text = elf.ni.elf;
3744 elf.ni.data = elf.ni.elf;
3745 elf.ni.data_rel_ro = elf.ni.elf;
3746 if (comp.config.any_non_single_threaded) {
3747 elf.ni.tls = .wrap(elf.ni.elf);
3748 }
3728 }3749 }
37293750
3730 switch (class) {3751 switch (class) {
...@@ -3736,10 +3757,9 @@ fn initHeaders(...@@ -3736,10 +3757,9 @@ fn initHeaders(
3736 .REL => elf.ni.elf,3757 .REL => elf.ni.elf,
3737 .DYN, .EXEC => elf.ni.rodata,3758 .DYN, .EXEC => elf.ni.rodata,
3738 };3759 };
3739 elf.ni.ehdr = try elf.mf.addFirstChildNode(gpa, parent_ni, .{3760 elf.ni.ehdr = try parent_ni.addOnlyHeaderChild(&elf.mf, gpa, .{
3740 .size = @sizeOf(ElfN.Ehdr),3761 .size = @sizeOf(ElfN.Ehdr),
3741 .alignment = addr_align,3762 .alignment = addr_align,
3742 .fixed = true,
3743 });3763 });
3744 elf.nodes.appendAssumeCapacity(.ehdr);3764 elf.nodes.appendAssumeCapacity(.ehdr);
37453765
...@@ -3785,14 +3805,14 @@ fn initHeaders(...@@ -3785,14 +3805,14 @@ fn initHeaders(
3785 ehdr.phentsize = @sizeOf(ElfN.Phdr);3805 ehdr.phentsize = @sizeOf(ElfN.Phdr);
3786 ehdr.phnum = @min(phnum, std.elf.PN_XNUM);3806 ehdr.phnum = @min(phnum, std.elf.PN_XNUM);
3787 ehdr.shentsize = @sizeOf(ElfN.Shdr);3807 ehdr.shentsize = @sizeOf(ElfN.Shdr);
3788 ehdr.shnum = 1; // Only the null shdr initially---will be incremented by `addSection`3808 ehdr.shnum = 1; // Only the SHN_UNDEF shdr initially---will be incremented by `addSection`
3789 ehdr.shstrndx = std.elf.SHN_UNDEF;3809 ehdr.shstrndx = std.elf.SHN_UNDEF;
3790 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);3810 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
3791 },3811 },
3792 }3812 }
37933813
3794 elf.ni.shdr = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{3814 elf.ni.shdr = try elf.ni.elf.addFloatingChild(&elf.mf, gpa, .{
3795 .size = 1 * entsize.sh, // as above, only the null shdr initially3815 .size = node_block_align.forward(1 * entsize.sh), // as above, only the SHN_UNDEF initially
3796 .alignment = addr_align.max(node_block_align),3816 .alignment = addr_align.max(node_block_align),
3797 .moved = true,3817 .moved = true,
3798 .resized = true,3818 .resized = true,
...@@ -3916,7 +3936,7 @@ fn initHeaders(...@@ -3916,7 +3936,7 @@ fn initHeaders(
3916 };3936 };
3917 }3937 }
39183938
3919 if (comp.config.any_non_single_threaded) {3939 if (elf.ni.tls.unwrap()) |tls_segment_ni| {
3920 const ph_tls = &phdr[phndx.tls];3940 const ph_tls = &phdr[phndx.tls];
3921 ph_tls.* = .{3941 ph_tls.* = .{
3922 .type = .TLS,3942 .type = .TLS,
...@@ -3926,7 +3946,7 @@ fn initHeaders(...@@ -3926,7 +3946,7 @@ fn initHeaders(
3926 .filesz = 0,3946 .filesz = 0,
3927 .memsz = 0,3947 .memsz = 0,
3928 .flags = .{ .R = true },3948 .flags = .{ .R = true },
3929 .@"align" = @intCast(elf.ni.tls.alignment(&elf.mf).toByteUnits()),3949 .@"align" = @intCast(tls_segment_ni.alignment(&elf.mf).toByteUnits()),
3930 };3950 };
3931 }3951 }
39323952
...@@ -3987,7 +4007,6 @@ fn initHeaders(...@@ -3987,7 +4007,6 @@ fn initHeaders(
3987 .entsize = 0,4007 .entsize = 0,
3988 };4008 };
3989 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_undef);4009 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_undef);
3990 elf.shdrs.appendAssumeCapacity(.{ .lsi = .null, .ni = .none, .rela = .{ .shndx = .UNDEF } });
39914010
3992 elf.symtab.addOneAssumeCapacity().* = .{4011 elf.symtab.addOneAssumeCapacity().* = .{
3993 .node = .none,4012 .node = .none,
...@@ -4092,7 +4111,7 @@ fn initHeaders(...@@ -4092,7 +4111,7 @@ fn initHeaders(
4092 .node_align = node_block_align,4111 .node_align = node_block_align,
4093 });4112 });
4094 } else {4113 } else {
4095 elf.shndx.plt = try elf.addSection(elf.phdrs.items[phndx.plt], .{4114 elf.shndx.plt = try elf.addSection(elf.phdrs.items[phndx.plt].unwrap().?, .{
4096 .name = ".plt",4115 .name = ".plt",
4097 .type = .PROGBITS,4116 .type = .PROGBITS,
4098 .flags = .{ .ALLOC = true, .WRITE = true, .EXECINSTR = true },4117 .flags = .{ .ALLOC = true, .WRITE = true, .EXECINSTR = true },
...@@ -4108,14 +4127,14 @@ fn initHeaders(...@@ -4108,14 +4127,14 @@ fn initHeaders(
4108 .node_align = node_block_align,4127 .node_align = node_block_align,
4109 });4128 });
4110 if (maybe_interp) |interp| {4129 if (maybe_interp) |interp| {
4111 const interp_ni = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{4130 const interp_ni = try elf.ni.rodata.addFloatingChild(&elf.mf, gpa, .{
4112 .size = interp.len + 1,4131 .size = interp.len + 1,
4113 .moved = true,4132 .moved = true,
4114 .resized = true,4133 .resized = true,
4115 .bubbles_moved = false,4134 .bubbles_moved = false,
4116 });4135 });
4117 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.interp });4136 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.interp });
4118 elf.phdrs.items[phndx.interp] = interp_ni;4137 elf.phdrs.items[phndx.interp] = .wrap(interp_ni);
41194138
4120 const sec_interp_shndx = try elf.addSection(interp_ni, .{4139 const sec_interp_shndx = try elf.addSection(interp_ni, .{
4121 .name = ".interp",4140 .name = ".interp",
...@@ -4129,13 +4148,13 @@ fn initHeaders(...@@ -4129,13 +4148,13 @@ fn initHeaders(
4129 }4148 }
4130 if (have_dynamic_section) {4149 if (have_dynamic_section) {
4131 assert(elf.ni.data_rel_ro.alignment(&elf.mf).compare(.gte, addr_align));4150 assert(elf.ni.data_rel_ro.alignment(&elf.mf).compare(.gte, addr_align));
4132 const dynamic_ni = try elf.mf.addLastChildNode(gpa, elf.ni.data_rel_ro, .{4151 const dynamic_ni = try elf.ni.data_rel_ro.addFloatingChild(&elf.mf, gpa, .{
4133 .alignment = addr_align,4152 .alignment = addr_align,
4134 .moved = true,4153 .moved = true,
4135 .bubbles_moved = false,4154 .bubbles_moved = false,
4136 });4155 });
4137 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.dynamic });4156 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.dynamic });
4138 elf.phdrs.items[phndx.dynamic] = dynamic_ni;4157 elf.phdrs.items[phndx.dynamic] = .wrap(dynamic_ni);
41394158
4140 const dynstr_shndx = try elf.addSection(elf.ni.rodata, .{4159 const dynstr_shndx = try elf.addSection(elf.ni.rodata, .{
4141 .name = ".dynstr",4160 .name = ".dynstr",
...@@ -4207,7 +4226,7 @@ fn initHeaders(...@@ -4207,7 +4226,7 @@ fn initHeaders(
4207 .flags = .{ .ALLOC = true, .WRITE = true },4226 .flags = .{ .ALLOC = true, .WRITE = true },
4208 .link = dynstr_shndx.toSection().?,4227 .link = dynstr_shndx.toSection().?,
4209 .entsize = @intCast(addr_align.toByteUnits() * 2),4228 .entsize = @intCast(addr_align.toByteUnits() * 2),
4210 .node_align = addr_align,4229 .addralign = addr_align,
4211 });4230 });
4212 switch (elf.targetDynsymHashInfo()) {4231 switch (elf.targetDynsymHashInfo()) {
4213 inline else => |info| {4232 inline else => |info| {
...@@ -4230,8 +4249,8 @@ fn initHeaders(...@@ -4230,8 +4249,8 @@ fn initHeaders(
4230 if (elf.targetEndian() != std.lang.Endian.native) {4249 if (elf.targetEndian() != std.lang.Endian.native) {
4231 std.mem.byteSwapAllFields(info.Header(), header);4250 std.mem.byteSwapAllFields(info.Header(), header);
4232 }4251 }
4233 // The initial bucket and chain values are all 0, but `MappedFile` initialized4252 // The initial bucket and chain values are all 0.
4234 // the node with zeroes anyway, so no need to memset.4253 @memset(hash_slice[@sizeOf(info.Header())..], 0);
4235 },4254 },
4236 }4255 }
42374256
...@@ -4347,7 +4366,7 @@ fn initHeaders(...@@ -4347,7 +4366,7 @@ fn initHeaders(
4347 try elf.ensureUnusedSymbolCapacity(10, .maybe_global);4366 try elf.ensureUnusedSymbolCapacity(10, .maybe_global);
4348 // Despite the name, `__dso_handle` is necessary even in static binaries.4367 // Despite the name, `__dso_handle` is necessary even in static binaries.
4349 _ = elf.addGlobalSymbolAssumeCapacity(.{4368 _ = elf.addGlobalSymbolAssumeCapacity(.{
4350 .node = Section.Index.text.get(elf).ni,4369 .node = .wrap(Section.Index.text.get(elf).ni),
4351 .name = try .string(elf, "__dso_handle"),4370 .name = try .string(elf, "__dso_handle"),
4352 .value = Section.Index.text.vaddr(elf),4371 .value = Section.Index.text.vaddr(elf),
4353 .size = 0,4372 .size = 0,
...@@ -4359,7 +4378,7 @@ fn initHeaders(...@@ -4359,7 +4378,7 @@ fn initHeaders(
4359 error.MultipleDefinitions => unreachable, // no inputs are processed yet4378 error.MultipleDefinitions => unreachable, // no inputs are processed yet
4360 };4379 };
4361 _ = elf.addGlobalSymbolAssumeCapacity(.{4380 _ = elf.addGlobalSymbolAssumeCapacity(.{
4362 .node = elf.shndx.plt.get(elf).ni,4381 .node = .wrap(elf.shndx.plt.get(elf).ni),
4363 .name = try .string(elf, "_PROCEDURE_LINKAGE_TABLE_"),4382 .name = try .string(elf, "_PROCEDURE_LINKAGE_TABLE_"),
4364 .value = elf.shndx.plt.vaddr(elf),4383 .value = elf.shndx.plt.vaddr(elf),
4365 .size = 0,4384 .size = 0,
...@@ -4371,7 +4390,7 @@ fn initHeaders(...@@ -4371,7 +4390,7 @@ fn initHeaders(
4371 error.MultipleDefinitions => unreachable, // no inputs are processed yet4390 error.MultipleDefinitions => unreachable, // no inputs are processed yet
4372 };4391 };
4373 _ = elf.addGlobalSymbolAssumeCapacity(.{4392 _ = elf.addGlobalSymbolAssumeCapacity(.{
4374 .node = elf.shndx.got.get(elf).ni,4393 .node = .wrap(elf.shndx.got.get(elf).ni),
4375 .name = try .string(elf, "_GLOBAL_OFFSET_TABLE_"),4394 .name = try .string(elf, "_GLOBAL_OFFSET_TABLE_"),
4376 .value = switch (machine) {4395 .value = switch (machine) {
4377 .AARCH64,4396 .AARCH64,
...@@ -4468,7 +4487,7 @@ fn initHeaders(...@@ -4468,7 +4487,7 @@ fn initHeaders(
4468 };4487 };
4469 if (have_dynamic_section) {4488 if (have_dynamic_section) {
4470 _ = elf.addGlobalSymbolAssumeCapacity(.{4489 _ = elf.addGlobalSymbolAssumeCapacity(.{
4471 .node = elf.shndx.dynamic.get(elf).ni,4490 .node = .wrap(elf.shndx.dynamic.get(elf).ni),
4472 .name = try .string(elf, "_DYNAMIC"),4491 .name = try .string(elf, "_DYNAMIC"),
4473 .value = elf.shndx.dynamic.vaddr(elf),4492 .value = elf.shndx.dynamic.vaddr(elf),
4474 .size = 0,4493 .size = 0,
...@@ -4484,16 +4503,16 @@ fn initHeaders(...@@ -4484,16 +4503,16 @@ fn initHeaders(
4484 assert(maybe_interp == null);4503 assert(maybe_interp == null);
4485 assert(!have_dynamic_section);4504 assert(!have_dynamic_section);
4486 }4505 }
4487 if (comp.config.any_non_single_threaded) elf.shndx.tdata = try elf.addSection(elf.ni.tls, .{4506 if (elf.ni.tls.unwrap()) |tls_segment_ni| elf.shndx.tdata = try elf.addSection(tls_segment_ni, .{
4488 .name = ".tdata",4507 .name = ".tdata",
4489 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },4508 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
4490 .node_align = node_block_align,4509 .node_align = node_block_align,
4491 });4510 });
44924511
4493 assert(elf.nodes.len == expected_nodes_len);4512 assert(elf.nodes.len == expected_nodes_len);
4494 assert(elf.shdrs.items.len == shnum);4513 assert(elf.shdrs.items.len == shnum - 1); // -1 to exclude SHN_UNDEF
44954514
4496 for (0..shnum) |shndx_raw| {4515 for (1..shnum) |shndx_raw| { // start at 1 to exclude SHN_UNDEF
4497 const shndx: Section.Index = @fromBackingInt(@intCast(shndx_raw));4516 const shndx: Section.Index = @fromBackingInt(@intCast(shndx_raw));
4498 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});4517 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
4499 }4518 }
...@@ -4520,8 +4539,6 @@ fn initHeaders(...@@ -4520,8 +4539,6 @@ fn initHeaders(
4520 break :str try elf.string(.dynstr, slice);4539 break :str try elf.string(.dynstr, slice);
4521 },4540 },
4522 };4541 };
4523
4524 try elf.ensureElfNodeSize();
4525}4542}
45264543
4527pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {4544pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {
...@@ -4556,6 +4573,7 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {...@@ -4556,6 +4573,7 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {
4556 return switch (elf.getNode(ni)) {4573 return switch (elf.getNode(ni)) {
4557 .archive,4574 .archive,
4558 .archive_header,4575 .archive_header,
4576 .archive_elf_footer,
4559 .elf,4577 .elf,
4560 .ehdr,4578 .ehdr,
4561 .shdr,4579 .shdr,
...@@ -4569,13 +4587,14 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {...@@ -4569,13 +4587,14 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {
4569 .uav,4587 .uav,
4570 .lazy_code,4588 .lazy_code,
4571 .lazy_const_data,4589 .lazy_const_data,
4572 => elf.getNode(ni.parent(&elf.mf)).section,4590 => elf.getNode(ni.parent(&elf.mf).unwrap().?).section,
4573 };4591 };
4574}4592}
4575fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {4593fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
4576 return switch (elf.getNode(ni)) {4594 return switch (elf.getNode(ni)) {
4577 .archive,4595 .archive,
4578 .archive_header,4596 .archive_header,
4597 .archive_elf_footer,
4579 .elf,4598 .elf,
4580 .ehdr,4599 .ehdr,
4581 .shdr,4600 .shdr,
...@@ -4593,8 +4612,8 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {...@@ -4593,8 +4612,8 @@ fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
4593 };4612 };
4594}4613}
4595fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {4614fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
4596 const parent_vaddr = switch (elf.getNode(ni.parent(&elf.mf))) {4615 const parent_vaddr = switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) {
4597 .archive, .archive_header => unreachable,4616 .archive, .archive_header, .archive_elf_footer => unreachable,
4598 .elf => return 0,4617 .elf => return 0,
4599 .ehdr, .shdr => unreachable,4618 .ehdr, .shdr => unreachable,
4600 .segment => |phndx| switch (elf.phdrSlice()) {4619 .segment => |phndx| switch (elf.phdrSlice()) {
...@@ -4620,6 +4639,7 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {...@@ -4620,6 +4639,7 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
4620 const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) {4639 const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) {
4621 .archive,4640 .archive,
4622 .archive_header,4641 .archive_header,
4642 .archive_elf_footer,
4623 .elf,4643 .elf,
4624 .ehdr,4644 .ehdr,
4625 .shdr,4645 .shdr,
...@@ -4660,7 +4680,7 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {...@@ -4660,7 +4680,7 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
4660 if (got_relocs) |ptr| {4680 if (got_relocs) |ptr| {
4661 if (ptr.* != .none) {4681 if (ptr.* != .none) {
4662 for (elf.got_relocs.items[@backingInt(ptr.*)..]) |*reloc| {4682 for (elf.got_relocs.items[@backingInt(ptr.*)..]) |*reloc| {
4663 if (reloc.node != ni) break;4683 if (reloc.node != ni.toOptional()) break;
4664 reloc.delete(elf);4684 reloc.delete(elf);
4665 }4685 }
4666 }4686 }
...@@ -4691,7 +4711,7 @@ fn flushMovedNodeRelocs(...@@ -4691,7 +4711,7 @@ fn flushMovedNodeRelocs(
46914711
4692 if (first_got_reloc != .none) {4712 if (first_got_reloc != .none) {
4693 for (elf.got_relocs.items[@backingInt(first_got_reloc)..]) |*reloc| {4713 for (elf.got_relocs.items[@backingInt(first_got_reloc)..]) |*reloc| {
4694 if (reloc.node != node) break;4714 if (reloc.node != node.toOptional()) break;
4695 reloc.apply(elf);4715 reloc.apply(elf);
4696 }4716 }
4697 }4717 }
...@@ -4756,7 +4776,7 @@ fn targetPtrSize(elf: *const Elf) u8 {...@@ -4756,7 +4776,7 @@ fn targetPtrSize(elf: *const Elf) u8 {
4756/// Page alignment for the target platform.4776/// Page alignment for the target platform.
4757/// Usually this returns the maximum page size supported on the4777/// Usually this returns the maximum page size supported on the
4758/// target to maximize compatibility but there can be exceptions.4778/// target to maximize compatibility but there can be exceptions.
4759fn targetPageAlign(elf: *const Elf) std.mem.Alignment {4779fn targetPageAlign(elf: *const Elf) Alignment {
4760 return .fromByteUnits(switch (elf.ehdrMachine()) {4780 return .fromByteUnits(switch (elf.ehdrMachine()) {
4761 .AARCH64 => 0x10000,4781 .AARCH64 => 0x10000,
4762 .LOONGARCH => 0x10000,4782 .LOONGARCH => 0x10000,
...@@ -4810,7 +4830,7 @@ const PltInfo = struct {...@@ -4810,7 +4830,7 @@ const PltInfo = struct {
4810 /// entry, not the `.plt` entry. The `.plt.sec` section has no header entries, and is aligned to4830 /// entry, not the `.plt` entry. The `.plt.sec` section has no header entries, and is aligned to
4811 /// the same boundary as the `.plt` section.4831 /// the same boundary as the `.plt` section.
4812 plt_sec: ?struct { entry_size: u8 },4832 plt_sec: ?struct { entry_size: u8 },
4813 @"align": std.mem.Alignment,4833 @"align": Alignment,
4814 entry_size: u8,4834 entry_size: u8,
4815 header_entries: u8,4835 header_entries: u8,
48164836
...@@ -4868,7 +4888,7 @@ fn targetDynsymHashInfo(elf: *const Elf) DynsymHashInfo {...@@ -4868,7 +4888,7 @@ fn targetDynsymHashInfo(elf: *const Elf) DynsymHashInfo {
4868 // TODO: Alpha and S390x will need to use either `."@4"` or `.@"8"` depending on `elf.identClass()`.4888 // TODO: Alpha and S390x will need to use either `."@4"` or `.@"8"` depending on `elf.identClass()`.
4869 };4889 };
4870}4890}
4871fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {4891pub fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
4872 const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer;4892 const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer;
4873 const Child = pointer_ty.child;4893 const Child = pointer_ty.child;
4874 const alignment = pointer_ty.attrs.@"align" orelse @alignOf(Child);4894 const alignment = pointer_ty.attrs.@"align" orelse @alignOf(Child);
...@@ -4941,8 +4961,9 @@ fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr {...@@ -4941,8 +4961,9 @@ fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr {
4941 switch (elf.identClass()) {4961 switch (elf.identClass()) {
4942 .NONE, _ => unreachable,4962 .NONE, _ => unreachable,
4943 inline else => |class| {4963 inline else => |class| {
4964 const shdrs_len = elf.shdrs.items.len + 1; // +1 for SHN_UNDEF
4944 const shdr_slice: []class.ElfN().Shdr = @ptrCast(@alignCast(4965 const shdr_slice: []class.ElfN().Shdr = @ptrCast(@alignCast(
4945 raw_slice[0 .. elf.shdrs.items.len * @sizeOf(class.ElfN().Shdr)],4966 raw_slice[0 .. shdrs_len * @sizeOf(class.ElfN().Shdr)],
4946 ));4967 ));
4947 const shdr_ptr = &shdr_slice[@backingInt(shndx)];4968 const shdr_ptr = &shdr_slice[@backingInt(shndx)];
4948 return @unionInit(ShdrPtr, @tagName(class), shdr_ptr);4969 return @unionInit(ShdrPtr, @tagName(class), shdr_ptr);
...@@ -4951,7 +4972,7 @@ fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr {...@@ -4951,7 +4972,7 @@ fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr {
4951}4972}
49524973
4953fn arHdrPtr(elf: *Elf, ni: MappedFile.Node.Index) *align(2) std.elf.ar_hdr {4974fn arHdrPtr(elf: *Elf, ni: MappedFile.Node.Index) *align(2) std.elf.ar_hdr {
4954 assert(elf.ni.elf != MappedFile.Node.Index.root);4975 assert(elf.ni.elf != .root);
4955 const file_offset = ni.fileLocation(&elf.mf, false).offset;4976 const file_offset = ni.fileLocation(&elf.mf, false).offset;
4956 return @ptrCast(@alignCast(elf.mf.memory_map.memory[@intCast(switch (elf.getNode(ni)) {4977 return @ptrCast(@alignCast(elf.mf.memory_map.memory[@intCast(switch (elf.getNode(ni)) {
4957 else => unreachable,4978 else => unreachable,
...@@ -5049,13 +5070,13 @@ fn mapInputSection(elf: *Elf, opts: struct {...@@ -5049,13 +5070,13 @@ fn mapInputSection(elf: *Elf, opts: struct {
5049 const name_shstrtab = try elf.string(.shstrtab, name);5070 const name_shstrtab = try elf.string(.shstrtab, name);
5050 const gop = try elf.section_by_name.getOrPut(gpa, name_shstrtab);5071 const gop = try elf.section_by_name.getOrPut(gpa, name_shstrtab);
5051 if (gop.found_existing) {5072 if (gop.found_existing) {
5052 break :existing @fromBackingInt(@intCast(gop.index));5073 break :existing @fromBackingInt(@intCast(gop.index + 1)); // +1 to account for SHN_UDNEF
5053 }5074 }
5054 errdefer assert(elf.section_by_name.pop().?.key == name_shstrtab);5075 errdefer assert(elf.section_by_name.pop().?.key == name_shstrtab);
5055 const parent_node: MappedFile.Node.Index = parent: {5076 const parent_node: MappedFile.Node.Index = parent: {
5056 if (!opts.flags.ALLOC) break :parent elf.ni.elf;5077 if (!opts.flags.ALLOC) break :parent elf.ni.elf;
5057 if (opts.flags.EXECINSTR) break :parent elf.ni.text;5078 if (opts.flags.EXECINSTR) break :parent elf.ni.text;
5058 if (opts.flags.TLS) break :parent elf.ni.tls;5079 if (opts.flags.TLS) break :parent elf.ni.tls.unwrap().?;
5059 if (opts.flags.WRITE) break :parent elf.ni.data;5080 if (opts.flags.WRITE) break :parent elf.ni.data;
5060 break :parent elf.ni.rodata;5081 break :parent elf.ni.rodata;
5061 };5082 };
...@@ -5148,12 +5169,12 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node...@@ -5148,12 +5169,12 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
5148 break :section .data_rel_ro; // TODO: it would be better to use `.rodata` if the NAV value doesn't have relocs5169 break :section .data_rel_ro; // TODO: it would be better to use `.rodata` if the NAV value doesn't have relocs
5149 }5170 }
5150 };5171 };
5151 const alignment: InternPool.Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) {5172 const alignment: Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) {
5152 .@"fn" => a: {5173 .@"fn" => a: {
5153 const mod = zcu.navFileScope(nav_index).mod.?;5174 const mod = zcu.navFileScope(nav_index).mod.?;
5154 const target = &mod.resolved_target.result;5175 const target = &mod.resolved_target.result;
5155 const min = target_util.minFunctionAlignment(target);5176 const min = target_util.minFunctionAlignment(target);
5156 break :a switch (nav.resolved.?.@"align") {5177 break :a .fromIp(switch (nav.resolved.?.@"align") {
5157 else => |a| a.maxStrict(min),5178 else => |a| a.maxStrict(min),
5158 .none => switch (mod.optimize_mode) {5179 .none => switch (mod.optimize_mode) {
5159 .debug,5180 .debug,
...@@ -5162,20 +5183,20 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node...@@ -5162,20 +5183,20 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
5162 => target_util.defaultFunctionAlignment(target),5183 => target_util.defaultFunctionAlignment(target),
5163 .small => min,5184 .small => min,
5164 }.maxStrict(Type.fromInterned(nav.resolved.?.type).abiAlignment(zcu)),5185 }.maxStrict(Type.fromInterned(nav.resolved.?.type).abiAlignment(zcu)),
5165 };5186 });
5166 },5187 },
5167 else => switch (nav.resolved.?.@"align") {5188 else => switch (nav.resolved.?.@"align") {
5168 .none => Type.fromInterned(nav.resolved.?.type).abiAlignment(zcu),5189 .none => .fromIp(Type.fromInterned(nav.resolved.?.type).abiAlignment(zcu)),
5169 else => |a| a,5190 else => |a| .fromIp(a),
5170 },5191 },
5171 };5192 };
5172 try shndx.ensureAligned(elf, alignment.toStdMem());5193 try shndx.ensureAligned(elf, alignment);
5173 const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{5194 const node = try shndx.get(elf).ni.addFloatingChild(&elf.mf, gpa, .{
5174 .alignment = alignment.toStdMem(),5195 .alignment = alignment,
5175 });5196 });
5176 nav_gop.value_ptr.* = .{5197 nav_gop.value_ptr.* = .{
5177 .lsi = elf.addLocalSymbolAssumeCapacity(.{5198 .lsi = elf.addLocalSymbolAssumeCapacity(.{
5178 .node = node,5199 .node = .wrap(node),
5179 .name = try elf.string(.strtab, nav.fqn.toSlice(ip)),5200 .name = try elf.string(.strtab, nav.fqn.toSlice(ip)),
5180 .value = 0,5201 .value = 0,
5181 .size = 0,5202 .size = 0,
...@@ -5204,19 +5225,19 @@ fn uavMapIndex(...@@ -5204,19 +5225,19 @@ fn uavMapIndex(
5204 try elf.pending_uavs.ensureUnusedCapacity(gpa, 1);5225 try elf.pending_uavs.ensureUnusedCapacity(gpa, 1);
52055226
5206 const abi_align = Value.fromInterned(uav_val).typeOf(zcu).abiAlignment(zcu);5227 const abi_align = Value.fromInterned(uav_val).typeOf(zcu).abiAlignment(zcu);
5207 const resolved_align: InternPool.Alignment = switch (uav_align) {5228 const resolved_align: Alignment = switch (uav_align) {
5208 .none => abi_align,5229 .none => .fromIp(abi_align),
5209 else => |a| a.minStrict(abi_align),5230 else => |a| .fromIp(a.minStrict(abi_align)),
5210 };5231 };
52115232
5212 const uav_gop = elf.uavs.getOrPutAssumeCapacity(uav_val);5233 const uav_gop = elf.uavs.getOrPutAssumeCapacity(uav_val);
5213 const umi: Node.UavMapIndex = @fromBackingInt(@intCast(uav_gop.index));5234 const umi: Node.UavMapIndex = @fromBackingInt(@intCast(uav_gop.index));
5214 if (!uav_gop.found_existing) {5235 if (!uav_gop.found_existing) {
5215 const shndx: Section.Index = .data_rel_ro; // TODO: it would be better to use `.rodata` if the UAV value doesn't have relocs5236 const shndx: Section.Index = .data_rel_ro; // TODO: it would be better to use `.rodata` if the UAV value doesn't have relocs
5216 try shndx.ensureAligned(elf, resolved_align.toStdMem());5237 try shndx.ensureAligned(elf, resolved_align);
5217 const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{5238 const node = try shndx.get(elf).ni.addFloatingChild(&elf.mf, gpa, .{
5218 .moved = true, // see assert at end of `genUav`5239 .moved = true, // see assert at end of `genUav`
5219 .alignment = resolved_align.toStdMem(),5240 .alignment = resolved_align,
5220 });5241 });
5221 var name_buf: [32]u8 = undefined;5242 var name_buf: [32]u8 = undefined;
5222 const name = std.fmt.bufPrint(5243 const name = std.fmt.bufPrint(
...@@ -5226,7 +5247,7 @@ fn uavMapIndex(...@@ -5226,7 +5247,7 @@ fn uavMapIndex(
5226 ) catch unreachable;5247 ) catch unreachable;
5227 uav_gop.value_ptr.* = .{5248 uav_gop.value_ptr.* = .{
5228 .lsi = elf.addLocalSymbolAssumeCapacity(.{5249 .lsi = elf.addLocalSymbolAssumeCapacity(.{
5229 .node = node,5250 .node = .wrap(node),
5230 .name = try elf.string(.strtab, name),5251 .name = try elf.string(.strtab, name),
5231 .value = 0,5252 .value = 0,
5232 .size = 0,5253 .size = 0,
...@@ -5239,11 +5260,11 @@ fn uavMapIndex(...@@ -5239,11 +5260,11 @@ fn uavMapIndex(
5239 elf.const_prog_node.increaseEstimatedTotalItems(1);5260 elf.const_prog_node.increaseEstimatedTotalItems(1);
5240 elf.pending_uavs.appendAssumeCapacity(umi);5261 elf.pending_uavs.appendAssumeCapacity(umi);
5241 } else {5262 } else {
5242 const node = uav_gop.value_ptr.lsi.index().ptr(elf).node;5263 const node = uav_gop.value_ptr.lsi.index().ptr(elf).node.unwrap().?;
5243 const shndx = elf.getNode(node.parent(&elf.mf)).section;5264 const shndx = elf.getNode(node.parent(&elf.mf).unwrap().?).section;
5244 try shndx.ensureAligned(elf, resolved_align.toStdMem());5265 try shndx.ensureAligned(elf, resolved_align);
5245 if (resolved_align.toStdMem().order(node.alignment(&elf.mf)).compare(.gt)) {5266 if (resolved_align.order(node.alignment(&elf.mf)).compare(.gt)) {
5246 try node.realign(&elf.mf, gpa, resolved_align.toStdMem(), .{});5267 try node.realign(&elf.mf, gpa, resolved_align);
5247 }5268 }
5248 }5269 }
5249 return umi;5270 return umi;
...@@ -5459,10 +5480,11 @@ fn loadObject(...@@ -5459,10 +5480,11 @@ fn loadObject(
5459 .member = if (member) |m| try gpa.dupe(u8, m) else null,5480 .member = if (member) |m| try gpa.dupe(u8, m) else null,
5460 .extra = undefined,5481 .extra = undefined,
5461 };5482 };
5462 if (elf.ni.elf != MappedFile.Node.Index.root) {5483 if (elf.ni.elf != .root) {
5484 const archive_ni: MappedFile.Node.Index = .root;
5463 try elf.nodes.ensureUnusedCapacity(gpa, 1);5485 try elf.nodes.ensureUnusedCapacity(gpa, 1);
5464 input.extra = .{ .node = try elf.mf.addLastChildNode(gpa, .root, .{5486 input.extra = .{ .node = try archive_ni.addFloatingChild(&elf.mf, gpa, .{
5465 .size = fl.size + @sizeOf(std.elf.ar_hdr),5487 .size = Alignment.@"2".forward(fl.size + @sizeOf(std.elf.ar_hdr)),
5466 .alignment = .@"2",5488 .alignment = .@"2",
5467 .next_moved = true,5489 .next_moved = true,
5468 .bubbles_moved = false,5490 .bubbles_moved = false,
...@@ -5640,16 +5662,28 @@ fn loadObject(...@@ -5640,16 +5662,28 @@ fn loadObject(
5640 .node_fixed = true,5662 .node_fixed = true,
5641 },5663 },
5642 };5664 };
5643 const need_align: std.mem.Alignment = .fromByteUnits(5665 const need_align: Alignment = .fromByteUnits(
5644 std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))),5666 std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))),
5645 );5667 );
5646 try opts.shndx.ensureAligned(elf, need_align);5668 try opts.shndx.ensureAligned(elf, need_align);
5647 const ni = try elf.mf.addLastChildNode(gpa, opts.shndx.get(elf).ni, .{5669 const add_node_opts: MappedFile.Node.AddOptions = .{
5648 .size = section.shdr.size,5670 .size = need_align.forward(section.shdr.size),
5649 .alignment = need_align,5671 .alignment = need_align,
5650 .moved = true, // see assert at end of `flushInputSection`5672 .moved = true, // see assert at end of `flushInputSection`
5651 .fixed = opts.node_fixed,5673 };
5652 });5674 const ni = if (opts.node_fixed) ni: {
5675 const shndx_ni = opts.shndx.get(elf).ni;
5676 const after_oni: MappedFile.Node.Index.Optional = after: {
5677 const last_ni = shndx_ni.last(&elf.mf).unwrap() orelse break :after .none;
5678 break :after switch (last_ni.position(&elf.mf)) {
5679 .header => .wrap(last_ni),
5680 .footer, .floating => .none,
5681 };
5682 };
5683 break :ni try shndx_ni.addHeaderChildAfter(&elf.mf, gpa, after_oni, add_node_opts);
5684 } else ni: {
5685 break :ni try opts.shndx.get(elf).ni.addFloatingChild(&elf.mf, gpa, add_node_opts);
5686 };
5653 elf.nodes.appendAssumeCapacity(.{5687 elf.nodes.appendAssumeCapacity(.{
5654 .input_section = @fromBackingInt(@intCast(elf.input_sections.items.len)),5688 .input_section = @fromBackingInt(@intCast(elf.input_sections.items.len)),
5655 });5689 });
...@@ -5754,7 +5788,7 @@ fn loadObject(...@@ -5754,7 +5788,7 @@ fn loadObject(
5754 ),5788 ),
5755 .LOCAL => {5789 .LOCAL => {
5756 const lsi = elf.addLocalSymbolAssumeCapacity(.{5790 const lsi = elf.addLocalSymbolAssumeCapacity(.{
5757 .node = input_section_node,5791 .node = .wrap(input_section_node),
5758 .name = try elf.string(.strtab, name),5792 .name = try elf.string(.strtab, name),
5759 .value = input_sym.value,5793 .value = input_sym.value,
5760 .size = input_sym.size,5794 .size = input_sym.size,
...@@ -5765,7 +5799,7 @@ fn loadObject(...@@ -5765,7 +5799,7 @@ fn loadObject(
5765 },5799 },
5766 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {5800 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
5767 si.* = elf.addGlobalSymbolAssumeCapacity(.{5801 si.* = elf.addGlobalSymbolAssumeCapacity(.{
5768 .node = input_section_node,5802 .node = .wrap(input_section_node),
5769 .name = try .string(elf, name),5803 .name = try .string(elf, name),
5770 .value = input_sym.value,5804 .value = input_sym.value,
5771 .size = input_sym.size,5805 .size = input_sym.size,
...@@ -5893,7 +5927,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars...@@ -5893,7 +5927,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars
5893 return diags.failParse(path, "bad machine", .{});5927 return diags.failParse(path, "bad machine", .{});
5894 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);5928 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);
5895 // We're going to need to know the alignment of every section later.5929 // We're going to need to know the alignment of every section later.
5896 const section_aligns = try gpa.alloc(std.mem.Alignment, ehdr.shnum);5930 const section_aligns = try gpa.alloc(Alignment, ehdr.shnum);
5897 defer gpa.free(section_aligns);5931 defer gpa.free(section_aligns);
5898 const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: {5932 const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: {
5899 var dynamic_sh: ?ElfN.Shdr = null;5933 var dynamic_sh: ?ElfN.Shdr = null;
...@@ -5999,7 +6033,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars...@@ -5999,7 +6033,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars
59996033
6000 // We need to guess the worst-case alignment of the symbol. Yes, I know this seems6034 // We need to guess the worst-case alignment of the symbol. Yes, I know this seems
6001 // insane---refer to the doc comment on `alignment` in `Elf.dso_globals`.6035 // insane---refer to the doc comment on `alignment` in `Elf.dso_globals`.
6002 const sym_align: std.mem.Alignment = switch (sym.value) {6036 const sym_align: Alignment = switch (sym.value) {
6003 0 => section_aligns[sym.shndx],6037 0 => section_aligns[sym.shndx],
6004 else => section_aligns[sym.shndx].min(@fromBackingInt(@intCast(@ctz(sym.value)))),6038 else => section_aligns[sym.shndx].min(@fromBackingInt(@intCast(@ctz(sym.value)))),
6005 };6039 };
...@@ -6017,8 +6051,8 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars...@@ -6017,8 +6051,8 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars
6017 // We have a copy relocation for this global, but the amount of space we6051 // We have a copy relocation for this global, but the amount of space we
6018 // reserved for it could be too small or underaligned!6052 // reserved for it could be too small or underaligned!
6019 try Section.Index.data.ensureAligned(elf, gop.value_ptr.alignment);6053 try Section.Index.data.ensureAligned(elf, gop.value_ptr.alignment);
6020 try copied_global.node.resize(&elf.mf, gpa, gop.value_ptr.size);6054 try copied_global.node.resizeLeaf(&elf.mf, gpa, gop.value_ptr.alignment.forward(gop.value_ptr.size));
6021 try copied_global.node.realign(&elf.mf, gpa, gop.value_ptr.alignment, .{});6055 try copied_global.node.realign(&elf.mf, gpa, gop.value_ptr.alignment);
6022 const global_ptr = elf.globalByName(name).?;6056 const global_ptr = elf.globalByName(name).?;
6023 switch (elf.symPtr(global_ptr.symtab_index)) {6057 switch (elf.symPtr(global_ptr.symtab_index)) {
6024 inline else => |sym_ptr| elf.targetStore(&sym_ptr.size, @intCast(gop.value_ptr.size)),6058 inline else => |sym_ptr| elf.targetStore(&sym_ptr.size, @intCast(gop.value_ptr.size)),
...@@ -6158,7 +6192,7 @@ fn createInitFiniArraySection(...@@ -6158,7 +6192,7 @@ fn createInitFiniArraySection(
6158) Error!void {6192) Error!void {
6159 assert(shndx.* == .UNDEF);6193 assert(shndx.* == .UNDEF);
6160 const gpa = elf.base.comp.gpa;6194 const gpa = elf.base.comp.gpa;
6161 const addr_align: std.mem.Alignment = switch (elf.identClass()) {6195 const addr_align: Alignment = switch (elf.identClass()) {
6162 .NONE, _ => unreachable,6196 .NONE, _ => unreachable,
6163 .@"32" => .@"4",6197 .@"32" => .@"4",
6164 .@"64" => .@"8",6198 .@"64" => .@"8",
...@@ -6178,14 +6212,14 @@ fn createInitFiniArraySection(...@@ -6178,14 +6212,14 @@ fn createInitFiniArraySection(
6178 const start_sym_name = try elf.string(.strtab, "__" ++ name ++ "_start");6212 const start_sym_name = try elf.string(.strtab, "__" ++ name ++ "_start");
6179 const end_sym_name = try elf.string(.strtab, "__" ++ name ++ "_end");6213 const end_sym_name = try elf.string(.strtab, "__" ++ name ++ "_end");
6180 elf.setGlobalSymbolValue(start_sym_name, elf.globals.strong_def.getPtr(start_sym_name).?, .{6214 elf.setGlobalSymbolValue(start_sym_name, elf.globals.strong_def.getPtr(start_sym_name).?, .{
6181 .node = shndx.get(elf).ni,6215 .node = .wrap(shndx.get(elf).ni),
6182 .value = shndx.vaddr(elf),6216 .value = shndx.vaddr(elf),
6183 .size = 0,6217 .size = 0,
6184 .type = .NOTYPE,6218 .type = .NOTYPE,
6185 .shndx = shndx.*,6219 .shndx = shndx.*,
6186 });6220 });
6187 elf.setGlobalSymbolValue(end_sym_name, elf.globals.strong_def.getPtr(end_sym_name).?, .{6221 elf.setGlobalSymbolValue(end_sym_name, elf.globals.strong_def.getPtr(end_sym_name).?, .{
6188 .node = shndx.get(elf).ni,6222 .node = .wrap(shndx.get(elf).ni),
6189 .value = shndx.vaddr(elf),6223 .value = shndx.vaddr(elf),
6190 .size = 0,6224 .size = 0,
6191 .type = .NOTYPE,6225 .type = .NOTYPE,
...@@ -6218,7 +6252,7 @@ fn prelinkInner(elf: *Elf) Error!void {...@@ -6218,7 +6252,7 @@ fn prelinkInner(elf: *Elf) Error!void {
6218 const comp = elf.base.comp;6252 const comp = elf.base.comp;
6219 const gpa = comp.gpa;6253 const gpa = comp.gpa;
62206254
6221 if (comp.zcu != null and !comp.config.use_llvm and elf.ni.elf == MappedFile.Node.Index.root) {6255 if (comp.zcu != null and !comp.config.use_llvm and elf.ni.elf == .root) {
6222 // We're using self-hosted codegen---add an input representing the Zig "object".6256 // We're using self-hosted codegen---add an input representing the Zig "object".
6223 try elf.ensureUnusedSymbolCapacity(1, .all_local);6257 try elf.ensureUnusedSymbolCapacity(1, .all_local);
6224 try elf.inputs.ensureUnusedCapacity(gpa, 1);6258 try elf.inputs.ensureUnusedCapacity(gpa, 1);
...@@ -6239,8 +6273,6 @@ fn prelinkInner(elf: *Elf) Error!void {...@@ -6239,8 +6273,6 @@ fn prelinkInner(elf: *Elf) Error!void {
6239 };6273 };
6240 elf.input_pending_index += 1;6274 elf.input_pending_index += 1;
6241 }6275 }
6242
6243 try elf.ensureElfNodeSize();
6244}6276}
62456277
6246fn prepareDynamic(elf: *Elf) Error!void {6278fn prepareDynamic(elf: *Elf) Error!void {
...@@ -6265,7 +6297,7 @@ fn prepareDynamic(elf: *Elf) Error!void {...@@ -6265,7 +6297,7 @@ fn prepareDynamic(elf: *Elf) Error!void {
62656297
6266 const dynamic_size = dynamic_len * 2 * elf.targetPtrSize();6298 const dynamic_size = dynamic_len * 2 * elf.targetPtrSize();
62676299
6268 try elf.shndx.dynamic.get(elf).ni.resize(&elf.mf, comp.gpa, dynamic_size);6300 try elf.shndx.dynamic.get(elf).ni.resizeLeaf(&elf.mf, comp.gpa, dynamic_size);
6269 switch (elf.shdrPtr(elf.shndx.dynamic)) {6301 switch (elf.shdrPtr(elf.shndx.dynamic)) {
6270 inline else => |shdr| elf.targetStore(&shdr.size, @intCast(dynamic_size)),6302 inline else => |shdr| elf.targetStore(&shdr.size, @intCast(dynamic_size)),
6271 }6303 }
...@@ -6388,10 +6420,9 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -6388,10 +6420,9 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
6388 size: std.elf.Xword = 0,6420 size: std.elf.Xword = 0,
6389 link: std.elf.Word = 0,6421 link: std.elf.Word = 0,
6390 info: std.elf.Word = 0,6422 info: std.elf.Word = 0,
6391 addralign: std.mem.Alignment = .@"1",6423 addralign: Alignment = .@"1",
6392 entsize: std.elf.Word = 0,6424 entsize: std.elf.Word = 0,
6393 node_align: std.mem.Alignment = .@"1",6425 node_align: Alignment = .@"1",
6394 fixed: bool = false,
6395}) Error!Section.Index {6426}) Error!Section.Index {
6396 switch (opts.type) {6427 switch (opts.type) {
6397 .NULL => assert(opts.size == 0),6428 .NULL => assert(opts.size == 0),
...@@ -6435,19 +6466,20 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -6435,19 +6466,20 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
6435 break :shndx .{ @fromBackingInt(shndx), @as(u64, elf.targetLoad(&ehdr.shentsize)) * @as(u64, shnum) };6466 break :shndx .{ @fromBackingInt(shndx), @as(u64, elf.targetLoad(&ehdr.shentsize)) * @as(u64, shnum) };
6436 },6467 },
6437 };6468 };
6438 try elf.ensureNodeSize(elf.ni.shdr, new_shdr_size);6469 try elf.ni.shdr.ensureMinimumSize(&elf.mf, gpa, new_shdr_size);
6439 const ni = try elf.mf.addLastChildNode(gpa, switch (elf.ehdrType()) {6470 const parent_ni = switch (elf.ehdrType()) {
6440 .REL => elf.ni.elf,6471 .REL => elf.ni.elf,
6441 .EXEC, .DYN => segment_ni,6472 .EXEC, .DYN => segment_ni,
6442 }, .{6473 };
6443 .size = opts.size,6474 assert(opts.addralign.check(opts.size));
6475 const ni = try parent_ni.addFloatingChild(&elf.mf, gpa, .{
6476 .size = opts.node_align.forward(opts.size),
6444 .alignment = opts.addralign.max(opts.node_align),6477 .alignment = opts.addralign.max(opts.node_align),
6445 .fixed = opts.fixed,
6446 .resized = opts.size > 0,6478 .resized = opts.size > 0,
6447 });6479 });
6448 const addr = elf.computeNodeVAddr(ni);6480 const addr = elf.computeNodeVAddr(ni);
6449 const lsi: Symbol.LocalIndex = if (opts.flags.ALLOC) elf.addLocalSymbolAssumeCapacity(.{6481 const lsi: Symbol.LocalIndex = if (opts.flags.ALLOC) elf.addLocalSymbolAssumeCapacity(.{
6450 .node = ni,6482 .node = .wrap(ni),
6451 .name = .empty,6483 .name = .empty,
6452 .value = addr,6484 .value = addr,
6453 .size = 0,6485 .size = 0,
...@@ -6499,7 +6531,7 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)...@@ -6499,7 +6531,7 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)
64996531
6500 assert(elf.section_by_name.count() == elf.shdrs.items.len);6532 assert(elf.section_by_name.count() == elf.shdrs.items.len);
6501 try elf.section_by_name.ensureUnusedCapacity(gpa, 1);6533 try elf.section_by_name.ensureUnusedCapacity(gpa, 1);
6502 const rela_shndx = try elf.addSection(.none, .{6534 const rela_shndx = try elf.addSection(elf.ni.elf, .{
6503 .name = rela_name,6535 .name = rela_name,
6504 .type = .RELA,6536 .type = .RELA,
6505 .link = @backingInt(Section.Index.symtab),6537 .link = @backingInt(Section.Index.symtab),
...@@ -6528,7 +6560,7 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)...@@ -6528,7 +6560,7 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)
6528 .NONE, _ => unreachable,6560 .NONE, _ => unreachable,
6529 inline else => |ct_class| (elf.got.count() + new_got_entries) * @sizeOf(ct_class.ElfN().Addr),6561 inline else => |ct_class| (elf.got.count() + new_got_entries) * @sizeOf(ct_class.ElfN().Addr),
6530 };6562 };
6531 try elf.ensureNodeSize(elf.shndx.got.get(elf).ni, need_got_size);6563 try elf.shndx.got.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_got_size);
65326564
6533 if (elf.shndx.dynamic != .UNDEF) {6565 if (elf.shndx.dynamic != .UNDEF) {
6534 try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, new_got_entries);6566 try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, new_got_entries);
...@@ -6546,7 +6578,6 @@ fn addRelocAssumeCapacity(...@@ -6546,7 +6578,6 @@ fn addRelocAssumeCapacity(
6546 addend: i64,6578 addend: i64,
6547 @"type": MachineRelocType,6579 @"type": MachineRelocType,
6548) (Error || error{ UnknownRelocation, NonStaticRelocation, UnimplementedRelocation })!void {6580) (Error || error{ UnknownRelocation, NonStaticRelocation, UnimplementedRelocation })!void {
6549 assert(node != .none);
6550 switch (elf.ehdrType()) {6581 switch (elf.ehdrType()) {
6551 .REL => {6582 .REL => {
6552 const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx;6583 const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx;
...@@ -6894,7 +6925,6 @@ fn addSymbolRelocAssumeCapacity(...@@ -6894,7 +6925,6 @@ fn addSymbolRelocAssumeCapacity(
6894 @"type": SymbolReloc.Type,6925 @"type": SymbolReloc.Type,
6895) Error!void {6926) Error!void {
6896 assert(elf.ehdrType() != .REL);6927 assert(elf.ehdrType() != .REL);
6897 assert(node != .none);
68986928
6899 const rela_index: Section.RelaIndex.Optional = r: {6929 const rela_index: Section.RelaIndex.Optional = r: {
6900 if (elf.shndx.dynamic == .UNDEF) break :r .none;6930 if (elf.shndx.dynamic == .UNDEF) break :r .none;
...@@ -7042,6 +7072,7 @@ fn addGotRelocAssumeCapacity(...@@ -7042,6 +7072,7 @@ fn addGotRelocAssumeCapacity(
7042 switch (elf.getNode(node)) {7072 switch (elf.getNode(node)) {
7043 .archive,7073 .archive,
7044 .archive_header,7074 .archive_header,
7075 .archive_elf_footer,
7045 .elf,7076 .elf,
7046 .ehdr,7077 .ehdr,
7047 .shdr,7078 .shdr,
...@@ -7089,7 +7120,7 @@ fn addGotRelocAssumeCapacity(...@@ -7089,7 +7120,7 @@ fn addGotRelocAssumeCapacity(
7089 }7120 }
70907121
7091 elf.got_relocs.appendAssumeCapacity(.{7122 elf.got_relocs.appendAssumeCapacity(.{
7092 .node = node,7123 .node = .wrap(node),
7093 .offset = offset,7124 .offset = offset,
7094 .target = target,7125 .target = target,
7095 .addend = addend,7126 .addend = addend,
...@@ -7111,7 +7142,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -7111,7 +7142,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
7111 .tpoff => |sym_id| val: {7142 .tpoff => |sym_id| val: {
7112 // Only the executable's per-module TLS block is at a known offset from the TLS pointer.7143 // Only the executable's per-module TLS block is at a known offset from the TLS pointer.
7113 if (elf.base.comp.config.output_mode == .Exe and elf.classifySymbolValue(sym_id) != .dynamic) {7144 if (elf.base.comp.config.output_mode == .Exe and elf.classifySymbolValue(sym_id) != .dynamic) {
7114 const tls_phndx = elf.getNode(elf.ni.tls).segment;7145 const tls_phndx = elf.getNode(elf.ni.tls.unwrap().?).segment;
7115 const tls_size: u64 = switch (elf.phdrSlice()) {7146 const tls_size: u64 = switch (elf.phdrSlice()) {
7116 inline else => |phdr| tls_size: {7147 inline else => |phdr| tls_size: {
7117 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);7148 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
...@@ -7284,8 +7315,8 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {...@@ -7284,8 +7315,8 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
7284 try Section.Index.data.ensureAligned(elf, dso_global.alignment);7315 try Section.Index.data.ensureAligned(elf, dso_global.alignment);
72857316
7286 try elf.nodes.ensureUnusedCapacity(gpa, 1);7317 try elf.nodes.ensureUnusedCapacity(gpa, 1);
7287 const node = try elf.mf.addLastChildNode(gpa, Section.Index.data.get(elf).ni, .{7318 const node = try Section.Index.data.get(elf).ni.addFloatingChild(&elf.mf, gpa, .{
7288 .size = dso_global.size,7319 .size = dso_global.alignment.forward(dso_global.size),
7289 .alignment = dso_global.alignment,7320 .alignment = dso_global.alignment,
7290 });7321 });
7291 errdefer comptime unreachable;7322 errdefer comptime unreachable;
...@@ -7336,7 +7367,7 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)...@@ -7336,7 +7367,7 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
7336 if (!Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) return;7367 if (!Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) return;
73377368
7338 const nmi = try elf.navMapIndex(zcu, nav_index);7369 const nmi = try elf.navMapIndex(zcu, nav_index);
7339 const ni = nmi.symbol(elf).index().ptr(elf).node;7370 const ni = nmi.symbol(elf).index().ptr(elf).node.unwrap().?;
7340 elf.resetNodeRelocs(ni);7371 elf.resetNodeRelocs(ni);
73417372
7342 // Ensure the NAV is marked as moved so that once we're done, `flushMoved` will eventually be7373 // Ensure the NAV is marked as moved so that once we're done, `flushMoved` will eventually be
...@@ -7392,7 +7423,7 @@ fn updateFuncInner(...@@ -7392,7 +7423,7 @@ fn updateFuncInner(
73927423
7393 const nmi = try elf.navMapIndex(zcu, func.owner_nav);7424 const nmi = try elf.navMapIndex(zcu, func.owner_nav);
7394 log.debug("updateFunc({f}) = {d}", .{ nav.fqn.fmt(ip), nmi.symbol(elf) });7425 log.debug("updateFunc({f}) = {d}", .{ nav.fqn.fmt(ip), nmi.symbol(elf) });
7395 const ni = nmi.symbol(elf).index().ptr(elf).node;7426 const ni = nmi.symbol(elf).index().ptr(elf).node.unwrap().?;
7396 elf.resetNodeRelocs(ni);7427 elf.resetNodeRelocs(ni);
73977428
7398 // Ensure the NAV is marked as moved so that once we're done, `flushMoved` will eventually be7429 // Ensure the NAV is marked as moved so that once we're done, `flushMoved` will eventually be
...@@ -7471,7 +7502,6 @@ fn flushInner(...@@ -7471,7 +7502,6 @@ fn flushInner(
74717502
7472 try elf.prepareDynamic();7503 try elf.prepareDynamic();
74737504
7474 try elf.ensureElfNodeSize();
7475 while (try elf.idle(tid)) {}7505 while (try elf.idle(tid)) {}
74767506
7477 // We've done the final `idle` loop, so everything is at its final place in the file. We have a7507 // We've done the final `idle` loop, so everything is at its final place in the file. We have a
...@@ -7677,7 +7707,7 @@ fn idleProgNode(...@@ -7677,7 +7707,7 @@ fn idleProgNode(
7677 break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{7707 break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{
7678 ii.path(elf).fmtEscapeString(),7708 ii.path(elf).fmtEscapeString(),
7679 fmtMemberString(ii.member(elf)),7709 fmtMemberString(ii.member(elf)),
7680 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf),7710 elf.getNode(isi.node(elf).parent(&elf.mf).unwrap().?).section.name(elf).slice(elf),
7681 }) catch &name;7711 }) catch &name;
7682 },7712 },
7683 .nav => |nmi| {7713 .nav => |nmi| {
...@@ -7724,8 +7754,6 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) Error!void {...@@ -7724,8 +7754,6 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) Error!void {
7724 };7754 };
7725 break;7755 break;
7726 }7756 }
7727
7728 try elf.ensureElfNodeSize();
7729}7757}
77307758
7731fn genUav(7759fn genUav(
...@@ -7737,7 +7765,7 @@ fn genUav(...@@ -7737,7 +7765,7 @@ fn genUav(
7737 const gpa = comp.gpa;7765 const gpa = comp.gpa;
77387766
7739 const uav_val = umi.uavValue(elf);7767 const uav_val = umi.uavValue(elf);
7740 const ni = umi.symbol(elf).index().ptr(elf).node;7768 const ni = umi.symbol(elf).index().ptr(elf).node.unwrap().?;
7741 elf.resetNodeRelocs(ni);7769 elf.resetNodeRelocs(ni);
77427770
7743 var nw: MappedFile.Node.Writer = undefined;7771 var nw: MappedFile.Node.Writer = undefined;
...@@ -7766,7 +7794,7 @@ fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) Error!void {...@@ -7766,7 +7794,7 @@ fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) Error!void {
7766 const gpa = zcu.gpa;7794 const gpa = zcu.gpa;
77677795
7768 const lazy = lmr.lazySymbol(elf);7796 const lazy = lmr.lazySymbol(elf);
7769 const ni = lmr.symbol(elf).index().ptr(elf).node;7797 const ni = lmr.symbol(elf).index().ptr(elf).node.unwrap().?;
7770 elf.resetNodeRelocs(ni);7798 elf.resetNodeRelocs(ni);
77717799
7772 // Ensure the lazy node is marked as moved so that once we're done, `flushMoved` will eventually7800 // Ensure the lazy node is marked as moved so that once we're done, `flushMoved` will eventually
...@@ -7842,7 +7870,7 @@ fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void {...@@ -7842,7 +7870,7 @@ fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void {
7842 fr.seekTo(file_loc.offset) catch |err| switch (err) {7870 fr.seekTo(file_loc.offset) catch |err| switch (err) {
7843 error.Canceled => |e| return e,7871 error.Canceled => |e| return e,
7844 else => |e| return diags.fail("failed to read input section '{s}' from \"{f}{f}\": {t}", .{7872 else => |e| return diags.fail("failed to read input section '{s}' from \"{f}{f}\": {t}", .{
7845 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf),7873 elf.getNode(isi.node(elf).parent(&elf.mf).unwrap().?).section.name(elf).slice(elf),
7846 path.fmtEscapeString(),7874 path.fmtEscapeString(),
7847 fmtMemberString(ii.member(elf)),7875 fmtMemberString(ii.member(elf)),
7848 e,7876 e,
...@@ -7853,7 +7881,7 @@ fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void {...@@ -7853,7 +7881,7 @@ fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void {
7853 defer nw.deinit();7881 defer nw.deinit();
7854 const n_bytes = nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) catch |err| switch (err) {7882 const n_bytes = nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) catch |err| switch (err) {
7855 error.ReadFailed => return diags.fail("failed to read input section '{s}' from \"{f}{f}\": {t}", .{7883 error.ReadFailed => return diags.fail("failed to read input section '{s}' from \"{f}{f}\": {t}", .{
7856 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf),7884 elf.getNode(isi.node(elf).parent(&elf.mf).unwrap().?).section.name(elf).slice(elf),
7857 path.fmtEscapeString(),7885 path.fmtEscapeString(),
7858 fmtMemberString(ii.member(elf)),7886 fmtMemberString(ii.member(elf)),
7859 fr.err orelse (fr.seek_err orelse fr.size_err.?),7887 fr.err orelse (fr.seek_err orelse fr.size_err.?),
...@@ -7861,7 +7889,7 @@ fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void {...@@ -7861,7 +7889,7 @@ fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void {
7861 error.WriteFailed => return nw.err.?,7889 error.WriteFailed => return nw.err.?,
7862 };7890 };
7863 if (n_bytes != file_loc.size) return diags.fail("failed to read input section '{s}' from \"{f}{f}\": unexpected eof", .{7891 if (n_bytes != file_loc.size) return diags.fail("failed to read input section '{s}' from \"{f}{f}\": unexpected eof", .{
7864 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf),7892 elf.getNode(isi.node(elf).parent(&elf.mf).unwrap().?).section.name(elf).slice(elf),
7865 path.fmtEscapeString(),7893 path.fmtEscapeString(),
7866 fmtMemberString(ii.member(elf)),7894 fmtMemberString(ii.member(elf)),
7867 });7895 });
...@@ -7888,8 +7916,10 @@ fn flushElfOffset(elf: *Elf, ni: MappedFile.Node.Index) void {...@@ -7888,8 +7916,10 @@ fn flushElfOffset(elf: *Elf, ni: MappedFile.Node.Index) void {
7888 }7916 }
7889 },7917 },
7890 }7918 }
7891 var child_it = ni.children(&elf.mf);7919 var child_oni = ni.first(&elf.mf);
7892 while (child_it.next()) |child_ni| elf.flushElfOffset(child_ni);7920 while (child_oni.unwrap()) |child_ni| : (child_oni = child_ni.next(&elf.mf)) {
7921 elf.flushElfOffset(child_ni);
7922 }
7893 },7923 },
7894 .section => |shndx| switch (elf.shdrPtr(shndx)) {7924 .section => |shndx| switch (elf.shdrPtr(shndx)) {
7895 inline else => |shdr| elf.targetStore(&shdr.offset, @intCast(elf_offset)),7925 inline else => |shdr| elf.targetStore(&shdr.offset, @intCast(elf_offset)),
...@@ -7906,7 +7936,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -7906,7 +7936,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
79067936
7907 switch (elf.getNode(ni)) {7937 switch (elf.getNode(ni)) {
7908 .archive, .archive_header => unreachable,7938 .archive, .archive_header => unreachable,
7909 .elf => {},7939 .archive_elf_footer, .elf => {},
7910 .ehdr, .shdr => elf.flushElfOffset(ni),7940 .ehdr, .shdr => elf.flushElfOffset(ni),
7911 .segment => |phndx| {7941 .segment => |phndx| {
7912 elf.flushElfOffset(ni);7942 elf.flushElfOffset(ni);
...@@ -7994,7 +8024,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -7994,7 +8024,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
7994 const ii = isi.input(elf);8024 const ii = isi.input(elf);
7995 var lsi, const end_lsi = ii.localSymbolRange(elf);8025 var lsi, const end_lsi = ii.localSymbolRange(elf);
7996 while (lsi != end_lsi) : (lsi = @fromBackingInt(@backingInt(lsi) + 1)) {8026 while (lsi != end_lsi) : (lsi = @fromBackingInt(@backingInt(lsi) + 1)) {
7997 if (lsi.index().ptr(elf).node != ni) continue;8027 if (lsi.index().ptr(elf).node != ni.toOptional()) continue;
7998 const visibility: std.elf.STV = switch (elf.symPtr(lsi.index())) {8028 const visibility: std.elf.STV = switch (elf.symPtr(lsi.index())) {
7999 inline else => |sym| elf.targetLoad(&sym.other).visibility,8029 inline else => |sym| elf.targetLoad(&sym.other).visibility,
8000 };8030 };
...@@ -8079,7 +8109,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -8079,7 +8109,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
8079/// moving or resizing of a segment could reorder them and thereby affect how we handle *future*8109/// moving or resizing of a segment could reorder them and thereby affect how we handle *future*
8080/// changes to segments.8110/// changes to segments.
8081fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Error!void {8111fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Error!void {
8082 const segment_ni = elf.phdrs.items[orig_phndx];8112 const segment_ni = elf.phdrs.items[orig_phndx].unwrap().?;
8083 assert(elf.getNode(segment_ni).segment == orig_phndx);8113 assert(elf.getNode(segment_ni).segment == orig_phndx);
8084 const page_align = elf.targetPageAlign();8114 const page_align = elf.targetPageAlign();
8085 const node_align = segment_ni.alignment(&elf.mf);8115 const node_align = segment_ni.alignment(&elf.mf);
...@@ -8165,7 +8195,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro...@@ -8165,7 +8195,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro
8165 const next_ni = elf.phdrs.items[next_phndx];8195 const next_ni = elf.phdrs.items[next_phndx];
8166 elf.phdrs.items[phndx] = next_ni;8196 elf.phdrs.items[phndx] = next_ni;
8167 elf.nodes.items(.data)[@backingInt(next_ni)] = .{ .segment = phndx };8197 elf.nodes.items(.data)[@backingInt(next_ni)] = .{ .segment = phndx };
8168 elf.phdrs.items[next_phndx] = segment_ni;8198 elf.phdrs.items[next_phndx] = .wrap(segment_ni);
8169 elf.nodes.items(.data)[@backingInt(segment_ni)] = .{ .segment = @intCast(next_phndx) };8199 elf.nodes.items(.data)[@backingInt(segment_ni)] = .{ .segment = @intCast(next_phndx) };
8170 phndx = @intCast(next_phndx);8200 phndx = @intCast(next_phndx);
8171 }8201 }
...@@ -8189,9 +8219,10 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo...@@ -8189,9 +8219,10 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
8189 _, const size = ni.location(&elf.mf).resolve(&elf.mf);8219 _, const size = ni.location(&elf.mf).resolve(&elf.mf);
8190 switch (elf.getNode(ni)) {8220 switch (elf.getNode(ni)) {
8191 .archive => {8221 .archive => {
8192 var child_it = ni.reverseChildren(&elf.mf);8222 if (ni.last(&elf.mf).unwrap()) |last_ni| {
8193 if (child_it.next()) |last_ni| {8223 if (last_ni.prev(&elf.mf).unwrap()) |prev_ni| {
8194 if (child_it.next()) |prev_ni| if (prev_ni.hasNextMoved(&elf.mf)) return;8224 if (prev_ni.hasNextMoved(&elf.mf)) return;
8225 }
8195 const offset, _ = last_ni.location(&elf.mf).resolve(&elf.mf);8226 const offset, _ = last_ni.location(&elf.mf).resolve(&elf.mf);
8196 _ = std.mem.print(&elf.arHdrPtr(last_ni).ar_size, "{d:<10}", .{8227 _ = std.mem.print(&elf.arHdrPtr(last_ni).ar_size, "{d:<10}", .{
8197 size - offset,8228 size - offset,
...@@ -8199,11 +8230,11 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo...@@ -8199,11 +8230,11 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
8199 }8230 }
8200 },8231 },
8201 .archive_header, .elf => {},8232 .archive_header, .elf => {},
8202 .ehdr => unreachable,8233 .ehdr, .archive_elf_footer => unreachable,
8203 .shdr => {},8234 .shdr => {},
8204 .segment => |phndx| switch (elf.phdrSlice()) {8235 .segment => |phndx| switch (elf.phdrSlice()) {
8205 inline else => |phdr| {8236 inline else => |phdr| {
8206 assert(elf.phdrs.items[phndx] == ni);8237 assert(elf.phdrs.items[phndx].unwrap().? == ni);
8207 const ph = &phdr[phndx];8238 const ph = &phdr[phndx];
8208 elf.targetStore(&ph.filesz, @intCast(size));8239 elf.targetStore(&ph.filesz, @intCast(size));
8209 switch (elf.targetLoad(&ph.type)) {8240 switch (elf.targetLoad(&ph.type)) {
...@@ -8284,6 +8315,7 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!...@@ -8284,6 +8315,7 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!
82848315
8285 switch (elf.getNode(ni)) {8316 switch (elf.getNode(ni)) {
8286 .archive,8317 .archive,
8318 .archive_elf_footer,
8287 .ehdr,8319 .ehdr,
8288 .shdr,8320 .shdr,
8289 .segment,8321 .segment,
...@@ -8301,51 +8333,45 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!...@@ -8301,51 +8333,45 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!
8301 break :member_offset switch (tag) {8333 break :member_offset switch (tag) {
8302 else => unreachable,8334 else => unreachable,
8303 .archive_header => .{ offset + std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr), true },8335 .archive_header => .{ offset + std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr), true },
8304 .elf, .input_member => .{ offset, switch (ni.prev(&elf.mf)) {8336 .elf, .input_member => .{ offset, !ni.prev(&elf.mf).unwrap().?.hasNextMoved(&elf.mf) },
8305 .none => unreachable,
8306 else => |prev_ni| !prev_ni.hasNextMoved(&elf.mf),
8307 } },
8308 };8337 };
8309 };8338 };
8310 const member_size = member_end: switch (ni.next(&elf.mf)) {8339 const member_size = if (ni.next(&elf.mf).unwrap()) |next_ni| member_size: {
8311 else => |next_ni| {8340 const next_offset, _ = next_ni.location(&elf.mf).resolve(&elf.mf);
8312 const next_offset, _ = next_ni.location(&elf.mf).resolve(&elf.mf);8341 const next_member_size = if (next_ni.next(&elf.mf).unwrap()) |next_next_ni| next_member_size: {
8313 const next_member_size = next_member_end: switch (next_ni.next(&elf.mf)) {8342 const next_next_offset, _ = next_next_ni.location(&elf.mf).resolve(&elf.mf);
8314 else => |next_next_ni| {8343 const next_member_end = next_next_offset - @sizeOf(std.elf.ar_hdr);
8315 const next_next_offset, _ = next_next_ni.location(&elf.mf).resolve(&elf.mf);8344 break :next_member_size next_member_end - next_offset;
8316 break :next_member_end next_next_offset - @sizeOf(std.elf.ar_hdr);8345 } else next_member_size: {
8317 },8346 _, const parent_size = ni.parent(&elf.mf).unwrap().?.location(&elf.mf).resolve(&elf.mf);
8318 .none => {8347 const next_member_end = parent_size;
8319 _, const parent_size =8348 break :next_member_size next_member_end - next_offset;
8320 ni.parent(&elf.mf).location(&elf.mf).resolve(&elf.mf);8349 };
8321 break :next_member_end parent_size;8350 const ar_hdr = elf.arHdrPtr(next_ni);
8322 },8351 var name_buf: [16]u8 = undefined;
8323 } - next_offset;8352 _ = std.mem.print(&ar_hdr.ar_name, "{s:<16}", .{
8324 const ar_hdr = elf.arHdrPtr(next_ni);8353 switch (elf.getNode(next_ni)) {
8325 var name_buf: [16]u8 = undefined;8354 else => unreachable,
8326 _ = std.mem.print(&ar_hdr.ar_name, "{s:<16}", .{8355 .elf => std.mem.print(&name_buf, "{s}_zcu.o/", .{elf.base.comp.root_name}),
8327 switch (elf.getNode(next_ni)) {8356 .input_member => |ii| std.mem.print(&name_buf, "{s}/", .{
8328 else => unreachable,8357 std.fs.path.basename(ii.path(elf).sub_path),
8329 .elf => std.mem.print(&name_buf, "{s}_zcu.o/", .{elf.base.comp.root_name}),8358 }),
8330 .input_member => |ii| std.mem.print(&name_buf, "{s}/", .{8359 } catch @panic("TODO: long archive member names"),
8331 std.fs.path.basename(ii.path(elf).sub_path),8360 }) catch @panic("TODO: long archive member names");
8332 }),8361 ar_hdr.ar_date = "0 ".*;
8333 } catch @panic("TODO: long archive member names"),8362 ar_hdr.ar_uid = "0 ".*;
8334 }) catch @panic("TODO: long archive member names");8363 ar_hdr.ar_gid = "0 ".*;
8335 ar_hdr.ar_date = "0 ".*;8364 ar_hdr.ar_mode = "644 ".*;
8336 ar_hdr.ar_uid = "0 ".*;8365 _ = std.mem.print(&ar_hdr.ar_size, "{d:<10}", .{next_member_size}) catch
8337 ar_hdr.ar_gid = "0 ".*;8366 @panic("archive member too large");
8338 ar_hdr.ar_mode = "644 ".*;8367 ar_hdr.ar_fmag = std.elf.ARFMAG.*;
8339 _ = std.mem.print(&ar_hdr.ar_size, "{d:<10}", .{next_member_size}) catch8368 const member_end = next_offset - @sizeOf(std.elf.ar_hdr);
8340 @panic("archive member too large");8369 break :member_size member_end - member_offset;
8341 ar_hdr.ar_fmag = std.elf.ARFMAG.*;8370 } else member_size: {
8342 break :member_end next_offset - @sizeOf(std.elf.ar_hdr);8371 _, const parent_size = ni.parent(&elf.mf).unwrap().?.location(&elf.mf).resolve(&elf.mf);
8343 },8372 const member_end = parent_size;
8344 .none => {8373 break :member_size member_end - member_offset;
8345 _, const parent_size = ni.parent(&elf.mf).location(&elf.mf).resolve(&elf.mf);8374 };
8346 break :member_end parent_size;
8347 },
8348 } - member_offset;
8349 if (update_size) _ = std.mem.print(&elf.arHdrPtr(ni).ar_size, "{d:<10}", .{8375 if (update_size) _ = std.mem.print(&elf.arHdrPtr(ni).ar_size, "{d:<10}", .{
8350 member_size,8376 member_size,
8351 }) catch @panic("archive member too large");8377 }) catch @panic("archive member too large");
...@@ -8735,8 +8761,6 @@ fn updateExportInner(...@@ -8735,8 +8761,6 @@ fn updateExportInner(
8735 .uav => |uav| (try elf.uavMapIndex(uav, .none)).symbol(elf),8761 .uav => |uav| (try elf.uavMapIndex(uav, .none)).symbol(elf),
8736 };8762 };
87378763
8738 try elf.ensureElfNodeSize();
8739
8740 // Initialize the global symbol with the same values that the local one currently has. If the8764 // Initialize the global symbol with the same values that the local one currently has. If the
8741 // NAV/UAV is updated, then `updateNavInner` or `genUav` will update the global symbol sizes,8765 // NAV/UAV is updated, then `updateNavInner` or `genUav` will update the global symbol sizes,
8742 // and `flushMoved` will update their values.8766 // and `flushMoved` will update their values.
...@@ -8775,12 +8799,13 @@ fn updateExportInner(...@@ -8775,12 +8799,13 @@ fn updateExportInner(
8775 // only emitting this error if the symbol we're conflicting with comes from an input8799 // only emitting this error if the symbol we're conflicting with comes from an input
8776 // section (as opposed to the ZCU).8800 // section (as opposed to the ZCU).
8777 const conflicting_global = elf.globalByName(try elf.string(.strtab, name)).?;8801 const conflicting_global = elf.globalByName(try elf.string(.strtab, name)).?;
8778 const conflicting_node = conflicting_global.symtab_index.ptr(elf).node;8802 if (conflicting_global.symtab_index.ptr(elf).node.unwrap()) |conflicting_node| {
8779 if (elf.getNode(conflicting_node) == .input_section) {8803 if (elf.getNode(conflicting_node) == .input_section) {
8780 return elf.base.comp.link_diags.fail(8804 return elf.base.comp.link_diags.fail(
8781 "multiple definitions of '{s}'",8805 "multiple definitions of '{s}'",
8782 .{name},8806 .{name},
8783 );8807 );
8808 }
8784 }8809 }
8785 },8810 },
8786 };8811 };
...@@ -8842,7 +8867,7 @@ pub fn printNode(...@@ -8842,7 +8867,7 @@ pub fn printNode(
8842 try w.print("({f}{f}, {s})", .{8867 try w.print("({f}{f}, {s})", .{
8843 ii.path(elf).fmtEscapeString(),8868 ii.path(elf).fmtEscapeString(),
8844 fmtMemberString(ii.member(elf)),8869 fmtMemberString(ii.member(elf)),
8845 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf),8870 elf.getNode(isi.node(elf).parent(&elf.mf).unwrap().?).section.name(elf).slice(elf),
8846 });8871 });
8847 },8872 },
8848 .copied_global => |name| try w.print("(copy:{s})", .{name.slice(elf)}),8873 .copied_global => |name| try w.print("(copy:{s})", .{name.slice(elf)}),
...@@ -8873,25 +8898,27 @@ pub fn printNode(...@@ -8873,25 +8898,27 @@ pub fn printNode(
8873 {8898 {
8874 const mf_node = &elf.mf.nodes.items[@backingInt(ni)];8899 const mf_node = &elf.mf.nodes.items[@backingInt(ni)];
8875 const off, const size = mf_node.location().resolve(&elf.mf);8900 const off, const size = mf_node.location().resolve(&elf.mf);
8876 try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x}{s}{s}{s}{s}{s}\n", .{8901 try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x} {t}{s}{s}{s}{s}\n", .{
8877 @backingInt(ni),8902 @backingInt(ni),
8878 off,8903 off,
8879 size,8904 size,
8880 mf_node.flags.alignment.toByteUnits(),8905 mf_node.flags.alignment.toByteUnits(),
8881 if (mf_node.flags.fixed) " fixed" else "",8906 mf_node.flags.position,
8882 if (mf_node.flags.moved) " moved" else "",8907 if (mf_node.flags.moved) " moved" else "",
8883 if (mf_node.flags.next_moved) " next_moved" else "",8908 if (mf_node.flags.next_moved) " next_moved" else "",
8884 if (mf_node.flags.resized) " resized" else "",8909 if (mf_node.flags.resized) " resized" else "",
8885 if (mf_node.flags.has_content) " has_content" else "",8910 if (mf_node.flags.has_content) " has_content" else "",
8886 });8911 });
8887 }8912 }
8888 var leaf = true;8913 if (ni.first(&elf.mf).unwrap()) |first_ni| {
8889 var child_it = ni.children(&elf.mf);8914 // non-leaf, just print children
8890 while (child_it.next()) |child_ni| {8915 var child_ni = first_ni;
8891 leaf = false;8916 while (true) {
8892 try elf.printNode(tid, w, child_ni, indent + 1);8917 try elf.printNode(tid, w, child_ni, indent + 1);
8918 child_ni = child_ni.next(&elf.mf).unwrap() orelse break;
8919 }
8920 return;
8893 }8921 }
8894 if (!leaf) return;
8895 const file_loc = ni.fileLocation(&elf.mf, false);8922 const file_loc = ni.fileLocation(&elf.mf, false);
8896 var address = file_loc.offset;8923 var address = file_loc.offset;
8897 if (file_loc.size == 0) {8924 if (file_loc.size == 0) {
...@@ -8916,16 +8943,16 @@ pub fn printNode(...@@ -8916,16 +8943,16 @@ pub fn printNode(
8916 }8943 }
8917}8944}
89188945
8919fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: std.mem.Alignment) Error!void {8946fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: Alignment) Error!void {
8920 const gpa = elf.base.comp.gpa;8947 const gpa = elf.base.comp.gpa;
8921 // We need to loop through parent nodes because segments may be nested (e.g. a PT_TLS segment8948 // We need to loop through parent nodes because segments may be nested (e.g. a PT_TLS segment
8922 // inside a PT_LOAD segment).8949 // inside a PT_LOAD segment).
8923 var phndx = start_phndx;8950 var phndx = start_phndx;
8924 while (true) {8951 while (true) {
8925 // Align the actual node8952 // Align the actual node
8926 const seg_ni = elf.phdrs.items[phndx];8953 const seg_ni = elf.phdrs.items[phndx].unwrap().?;
8927 if (min_align.compare(.gt, seg_ni.alignment(&elf.mf))) {8954 if (min_align.compare(.gt, seg_ni.alignment(&elf.mf))) {
8928 try seg_ni.realign(&elf.mf, gpa, min_align, .{});8955 try seg_ni.realign(&elf.mf, gpa, min_align);
8929 }8956 }
8930 // Update the phdr `@"align"` field if necessary8957 // Update the phdr `@"align"` field if necessary
8931 switch (elf.phdrSlice()) {8958 switch (elf.phdrSlice()) {
...@@ -8948,7 +8975,7 @@ fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: std.mem.Alignmen...@@ -8948,7 +8975,7 @@ fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: std.mem.Alignmen
8948 },8975 },
8949 }8976 }
8950 // Continue on to the parent segment, if any8977 // Continue on to the parent segment, if any
8951 switch (elf.getNode(seg_ni.parent(&elf.mf))) {8978 switch (elf.getNode(seg_ni.parent(&elf.mf).unwrap().?)) {
8952 .segment => |parent_phndx| phndx = parent_phndx,8979 .segment => |parent_phndx| phndx = parent_phndx,
8953 .elf => return,8980 .elf => return,
8954 else => unreachable,8981 else => unreachable,
...@@ -8956,26 +8983,6 @@ fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: std.mem.Alignmen...@@ -8956,26 +8983,6 @@ fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: std.mem.Alignmen
8956 }8983 }
8957}8984}
89588985
8959/// Must be called deterministically after any call to `MappedFile.Node.Index.resize`
8960/// (of `elf.ni.elf` or one of its children) before any possible calls to `idle`.
8961fn ensureElfNodeSize(elf: *Elf) MappedFile.Error!void {
8962 if (elf.ni.elf == MappedFile.Node.Index.root) return;
8963 var child_it = elf.ni.elf.reverseChildren(&elf.mf);
8964 const last_end = if (child_it.next()) |last_ni| last_end: {
8965 const last_offset, const last_size = last_ni.location(&elf.mf).resolve(&elf.mf);
8966 break :last_end last_offset + last_size;
8967 } else 0;
8968 try elf.ensureNodeSize(elf.ni.elf, last_end + @sizeOf(std.elf.ar_hdr));
8969}
8970
8971fn ensureNodeSize(elf: *Elf, node: MappedFile.Node.Index, need_size: u64) MappedFile.Error!void {
8972 _, const node_size = node.location(&elf.mf).resolve(&elf.mf);
8973 if (need_size <= node_size) return;
8974 const gpa = elf.base.comp.gpa;
8975 const new_size = need_size + need_size / MappedFile.growth_factor;
8976 try node.resize(&elf.mf, gpa, new_size);
8977}
8978
8979/// If `sym` has a PLT entry, returns the address of that entry (specifically, the address which a8986/// If `sym` has a PLT entry, returns the address of that entry (specifically, the address which a
8980/// branch to the PLT should target). If `sym` does not have a PLT entry, returns `null`.8987/// branch to the PLT should target). If `sym` does not have a PLT entry, returns `null`.
8981fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {8988fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {
src/link/MappedFile.zig+1906-917
...@@ -13,14 +13,14 @@ const windows = std.os.windows;...@@ -13,14 +13,14 @@ const windows = std.os.windows;
1313
14io: Io,14io: Io,
15flags: packed struct {15flags: packed struct {
16 block_size: std.mem.Alignment,16 block_size: Alignment,
17 copy_file_range_unsupported: bool,17 copy_file_range_unsupported: bool,
18 fallocate_punch_hole_unsupported: bool,18 fallocate_punch_hole_unsupported: bool,
19 fallocate_insert_range_unsupported: bool,19 fallocate_insert_range_unsupported: bool,
20},20},
21memory_map: Io.File.MemoryMap,21memory_map: Io.File.MemoryMap,
22nodes: std.ArrayList(Node),22nodes: std.ArrayList(Node),
23free_ni: Node.Index,23free_ni: Node.Index.Optional,
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`.26/// This progress node's estimated total items is increased once for each node appended to `updates`.
...@@ -62,6 +62,94 @@ pub const Error = Allocator.Error || Io.Cancelable || error{...@@ -62,6 +62,94 @@ pub const Error = Allocator.Error || Io.Cancelable || error{
62 MappedFileIo,62 MappedFileIo,
63};63};
6464
65/// This separate `Alignment` type exists because neither of the other options is really suitable:
66///
67/// * `std.mem.Alignment` is based on `usize`, which---while technically okay since the file is
68/// memory-mapped---is in practice very annoying to work with in linker implementations
69///
70/// * `InternPool.Alignment` is based on `u64`, which is better, but it has the value `.none`, which
71/// is also really annoying to handle, because no alignment is ever nullable in this API
72///
73/// At some point we should probably just change `InternPool.Alignment` to be non-optional, and add
74/// a new `InternPool.Alignment.Optional` type for the case where it can actually be `.none`. At
75/// that point we can transition this code to using `InternPool.Alignment` (although it should
76/// probably be namespaced elsewhere, it has nothing to do with the `InternPool`!).
77pub const Alignment = enum(u6) {
78 @"1" = 0,
79 @"2" = 1,
80 @"4" = 2,
81 @"8" = 3,
82 @"16" = 4,
83 @"32" = 5,
84 @"64" = 6,
85 _,
86
87 pub fn fromIp(a: @import("../InternPool.zig").Alignment) Alignment {
88 assert(a != .none);
89 return @bitCast(a);
90 }
91
92 pub fn toLog2Units(a: Alignment) u6 {
93 return @backingInt(a);
94 }
95
96 pub fn fromLog2Units(a: u6) Alignment {
97 return @fromBackingInt(a);
98 }
99
100 pub fn toByteUnits(a: Alignment) u64 {
101 return @as(u64, 1) << @backingInt(a);
102 }
103
104 pub fn fromByteUnits(n: u64) Alignment {
105 assert(std.math.isPowerOfTwo(n));
106 return @fromBackingInt(@intCast(@ctz(n)));
107 }
108
109 pub fn order(lhs: Alignment, rhs: Alignment) std.math.Order {
110 return std.math.order(@backingInt(lhs), @backingInt(rhs));
111 }
112
113 pub fn compare(lhs: Alignment, op: std.math.CompareOperator, rhs: Alignment) bool {
114 return std.math.compare(@backingInt(lhs), op, @backingInt(rhs));
115 }
116
117 pub fn max(lhs: Alignment, rhs: Alignment) Alignment {
118 return @fromBackingInt(@max(@backingInt(lhs), @backingInt(rhs)));
119 }
120
121 pub fn min(lhs: Alignment, rhs: Alignment) Alignment {
122 return @fromBackingInt(@min(@backingInt(lhs), @backingInt(rhs)));
123 }
124
125 pub inline fn of(comptime T: type) Alignment {
126 return comptime .fromByteUnits(@alignOf(T));
127 }
128
129 /// Given that a base address is known to be aligned to `a`, computes the known alignment of
130 /// that base address plus `off`.
131 pub fn offset(a: Alignment, off: u64) Alignment {
132 return .fromLog2Units(@min(a.toLog2Units(), @ctz(off)));
133 }
134
135 /// Align an address forwards to this alignment.
136 pub fn forward(a: Alignment, addr: u64) u64 {
137 const x = (@as(u64, 1) << @backingInt(a)) - 1;
138 return (addr + x) & ~x;
139 }
140
141 /// Align an address backwards to this alignment.
142 pub fn backward(a: Alignment, addr: u64) u64 {
143 const x = (@as(u64, 1) << @backingInt(a)) - 1;
144 return addr & ~x;
145 }
146
147 /// Check if an address is aligned to this amount.
148 pub fn check(a: Alignment, addr: u64) bool {
149 return @ctz(addr) >= @backingInt(a);
150 }
151};
152
65pub fn init(file: Io.File, gpa: Allocator, io: Io) (Allocator.Error || Io.Cancelable || IoError)!MappedFile {153pub fn init(file: Io.File, gpa: Allocator, io: Io) (Allocator.Error || Io.Cancelable || IoError)!MappedFile {
66 var mf: MappedFile = .{154 var mf: MappedFile = .{
67 .io = io,155 .io = io,
...@@ -95,14 +183,42 @@ pub fn init(file: Io.File, gpa: Allocator, io: Io) (Allocator.Error || Io.Cancel...@@ -95,14 +183,42 @@ pub fn init(file: Io.File, gpa: Allocator, io: Io) (Allocator.Error || Io.Cancel
95 .fallocate_insert_range_unsupported = false,183 .fallocate_insert_range_unsupported = false,
96 .fallocate_punch_hole_unsupported = false,184 .fallocate_punch_hole_unsupported = false,
97 };185 };
98 try mf.nodes.ensureUnusedCapacity(gpa, 1);186
99 const root_ni = try mf.addNode(gpa, .{ .add_node = .{187 const root_location: Node.Location = l: {
100 .size = size,188 if (std.math.cast(u32, size)) |small_size| {
101 .alignment = mf.flags.block_size,189 break :l .{ .small = .{ .offset = 0, .size = small_size } };
102 .fixed = true,190 }
103 } });191 try mf.large.appendSlice(gpa, &.{ 0, size });
104 assert(root_ni == Node.Index.root);192 break :l .{ .large = .{ .index = 0 } };
105 try mf.ensureTotalCapacityInner(@intCast(size));193 };
194 try mf.nodes.append(gpa, .{
195 .parent = .none,
196 .prev = .none,
197 .next = .none,
198 .first = .none,
199 .last = .none,
200 .flags = .{
201 .alignment = mf.flags.block_size,
202 .position = .floating,
203 .bubbles_moved = true,
204 .enable_next_moved = false,
205 .location_tag = root_location,
206 .moved = false,
207 .resized = false,
208 .next_moved = false,
209 .has_content = false,
210 },
211 .location_payload = switch (root_location) {
212 .small => |small| .{ .small = small },
213 .large => |large| .{ .large = large },
214 },
215 });
216
217 mf.ensureTotalCapacity(@intCast(size)) catch |err| switch (err) {
218 error.MappedFileIo => return mf.io_err.?,
219 else => |e| return e,
220 };
221
106 return mf;222 return mf;
107}223}
108224
...@@ -117,32 +233,61 @@ pub fn deinit(mf: *MappedFile, gpa: Allocator) void {...@@ -117,32 +233,61 @@ pub fn deinit(mf: *MappedFile, gpa: Allocator) void {
117}233}
118234
119pub const Node = extern struct {235pub const Node = extern struct {
120 parent: Node.Index,236 parent: Node.Index.Optional,
121 prev: Node.Index,237 prev: Node.Index.Optional,
122 next: Node.Index,238 next: Node.Index.Optional,
123 first: Node.Index,239 first: Node.Index.Optional,
124 last: Node.Index,240 last: Node.Index.Optional,
125 flags: Flags,241 flags: Flags,
126 location_payload: Location.Payload,242 location_payload: Location.Payload,
127243
244 /// Any non-leaf node may designate its first N children as "header" nodes. This means that its
245 /// first N children must be densely packed together and positioned at the start of the parent.
246 /// The implementation guarantees that it will never re-order these nodes, nor will it introduce
247 /// padding between them.
248 ///
249 /// Likewise, any non-leaf node may designate its *last* M children as "footer" nodes, which are
250 /// like header nodes except they are positioned at the *end* of the parent rather than the
251 /// start.
252 ///
253 /// Nodes which are neither headers nor footers are called "floating". The implementation is
254 /// always free to re-order floating nodes relative to one another, and to add or remove padding
255 /// between them.
256 pub const Position = enum(u2) {
257 header,
258 footer,
259 floating,
260 };
261
128 pub const Flags = packed struct(u32) {262 pub const Flags = packed struct(u32) {
263 /// While the number of header and footer nodes within a parent node is logically a part of
264 /// that parent, we actually store this information on the child nodes for efficiency: this
265 /// field indicates whether each child is a header node, a footer node, or a floating node.
266 ///
267 /// This value is meaningless for the root node, so is arbitrarily set to `.floating`.
268 position: Position,
269 /// For floating nodes, this node's offset into its parent will always be aligned to this
270 /// boundary. (This is not the case for header and footer nodes due to the requirement that
271 /// they be densely packed against the start/end of the parent node.)
272 ///
273 /// This node's size will also always be aligned to this boundary. (This applies regardless
274 /// of whether this is a floating node, a header node, or a footer node.)
275 alignment: Alignment,
276 /// Whether `moved` events on this node bubble down to children.
277 bubbles_moved: bool,
278 /// Whether `next_moved` events are reported in `updates`.
279 enable_next_moved: bool,
280
129 location_tag: Location.Tag,281 location_tag: Location.Tag,
130 alignment: std.mem.Alignment,
131 /// Whether this node can be moved.
132 fixed: bool,
133 /// Whether this node has been moved.282 /// Whether this node has been moved.
134 moved: bool,283 moved: bool,
135 /// Whether this node has been resized.284 /// Whether this node has been resized.
136 resized: bool,285 resized: bool,
137 /// Whether the next sibling has moved or is a different node.286 /// Whether the next sibling has moved or is a different node.
138 next_moved: bool,287 next_moved: bool,
139 /// Whether this node might contain non-zero bytes.288 /// Whether this node might contain initialized bytes.
140 has_content: bool,289 has_content: bool,
141 /// Whether `moved` events on this node bubble down to children.290 unused: u17 = 0,
142 bubbles_moved: bool,
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,
146 };291 };
147292
148 pub const Location = union(enum(u1)) {293 pub const Location = union(enum(u1)) {
...@@ -179,74 +324,183 @@ pub const Node = extern struct {...@@ -179,74 +324,183 @@ pub const Node = extern struct {
179 }324 }
180 };325 };
181326
327 pub const AddOptions = struct {
328 /// Must be aligned to the given `alignment`.
329 size: u64 = 0,
330 alignment: Alignment = .@"1",
331 bubbles_moved: bool = true,
332 enable_next_moved: bool = false,
333
334 moved: bool = false,
335 resized: bool = false,
336 next_moved: bool = false,
337 };
338
182 pub const Index = enum(u32) {339 pub const Index = enum(u32) {
183 none,340 root,
184 _,341 _,
185342
186 pub const root: Node.Index = .none;343 pub const Optional = enum(u32) {
344 none = std.math.maxInt(u32),
345 _,
346
347 pub fn unwrap(oi: Optional) ?Index {
348 return switch (oi) {
349 _ => @fromBackingInt(@backingInt(oi)),
350 .none => null,
351 };
352 }
353 pub fn wrap(i: Index) Optional {
354 const oi: Optional = @bitCast(i);
355 assert(oi != .none);
356 return oi;
357 }
358 };
187359
188 fn get(ni: Node.Index, mf: *const MappedFile) *Node {360 fn get(ni: Node.Index, mf: *const MappedFile) *Node {
189 return &mf.nodes.items[@backingInt(ni)];361 return &mf.nodes.items[@backingInt(ni)];
190 }362 }
191363
192 pub fn parent(ni: Node.Index, mf: *const MappedFile) Node.Index {364 /// Adds a floating child node to `parent_ni`. Returns the index of the new child.
365 pub fn addFloatingChild(parent_ni: Node.Index, mf: *MappedFile, gpa: Allocator, opts: AddOptions) Error!Node.Index {
366 return mf.addNode(gpa, .{
367 .add_options = opts,
368 .position = .floating,
369 .parent = parent_ni,
370 .prev = parent_ni.lastHeader(mf),
371 });
372 }
373 /// Adds a header child node to `parent_ni`. Returns the index of the new child.
374 ///
375 /// Asserts that `parent_ni` has no existing header children.
376 pub fn addOnlyHeaderChild(parent_ni: Node.Index, mf: *MappedFile, gpa: Allocator, opts: AddOptions) Error!Node.Index {
377 if (parent_ni.first(mf).unwrap()) |first_ni| {
378 assert(first_ni.position(mf) != .header); // `parent_ni` already has a header child
379 }
380 return parent_ni.addHeaderChildAfter(mf, gpa, .none, opts);
381 }
382 /// Adds a header child node to `parent_ni`. Returns the index of the new child.
383 ///
384 /// If `prev_oni` is `.none`, the new child is placed at the very start of the parent,
385 /// before any existing header nodes.
386 ///
387 /// Otherwise, asserts that `prev_oni` is a header node and a child of `parent_ni`, and
388 /// places the new child node immediately after `prev_oni`.
389 pub fn addHeaderChildAfter(parent_ni: Node.Index, mf: *MappedFile, gpa: Allocator, prev_oni: Node.Index.Optional, opts: AddOptions) Error!Node.Index {
390 return mf.addNode(gpa, .{
391 .add_options = opts,
392 .position = .header,
393 .parent = parent_ni,
394 .prev = prev_oni,
395 });
396 }
397 /// Adds a footer child node to `parent_ni`. Returns the index of the new child.
398 ///
399 /// Asserts that `parent_ni` has no existing footer children.
400 pub fn addOnlyFooterChild(parent_ni: Node.Index, mf: *MappedFile, gpa: Allocator, opts: AddOptions) Error!Node.Index {
401 if (parent_ni.last(mf).unwrap()) |last_ni| {
402 assert(last_ni.position(mf) != .footer); // `parent_ni` already has a footer child
403 }
404 return parent_ni.addFooterChildBefore(mf, gpa, .none, opts);
405 }
406 /// Adds a footer child node to `parent_ni`. Returns the index of the new child.
407 ///
408 /// If `next_oni` is `.none`, the new child is placed at the very end of the parent, after
409 /// any existing footer nodes.
410 ///
411 /// Otherwise, asserts that `next_oni` is a footer node and a child of `parent_ni`, and
412 /// places the new child node immediately before `next_oni`.
413 pub fn addFooterChildBefore(parent_ni: Node.Index, mf: *MappedFile, gpa: Allocator, next_oni: Node.Index.Optional, opts: AddOptions) Error!Node.Index {
414 const prev_oni: Node.Index.Optional = prev: {
415 const next_ni = next_oni.unwrap() orelse {
416 break :prev parent_ni.last(mf);
417 };
418 break :prev next_ni.prev(mf);
419 };
420 return mf.addNode(gpa, .{
421 .add_options = opts,
422 .position = .footer,
423 .parent = parent_ni,
424 .prev = prev_oni,
425 });
426 }
427
428 /// Alias for `Optional.wrap`, provided for convenience when a result type is not available.
429 pub const toOptional = Optional.wrap;
430
431 pub fn parent(ni: Node.Index, mf: *const MappedFile) Node.Index.Optional {
193 return ni.get(mf).parent;432 return ni.get(mf).parent;
194 }433 }
195434
196 pub fn next(ni: Node.Index, mf: *const MappedFile) Node.Index {435 pub fn first(ni: Node.Index, mf: *const MappedFile) Node.Index.Optional {
436 return ni.get(mf).first;
437 }
438
439 pub fn last(ni: Node.Index, mf: *const MappedFile) Node.Index.Optional {
440 return ni.get(mf).last;
441 }
442
443 fn lastHeader(ni: Node.Index, mf: *const MappedFile) Node.Index.Optional {
444 var header_ni = ni.first(mf).unwrap() orelse return .none;
445 if (header_ni.position(mf) != .header) return .none;
446 while (true) {
447 const next_ni = header_ni.next(mf).unwrap() orelse break;
448 if (next_ni.position(mf) != .header) break;
449 header_ni = next_ni;
450 }
451 return .wrap(header_ni);
452 }
453 fn firstFooter(ni: Node.Index, mf: *const MappedFile) Node.Index.Optional {
454 var footer_ni = ni.last(mf).unwrap() orelse return .none;
455 if (footer_ni.position(mf) != .footer) return .none;
456 while (true) {
457 const prev_ni = footer_ni.prev(mf).unwrap() orelse break;
458 if (prev_ni.position(mf) != .footer) break;
459 footer_ni = prev_ni;
460 }
461 return .wrap(footer_ni);
462 }
463
464 /// Asserts that `ni` is not `.root`, because `Position` is meaningless for the root node.
465 pub fn position(ni: Node.Index, mf: *const MappedFile) Node.Position {
466 assert(ni != .root);
467 return ni.get(mf).flags.position;
468 }
469
470 pub fn next(ni: Node.Index, mf: *const MappedFile) Node.Index.Optional {
197 return ni.get(mf).next;471 return ni.get(mf).next;
198 }472 }
199 fn setNext(473 fn setNext(
200 prev_ni: Node.Index,474 ni: Node.Index,
201 gpa: Allocator,475 gpa: Allocator,
202 next_ni: Node.Index,476 next_ni: Node.Index.Optional,
203 mf: *MappedFile,477 mf: *MappedFile,
204 ) Allocator.Error!void {478 ) Allocator.Error!void {
205 assert(prev_ni != .none);479 const next_ptr = &ni.get(mf).next;
206 const prev_next = &prev_ni.get(mf).next;480 if (next_ptr.* == next_ni) return;
207 if (prev_next.* == next_ni) return;481 next_ptr.* = next_ni;
208 prev_next.* = next_ni;482 try ni.nextMoved(gpa, mf);
209 try prev_ni.nextMoved(gpa, mf);
210 }483 }
211484
212 pub fn prev(ni: Node.Index, mf: *const MappedFile) Node.Index {485 pub fn prev(ni: Node.Index, mf: *const MappedFile) Node.Index.Optional {
213 return ni.get(mf).prev;486 return ni.get(mf).prev;
214 }487 }
215488
216 pub fn ChildIterator(comptime direction: enum { prev, next }) type {
217 return struct {
218 mf: *const MappedFile,
219 ni: Node.Index,
220 pub fn next(it: *@This()) ?Node.Index {
221 const ni = it.ni;
222 if (ni == .none) return null;
223 it.ni = @field(ni.get(it.mf), @tagName(direction));
224 return ni;
225 }
226 };
227 }
228 pub fn children(ni: Node.Index, mf: *const MappedFile) ChildIterator(.next) {
229 return .{ .mf = mf, .ni = ni.get(mf).first };
230 }
231 pub fn reverseChildren(ni: Node.Index, mf: *const MappedFile) ChildIterator(.prev) {
232 return .{ .mf = mf, .ni = ni.get(mf).last };
233 }
234
235 pub fn childrenMoved(ni: Node.Index, gpa: Allocator, mf: *MappedFile) Allocator.Error!void {489 pub fn childrenMoved(ni: Node.Index, gpa: Allocator, mf: *MappedFile) Allocator.Error!void {
236 var child_ni = ni.get(mf).last;490 var child_oni = ni.get(mf).last;
237 while (child_ni != .none) {491 while (child_oni.unwrap()) |child_ni| {
238 try child_ni.moved(gpa, mf);492 try child_ni.moved(gpa, mf);
239 child_ni = child_ni.get(mf).prev;493 child_oni = child_ni.get(mf).prev;
240 }494 }
241 }495 }
242496
243 pub fn hasMoved(ni: Node.Index, mf: *const MappedFile) bool {497 pub fn hasMoved(ni: Node.Index, mf: *const MappedFile) bool {
244 var parent_ni = ni;498 var parent_ni = ni;
245 while (parent_ni != Node.Index.root) {499 while (parent_ni != .root) {
246 const parent_node = parent_ni.get(mf);500 const parent_node = parent_ni.get(mf);
247 if (!parent_node.flags.bubbles_moved) break;501 if (!parent_node.flags.bubbles_moved) break;
248 if (parent_node.flags.moved) return true;502 if (parent_node.flags.moved) return true;
249 parent_ni = parent_node.parent;503 parent_ni = parent_node.parent.unwrap().?;
250 }504 }
251 return false;505 return false;
252 }506 }
...@@ -263,9 +517,8 @@ pub const Node = extern struct {...@@ -263,9 +517,8 @@ pub const Node = extern struct {
263 if (ni.hasMoved(mf)) return;517 if (ni.hasMoved(mf)) return;
264 const node = ni.get(mf);518 const node = ni.get(mf);
265 node.flags.moved = true;519 node.flags.moved = true;
266 switch (node.prev) {520 if (node.prev.unwrap()) |prev_ni| {
267 .none => {},521 prev_ni.nextMovedAssumeCapacity(mf);
268 else => |prev_ni| prev_ni.nextMovedAssumeCapacity(mf),
269 }522 }
270 if (node.flags.resized or node.flags.next_moved) return;523 if (node.flags.resized or node.flags.next_moved) return;
271 mf.updates.appendAssumeCapacity(ni);524 mf.updates.appendAssumeCapacity(ni);
...@@ -314,12 +567,18 @@ pub const Node = extern struct {...@@ -314,12 +567,18 @@ pub const Node = extern struct {
314 mf.update_prog_node.increaseEstimatedTotalItems(1);567 mf.update_prog_node.increaseEstimatedTotalItems(1);
315 }568 }
316569
317 pub fn alignment(ni: Node.Index, mf: *const MappedFile) std.mem.Alignment {570 pub fn alignment(ni: Node.Index, mf: *const MappedFile) Alignment {
318 return ni.get(mf).flags.alignment;571 return ni.get(mf).flags.alignment;
319 }572 }
320573
321 fn setLocationAssumeCapacity(ni: Node.Index, mf: *MappedFile, offset: u64, size: u64) void {574 fn setLocation(ni: Node.Index, mf: *MappedFile, gpa: Allocator, offset: u64, size: u64) Allocator.Error!void {
575 try mf.large.ensureUnusedCapacity(gpa, 2);
576 try mf.updates.ensureUnusedCapacity(gpa, 2);
322 const node = ni.get(mf);577 const node = ni.get(mf);
578 if (node.flags.position == .floating) {
579 assert(node.flags.alignment.check(offset));
580 }
581 assert(node.flags.alignment.check(size));
323 if (size == 0) node.flags.has_content = false;582 if (size == 0) node.flags.has_content = false;
324 switch (node.location()) {583 switch (node.location()) {
325 .small => |small| {584 .small => |small| {
...@@ -361,8 +620,11 @@ pub const Node = extern struct {...@@ -361,8 +620,11 @@ pub const Node = extern struct {
361 while (true) {620 while (true) {
362 const parent_node = parent_ni.get(mf);621 const parent_node = parent_ni.get(mf);
363 if (set_has_content) parent_node.flags.has_content = true;622 if (set_has_content) parent_node.flags.has_content = true;
364 if (parent_ni == .none) break;623 if (parent_ni == .root) {
365 parent_ni = parent_node.parent;624 assert(parent_node.parent == .none);
625 break;
626 }
627 parent_ni = parent_node.parent.unwrap().?;
366 const parent_offset, _ = parent_ni.location(mf).resolve(mf);628 const parent_offset, _ = parent_ni.location(mf).resolve(mf);
367 offset += parent_offset;629 offset += parent_offset;
368 }630 }
...@@ -379,62 +641,46 @@ pub const Node = extern struct {...@@ -379,62 +641,46 @@ pub const Node = extern struct {
379 return mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)];641 return mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)];
380 }642 }
381643
382 pub fn resize(ni: Node.Index, mf: *MappedFile, gpa: Allocator, size: u64) Error!void {644 /// Ensures that the size of `ni` is at least `min_size`. Valid for any node.
383 mf.resizeNode(gpa, ni, size) catch |err| switch (err) {645 ///
384 error.OutOfMemory,646 /// Applies `growth_factor` if necessary (so the caller should *not* apply `growth_factor`).
385 error.Canceled,647 pub fn ensureMinimumSize(ni: Node.Index, mf: *MappedFile, gpa: Allocator, min_size: u64) Error!void {
386 => |e| return e,648 _, const current_size = ni.location(mf).resolve(mf);
387 else => |e| {649 if (current_size >= min_size) return;
388 mf.io_err = e;650 const new_size = ni.alignment(mf).forward(min_size +| min_size / growth_factor);
389 return error.MappedFileIo;651 try mf.growNode(gpa, ni, new_size, .minimum);
390 },652 mf.updateWriters();
391 };
392 var writers_it = mf.writers.first;
393 while (writers_it) |writer_node| : (writers_it = writer_node.next) {
394 const w: *Node.Writer = @fieldParentPtr("writer_node", writer_node);
395 w.interface.buffer = w.ni.slice(mf);
396 }
397 }653 }
398654
399 pub const RealignNodeOptions = struct {655 /// Sets the size of `ni` to exactly `size`.
400 /// Shift the node backwards if possible656 ///
401 try_backwards: bool = false,657 /// Asserts that `ni` is a leaf node, i.e. has no children.
402 };658 ///
403659 /// Asserts that `size` is aligned to `ni.alignment(mf)`.
404 /// Moves and expands a node such that its offset and size are aligned to `new_alignment`.660 pub fn resizeLeaf(ni: Node.Index, mf: *MappedFile, gpa: Allocator, size: u64) Error!void {
405 /// Asserts that `ni` is not `Node.Index.root`.661 assert(ni.first(mf) == .none);
406 pub fn realign(662 // The alignment of `size` is asserted by `shrinkLeafNode` and `growNode`.
407 ni: Node.Index,663 _, const old_size = ni.location(mf).resolve(mf);
408 mf: *MappedFile,664 switch (std.math.order(size, old_size)) {
409 gpa: Allocator,665 .lt => try mf.shrinkLeafNode(gpa, ni, size),
410 new_alignment: std.mem.Alignment,666 .eq => {}, // `old_size` must be well-aligned, so `size` is too
411 opts: RealignNodeOptions,667 .gt => try mf.growNode(gpa, ni, size, .exact),
412 ) Error!void {668 }
413 mf.realignNode(gpa, ni, new_alignment, opts) catch |err| switch (err) {
414 error.OutOfMemory,
415 error.Canceled,
416 => |e| return e,
417 else => |e| {
418 mf.io_err = e;
419 return error.MappedFileIo;
420 },
421 };
422 mf.updateWriters();669 mf.updateWriters();
423 }670 }
424671
425 /// Shrink a node to `size`, exactly.672 /// Updates a node's alignment to exactly `new_alignment`. Valid for any node.
426 /// Asserts that the new size can contain all the children.673 ///
427 /// If `shift_next` is set, then the following node is shifted backwards into674 /// If the node's current offset or size is not sufficiently aligned, it will be moved
428 /// the free space as much as alignment allows.675 /// and/or resized to match the new alignment. The node's size may be increased by any
429 /// Asserts that `size` is >= the end of the last child node.676 /// amount, as if `ensureMinimumSize` were used.
430 pub fn shrink(677 pub fn realign(
431 ni: Node.Index,678 ni: Node.Index,
432 mf: *MappedFile,679 mf: *MappedFile,
433 gpa: Allocator,680 gpa: Allocator,
434 size: u64,681 new_alignment: Alignment,
435 shift_next: bool,
436 ) Error!void {682 ) Error!void {
437 try mf.shrinkNode(gpa, ni, size, shift_next);683 try mf.realignNode(gpa, ni, new_alignment);
438 mf.updateWriters();684 mf.updateWriters();
439 }685 }
440686
...@@ -538,16 +784,9 @@ pub const Node = extern struct {...@@ -538,16 +784,9 @@ pub const Node = extern struct {
538 file_reader.pos,784 file_reader.pos,
539 w.ni.fileLocation(w.mf, true).offset + interface.end,785 w.ni.fileLocation(w.mf, true).offset + interface.end,
540 limit.minInt(interface.unusedCapacityLen()),786 limit.minInt(interface.unusedCapacityLen()),
541 ) catch |err| switch (err) {787 ) catch |err| {
542 error.Canceled => |e| {788 w.err = err;
543 w.err = e;789 return error.WriteFailed;
544 return error.WriteFailed;
545 },
546 else => |e| {
547 w.mf.io_err = e;
548 w.err = error.MappedFileIo;
549 return error.WriteFailed;
550 },
551 });790 });
552 if (n == 0) return error.Unimplemented;791 if (n == 0) return error.Unimplemented;
553 file_reader.pos += n;792 file_reader.pos += n;
...@@ -574,10 +813,8 @@ pub const Node = extern struct {...@@ -574,10 +813,8 @@ pub const Node = extern struct {
574 unused_capacity: usize,813 unused_capacity: usize,
575 ) Io.Writer.Error!void {814 ) Io.Writer.Error!void {
576 _ = preserve;815 _ = preserve;
577 const total_capacity = interface.end + unused_capacity;
578 if (interface.buffer.len >= total_capacity) return;
579 const w: *Writer = @fieldParentPtr("interface", interface);816 const w: *Writer = @fieldParentPtr("interface", interface);
580 w.ni.resize(w.mf, w.gpa, total_capacity +| total_capacity / growth_factor) catch |err| {817 w.ni.ensureMinimumSize(w.mf, w.gpa, interface.end + unused_capacity) catch |err| {
581 w.err = err;818 w.err = err;
582 return error.WriteFailed;819 return error.WriteFailed;
583 };820 };
...@@ -585,617 +822,1265 @@ pub const Node = extern struct {...@@ -585,617 +822,1265 @@ pub const Node = extern struct {
585 };822 };
586823
587 comptime {824 comptime {
588 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Node) == 32);825 if (!std.debug.runtime_safety) assert(@sizeOf(Node) == 32);
589 }826 }
590};827};
591828
829/// Asserts that `opts.position` is compatible with `opts.prev` (i.e. that this addition will not
830/// violate the requirement that header nodes come before floating nodes come before footer nodes).
592fn addNode(mf: *MappedFile, gpa: Allocator, opts: struct {831fn addNode(mf: *MappedFile, gpa: Allocator, opts: struct {
593 parent: Node.Index = .none,832 add_options: Node.AddOptions,
594 prev: Node.Index = .none,833 position: Node.Position,
595 next: Node.Index = .none,834 parent: Node.Index,
596 offset: u64 = 0,835 /// If `position == .floating`, this is just used as an initial value, and may be immediately
597 add_node: AddNodeOptions,836 /// replaced when finding a location for this node. In this case, it is still necessary that
598}) (Allocator.Error || Io.Cancelable || IoError)!Node.Index {837 /// `prev` be compatible with `position` (so `prev` must be either a floating node or the last
838 /// header node in `parent`).
839 prev: Node.Index.Optional,
840}) Error!Node.Index {
599 mf.nodes_lock.assertUnlocked();841 mf.nodes_lock.assertUnlocked();
600 const location_tag: Node.Location.Tag, const location_payload: Node.Location.Payload = location: {842
601 if (std.math.cast(u32, opts.offset)) |small_offset| break :location .{ .small, .{843 try mf.nodes.ensureUnusedCapacity(gpa, 1);
602 .small = .{ .offset = small_offset, .size = 0 },844 try mf.large.ensureUnusedCapacity(gpa, 2);
603 } };845
604 try mf.large.ensureUnusedCapacity(gpa, 2);846 const new_ni: Node.Index = new: {
605 defer mf.large.appendSliceAssumeCapacity(&.{ opts.offset, 0 });847 if (mf.free_ni.unwrap()) |free_ni| {
606 break :location .{ .large, .{ .large = .{ .index = mf.large.items.len } } };848 mf.free_ni = free_ni.get(mf).next;
849 break :new free_ni;
850 }
851 const new_ni: Node.Index = @fromBackingInt(@intCast(mf.nodes.items.len));
852 _ = mf.nodes.addOneAssumeCapacity();
853 break :new new_ni;
607 };854 };
608 const free_ni: Node.Index, const free_node = free: switch (mf.free_ni) {855
609 .none => .{ @fromBackingInt(@intCast(mf.nodes.items.len)), mf.nodes.addOneAssumeCapacity() },856 const next_oni: Node.Index.Optional = if (opts.prev.unwrap()) |prev_ni| next: {
610 else => |free_ni| {857 assert(prev_ni.parent(mf) == opts.parent.toOptional()); // `prev` is not a child of `parent`
611 const free_node = free_ni.get(mf);858 break :next prev_ni.get(mf).next;
612 mf.free_ni = free_node.next;859 } else opts.parent.first(mf);
613 break :free .{ free_ni, free_node };860
861 // Validate node ordering
862 switch (opts.position) {
863 .floating => {
864 if (opts.prev.unwrap()) |prev_ni| {
865 assert(prev_ni.position(mf) != .footer); // tried to add floating node after footer node
866 }
867 if (next_oni.unwrap()) |next_ni| {
868 assert(next_ni.position(mf) != .header); // tried to add floating node before header node
869 }
870 },
871 .header => if (opts.prev.unwrap()) |prev_ni| {
872 switch (prev_ni.position(mf)) {
873 .header => {},
874 .floating => unreachable, // tried to add header node after floating node
875 .footer => unreachable, // tried to add header node after footer node
876 }
877 },
878 .footer => if (next_oni.unwrap()) |next_ni| {
879 switch (next_ni.position(mf)) {
880 .header => unreachable, // tried to add footer node before header node
881 .floating => unreachable, // tried to add footer node before floating node
882 .footer => {},
883 }
614 },884 },
615 };
616 switch (opts.prev) {
617 .none => opts.parent.get(mf).first = free_ni,
618 else => |prev_ni| try prev_ni.setNext(gpa, free_ni, mf),
619 }
620 switch (opts.next) {
621 .none => opts.parent.get(mf).last = free_ni,
622 else => |next_ni| next_ni.get(mf).prev = free_ni,
623 }885 }
624 free_node.* = .{886
625 .parent = opts.parent,887 // Initialize the node as empty with alignment 1
626 .prev = opts.prev,888 const location: Node.Location = loc: {
627 .next = opts.next,889 const offset: u64 = switch (opts.position) {
890 .header, .floating => offset: {
891 const prev_ni = opts.prev.unwrap() orelse break :offset 0;
892 const prev_offset, const prev_size = prev_ni.location(mf).resolve(mf);
893 break :offset prev_offset + prev_size;
894 },
895 .footer => offset: {
896 const next_ni = next_oni.unwrap() orelse {
897 _, const parent_size = opts.parent.location(mf).resolve(mf);
898 break :offset parent_size;
899 };
900 const next_offset, _ = next_ni.location(mf).resolve(mf);
901 break :offset next_offset;
902 },
903 };
904 if (std.math.cast(u32, offset)) |small_offset| {
905 break :loc .{ .small = .{ .offset = small_offset, .size = 0 } };
906 }
907 const large_index = mf.large.items.len;
908 mf.large.appendSliceAssumeCapacity(&.{ offset, 0 });
909 break :loc .{ .large = .{ .index = large_index } };
910 };
911 new_ni.get(mf).* = .{
912 .parent = .wrap(opts.parent),
913 .prev = .none,
914 .next = .none,
628 .first = .none,915 .first = .none,
629 .last = .none,916 .last = .none,
630 .flags = .{917 .flags = .{
631 .location_tag = location_tag,918 .position = opts.position,
632 .alignment = .@"1",919 .alignment = .@"1",
633 .fixed = opts.add_node.fixed,920 .bubbles_moved = opts.add_options.bubbles_moved,
634 .moved = true,921 .enable_next_moved = opts.add_options.enable_next_moved,
635 .resized = true,922 .location_tag = location,
636 .next_moved = true,923 .moved = false,
924 .resized = false,
925 .next_moved = false,
637 .has_content = false,926 .has_content = false,
638 .bubbles_moved = opts.add_node.bubbles_moved,
639 .enable_next_moved = opts.add_node.enable_next_moved,
640 },927 },
641 .location_payload = location_payload,928 .location_payload = switch (location) {
929 .small => |small| .{ .small = small },
930 .large => |large| .{ .large = large },
931 },
642 };932 };
643933
644 {934 try mf.addNodesToChildListBefore(gpa, next_oni, new_ni, new_ni);
645 defer {935
646 free_node.flags.moved = false;936 try mf.realignNode(gpa, new_ni, opts.add_options.alignment);
647 free_node.flags.resized = false;937 if (opts.add_options.size > 0) {
648 free_node.flags.next_moved = false;938 try mf.growNode(gpa, new_ni, opts.add_options.size, .exact);
649 }
650 try mf.realignNode(gpa, free_ni, opts.add_node.alignment, .{});
651 try mf.resizeNode(gpa, free_ni, opts.add_node.size);
652 }939 }
653 mf.updateWriters();940 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);
657 return free_ni;
658}
659941
660pub const AddNodeOptions = struct {942 new_ni.get(mf).flags.moved = false;
661 size: u64 = 0,943 new_ni.get(mf).flags.resized = false;
662 alignment: std.mem.Alignment = .@"1",944 new_ni.get(mf).flags.next_moved = false;
663 fixed: bool = false,
664 moved: bool = false,
665 resized: bool = false,
666 next_moved: bool = false,
667 bubbles_moved: bool = true,
668 enable_next_moved: bool = false,
669};
670945
671pub fn addOnlyChildNode(946 if (opts.add_options.moved) try new_ni.moved(gpa, mf);
672 mf: *MappedFile,947 if (opts.add_options.resized) try new_ni.resized(gpa, mf);
673 gpa: Allocator,948 if (opts.add_options.next_moved) try new_ni.nextMoved(gpa, mf);
674 parent_ni: Node.Index,
675 opts: AddNodeOptions,
676) Error!Node.Index {
677 try mf.nodes.ensureUnusedCapacity(gpa, 1);
678 const parent = parent_ni.get(mf);
679 assert(parent.first == .none and parent.last == .none);
680 return mf.addNode(gpa, .{
681 .parent = parent_ni,
682 .add_node = opts,
683 }) catch |err| switch (err) {
684 error.OutOfMemory,
685 error.Canceled,
686 => |e| return e,
687 else => |e| {
688 mf.io_err = e;
689 return error.MappedFileIo;
690 },
691 };
692}
693949
694pub fn addFirstChildNode(950 return new_ni;
695 mf: *MappedFile,
696 gpa: Allocator,
697 parent_ni: Node.Index,
698 opts: AddNodeOptions,
699) Error!Node.Index {
700 try mf.nodes.ensureUnusedCapacity(gpa, 1);
701 const parent = parent_ni.get(mf);
702 return mf.addNode(gpa, .{
703 .parent = parent_ni,
704 .next = parent.first,
705 .add_node = opts,
706 }) catch |err| switch (err) {
707 error.OutOfMemory,
708 error.Canceled,
709 => |e| return e,
710 else => |e| {
711 mf.io_err = e;
712 return error.MappedFileIo;
713 },
714 };
715}951}
716952
717pub fn addLastChildNode(953fn shrinkLeafNode(
718 mf: *MappedFile,954 mf: *MappedFile,
719 gpa: Allocator,955 gpa: Allocator,
720 parent_ni: Node.Index,956 ni: Node.Index,
721 opts: AddNodeOptions,957 new_size: u64,
722) Error!Node.Index {958) Error!void {
723 try mf.nodes.ensureUnusedCapacity(gpa, 1);959 mf.nodes_lock.assertUnlocked();
724 const parent = parent_ni.get(mf);
725 return mf.addNode(gpa, .{
726 .parent = parent_ni,
727 .prev = parent.last,
728 .offset = offset: switch (parent.last) {
729 .none => 0,
730 else => |last_ni| {
731 const last_offset, const last_size = last_ni.location(mf).resolve(mf);
732 break :offset last_offset + last_size;
733 },
734 },
735 .add_node = opts,
736 }) catch |err| switch (err) {
737 error.OutOfMemory,
738 error.Canceled,
739 => |e| return e,
740 else => |e| {
741 mf.io_err = e;
742 return error.MappedFileIo;
743 },
744 };
745}
746960
747pub fn addNodeAfter(961 const old_offset, const old_size = ni.location(mf).resolve(mf);
748 mf: *MappedFile,962
749 gpa: Allocator,963 assert(new_size < old_size);
750 prev_ni: Node.Index,964 assert(ni.alignment(mf).check(new_size));
751 opts: AddNodeOptions,965 assert(ni.first(mf) == .none); // `ni` must be a leaf node
752) Error!Node.Index {966
753 assert(prev_ni != .none);967 const parent_ni = ni.parent(mf).unwrap() orelse {
754 try mf.nodes.ensureUnusedCapacity(gpa, 1);968 assert(ni == .root);
755 const prev = prev_ni.get(mf);969 mf.memory_map.write(mf.io) catch |err| {
756 const prev_offset, const prev_size = prev.location().resolve(mf);970 mf.io_err = switch (err) {
757 return mf.addNode(gpa, .{971 error.Canceled => |e| return e,
758 .parent = prev.parent,972 error.WouldBlock => error.Unexpected, // file was not opened as non-blocking
759 .prev = prev_ni,973 error.NotOpenForWriting => error.Unexpected, // we definitely opened the file for writing
760 .next = prev.next,974 else => |e| e,
761 .offset = prev_offset + prev_size,975 };
762 .add_node = opts,
763 }) catch |err| switch (err) {
764 error.OutOfMemory,
765 error.Canceled,
766 => |e| return e,
767 else => |e| {
768 mf.io_err = e;
769 return error.MappedFileIo;976 return error.MappedFileIo;
770 },977 };
978 mf.memory_map.file.setLength(mf.io, new_size) catch |err| switch (err) {
979 error.Canceled => |e| return e,
980 else => |e| {
981 mf.io_err = e;
982 return error.MappedFileIo;
983 },
984 };
985 try mf.ensureTotalCapacityPrecise(@intCast(new_size));
986 try ni.setLocation(mf, gpa, old_offset, new_size);
987 return;
771 };988 };
772}
773989
774fn shrinkNode(990 switch (ni.position(mf)) {
775 mf: *MappedFile,991 .header => {
776 gpa: Allocator,992 const shift = old_size - new_size;
777 ni: Node.Index,
778 size: u64,
779 shift_next: bool,
780) !void {
781 mf.nodes_lock.assertUnlocked();
782 const node = ni.get(mf);
783 const old_offset, _ = node.location().resolve(mf);
784993
785 // This would require unmapping first994 try ni.setLocation(mf, gpa, old_offset, new_size);
786 assert(ni != Node.Index.root);
787995
788 if (node.last != .none) {996 // We need to shift backwards all header nodes following us.
789 const last = node.last.get(mf);997 const next_header_ni = ni.next(mf).unwrap() orelse return;
790 const last_offset, const last_size = last.location().resolve(mf);998 if (next_header_ni.position(mf) != .header) return;
791 assert(last_offset + last_size > size);
792 }
793999
794 try mf.large.ensureUnusedCapacity(gpa, 4);1000 var header_ni = next_header_ni;
795 try mf.updates.ensureUnusedCapacity(gpa, 4);1001 while (true) {
1002 const old_header_off, const old_header_size = header_ni.location(mf).resolve(mf);
1003 try header_ni.setLocation(mf, gpa, old_header_off - shift, old_header_size);
7961004
797 ni.setLocationAssumeCapacity(mf, old_offset, size);1005 const next_ni = header_ni.next(mf).unwrap() orelse break;
798 if (!shift_next or node.next == .none) return;1006 if (next_ni.position(mf) != .header) break;
1007 header_ni = next_ni;
1008 }
7991009
800 const next = node.next.get(mf);1010 // Now we must shift the actual header bytes of those nodes backwards.
801 const old_next_offset, const next_size = next.location().resolve(mf);1011 const parent_file_off = parent_ni.fileLocation(mf, false).offset;
802 const padding = old_next_offset - (old_offset + size);1012 const move_src_off = old_offset + old_size;
803 const new_next_offset = next.flags.alignment.forward(@intCast(old_next_offset - padding));1013 const move_dest_off = old_offset + new_size;
1014 assert(next_header_ni.location(mf).resolve(mf)[0] == move_dest_off); // `move_dest_off` because we already updated the location
1015 const move_size = size: {
1016 // `header_ni` is the last header in the parent.
1017 const last_off, const last_size = header_ni.location(mf).resolve(mf);
1018 const move_end = last_off + last_size;
1019 break :size move_end - move_dest_off; // `move_dest_off` because we already updated the location
1020 };
1021 try mf.moveRange(
1022 parent_file_off + move_src_off,
1023 parent_file_off + move_dest_off,
1024 move_size,
1025 );
1026 },
1027 .floating => {
1028 try ni.setLocation(mf, gpa, old_offset, new_size);
1029 },
1030 .footer => {
1031 const shift = old_size - new_size;
1032
1033 const new_offset = old_offset + shift;
1034 try ni.setLocation(mf, gpa, new_offset, new_size);
1035
1036 const prev_footers_size = prev_footers_size: {
1037 // We need to shift forwards all footer nodes preceding us.
1038 const prev_footer_ni = ni.prev(mf).unwrap() orelse {
1039 break :prev_footers_size 0;
1040 };
1041 if (prev_footer_ni.position(mf) != .footer) {
1042 break :prev_footers_size 0;
1043 }
8041044
805 if (next.flags.has_content and new_next_offset < old_next_offset) {1045 var footer_ni = prev_footer_ni;
806 const old_file_offset = node.next.fileLocation(mf, false).offset;1046 while (true) {
807 const new_file_offset = (old_file_offset - old_next_offset) + new_next_offset;1047 const old_footer_off, const old_footer_size = footer_ni.location(mf).resolve(mf);
808 @memmove(1048 try footer_ni.setLocation(mf, gpa, old_footer_off + shift, old_footer_size);
809 mf.memory_map.memory[@intCast(new_file_offset)..][0..@intCast(next_size)],
810 mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(next_size)],
811 );
812 @memset(mf.memory_map.memory[@intCast(new_file_offset + next_size)..@intCast(old_file_offset + next_size)], 0);
813 }
8141049
815 node.next.setLocationAssumeCapacity(mf, new_next_offset, next_size);1050 const prev_ni = footer_ni.prev(mf).unwrap() orelse break;
1051 if (prev_ni.position(mf) != .footer) break;
1052 footer_ni = prev_ni;
1053 }
1054
1055 // `footer_ni` is the first footer in the parent. This expression gets its *new*
1056 // offset because we already did the `setLocation` calls.
1057 const first_footer_new_offset = footer_ni.location(mf).resolve(mf)[0];
1058
1059 break :prev_footers_size new_offset - first_footer_new_offset;
1060 };
1061
1062 // Now we must shift the actual footer bytes forwards, including our own.
1063 const parent_file_offset = parent_ni.fileLocation(mf, false).offset;
1064 try mf.moveRange(
1065 parent_file_offset + old_offset - prev_footers_size,
1066 parent_file_offset + new_offset - prev_footers_size,
1067 prev_footers_size + new_size,
1068 );
1069 },
1070 }
816}1071}
8171072
818fn resizeNode(1073const GrowMode = enum { exact, minimum };
1074
1075/// Increases the size of a node. If `grow_mode` is `.exact`, the new size will be exactly `new_size`.
1076/// If `grow_mode` is `.minimum`, the new size will be greater than or equal to `new_size`.
1077///
1078/// Asserts that `new_size` is aligned to `ni.alignment(mf)` (even if `grow_mode` is `.minimum`!).
1079///
1080/// Asserts that `new_size` is greater than the current size of `ni`.
1081fn growNode(
819 mf: *MappedFile,1082 mf: *MappedFile,
820 gpa: Allocator,1083 gpa: Allocator,
821 ni: Node.Index,1084 ni: Node.Index,
822 requested_size: u64,1085 new_size: u64,
823) (Allocator.Error || Io.Cancelable || IoError)!void {1086 grow_mode: GrowMode,
1087) Error!void {
824 mf.nodes_lock.assertUnlocked();1088 mf.nodes_lock.assertUnlocked();
825 const io = mf.io;1089
826 const node = ni.get(mf);1090 const node = ni.get(mf);
1091
827 const old_offset, const old_size = node.location().resolve(mf);1092 const old_offset, const old_size = node.location().resolve(mf);
828 const new_size = node.flags.alignment.forward(@intCast(requested_size));1093
8291094 assert(node.flags.alignment.check(old_size));
830 // Resize the entire file1095 assert(node.flags.alignment.check(new_size));
831 if (ni == Node.Index.root) {1096 assert(new_size > old_size);
832 try mf.ensureCapacityForSetLocation(gpa);1097
833 mf.memory_map.write(io) catch |err| switch (err) {1098 const parent_ni = node.parent.unwrap() orelse {
834 error.WouldBlock => return error.Unexpected, // file was not opened as non-blocking1099 assert(ni == .root);
835 error.NotOpenForWriting => return error.Unexpected, // we definitely opened the file for writing1100
836 else => |e| return e,1101 if (try mf.growNodeViaInsertRange(gpa, ni, new_size, grow_mode)) {
837 };1102 return;
838 try mf.memory_map.file.setLength(io, new_size);1103 }
839 try mf.ensureTotalCapacityInner(@intCast(new_size));1104
840 ni.setLocationAssumeCapacity(mf, old_offset, new_size);1105 mf.memory_map.write(mf.io) catch |err| {
841 return;1106 mf.io_err = switch (err) {
842 }1107 error.Canceled => |e| return e,
843 const parent = node.parent.get(mf);1108 error.WouldBlock => error.Unexpected, // file was not opened as non-blocking
844 _, var old_parent_size = parent.location().resolve(mf);1109 error.NotOpenForWriting => error.Unexpected, // we definitely opened the file for writing
845 const trailing_end = trailing_end: switch (node.next) {1110 else => |e| e,
846 .none => old_parent_size,1111 };
847 else => |next_ni| {1112 return error.MappedFileIo;
848 const next_offset, _ = next_ni.location(mf).resolve(mf);
849 break :trailing_end next_offset;
850 },
851 };
852 assert(old_offset + old_size <= trailing_end);
853 if (old_offset + new_size <= trailing_end) {
854 // Expand the node into trailing free space
855 try mf.ensureCapacityForSetLocation(gpa);
856 ni.setLocationAssumeCapacity(mf, old_offset, new_size);
857 return;
858 }
859 insert_range: {
860 if (!is_linux) break :insert_range;
861 if (mf.flags.fallocate_insert_range_unsupported) break :insert_range;
862
863 // We need the node to be aligned to `mf.flags.block_size` in the file in order to use this
864 // fast path. It is not sufficient to check `node.flags.alignment`, because that doesn't
865 // necessarily mean that all *parent* nodes are equally aligned; instead we must compute the
866 // actual file offset.
867 const range_file_offset = ni.fileLocation(mf, false).offset + old_size;
868 const range_size = node.flags.alignment.forward(
869 @intCast(requested_size +| requested_size / growth_factor),
870 ) - old_size;
871 if (!mf.flags.block_size.check(@intCast(range_file_offset))) break :insert_range;
872 if (!mf.flags.block_size.check(@intCast(range_size))) break :insert_range;
873
874 mf.memory_map.write(io) catch |err| switch (err) {
875 error.WouldBlock => return error.Unexpected, // file was not opened as non-blocking
876 error.NotOpenForWriting => return error.Unexpected, // we definitely opened the file for writing
877 else => |e| return e,
878 };1113 };
879 // Ask the filesystem driver to insert extents into the file without copying any data1114 mf.memory_map.file.setLength(mf.io, new_size) catch |err| switch (err) {
880 const last_offset, const last_size = parent.last.location(mf).resolve(mf);1115 error.Canceled => |e| return e,
881 const last_end = last_offset + last_size;1116 else => |e| {
882 assert(last_end <= old_parent_size);1117 mf.io_err = e;
883 _, const file_size = Node.Index.root.location(mf).resolve(mf);1118 return error.MappedFileIo;
884 while (true) switch (linux.errno(switch (std.math.order(range_file_offset, file_size)) {
885 .lt => linux.fallocate(
886 mf.memory_map.file.handle,
887 linux.FALLOC.FL_INSERT_RANGE,
888 @intCast(range_file_offset),
889 @intCast(range_size),
890 ),
891 .eq => linux.ftruncate(mf.memory_map.file.handle, @intCast(range_file_offset + range_size)),
892 .gt => unreachable,
893 })) {
894 .SUCCESS => {
895 var enclosing_ni = ni;
896 while (true) {
897 try mf.ensureCapacityForSetLocation(gpa);
898 const enclosing = enclosing_ni.get(mf);
899 const enclosing_offset, const old_enclosing_size =
900 enclosing.location().resolve(mf);
901 const new_enclosing_size = old_enclosing_size + range_size;
902 enclosing_ni.setLocationAssumeCapacity(mf, enclosing_offset, new_enclosing_size);
903 if (enclosing_ni == Node.Index.root) {
904 assert(enclosing_offset == 0);
905 try mf.ensureTotalCapacityInner(@intCast(new_enclosing_size));
906 break;
907 }
908 var after_ni = enclosing.next;
909 while (after_ni != .none) {
910 try mf.ensureCapacityForSetLocation(gpa);
911 const after = after_ni.get(mf);
912 const after_offset, const after_size = after.location().resolve(mf);
913 after_ni.setLocationAssumeCapacity(
914 mf,
915 range_size + after_offset,
916 after_size,
917 );
918 after_ni = after.next;
919 }
920 enclosing_ni = enclosing.parent;
921 }
922 return;
923 },
924 .INTR => continue,
925 .BADF, .FBIG, .INVAL => unreachable,
926 .IO => return error.InputOutput,
927 .NODEV => return error.NotFile,
928 .NOSPC => return error.NoSpaceLeft,
929 .NOSYS, .OPNOTSUPP => {
930 mf.flags.fallocate_insert_range_unsupported = true;
931 break :insert_range;
932 },1119 },
933 .PERM => return error.PermissionDenied,
934 .SPIPE => return error.Unseekable,
935 .TXTBSY => return error.FileBusy,
936 else => |e| return std.posix.unexpectedErrno(e),
937 };1120 };
938 }1121 try mf.ensureTotalCapacityPrecise(@intCast(new_size));
939 if (node.next == .none) {1122 try ni.setLocation(mf, gpa, old_offset, new_size);
940 // As this is the last node, we simply need more space in the parent1123 // We need to move any footers to be at the *new* end of the file.
941 const new_parent_size = old_offset + new_size;1124 if (ni.firstFooter(mf).unwrap()) |first_footer_ni| {
942 try mf.resizeNode(gpa, node.parent, new_parent_size +| new_parent_size / growth_factor);1125 const old_footers_offset, _ = first_footer_ni.location(mf).resolve(mf);
943 try mf.ensureCapacityForSetLocation(gpa);1126 const footers_size = old_size - old_footers_offset;
944 ni.setLocationAssumeCapacity(mf, old_offset, new_size);
945 return;
946 }
947 if (!node.flags.fixed) {
948 // Make space at the end of the parent for this floating node
949 const last = parent.last.get(mf);
950 const last_offset, const last_size = last.location().resolve(mf);
951 const new_offset = node.flags.alignment.forward(@intCast(last_offset + last_size));
952 const new_parent_size = new_offset + new_size;
953 if (new_parent_size > old_parent_size)
954 try mf.resizeNode(gpa, node.parent, new_parent_size +| new_parent_size / growth_factor);
955 try mf.ensureCapacityForSetLocation(gpa);
956 const next_ni = node.next;
957 next_ni.get(mf).prev = node.prev;
958 switch (node.prev) {
959 .none => parent.first = next_ni,
960 else => |prev_ni| try prev_ni.setNext(gpa, next_ni, mf),
961 }
962 try parent.last.setNext(gpa, ni, mf);
963 node.prev = parent.last;
964 try ni.setNext(gpa, .none, mf);
965 parent.last = ni;
966 if (node.flags.has_content) {
967 const parent_file_offset = node.parent.fileLocation(mf, false).offset;
968 try mf.moveRange(1127 try mf.moveRange(
969 parent_file_offset + old_offset,1128 old_footers_offset,
970 parent_file_offset + new_offset,1129 old_footers_offset + (new_size - old_size),
971 old_size,1130 footers_size,
972 );1131 );
1132 // Also update the footers' locations.
1133 var cur_ni = first_footer_ni;
1134 while (true) {
1135 const old_footer_offset, const footer_size = cur_ni.location(mf).resolve(mf);
1136 try cur_ni.setLocation(mf, gpa, old_footer_offset + (new_size - old_size), footer_size);
1137 cur_ni = cur_ni.next(mf).unwrap() orelse break;
1138 }
973 }1139 }
974 ni.setLocationAssumeCapacity(mf, new_offset, new_size);
975 return;1140 return;
976 }1141 };
977 // Search for the first floating node following this fixed node1142
978 var last_fixed_ni = ni;1143 switch (node.flags.position) {
979 var first_floating_ni = node.next;1144 .header => {
980 var shift = new_size - old_size;1145 if (try mf.growNodeViaInsertRange(gpa, ni, new_size, grow_mode)) {
981 var max_shift_align: std.mem.Alignment = .@"1";1146 return;
982 var direction: enum { forward, reverse } = .forward;1147 }
983 while (true) {1148
984 assert(last_fixed_ni != .none);1149 try mf.ensureAdditionalHeaderCapacity(gpa, parent_ni, new_size - old_size);
985 const last_fixed = last_fixed_ni.get(mf);1150
986 assert(last_fixed.flags.fixed);1151 // `old_offset` is still valid because header nodes don't move when the parent resizes.
987 const old_last_fixed_offset, const last_fixed_size = last_fixed.location().resolve(mf);1152
988 const new_last_fixed_offset = old_last_fixed_offset + shift;1153 const last_header_ni: Node.Index = last_header: {
989 make_space: switch (first_floating_ni) {1154 var header_ni = ni;
990 else => {1155 while (true) {
991 const first_floating = first_floating_ni.get(mf);1156 const next_ni = header_ni.next(mf).unwrap() orelse break;
992 const old_first_floating_offset, const first_floating_size =1157 if (next_ni.position(mf) != .header) break;
993 first_floating.location().resolve(mf);1158 header_ni = next_ni;
994 assert(old_last_fixed_offset + last_fixed_size <= old_first_floating_offset);
995 if (new_last_fixed_offset + last_fixed_size <= old_first_floating_offset)
996 break :make_space;
997 assert(direction == .forward);
998 max_shift_align = max_shift_align.max(first_floating.flags.alignment.max(last_fixed.flags.alignment));
999 if (first_floating.flags.fixed) {
1000 shift = max_shift_align.forward(@intCast(
1001 @max(shift, first_floating_size),
1002 ));
1003
1004 // Not enough space, try the next node
1005 last_fixed_ni = first_floating_ni;
1006 first_floating_ni = first_floating.next;
1007 continue;
1008 }1159 }
1009 // Move the found floating node to make space for preceding fixed nodes1160 break :last_header header_ni;
1010 const last = parent.last.get(mf);1161 };
1011 const last_offset, const last_size = last.location().resolve(mf);1162 const last_header_offset, const last_header_size = last_header_ni.location(mf).resolve(mf);
1012 const new_first_floating_offset = max_shift_align.forward(1163 const old_headers_size = last_header_offset + last_header_size;
1013 @intCast(@max(new_last_fixed_offset + last_fixed_size, last_offset + last_size)),1164
1014 );1165 // This is the first footer *inside* of `ni`.
1015 const new_parent_size = new_first_floating_offset + first_floating_size;1166 const first_sub_footer_oni = ni.firstFooter(mf);
1016 if (new_parent_size > old_parent_size) {1167 const sub_footers_size = size: {
1017 try mf.resizeNode(1168 const first_sub_footer_ni = first_sub_footer_oni.unwrap() orelse break :size 0;
1169 const first_sub_footer_offset, _ = first_sub_footer_ni.location(mf).resolve(mf);
1170 break :size old_size - first_sub_footer_offset;
1171 };
1172
1173 // We need to shift two things forwards; any header nodes which follow us, and any
1174 // footer nodes *within* us (since they need to be at the end of our new size).
1175 const parent_file_offset = parent_ni.fileLocation(mf, false).offset;
1176 try mf.moveRange(
1177 parent_file_offset + old_offset + old_size - sub_footers_size,
1178 parent_file_offset + old_offset + new_size - sub_footers_size,
1179 old_headers_size - (old_offset + old_size - sub_footers_size),
1180 );
1181
1182 // Any footers inside of us have had their offsets changed due to us growing:
1183 if (first_sub_footer_oni.unwrap()) |first_sub_footer_ni| {
1184 var cur_ni = first_sub_footer_ni;
1185 while (true) {
1186 const old_sub_footer_offset, const sub_footer_size = cur_ni.location(mf).resolve(mf);
1187 try cur_ni.setLocation(
1188 mf,
1018 gpa,1189 gpa,
1019 node.parent,1190 old_sub_footer_offset + (new_size - old_size),
1020 new_parent_size +| new_parent_size / growth_factor,1191 sub_footer_size,
1021 );1192 );
1022 _, old_parent_size = parent.location().resolve(mf);1193 cur_ni = cur_ni.next(mf).unwrap() orelse break;
1023 }1194 }
1024 try mf.ensureCapacityForSetLocation(gpa);1195 }
1025 if (parent.last != first_floating_ni) {1196
1026 const old_last = parent.last;1197 // Update the offsets of all header nodes following us:
1027 first_floating.prev = old_last;1198 {
1028 parent.last = first_floating_ni;1199 var moved_header_ni = last_header_ni;
1029 try old_last.setNext(gpa, first_floating_ni, mf);1200 while (moved_header_ni != ni) {
1030 try last_fixed_ni.setNext(gpa, first_floating.next, mf);1201 assert(moved_header_ni.position(mf) == .header);
1031 switch (first_floating.next) {1202 const moved_header_offset, const moved_header_size = moved_header_ni.location(mf).resolve(mf);
1032 .none => {},1203 try moved_header_ni.setLocation(
1033 else => |next_ni| next_ni.get(mf).prev = last_fixed_ni,1204 mf,
1034 }1205 gpa,
1035 try first_floating_ni.setNext(gpa, .none, mf);1206 moved_header_offset - old_size + new_size,
1207 moved_header_size,
1208 );
1209 moved_header_ni = moved_header_ni.prev(mf).unwrap().?;
1036 }1210 }
1037 if (first_floating.flags.has_content) {1211 }
1038 const parent_file_offset =1212
1039 node.parent.fileLocation(mf, false).offset;1213 // Finally, update our own size:
1040 try mf.moveRange(1214 try ni.setLocation(mf, gpa, old_offset, new_size);
1041 parent_file_offset + old_first_floating_offset,1215 return;
1042 parent_file_offset + new_first_floating_offset,1216 },
1043 first_floating_size,1217 .floating => {
1218 try mf.growFloatingNodeWithAlignment(gpa, ni, null, new_size, grow_mode);
1219 },
1220 .footer => {
1221 if (try mf.growNodeViaInsertRange(gpa, ni, new_size, grow_mode)) {
1222 return;
1223 }
1224
1225 try mf.ensureAdditionalFooterCapacity(gpa, parent_ni, new_size - old_size);
1226
1227 const first_footer_ni: Node.Index = first_footer: {
1228 var footer_ni = ni;
1229 while (true) {
1230 const prev_ni = footer_ni.prev(mf).unwrap() orelse break;
1231 if (prev_ni.position(mf) != .footer) break;
1232 footer_ni = prev_ni;
1233 }
1234 break :first_footer footer_ni;
1235 };
1236
1237 // This is the first footer *inside* of `ni` (unrelated to the fact that `ni` is itself
1238 // a footer within its parent).
1239 const first_sub_footer_oni = ni.firstFooter(mf);
1240 const sub_footers_size = size: {
1241 const first_sub_footer_ni = first_sub_footer_oni.unwrap() orelse break :size 0;
1242 const first_sub_footer_offset, _ = first_sub_footer_ni.location(mf).resolve(mf);
1243 break :size old_size - first_sub_footer_offset;
1244 };
1245
1246 _, const parent_size = parent_ni.location(mf).resolve(mf);
1247
1248 const old_footers_size = parent_size - first_footer_ni.location(mf).resolve(mf)[0];
1249 const new_footers_size = old_footers_size - old_size + new_size;
1250
1251 // Shift ourselves, and any footer before us, backwards. Unlike header nodes, this node
1252 // itself needs to shift its contents, because our offset was shifted backwards by
1253 // `new_size - old_size`, and the added bytes should go at the end of this footer node.
1254 // However, if we *contain* any footer nodes, they need to stay at the end of `ni`, so
1255 // we *shouldn't* shift *that* data.
1256 const old_footers_start = parent_size - old_footers_size;
1257 const new_footers_start = parent_size - new_footers_size;
1258 const end_offset = node.location().resolve(mf)[0] + old_size;
1259 const parent_file_offset = parent_ni.fileLocation(mf, false).offset;
1260 try mf.moveRange(
1261 parent_file_offset + old_footers_start,
1262 parent_file_offset + new_footers_start,
1263 end_offset - old_footers_start - sub_footers_size,
1264 );
1265
1266 // Update our own offset and size:
1267 try ni.setLocation(mf, gpa, end_offset - new_size, new_size);
1268
1269 // Any footers inside of us have had their offsets changed due to us growing:
1270 if (first_sub_footer_oni.unwrap()) |first_sub_footer_ni| {
1271 var cur_ni = first_sub_footer_ni;
1272 while (true) {
1273 const old_sub_footer_offset, const sub_footer_size = cur_ni.location(mf).resolve(mf);
1274 try cur_ni.setLocation(
1275 mf,
1276 gpa,
1277 old_sub_footer_offset + (new_size - old_size),
1278 sub_footer_size,
1044 );1279 );
1280 cur_ni = cur_ni.next(mf).unwrap() orelse break;
1045 }1281 }
1046 first_floating_ni.setLocationAssumeCapacity(1282 }
1047 mf,1283
1048 new_first_floating_offset,1284 // Finally, update the offsets of every footer before us:
1049 first_floating_size,1285 if (node.prev.unwrap()) |prev_ni| {
1050 );1286 var maybe_footer_ni = prev_ni;
1051 // Continue the search after the just-moved floating node1287 while (true) {
1052 first_floating_ni = last_fixed.next;1288 switch (maybe_footer_ni.position(mf)) {
1053 continue;1289 .header, .floating => break,
1054 },1290 .footer => {},
1055 .none => {1291 }
1056 assert(direction == .forward);1292 const moved_footer_offset, const moved_footer_size = maybe_footer_ni.location(mf).resolve(mf);
1057 const new_parent_size = new_last_fixed_offset + last_fixed_size;1293 try maybe_footer_ni.setLocation(
1058 if (new_parent_size > old_parent_size) {1294 mf,
1059 try mf.resizeNode(
1060 gpa,1295 gpa,
1061 node.parent,1296 moved_footer_offset + old_size - new_size,
1062 new_parent_size +| new_parent_size / growth_factor,1297 moved_footer_size,
1063 );1298 );
1064 _, old_parent_size = parent.location().resolve(mf);1299 maybe_footer_ni = maybe_footer_ni.prev(mf).unwrap() orelse break;
1065 }1300 }
1066 },1301 }
1067 }1302
1068 try mf.ensureCapacityForSetLocation(gpa);
1069 if (last_fixed_ni == ni) {
1070 // The original fixed node now has enough space
1071 last_fixed_ni.setLocationAssumeCapacity(
1072 mf,
1073 old_last_fixed_offset,
1074 new_size,
1075 );
1076 return;1303 return;
1077 }1304 },
1078 // Move a fixed node into trailing free space
1079 if (last_fixed.flags.has_content) {
1080 const parent_file_offset = node.parent.fileLocation(mf, false).offset;
1081 try mf.moveRange(
1082 parent_file_offset + old_last_fixed_offset,
1083 parent_file_offset + new_last_fixed_offset,
1084 last_fixed_size,
1085 );
1086 }
1087 last_fixed_ni.setLocationAssumeCapacity(mf, new_last_fixed_offset, last_fixed_size);
1088 // Retry the previous nodes now that there is enough space
1089 first_floating_ni = last_fixed_ni;
1090 last_fixed_ni = last_fixed.prev;
1091 direction = .reverse;
1092 }1305 }
1093}1306}
10941307
1095fn realignNode(1308/// Moves a floating node to an unused region with the given size, which may be greater than the
1309/// current size. If `new_alignment` is not `null`, then the offset and size of the new region will
1310/// have that alignment instead of `ni.alignment(mf)`.
1311///
1312/// Asserts that `ni` is a floating node (and not `.root`).
1313///
1314/// Asserts that `new_size` is aligned to `new_alignment orelse ni.alignment(mf)`.
1315///
1316/// Asserts that `new_size` is greater than or equal to the current size of `ni`.
1317fn growFloatingNodeWithAlignment(
1096 mf: *MappedFile,1318 mf: *MappedFile,
1097 gpa: Allocator,1319 gpa: Allocator,
1098 ni: Node.Index,1320 ni: Node.Index,
1099 new_alignment: std.mem.Alignment,1321 new_alignment: ?Alignment,
1100 opts: Node.Index.RealignNodeOptions,1322 new_size: u64,
1101) (Allocator.Error || Io.Cancelable || IoError)!void {1323 grow_mode: GrowMode,
1324) Error!void {
1102 mf.nodes_lock.assertUnlocked();1325 mf.nodes_lock.assertUnlocked();
11031326
1104 const node = ni.get(mf);1327 const parent_ni = ni.parent(mf).unwrap().?; // `ni` cannot be `.root`
1105 {1328 const old_offset, const old_size = ni.location(mf).resolve(mf);
1106 const prev_alignment = node.flags.alignment;
1107 node.flags.alignment = new_alignment;
1108 if (new_alignment.compare(.lte, prev_alignment)) return;
1109 }
11101329
1111 const old_offset, const size = node.location().resolve(mf);1330 const alignment = new_alignment orelse ni.alignment(mf);
1112 if (ni == Node.Index.root) return mf.resizeNode(gpa, ni, size);
11131331
1114 const new_size = new_alignment.forward(@intCast(size));1332 assert(new_size >= old_size);
1115 if (new_alignment.check(@intCast(old_offset))) return mf.resizeNode(gpa, ni, new_size);1333 assert(ni.position(mf) == .floating);
1334 assert(alignment.check(new_size));
11161335
1117 _, const parent_size = node.parent.location(mf).resolve(mf);1336 grow_in_place: {
1118 const trailing_end = trailing_end: switch (node.next) {1337 if (!alignment.check(old_offset)) {
1119 .none => parent_size,1338 break :grow_in_place;
1120 else => |next_ni| {1339 }
1340 const limit: u64 = limit: {
1341 const next_ni = ni.next(mf).unwrap() orelse break :limit parent_ni.location(mf).resolve(mf)[1];
1121 const next_offset, _ = next_ni.location(mf).resolve(mf);1342 const next_offset, _ = next_ni.location(mf).resolve(mf);
1122 break :trailing_end next_offset;1343 break :limit next_offset;
1123 },1344 };
1124 };1345 if (old_offset + new_size > limit) {
1346 break :grow_in_place; // the parent is not big enough
1347 }
1348 // Great, we can grow this node without changing its offset or moving any siblings.
1349 try ni.setLocation(mf, gpa, old_offset, new_size);
1350 // If we have any footers, we need to move them to the end of our new size, and update their
1351 // offsets accordingly.
1352 if (ni.firstFooter(mf).unwrap()) |first_footer_ni| {
1353 var cur_ni = first_footer_ni;
1354 var footers_have_content = false;
1355 while (true) {
1356 footers_have_content = footers_have_content or cur_ni.get(mf).flags.has_content;
1357 const old_footer_offset, const footer_size = cur_ni.location(mf).resolve(mf);
1358 try cur_ni.setLocation(mf, gpa, old_footer_offset + (new_size - old_size), footer_size);
1359 cur_ni = cur_ni.next(mf).unwrap() orelse break;
1360 }
1361 if (footers_have_content) {
1362 const parent_file_off = parent_ni.fileLocation(mf, false).offset;
1363 // This gets the *new* offset because we already updated the offsets above.
1364 const new_footers_offset, _ = first_footer_ni.location(mf).resolve(mf);
1365 const footers_size = new_size - new_footers_offset;
1366 try mf.moveRange(
1367 parent_file_off + old_offset + old_size - footers_size,
1368 parent_file_off + old_offset + new_size - footers_size,
1369 footers_size,
1370 );
1371 }
1372 }
1373 return;
1374 }
1375
1376 const new_loc: struct {
1377 offset: u64,
1378 prev: Node.Index.Optional,
1379 } = new_loc: {
1380 _, const parent_size = parent_ni.location(mf).resolve(mf);
1381
1382 {
1383 // See if there's space at the start of the parent.
1384 const last_header_oni = parent_ni.lastHeader(mf);
1385 const headers_end: u64 = if (last_header_oni.unwrap()) |last_header_ni| headers_end: {
1386 const last_header_off, const last_header_size = last_header_ni.location(mf).resolve(mf);
1387 break :headers_end last_header_off + last_header_size;
1388 } else 0;
1389 const limit: u64 = limit: {
1390 const after_header_oni: Node.Index.Optional = after_header: {
1391 if (last_header_oni.unwrap()) |last_header_ni| {
1392 break :after_header last_header_ni.next(mf);
1393 }
1394 break :after_header parent_ni.first(mf);
1395 };
1396 if (after_header_oni.unwrap()) |after_header_ni| {
1397 break :limit after_header_ni.location(mf).resolve(mf)[0];
1398 } else {
1399 break :limit parent_size;
1400 }
1401 };
1402 if (alignment.forward(headers_end) + new_size <= limit) {
1403 // There's space here!
1404 break :new_loc .{
1405 // Put ourselves at the *end* of this range, so that the free space remains at the start of the parent.
1406 .offset = alignment.backward(limit - new_size),
1407 .prev = last_header_oni,
1408 };
1409 }
1410 }
1411
1412 // Otherwise, use space at the end of the parent, or make space there if necessary.
1413
1414 const first_footer_oni = parent_ni.firstFooter(mf);
11251415
1126 if (opts.try_backwards) {1416 // We know there is a node before the footer[s], because `ni` itself is such a node.
1127 const backward_offset = new_alignment.backward(@intCast(old_offset));1417 const prev_ni: Node.Index = if (first_footer_oni.unwrap()) |first_footer_ni| prev: {
1128 const prev_end = if (node.prev == .none) 0 else prev: {1418 break :prev first_footer_ni.prev(mf).unwrap().?;
1129 const prev_offset, const prev_size = node.prev.location(mf).resolve(mf);1419 } else prev: {
1130 break :prev prev_offset + prev_size;1420 break :prev parent_ni.last(mf).unwrap().?;
1131 };1421 };
11321422
1133 if (backward_offset >= prev_end) {1423 const result_offset: u64 = result_offset: {
1134 try mf.ensureCapacityForSetLocation(gpa);1424 if (prev_ni == ni and alignment.check(old_offset)) {
1425 // We're already at the end of the parent, and our offset is already well-aligned.
1426 // The only reason we didn't simply grow in place earlier is that the parent wasn't
1427 // big enough---but now we're resizing the parent anyway, so growing in-place stops
1428 // us from unnecessarily moving!
1429 break :result_offset old_offset;
1430 }
1431 // Otherwise, just move after the last node.
1432 const prev_offset, const prev_size = prev_ni.location(mf).resolve(mf);
1433 break :result_offset alignment.forward(prev_offset + prev_size);
1434 };
11351435
1136 if (node.flags.has_content) {1436 const footers_size: u64 = if (first_footer_oni.unwrap()) |first_footer_ni| footers_size: {
1137 const old_file_offset = ni.fileLocation(mf, false).offset;1437 const first_footer_offset, _ = first_footer_ni.location(mf).resolve(mf);
1138 const new_file_offset = (old_file_offset - old_offset) + backward_offset;1438 break :footers_size parent_size - first_footer_offset;
1139 @memmove(1439 } else 0;
1140 mf.memory_map.memory[@intCast(new_file_offset)..][0..@intCast(size)],1440
1141 mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)],1441 const min_parent_size = result_offset + new_size + footers_size;
1142 );1442 if (parent_size < min_parent_size) {
1143 @memset(mf.memory_map.memory[@intCast(new_file_offset + size)..@intCast(old_file_offset + size)], 0);1443 // Okay, at this point we're planning to expand the parent---so before we actually do
1444 // that, let's first try the Linux "insert range" fast path. We didn't try it before now
1445 // because it would have been more efficient to just move ourselves into existing space.
1446 //
1447 // If we were given a custom alignment, we cannot pass `grow_mode` directly into the
1448 // "insert range" path, because that function is unaware of `new_alignment`.
1449 const sub_grow_mode: GrowMode = if (new_alignment == null) grow_mode else .exact;
1450 if (alignment.check(old_offset) and
1451 try mf.growNodeViaInsertRange(gpa, ni, new_size, sub_grow_mode))
1452 {
1453 // The Linux fast path did our job for us!
1454 return;
1144 }1455 }
11451456
1146 if (backward_offset + new_size <= trailing_end) {1457 // Grow the parent and move to the end of the parent.
1147 ni.setLocationAssumeCapacity(mf, backward_offset, new_size);1458 const new_parent_size = parent_ni.alignment(mf).forward(
1459 min_parent_size +| min_parent_size / growth_factor,
1460 );
1461 try mf.growNode(gpa, parent_ni, new_parent_size, .minimum);
1462 }
1463
1464 break :new_loc .{
1465 .offset = result_offset,
1466 .prev = .wrap(prev_ni),
1467 };
1468 };
1469
1470 // We've found our new location in `parent_ni`, now to actually move ourselves there.
1471
1472 // Footers need to move to a different place than the rest of our content.
1473 const footers_size: u64, const footers_have_content: bool = footers: {
1474 const first_footer_ni = ni.firstFooter(mf).unwrap() orelse {
1475 break :footers .{ 0, false };
1476 };
1477
1478 var cur_ni = first_footer_ni;
1479 var footers_have_content = false;
1480 while (true) {
1481 footers_have_content = footers_have_content or cur_ni.get(mf).flags.has_content;
1482 const old_footer_offset, const footer_size = cur_ni.location(mf).resolve(mf);
1483 // Our footers' offsets must change to be at the end of our new size.
1484 try cur_ni.setLocation(mf, gpa, old_footer_offset + (new_size - old_size), footer_size);
1485 cur_ni = cur_ni.next(mf).unwrap() orelse break;
1486 }
1487
1488 // This is the *new* offset because we already updated the offsets above.
1489 const new_footers_offset, _ = first_footer_ni.location(mf).resolve(mf);
1490 const footers_size = new_size - new_footers_offset;
1491
1492 break :footers .{ footers_size, footers_have_content };
1493 };
1494
1495 if (ni.get(mf).flags.has_content) {
1496 const parent_file_off = parent_ni.fileLocation(mf, false).offset;
1497 try mf.moveRange(
1498 parent_file_off + old_offset,
1499 parent_file_off + new_loc.offset,
1500 old_size - footers_size,
1501 );
1502 if (footers_have_content) try mf.moveRange(
1503 parent_file_off + old_offset + old_size - footers_size,
1504 parent_file_off + new_loc.offset + new_size - footers_size,
1505 footers_size,
1506 );
1507 } else {
1508 assert(!footers_have_content);
1509 }
1510
1511 try ni.setLocation(mf, gpa, new_loc.offset, new_size);
1512
1513 if (new_loc.prev != ni.toOptional()) {
1514 // We're potentially in a different place in `parent_ni`'s child list, so remove and re-add ourselves.
1515 try mf.removeNodesFromChildList(gpa, ni, ni);
1516 try mf.addNodesToChildListAfter(gpa, new_loc.prev, ni, ni);
1517 }
1518}
1519
1520/// Attempts to grow `ni` to `new_size` using `FALLOCATE_FL_INSERT_RANGE` on Linux. This strategy
1521/// has the advantage that it does not require manually moving any bytes in the file, but has the
1522/// disadvantages that it may increase the file size more than necessary, and that it changes the
1523/// offsets of all following nodes, recursively.
1524///
1525/// If this strategy is inapplicable or unsuitable for this operation, this function returns `false`
1526/// without changing any nodes' locations or invalidating any slices.
1527///
1528/// Otherwise, this function grows `ni` to `new_size`, updates the location of `ni` and every node
1529/// whose offset has changed, and returns `true`. Like in `growNode`, if `grow_mode` is `.minimum`,
1530/// the actual new size of `ni` may be greater than `new_size`.
1531fn growNodeViaInsertRange(
1532 mf: *MappedFile,
1533 gpa: Allocator,
1534 ni: Node.Index,
1535 new_size: u64,
1536 grow_mode: GrowMode,
1537) Error!bool {
1538 if (!is_linux or mf.flags.fallocate_insert_range_unsupported) {
1539 return false;
1540 }
1541
1542 _, const old_size = ni.location(mf).resolve(mf);
1543
1544 // We don't compute the size of the range yet, because depending on `grow_mode` we might want to
1545 // bump it based on our sibling and parent nodes' alignments. However, we can do an early check
1546 // for cases where we should obviously exit.
1547 const requested_range_size = new_size - old_size;
1548 if (!mf.flags.block_size.check(requested_range_size)) {
1549 // The requested size isn't exactly aligned.
1550 switch (grow_mode) {
1551 .exact => return false,
1552 .minimum => {
1553 // We can still choose to allow it by increasing the size a bit, but we shouldn't do
1554 // that if it would *significantly* increase the requested size.
1555 const block_size = mf.flags.block_size.toByteUnits();
1556 if (requested_range_size < block_size * 2) {
1557 // Bumping this size up to the next block boundary would be a quite significant
1558 // increase; let's not do it.
1559 return false;
1560 }
1561 },
1562 }
1563 }
1564 // If `grow_mode` is exact, we will use exactly this size, but if it is `.minimum`, we may bump
1565 // the size a little more.
1566 const min_range_size: u64 = s: {
1567 const exact_size = new_size - old_size;
1568 if (mf.flags.block_size.check(exact_size)) {
1569 break :s exact_size;
1570 }
1571 switch (grow_mode) {
1572 .exact => return false,
1573 .minimum => if (exact_size >= mf.flags.block_size.toByteUnits() * 2) {
1574 // We're growing by at least a few blocks, so allow ourselves to bump the size
1575 // slightly to give it the needed alignment.
1576 break :s mf.flags.block_size.forward(exact_size);
1148 } else {1577 } else {
1149 ni.setLocationAssumeCapacity(mf, backward_offset, size);1578 return false;
1150 try mf.resizeNode(gpa, ni, new_size);1579 },
1580 }
1581 };
1582 assert(min_range_size > 0);
1583 assert(mf.flags.block_size.check(min_range_size));
1584
1585 const range_file_offset: u64 = range_file_offset: {
1586 const node_file_offset = ni.fileLocation(mf, false).offset;
1587 const last_ni = ni.last(mf).unwrap() orelse {
1588 // If `ni` has no children (i.e. is a leaf node), we need to insert exactly at its end.
1589 const range_file_offset = node_file_offset + old_size;
1590 if (!mf.flags.block_size.check(range_file_offset)) {
1591 return false;
1592 }
1593 break :range_file_offset range_file_offset;
1594 };
1595 const first_footer_oni = ni.firstFooter(mf);
1596 const footers_size: u64 = if (first_footer_oni.unwrap()) |first_footer_ni| size: {
1597 const first_footer_offset, _ = first_footer_ni.location(mf).resolve(mf);
1598 break :size old_size - first_footer_offset;
1599 } else 0;
1600 const pre_footer_oni: Node.Index.Optional = if (first_footer_oni.unwrap()) |first_footer_ni| pre_footer: {
1601 break :pre_footer first_footer_ni.prev(mf);
1602 } else .wrap(last_ni);
1603 const pre_footer_end: u64 = if (pre_footer_oni.unwrap()) |pre_footer_ni| end: {
1604 const pre_footer_off, const pre_footer_size = pre_footer_ni.location(mf).resolve(mf);
1605 break :end pre_footer_off + pre_footer_size;
1606 } else 0;
1607
1608 const min_file_offset = node_file_offset + pre_footer_end;
1609 const max_file_offset = node_file_offset + old_size - footers_size;
1610 // We can go anywhere between `min_file_offset` and `max_file_offset`.
1611 const candidate_file_offset = mf.flags.block_size.forward(min_file_offset);
1612 if (candidate_file_offset > max_file_offset) {
1613 return false;
1614 }
1615 break :range_file_offset candidate_file_offset;
1616 };
1617 assert(mf.flags.block_size.check(range_file_offset));
1618
1619 const range_size: u64 = range_size: {
1620 // For this strategy to be valid, the number of bytes we insert needs to be compatible with
1621 // the alignments of all nodes following us (and following our parents, their parents, etc).
1622 // We also probably don't want to trigger too many "node moved" events, since doing that
1623 // repeatedly could result in a lot of extra work. Therefore, while we traverse parents and
1624 // siblings to check their alignment requirements, we will also set an arbitrary limit on
1625 // the number of nodes we can move, and give up if we walk more than that.
1626 const max_moved_nodes = 32;
1627 var num_moved: u32 = 0;
1628 var cur_ni = ni;
1629 // Alignment required for `range_size`: initially the block size (required for the syscall),
1630 // then updated as we traverse based on how the operation would affect surrounding nodes.
1631 var need_range_align: Alignment = mf.flags.block_size.max(ni.alignment(mf));
1632 while (true) {
1633 // `cur_ni` will grow as a result of the range insertion. Its size must be well-aligned.
1634 need_range_align = need_range_align.max(cur_ni.alignment(mf));
1635
1636 // Siblings following `cur_ni` don't get bigger, but their offsets change.
1637 while (cur_ni.next(mf).unwrap()) |next_ni| {
1638 // Only floating children need well-aligned offsets.
1639 if (next_ni.position(mf) == .floating) {
1640 need_range_align = need_range_align.max(next_ni.alignment(mf));
1641 }
1642 num_moved += 1;
1643 if (num_moved > max_moved_nodes) return false;
1644 cur_ni = next_ni;
1151 }1645 }
11521646
1153 return;1647 // Move up to the parent.
1648 cur_ni = cur_ni.parent(mf).unwrap() orelse break;
1649 }
1650 // Traversal done. We didn't hit `max_moved_nodes`, so now we can use the computed alignment
1651 // requirement to figure out whether we're actually going to insert a range.
1652 if (need_range_align.check(requested_range_size)) {
1653 break :range_size requested_range_size;
1654 }
1655 // Perhaps we're allowed to grow by more than `requested_range_size`?
1656 switch (grow_mode) {
1657 .exact => return false,
1658 .minimum => {
1659 const candidate_range_size = need_range_align.forward(min_range_size);
1660 // Allow growing by up to 50% more than was requested.
1661 if (candidate_range_size <= requested_range_size +| requested_range_size / 2) {
1662 break :range_size candidate_range_size;
1663 } else {
1664 return false;
1665 }
1666 },
1667 }
1668 };
1669
1670 // This `range_size` is compatible with everyone's alignment requirements, and we won't move too
1671 // many nodes, so let's do it!
1672
1673 mf.memory_map.write(mf.io) catch |err| {
1674 mf.io_err = switch (err) {
1675 error.Canceled => |e| return e,
1676 error.WouldBlock => error.Unexpected, // file was not opened as non-blocking
1677 error.NotOpenForWriting => error.Unexpected, // we definitely opened the file for writing
1678 else => |e| e,
1679 };
1680 return error.MappedFileIo;
1681 };
1682
1683 // If we happen to be inserting at the very end of the file, we need to resize the file instead
1684 // of using `FALLOCATE_FL_INSERT_RANGE`.
1685 if (range_file_offset == Node.Index.root.location(mf).resolve(mf)[1]) {
1686 mf.memory_map.file.setLength(mf.io, range_file_offset + range_size) catch |err| switch (err) {
1687 error.Canceled => |e| return e,
1688 else => |e| {
1689 mf.io_err = e;
1690 return error.MappedFileIo;
1691 },
1692 };
1693 } else {
1694 while (true) switch (linux.errno(linux.fallocate(
1695 mf.memory_map.file.handle,
1696 linux.FALLOC.FL_INSERT_RANGE,
1697 @intCast(range_file_offset),
1698 @intCast(range_size),
1699 ))) {
1700 .SUCCESS => break,
1701 .INTR => continue,
1702 .NOSYS, .OPNOTSUPP => {
1703 // After all that setup work, it turns out the operation is actually unsupported!
1704 mf.flags.fallocate_insert_range_unsupported = true;
1705 return false;
1706 },
1707 else => |e| {
1708 mf.io_err = switch (e) {
1709 .SUCCESS, .INTR, .NOSYS, .OPNOTSUPP => unreachable, // handled above
1710 .BADF => unreachable,
1711 .FBIG => unreachable,
1712 .INVAL => unreachable,
1713 .IO => error.InputOutput,
1714 .NODEV => error.NotFile,
1715 .NOSPC => error.NoSpaceLeft,
1716 .PERM => error.PermissionDenied,
1717 .SPIPE => error.Unseekable,
1718 .TXTBSY => error.FileBusy,
1719 else => std.posix.unexpectedErrno(e),
1720 };
1721 return error.MappedFileIo;
1722 },
1723 };
1724 }
1725
1726 // We did it! Now to update all the sizes and offsets. This loop is exactly the same shape as
1727 // above, except we're updating locations instead of checking alignments.
1728 var cur_ni = ni;
1729 while (true) {
1730 const this_offset, const this_old_size = cur_ni.location(mf).resolve(mf);
1731 if (cur_ni == .root) {
1732 try mf.ensureTotalCapacityPrecise(@intCast(this_old_size + range_size));
1154 }1733 }
1734 try cur_ni.setLocation(mf, gpa, this_offset, this_old_size + range_size);
1735
1736 while (cur_ni.next(mf).unwrap()) |next_ni| {
1737 const next_old_offset, const next_size = next_ni.location(mf).resolve(mf);
1738 try next_ni.setLocation(mf, gpa, next_old_offset + range_size, next_size);
1739 cur_ni = next_ni;
1740 }
1741
1742 cur_ni = cur_ni.parent(mf).unwrap() orelse break;
1155 }1743 }
11561744
1157 const forward_offset = new_alignment.forward(@intCast(old_offset));1745 // The only thing left is to update the offsets of any footers inside of `ni`.
1158 if (forward_offset + new_size <= trailing_end) {1746 if (ni.firstFooter(mf).unwrap()) |first_footer_ni| {
1159 // Shift into the free space if possible1747 var footer_ni = first_footer_ni;
1160 try mf.ensureCapacityForSetLocation(gpa);1748 while (true) {
1161 if (node.flags.has_content) {1749 const old_footer_offset, const footer_size = footer_ni.location(mf).resolve(mf);
1162 const old_file_offset = ni.fileLocation(mf, false).offset;1750 try footer_ni.setLocation(mf, gpa, old_footer_offset + range_size, footer_size);
1163 const new_file_offset = (old_file_offset - old_offset) + forward_offset;1751 footer_ni = footer_ni.next(mf).unwrap() orelse break;
1164 if (new_file_offset < old_file_offset + size) {
1165 @memmove(
1166 mf.memory_map.memory[@intCast(new_file_offset)..][0..@intCast(size)],
1167 mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)],
1168 );
1169 } else try mf.moveRange(old_file_offset, new_file_offset, size);
1170 @memset(mf.memory_map.memory[@intCast(new_file_offset + size)..][0..@intCast(new_size - size)], 0);
1171 }1752 }
1753 }
11721754
1173 ni.setLocationAssumeCapacity(mf, forward_offset, new_size);1755 return true;
1174 } else {1756}
1175 const temp_size = new_alignment.forward(@intCast(new_size + 1));1757
1176 try mf.resizeNode(gpa, ni, temp_size);1758/// Ensures that `parent_ni` has at least `extra_capacity` padding bytes following its current
1177 const new_offset, _ = ni.location(mf).resolve(mf);1759/// headers, so that the headers can grow into that space.
11781760fn ensureAdditionalHeaderCapacity(
1179 try mf.ensureCapacityForSetLocation(gpa);1761 mf: *MappedFile,
11801762 gpa: Allocator,
1181 // Non-fixed nodes may now be aligned if the resize moved them1763 parent_ni: Node.Index,
1182 const new_forward_offset = new_alignment.forward(@intCast(new_offset));1764 extra_capacity: u64,
1183 const final_offset = if (new_forward_offset != new_offset) final_offset: {1765) Error!void {
1184 if (node.flags.has_content) {1766 _, const parent_size = parent_ni.location(mf).resolve(mf);
1185 const old_file_offset = ni.fileLocation(mf, false).offset;1767
1186 const new_file_offset = (old_file_offset - new_offset) + new_forward_offset;1768 const last_header_oni = parent_ni.lastHeader(mf);
1187 @memmove(1769 const first_footer_oni = parent_ni.firstFooter(mf);
1188 mf.memory_map.memory[@intCast(new_file_offset)..][0..@intCast(size)],1770
1189 mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)],1771 const headers_size: u64 = headers_size: {
1190 );1772 const last_header_ni = last_header_oni.unwrap() orelse break :headers_size 0;
1191 @memset(mf.memory_map.memory[@intCast(old_file_offset)..@intCast(new_file_offset)], 0);1773 const last_header_off, const last_header_size = last_header_ni.location(mf).resolve(mf);
1774 break :headers_size last_header_off + last_header_size;
1775 };
1776
1777 const footers_size: u64 = footers_size: {
1778 const first_footer_ni = first_footer_oni.unwrap() orelse break :footers_size 0;
1779 const first_footer_off, _ = first_footer_ni.location(mf).resolve(mf);
1780 break :footers_size parent_size - first_footer_off;
1781 };
1782
1783 const first_floating_oni: Node.Index.Optional = if (last_header_oni.unwrap()) |last_header_ni| first_floating: {
1784 const after_header_ni = last_header_ni.next(mf).unwrap() orelse break :first_floating .none;
1785 break :first_floating switch (after_header_ni.position(mf)) {
1786 .header => unreachable,
1787 .floating => .wrap(after_header_ni),
1788 .footer => .none,
1789 };
1790 } else first_floating: {
1791 const first_ni = parent_ni.first(mf).unwrap() orelse break :first_floating .none;
1792 break :first_floating switch (first_ni.position(mf)) {
1793 .header => unreachable,
1794 .floating => .wrap(first_ni),
1795 .footer => .none,
1796 };
1797 };
1798 const first_floating_ni = first_floating_oni.unwrap() orelse {
1799 // This node has only headers and footers.
1800 const min_parent_size = headers_size + extra_capacity + footers_size;
1801 if (parent_size < min_parent_size) {
1802 const new_parent_size = parent_ni.alignment(mf).forward(
1803 min_parent_size +| min_parent_size / growth_factor,
1804 );
1805 try mf.growNode(gpa, parent_ni, new_parent_size, .minimum);
1806 }
1807 return;
1808 };
1809
1810 const last_floating_ni = if (first_footer_oni.unwrap()) |first_footer_ni| last_floating: {
1811 break :last_floating first_footer_ni.prev(mf).unwrap().?;
1812 } else last_floating: {
1813 break :last_floating parent_ni.last(mf).unwrap().?;
1814 };
1815 assert(last_floating_ni.position(mf) == .floating); // we know `parent_ni` contains at least `first_floating_ni`
1816
1817 // Find the first floating child, if any, which does not overlap the new header space.
1818 const first_good_floating_oni: Node.Index.Optional = first_good_floating: {
1819 var floating_ni = first_floating_ni;
1820 while (true) {
1821 const floating_offset, _ = floating_ni.location(mf).resolve(mf);
1822 if (floating_offset >= headers_size + extra_capacity) {
1823 break :first_good_floating .wrap(floating_ni);
1824 }
1825 const next_ni = floating_ni.next(mf).unwrap() orelse {
1826 break :first_good_floating .none;
1827 };
1828 switch (next_ni.position(mf)) {
1829 .header => unreachable, // after the last header
1830 .floating => floating_ni = next_ni,
1831 .footer => break :first_good_floating .none,
1192 }1832 }
1833 }
1834 };
11931835
1194 break :final_offset new_forward_offset;1836 if (first_good_floating_oni == first_floating_ni.toOptional()) {
1195 } else new_offset;1837 // None of the floating children are in our way! That means there's already enough space.
1838 return;
1839 }
1840
1841 const last_moving_ni = if (first_good_floating_oni.unwrap()) |first_good_floating_ni| last_moving: {
1842 break :last_moving first_good_floating_ni.prev(mf).unwrap().?;
1843 } else last_moving: {
1844 break :last_moving last_floating_ni;
1845 };
1846
1847 // We are going to move all nodes between `first_floating_ni` and `last_moving_ni` to the end of
1848 // the parent. We'll move all the node data in one big block.
11961849
1197 ni.setLocationAssumeCapacity(mf, final_offset, new_size);1850 const moving_offset: u64 = first_floating_ni.location(mf).resolve(mf)[0];
1851 const moving_size: u64 = size: {
1852 const last_moving_off, const last_moving_size = last_moving_ni.location(mf).resolve(mf);
1853 break :size last_moving_off + last_moving_size - moving_offset;
1854 };
1855
1856 var moving_alignment: Alignment = .@"1";
1857 var moving_has_content = false; // optimization: no need to move data if it's all uninitialized
1858 {
1859 var cur_ni = first_floating_ni;
1860 while (true) {
1861 moving_alignment = moving_alignment.max(cur_ni.alignment(mf));
1862 moving_has_content = moving_has_content or cur_ni.get(mf).flags.has_content;
1863 if (cur_ni == last_moving_ni) break;
1864 cur_ni = cur_ni.next(mf).unwrap().?;
1865 }
1866 }
1867
1868 const first_free_offset = free_offset: {
1869 const last_floating_off, const last_floating_size = last_floating_ni.location(mf).resolve(mf);
1870 break :free_offset @max(last_floating_off + last_floating_size, headers_size + extra_capacity);
1871 };
1872 // Alignment is a little tricky here. We don't necessarily want the new offset to be aligned to
1873 // `moving_alignment` exactly, because if (e.g.) the first floating node is align(2) and the
1874 // second is align(4), then the overall range we're moving may not be 4-byte aligned even though
1875 // one of the nodes is. Instead, the old and new offsets must be congruent modulo the alignment.
1876 const aligned_dest_offset = moving_alignment.forward(first_free_offset);
1877 const dest_offset = aligned_dest_offset + (moving_offset - moving_alignment.backward(moving_offset));
1878 assert(dest_offset % moving_alignment.toByteUnits() == moving_offset % moving_alignment.toByteUnits());
1879
1880 // This expression is correct because `dest_offset` is after all floating nodes (except the ones
1881 // we're moving there of course).
1882 const min_parent_size = dest_offset + moving_size + footers_size;
1883 if (parent_size < min_parent_size) {
1884 const new_parent_size = parent_ni.alignment(mf).forward(
1885 min_parent_size +| min_parent_size / growth_factor,
1886 );
1887 try mf.growNode(gpa, parent_ni, new_parent_size, .minimum);
1888 }
1889
1890 if (moving_has_content) {
1891 const parent_file_off = parent_ni.fileLocation(mf, false).offset;
1892 try mf.moveRange(
1893 parent_file_off + moving_offset,
1894 parent_file_off + dest_offset,
1895 moving_size,
1896 );
1897 }
1898
1899 // Remove everything between `first_floating_ni` and `last_moving_ni` from the linked list, then
1900 // re-insert them in their new position.
1901 try mf.removeNodesFromChildList(gpa, first_floating_ni, last_moving_ni);
1902 try mf.addNodesToChildListBefore(gpa, first_footer_oni, first_floating_ni, last_moving_ni);
1903
1904 // Finally, we need to update the locations of all of those nodes.
1905 var cur_ni = first_floating_ni;
1906 while (true) {
1907 assert(cur_ni.position(mf) == .floating);
1908 const old_offset, const old_size = cur_ni.location(mf).resolve(mf);
1909 const new_offset = old_offset - moving_offset + dest_offset;
1910 assert(cur_ni.alignment(mf).check(new_offset));
1911 try cur_ni.setLocation(mf, gpa, new_offset, old_size);
1912 if (cur_ni == last_moving_ni) break;
1913 cur_ni = cur_ni.next(mf).unwrap().?;
1914 }
1915}
1916
1917/// Ensures that `parent_ni` has at least `extra_capacity` padding bytes preceding its current
1918/// footers, so that the footers can grow into that space.
1919fn ensureAdditionalFooterCapacity(
1920 mf: *MappedFile,
1921 gpa: Allocator,
1922 parent_ni: Node.Index,
1923 extra_capacity: u64,
1924) Error!void {
1925 // This is way easier than the header case, because we don't need to actually move anything; we
1926 // just need to expand the parent if there isn't space, and that will add padding after the
1927 // parent's floating children, which is exactly where we want it.
1928
1929 const first_footer_oni = parent_ni.firstFooter(mf);
1930
1931 _, const parent_size = parent_ni.location(mf).resolve(mf);
1932
1933 const footers_size: u64 = footers_size: {
1934 const first_footer_ni = first_footer_oni.unwrap() orelse break :footers_size 0;
1935 const first_footer_off, _ = first_footer_ni.location(mf).resolve(mf);
1936 break :footers_size parent_size - first_footer_off;
1937 };
1938
1939 const header_and_floating_end: u64 = end: {
1940 const before_footers_oni = if (first_footer_oni.unwrap()) |first_footer_ni| before_footers: {
1941 break :before_footers first_footer_ni.prev(mf);
1942 } else before_footers: {
1943 break :before_footers parent_ni.last(mf);
1944 };
1945 const before_footers_ni = before_footers_oni.unwrap() orelse break :end 0;
1946 const offset, const size = before_footers_ni.location(mf).resolve(mf);
1947 break :end offset + size;
1948 };
1949
1950 assert(header_and_floating_end + footers_size <= parent_size);
1951
1952 const min_parent_size = header_and_floating_end + footers_size + extra_capacity;
1953 if (parent_size < min_parent_size) {
1954 const new_parent_size = parent_ni.alignment(mf).forward(
1955 min_parent_size +| min_parent_size / growth_factor,
1956 );
1957 try mf.growNode(gpa, parent_ni, new_parent_size, .minimum);
1958 }
1959}
1960
1961fn removeNodesFromChildList(
1962 mf: *MappedFile,
1963 gpa: Allocator,
1964 first_remove_ni: Node.Index,
1965 last_remove_ni: Node.Index,
1966) Allocator.Error!void {
1967 const parent_ni = first_remove_ni.parent(mf).unwrap().?;
1968 assert(last_remove_ni.parent(mf).unwrap().? == parent_ni);
1969
1970 const prev_oni = first_remove_ni.prev(mf);
1971 const next_oni = last_remove_ni.next(mf);
1972
1973 if (prev_oni.unwrap()) |prev_ni| {
1974 assert(prev_ni.next(mf).unwrap().? == first_remove_ni);
1975 try prev_ni.setNext(gpa, next_oni, mf);
1976 } else {
1977 assert(parent_ni.first(mf).unwrap().? == first_remove_ni);
1978 parent_ni.get(mf).first = next_oni;
1979 }
1980
1981 if (next_oni.unwrap()) |next_ni| {
1982 assert(next_ni.prev(mf).unwrap().? == last_remove_ni);
1983 next_ni.get(mf).prev = prev_oni;
1984 } else {
1985 assert(parent_ni.last(mf).unwrap().? == last_remove_ni);
1986 parent_ni.get(mf).last = prev_oni;
1987 }
1988}
1989/// Assumes `first_add_ni` and `last_add_ni` are connected, and that all nodes in between them
1990/// already have their `parent` field correctly populated.
1991///
1992/// To add a single node, set `first_add_ni` equal to `last_add_ni`.
1993fn addNodesToChildListBefore(
1994 mf: *MappedFile,
1995 gpa: Allocator,
1996 /// `null` means to add at the end of the parent.
1997 next_oni: Node.Index.Optional,
1998 first_add_ni: Node.Index,
1999 last_add_ni: Node.Index,
2000) Allocator.Error!void {
2001 const parent_ni = first_add_ni.parent(mf).unwrap().?;
2002 assert(last_add_ni.parent(mf).unwrap().? == parent_ni);
2003 if (next_oni.unwrap()) |next_ni| {
2004 assert(next_ni.parent(mf).unwrap().? == parent_ni);
2005 }
2006
2007 const prev_oni: Node.Index.Optional = if (next_oni.unwrap()) |next_ni| prev: {
2008 break :prev next_ni.prev(mf);
2009 } else prev: {
2010 break :prev parent_ni.last(mf);
2011 };
2012
2013 first_add_ni.get(mf).prev = prev_oni;
2014 try last_add_ni.setNext(gpa, next_oni, mf);
2015
2016 if (prev_oni.unwrap()) |prev_ni| {
2017 assert(prev_ni.next(mf) == next_oni);
2018 try prev_ni.setNext(gpa, .wrap(first_add_ni), mf);
2019 } else {
2020 assert(parent_ni.first(mf) == next_oni);
2021 parent_ni.get(mf).first = .wrap(first_add_ni);
1198 }2022 }
2023
2024 if (next_oni.unwrap()) |next_ni| {
2025 assert(next_ni.prev(mf) == prev_oni);
2026 next_ni.get(mf).prev = .wrap(last_add_ni);
2027 } else {
2028 assert(parent_ni.last(mf) == prev_oni);
2029 parent_ni.get(mf).last = .wrap(last_add_ni);
2030 }
2031}
2032fn addNodesToChildListAfter(
2033 mf: *MappedFile,
2034 gpa: Allocator,
2035 /// `null` means to add at the start of the parent.
2036 prev_oni: Node.Index.Optional,
2037 first_add_ni: Node.Index,
2038 last_add_ni: Node.Index,
2039) Allocator.Error!void {
2040 const next_oni: Node.Index.Optional = next: {
2041 if (prev_oni.unwrap()) |prev_ni| break :next prev_ni.next(mf);
2042 const parent_ni = first_add_ni.parent(mf).unwrap().?;
2043 break :next parent_ni.first(mf);
2044 };
2045 return mf.addNodesToChildListBefore(gpa, next_oni, first_add_ni, last_add_ni);
2046}
2047
2048fn realignNode(
2049 mf: *MappedFile,
2050 gpa: Allocator,
2051 ni: Node.Index,
2052 new_alignment: Alignment,
2053) Error!void {
2054 mf.nodes_lock.assertUnlocked();
2055
2056 const old_offset, const old_size = ni.location(mf).resolve(mf);
2057
2058 if (ni == .root or ni.position(mf) != .floating) {
2059 // Only this node's size is aligned, not its offset.
2060 if (!new_alignment.check(old_size)) {
2061 assert(new_alignment.compare(.gt, ni.alignment(mf)));
2062 try mf.growNode(
2063 gpa,
2064 ni,
2065 new_alignment.forward(old_size),
2066 .exact, // because `growNode` is not aware that the size needs to match `new_alignment`
2067 );
2068 }
2069 } else {
2070 // This is a floating node, so its size and offset are both aligned.
2071 if (!new_alignment.check(old_offset) or !new_alignment.check(old_size)) {
2072 assert(new_alignment.compare(.gt, ni.alignment(mf)));
2073 try mf.growFloatingNodeWithAlignment(
2074 gpa,
2075 ni,
2076 new_alignment,
2077 new_alignment.forward(old_size),
2078 .minimum,
2079 );
2080 }
2081 }
2082
2083 ni.get(mf).flags.alignment = new_alignment;
1199}2084}
12002085
1201fn updateWriters(mf: *MappedFile) void {2086fn updateWriters(mf: *MappedFile) void {
...@@ -1206,10 +2091,47 @@ fn updateWriters(mf: *MappedFile) void {...@@ -1206,10 +2091,47 @@ fn updateWriters(mf: *MappedFile) void {
1206 }2091 }
1207}2092}
12082093
1209fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) (Io.Cancelable || IoError)!void {2094fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) Error!void {
1210 // make a copy of this node at the new location2095 if (old_file_offset == new_file_offset) return;
1211 try mf.copyRange(old_file_offset, new_file_offset, size);2096
1212 // delete the copy of this node at the old location2097 if (old_file_offset >= new_file_offset + size or
2098 new_file_offset >= old_file_offset + size)
2099 {
2100 const n = try mf.copyFileRange(
2101 mf.memory_map.file,
2102 old_file_offset,
2103 new_file_offset,
2104 size,
2105 );
2106 @memcpy(
2107 mf.memory_map.memory[@intCast(new_file_offset + n)..][0..@intCast(size - n)],
2108 mf.memory_map.memory[@intCast(old_file_offset + n)..][0..@intCast(size - n)],
2109 );
2110
2111 try mf.zeroRange(old_file_offset, size);
2112
2113 return;
2114 }
2115
2116 // TODO: if the non-overlapping region is greater than or equal to a filesystem block, is it
2117 // ever worth doing multiple `copyFileRange` calls instead of a big `@memmove`?
2118
2119 @memmove(
2120 mf.memory_map.memory[@intCast(new_file_offset)..][0..@intCast(size)],
2121 mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)],
2122 );
2123
2124 if (new_file_offset > old_file_offset) {
2125 const clear_size = new_file_offset - old_file_offset;
2126 assert(clear_size < size);
2127 try mf.zeroRange(old_file_offset, clear_size);
2128 } else {
2129 const clear_size = old_file_offset - new_file_offset;
2130 assert(clear_size < size);
2131 try mf.zeroRange(new_file_offset + size, clear_size);
2132 }
2133}
2134fn zeroRange(mf: *MappedFile, file_offset: u64, size: u64) Error!void {
1213 if (is_linux and2135 if (is_linux and
1214 !mf.flags.fallocate_punch_hole_unsupported and2136 !mf.flags.fallocate_punch_hole_unsupported and
1215 size >= mf.flags.block_size.toByteUnits() * 2 - 1)2137 size >= mf.flags.block_size.toByteUnits() * 2 - 1)
...@@ -1217,147 +2139,149 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size:...@@ -1217,147 +2139,149 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size:
1217 while (true) switch (linux.errno(linux.fallocate(2139 while (true) switch (linux.errno(linux.fallocate(
1218 mf.memory_map.file.handle,2140 mf.memory_map.file.handle,
1219 linux.FALLOC.FL_PUNCH_HOLE | linux.FALLOC.FL_KEEP_SIZE,2141 linux.FALLOC.FL_PUNCH_HOLE | linux.FALLOC.FL_KEEP_SIZE,
1220 @intCast(old_file_offset),2142 @intCast(file_offset),
1221 @intCast(size),2143 @intCast(size),
1222 ))) {2144 ))) {
1223 .SUCCESS => return,2145 .SUCCESS => return,
1224 .INTR => continue,2146 .INTR => continue,
1225 .BADF, .FBIG, .INVAL => unreachable,
1226 .IO => return error.InputOutput,
1227 .NODEV => return error.NotFile,
1228 .NOSPC => return error.NoSpaceLeft,
1229 .NOSYS, .OPNOTSUPP => {2147 .NOSYS, .OPNOTSUPP => {
1230 mf.flags.fallocate_punch_hole_unsupported = true;2148 mf.flags.fallocate_punch_hole_unsupported = true;
1231 break; // fall back to slow path2149 break; // fall back to slow path
1232 },2150 },
1233 .PERM => return error.PermissionDenied,2151 else => |e| {
1234 .SPIPE => return error.Unseekable,2152 mf.io_err = switch (e) {
1235 .TXTBSY => return error.FileBusy,2153 .SUCCESS, .INTR, .NOSYS, .OPNOTSUPP => unreachable, // handled above
1236 else => |e| return std.posix.unexpectedErrno(e),2154 .BADF => unreachable,
2155 .FBIG => unreachable,
2156 .INVAL => unreachable,
2157 .IO => error.InputOutput,
2158 .NODEV => error.NotFile,
2159 .NOSPC => error.NoSpaceLeft,
2160 .PERM => error.PermissionDenied,
2161 .SPIPE => error.Unseekable,
2162 .TXTBSY => error.FileBusy,
2163 else => std.posix.unexpectedErrno(e),
2164 };
2165 return error.MappedFileIo;
2166 },
1237 };2167 };
1238 }2168 }
1239 @memset(mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)], 0);2169 @memset(mf.memory_map.memory[@intCast(file_offset)..][0..@intCast(size)], 0);
1240}
1241
1242fn copyRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) (Io.Cancelable || IoError)!void {
1243 const copy_size = try mf.copyFileRange(mf.memory_map.file, old_file_offset, new_file_offset, size);
1244 if (copy_size < size) @memcpy(
1245 mf.memory_map.memory[@intCast(new_file_offset + copy_size)..][0..@intCast(size - copy_size)],
1246 mf.memory_map.memory[@intCast(old_file_offset + copy_size)..][0..@intCast(size - copy_size)],
1247 );
1248}2170}
1249
1250fn copyFileRange(2171fn copyFileRange(
1251 mf: *MappedFile,2172 mf: *MappedFile,
1252 old_file: Io.File,2173 old_file: Io.File,
1253 old_file_offset: u64,2174 old_file_offset: u64,
1254 new_file_offset: u64,2175 new_file_offset: u64,
1255 size: u64,2176 size: u64,
1256) (Io.Cancelable || IoError)!u64 {2177) Error!u64 {
2178 if (!is_linux or mf.flags.copy_file_range_unsupported) {
2179 return 0;
2180 }
2181
2182 const min_size = mf.flags.block_size.toByteUnits() * 2 - 1;
2183 if (size < min_size) return 0;
2184
1257 const io = mf.io;2185 const io = mf.io;
1258 mf.memory_map.write(io) catch |err| switch (err) {2186 mf.memory_map.write(io) catch |err| {
1259 error.WouldBlock => return error.Unexpected, // file was not opened as non-blocking2187 mf.io_err = switch (err) {
1260 error.NotOpenForWriting => return error.Unexpected, // we definitely opened the file for writing2188 error.Canceled => |e| return e,
1261 else => |e| return e,2189 error.WouldBlock => error.Unexpected, // file was not opened as non-blocking
2190 error.NotOpenForWriting => error.Unexpected, // we definitely opened the file for writing
2191 else => |e| e,
2192 };
2193 return error.MappedFileIo;
1262 };2194 };
1263 var remaining_size = size;2195 var remaining_size = size;
1264 if (is_linux and !mf.flags.copy_file_range_unsupported) {2196 var old_file_offset_mut: i64 = @intCast(old_file_offset);
1265 var old_file_offset_mut: i64 = @intCast(old_file_offset);2197 var new_file_offset_mut: i64 = @intCast(new_file_offset);
1266 var new_file_offset_mut: i64 = @intCast(new_file_offset);2198 while (remaining_size >= min_size) {
1267 while (remaining_size >= mf.flags.block_size.toByteUnits() * 2 - 1) {2199 const copy_len = linux.copy_file_range(
1268 const copy_len = linux.copy_file_range(2200 old_file.handle,
1269 old_file.handle,2201 &old_file_offset_mut,
1270 &old_file_offset_mut,2202 mf.memory_map.file.handle,
1271 mf.memory_map.file.handle,2203 &new_file_offset_mut,
1272 &new_file_offset_mut,2204 @intCast(remaining_size),
1273 @intCast(remaining_size),2205 0,
1274 0,2206 );
1275 );2207 switch (linux.errno(copy_len)) {
1276 switch (linux.errno(copy_len)) {2208 .SUCCESS => {
1277 .SUCCESS => {2209 if (copy_len == 0) break;
1278 if (copy_len == 0) break;2210 remaining_size -= copy_len;
1279 remaining_size -= copy_len;2211 if (remaining_size == 0) break;
1280 if (remaining_size == 0) break;2212 },
1281 },2213 .INTR => continue,
1282 .INTR => continue,2214 .NOSYS, .OPNOTSUPP, .XDEV => {
1283 .BADF, .FBIG, .INVAL, .OVERFLOW => unreachable,2215 mf.flags.copy_file_range_unsupported = true;
1284 .IO => return error.InputOutput,2216 break;
1285 .ISDIR => return error.IsDir,2217 },
1286 .NOMEM => return error.SystemResources,2218 else => |e| {
1287 .NOSPC => return error.NoSpaceLeft,2219 mf.io_err = switch (e) {
1288 .NOSYS, .OPNOTSUPP, .XDEV => {2220 .SUCCESS, .INTR, .NOSYS, .OPNOTSUPP, .XDEV => unreachable, // handled above
1289 mf.flags.copy_file_range_unsupported = true;2221 .BADF => unreachable,
1290 break;2222 .FBIG => unreachable,
1291 },2223 .INVAL => unreachable,
1292 .PERM => return error.PermissionDenied,2224 .OVERFLOW => unreachable,
1293 .TXTBSY => return error.FileBusy,2225 .IO => error.InputOutput,
1294 else => |e| return std.posix.unexpectedErrno(e),2226 .ISDIR => error.IsDir,
1295 }2227 .NOMEM => error.SystemResources,
2228 .NOSPC => error.NoSpaceLeft,
2229 .PERM => error.PermissionDenied,
2230 .TXTBSY => error.FileBusy,
2231 else => std.posix.unexpectedErrno(e),
2232 };
2233 return error.MappedFileIo;
2234 },
1296 }2235 }
1297 }2236 }
1298 return size - remaining_size;2237 return size - remaining_size;
1299}2238}
13002239
1301fn ensureCapacityForSetLocation(mf: *MappedFile, gpa: Allocator) Allocator.Error!void {
1302 try mf.large.ensureUnusedCapacity(gpa, 2);
1303 try mf.updates.ensureUnusedCapacity(gpa, 2);
1304}
1305
1306pub fn ensureTotalCapacity(mf: *MappedFile, new_capacity: usize) Error!void {2240pub fn ensureTotalCapacity(mf: *MappedFile, new_capacity: usize) Error!void {
1307 mf.ensureTotalCapacityInner(new_capacity) catch |err| switch (err) {
1308 error.OutOfMemory,
1309 error.Canceled,
1310 => |e| return e,
1311
1312 else => |e| {
1313 mf.io_err = e;
1314 return error.MappedFileIo;
1315 },
1316 };
1317}
1318fn ensureTotalCapacityInner(mf: *MappedFile, new_capacity: usize) (Allocator.Error || Io.Cancelable || IoError)!void {
1319 if (mf.memory_map.memory.len >= new_capacity) return;2241 if (mf.memory_map.memory.len >= new_capacity) return;
1320 try mf.ensureTotalCapacityPreciseInner(new_capacity +| new_capacity / growth_factor);2242 try mf.ensureTotalCapacityPrecise(new_capacity +| new_capacity / growth_factor);
1321}2243}
13222244
1323pub fn ensureTotalCapacityPrecise(mf: *MappedFile, new_capacity: usize) Error!void {2245pub fn ensureTotalCapacityPrecise(mf: *MappedFile, new_capacity: usize) Error!void {
1324 mf.ensureTotalCapacityPreciseInner(new_capacity) catch |err| switch (err) {
1325 error.OutOfMemory,
1326 error.Canceled,
1327 => |e| return e,
1328
1329 else => |e| {
1330 mf.io_err = e;
1331 return error.MappedFileIo;
1332 },
1333 };
1334}
1335fn ensureTotalCapacityPreciseInner(mf: *MappedFile, new_capacity: usize) (Allocator.Error || Io.Cancelable || IoError)!void {
1336 if (mf.memory_map.memory.len >= new_capacity) return;2246 if (mf.memory_map.memory.len >= new_capacity) return;
1337 const io = mf.io;2247 const io = mf.io;
1338 const aligned_capacity = mf.flags.block_size.forward(new_capacity);2248 const aligned_capacity: usize = @intCast(
2249 mf.flags.block_size.forward(new_capacity),
2250 );
13392251
1340 if (mf.memory_map.memory.len > 0) {2252 if (mf.memory_map.memory.len > 0) {
1341 if (mf.memory_map.setLength(io, aligned_capacity)) |_| {2253 if (mf.memory_map.setLength(io, aligned_capacity)) |_| {
1342 return;2254 return;
1343 } else |err| switch (err) {2255 } else |err| switch (err) {
1344 error.OperationUnsupported => {},2256 error.OperationUnsupported => {},
1345 else => |e| return e,2257 error.OutOfMemory, error.Canceled => |e| return e,
2258 else => |e| {
2259 mf.io_err = e;
2260 return error.MappedFileIo;
2261 },
1346 }2262 }
13472263
1348 mf.memory_map.write(io) catch |err| switch (err) {2264 mf.memory_map.write(io) catch |err| {
1349 error.WouldBlock => return error.Unexpected, // file was not opened as non-blocking2265 mf.io_err = switch (err) {
1350 error.NotOpenForWriting => return error.Unexpected, // we definitely opened the file for writing2266 error.Canceled => |e| return e,
1351 else => |e| return e,2267 error.WouldBlock => error.Unexpected, // file was not opened as non-blocking
2268 error.NotOpenForWriting => error.Unexpected, // we definitely opened the file for writing
2269 else => |e| e,
2270 };
2271 return error.MappedFileIo;
1352 };2272 };
1353 unmap(mf);2273 unmap(mf);
1354 }2274 }
13552275
1356 const file = mf.memory_map.file;2276 const file = mf.memory_map.file;
1357 mf.memory_map = Io.File.MemoryMap.create(io, file, .{ .len = aligned_capacity }) catch |err| switch (err) {2277 mf.memory_map = Io.File.MemoryMap.create(io, file, .{ .len = aligned_capacity }) catch |err| {
1358 error.WouldBlock => return error.Unexpected, // file was not opened as non-blocking2278 mf.io_err = switch (err) {
1359 error.NotOpenForReading => return error.Unexpected, // we definitely opened the file for writing2279 error.OutOfMemory, error.Canceled => |e| return e,
1360 else => |e| return e,2280 error.WouldBlock => error.Unexpected, // file was not opened as non-blocking
2281 error.NotOpenForReading => error.Unexpected, // we definitely opened the file for writing
2282 else => |e| e,
2283 };
2284 return error.MappedFileIo;
1361 };2285 };
1362}2286}
13632287
...@@ -1376,7 +2300,7 @@ pub fn flush(mf: *MappedFile) (Io.Cancelable || error{MappedFileIo})!void {...@@ -1376,7 +2300,7 @@ pub fn flush(mf: *MappedFile) (Io.Cancelable || error{MappedFileIo})!void {
13762300
1377 error.WouldBlock, // file was not opened as non-blocking2301 error.WouldBlock, // file was not opened as non-blocking
1378 error.NotOpenForWriting, // we definitely opened the file for writing2302 error.NotOpenForWriting, // we definitely opened the file for writing
1379 error.ReadOnlyFileSystem,2303 error.ReadOnlyFileSystem, // again, we opened the file for writing
1380 => {2304 => {
1381 mf.io_err = error.Unexpected;2305 mf.io_err = error.Unexpected;
1382 return error.MappedFileIo;2306 return error.MappedFileIo;
...@@ -1399,213 +2323,278 @@ fn verify(mf: *MappedFile) void {...@@ -1399,213 +2323,278 @@ fn verify(mf: *MappedFile) void {
1399 assert(root.parent == .none);2323 assert(root.parent == .none);
1400 assert(root.prev == .none);2324 assert(root.prev == .none);
1401 assert(root.next == .none);2325 assert(root.next == .none);
1402 mf.verifyNode(Node.Index.root);2326 mf.verifyNode(.root);
1403}2327}
1404
1405fn verifyNode(mf: *MappedFile, parent_ni: Node.Index) void {2328fn verifyNode(mf: *MappedFile, parent_ni: Node.Index) void {
1406 const parent = parent_ni.get(mf);2329 const parent = parent_ni.get(mf);
1407 const parent_offset, const parent_size = parent.location().resolve(mf);2330 _, const parent_size = parent.location().resolve(mf);
1408 var prev_ni: Node.Index = .none;2331
2332 var prev_oni: Node.Index.Optional = .none;
1409 var prev_end: u64 = 0;2333 var prev_end: u64 = 0;
1410 var ni = parent.first;2334 var prev_pos: Node.Position = .header;
1411 while (true) {2335 var oni = parent.first;
1412 if (ni == .none) {2336 while (oni.unwrap()) |ni| {
1413 assert(parent.last == prev_ni);
1414 return;
1415 }
1416 const node = ni.get(mf);2337 const node = ni.get(mf);
1417 assert(node.parent == parent_ni);2338 assert(node.parent == parent_ni.toOptional());
2339 assert(node.prev == prev_oni);
2340
1418 const offset, const size = node.location().resolve(mf);2341 const offset, const size = node.location().resolve(mf);
1419 assert(node.flags.alignment.check(@intCast(offset)));
1420 assert(node.flags.alignment.check(@intCast(size)));
1421 const end = offset + size;2342 const end = offset + size;
1422 assert(end <= parent_offset + parent_size);2343
2344 assert(node.flags.alignment.check(size));
1423 assert(offset >= prev_end);2345 assert(offset >= prev_end);
1424 assert(node.prev == prev_ni);2346 assert(end <= parent_size);
2347
2348 switch (node.flags.position) {
2349 .header => {
2350 assert(prev_pos == .header);
2351 assert(offset == prev_end);
2352 },
2353 .floating => {
2354 assert(prev_pos != .footer);
2355 assert(node.flags.alignment.check(offset));
2356 },
2357 .footer => {
2358 if (prev_pos == .footer) assert(offset == prev_end);
2359 },
2360 }
2361
1425 mf.verifyNode(ni);2362 mf.verifyNode(ni);
1426 prev_ni = ni;2363
2364 prev_oni = .wrap(ni);
1427 prev_end = end;2365 prev_end = end;
1428 ni = node.next;2366 prev_pos = ni.position(mf);
2367
2368 oni = node.next;
2369 }
2370 assert(parent.last == prev_oni);
2371 if (prev_pos == .footer) {
2372 assert(prev_end == parent_size);
1429 }2373 }
1430}2374}
14312375
1432const testing = std.testing;2376test "fuzz node operations" {
1433fn testVerifyContent(mf: *@This(), ni: Node.Index, value: u8, init_len: usize) !void {2377 try std.testing.fuzz({}, fuzzOneNodeOperations, .{});
1434 // Not using std.mem.allEqual, so we can get useful output
1435 const slice = ni.slice(mf);
1436 var buf: [256]u8 = undefined;
1437 @memset(buf[0..init_len], value);
1438 @memset(buf[init_len..], 0);
1439 try testing.expectEqualSlices(u8, buf[0..slice.len], slice);
1440}2378}
2379fn fuzzOneNodeOperations(_: void, smith: *std.testing.Smith) anyerror!void {
2380 const gpa = std.testing.allocator;
2381 const io = std.testing.io;
14412382
1442test {2383 var tmp_dir = std.testing.tmpDir(.{});
1443 const gpa = testing.allocator;
1444
1445 var tmp_dir = testing.tmpDir(.{});
1446 defer tmp_dir.cleanup();2384 defer tmp_dir.cleanup();
14472385
1448 var file = try tmp_dir.dir.createFile(testing.io, "test.mf", .{ .read = true });2386 var tmp_file = try tmp_dir.dir.createFile(io, "test.mf", .{ .read = true });
1449 defer file.close(testing.io);2387 defer tmp_file.close(io);
14502388
1451 var mf = try init(file, gpa, testing.io);2389 var mf: MappedFile = try .init(tmp_file, gpa, io);
1452 defer mf.deinit(gpa);2390 defer mf.deinit(gpa);
14532391
1454 const a = try mf.addFirstChildNode(gpa, .root, .{ .fixed = true, .alignment = .@"4" });2392 var nodes: std.array_hash_map.Auto(MappedFile.Node.Index, struct {
1455 const c = try mf.addLastChildNode(gpa, .root, .{ .fixed = true, .alignment = .@"4" });2393 parent: MappedFile.Node.Index.Optional,
1456 const b = try mf.addNodeAfter(gpa, a, .{ .fixed = true, .alignment = .@"16" });2394 position: MappedFile.Node.Position,
1457 const d = try mf.addNodeAfter(gpa, b, .{ .alignment = .@"4" });2395 num_headers: u32,
2396 num_footers: u32,
2397 /// For leaf nodes, this value is whether we have initialized the contents of the node or
2398 /// not. For non-leaf nodes, this value is unspecified and should be ignored.
2399 initialized: bool,
2400 }) = .empty;
2401 defer nodes.deinit(gpa);
2402
2403 // When initializing a leaf node, we will place its 4-byte node index at the start of its range,
2404 // and the bitwise NOT of its node index at the end of its range (both little-endian). This is
2405 // just a simple way to put distinct values we can validate at all node boundaries.
2406
2407 try nodes.putNoClobber(gpa, .root, .{
2408 .parent = .none,
2409 .position = .floating,
2410 .num_headers = 0,
2411 .num_footers = 0,
2412 .initialized = false,
2413 });
2414
2415 // Allow a range of alignments, with most nodes having a small alignment of 1--32 bytes (most
2416 // commonly 1 byte), but with a small chance for some large alignments too.
2417 const alignment_weights: []const std.testing.Smith.Weight = comptime &.{
2418 .value(Alignment, .@"1", 20),
2419 .value(Alignment, .@"2", 5),
2420 .value(Alignment, .@"4", 5),
2421 .value(Alignment, .@"8", 5),
2422 .value(Alignment, .@"16", 5),
2423 .value(Alignment, .@"32", 5),
2424 .value(Alignment, .fromByteUnits(0x200), 1),
2425 .value(Alignment, .fromByteUnits(0x400), 1),
2426 .value(Alignment, .fromByteUnits(0x800), 1),
2427 .value(Alignment, .fromByteUnits(0x1000), 1),
2428 .value(Alignment, .fromByteUnits(0x2000), 1),
2429 .value(Alignment, .fromByteUnits(0x4000), 1),
2430 .value(Alignment, .fromByteUnits(0x8000), 1),
2431 };
14582432
1459 const a_init_size = 8;2433 const min_nonzero_size = 2 * @sizeOf(MappedFile.Node.Index);
1460 const b_init_size = 16;2434 const max_size = 0x10_000;
1461 const c_init_size = 24;2435 const initial_size_weights: []const std.testing.Smith.Weight = comptime &.{
1462 const d_init_size = 28;2436 // initially, make nodes just as likely to be empty as non-empty
2437 .value(u64, 0, max_size - min_nonzero_size + 1),
2438 .rangeAtMost(u64, min_nonzero_size, max_size, 1),
2439 };
14632440
1464 // Resize without content2441 while (!smith.eos()) switch (smith.value(enum { add, resize, realign })) {
1465 {2442 .add => {
1466 // Verify size is aligned forward2443 const parent_ni = nodes.keys()[smith.index(nodes.count())];
1467 try d.resize(&mf, gpa, d_init_size - 1);
1468 try a.resize(&mf, gpa, a_init_size - 2);
1469 try c.resize(&mf, gpa, c_init_size);
1470 try b.resize(&mf, gpa, b_init_size);
1471 mf.verify();
1472
1473 const a_loc, const a_size = a.location(&mf).resolve(&mf);
1474 const b_loc, const b_size = b.location(&mf).resolve(&mf);
1475 const c_loc, const c_size = c.location(&mf).resolve(&mf);
1476 _, const d_size = d.location(&mf).resolve(&mf);
1477 try testing.expect(a_size >= a_init_size);
1478 try testing.expect(b_size >= b_init_size);
1479 try testing.expect(c_size >= c_init_size);
1480 try testing.expect(d_size >= d_init_size);
1481 try testing.expect(b_loc >= a_loc + a_size);
1482 try testing.expect(c_loc >= b_loc + b_size);
1483 }
14842444
1485 const a_exp_size = 24;2445 const alignment = smith.valueWeighted(Alignment, alignment_weights);
1486 const b_exp_size = 28;2446 const size = alignment.forward(smith.valueWeighted(u64, initial_size_weights));
1487 const c_exp_size = 48;
1488 const d_exp_size = 32;
14892447
1490 // Resize with content2448 const position = smith.valueWeighted(Node.Position, comptime &.{
1491 {2449 // make floating nodes more common than header and footer nodes
1492 @memset(a.slice(&mf)[0..a_init_size], 0xaa);2450 .value(Node.Position, .header, 1),
1493 @memset(b.slice(&mf)[0..b_init_size], 0xbb);2451 .value(Node.Position, .footer, 1),
1494 @memset(c.slice(&mf)[0..c_init_size], 0xcc);2452 .value(Node.Position, .floating, 4),
1495 @memset(d.slice(&mf)[0..d_init_size], 0xdd);2453 });
14962454 const new_ni: Node.Index = switch (position) {
1497 try a.resize(&mf, gpa, a_exp_size);2455 .header => new_ni: {
1498 try b.resize(&mf, gpa, b_exp_size);2456 const parent_info = nodes.getPtr(parent_ni).?;
1499 try c.resize(&mf, gpa, c_exp_size);2457 const prev_oni: Node.Index.Optional = prev_oni: {
1500 try d.resize(&mf, gpa, d_exp_size);2458 const n = smith.valueRangeAtMost(u32, 0, parent_info.num_headers);
1501 mf.verify();2459 if (n == 0) break :prev_oni .none;
15022460 var cur_ni = parent_ni.first(&mf).unwrap().?;
1503 const a_loc, const a_size = a.location(&mf).resolve(&mf);2461 for (1..n) |_| cur_ni = cur_ni.next(&mf).unwrap().?;
1504 const b_loc, const b_size = b.location(&mf).resolve(&mf);2462 break :prev_oni .wrap(cur_ni);
1505 const c_loc, const c_size = c.location(&mf).resolve(&mf);2463 };
1506 _, const d_size = d.location(&mf).resolve(&mf);2464 const new_ni = try parent_ni.addHeaderChildAfter(&mf, gpa, prev_oni, .{
1507 try testing.expect(a_size >= a_exp_size);2465 .size = size,
1508 try testing.expect(b_size >= b_exp_size);2466 .alignment = alignment,
1509 try testing.expect(c_size >= c_exp_size);2467 });
1510 try testing.expect(d_size >= d_exp_size);2468 parent_info.num_headers += 1;
1511 try testing.expect(b_loc >= a_loc + a_size);2469 break :new_ni new_ni;
1512 try testing.expect(c_loc >= b_loc + b_size);2470 },
1513
1514 try testVerifyContent(&mf, a, 0xaa, a_init_size);
1515 try testVerifyContent(&mf, b, 0xbb, b_init_size);
1516 try testVerifyContent(&mf, c, 0xcc, c_init_size);
1517 try testVerifyContent(&mf, d, 0xdd, d_init_size);
1518 }
15192471
1520 const child_init: []const struct { std.mem.Alignment, usize } = &.{2472 .floating => try parent_ni.addFloatingChild(&mf, gpa, .{
1521 .{ .@"16", 16 },2473 .size = size,
1522 .{ .@"1", 1 },2474 .alignment = alignment,
1523 .{ .@"1", 19 },2475 }),
1524 .{ .@"1", 3 },2476
1525 .{ .@"8", 30 },2477 .footer => new_ni: {
1526 .{ .@"2", 5 },2478 const parent_info = nodes.getPtr(parent_ni).?;
1527 .{ .@"1", 60 },2479 const next_oni: Node.Index.Optional = next_oni: {
1528 .{ .@"2", 2 },2480 const n = smith.valueRangeAtMost(u32, 0, parent_info.num_footers);
1529 .{ .@"16", 32 },2481 if (n == 0) break :next_oni .none;
1530 };2482 var cur_ni = parent_ni.last(&mf).unwrap().?;
2483 for (1..n) |_| cur_ni = cur_ni.prev(&mf).unwrap().?;
2484 break :next_oni .wrap(cur_ni);
2485 };
2486 const new_ni = try parent_ni.addFooterChildBefore(&mf, gpa, next_oni, .{
2487 .size = size,
2488 .alignment = alignment,
2489 });
2490 parent_info.num_footers += 1;
2491 break :new_ni new_ni;
2492 },
2493 };
15312494
1532 var children: [child_init.len]Node.Index = undefined;2495 const initialize = size > 0 and smith.value(bool);
2496 if (initialize) {
2497 const slice = new_ni.slice(&mf);
2498 std.mem.writeInt(u32, slice[0..4], @backingInt(new_ni), .little);
2499 std.mem.writeInt(u32, slice[slice.len - 4 ..][0..4], ~@backingInt(new_ni), .little);
2500 }
15332501
1534 // Differently-aligned fixed sibling nodes2502 try nodes.putNoClobber(gpa, new_ni, .{
1535 {2503 .parent = .wrap(parent_ni),
1536 for (children[0 .. children.len - 1], child_init[0 .. children.len - 1], 0..) |*ni, opts, i| {2504 .position = position,
1537 ni.* = try mf.addLastChildNode(gpa, b, .{2505 .num_headers = 0,
1538 .alignment = opts.@"0",2506 .num_footers = 0,
1539 .size = opts.@"1",2507 .initialized = initialize,
1540 .fixed = true,
1541 });2508 });
2509 },
15422510
1543 @memset(ni.slice(&mf)[0..opts.@"1"], @intCast(i + 1));2511 .resize => {
1544 }2512 const ni = nodes.keys()[smith.index(nodes.count())];
1545 // Shift differently-aligned nodes by inserting a node2513 const node_info = nodes.getPtr(ni).?;
1546 children[children.len - 1] = try mf.addNodeAfter(gpa, children[3], .{
1547 .alignment = child_init[children.len - 1].@"0",
1548 .size = child_init[children.len - 1].@"1",
1549 .fixed = true,
1550 });
1551 @memset(children[children.len - 1].slice(&mf), @intCast(children.len));
1552
1553 mf.verify();
1554 for (children, child_init, 0..) |ni, opts, i| {
1555 try testVerifyContent(&mf, ni, @intCast(i + 1), opts.@"1");
1556 }
1557 }
15582514
1559 // Shifting child nodes forward due via resize of parent.prev2515 const alignment = ni.alignment(&mf);
1560 {
1561 try testing.expect(a.location(&mf).resolve(&mf)[1] < 64);
1562 try a.resize(&mf, gpa, 64);
15632516
1564 try testVerifyContent(&mf, a, 0xaa, a_init_size);2517 if (ni.first(&mf) == .none and smith.value(bool)) {
1565 try testVerifyContent(&mf, c, 0xcc, c_init_size);2518 // Since this is a leaf node, we can use `resizeLeaf`.
1566 try testVerifyContent(&mf, d, 0xdd, d_init_size);2519 const new_size = alignment.forward(smith.valueWeighted(u64, initial_size_weights));
1567 for (children, child_init, 0..) |ni, opts, i| {2520 try ni.resizeLeaf(&mf, gpa, new_size);
1568 try testVerifyContent(&mf, ni, @intCast(i + 1), opts.@"1");2521 if (new_size == 0) {
1569 }2522 node_info.initialized = false;
1570 }2523 }
2524 } else {
2525 const min_size = alignment.forward(smith.valueWeighted(u64, initial_size_weights));
2526 try ni.ensureMinimumSize(&mf, gpa, min_size);
2527 }
15712528
1572 // Re-align last node into trailing free space within parent2529 if (ni.first(&mf) == .none) {
1573 {2530 // This is a leaf node, so it can contain data.
1574 try b.resize(&mf, gpa, b.location(&mf).resolve(&mf)[1] + 64);2531 if (node_info.initialized) {
2532 // It's already initialized, so we'll write the expected footer at the new end.
2533 const slice = ni.slice(&mf);
2534 std.mem.writeInt(u32, slice[slice.len - 4 ..][0..4], ~@backingInt(ni), .little);
2535 } else if (ni.location(&mf).resolve(&mf)[1] > 0) {
2536 // It was uninitialized, but it has a non-zero size, so maybe we'd like to
2537 // initialize it now?
2538 if (smith.value(bool)) {
2539 node_info.initialized = true;
2540 const slice = ni.slice(&mf);
2541 std.mem.writeInt(u32, slice[0..4], @backingInt(ni), .little);
2542 std.mem.writeInt(u32, slice[slice.len - 4 ..][0..4], ~@backingInt(ni), .little);
2543 }
2544 }
2545 }
2546 },
2547 .realign => {
2548 const ni = nodes.keys()[smith.index(nodes.count())];
2549 const new_alignment = smith.valueWeighted(Alignment, alignment_weights);
2550 if (new_alignment.compare(.gt, ni.alignment(&mf))) {
2551 _, const old_size = ni.location(&mf).resolve(&mf);
2552 try ni.realign(&mf, gpa, new_alignment);
2553 if (ni.first(&mf) == .none and nodes.get(ni).?.initialized) {
2554 const slice = ni.slice(&mf);
2555 @memmove(slice[slice.len - 4 ..][0..4], slice[old_size - 4 ..][0..4]);
2556 }
2557 }
2558 },
2559 };
15752560
1576 const last = children[children.len - 2];2561 mf.verify();
1577 try last.realign(&mf, gpa, .@"4", true);
1578 mf.verify();
15792562
1580 for (children, child_init, 0..) |ni, opts, i|2563 for (nodes.keys(), nodes.values()) |ni, expected| {
1581 try testVerifyContent(&mf, ni, @intCast(i + 1), opts.@"1");2564 try std.testing.expectEqual(expected.parent, ni.parent(&mf));
1582 try testVerifyContent(&mf, c, 0xcc, c_init_size);2565 if (ni != .root) {
1583 }2566 try std.testing.expectEqual(expected.position, ni.position(&mf));
2567 }
15842568
1585 // Re-align, shifting sibling nodes2569 {
1586 {2570 var num_headers: u32 = 0;
1587 try children[1].realign(&mf, gpa, .@"8", true);2571 var header_oni = ni.lastHeader(&mf);
1588 mf.verify();2572 while (header_oni.unwrap()) |header_ni| {
2573 num_headers += 1;
2574 header_oni = header_ni.prev(&mf);
2575 }
2576 try std.testing.expectEqual(expected.num_headers, num_headers);
2577 }
15892578
1590 for (children, child_init, 0..) |ni, opts, i|2579 {
1591 try testVerifyContent(&mf, ni, @intCast(i + 1), opts.@"1");2580 var num_footers: u32 = 0;
1592 try testVerifyContent(&mf, c, 0xcc, c_init_size);2581 var footer_oni = ni.firstFooter(&mf);
1593 }2582 while (footer_oni.unwrap()) |footer_ni| {
2583 num_footers += 1;
2584 footer_oni = footer_ni.next(&mf);
2585 }
2586 try std.testing.expectEqual(expected.num_footers, num_footers);
2587 }
15942588
1595 // Shrink and shift start of trailing node into free space2589 if (ni.first(&mf) == .none and expected.initialized) {
1596 {2590 const slice = ni.sliceConst(&mf);
1597 try mf.shrinkNode(gpa, a, 16, true);2591 if (slice.len > 0) {
1598 mf.verify();2592 try std.testing.expect(slice.len >= min_nonzero_size);
15992593 const header = std.mem.readInt(u32, slice[0..4], .little);
1600 const a_loc, const a_size = a.location(&mf).resolve(&mf);2594 const footer = std.mem.readInt(u32, slice[slice.len - 4 ..][0..4], .little);
1601 const b_loc, _ = b.location(&mf).resolve(&mf);2595 try std.testing.expectEqual(@backingInt(ni), header);
1602 try testing.expectEqual(b_loc, a_loc + a_size);2596 try std.testing.expectEqual(~@backingInt(ni), footer);
16032597 }
1604 try testVerifyContent(&mf, a, 0xaa, a_init_size);
1605 try testVerifyContent(&mf, c, 0xcc, c_init_size);
1606 try testVerifyContent(&mf, d, 0xdd, d_init_size);
1607 for (children, child_init, 0..) |ni, opts, i| {
1608 try testVerifyContent(&mf, ni, @intCast(i + 1), opts.@"1");
1609 }2598 }
1610 }2599 }
1611}2600}
src/main.zig+1
...@@ -36,6 +36,7 @@ const Module = @import("Module.zig");...@@ -36,6 +36,7 @@ const Module = @import("Module.zig");
3636
37test {37test {
38 _ = @import("codegen.zig");38 _ = @import("codegen.zig");
39 _ = @import("link/MappedFile.zig");
39}40}
4041
41const thread_stack_size = 60 << 20;42const thread_stack_size = 60 << 20;