authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-17 10:23:45+02:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-17 10:23:45+02:00
log7b02ab758845d553ef4399548274ef2e23eaeb2f
tree7a4e5d91a6bf029cd29d5e2fe9b2ad74dcd5ccd0
parent75044cb04cc67454db98ed7c054081806c3830c6
parenta402fdc813595b7c7ae51b27c96ad7443ebb33f8

Merge pull request 'Elf2: many enhancements' (#36528) from mlugg/elf2-again into master

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

6 files changed, 923 insertions(+), 394 deletions(-)

lib/std/elf.zig+49-5
...@@ -3290,13 +3290,11 @@ pub const gnu_hash = struct {...@@ -3290,13 +3290,11 @@ pub const gnu_hash = struct {
32903290
3291 /// Calculate the hash value for a name3291 /// Calculate the hash value for a name
3292 pub fn calculate(name: []const u8) u32 {3292 pub fn calculate(name: []const u8) u32 {
3293 var hash: u32 = 5381;3293 var h: u32 = 5381;
3294
3295 for (name) |char| {3294 for (name) |char| {
3296 hash = (hash << 5) +% hash +% char;3295 h = (h << 5) +% h +% char;
3297 }3296 }
32983297 return h;
3299 return hash;
3300 }3298 }
33013299
3302 test calculate {3300 test calculate {
...@@ -3308,6 +3306,52 @@ pub const gnu_hash = struct {...@@ -3308,6 +3306,52 @@ pub const gnu_hash = struct {
3308 }3306 }
3309};3307};
33103308
3309/// Things for the `SHT.HASH` section type.
3310///
3311/// Resources:
3312/// * https://refspecs.linuxfoundation.org/elf/gabi4+/ch5.dynamic.html#hash
3313/// * https://flapenguin.me/elf-dt-hash
3314/// * https://github.com/IBM/s390x-abi
3315pub const hash = struct {
3316 pub fn calculate(name: []const u8) u32 {
3317 var h: u32 = 0;
3318 for (name) |c| {
3319 h = (h << 4) +% c;
3320 const g = h & 0xF000_0000;
3321 h = (h ^ (g >> 24)) & ~g;
3322 }
3323 return h;
3324 }
3325
3326 /// The header of a `SHT.HASH` section on most architectures. Immediately followed by:
3327 /// * `buckets: [nbucket]u32`
3328 /// * `chains: [nchain]u32`
3329 ///
3330 /// The bucket for a symbol named `name` is `std.elf.hash.calculate(name) % nbuckets`.
3331 ///
3332 /// `buckets[b]` is the index of the first symbol in bucket `b`. If bucket `b` is empty then the
3333 /// value is 0 (`STN_UNDEF`).
3334 ///
3335 /// `chain[sym_index]` is the index of the next symbol in the same bucket as `sym_index`. If
3336 /// `sym_index` is the last symbol in its bucket then the value is 0 (`STN_UNDEF`).
3337 ///
3338 /// See also `Header64`.
3339 pub const Header32 = extern struct {
3340 nbucket: u32,
3341 nchain: u32,
3342 };
3343
3344 /// The header of a `SHT.HASH` section on alpha and s390x. Immediately followed by:
3345 /// * `buckets: [nbucket]u64`
3346 /// * `chains: [nchain]u64`
3347 ///
3348 /// See also `Header32`.
3349 pub const Header64 = extern struct {
3350 nbucket: u64,
3351 nchain: u64,
3352 };
3353};
3354
3311pub const EhdrFlags = packed union(Word) {3355pub const EhdrFlags = packed union(Word) {
3312 int: u32,3356 int: u32,
3313 loongarch: Loongarch,3357 loongarch: Loongarch,
lib/std/os/linux.zig+4-1
...@@ -2135,7 +2135,10 @@ pub fn flock(fd: fd_t, operation: i32) usize {...@@ -2135,7 +2135,10 @@ pub fn flock(fd: fd_t, operation: i32) usize {
2135 return syscall2(.flock, @as(u32, @bitCast(fd)), @as(u32, @bitCast(operation)));2135 return syscall2(.flock, @as(u32, @bitCast(fd)), @as(u32, @bitCast(operation)));
2136}2136}
21372137
2138pub const Elf_Symndx = if (native_arch == .s390x) u64 else u32;2138pub const Elf_Symndx = switch (native_arch) {
2139 .alpha, .s390x => u64,
2140 else => u32,
2141};
21392142
2140// We must follow the C calling convention when we call into the VDSO2143// We must follow the C calling convention when we call into the VDSO
2141const VdsoClockGettime = *align(1) const fn (clockid_t, *timespec) callconv(.c) usize;2144const VdsoClockGettime = *align(1) const fn (clockid_t, *timespec) callconv(.c) usize;
lib/std/posix/test.zig+16-25
...@@ -64,42 +64,33 @@ const have_dl_phdr_info = posix.system.dl_phdr_info != void;...@@ -64,42 +64,33 @@ const have_dl_phdr_info = posix.system.dl_phdr_info != void;
64const dl_phdr_info = if (have_dl_phdr_info) posix.dl_phdr_info else anyopaque;64const dl_phdr_info = if (have_dl_phdr_info) posix.dl_phdr_info else anyopaque;
6565
66const IterFnError = error{66const IterFnError = error{
67 MissingPtLoadSegment,67 MissingLoadSegment,
68 MissingLoad,68 MissingEhdrLoadSegment,
69 BadElfMagic,69 BadElfMagic,
70 FailedConsistencyCheck,70 PhnumMismatch,
71};71};
7272
73fn iter_fn(info: *dl_phdr_info, size: usize, counter: *usize) IterFnError!void {73fn iter_fn(info: *dl_phdr_info, size: usize, counter: *usize) IterFnError!void {
74 _ = size;74 _ = size;
75 // Count how many libraries are loaded75 // Count how many libraries are loaded
76 counter.* += @as(usize, 1);76 counter.* += 1;
7777
78 // The image should contain at least a PT.LOAD segment78 // The image should contain at least one loadable segment
79 if (info.phnum < 1) return error.MissingPtLoadSegment;79 if (info.phnum < 1) return error.MissingLoadSegment;
80
81 // Quick & dirty validation of the phdr pointers, make sure we're not
82 // pointing to some random gibberish
83 var i: usize = 0;
84 var found_load = false;
85 while (i < info.phnum) : (i += 1) {
86 const phdr = info.phdr[i];
8780
81 // For some quick and dirty validation, find the phdr which contains the ELF
82 // header, and check it makes sense.
83 for (info.phdr[0..info.phnum]) |phdr| {
88 if (phdr.type != .LOAD) continue;84 if (phdr.type != .LOAD) continue;
8985 if (phdr.offset != 0) continue;
90 const reloc_addr = info.addr + phdr.vaddr;86 // This segment holds the ELF header at the start
91 // Find the ELF header87 const ehdr: *elf.Ehdr = @ptrFromInt(info.addr + phdr.vaddr);
92 const elf_header = @as(*elf.Ehdr, @ptrFromInt(reloc_addr - phdr.offset));88 if (!mem.eql(u8, ehdr.e_ident[0..4], elf.MAGIC)) return error.BadElfMagic;
93 // Validate the magic89 if (ehdr.e_phnum != info.phnum) return error.PhnumMismatch;
94 if (!mem.eql(u8, elf_header.e_ident[0..4], elf.MAGIC)) return error.BadElfMagic;
95 // Consistency check
96 if (elf_header.e_phnum != info.phnum) return error.FailedConsistencyCheck;
97
98 found_load = true;
99 break;90 break;
91 } else {
92 return error.MissingEhdrLoadSegment;
100 }93 }
101
102 if (!found_load) return error.MissingLoad;
103}94}
10495
105test "dl_iterate_phdr" {96test "dl_iterate_phdr" {
src/link/Elf2.zig+799-347
...@@ -36,6 +36,7 @@ shndx: struct {...@@ -36,6 +36,7 @@ shndx: struct {
36 dynsym: Section.Index,36 dynsym: Section.Index,
37 dynstr: Section.Index,37 dynstr: Section.Index,
38 dynamic: Section.Index,38 dynamic: Section.Index,
39 hash: Section.Index,
39 tdata: Section.Index,40 tdata: Section.Index,
40 rela_dyn: Section.Index,41 rela_dyn: Section.Index,
41 rela_plt: Section.Index,42 rela_plt: Section.Index,
...@@ -119,8 +120,6 @@ got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),...@@ -119,8 +120,6 @@ got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),
119plt: std.array_hash_map.Auto(String(.strtab), void),120plt: std.array_hash_map.Auto(String(.strtab), void),
120/// The `.plt` section contains zero or more symbol relocations starting at this index.121/// The `.plt` section contains zero or more symbol relocations starting at this index.
121plt_first_symbol_reloc: SymbolReloc.Index,122plt_first_symbol_reloc: SymbolReloc.Index,
122/// The `.dynamic` section contains zero or more symbol relocations starting at this index.
123dynamic_first_symbol_reloc: SymbolReloc.Index,
124123
125needed: std.array_hash_map.Auto(String(.dynstr), void),124needed: std.array_hash_map.Auto(String(.dynstr), void),
126inputs: std.ArrayList(struct {125inputs: std.ArrayList(struct {
...@@ -136,6 +135,18 @@ inputs: std.ArrayList(struct {...@@ -136,6 +135,18 @@ inputs: std.ArrayList(struct {
136input_pending_index: u32,135input_pending_index: u32,
137input_sections: std.ArrayList(InputSection),136input_sections: std.ArrayList(InputSection),
138input_section_pending_index: u32,137input_section_pending_index: u32,
138/// SPARC has some weird relocations which involve setting some bits to fixed constant values. When
139/// we encounter such a relocation, we queue the action here, and apply them during `idle`.
140one_shot_fixups: std.ArrayList(struct {
141 node: MappedFile.Node.Index,
142 offset: u64,
143 /// The syntax in these tag names matches the syntax used in `SymbolReloc.Type.Simple.dest`.
144 action: enum {
145 @"32[12:10] = 0b000",
146 @"32[12:10] = 0b111",
147 @"32[12:12] = 0b0",
148 },
149}),
139navs: std.array_hash_map.Auto(InternPool.Nav.Index, struct {150navs: std.array_hash_map.Auto(InternPool.Nav.Index, struct {
140 lsi: Symbol.LocalIndex,151 lsi: Symbol.LocalIndex,
141 /// The start index of the contiguous sequence of symbol relocations in this NAV.152 /// The start index of the contiguous sequence of symbol relocations in this NAV.
...@@ -195,8 +206,6 @@ const Node = union(enum) {...@@ -195,8 +206,6 @@ const Node = union(enum) {
195 shdr,206 shdr,
196 segment: u32,207 segment: u32,
197 /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`.208 /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`.
198 ///
199 /// The section '.dynamic' may contain relocations via `elf.dynamic_first_symbol_reloc`.
200 section: Section.Index,209 section: Section.Index,
201 /// Only valid for static libraries, represents one non-zcu archive member.210 /// Only valid for static libraries, represents one non-zcu archive member.
202 input_member: InputIndex,211 input_member: InputIndex,
...@@ -530,6 +539,26 @@ const Section = struct {...@@ -530,6 +539,26 @@ const Section = struct {
530 }539 }
531 }540 }
532541
542 fn ensureAligned(shndx: Index, elf: *Elf, min_align: std.mem.Alignment) Error!void {
543 switch (elf.shdrPtr(shndx)) {
544 inline else => |shdr| {
545 if (elf.targetLoad(&shdr.addralign) >= min_align.toByteUnits()) {
546 return; // already aligned
547 }
548 elf.targetStore(&shdr.addralign, @intCast(min_align.toByteUnits()));
549 },
550 }
551 const ni = shndx.get(elf).ni;
552 if (min_align.compare(.gt, ni.alignment(&elf.mf))) {
553 try ni.realign(&elf.mf, elf.base.comp.gpa, min_align, .{});
554 }
555 switch (elf.getNode(ni.parent(&elf.mf))) {
556 .elf => {},
557 .segment => |phndx| try elf.ensureSegmentAligned(phndx, min_align),
558 else => unreachable,
559 }
560 }
561
533 /// Asserts that `rela_shndx` is a `SHT_RELA` section and ensures that its node has enough562 /// Asserts that `rela_shndx` is a `SHT_RELA` section and ensures that its node has enough
534 /// unused space to hold `n` additional `ElfN.Rela` entries.563 /// unused space to hold `n` additional `ElfN.Rela` entries.
535 fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) Error!void {564 fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) Error!void {
...@@ -634,11 +663,6 @@ const Section = struct {...@@ -634,11 +663,6 @@ const Section = struct {
634 const old_size = elf.targetLoad(&shdr.size);663 const old_size = elf.targetLoad(&shdr.size);
635 const new_size = old_size + ent_size;664 const new_size = old_size + ent_size;
636 elf.targetStore(&shdr.size, new_size);665 elf.targetStore(&shdr.size, new_size);
637 if (rela_shndx == elf.shndx.rela_dyn) {
638 elf.updateDynamicEntry(std.elf.DT_RELASZ, new_size);
639 } else if (rela_shndx == elf.shndx.rela_plt) {
640 elf.updateDynamicEntry(std.elf.DT_PLTRELSZ, new_size);
641 }
642 break :new_index @fromBackingInt(@intCast(@divExact(old_size, ent_size)));666 break :new_index @fromBackingInt(@intCast(@divExact(old_size, ent_size)));
643 };667 };
644 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(668 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
...@@ -1378,7 +1402,7 @@ const SymbolReloc = struct {...@@ -1378,7 +1402,7 @@ const SymbolReloc = struct {
1378 const shift: u6, const shift_exact: bool = switch (s.shift) {1402 const shift: u6, const shift_exact: bool = switch (s.shift) {
1379 .@"0" => .{ 0, false },1403 .@"0" => .{ 0, false },
1380 .@"2_exact" => .{ 2, true },1404 .@"2_exact" => .{ 2, true },
1381 .@"10" => .{ 10, true },1405 .@"10" => .{ 10, false },
1382 .@"12" => .{ 12, false },1406 .@"12" => .{ 12, false },
1383 .@"22" => .{ 22, false },1407 .@"22" => .{ 22, false },
1384 .@"32" => .{ 32, false },1408 .@"32" => .{ 32, false },
...@@ -1760,6 +1784,170 @@ const SymbolReloc = struct {...@@ -1760,6 +1784,170 @@ const SymbolReloc = struct {
1760 }1784 }
1761};1785};
17621786
1787fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {
1788 const min_buckets = max_dynsym_count / 2;
1789
1790 const cur_dynsym_count: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
1791 inline else => |shdr, class| @intCast(@divExact(
1792 elf.targetLoad(&shdr.size),
1793 @sizeOf(class.ElfN().Sym),
1794 )),
1795 };
1796
1797 switch (elf.targetDynsymHashInfo()) {
1798 inline else => |info| {
1799 {
1800 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
1801 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
1802 assert(elf.targetLoad(&header.nchain) == cur_dynsym_count);
1803 const nbucket = elf.targetLoad(&header.nbucket);
1804 if (nbucket >= min_buckets) {
1805 // 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.
1807 const need_size = @sizeOf(info.Header()) + (nbucket + max_dynsym_count) * 4;
1808 try elf.ensureNodeSize(elf.shndx.hash.get(elf).ni, need_size);
1809 return;
1810 }
1811 // We need more buckets, so we'll have to rebuild the hash table.
1812 }
1813
1814 // Rebuilding the hash table is quite expensive, so to avoid doing it too often we use a large
1815 // growth factor (* 2) for `nbucket`.
1816 const new_nbucket = min_buckets * 2;
1817
1818 {
1819 const need_size = @sizeOf(info.Header()) + (new_nbucket + max_dynsym_count) * 4;
1820 try elf.ensureNodeSize(elf.shndx.hash.get(elf).ni, need_size);
1821 }
1822
1823 elf.mf.nodes_lock.lock();
1824 defer elf.mf.nodes_lock.unlock();
1825
1826 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
1827 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
1828 const trailing: []info.Int() = @ptrCast(section_slice[@sizeOf(info.Header())..]);
1829
1830 header.* = .{ .nbucket = new_nbucket, .nchain = cur_dynsym_count };
1831 if (elf.targetEndian() != std.lang.Endian.native) {
1832 std.mem.byteSwapAllFields(info.Header(), header);
1833 }
1834 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];
1835 const chains: []info.Int() = trailing[@intCast(elf.targetLoad(&header.nbucket))..][0..@intCast(elf.targetLoad(&header.nchain))];
1836
1837 @memset(buckets, 0);
1838 chains[0] = 0;
1839 for (1..cur_dynsym_count, chains[1..]) |dynsym_index_usize, *chain| {
1840 const dynsym_index: u32 = @intCast(dynsym_index_usize);
1841 const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {
1842 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
1843 };
1844 const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len;
1845 // Make this symbol the head of that bucket, and chain to the old head.
1846 chain.* = buckets[b];
1847 elf.targetStore(&buckets[b], dynsym_index);
1848 }
1849 },
1850 }
1851}
1852
1853fn appendDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
1854 switch (elf.targetDynsymHashInfo()) {
1855 inline else => |info| {
1856 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
1857 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
1858 assert(elf.targetLoad(&header.nchain) == dynsym_index);
1859 elf.targetStore(&header.nchain, dynsym_index + 1);
1860
1861 switch (elf.shdrPtr(elf.shndx.hash)) {
1862 inline else => |shdr| elf.targetStore(&shdr.size, elf.targetLoad(&shdr.size) + @sizeOf(info.Int())),
1863 }
1864 },
1865 }
1866
1867 elf.populateDynsymHashEntry(dynsym_index);
1868}
1869fn populateDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
1870 elf.mf.nodes_lock.lock();
1871 defer elf.mf.nodes_lock.unlock();
1872
1873 assert(dynsym_index != 0);
1874
1875 switch (elf.targetDynsymHashInfo()) {
1876 inline else => |info| {
1877 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
1878 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
1879 const trailing: []info.Int() = @ptrCast(section_slice[@sizeOf(info.Header())..]);
1880
1881 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];
1882 const chains: []info.Int() = trailing[@intCast(elf.targetLoad(&header.nbucket))..][0..@intCast(elf.targetLoad(&header.nchain))];
1883
1884 const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {
1885 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
1886 };
1887 const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len;
1888 // Make this symbol the head of that bucket, and chain to the old head.
1889 chains[dynsym_index] = buckets[b];
1890 elf.targetStore(&buckets[b], dynsym_index);
1891 },
1892 }
1893}
1894fn popDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
1895 elf.clearDynsymHashEntry(dynsym_index);
1896
1897 switch (elf.targetDynsymHashInfo()) {
1898 inline else => |info| {
1899 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
1900 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
1901 assert(elf.targetLoad(&header.nchain) == dynsym_index + 1);
1902 elf.targetStore(&header.nchain, dynsym_index);
1903
1904 switch (elf.shdrPtr(elf.shndx.hash)) {
1905 inline else => |shdr| elf.targetStore(&shdr.size, elf.targetLoad(&shdr.size) - @sizeOf(info.Int())),
1906 }
1907 },
1908 }
1909}
1910fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
1911 elf.mf.nodes_lock.lock();
1912 defer elf.mf.nodes_lock.unlock();
1913
1914 assert(dynsym_index != 0);
1915
1916 switch (elf.targetDynsymHashInfo()) {
1917 inline else => |info| {
1918 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
1919 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
1920 const trailing: []info.Int() = @ptrCast(section_slice[@sizeOf(info.Header())..]);
1921
1922 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];
1923 const chains: []info.Int() = trailing[@intCast(elf.targetLoad(&header.nbucket))..][0..@intCast(elf.targetLoad(&header.nchain))];
1924
1925 const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {
1926 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
1927 };
1928 const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len;
1929
1930 const next_dynsym_index = elf.targetLoad(&chains[dynsym_index]);
1931 elf.targetStore(&chains[dynsym_index], 0);
1932
1933 // To remove `dynsym_index` from the singly-linked list, we need to iterate the chain to find
1934 // and replace it. But since this is, well, a hash table, that's actually fine.
1935 if (elf.targetLoad(&buckets[b]) == dynsym_index) {
1936 elf.targetStore(&buckets[b], next_dynsym_index);
1937 } else {
1938 var cur: usize = @intCast(elf.targetLoad(&buckets[b]));
1939 while (true) {
1940 assert(cur != 0); // `dynsym_index` is definitely somewhere in the chain
1941 if (elf.targetLoad(&chains[cur]) == dynsym_index) break;
1942 cur = @intCast(elf.targetLoad(&chains[cur]));
1943 }
1944 // We found `dynsym_index`; replace it with `next_dynsym_index`.
1945 elf.targetStore(&chains[cur], next_dynsym_index);
1946 }
1947 },
1948 }
1949}
1950
1763fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) Error!void {1951fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) Error!void {
1764 const gpa = elf.base.comp.gpa;1952 const gpa = elf.base.comp.gpa;
17651953
...@@ -1789,12 +1977,19 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe...@@ -1789,12 +1977,19 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe
1789 try elf.node_global_symbols.ensureUnusedCapacity(gpa, len);1977 try elf.node_global_symbols.ensureUnusedCapacity(gpa, len);
17901978
1791 if (elf.shndx.dynsym != .UNDEF) {1979 if (elf.shndx.dynsym != .UNDEF) {
1792 // Ensure the `.dynsym` section's node is big enough1980 const dynsym_cur_size: u64, const dynsym_ent_size: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
1793 const dynsym_need_size: u64 = switch (elf.shdrPtr(elf.shndx.dynsym)) {1981 inline else => |shdr, class| .{
1794 inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym),1982 elf.targetLoad(&shdr.size),
1983 @sizeOf(class.ElfN().Sym),
1984 },
1795 };1985 };
1986 const dynsym_cur_len: u32 = @intCast(@divExact(dynsym_cur_size, dynsym_ent_size));
1987
1988 const dynsym_need_size: u64 = (dynsym_cur_len + len) * dynsym_ent_size;
1796 try elf.ensureNodeSize(elf.shndx.dynsym.get(elf).ni, dynsym_need_size);1989 try elf.ensureNodeSize(elf.shndx.dynsym.get(elf).ni, dynsym_need_size);
17971990
1991 try elf.ensureDynsymHashCapacity(dynsym_cur_len + len);
1992
1798 try elf.ensureUnusedPltCapacity(len);1993 try elf.ensureUnusedPltCapacity(len);
1799 }1994 }
1800 },1995 },
...@@ -2131,6 +2326,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -2131,6 +2326,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
2131 if (elf.targetEndian() != native_endian) {2326 if (elf.targetEndian() != native_endian) {
2132 std.mem.byteSwapAllFields(Sym, sym);2327 std.mem.byteSwapAllFields(Sym, sym);
2133 }2328 }
2329 elf.appendDynsymHashEntry(dynsym_index);
2134 break :dynsym_index dynsym_index;2330 break :dynsym_index dynsym_index;
2135 },2331 },
2136 }2332 }
...@@ -2279,8 +2475,9 @@ fn setGlobalSymbolValue(...@@ -2279,8 +2475,9 @@ fn setGlobalSymbolValue(
2279 }2475 }
22802476
2281 // If this symbol was previously undefined, relocations targeting it may have been lowered to2477 // If this symbol was previously undefined, relocations targeting it may have been lowered to
2282 // runtime relocations which we have now discovered we do not need, so delete those.2478 // runtime relocations which we have now discovered we do not need, so delete those. This does
2283 if (elf.shndx.dynamic != .UNDEF) {2479 // not apply if the symbol is preemptible, which we check with `classifySymbolValue`.
2480 if (elf.shndx.dynamic != .UNDEF and elf.classifySymbolValue(.global(global_name)) != .dynamic) {
2284 Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf);2481 Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf);
2285 }2482 }
22862483
...@@ -2404,6 +2601,8 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {...@@ -2404,6 +2601,8 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
2404 const new_size = old_size - ent_size;2601 const new_size = old_size - ent_size;
2405 const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));2602 const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));
24062603
2604 elf.popDynsymHashEntry(remove_dynsym_index);
2605
2407 const free_dynsym_index = global_ptr.dynsym_index;2606 const free_dynsym_index = global_ptr.dynsym_index;
2408 global_ptr.dynsym_index = 0;2607 global_ptr.dynsym_index = 0;
24092608
...@@ -2411,6 +2610,8 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {...@@ -2411,6 +2610,8 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
2411 // The demoted global wasn't the last entry, so move whatever entry we just2610 // The demoted global wasn't the last entry, so move whatever entry we just
2412 // truncated out of dynsym into its place.2611 // truncated out of dynsym into its place.
24132612
2613 elf.clearDynsymHashEntry(free_dynsym_index);
2614
2414 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));2615 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));
2415 const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class));2616 const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class));
24162617
...@@ -2423,6 +2624,8 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {...@@ -2423,6 +2624,8 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
2423 assert(moved_global_ptr.dynsym_index == remove_dynsym_index);2624 assert(moved_global_ptr.dynsym_index == remove_dynsym_index);
2424 moved_global_ptr.dynsym_index = free_dynsym_index;2625 moved_global_ptr.dynsym_index = free_dynsym_index;
24252626
2627 elf.populateDynsymHashEntry(free_dynsym_index);
2628
2426 // Since that symbol's dynsym index has changed, we'll have to update any2629 // Since that symbol's dynsym index has changed, we'll have to update any
2427 // relocation entries targeting it.2630 // relocation entries targeting it.
2428 elf.changed_symtab_index.putAssumeCapacity(moved_name, {});2631 elf.changed_symtab_index.putAssumeCapacity(moved_name, {});
...@@ -3045,9 +3248,6 @@ const StringTable = struct {...@@ -3045,9 +3248,6 @@ const StringTable = struct {
3045 break :size .{ old_size, new_size };3248 break :size .{ old_size, new_size };
3046 },3249 },
3047 };3250 };
3048 if (shndx == elf.shndx.dynstr) {
3049 elf.updateDynamicEntry(std.elf.DT_STRSZ, new_size);
3050 }
3051 try elf.ensureNodeSize(ni, new_size);3251 try elf.ensureNodeSize(ni, new_size);
3052 const slice = ni.slice(&elf.mf)[old_size..];3252 const slice = ni.slice(&elf.mf)[old_size..];
3053 @memcpy(slice[0..key.len], key);3253 @memcpy(slice[0..key.len], key);
...@@ -3172,6 +3372,7 @@ fn create(...@@ -3172,6 +3372,7 @@ fn create(
3172 .dynsym = .UNDEF,3372 .dynsym = .UNDEF,
3173 .dynstr = .UNDEF,3373 .dynstr = .UNDEF,
3174 .dynamic = .UNDEF,3374 .dynamic = .UNDEF,
3375 .hash = .UNDEF,
3175 .tdata = .UNDEF,3376 .tdata = .UNDEF,
3176 .rela_dyn = .UNDEF,3377 .rela_dyn = .UNDEF,
3177 .rela_plt = .UNDEF,3378 .rela_plt = .UNDEF,
...@@ -3202,12 +3403,12 @@ fn create(...@@ -3202,12 +3403,12 @@ fn create(
3202 .got = .empty,3403 .got = .empty,
3203 .plt = .empty,3404 .plt = .empty,
3204 .plt_first_symbol_reloc = .none,3405 .plt_first_symbol_reloc = .none,
3205 .dynamic_first_symbol_reloc = .none,
3206 .needed = .empty,3406 .needed = .empty,
3207 .inputs = .empty,3407 .inputs = .empty,
3208 .input_pending_index = 0,3408 .input_pending_index = 0,
3209 .input_sections = .empty,3409 .input_sections = .empty,
3210 .input_section_pending_index = 0,3410 .input_section_pending_index = 0,
3411 .one_shot_fixups = .empty,
3211 .navs = .empty,3412 .navs = .empty,
3212 .uavs = .empty,3413 .uavs = .empty,
3213 .lazy = comptime .initFill(.{3414 .lazy = comptime .initFill(.{
...@@ -3257,6 +3458,7 @@ pub fn deinit(elf: *Elf) void {...@@ -3257,6 +3458,7 @@ pub fn deinit(elf: *Elf) void {
3257 for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m);3458 for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m);
3258 elf.inputs.deinit(gpa);3459 elf.inputs.deinit(gpa);
3259 elf.input_sections.deinit(gpa);3460 elf.input_sections.deinit(gpa);
3461 elf.one_shot_fixups.deinit(gpa);
3260 elf.navs.deinit(gpa);3462 elf.navs.deinit(gpa);
3261 elf.uavs.deinit(gpa);3463 elf.uavs.deinit(gpa);
3262 for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);3464 for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);
...@@ -3293,6 +3495,16 @@ fn initHeaders(...@@ -3293,6 +3495,16 @@ fn initHeaders(
3293 .@"64" => .@"8",3495 .@"64" => .@"8",
3294 };3496 };
32953497
3498 // Minimum alignment for an arbitrarily-chosen set of "large" nodes in the file (e.g. common
3499 // sections), to allow `MappedFile` to perform operations more efficiently. The downside to
3500 // using `elf.mf.flags.block_size` is that it causes outputs to be potentially unreproducible
3501 // across host filesystems, so in the future we may want to set this to `.@"1"` when using a
3502 // build mode that requires reproducibility.
3503 //
3504 // 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.
3506 const node_block_align: std.mem.Alignment = elf.mf.flags.block_size;
3507
3296 const plt: PltInfo = .fromMachine(machine);3508 const plt: PltInfo = .fromMachine(machine);
32973509
3298 const shnum: u32 = shnum: {3510 const shnum: u32 = shnum: {
...@@ -3310,6 +3522,7 @@ fn initHeaders(...@@ -3310,6 +3522,7 @@ fn initHeaders(
3310 shnum += 1; // .dynamic3522 shnum += 1; // .dynamic
3311 shnum += 1; // .dynstr3523 shnum += 1; // .dynstr
3312 shnum += 1; // .dynsym3524 shnum += 1; // .dynsym
3525 shnum += 1; // .hash
3313 shnum += 1; // .rela.dyn3526 shnum += 1; // .rela.dyn
3314 shnum += 1; // .rela.plt3527 shnum += 1; // .rela.plt
3315 }3528 }
...@@ -3328,6 +3541,10 @@ fn initHeaders(...@@ -3328,6 +3541,10 @@ fn initHeaders(
3328 rodata: u32,3541 rodata: u32,
3329 text: u32,3542 text: u32,
3330 data: u32,3543 data: u32,
3544 /// On most targets this is `undefined`, but on machines where JUMP_SLOT relocations write
3545 /// directly to the PLT, we place the PLT in its own segment in order to avoid making the
3546 /// general data segment RWX.
3547 plt: u32,
3331 tls: u32,3548 tls: u32,
3332 dynamic: u32,3549 dynamic: u32,
3333 relro: u32,3550 relro: u32,
...@@ -3359,6 +3576,10 @@ fn initHeaders(...@@ -3359,6 +3576,10 @@ fn initHeaders(
3359 defer phnum += 1;3576 defer phnum += 1;
3360 break :phndx phnum;3577 break :phndx phnum;
3361 },3578 },
3579 .plt = if (plt.got_plt == null) phndx: {
3580 defer phnum += 1;
3581 break :phndx phnum;
3582 } else undefined,
3362 .tls = if (comp.config.any_non_single_threaded) phndx: {3583 .tls = if (comp.config.any_non_single_threaded) phndx: {
3363 defer phnum += 1;3584 defer phnum += 1;
3364 break :phndx phnum;3585 break :phndx phnum;
...@@ -3414,7 +3635,7 @@ fn initHeaders(...@@ -3414,7 +3635,7 @@ fn initHeaders(
34143635
3415 elf.nodes.appendAssumeCapacity(.archive_header);3636 elf.nodes.appendAssumeCapacity(.archive_header);
3416 elf.ni.elf = try elf.mf.addLastChildNode(gpa, elf.ni.archive, .{3637 elf.ni.elf = try elf.mf.addLastChildNode(gpa, elf.ni.archive, .{
3417 .alignment = elf.mf.flags.block_size.max(.@"2"),3638 .alignment = node_block_align.max(.@"2"),
3418 .next_moved = true,3639 .next_moved = true,
3419 .bubbles_moved = false,3640 .bubbles_moved = false,
3420 .enable_next_moved = true,3641 .enable_next_moved = true,
...@@ -3424,9 +3645,98 @@ fn initHeaders(...@@ -3424,9 +3645,98 @@ fn initHeaders(
34243645
3425 const entsize: struct { ph: u32, sh: u32 } = switch (class) {3646 const entsize: struct { ph: u32, sh: u32 } = switch (class) {
3426 .NONE, _ => unreachable,3647 .NONE, _ => unreachable,
3427 inline else => |ct_class| entsize: {3648 inline else => |ct_class| .{
3649 .ph = @sizeOf(ct_class.ElfN().Phdr),
3650 .sh = @sizeOf(ct_class.ElfN().Shdr),
3651 },
3652 };
3653
3654 // We want to create the segment nodes *before* the ehdr, because the ehdr should go inside of
3655 // 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.
3657 if (@"type" != .REL) {
3658 elf.ni.rodata = try elf.mf.addOnlyChildNode(gpa, elf.ni.elf, .{
3659 // Must be at least `addr_align` for `elf.ni.phdr` to be placed inside this node
3660 .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,
3665 .bubbles_moved = false,
3666 });
3667 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.rodata });
3668 elf.phdrs.items[phndx.rodata] = elf.ni.rodata;
3669
3670 elf.ni.phdr = try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{
3671 .size = @as(u64, phnum) * entsize.ph,
3672 .alignment = addr_align, // keep in sync with `elf.ni.rodata` alignment above
3673 .moved = true,
3674 .resized = true,
3675 .bubbles_moved = false,
3676 });
3677 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.phdr });
3678 elf.phdrs.items[phndx.phdr] = elf.ni.phdr;
3679
3680 elf.ni.text = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
3681 .alignment = node_block_align,
3682 .moved = true,
3683 .bubbles_moved = false,
3684 });
3685 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.text });
3686 elf.phdrs.items[phndx.text] = elf.ni.text;
3687
3688 elf.ni.data = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
3689 // 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),
3691 .moved = true,
3692 .bubbles_moved = false,
3693 });
3694 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data });
3695 elf.phdrs.items[phndx.data] = elf.ni.data;
3696
3697 if (plt.got_plt == null) {
3698 const plt_ni = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
3699 .alignment = node_block_align,
3700 .moved = true,
3701 .bubbles_moved = false,
3702 });
3703 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.plt });
3704 elf.phdrs.items[phndx.plt] = plt_ni;
3705 }
3706
3707 elf.ni.data_rel_ro = try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{
3708 // 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.
3710 .alignment = node_block_align.max(addr_align),
3711 .moved = true,
3712 .bubbles_moved = false,
3713 });
3714 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro });
3715 elf.phdrs.items[phndx.relro] = elf.ni.data_rel_ro;
3716
3717 if (comp.config.any_non_single_threaded) {
3718 elf.ni.tls = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{
3719 .alignment = node_block_align,
3720 .moved = true,
3721 .bubbles_moved = false,
3722 });
3723 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.tls });
3724 elf.phdrs.items[phndx.tls] = elf.ni.tls;
3725 }
3726
3727 elf.phdrs.items[phndx.gnu_stack] = .none;
3728 }
3729
3730 switch (class) {
3731 .NONE, _ => unreachable,
3732 inline else => |ct_class| {
3428 const ElfN = ct_class.ElfN();3733 const ElfN = ct_class.ElfN();
3429 elf.ni.ehdr = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{3734 // In loadable modules, the ehdr goes in the rodata segment, as described above.
3735 const parent_ni = switch (@"type") {
3736 .REL => elf.ni.elf,
3737 .DYN, .EXEC => elf.ni.rodata,
3738 };
3739 elf.ni.ehdr = try elf.mf.addFirstChildNode(gpa, parent_ni, .{
3430 .size = @sizeOf(ElfN.Ehdr),3740 .size = @sizeOf(ElfN.Ehdr),
3431 .alignment = addr_align,3741 .alignment = addr_align,
3432 .fixed = true,3742 .fixed = true,
...@@ -3478,106 +3788,58 @@ fn initHeaders(...@@ -3478,106 +3788,58 @@ fn initHeaders(
3478 ehdr.shnum = 1; // Only the null shdr initially---will be incremented by `addSection`3788 ehdr.shnum = 1; // Only the null shdr initially---will be incremented by `addSection`
3479 ehdr.shstrndx = std.elf.SHN_UNDEF;3789 ehdr.shstrndx = std.elf.SHN_UNDEF;
3480 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);3790 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
3481
3482 break :entsize .{ .ph = @sizeOf(ElfN.Phdr), .sh = @sizeOf(ElfN.Shdr) };
3483 },3791 },
3484 };3792 }
34853793
3486 elf.ni.shdr = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{3794 elf.ni.shdr = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
3487 .size = 1 * entsize.sh, // as above, only the null shdr initially3795 .size = 1 * entsize.sh, // as above, only the null shdr initially
3488 .alignment = elf.mf.flags.block_size,3796 .alignment = addr_align.max(node_block_align),
3489 .moved = true,3797 .moved = true,
3490 .resized = true,3798 .resized = true,
3491 });3799 });
3492 elf.nodes.appendAssumeCapacity(.shdr);3800 elf.nodes.appendAssumeCapacity(.shdr);
34933801
3494 const page_align: std.mem.Alignment = .fromByteUnits(switch (machine) {
3495 .AARCH64 => 0x10000,
3496 .LOONGARCH => 0x4000,
3497 .PPC64 => 0x10000,
3498 .RISCV => 0x1000,
3499 .SPARCV9 => 0x100000,
3500 .X86_64 => 0x1000,
3501
3502 //.@"68K" => 0x2000,
3503 //.AMDGPU => 0x10000,
3504 //.ARC_COMPACT2 => 0x2000,
3505 //.AVR => 0x1,
3506 //.BPF => 0x100000,
3507 //.MIPS => 0x10000,
3508 //.MSP430 => 0x4,
3509 //.PPC => 0x10000,
3510 //.QDSP6 => 0x10000,
3511 //.SPARC => 0x10000,
3512 //.SPARC32PLUS => 0x10000,
3513 });
3514
3515 var ph_vaddr: u32 = if (@"type" != .REL) ph_vaddr: {
3516 elf.ni.rodata = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
3517 .alignment = elf.mf.flags.block_size,
3518 .moved = true,
3519 .bubbles_moved = false,
3520 });
3521 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.rodata });
3522 elf.phdrs.items[phndx.rodata] = elf.ni.rodata;
3523
3524 elf.ni.phdr = try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{
3525 .size = @as(u64, phnum) * entsize.ph,
3526 .alignment = addr_align,
3527 .moved = true,
3528 .resized = true,
3529 .bubbles_moved = false,
3530 });
3531 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.phdr });
3532 elf.phdrs.items[phndx.phdr] = elf.ni.phdr;
3533
3534 elf.ni.text = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
3535 .alignment = elf.mf.flags.block_size,
3536 .moved = true,
3537 .bubbles_moved = false,
3538 });
3539 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.text });
3540 elf.phdrs.items[phndx.text] = elf.ni.text;
3541
3542 elf.ni.data = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
3543 .alignment = elf.mf.flags.block_size,
3544 .moved = true,
3545 .bubbles_moved = false,
3546 });
3547 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data });
3548 elf.phdrs.items[phndx.data] = elf.ni.data;
3549
3550 elf.ni.data_rel_ro = try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{
3551 .alignment = elf.mf.flags.block_size,
3552 .moved = true,
3553 .bubbles_moved = false,
3554 });
3555 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro });
3556 elf.phdrs.items[phndx.relro] = elf.ni.data_rel_ro;
3557
3558 elf.phdrs.items[phndx.gnu_stack] = .none;
3559
3560 break :ph_vaddr switch (elf.ehdrType()) {
3561 .REL, .DYN => 0,
3562 .EXEC => switch (machine) {
3563 .AARCH64,
3564 => 0x200000,
3565 .LOONGARCH => 0x10000,
3566 .PPC64 => 0x10000000,
3567 .RISCV => 0x10000,
3568 .SPARCV9 => 0x100000,
3569 .X86_64 => 0x200000,
3570 },
3571 };
3572 } else undefined;
3573 switch (class) {3802 switch (class) {
3574 .NONE, _ => unreachable,3803 .NONE, _ => unreachable,
3575 inline else => |ct_class| {3804 inline else => |ct_class| {
3576 const ElfN = ct_class.ElfN();3805 const ElfN = ct_class.ElfN();
3577 const target_endian = elf.targetEndian();3806 const target_endian = elf.targetEndian();
35783807
3579 if (@"type" != .REL) {3808 populate_phdrs: {
3580 const phdr: []ElfN.Phdr = @ptrCast(@alignCast(elf.ni.phdr.slice(&elf.mf)));3809 // Initially we will give every `PT_LOAD` segment this address. When we re-allocate
3810 // segments in the virtual address space in `flushMoved` and `flushResized`, we will
3811 // move some segments to higher addresses to prevent overlap. This address therefore
3812 // becomes the image's "base address"; i.e. the first `PT_LOAD` segment will start
3813 // at this address. The base address could eventually end up higher than this due to
3814 // how we re-allocate the address space, but never lower.
3815 const base_vaddr: u64 = switch (@"type") {
3816 .REL => break :populate_phdrs,
3817 .DYN => 0,
3818 .EXEC => switch (machine) {
3819 .AARCH64 => 0x200000,
3820 .LOONGARCH => 0x10000,
3821 .PPC64 => 0x10000000,
3822 .RISCV => 0x10000,
3823 .SPARCV9 => 0x100000,
3824 .X86_64 => 0x200000,
3825 },
3826 };
3827
3828 // All `PT_LOAD` segments are given this `.@"align"`. However, to avoid bloating the
3829 // binary, their *nodes* are not aligned to this boundary---ELF only requires that
3830 // ecah segment's address equals its file offset modulo this alignment, not that its
3831 // file offset is actually aligned to this boundary. This property is maintained by
3832 // the segment virtual address space allocation logic.
3833 const page_align = elf.targetPageAlign();
3834
3835 // We will populate elements in this slice (by index). The `PT_LOAD` segments are
3836 // actually `PT_NULL` for now, because we initialize `filesz` and `memsz` to zero.
3837 // Any which end up non-empty will have their size populated (and their type set to
3838 // `PT_LOAD`) by the segment virtual address space allocation logic.
3839 const phdr: []ElfN.Phdr = @ptrCast(@alignCast(
3840 elf.ni.phdr.slice(&elf.mf)[0 .. phnum * @sizeOf(ElfN.Phdr)],
3841 ));
3842
3581 const ph_phdr = &phdr[phndx.phdr];3843 const ph_phdr = &phdr[phndx.phdr];
3582 ph_phdr.* = .{3844 ph_phdr.* = .{
3583 .type = .PHDR,3845 .type = .PHDR,
...@@ -3589,7 +3851,6 @@ fn initHeaders(...@@ -3589,7 +3851,6 @@ fn initHeaders(
3589 .flags = .{ .R = true },3851 .flags = .{ .R = true },
3590 .@"align" = @intCast(elf.ni.phdr.alignment(&elf.mf).toByteUnits()),3852 .@"align" = @intCast(elf.ni.phdr.alignment(&elf.mf).toByteUnits()),
3591 };3853 };
3592 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr);
35933854
3594 if (maybe_interp) |_| {3855 if (maybe_interp) |_| {
3595 const ph_interp = &phdr[phndx.interp];3856 const ph_interp = &phdr[phndx.interp];
...@@ -3603,53 +3864,57 @@ fn initHeaders(...@@ -3603,53 +3864,57 @@ fn initHeaders(
3603 .flags = .{ .R = true },3864 .flags = .{ .R = true },
3604 .@"align" = 1,3865 .@"align" = 1,
3605 };3866 };
3606 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_interp);
3607 }3867 }
36083868
3609 _, const rodata_size = elf.ni.rodata.location(&elf.mf).resolve(&elf.mf);
3610 const ph_rodata = &phdr[phndx.rodata];3869 const ph_rodata = &phdr[phndx.rodata];
3611 ph_rodata.* = .{3870 ph_rodata.* = .{
3612 .type = if (rodata_size == 0) .NULL else .LOAD,3871 .type = .NULL,
3613 .offset = 0,3872 .offset = 0,
3614 .vaddr = ph_vaddr,3873 .vaddr = @intCast(base_vaddr),
3615 .paddr = ph_vaddr,3874 .paddr = @intCast(base_vaddr),
3616 .filesz = @intCast(rodata_size),3875 .filesz = 0,
3617 .memsz = @intCast(rodata_size),3876 .memsz = 0,
3618 .flags = .{ .R = true },3877 .flags = .{ .R = true },
3619 .@"align" = @intCast(elf.ni.rodata.alignment(&elf.mf).max(page_align).toByteUnits()),3878 .@"align" = @intCast(page_align.toByteUnits()),
3620 };3879 };
3621 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_rodata);
3622 ph_vaddr += @intCast(rodata_size);
36233880
3624 _, const text_size = elf.ni.text.location(&elf.mf).resolve(&elf.mf);
3625 const ph_text = &phdr[phndx.text];3881 const ph_text = &phdr[phndx.text];
3626 ph_text.* = .{3882 ph_text.* = .{
3627 .type = if (text_size == 0) .NULL else .LOAD,3883 .type = .NULL,
3628 .offset = 0,3884 .offset = 0,
3629 .vaddr = ph_vaddr,3885 .vaddr = @intCast(base_vaddr),
3630 .paddr = ph_vaddr,3886 .paddr = @intCast(base_vaddr),
3631 .filesz = @intCast(text_size),3887 .filesz = 0,
3632 .memsz = @intCast(text_size),3888 .memsz = 0,
3633 .flags = .{ .R = true, .X = true },3889 .flags = .{ .R = true, .X = true },
3634 .@"align" = @intCast(elf.ni.text.alignment(&elf.mf).max(page_align).toByteUnits()),3890 .@"align" = @intCast(page_align.toByteUnits()),
3635 };3891 };
3636 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_text);
3637 ph_vaddr += @intCast(text_size);
36383892
3639 _, const data_size = elf.ni.data.location(&elf.mf).resolve(&elf.mf);
3640 const ph_data = &phdr[phndx.data];3893 const ph_data = &phdr[phndx.data];
3641 ph_data.* = .{3894 ph_data.* = .{
3642 .type = if (data_size == 0) .NULL else .LOAD,3895 .type = .NULL,
3643 .offset = 0,3896 .offset = 0,
3644 .vaddr = ph_vaddr,3897 .vaddr = @intCast(base_vaddr),
3645 .paddr = ph_vaddr,3898 .paddr = @intCast(base_vaddr),
3646 .filesz = @intCast(data_size),3899 .filesz = 0,
3647 .memsz = @intCast(data_size),3900 .memsz = 0,
3648 .flags = .{ .R = true, .W = true },3901 .flags = .{ .R = true, .W = true },
3649 .@"align" = @intCast(elf.ni.data.alignment(&elf.mf).max(page_align).toByteUnits()),3902 .@"align" = @intCast(page_align.toByteUnits()),
3650 };3903 };
3651 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_data);3904
3652 ph_vaddr += @intCast(data_size);3905 if (plt.got_plt == null) {
3906 const ph_plt = &phdr[phndx.plt];
3907 ph_plt.* = .{
3908 .type = .NULL,
3909 .offset = 0,
3910 .vaddr = @intCast(base_vaddr),
3911 .paddr = @intCast(base_vaddr),
3912 .filesz = 0,
3913 .memsz = 0,
3914 .flags = .{ .R = true, .W = true, .X = true },
3915 .@"align" = @intCast(page_align.toByteUnits()),
3916 };
3917 }
36533918
3654 if (comp.config.any_non_single_threaded) {3919 if (comp.config.any_non_single_threaded) {
3655 const ph_tls = &phdr[phndx.tls];3920 const ph_tls = &phdr[phndx.tls];
...@@ -3661,9 +3926,8 @@ fn initHeaders(...@@ -3661,9 +3926,8 @@ fn initHeaders(
3661 .filesz = 0,3926 .filesz = 0,
3662 .memsz = 0,3927 .memsz = 0,
3663 .flags = .{ .R = true },3928 .flags = .{ .R = true },
3664 .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()),3929 .@"align" = @intCast(elf.ni.tls.alignment(&elf.mf).toByteUnits()),
3665 };3930 };
3666 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_tls);
3667 }3931 }
36683932
3669 if (have_dynamic_section) {3933 if (have_dynamic_section) {
...@@ -3678,7 +3942,6 @@ fn initHeaders(...@@ -3678,7 +3942,6 @@ fn initHeaders(
3678 .flags = .{ .R = true, .W = true },3942 .flags = .{ .R = true, .W = true },
3679 .@"align" = @intCast(addr_align.toByteUnits()),3943 .@"align" = @intCast(addr_align.toByteUnits()),
3680 };3944 };
3681 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_dynamic);
3682 }3945 }
36833946
3684 const ph_relro = &phdr[phndx.relro];3947 const ph_relro = &phdr[phndx.relro];
...@@ -3690,9 +3953,8 @@ fn initHeaders(...@@ -3690,9 +3953,8 @@ fn initHeaders(
3690 .filesz = 0,3953 .filesz = 0,
3691 .memsz = 0,3954 .memsz = 0,
3692 .flags = .{ .R = true },3955 .flags = .{ .R = true },
3693 .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()),3956 .@"align" = @intCast(elf.ni.data_rel_ro.alignment(&elf.mf).toByteUnits()),
3694 };3957 };
3695 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_relro);
36963958
3697 const ph_gnu_stack = &phdr[phndx.gnu_stack];3959 const ph_gnu_stack = &phdr[phndx.gnu_stack];
3698 ph_gnu_stack.* = .{3960 ph_gnu_stack.* = .{
...@@ -3705,7 +3967,10 @@ fn initHeaders(...@@ -3705,7 +3967,10 @@ fn initHeaders(
3705 .flags = .{ .R = true, .W = true },3967 .flags = .{ .R = true, .W = true },
3706 .@"align" = 1,3968 .@"align" = 1,
3707 };3969 };
3708 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_gnu_stack);3970
3971 if (target_endian != std.lang.Endian.native) {
3972 std.mem.byteSwapAllElements(ElfN.Phdr, phdr);
3973 }
3709 }3974 }
37103975
3711 const sh_undef: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf)));3976 const sh_undef: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf)));
...@@ -3733,7 +3998,7 @@ fn initHeaders(...@@ -3733,7 +3998,7 @@ fn initHeaders(
3733 .size = @sizeOf(ElfN.Sym) * 1,3998 .size = @sizeOf(ElfN.Sym) * 1,
3734 .addralign = addr_align,3999 .addralign = addr_align,
3735 .entsize = @sizeOf(ElfN.Sym),4000 .entsize = @sizeOf(ElfN.Sym),
3736 .node_align = elf.mf.flags.block_size,4001 .node_align = node_block_align,
3737 .info = 1, // index of first non-local symbol4002 .info = 1, // index of first non-local symbol
3738 }));4003 }));
3739 const symtab_null = @field(elf.symPtr(.null), @tagName(ct_class));4004 const symtab_null = @field(elf.symPtr(.null), @tagName(ct_class));
...@@ -3755,7 +4020,7 @@ fn initHeaders(...@@ -3755,7 +4020,7 @@ fn initHeaders(
3755 .type = .STRTAB,4020 .type = .STRTAB,
3756 .size = 1,4021 .size = 1,
3757 .entsize = 1,4022 .entsize = 1,
3758 .node_align = elf.mf.flags.block_size,4023 .node_align = node_block_align,
3759 }));4024 }));
3760 Section.Index.get(.shstrtab, elf).ni.slice(&elf.mf)[0] = 0;4025 Section.Index.get(.shstrtab, elf).ni.slice(&elf.mf)[0] = 0;
37614026
...@@ -3767,7 +4032,7 @@ fn initHeaders(...@@ -3767,7 +4032,7 @@ fn initHeaders(
3767 .type = .STRTAB,4032 .type = .STRTAB,
3768 .size = 1,4033 .size = 1,
3769 .entsize = 1,4034 .entsize = 1,
3770 .node_align = elf.mf.flags.block_size,4035 .node_align = node_block_align,
3771 }));4036 }));
3772 Section.Index.get(.strtab, elf).ni.slice(&elf.mf)[0] = 0;4037 Section.Index.get(.strtab, elf).ni.slice(&elf.mf)[0] = 0;
3773 switch (elf.shdrPtr(.symtab)) {4038 switch (elf.shdrPtr(.symtab)) {
...@@ -3777,22 +4042,22 @@ fn initHeaders(...@@ -3777,22 +4042,22 @@ fn initHeaders(
3777 assert(.rodata == try elf.addSection(elf.ni.rodata, .{4042 assert(.rodata == try elf.addSection(elf.ni.rodata, .{
3778 .name = ".rodata",4043 .name = ".rodata",
3779 .flags = .{ .ALLOC = true },4044 .flags = .{ .ALLOC = true },
3780 .addralign = elf.mf.flags.block_size,4045 .node_align = node_block_align,
3781 }));4046 }));
3782 assert(.text == try elf.addSection(elf.ni.text, .{4047 assert(.text == try elf.addSection(elf.ni.text, .{
3783 .name = ".text",4048 .name = ".text",
3784 .flags = .{ .ALLOC = true, .EXECINSTR = true },4049 .flags = .{ .ALLOC = true, .EXECINSTR = true },
3785 .addralign = elf.mf.flags.block_size,4050 .node_align = node_block_align,
3786 }));4051 }));
3787 assert(.data == try elf.addSection(elf.ni.data, .{4052 assert(.data == try elf.addSection(elf.ni.data, .{
3788 .name = ".data",4053 .name = ".data",
3789 .flags = .{ .WRITE = true, .ALLOC = true },4054 .flags = .{ .WRITE = true, .ALLOC = true },
3790 .addralign = elf.mf.flags.block_size,4055 .node_align = node_block_align,
3791 }));4056 }));
3792 assert(.data_rel_ro == try elf.addSection(elf.ni.data_rel_ro, .{4057 assert(.data_rel_ro == try elf.addSection(elf.ni.data_rel_ro, .{
3793 .name = ".data.rel.ro",4058 .name = ".data.rel.ro",
3794 .flags = .{ .WRITE = true, .ALLOC = true },4059 .flags = .{ .WRITE = true, .ALLOC = true },
3795 .addralign = elf.mf.flags.block_size,4060 .node_align = node_block_align,
3796 }));4061 }));
3797 if (@"type" != .REL) {4062 if (@"type" != .REL) {
3798 elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{4063 elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{
...@@ -3808,34 +4073,39 @@ fn initHeaders(...@@ -3808,34 +4073,39 @@ fn initHeaders(
3808 .addralign = addr_align,4073 .addralign = addr_align,
3809 .entsize = @intCast(addr_align.toByteUnits()),4074 .entsize = @intCast(addr_align.toByteUnits()),
3810 });4075 });
3811 if (plt.got_plt) |got_plt| elf.shndx.got_plt = try elf.addSection(4076 if (plt.got_plt) |got_plt| {
3812 if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data,4077 const got_plt_segment_ni = if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data;
3813 .{4078 elf.shndx.got_plt = try elf.addSection(got_plt_segment_ni, .{
3814 .name = ".got.plt",4079 .name = ".got.plt",
3815 .type = .PROGBITS,4080 .type = .PROGBITS,
3816 .flags = .{ .WRITE = true, .ALLOC = true },4081 .flags = .{ .WRITE = true, .ALLOC = true },
3817 .size = got_plt.header_entries * elf.targetPtrSize(),4082 .size = got_plt.header_entries * elf.targetPtrSize(),
3818 .addralign = addr_align,4083 .addralign = addr_align,
3819 .entsize = @intCast(addr_align.toByteUnits()),4084 .entsize = @intCast(addr_align.toByteUnits()),
3820 },4085 });
3821 );4086 elf.shndx.plt = try elf.addSection(elf.ni.text, .{
3822 elf.shndx.plt = try elf.addSection(elf.ni.text, .{4087 .name = ".plt",
3823 .name = ".plt",4088 .type = .PROGBITS,
3824 .type = .PROGBITS,4089 .flags = .{ .ALLOC = true, .EXECINSTR = true },
3825 .flags = .{4090 .size = plt.entry_size * plt.header_entries,
3826 .ALLOC = true,4091 .addralign = plt.@"align",
3827 .EXECINSTR = true,4092 .node_align = node_block_align,
3828 .WRITE = plt.got_plt == null,4093 });
3829 },4094 } else {
3830 .size = plt.entry_size * plt.header_entries,4095 elf.shndx.plt = try elf.addSection(elf.phdrs.items[phndx.plt], .{
3831 .addralign = plt.@"align",4096 .name = ".plt",
3832 .node_align = elf.mf.flags.block_size,4097 .type = .PROGBITS,
3833 });4098 .flags = .{ .ALLOC = true, .WRITE = true, .EXECINSTR = true },
4099 .size = plt.entry_size * plt.header_entries,
4100 .addralign = plt.@"align",
4101 .node_align = node_block_align,
4102 });
4103 }
3834 if (plt.plt_sec != null) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{4104 if (plt.plt_sec != null) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{
3835 .name = ".plt.sec",4105 .name = ".plt.sec",
3836 .flags = .{ .ALLOC = true, .EXECINSTR = true },4106 .flags = .{ .ALLOC = true, .EXECINSTR = true },
3837 .addralign = plt.@"align",4107 .addralign = plt.@"align",
3838 .node_align = elf.mf.flags.block_size,4108 .node_align = node_block_align,
3839 });4109 });
3840 if (maybe_interp) |interp| {4110 if (maybe_interp) |interp| {
3841 const interp_ni = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{4111 const interp_ni = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{
...@@ -3858,6 +4128,7 @@ fn initHeaders(...@@ -3858,6 +4128,7 @@ fn initHeaders(
3858 sec_interp[interp.len] = 0;4128 sec_interp[interp.len] = 0;
3859 }4129 }
3860 if (have_dynamic_section) {4130 if (have_dynamic_section) {
4131 assert(elf.ni.data_rel_ro.alignment(&elf.mf).compare(.gte, addr_align));
3861 const dynamic_ni = try elf.mf.addLastChildNode(gpa, elf.ni.data_rel_ro, .{4132 const dynamic_ni = try elf.mf.addLastChildNode(gpa, elf.ni.data_rel_ro, .{
3862 .alignment = addr_align,4133 .alignment = addr_align,
3863 .moved = true,4134 .moved = true,
...@@ -3872,7 +4143,7 @@ fn initHeaders(...@@ -3872,7 +4143,7 @@ fn initHeaders(
3872 .flags = .{ .ALLOC = true },4143 .flags = .{ .ALLOC = true },
3873 .size = 1,4144 .size = 1,
3874 .entsize = 1,4145 .entsize = 1,
3875 .node_align = elf.mf.flags.block_size,4146 .node_align = node_block_align,
3876 });4147 });
3877 dynstr_shndx.get(elf).ni.slice(&elf.mf)[0] = 0;4148 dynstr_shndx.get(elf).ni.slice(&elf.mf)[0] = 0;
3878 elf.shndx.dynstr = dynstr_shndx;4149 elf.shndx.dynstr = dynstr_shndx;
...@@ -3890,7 +4161,7 @@ fn initHeaders(...@@ -3890,7 +4161,7 @@ fn initHeaders(
3890 .info = 1,4161 .info = 1,
3891 .addralign = addr_align,4162 .addralign = addr_align,
3892 .entsize = @sizeOf(Sym),4163 .entsize = @sizeOf(Sym),
3893 .node_align = elf.mf.flags.block_size,4164 .node_align = node_block_align,
3894 });4165 });
3895 const dynsym_null = @field(elf.dynsymPtr(0), @tagName(ct_class));4166 const dynsym_null = @field(elf.dynsymPtr(0), @tagName(ct_class));
3896 dynsym_null.* = .{4167 dynsym_null.* = .{
...@@ -3918,7 +4189,7 @@ fn initHeaders(...@@ -3918,7 +4189,7 @@ fn initHeaders(
3918 .link = elf.shndx.dynsym.toSection().?,4189 .link = elf.shndx.dynsym.toSection().?,
3919 .addralign = addr_align,4190 .addralign = addr_align,
3920 .entsize = rela_size,4191 .entsize = rela_size,
3921 .node_align = elf.mf.flags.block_size,4192 .node_align = node_block_align,
3922 });4193 });
3923 elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{4194 elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{
3924 .name = ".rela.plt",4195 .name = ".rela.plt",
...@@ -3928,7 +4199,7 @@ fn initHeaders(...@@ -3928,7 +4199,7 @@ fn initHeaders(
3928 .info = (if (plt.got_plt != null) elf.shndx.got_plt else elf.shndx.plt).toSection().?,4199 .info = (if (plt.got_plt != null) elf.shndx.got_plt else elf.shndx.plt).toSection().?,
3929 .addralign = addr_align,4200 .addralign = addr_align,
3930 .entsize = rela_size,4201 .entsize = rela_size,
3931 .node_align = elf.mf.flags.block_size,4202 .node_align = node_block_align,
3932 });4203 });
3933 elf.shndx.dynamic = try elf.addSection(dynamic_ni, .{4204 elf.shndx.dynamic = try elf.addSection(dynamic_ni, .{
3934 .name = ".dynamic",4205 .name = ".dynamic",
...@@ -3938,6 +4209,32 @@ fn initHeaders(...@@ -3938,6 +4209,32 @@ fn initHeaders(
3938 .entsize = @intCast(addr_align.toByteUnits() * 2),4209 .entsize = @intCast(addr_align.toByteUnits() * 2),
3939 .node_align = addr_align,4210 .node_align = addr_align,
3940 });4211 });
4212 switch (elf.targetDynsymHashInfo()) {
4213 inline else => |info| {
4214 elf.shndx.hash = try elf.addSection(elf.ni.rodata, .{
4215 .name = ".hash",
4216 .type = .HASH,
4217 .flags = .{ .ALLOC = true },
4218 .link = elf.shndx.dynsym.toSection().?,
4219 // It's unclear what value is correct for the alignment. binutils uses 8 everywhere,
4220 // while lld uses 4 everywhere (but lld lacks support for the alpha/s390x special
4221 // case). Matching the hash word (= entry) size seems like the actually sane choice,
4222 // and is what mold does too.
4223 .addralign = .fromByteUnits(@sizeOf(info.Int())),
4224 // initially: nbucket = 8 + nchain = 1
4225 .size = @sizeOf(info.Header()) + @sizeOf(info.Int()) * (8 + 1),
4226 });
4227 const hash_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
4228 const header: *info.Header() = @ptrCast(hash_slice[0..@sizeOf(info.Header())]);
4229 header.* = .{ .nbucket = 8, .nchain = 1 };
4230 if (elf.targetEndian() != std.lang.Endian.native) {
4231 std.mem.byteSwapAllFields(info.Header(), header);
4232 }
4233 // The initial bucket and chain values are all 0, but `MappedFile` initialized
4234 // the node with zeroes anyway, so no need to memset.
4235 },
4236 }
4237
3941 switch (machine) {4238 switch (machine) {
3942 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),4239 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
3943 .X86_64 => {4240 .X86_64 => {
...@@ -4015,15 +4312,6 @@ fn initHeaders(...@@ -4015,15 +4312,6 @@ fn initHeaders(
4015 .SPARCV9 => {},4312 .SPARCV9 => {},
4016 }4313 }
4017 }4314 }
4018 if (comp.config.any_non_single_threaded) {
4019 elf.ni.tls = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{
4020 .alignment = elf.mf.flags.block_size,
4021 .moved = true,
4022 .bubbles_moved = false,
4023 });
4024 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.tls });
4025 elf.phdrs.items[phndx.tls] = elf.ni.tls;
4026 }
40274315
4028 // Populate reserved GOT words.4316 // Populate reserved GOT words.
4029 switch (machine) {4317 switch (machine) {
...@@ -4199,7 +4487,7 @@ fn initHeaders(...@@ -4199,7 +4487,7 @@ fn initHeaders(
4199 if (comp.config.any_non_single_threaded) elf.shndx.tdata = try elf.addSection(elf.ni.tls, .{4487 if (comp.config.any_non_single_threaded) elf.shndx.tdata = try elf.addSection(elf.ni.tls, .{
4200 .name = ".tdata",4488 .name = ".tdata",
4201 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },4489 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
4202 .addralign = elf.mf.flags.block_size,4490 .node_align = node_block_align,
4203 });4491 });
42044492
4205 assert(elf.nodes.len == expected_nodes_len);4493 assert(elf.nodes.len == expected_nodes_len);
...@@ -4465,6 +4753,28 @@ fn ehdrType(elf: *const Elf) EhdrType {...@@ -4465,6 +4753,28 @@ fn ehdrType(elf: *const Elf) EhdrType {
4465fn targetPtrSize(elf: *const Elf) u8 {4753fn targetPtrSize(elf: *const Elf) u8 {
4466 return elf.identClass().size();4754 return elf.identClass().size();
4467}4755}
4756fn targetPageAlign(elf: *const Elf) std.mem.Alignment {
4757 return .fromByteUnits(switch (elf.ehdrMachine()) {
4758 .AARCH64 => 0x10000,
4759 .LOONGARCH => 0x4000,
4760 .PPC64 => 0x10000,
4761 .RISCV => 0x1000,
4762 .SPARCV9 => 0x100000,
4763 .X86_64 => 0x1000,
4764
4765 //.@"68K" => 0x2000,
4766 //.AMDGPU => 0x10000,
4767 //.ARC_COMPACT2 => 0x2000,
4768 //.AVR => 0x1,
4769 //.BPF => 0x100000,
4770 //.MIPS => 0x10000,
4771 //.MSP430 => 0x4,
4772 //.PPC => 0x10000,
4773 //.QDSP6 => 0x10000,
4774 //.SPARC => 0x10000,
4775 //.SPARC32PLUS => 0x10000,
4776 });
4777}
4468fn targetEndian(elf: *const Elf) std.lang.Endian {4778fn targetEndian(elf: *const Elf) std.lang.Endian {
4469 const ident_data: std.elf.DATA = @fromBackingInt(elf.ni.elf.sliceConst(&elf.mf)[std.elf.EI.DATA]);4779 const ident_data: std.elf.DATA = @fromBackingInt(elf.ni.elf.sliceConst(&elf.mf)[std.elf.EI.DATA]);
4470 return ident_data.endian();4780 return ident_data.endian();
...@@ -4531,6 +4841,30 @@ const PltInfo = struct {...@@ -4531,6 +4841,30 @@ const PltInfo = struct {
4531fn targetPltInfo(elf: *const Elf) PltInfo {4841fn targetPltInfo(elf: *const Elf) PltInfo {
4532 return .fromMachine(elf.ehdrMachine());4842 return .fromMachine(elf.ehdrMachine());
4533}4843}
4844const DynsymHashInfo = enum(u32) {
4845 @"4" = 4,
4846 @"8" = 8,
4847
4848 fn Int(comptime self: DynsymHashInfo) type {
4849 return switch (self) {
4850 .@"4" => u32,
4851 .@"8" => u64,
4852 };
4853 }
4854
4855 fn Header(comptime self: DynsymHashInfo) type {
4856 return switch (self) {
4857 .@"4" => std.elf.hash.Header32,
4858 .@"8" => std.elf.hash.Header64,
4859 };
4860 }
4861};
4862fn targetDynsymHashInfo(elf: *const Elf) DynsymHashInfo {
4863 return switch (elf.ehdrMachine()) {
4864 else => .@"4",
4865 // TODO: Alpha and S390x will need to use either `."@4"` or `.@"8"` depending on `elf.identClass()`.
4866 };
4867}
4534fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {4868fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
4535 const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer;4869 const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer;
4536 const Child = pointer_ty.child;4870 const Child = pointer_ty.child;
...@@ -4586,14 +4920,11 @@ const PhdrSlice = union(std.elf.CLASS) {...@@ -4586,14 +4920,11 @@ const PhdrSlice = union(std.elf.CLASS) {
4586};4920};
4587fn phdrSlice(elf: *Elf) PhdrSlice {4921fn phdrSlice(elf: *Elf) PhdrSlice {
4588 assert(elf.ehdrType() != .REL);4922 assert(elf.ehdrType() != .REL);
4589 const slice = elf.ni.phdr.slice(&elf.mf);
4590 return switch (elf.identClass()) {4923 return switch (elf.identClass()) {
4591 .NONE, _ => unreachable,4924 .NONE, _ => unreachable,
4592 inline else => |class| @unionInit(4925 inline else => |class| @unionInit(PhdrSlice, @tagName(class), @ptrCast(@alignCast(
4593 PhdrSlice,4926 elf.ni.phdr.slice(&elf.mf)[0 .. elf.phdrs.items.len * @sizeOf(class.ElfN().Phdr)],
4594 @tagName(class),4927 ))),
4595 @ptrCast(@alignCast(slice)),
4596 ),
4597 };4928 };
4598}4929}
45994930
...@@ -4607,7 +4938,9 @@ fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr {...@@ -4607,7 +4938,9 @@ fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr {
4607 switch (elf.identClass()) {4938 switch (elf.identClass()) {
4608 .NONE, _ => unreachable,4939 .NONE, _ => unreachable,
4609 inline else => |class| {4940 inline else => |class| {
4610 const shdr_slice: []class.ElfN().Shdr = @ptrCast(@alignCast(raw_slice));4941 const shdr_slice: []class.ElfN().Shdr = @ptrCast(@alignCast(
4942 raw_slice[0 .. elf.shdrs.items.len * @sizeOf(class.ElfN().Shdr)],
4943 ));
4611 const shdr_ptr = &shdr_slice[@backingInt(shndx)];4944 const shdr_ptr = &shdr_slice[@backingInt(shndx)];
4612 return @unionInit(ShdrPtr, @tagName(class), shdr_ptr);4945 return @unionInit(ShdrPtr, @tagName(class), shdr_ptr);
4613 },4946 },
...@@ -4662,7 +4995,6 @@ fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT {...@@ -4662,7 +4995,6 @@ fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT {
4662fn mapInputSection(elf: *Elf, opts: struct {4995fn mapInputSection(elf: *Elf, opts: struct {
4663 name: []const u8,4996 name: []const u8,
4664 flags: std.elf.SHF,4997 flags: std.elf.SHF,
4665 addralign: std.elf.Xword,
4666 entsize: std.elf.Xword,4998 entsize: std.elf.Xword,
4667}) (Error || error{4999}) (Error || error{
4668 UnsupportedSectionFlags,5000 UnsupportedSectionFlags,
...@@ -4734,16 +5066,12 @@ fn mapInputSection(elf: *Elf, opts: struct {...@@ -4734,16 +5066,12 @@ fn mapInputSection(elf: *Elf, opts: struct {
4734 flags.COMPRESSED = false;5066 flags.COMPRESSED = false;
4735 break :flags flags;5067 break :flags flags;
4736 },5068 },
4737 .node_align = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
4738 usize,
4739 @intCast(@max(opts.addralign, 1)),
4740 )),
4741 .entsize = std.math.lossyCast(u32, opts.entsize),5069 .entsize = std.math.lossyCast(u32, opts.entsize),
4742 });5070 });
4743 };5071 };
4744 // Validate that the input is compatible with this section...
4745 switch (elf.shdrPtr(existing_shndx)) {5072 switch (elf.shdrPtr(existing_shndx)) {
4746 inline else => |shdr| {5073 inline else => |shdr| {
5074 // Validate that the input is compatible with this section
4747 const cur_flags = elf.targetLoad(&shdr.flags).shf;5075 const cur_flags = elf.targetLoad(&shdr.flags).shf;
4748 if (cur_flags.EXECINSTR != opts.flags.EXECINSTR or5076 if (cur_flags.EXECINSTR != opts.flags.EXECINSTR or
4749 cur_flags.WRITE != opts.flags.WRITE or5077 cur_flags.WRITE != opts.flags.WRITE or
...@@ -4756,20 +5084,8 @@ fn mapInputSection(elf: *Elf, opts: struct {...@@ -4756,20 +5084,8 @@ fn mapInputSection(elf: *Elf, opts: struct {
4756 .NULL, .PROGBITS => {},5084 .NULL, .PROGBITS => {},
4757 else => return error.SectionTypeConflict,5085 else => return error.SectionTypeConflict,
4758 }5086 }
4759 },5087
4760 }5088 // All okay, combine the section flags
4761 // ...then realign the section's node if necessary...
4762 if (opts.addralign > existing_shndx.get(elf).ni.alignment(&elf.mf).toByteUnits()) {
4763 const new_alignment: std.mem.Alignment = .fromByteUnits(
4764 std.math.ceilPowerOfTwoAssert(usize, @intCast(opts.addralign)),
4765 );
4766 try existing_shndx.get(elf).ni.realign(&elf.mf, gpa, new_alignment, .{});
4767 }
4768 // ...and update the shdr as needed.
4769 switch (elf.shdrPtr(existing_shndx)) {
4770 inline else => |shdr| {
4771 // Combine the section flags.
4772 const cur_flags = elf.targetLoad(&shdr.flags).shf;
4773 elf.targetStore(&shdr.flags, .{ .shf = .{5089 elf.targetStore(&shdr.flags, .{ .shf = .{
4774 .EXECINSTR = cur_flags.EXECINSTR,5090 .EXECINSTR = cur_flags.EXECINSTR,
4775 .WRITE = cur_flags.WRITE,5091 .WRITE = cur_flags.WRITE,
...@@ -4778,11 +5094,6 @@ fn mapInputSection(elf: *Elf, opts: struct {...@@ -4778,11 +5094,6 @@ fn mapInputSection(elf: *Elf, opts: struct {
4778 .STRINGS = cur_flags.STRINGS and opts.flags.STRINGS,5094 .STRINGS = cur_flags.STRINGS and opts.flags.STRINGS,
4779 .MERGE = cur_flags.MERGE and opts.flags.MERGE,5095 .MERGE = cur_flags.MERGE and opts.flags.MERGE,
4780 } });5096 } });
4781 // Increase addralign to the maximum of the current value and the new value---the node
4782 // alignment was already increased above.
4783 if (opts.addralign > elf.targetLoad(&shdr.addralign)) {
4784 elf.targetStore(&shdr.addralign, @intCast(opts.addralign));
4785 }
4786 },5097 },
4787 }5098 }
4788 return existing_shndx;5099 return existing_shndx;
...@@ -4810,7 +5121,6 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node...@@ -4810,7 +5121,6 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
4810 .TLS = elf.base.comp.config.any_non_single_threaded and5121 .TLS = elf.base.comp.config.any_non_single_threaded and
4811 nav.resolved.?.@"threadlocal",5122 nav.resolved.?.@"threadlocal",
4812 },5123 },
4813 .addralign = 1,
4814 .entsize = 0,5124 .entsize = 0,
4815 })) |shndx| {5125 })) |shndx| {
4816 break :section shndx;5126 break :section shndx;
...@@ -4856,6 +5166,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node...@@ -4856,6 +5166,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
4856 else => |a| a,5166 else => |a| a,
4857 },5167 },
4858 };5168 };
5169 try shndx.ensureAligned(elf, alignment.toStdMem());
4859 const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{5170 const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{
4860 .alignment = alignment.toStdMem(),5171 .alignment = alignment.toStdMem(),
4861 });5172 });
...@@ -4899,6 +5210,7 @@ fn uavMapIndex(...@@ -4899,6 +5210,7 @@ fn uavMapIndex(
4899 const umi: Node.UavMapIndex = @fromBackingInt(@intCast(uav_gop.index));5210 const umi: Node.UavMapIndex = @fromBackingInt(@intCast(uav_gop.index));
4900 if (!uav_gop.found_existing) {5211 if (!uav_gop.found_existing) {
4901 const shndx: Section.Index = .data_rel_ro; // TODO: it would be better to use `.rodata` if the UAV value doesn't have relocs5212 const shndx: Section.Index = .data_rel_ro; // TODO: it would be better to use `.rodata` if the UAV value doesn't have relocs
5213 try shndx.ensureAligned(elf, resolved_align.toStdMem());
4902 const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{5214 const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{
4903 .moved = true, // see assert at end of `genUav`5215 .moved = true, // see assert at end of `genUav`
4904 .alignment = resolved_align.toStdMem(),5216 .alignment = resolved_align.toStdMem(),
...@@ -4925,6 +5237,8 @@ fn uavMapIndex(...@@ -4925,6 +5237,8 @@ fn uavMapIndex(
4925 elf.pending_uavs.appendAssumeCapacity(umi);5237 elf.pending_uavs.appendAssumeCapacity(umi);
4926 } else {5238 } else {
4927 const node = uav_gop.value_ptr.lsi.index().ptr(elf).node;5239 const node = uav_gop.value_ptr.lsi.index().ptr(elf).node;
5240 const shndx = elf.getNode(node.parent(&elf.mf)).section;
5241 try shndx.ensureAligned(elf, resolved_align.toStdMem());
4928 if (resolved_align.toStdMem().order(node.alignment(&elf.mf)).compare(.gt)) {5242 if (resolved_align.toStdMem().order(node.alignment(&elf.mf)).compare(.gt)) {
4929 try node.realign(&elf.mf, gpa, resolved_align.toStdMem(), .{});5243 try node.realign(&elf.mf, gpa, resolved_align.toStdMem(), .{});
4930 }5244 }
...@@ -5225,7 +5539,6 @@ fn loadObject(...@@ -5225,7 +5539,6 @@ fn loadObject(
5225 const shndx = elf.mapInputSection(.{5539 const shndx = elf.mapInputSection(.{
5226 .name = name,5540 .name = name,
5227 .flags = section.shdr.flags.shf,5541 .flags = section.shdr.flags.shf,
5228 .addralign = section.shdr.addralign,
5229 .entsize = section.shdr.entsize,5542 .entsize = section.shdr.entsize,
5230 }) catch |err| switch (err) {5543 }) catch |err| switch (err) {
5231 error.StripSection => continue,5544 error.StripSection => continue,
...@@ -5313,7 +5626,7 @@ fn loadObject(...@@ -5313,7 +5626,7 @@ fn loadObject(
5313 const old_size = elf.targetLoad(&shdr.size);5626 const old_size = elf.targetLoad(&shdr.size);
5314 const new_size = old_size + section.shdr.size;5627 const new_size = old_size + section.shdr.size;
5315 elf.targetStore(&shdr.size, @intCast(new_size));5628 elf.targetStore(&shdr.size, @intCast(new_size));
5316 elf.updateInitFiniArraySectionSize(shndx.*, init_fini_section_name, @"type", new_size);5629 elf.updateInitFiniArraySectionSize(shndx.*, init_fini_section_name);
5317 },5630 },
5318 }5631 }
5319 break :shndx shndx.*;5632 break :shndx shndx.*;
...@@ -5324,12 +5637,13 @@ fn loadObject(...@@ -5324,12 +5637,13 @@ fn loadObject(
5324 .node_fixed = true,5637 .node_fixed = true,
5325 },5638 },
5326 };5639 };
5640 const need_align: std.mem.Alignment = .fromByteUnits(
5641 std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))),
5642 );
5643 try opts.shndx.ensureAligned(elf, need_align);
5327 const ni = try elf.mf.addLastChildNode(gpa, opts.shndx.get(elf).ni, .{5644 const ni = try elf.mf.addLastChildNode(gpa, opts.shndx.get(elf).ni, .{
5328 .size = section.shdr.size,5645 .size = section.shdr.size,
5329 .alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert(5646 .alignment = need_align,
5330 usize,
5331 @intCast(@max(section.shdr.addralign, 1)),
5332 )),
5333 .moved = true, // see assert at end of `flushInputSection`5647 .moved = true, // see assert at end of `flushInputSection`
5334 .fixed = opts.node_fixed,5648 .fixed = opts.node_fixed,
5335 });5649 });
...@@ -5699,6 +6013,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars...@@ -5699,6 +6013,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars
5699 if (elf.copied_globals.get(name)) |copied_global| {6013 if (elf.copied_globals.get(name)) |copied_global| {
5700 // We have a copy relocation for this global, but the amount of space we6014 // We have a copy relocation for this global, but the amount of space we
5701 // reserved for it could be too small or underaligned!6015 // reserved for it could be too small or underaligned!
6016 try Section.Index.data.ensureAligned(elf, gop.value_ptr.alignment);
5702 try copied_global.node.resize(&elf.mf, gpa, gop.value_ptr.size);6017 try copied_global.node.resize(&elf.mf, gpa, gop.value_ptr.size);
5703 try copied_global.node.realign(&elf.mf, gpa, gop.value_ptr.alignment, .{});6018 try copied_global.node.realign(&elf.mf, gpa, gop.value_ptr.alignment, .{});
5704 const global_ptr = elf.globalByName(name).?;6019 const global_ptr = elf.globalByName(name).?;
...@@ -5878,19 +6193,7 @@ fn updateInitFiniArraySectionSize(...@@ -5878,19 +6193,7 @@ fn updateInitFiniArraySectionSize(
5878 elf: *Elf,6193 elf: *Elf,
5879 shndx: Section.Index,6194 shndx: Section.Index,
5880 comptime name: []const u8,6195 comptime name: []const u8,
5881 @"type": std.elf.SHT,
5882 new_size: u64,
5883) void {6196) void {
5884 if (elf.shndx.dynamic != .UNDEF) {
5885 const arraysz_dyn_key: u32 = switch (@"type") {
5886 .INIT_ARRAY => std.elf.DT_INIT_ARRAYSZ,
5887 .FINI_ARRAY => std.elf.DT_FINI_ARRAYSZ,
5888 .PREINIT_ARRAY => std.elf.DT_PREINIT_ARRAYSZ,
5889 else => unreachable,
5890 };
5891 elf.updateDynamicEntry(arraysz_dyn_key, new_size);
5892 }
5893
5894 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {6197 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {
5895 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),6198 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),
5896 };6199 };
...@@ -5955,7 +6258,7 @@ fn prepareDynamic(elf: *Elf) Error!void {...@@ -5955,7 +6258,7 @@ fn prepareDynamic(elf: *Elf) Error!void {
5955 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +6258 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +
5956 @as(usize, @intFromBool(use_plt)) * 4 +6259 @as(usize, @intFromBool(use_plt)) * 4 +
5957 @intFromBool(comp.config.output_mode == .Exe) +6260 @intFromBool(comp.config.output_mode == .Exe) +
5958 @intFromBool(elf.textrel_count > 0) + 8;6261 @intFromBool(elf.textrel_count > 0) + 9;
59596262
5960 const dynamic_size = dynamic_len * 2 * elf.targetPtrSize();6263 const dynamic_size = dynamic_len * 2 * elf.targetPtrSize();
59616264
...@@ -6055,7 +6358,7 @@ fn flushDynamic(elf: *Elf) void {...@@ -6055,7 +6358,7 @@ fn flushDynamic(elf: *Elf) void {
6055 dynamic_index += 4;6358 dynamic_index += 4;
6056 }6359 }
60576360
6058 dynamic_entries[dynamic_index..][0..8].* = .{6361 dynamic_entries[dynamic_index..][0..9].* = .{
6059 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },6362 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },
6060 .{ std.elf.DT_RELASZ, @intCast(elf.shndx.rela_dyn.size(elf)) },6363 .{ std.elf.DT_RELASZ, @intCast(elf.shndx.rela_dyn.size(elf)) },
6061 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },6364 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
...@@ -6063,9 +6366,10 @@ fn flushDynamic(elf: *Elf) void {...@@ -6063,9 +6366,10 @@ fn flushDynamic(elf: *Elf) void {
6063 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },6366 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },
6064 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },6367 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },
6065 .{ std.elf.DT_STRSZ, @intCast(elf.shndx.dynstr.size(elf)) },6368 .{ std.elf.DT_STRSZ, @intCast(elf.shndx.dynstr.size(elf)) },
6369 .{ std.elf.DT_HASH, @intCast(elf.shndx.hash.vaddr(elf)) },
6066 .{ std.elf.DT_NULL, 0 },6370 .{ std.elf.DT_NULL, 0 },
6067 };6371 };
6068 dynamic_index += 8;6372 dynamic_index += 9;
60696373
6070 assert(dynamic_index == dynamic_entries.len);6374 assert(dynamic_index == dynamic_entries.len);
6071 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|6375 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|
...@@ -6092,7 +6396,8 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -6092,7 +6396,8 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
6092 else => {},6396 else => {},
6093 }6397 }
6094 if (opts.flags.ALLOC and elf.ehdrType() != .REL) {6398 if (opts.flags.ALLOC and elf.ehdrType() != .REL) {
6095 assert(elf.getNode(segment_ni) == .segment);6399 const phndx = elf.getNode(segment_ni).segment;
6400 try elf.ensureSegmentAligned(phndx, opts.addralign);
6096 }6401 }
6097 const gpa = elf.base.comp.gpa;6402 const gpa = elf.base.comp.gpa;
6098 try elf.nodes.ensureUnusedCapacity(gpa, 1);6403 try elf.nodes.ensureUnusedCapacity(gpa, 1);
...@@ -6427,7 +6732,7 @@ fn addRelocAssumeCapacity(...@@ -6427,7 +6732,7 @@ fn addRelocAssumeCapacity(
64276732
6428 .WDISP30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" })),6733 .WDISP30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" })),
6429 .WPLT30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" })),6734 .WPLT30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" })),
6430 .PC22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[21:0]", .cast = .signed, .shift = .@"10" })),6735 .PC22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[21:0]", .cast = .unsigned, .shift = .@"10" })),
6431 .H44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:0]", .cast = .unsigned, .shift = .@"22" })),6736 .H44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:0]", .cast = .unsigned, .shift = .@"22" })),
6432 .M44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"12" })),6737 .M44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"12" })),
64336738
...@@ -6457,36 +6762,36 @@ fn addRelocAssumeCapacity(...@@ -6457,36 +6762,36 @@ fn addRelocAssumeCapacity(
64576762
6458 // The following relocations are all represented by the ABI as writing to a 13 bit6763 // The following relocations are all represented by the ABI as writing to a 13 bit
6459 // field (32[12:0]), but masking out some bits of the value. To simplify our logic6764 // field (32[12:0]), but masking out some bits of the value. To simplify our logic
6460 // for applying relocations, we instead [un]set any fixed bits right now, then model6765 // for applying relocations, we split this action up: we create a relocation writing
6461 // the relocation as only writing to a smaller 10--12 bit field.6766 // to the 10--12 bit long field which is actually variable, and queue a one-shot
6462 // TODO: because we flush input sections lazily, we can't actually write these bits6767 // task to set the constant bits. We can't just write the bits now unfortunately
6463 // immediately---we'll instead have to queue the writes somehow.6768 // because they may be in an input section which has not yet been loaded.
6464 .PC10 => {6769 .PC10 => {
6465 // TODO: 32[12:10] = 0b0006770 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
6466 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));6771 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6467 },6772 },
6468 .L44 => {6773 .L44 => {
6469 // TODO: 32[12:12] = 0b06774 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:12] = 0b0" });
6470 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[11:0]", .cast = .trunc, .shift = .@"0" }));6775 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[11:0]", .cast = .trunc, .shift = .@"0" }));
6471 },6776 },
6472 .TLS_LDO_LOX10 => {6777 .TLS_LDO_LOX10 => {
6473 // TODO: 32[12:10] = 0b0006778 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
6474 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));6779 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6475 },6780 },
6476 .TLS_LE_LOX10 => {6781 .TLS_LE_LOX10 => {
6477 // TODO: 32[12:10] = 0b1116782 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b111" });
6478 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));6783 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6479 },6784 },
6480 .GOT10 => {6785 .GOT10 => {
6481 // TODO: 32[12:10] = 0b0006786 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
6482 elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));6787 elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6483 },6788 },
6484 .TLS_GD_LO10 => {6789 .TLS_GD_LO10 => {
6485 // TODO: 32[12:10] = 0b0006790 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
6486 elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));6791 elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6487 },6792 },
6488 .TLS_LDM_LO10 => {6793 .TLS_LDM_LO10 => {
6489 // TODO: 32[12:10] = 0b0006794 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
6490 elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));6795 elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
6491 },6796 },
6492 },6797 },
...@@ -6887,7 +7192,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -6887,7 +7192,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
6887 const entry_ptr: *class.ElfN().Addr = @ptrCast(@alignCast(7192 const entry_ptr: *class.ElfN().Addr = @ptrCast(@alignCast(
6888 elf.shndx.got.get(elf).ni.slice(&elf.mf)[offset..][0..addr_size],7193 elf.shndx.got.get(elf).ni.slice(&elf.mf)[offset..][0..addr_size],
6889 ));7194 ));
6890 entry_ptr.* = switch (entry_value) {7195 elf.targetStore(entry_ptr, switch (entry_value) {
6891 .unsigned => |x| @intCast(x),7196 .unsigned => |x| @intCast(x),
6892 .signed => |x| switch (class) {7197 .signed => |x| switch (class) {
6893 .NONE, _ => comptime unreachable,7198 .NONE, _ => comptime unreachable,
...@@ -6895,7 +7200,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -6895,7 +7200,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
6895 .@"64" => @bitCast(x),7200 .@"64" => @bitCast(x),
6896 },7201 },
6897 .reloc => 0,7202 .reloc => 0,
6898 };7203 });
6899 break :got_entry_addr elf.targetLoad(&got_shdr.addr) + offset;7204 break :got_entry_addr elf.targetLoad(&got_shdr.addr) + offset;
6900 },7205 },
6901 };7206 };
...@@ -6950,6 +7255,9 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye...@@ -6950,6 +7255,9 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye
6950fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {7255fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
6951 assert(elf.shndx.dynamic != .UNDEF);7256 assert(elf.shndx.dynamic != .UNDEF);
69527257
7258 // Only dynamic executables may contain `R_*_COPY` relocations.
7259 if (elf.base.comp.config.output_mode != .Exe) return false;
7260
6953 const gpa = elf.base.comp.gpa;7261 const gpa = elf.base.comp.gpa;
69547262
6955 const global_ptr = elf.globals.strong_undef.getPtr(global_name) orelse7263 const global_ptr = elf.globals.strong_undef.getPtr(global_name) orelse
...@@ -6957,10 +7265,6 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {...@@ -6957,10 +7265,6 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
69577265
6958 assert(global_ptr.dynsym_index != 0);7266 assert(global_ptr.dynsym_index != 0);
69597267
6960 // Only dynamic executables may contain `R_*_COPY` relocations.
6961 if (elf.shndx.dynamic == .UNDEF) return false;
6962 if (elf.base.comp.config.output_mode != .Exe) return false;
6963
6964 const dso_global = elf.dso_globals.get(global_name) orelse {7268 const dso_global = elf.dso_globals.get(global_name) orelse {
6965 // We do not have a definition to provide the correct size for the symbol. If a definition7269 // We do not have a definition to provide the correct size for the symbol. If a definition
6966 // is discovered in a later DSO, we may at that point be able to add a copy relocation.7270 // is discovered in a later DSO, we may at that point be able to add a copy relocation.
...@@ -6974,6 +7278,8 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {...@@ -6974,6 +7278,8 @@ fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
6974 if (gop.found_existing) return true;7278 if (gop.found_existing) return true;
6975 errdefer assert(elf.copied_globals.pop().?.key == global_name);7279 errdefer assert(elf.copied_globals.pop().?.key == global_name);
69767280
7281 try Section.Index.data.ensureAligned(elf, dso_global.alignment);
7282
6977 try elf.nodes.ensureUnusedCapacity(gpa, 1);7283 try elf.nodes.ensureUnusedCapacity(gpa, 1);
6978 const node = try elf.mf.addLastChildNode(gpa, Section.Index.data.get(elf).ni, .{7284 const node = try elf.mf.addLastChildNode(gpa, Section.Index.data.get(elf).ni, .{
6979 .size = dso_global.size,7285 .size = dso_global.size,
...@@ -7234,6 +7540,25 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {...@@ -7234,6 +7540,25 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
7234 };7540 };
7235 break :task;7541 break :task;
7236 }7542 }
7543 if (elf.one_shot_fixups.items.len > 0) {
7544 // Each of these is very simple, so an unreasonable amount of overhead would be
7545 // introduced if we only did one per `idle` call. Also, there is no risk of this work
7546 // being invalidated. So let's just flush the entire queue at once.
7547 for (elf.one_shot_fixups.items) |isw| {
7548 const dest_slice = isw.node.slice(&elf.mf)[@intCast(isw.offset)..][0..4];
7549 const old: u32 = std.mem.readInt(u32, dest_slice, elf.targetEndian());
7550 const new: u32 = switch (isw.action) {
7551 // zig fmt: off
7552 .@"32[12:10] = 0b000" => old & 0b11111111_11111111_11100011_11111111,
7553 .@"32[12:10] = 0b111" => old | 0b00000000_00000000_00011100_00000000,
7554 .@"32[12:12] = 0b0" => old & 0b11111111_11111111_11101111_11111111,
7555 // zig fmt: on
7556 };
7557 std.mem.writeInt(u32, dest_slice, new, elf.targetEndian());
7558 }
7559 elf.one_shot_fixups.clearRetainingCapacity();
7560 break :task;
7561 }
7237 if (elf.changed_symtab_index.pop()) |kv| {7562 if (elf.changed_symtab_index.pop()) |kv| {
7238 const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);7563 const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);
7239 defer sub_prog_node.end();7564 defer sub_prog_node.end();
...@@ -7324,6 +7649,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {...@@ -7324,6 +7649,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
7324 }7649 }
7325 }7650 }
7326 if (elf.input_sections.items.len > elf.input_section_pending_index) return true;7651 if (elf.input_sections.items.len > elf.input_section_pending_index) return true;
7652 if (elf.one_shot_fixups.items.len > 0) return true;
7327 if (elf.changed_symtab_index.count() > 0) return true;7653 if (elf.changed_symtab_index.count() > 0) return true;
7328 if (elf.mf.updates.items.len > 0) return true;7654 if (elf.mf.updates.items.len > 0) return true;
7329 return false;7655 return false;
...@@ -7586,17 +7912,22 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -7586,17 +7912,22 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
7586 const ph = &phdr[phndx];7912 const ph = &phdr[phndx];
7587 switch (elf.targetLoad(&ph.type)) {7913 switch (elf.targetLoad(&ph.type)) {
7588 else => unreachable,7914 else => unreachable,
7589 .NULL, .LOAD => return,7915
7916 .NULL, .LOAD => {
7917 try elf.allocateSegmentLoadAddress(phndx);
7918 },
75907919
7591 .DYNAMIC,7920 .DYNAMIC,
7592 .INTERP,7921 .INTERP,
7593 .PHDR,7922 .PHDR,
7594 .TLS,7923 .TLS,
7595 .GNU_RELRO,7924 .GNU_RELRO,
7596 => {},7925 => {
7926 const new_vaddr = elf.computeNodeVAddr(ni);
7927 elf.targetStore(&ph.vaddr, @intCast(new_vaddr));
7928 elf.targetStore(&ph.paddr, @intCast(new_vaddr));
7929 },
7597 }7930 }
7598 elf.targetStore(&ph.vaddr, @intCast(elf.computeNodeVAddr(ni)));
7599 ph.paddr = ph.vaddr;
7600 },7931 },
7601 }7932 }
7602 },7933 },
...@@ -7648,8 +7979,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -7648,8 +7979,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
7648 elf.flushMovedPltSection(.got_plt, old_addr, addr);7979 elf.flushMovedPltSection(.got_plt, old_addr, addr);
7649 } else if (shndx == elf.shndx.plt_sec) {7980 } else if (shndx == elf.shndx.plt_sec) {
7650 elf.flushMovedPltSection(.plt_sec, old_addr, addr);7981 elf.flushMovedPltSection(.plt_sec, old_addr, addr);
7651 } else if (shndx == elf.shndx.dynamic) {
7652 elf.flushMovedNodeRelocs(ni, addr, elf.dynamic_first_symbol_reloc, .none);
7653 }7982 }
7654 },7983 },
7655 .input_member => {},7984 .input_member => {},
...@@ -7739,6 +8068,114 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -7739,6 +8068,114 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
7739 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);8068 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
7740}8069}
77418070
8071/// Given the index of a `PT_LOAD`/`PT_NULL` segment, assumes that the phdr's `offset` and `filesz`
8072/// have been updated as needed by the caller, and updates the `@"align"`, `vaddr`, `paddr`, and
8073/// `memsz` fields of the segment, in order to place it at a valid virtual address.
8074///
8075/// TODO: this function is currently a source of non-determinism in the linker, because handling the
8076/// moving or resizing of a segment could reorder them and thereby affect how we handle *future*
8077/// changes to segments.
8078fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Error!void {
8079 const segment_ni = elf.phdrs.items[orig_phndx];
8080 assert(elf.getNode(segment_ni).segment == orig_phndx);
8081 const page_align = elf.targetPageAlign();
8082 const node_align = segment_ni.alignment(&elf.mf);
8083 const ph_align = page_align.max(node_align);
8084 switch (elf.phdrSlice()) {
8085 inline else => |phdr| {
8086 const offset = elf.targetLoad(&phdr[orig_phndx].offset);
8087 const size = elf.targetLoad(&phdr[orig_phndx].filesz);
8088
8089 if (size == 0) {
8090 assert(elf.targetLoad(&phdr[orig_phndx].type) == .NULL);
8091 } else {
8092 assert(elf.targetLoad(&phdr[orig_phndx].type) == .LOAD);
8093 }
8094
8095 elf.targetStore(&phdr[orig_phndx].memsz, size);
8096 elf.targetStore(&phdr[orig_phndx].@"align", @intCast(ph_align.toByteUnits()));
8097
8098 const orig_vaddr = elf.targetLoad(&phdr[orig_phndx].vaddr);
8099 assert(elf.targetLoad(&phdr[orig_phndx].paddr) == orig_vaddr);
8100
8101 var vaddr: u64 = orig_vaddr;
8102
8103 // First, we will shift the virtual address as needed in order to maintain the required
8104 // property that vaddr is congruent to offset modulo the phdr alignment.
8105 {
8106 // Compute the candidate address by undoing the current offset and then re-offsetting
8107 vaddr = std.mem.alignBackward(u64, vaddr, ph_align.toByteUnits()) + offset % ph_align.toByteUnits();
8108 // If `node_align` is greater than `page_align`, the address we just set might be in
8109 // the previous segment. The first page we "own" is the one in which the old vaddr
8110 // resides, so check against that.
8111 const first_good_vaddr = std.mem.alignBackward(u64, orig_vaddr, page_align.toByteUnits());
8112 if (vaddr < first_good_vaddr) {
8113 // Yep, we crossed into the previous segment's pages, so correct for that by
8114 // offsetting our address by another `ph_align`.
8115 vaddr += ph_align.toByteUnits();
8116 assert(vaddr >= first_good_vaddr);
8117 }
8118 }
8119
8120 // If our size has changed, or if the address shift above caused our "end" address to
8121 // cross a page boundary, then we might be overlapping with the next segment's pages. In
8122 // that case, we will jump past that segment and give ourselves a new address after it.
8123 // We'll need to repeat this for every loadable phdr after us, until we're no longer
8124 // overlapping anything.
8125 var phndx = orig_phndx;
8126 for (phdr[orig_phndx + 1 ..], orig_phndx + 1..) |*next_ph, next_phndx| {
8127 switch (elf.targetLoad(&next_ph.type)) {
8128 .NULL, .LOAD => {},
8129 else => {
8130 // All loadable segments have contiguous indices, so this indicates we have
8131 // become the last loadable segment, meaning we definitely don't overlap any
8132 // other loadable segment.
8133 break;
8134 },
8135 }
8136
8137 const next_vaddr = elf.targetLoad(&next_ph.vaddr);
8138 // Find the first virtual address which the next phdr "owns" by aligning its vaddr
8139 // backwards to the start of the page.
8140 const next_page_vaddr = std.mem.alignBackward(u64, next_vaddr, page_align.toByteUnits());
8141
8142 // If we're at the same vaddr we started at, then all we're worried about is the
8143 // segment fitting here. However, if we've already changed our virtual address, then
8144 // we might as well try to reserve a bit *more* virtual address space while we're at
8145 // it, because changing virtual address is quite disruptive (we need to re-flush a
8146 // lot of stuff!) and giving ourselves more space will make it less likely to happen
8147 // again.
8148 const target_size = if (vaddr == orig_vaddr) size else size * 4;
8149 if (vaddr + target_size <= next_page_vaddr) {
8150 break; // hooray, we fit here!
8151 }
8152
8153 // We don't fit here, so shift ourselves forward (i.e. swap with `next_phndx`). But
8154 // first we need to adjust `vaddr` to come after it.
8155 const next_size = elf.targetLoad(&next_ph.memsz);
8156 // Instead of putting ourselves right after `next_ph`, we'll go a bit later in the
8157 // address space so that `next_ph` has address space to grow into (like above).
8158 vaddr = ph_align.forward(@intCast(next_vaddr + next_size * 4)) + offset % ph_align.toByteUnits();
8159
8160 // Now just swap the phdrs and update our `phndx`.
8161 std.mem.swap(@TypeOf(next_ph.*), &phdr[phndx], next_ph);
8162 const next_ni = elf.phdrs.items[next_phndx];
8163 elf.phdrs.items[phndx] = next_ni;
8164 elf.nodes.items(.data)[@backingInt(next_ni)] = .{ .segment = phndx };
8165 elf.phdrs.items[next_phndx] = segment_ni;
8166 elf.nodes.items(.data)[@backingInt(segment_ni)] = .{ .segment = @intCast(next_phndx) };
8167 phndx = @intCast(next_phndx);
8168 }
8169
8170 if (vaddr != orig_vaddr) {
8171 elf.targetStore(&phdr[phndx].vaddr, @intCast(vaddr));
8172 elf.targetStore(&phdr[phndx].paddr, @intCast(vaddr));
8173 try segment_ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
8174 }
8175 },
8176 }
8177}
8178
7742fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {8179fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {
7743 const trace = tracy.trace(@src());8180 const trace = tracy.trace(@src());
7744 defer trace.end();8181 defer trace.end();
...@@ -7766,68 +8203,40 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo...@@ -7766,68 +8203,40 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
7766 assert(elf.phdrs.items[phndx] == ni);8203 assert(elf.phdrs.items[phndx] == ni);
7767 const ph = &phdr[phndx];8204 const ph = &phdr[phndx];
7768 elf.targetStore(&ph.filesz, @intCast(size));8205 elf.targetStore(&ph.filesz, @intCast(size));
7769 if (size > elf.targetLoad(&ph.memsz)) {8206 switch (elf.targetLoad(&ph.type)) {
7770 switch (elf.targetLoad(&ph.type)) {8207 else => unreachable,
7771 else => unreachable,8208 .NULL, .LOAD => {
7772 .NULL => if (size > 0) elf.targetStore(&ph.type, .LOAD),8209 elf.targetStore(&ph.type, if (size > 0) .LOAD else .NULL);
7773 .LOAD => if (size == 0) elf.targetStore(&ph.type, .NULL),8210 try elf.allocateSegmentLoadAddress(phndx);
7774 .DYNAMIC, .INTERP, .PHDR, std.elf.PT.GNU_RELRO => {8211 },
7775 elf.targetStore(&ph.memsz, @intCast(size));8212 .DYNAMIC, .INTERP, .PHDR, std.elf.PT.GNU_RELRO => {
7776 return;8213 elf.targetStore(&ph.memsz, @intCast(size));
7777 },8214 },
7778 .TLS => {8215 .TLS => {
7779 elf.targetStore(&ph.memsz, @intCast(size));8216 elf.targetStore(&ph.memsz, @intCast(size));
7780 // TPOFF relocations care about the size of the TLS segment. Re-apply8217 // TPOFF relocations care about the size of the TLS segment. Re-apply
7781 // those, and also update any GOT entries from GOTTPOFF relocations.8218 // those, and also update any GOT entries from GOTTPOFF relocations.
7782 for (elf.tls_size_symbol_relocs.keys()) |reloc| {8219 for (elf.tls_size_symbol_relocs.keys()) |reloc| {
7783 reloc.get(elf).apply(elf);8220 reloc.get(elf).apply(elf);
7784 }8221 }
7785 for (elf.got.keys(), 0..) |got_key, got_index| {8222 for (elf.got.keys(), 0..) |got_key, got_index| {
7786 switch (got_key) {8223 switch (got_key) {
7787 .reserved,8224 .reserved,
7788 .symbol,8225 .symbol,
7789 .tlsld0,8226 .tlsld0,
7790 .tlsld1,8227 .tlsld1,
7791 .tlsgd0,8228 .tlsgd0,
7792 .tlsgd1,8229 .tlsgd1,
7793 => {8230 => {
7794 @branchHint(.likely);8231 @branchHint(.likely);
7795 continue;8232 continue;
7796 },8233 },
77978234
7798 .tpoff => elf.updateGotEntry(got_index),8235 .tpoff => elf.updateGotEntry(got_index),
7799 }
7800 }8236 }
7801 return ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
7802 },
7803 }
7804 const memsz = ni.alignment(&elf.mf).forward(@intCast(size * 4));
7805 elf.targetStore(&ph.memsz, @intCast(memsz));
7806 var vaddr = elf.targetLoad(&ph.vaddr);
7807 var new_phndx = phndx;
7808 for (phdr[phndx + 1 ..], phndx + 1..) |*next_ph, next_phndx| {
7809 switch (elf.targetLoad(&next_ph.type)) {
7810 else => unreachable,
7811 .NULL, .LOAD => {},
7812 .DYNAMIC, .INTERP, .PHDR, .TLS, .GNU_RELRO, .GNU_STACK => break,
7813 }8237 }
7814 const next_vaddr = elf.targetLoad(&next_ph.vaddr);
7815 if (vaddr + memsz <= next_vaddr) break;
7816 vaddr = next_vaddr + elf.targetLoad(&next_ph.memsz);
7817 std.mem.swap(@TypeOf(ph.*), &phdr[new_phndx], next_ph);
7818 const next_ni = elf.phdrs.items[next_phndx];
7819 elf.phdrs.items[new_phndx] = next_ni;
7820 elf.nodes.items(.data)[@backingInt(next_ni)] = .{ .segment = new_phndx };
7821 new_phndx = @intCast(next_phndx);
7822 }
7823 if (new_phndx != phndx) {
7824 const new_ph = &phdr[new_phndx];
7825 elf.targetStore(&new_ph.vaddr, vaddr);
7826 new_ph.paddr = new_ph.vaddr;
7827 elf.phdrs.items[new_phndx] = ni;
7828 elf.nodes.items(.data)[@backingInt(ni)] = .{ .segment = new_phndx };
7829 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);8238 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
7830 }8239 },
7831 }8240 }
7832 },8241 },
7833 },8242 },
...@@ -7848,6 +8257,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo...@@ -7848,6 +8257,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
7848 .REL,8257 .REL,
7849 .RELA,8258 .RELA,
7850 .DYNSYM,8259 .DYNSYM,
8260 .HASH,
7851 => return,8261 => return,
7852 }8262 }
7853 if (shndx != elf.shndx.plt and8263 if (shndx != elf.shndx.plt and
...@@ -7940,21 +8350,6 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!...@@ -7940,21 +8350,6 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!
7940 }8350 }
7941}8351}
79428352
7943fn updateDynamicEntry(elf: *Elf, key: u32, new_val: u64) void {
7944 switch (elf.shdrPtr(elf.shndx.dynamic)) {
7945 inline else => |shdr, class| {
7946 const dynamic_size = elf.targetLoad(&shdr.size);
7947 const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
7948 elf.shndx.dynamic.get(elf).ni.slice(&elf.mf)[0..@intCast(dynamic_size)],
7949 ));
7950 for (dynamic_entries) |*dynamic_entry| {
7951 if (elf.targetLoad(&dynamic_entry[0]) == key) {
7952 elf.targetStore(&dynamic_entry[1], @intCast(new_val));
7953 }
7954 }
7955 },
7956 }
7957}
7958fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void {8353fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void {
7959 const target_endian = elf.targetEndian();8354 const target_endian = elf.targetEndian();
79608355
...@@ -8126,9 +8521,9 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void...@@ -8126,9 +8521,9 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
8126 const plt_slice: []Inst = @ptrCast(@alignCast(plt_ni.slice(&elf.mf)[@intCast(got_plt_offset)..][0..32]));8521 const plt_slice: []Inst = @ptrCast(@alignCast(plt_ni.slice(&elf.mf)[@intCast(got_plt_offset)..][0..32]));
8127 @memcpy(plt_slice, &[8]Inst{8522 @memcpy(plt_slice, &[8]Inst{
8128 // sethi (. - .plt[0]), %g18523 // sethi (. - .plt[0]), %g1
8129 .{ .imm22 = .{ .imm = @truncate(got_plt_offset), .op = 0b0000000011 } },8524 .{ .imm22 = .{ .imm = @truncate(got_plt_offset), .op = 0b0000001100 } },
8130 // ba,a %xcc, .plt[1]8525 // ba,a %xcc, .plt[1]
8131 .{ .disp19 = .{ .disp = @truncate((got_plt_offset + 4 - 32) >> 2), .op = 0b1100001101000 } },8526 .{ .disp19 = .{ .disp = @truncate((got_plt_offset + 4 - 32) >> 2), .op = 0b0011000001101 } },
8132 // nop8527 // nop
8133 .{ .raw = 0x0100_0000 },8528 .{ .raw = 0x0100_0000 },
8134 // nop8529 // nop
...@@ -8142,6 +8537,9 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void...@@ -8142,6 +8537,9 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
8142 // nop8537 // nop
8143 .{ .raw = 0x0100_0000 },8538 .{ .raw = 0x0100_0000 },
8144 });8539 });
8540 if (elf.targetEndian() != std.lang.Endian.native) {
8541 std.mem.byteSwapAllElements(Inst, plt_slice);
8542 }
8145 },8543 },
8146 }8544 }
8147 },8545 },
...@@ -8212,6 +8610,13 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad...@@ -8212,6 +8610,13 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
8212 .LOONGARCH => {8610 .LOONGARCH => {
8213 switch (which) {8611 switch (which) {
8214 .plt => {8612 .plt => {
8613 // Re-apply all PLT relocations. If a symbol is in the PLT then the majority of
8614 // its relocations are probably going through the PLT, so we don't bother with
8615 // specific tracking for PLT relocations---instead just re-apply all relocations
8616 // targeting symbols with PLT entries.
8617 for (elf.plt.keys()) |name| {
8618 Symbol.Id.global(name).applyTargetRelocs(elf);
8619 }
8215 // We also need to update all of the references from `.plt` to `.got.plt`.8620 // We also need to update all of the references from `.plt` to `.got.plt`.
8216 // However, if there's also a flush pending for `.got.plt`, don't bother doing8621 // However, if there's also a flush pending for `.got.plt`, don't bother doing
8217 // this now, because we'll do it when `.got.plt` is flushed anyway.8622 // this now, because we'll do it when `.got.plt` is flushed anyway.
...@@ -8272,6 +8677,13 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad...@@ -8272,6 +8677,13 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
8272 },8677 },
8273 .SPARCV9 => switch (which) {8678 .SPARCV9 => switch (which) {
8274 .plt => {8679 .plt => {
8680 // Re-apply all PLT relocations. If a symbol is in the PLT then the majority of
8681 // its relocations are probably going through the PLT, so we don't bother with
8682 // specific tracking for PLT relocations---instead just re-apply all relocations
8683 // targeting symbols with PLT entries.
8684 for (elf.plt.keys()) |name| {
8685 Symbol.Id.global(name).applyTargetRelocs(elf);
8686 }
8275 // Update the offsets of the relocation entries in `.rela.plt`.8687 // Update the offsets of the relocation entries in `.rela.plt`.
8276 const rela_plt_shndx = elf.shndx.rela_plt;8688 const rela_plt_shndx = elf.shndx.rela_plt;
8277 for (0..elf.plt.count()) |plt_index| {8689 for (0..elf.plt.count()) |plt_index| {
...@@ -8315,31 +8727,31 @@ fn updateExportInner(...@@ -8315,31 +8727,31 @@ fn updateExportInner(
8315 }),8727 }),
8316 }8728 }
8317 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);8729 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);
8318 const exported_lsi: Symbol.LocalIndex, const @"type": std.elf.STT = switch (@"export".exported) {8730 const exported_lsi: Symbol.LocalIndex = switch (@"export".exported) {
8319 .nav => |nav| .{8731 .nav => |nav| (try elf.navMapIndex(zcu, nav)).symbol(elf),
8320 (try elf.navMapIndex(zcu, nav)).symbol(elf),8732 .uav => |uav| (try elf.uavMapIndex(uav, .none)).symbol(elf),
8321 elf.navType(ip.getNav(nav).resolved.?),
8322 },
8323 .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT },
8324 };8733 };
83258734
8326 try elf.ensureElfNodeSize();8735 try elf.ensureElfNodeSize();
8327 while (try elf.idle(pt.tid)) {}
83288736
8329 const value: u64 = Symbol.Id.local(exported_lsi).value(elf);8737 // Initialize the global symbol with the same values that the local one currently has. If the
8330 const size: u64, const shndx: Section.Index = switch (elf.symPtr(exported_lsi.index())) {8738 // NAV/UAV is updated, then `updateNavInner` or `genUav` will update the global symbol sizes,
8739 // and `flushMoved` will update their values.
8740 const cur_value: u64, const cur_size: u64, const @"type": std.elf.STT, const shndx: Section.Index = switch (elf.symPtr(exported_lsi.index())) {
8331 inline else => |exported_sym| .{8741 inline else => |exported_sym| .{
8742 elf.targetLoad(&exported_sym.value),
8332 elf.targetLoad(&exported_sym.size),8743 elf.targetLoad(&exported_sym.size),
8744 elf.targetLoad(&exported_sym.info).type,
8333 .fromSection(elf.targetLoad(&exported_sym.shndx)),8745 .fromSection(elf.targetLoad(&exported_sym.shndx)),
8334 },8746 },
8335 };8747 };
83368748
8337 const name = @"export".opts.name.toSlice(ip);8749 const name = @"export".opts.name.toSlice(ip);
8338 _ = elf.addGlobalSymbolAssumeCapacity(.{8750 _ = elf.addGlobalSymbolAssumeCapacity(.{
8339 .node = .none,8751 .node = exported_lsi.index().ptr(elf).node,
8340 .name = try .string(elf, name),8752 .name = try .string(elf, name),
8341 .value = value,8753 .value = cur_value,
8342 .size = @intCast(size),8754 .size = cur_size,
8343 .type = @"type",8755 .type = @"type",
8344 .bind = switch (@"export".opts.linkage) {8756 .bind = switch (@"export".opts.linkage) {
8345 .strong => .strong,8757 .strong => .strong,
...@@ -8501,6 +8913,46 @@ pub fn printNode(...@@ -8501,6 +8913,46 @@ pub fn printNode(
8501 }8913 }
8502}8914}
85038915
8916fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: std.mem.Alignment) Error!void {
8917 const gpa = elf.base.comp.gpa;
8918 // We need to loop through parent nodes because segments may be nested (e.g. a PT_TLS segment
8919 // inside a PT_LOAD segment).
8920 var phndx = start_phndx;
8921 while (true) {
8922 // Align the actual node
8923 const seg_ni = elf.phdrs.items[phndx];
8924 if (min_align.compare(.gt, seg_ni.alignment(&elf.mf))) {
8925 try seg_ni.realign(&elf.mf, gpa, min_align, .{});
8926 }
8927 // Update the phdr `@"align"` field if necessary
8928 switch (elf.phdrSlice()) {
8929 inline else => |phdr| switch (elf.targetLoad(&phdr[phndx].type)) {
8930 .NULL, .LOAD => {
8931 // The `@"align"` field is managed by `allocateSegmentLoadAddress`.
8932 //
8933 // It's very likely that the node was moved and/or resized when we realigned it
8934 // just above, but it is possible that it was not moved *but* still has an
8935 // unaligned virtual address. In that case, we need to ensure the segment's
8936 // virtual address range will be recomputed.
8937 if (!min_align.check(@intCast(elf.targetLoad(&phdr[phndx].vaddr)))) {
8938 try seg_ni.moved(gpa, &elf.mf);
8939 }
8940 },
8941 else => elf.targetStore(&phdr[phndx].@"align", @intCast(@max(
8942 elf.targetLoad(&phdr[phndx].@"align"),
8943 min_align.toByteUnits(),
8944 ))),
8945 },
8946 }
8947 // Continue on to the parent segment, if any
8948 switch (elf.getNode(seg_ni.parent(&elf.mf))) {
8949 .segment => |parent_phndx| phndx = parent_phndx,
8950 .elf => return,
8951 else => unreachable,
8952 }
8953 }
8954}
8955
8504/// Must be called deterministically after any call to `MappedFile.Node.Index.resize`8956/// Must be called deterministically after any call to `MappedFile.Node.Index.resize`
8505/// (of `elf.ni.elf` or one of its children) before any possible calls to `idle`.8957/// (of `elf.ni.elf` or one of its children) before any possible calls to `idle`.
8506fn ensureElfNodeSize(elf: *Elf) MappedFile.Error!void {8958fn ensureElfNodeSize(elf: *Elf) MappedFile.Error!void {
src/link/MappedFile.zig+19-14
...@@ -856,19 +856,20 @@ fn resizeNode(...@@ -856,19 +856,20 @@ fn resizeNode(
856 ni.setLocationAssumeCapacity(mf, old_offset, new_size);856 ni.setLocationAssumeCapacity(mf, old_offset, new_size);
857 return;857 return;
858 }858 }
859 if (is_linux and !mf.flags.fallocate_insert_range_unsupported and
860 node.flags.alignment.order(mf.flags.block_size).compare(.gte))
861 insert_range: {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.
862 const range_file_offset = ni.fileLocation(mf, false).offset + old_size;867 const range_file_offset = ni.fileLocation(mf, false).offset + old_size;
863 const range_size = node.flags.alignment.forward(868 const range_size = node.flags.alignment.forward(
864 @intCast(requested_size +| requested_size / growth_factor),869 @intCast(requested_size +| requested_size / growth_factor),
865 ) - old_size;870 ) - old_size;
866871 if (!mf.flags.block_size.check(@intCast(range_file_offset))) break :insert_range;
867 // If this node is being realigned, its current state might not872 if (!mf.flags.block_size.check(@intCast(range_size))) break :insert_range;
868 // meet the requirements for fallocate
869 if (!mf.flags.block_size.check(@intCast(range_file_offset)) or
870 !mf.flags.block_size.check(@intCast(range_size)))
871 break :insert_range;
872873
873 mf.memory_map.write(io) catch |err| switch (err) {874 mf.memory_map.write(io) catch |err| switch (err) {
874 error.WouldBlock => return error.Unexpected, // file was not opened as non-blocking875 error.WouldBlock => return error.Unexpected, // file was not opened as non-blocking
...@@ -1022,9 +1023,10 @@ fn resizeNode(...@@ -1022,9 +1023,10 @@ fn resizeNode(
1022 }1023 }
1023 try mf.ensureCapacityForSetLocation(gpa);1024 try mf.ensureCapacityForSetLocation(gpa);
1024 if (parent.last != first_floating_ni) {1025 if (parent.last != first_floating_ni) {
1025 first_floating.prev = parent.last;1026 const old_last = parent.last;
1027 first_floating.prev = old_last;
1026 parent.last = first_floating_ni;1028 parent.last = first_floating_ni;
1027 try parent.last.setNext(gpa, first_floating_ni, mf);1029 try old_last.setNext(gpa, first_floating_ni, mf);
1028 try last_fixed_ni.setNext(gpa, first_floating.next, mf);1030 try last_fixed_ni.setNext(gpa, first_floating.next, mf);
1029 switch (first_floating.next) {1031 switch (first_floating.next) {
1030 .none => {},1032 .none => {},
...@@ -1208,9 +1210,11 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size:...@@ -1208,9 +1210,11 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size:
1208 // make a copy of this node at the new location1210 // make a copy of this node at the new location
1209 try mf.copyRange(old_file_offset, new_file_offset, size);1211 try mf.copyRange(old_file_offset, new_file_offset, size);
1210 // delete the copy of this node at the old location1212 // delete the copy of this node at the old location
1211 if (is_linux and !mf.flags.fallocate_punch_hole_unsupported and1213 if (is_linux and
1212 size >= mf.flags.block_size.toByteUnits() * 2 - 1) while (true)1214 !mf.flags.fallocate_punch_hole_unsupported and
1213 switch (linux.errno(linux.fallocate(1215 size >= mf.flags.block_size.toByteUnits() * 2 - 1)
1216 {
1217 while (true) switch (linux.errno(linux.fallocate(
1214 mf.memory_map.file.handle,1218 mf.memory_map.file.handle,
1215 linux.FALLOC.FL_PUNCH_HOLE | linux.FALLOC.FL_KEEP_SIZE,1219 linux.FALLOC.FL_PUNCH_HOLE | linux.FALLOC.FL_KEEP_SIZE,
1216 @intCast(old_file_offset),1220 @intCast(old_file_offset),
...@@ -1224,13 +1228,14 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size:...@@ -1224,13 +1228,14 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size:
1224 .NOSPC => return error.NoSpaceLeft,1228 .NOSPC => return error.NoSpaceLeft,
1225 .NOSYS, .OPNOTSUPP => {1229 .NOSYS, .OPNOTSUPP => {
1226 mf.flags.fallocate_punch_hole_unsupported = true;1230 mf.flags.fallocate_punch_hole_unsupported = true;
1227 break;1231 break; // fall back to slow path
1228 },1232 },
1229 .PERM => return error.PermissionDenied,1233 .PERM => return error.PermissionDenied,
1230 .SPIPE => return error.Unseekable,1234 .SPIPE => return error.Unseekable,
1231 .TXTBSY => return error.FileBusy,1235 .TXTBSY => return error.FileBusy,
1232 else => |e| return std.posix.unexpectedErrno(e),1236 else => |e| return std.posix.unexpectedErrno(e),
1233 };1237 };
1238 }
1234 @memset(mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)], 0);1239 @memset(mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)], 0);
1235}1240}
12361241
test/tests.zig+36-2
...@@ -26,6 +26,7 @@ const ModuleTestTarget = struct {...@@ -26,6 +26,7 @@ const ModuleTestTarget = struct {
26 single_threaded: ?bool = null,26 single_threaded: ?bool = null,
27 use_llvm: ?bool = null,27 use_llvm: ?bool = null,
28 use_lld: ?bool = null,28 use_lld: ?bool = null,
29 new_linker: ?bool = null,
29 pic: ?bool = null,30 pic: ?bool = null,
30 strip: ?bool = null,31 strip: ?bool = null,
31 function_sections: ?bool = null,32 function_sections: ?bool = null,
...@@ -1288,6 +1289,34 @@ const module_test_targets = blk: {...@@ -1288,6 +1289,34 @@ const module_test_targets = blk: {
1288 },1289 },
1289 .link_libc = true,1290 .link_libc = true,
1290 },1291 },
1292 .{
1293 .target = .{
1294 .cpu_arch = .x86_64,
1295 .os_tag = .linux,
1296 },
1297 .new_linker = true,
1298 .skip_modules = &.{ "compiler-rt", "behavior" }, // '@export' with '.internal' linkage
1299 },
1300 .{
1301 .target = .{
1302 .cpu_arch = .x86_64,
1303 .os_tag = .linux,
1304 .abi = .musl,
1305 },
1306 .link_libc = true,
1307 .new_linker = true,
1308 .skip_modules = &.{ "compiler-rt", "behavior" }, // '@export' with '.internal' linkage
1309 },
1310 .{
1311 .target = .{
1312 .cpu_arch = .x86_64,
1313 .os_tag = .linux,
1314 .abi = .gnu,
1315 },
1316 .link_libc = true,
1317 .new_linker = true,
1318 .skip_modules = &.{ "compiler-rt", "behavior" }, // '@export' with '.internal' linkage
1319 },
12911320
1292 // Darwin Targets1321 // Darwin Targets
12931322
...@@ -2821,6 +2850,7 @@ fn addOneModuleTest(...@@ -2821,6 +2850,7 @@ fn addOneModuleTest(
2821 .zig_lib_dir = b.path("lib"),2850 .zig_lib_dir = b.path("lib"),
2822 });2851 });
2823 these_tests.linkage = test_target.linkage;2852 these_tests.linkage = test_target.linkage;
2853 these_tests.use_new_linker = test_target.new_linker;
2824 // https://codeberg.org/ziglang/zig/issues/317012854 // https://codeberg.org/ziglang/zig/issues/31701
2825 if (!(mem.eql(u8, options.name, "compiler-rt") or mem.eql(u8, options.name, "libc"))) {2855 if (!(mem.eql(u8, options.name, "compiler-rt") or mem.eql(u8, options.name, "libc"))) {
2826 if (options.no_builtin) these_tests.root_module.no_builtin = true;2856 if (options.no_builtin) these_tests.root_module.no_builtin = true;
...@@ -2847,7 +2877,11 @@ fn addOneModuleTest(...@@ -2847,7 +2877,11 @@ fn addOneModuleTest(
2847 "-selfhosted"2877 "-selfhosted"
2848 else2878 else
2849 "";2879 "";
2850 const use_lld = if (test_target.use_lld == false) "-no-lld" else "";2880 const linker_suffix: []const u8 = s: {
2881 if (test_target.new_linker == true) break :s "-new-linker";
2882 if (test_target.use_lld == false) break :s "-no-lld";
2883 break :s "";
2884 };
2851 const linkage_name = if (test_target.linkage) |linkage| switch (linkage) {2885 const linkage_name = if (test_target.linkage) |linkage| switch (linkage) {
2852 inline else => |t| "-" ++ @tagName(t),2886 inline else => |t| "-" ++ @tagName(t),
2853 } else "";2887 } else "";
...@@ -2863,7 +2897,7 @@ fn addOneModuleTest(...@@ -2863,7 +2897,7 @@ fn addOneModuleTest(
2863 libc_suffix,2897 libc_suffix,
2864 single_threaded_suffix,2898 single_threaded_suffix,
2865 backend_suffix,2899 backend_suffix,
2866 use_lld,2900 linker_suffix,
2867 linkage_name,2901 linkage_name,
2868 use_pic,2902 use_pic,
2869 });2903 });