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 {
32903290
32913291 /// Calculate the hash value for a name
32923292 pub fn calculate(name: []const u8) u32 {
3293 var hash: u32 = 5381;
3294
3293 var h: u32 = 5381;
32953294 for (name) |char| {
3296 hash = (hash << 5) +% hash +% char;
3295 h = (h << 5) +% h +% char;
32973296 }
3298
3299 return hash;
3297 return h;
33003298 }
33013299
33023300 test calculate {
......@@ -3308,6 +3306,52 @@ pub const gnu_hash = struct {
33083306 }
33093307};
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
33113355pub const EhdrFlags = packed union(Word) {
33123356 int: u32,
33133357 loongarch: Loongarch,
lib/std/os/linux.zig+4-1
......@@ -2135,7 +2135,10 @@ pub fn flock(fd: fd_t, operation: i32) usize {
21352135 return syscall2(.flock, @as(u32, @bitCast(fd)), @as(u32, @bitCast(operation)));
21362136}
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
21402143// We must follow the C calling convention when we call into the VDSO
21412144const 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;
6464const dl_phdr_info = if (have_dl_phdr_info) posix.dl_phdr_info else anyopaque;
6565
6666const IterFnError = error{
67 MissingPtLoadSegment,
68 MissingLoad,
67 MissingLoadSegment,
68 MissingEhdrLoadSegment,
6969 BadElfMagic,
70 FailedConsistencyCheck,
70 PhnumMismatch,
7171};
7272
7373fn iter_fn(info: *dl_phdr_info, size: usize, counter: *usize) IterFnError!void {
7474 _ = size;
7575 // 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 segment
79 if (info.phnum < 1) return error.MissingPtLoadSegment;
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];
78 // The image should contain at least one loadable segment
79 if (info.phnum < 1) return error.MissingLoadSegment;
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| {
8884 if (phdr.type != .LOAD) continue;
89
90 const reloc_addr = info.addr + phdr.vaddr;
91 // Find the ELF header
92 const elf_header = @as(*elf.Ehdr, @ptrFromInt(reloc_addr - phdr.offset));
93 // Validate the magic
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;
85 if (phdr.offset != 0) continue;
86 // This segment holds the ELF header at the start
87 const ehdr: *elf.Ehdr = @ptrFromInt(info.addr + phdr.vaddr);
88 if (!mem.eql(u8, ehdr.e_ident[0..4], elf.MAGIC)) return error.BadElfMagic;
89 if (ehdr.e_phnum != info.phnum) return error.PhnumMismatch;
9990 break;
91 } else {
92 return error.MissingEhdrLoadSegment;
10093 }
101
102 if (!found_load) return error.MissingLoad;
10394}
10495
10596test "dl_iterate_phdr" {
src/link/Elf2.zig+799-347
......@@ -36,6 +36,7 @@ shndx: struct {
3636 dynsym: Section.Index,
3737 dynstr: Section.Index,
3838 dynamic: Section.Index,
39 hash: Section.Index,
3940 tdata: Section.Index,
4041 rela_dyn: Section.Index,
4142 rela_plt: Section.Index,
......@@ -119,8 +120,6 @@ got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),
119120plt: std.array_hash_map.Auto(String(.strtab), void),
120121/// The `.plt` section contains zero or more symbol relocations starting at this index.
121122plt_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
125124needed: std.array_hash_map.Auto(String(.dynstr), void),
126125inputs: std.ArrayList(struct {
......@@ -136,6 +135,18 @@ inputs: std.ArrayList(struct {
136135input_pending_index: u32,
137136input_sections: std.ArrayList(InputSection),
138137input_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}),
139150navs: std.array_hash_map.Auto(InternPool.Nav.Index, struct {
140151 lsi: Symbol.LocalIndex,
141152 /// The start index of the contiguous sequence of symbol relocations in this NAV.
......@@ -195,8 +206,6 @@ const Node = union(enum) {
195206 shdr,
196207 segment: u32,
197208 /// 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`.
200209 section: Section.Index,
201210 /// Only valid for static libraries, represents one non-zcu archive member.
202211 input_member: InputIndex,
......@@ -530,6 +539,26 @@ const Section = struct {
530539 }
531540 }
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
533562 /// Asserts that `rela_shndx` is a `SHT_RELA` section and ensures that its node has enough
534563 /// unused space to hold `n` additional `ElfN.Rela` entries.
535564 fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) Error!void {
......@@ -634,11 +663,6 @@ const Section = struct {
634663 const old_size = elf.targetLoad(&shdr.size);
635664 const new_size = old_size + ent_size;
636665 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 }
642666 break :new_index @fromBackingInt(@intCast(@divExact(old_size, ent_size)));
643667 };
644668 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
......@@ -1378,7 +1402,7 @@ const SymbolReloc = struct {
13781402 const shift: u6, const shift_exact: bool = switch (s.shift) {
13791403 .@"0" => .{ 0, false },
13801404 .@"2_exact" => .{ 2, true },
1381 .@"10" => .{ 10, true },
1405 .@"10" => .{ 10, false },
13821406 .@"12" => .{ 12, false },
13831407 .@"22" => .{ 22, false },
13841408 .@"32" => .{ 32, false },
......@@ -1760,6 +1784,170 @@ const SymbolReloc = struct {
17601784 }
17611785};
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
17631951fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) Error!void {
17641952 const gpa = elf.base.comp.gpa;
17651953
......@@ -1789,12 +1977,19 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe
17891977 try elf.node_global_symbols.ensureUnusedCapacity(gpa, len);
17901978
17911979 if (elf.shndx.dynsym != .UNDEF) {
1792 // Ensure the `.dynsym` section's node is big enough
1793 const dynsym_need_size: u64 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
1794 inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym),
1980 const dynsym_cur_size: u64, const dynsym_ent_size: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
1981 inline else => |shdr, class| .{
1982 elf.targetLoad(&shdr.size),
1983 @sizeOf(class.ElfN().Sym),
1984 },
17951985 };
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;
17961989 try elf.ensureNodeSize(elf.shndx.dynsym.get(elf).ni, dynsym_need_size);
17971990
1991 try elf.ensureDynsymHashCapacity(dynsym_cur_len + len);
1992
17981993 try elf.ensureUnusedPltCapacity(len);
17991994 }
18001995 },
......@@ -2131,6 +2326,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
21312326 if (elf.targetEndian() != native_endian) {
21322327 std.mem.byteSwapAllFields(Sym, sym);
21332328 }
2329 elf.appendDynsymHashEntry(dynsym_index);
21342330 break :dynsym_index dynsym_index;
21352331 },
21362332 }
......@@ -2279,8 +2475,9 @@ fn setGlobalSymbolValue(
22792475 }
22802476
22812477 // 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.
2283 if (elf.shndx.dynamic != .UNDEF) {
2478 // runtime relocations which we have now discovered we do not need, so delete those. This does
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) {
22842481 Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf);
22852482 }
22862483
......@@ -2404,6 +2601,8 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
24042601 const new_size = old_size - ent_size;
24052602 const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));
24062603
2604 elf.popDynsymHashEntry(remove_dynsym_index);
2605
24072606 const free_dynsym_index = global_ptr.dynsym_index;
24082607 global_ptr.dynsym_index = 0;
24092608
......@@ -2411,6 +2610,8 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
24112610 // The demoted global wasn't the last entry, so move whatever entry we just
24122611 // truncated out of dynsym into its place.
24132612
2613 elf.clearDynsymHashEntry(free_dynsym_index);
2614
24142615 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));
24152616 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 {
24232624 assert(moved_global_ptr.dynsym_index == remove_dynsym_index);
24242625 moved_global_ptr.dynsym_index = free_dynsym_index;
24252626
2627 elf.populateDynsymHashEntry(free_dynsym_index);
2628
24262629 // Since that symbol's dynsym index has changed, we'll have to update any
24272630 // relocation entries targeting it.
24282631 elf.changed_symtab_index.putAssumeCapacity(moved_name, {});
......@@ -3045,9 +3248,6 @@ const StringTable = struct {
30453248 break :size .{ old_size, new_size };
30463249 },
30473250 };
3048 if (shndx == elf.shndx.dynstr) {
3049 elf.updateDynamicEntry(std.elf.DT_STRSZ, new_size);
3050 }
30513251 try elf.ensureNodeSize(ni, new_size);
30523252 const slice = ni.slice(&elf.mf)[old_size..];
30533253 @memcpy(slice[0..key.len], key);
......@@ -3172,6 +3372,7 @@ fn create(
31723372 .dynsym = .UNDEF,
31733373 .dynstr = .UNDEF,
31743374 .dynamic = .UNDEF,
3375 .hash = .UNDEF,
31753376 .tdata = .UNDEF,
31763377 .rela_dyn = .UNDEF,
31773378 .rela_plt = .UNDEF,
......@@ -3202,12 +3403,12 @@ fn create(
32023403 .got = .empty,
32033404 .plt = .empty,
32043405 .plt_first_symbol_reloc = .none,
3205 .dynamic_first_symbol_reloc = .none,
32063406 .needed = .empty,
32073407 .inputs = .empty,
32083408 .input_pending_index = 0,
32093409 .input_sections = .empty,
32103410 .input_section_pending_index = 0,
3411 .one_shot_fixups = .empty,
32113412 .navs = .empty,
32123413 .uavs = .empty,
32133414 .lazy = comptime .initFill(.{
......@@ -3257,6 +3458,7 @@ pub fn deinit(elf: *Elf) void {
32573458 for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m);
32583459 elf.inputs.deinit(gpa);
32593460 elf.input_sections.deinit(gpa);
3461 elf.one_shot_fixups.deinit(gpa);
32603462 elf.navs.deinit(gpa);
32613463 elf.uavs.deinit(gpa);
32623464 for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);
......@@ -3293,6 +3495,16 @@ fn initHeaders(
32933495 .@"64" => .@"8",
32943496 };
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
32963508 const plt: PltInfo = .fromMachine(machine);
32973509
32983510 const shnum: u32 = shnum: {
......@@ -3310,6 +3522,7 @@ fn initHeaders(
33103522 shnum += 1; // .dynamic
33113523 shnum += 1; // .dynstr
33123524 shnum += 1; // .dynsym
3525 shnum += 1; // .hash
33133526 shnum += 1; // .rela.dyn
33143527 shnum += 1; // .rela.plt
33153528 }
......@@ -3328,6 +3541,10 @@ fn initHeaders(
33283541 rodata: u32,
33293542 text: u32,
33303543 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,
33313548 tls: u32,
33323549 dynamic: u32,
33333550 relro: u32,
......@@ -3359,6 +3576,10 @@ fn initHeaders(
33593576 defer phnum += 1;
33603577 break :phndx phnum;
33613578 },
3579 .plt = if (plt.got_plt == null) phndx: {
3580 defer phnum += 1;
3581 break :phndx phnum;
3582 } else undefined,
33623583 .tls = if (comp.config.any_non_single_threaded) phndx: {
33633584 defer phnum += 1;
33643585 break :phndx phnum;
......@@ -3414,7 +3635,7 @@ fn initHeaders(
34143635
34153636 elf.nodes.appendAssumeCapacity(.archive_header);
34163637 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"),
34183639 .next_moved = true,
34193640 .bubbles_moved = false,
34203641 .enable_next_moved = true,
......@@ -3424,9 +3645,98 @@ fn initHeaders(
34243645
34253646 const entsize: struct { ph: u32, sh: u32 } = switch (class) {
34263647 .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| {
34283733 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, .{
34303740 .size = @sizeOf(ElfN.Ehdr),
34313741 .alignment = addr_align,
34323742 .fixed = true,
......@@ -3478,106 +3788,58 @@ fn initHeaders(
34783788 ehdr.shnum = 1; // Only the null shdr initially---will be incremented by `addSection`
34793789 ehdr.shstrndx = std.elf.SHN_UNDEF;
34803790 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
3481
3482 break :entsize .{ .ph = @sizeOf(ElfN.Phdr), .sh = @sizeOf(ElfN.Shdr) };
34833791 },
3484 };
3792 }
34853793
34863794 elf.ni.shdr = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{
34873795 .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),
34893797 .moved = true,
34903798 .resized = true,
34913799 });
34923800 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;
35733802 switch (class) {
35743803 .NONE, _ => unreachable,
35753804 inline else => |ct_class| {
35763805 const ElfN = ct_class.ElfN();
35773806 const target_endian = elf.targetEndian();
35783807
3579 if (@"type" != .REL) {
3580 const phdr: []ElfN.Phdr = @ptrCast(@alignCast(elf.ni.phdr.slice(&elf.mf)));
3808 populate_phdrs: {
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
35813843 const ph_phdr = &phdr[phndx.phdr];
35823844 ph_phdr.* = .{
35833845 .type = .PHDR,
......@@ -3589,7 +3851,6 @@ fn initHeaders(
35893851 .flags = .{ .R = true },
35903852 .@"align" = @intCast(elf.ni.phdr.alignment(&elf.mf).toByteUnits()),
35913853 };
3592 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_phdr);
35933854
35943855 if (maybe_interp) |_| {
35953856 const ph_interp = &phdr[phndx.interp];
......@@ -3603,53 +3864,57 @@ fn initHeaders(
36033864 .flags = .{ .R = true },
36043865 .@"align" = 1,
36053866 };
3606 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_interp);
36073867 }
36083868
3609 _, const rodata_size = elf.ni.rodata.location(&elf.mf).resolve(&elf.mf);
36103869 const ph_rodata = &phdr[phndx.rodata];
36113870 ph_rodata.* = .{
3612 .type = if (rodata_size == 0) .NULL else .LOAD,
3871 .type = .NULL,
36133872 .offset = 0,
3614 .vaddr = ph_vaddr,
3615 .paddr = ph_vaddr,
3616 .filesz = @intCast(rodata_size),
3617 .memsz = @intCast(rodata_size),
3873 .vaddr = @intCast(base_vaddr),
3874 .paddr = @intCast(base_vaddr),
3875 .filesz = 0,
3876 .memsz = 0,
36183877 .flags = .{ .R = true },
3619 .@"align" = @intCast(elf.ni.rodata.alignment(&elf.mf).max(page_align).toByteUnits()),
3878 .@"align" = @intCast(page_align.toByteUnits()),
36203879 };
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);
36253881 const ph_text = &phdr[phndx.text];
36263882 ph_text.* = .{
3627 .type = if (text_size == 0) .NULL else .LOAD,
3883 .type = .NULL,
36283884 .offset = 0,
3629 .vaddr = ph_vaddr,
3630 .paddr = ph_vaddr,
3631 .filesz = @intCast(text_size),
3632 .memsz = @intCast(text_size),
3885 .vaddr = @intCast(base_vaddr),
3886 .paddr = @intCast(base_vaddr),
3887 .filesz = 0,
3888 .memsz = 0,
36333889 .flags = .{ .R = true, .X = true },
3634 .@"align" = @intCast(elf.ni.text.alignment(&elf.mf).max(page_align).toByteUnits()),
3890 .@"align" = @intCast(page_align.toByteUnits()),
36353891 };
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);
36403893 const ph_data = &phdr[phndx.data];
36413894 ph_data.* = .{
3642 .type = if (data_size == 0) .NULL else .LOAD,
3895 .type = .NULL,
36433896 .offset = 0,
3644 .vaddr = ph_vaddr,
3645 .paddr = ph_vaddr,
3646 .filesz = @intCast(data_size),
3647 .memsz = @intCast(data_size),
3897 .vaddr = @intCast(base_vaddr),
3898 .paddr = @intCast(base_vaddr),
3899 .filesz = 0,
3900 .memsz = 0,
36483901 .flags = .{ .R = true, .W = true },
3649 .@"align" = @intCast(elf.ni.data.alignment(&elf.mf).max(page_align).toByteUnits()),
3902 .@"align" = @intCast(page_align.toByteUnits()),
36503903 };
3651 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_data);
3652 ph_vaddr += @intCast(data_size);
3904
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
36543919 if (comp.config.any_non_single_threaded) {
36553920 const ph_tls = &phdr[phndx.tls];
......@@ -3661,9 +3926,8 @@ fn initHeaders(
36613926 .filesz = 0,
36623927 .memsz = 0,
36633928 .flags = .{ .R = true },
3664 .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()),
3929 .@"align" = @intCast(elf.ni.tls.alignment(&elf.mf).toByteUnits()),
36653930 };
3666 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_tls);
36673931 }
36683932
36693933 if (have_dynamic_section) {
......@@ -3678,7 +3942,6 @@ fn initHeaders(
36783942 .flags = .{ .R = true, .W = true },
36793943 .@"align" = @intCast(addr_align.toByteUnits()),
36803944 };
3681 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_dynamic);
36823945 }
36833946
36843947 const ph_relro = &phdr[phndx.relro];
......@@ -3690,9 +3953,8 @@ fn initHeaders(
36903953 .filesz = 0,
36913954 .memsz = 0,
36923955 .flags = .{ .R = true },
3693 .@"align" = @intCast(elf.mf.flags.block_size.toByteUnits()),
3956 .@"align" = @intCast(elf.ni.data_rel_ro.alignment(&elf.mf).toByteUnits()),
36943957 };
3695 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_relro);
36963958
36973959 const ph_gnu_stack = &phdr[phndx.gnu_stack];
36983960 ph_gnu_stack.* = .{
......@@ -3705,7 +3967,10 @@ fn initHeaders(
37053967 .flags = .{ .R = true, .W = true },
37063968 .@"align" = 1,
37073969 };
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 }
37093974 }
37103975
37113976 const sh_undef: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf)));
......@@ -3733,7 +3998,7 @@ fn initHeaders(
37333998 .size = @sizeOf(ElfN.Sym) * 1,
37343999 .addralign = addr_align,
37354000 .entsize = @sizeOf(ElfN.Sym),
3736 .node_align = elf.mf.flags.block_size,
4001 .node_align = node_block_align,
37374002 .info = 1, // index of first non-local symbol
37384003 }));
37394004 const symtab_null = @field(elf.symPtr(.null), @tagName(ct_class));
......@@ -3755,7 +4020,7 @@ fn initHeaders(
37554020 .type = .STRTAB,
37564021 .size = 1,
37574022 .entsize = 1,
3758 .node_align = elf.mf.flags.block_size,
4023 .node_align = node_block_align,
37594024 }));
37604025 Section.Index.get(.shstrtab, elf).ni.slice(&elf.mf)[0] = 0;
37614026
......@@ -3767,7 +4032,7 @@ fn initHeaders(
37674032 .type = .STRTAB,
37684033 .size = 1,
37694034 .entsize = 1,
3770 .node_align = elf.mf.flags.block_size,
4035 .node_align = node_block_align,
37714036 }));
37724037 Section.Index.get(.strtab, elf).ni.slice(&elf.mf)[0] = 0;
37734038 switch (elf.shdrPtr(.symtab)) {
......@@ -3777,22 +4042,22 @@ fn initHeaders(
37774042 assert(.rodata == try elf.addSection(elf.ni.rodata, .{
37784043 .name = ".rodata",
37794044 .flags = .{ .ALLOC = true },
3780 .addralign = elf.mf.flags.block_size,
4045 .node_align = node_block_align,
37814046 }));
37824047 assert(.text == try elf.addSection(elf.ni.text, .{
37834048 .name = ".text",
37844049 .flags = .{ .ALLOC = true, .EXECINSTR = true },
3785 .addralign = elf.mf.flags.block_size,
4050 .node_align = node_block_align,
37864051 }));
37874052 assert(.data == try elf.addSection(elf.ni.data, .{
37884053 .name = ".data",
37894054 .flags = .{ .WRITE = true, .ALLOC = true },
3790 .addralign = elf.mf.flags.block_size,
4055 .node_align = node_block_align,
37914056 }));
37924057 assert(.data_rel_ro == try elf.addSection(elf.ni.data_rel_ro, .{
37934058 .name = ".data.rel.ro",
37944059 .flags = .{ .WRITE = true, .ALLOC = true },
3795 .addralign = elf.mf.flags.block_size,
4060 .node_align = node_block_align,
37964061 }));
37974062 if (@"type" != .REL) {
37984063 elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{
......@@ -3808,34 +4073,39 @@ fn initHeaders(
38084073 .addralign = addr_align,
38094074 .entsize = @intCast(addr_align.toByteUnits()),
38104075 });
3811 if (plt.got_plt) |got_plt| elf.shndx.got_plt = try elf.addSection(
3812 if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data,
3813 .{
4076 if (plt.got_plt) |got_plt| {
4077 const got_plt_segment_ni = if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data;
4078 elf.shndx.got_plt = try elf.addSection(got_plt_segment_ni, .{
38144079 .name = ".got.plt",
38154080 .type = .PROGBITS,
38164081 .flags = .{ .WRITE = true, .ALLOC = true },
38174082 .size = got_plt.header_entries * elf.targetPtrSize(),
38184083 .addralign = addr_align,
38194084 .entsize = @intCast(addr_align.toByteUnits()),
3820 },
3821 );
3822 elf.shndx.plt = try elf.addSection(elf.ni.text, .{
3823 .name = ".plt",
3824 .type = .PROGBITS,
3825 .flags = .{
3826 .ALLOC = true,
3827 .EXECINSTR = true,
3828 .WRITE = plt.got_plt == null,
3829 },
3830 .size = plt.entry_size * plt.header_entries,
3831 .addralign = plt.@"align",
3832 .node_align = elf.mf.flags.block_size,
3833 });
4085 });
4086 elf.shndx.plt = try elf.addSection(elf.ni.text, .{
4087 .name = ".plt",
4088 .type = .PROGBITS,
4089 .flags = .{ .ALLOC = true, .EXECINSTR = true },
4090 .size = plt.entry_size * plt.header_entries,
4091 .addralign = plt.@"align",
4092 .node_align = node_block_align,
4093 });
4094 } else {
4095 elf.shndx.plt = try elf.addSection(elf.phdrs.items[phndx.plt], .{
4096 .name = ".plt",
4097 .type = .PROGBITS,
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 }
38344104 if (plt.plt_sec != null) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{
38354105 .name = ".plt.sec",
38364106 .flags = .{ .ALLOC = true, .EXECINSTR = true },
38374107 .addralign = plt.@"align",
3838 .node_align = elf.mf.flags.block_size,
4108 .node_align = node_block_align,
38394109 });
38404110 if (maybe_interp) |interp| {
38414111 const interp_ni = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{
......@@ -3858,6 +4128,7 @@ fn initHeaders(
38584128 sec_interp[interp.len] = 0;
38594129 }
38604130 if (have_dynamic_section) {
4131 assert(elf.ni.data_rel_ro.alignment(&elf.mf).compare(.gte, addr_align));
38614132 const dynamic_ni = try elf.mf.addLastChildNode(gpa, elf.ni.data_rel_ro, .{
38624133 .alignment = addr_align,
38634134 .moved = true,
......@@ -3872,7 +4143,7 @@ fn initHeaders(
38724143 .flags = .{ .ALLOC = true },
38734144 .size = 1,
38744145 .entsize = 1,
3875 .node_align = elf.mf.flags.block_size,
4146 .node_align = node_block_align,
38764147 });
38774148 dynstr_shndx.get(elf).ni.slice(&elf.mf)[0] = 0;
38784149 elf.shndx.dynstr = dynstr_shndx;
......@@ -3890,7 +4161,7 @@ fn initHeaders(
38904161 .info = 1,
38914162 .addralign = addr_align,
38924163 .entsize = @sizeOf(Sym),
3893 .node_align = elf.mf.flags.block_size,
4164 .node_align = node_block_align,
38944165 });
38954166 const dynsym_null = @field(elf.dynsymPtr(0), @tagName(ct_class));
38964167 dynsym_null.* = .{
......@@ -3918,7 +4189,7 @@ fn initHeaders(
39184189 .link = elf.shndx.dynsym.toSection().?,
39194190 .addralign = addr_align,
39204191 .entsize = rela_size,
3921 .node_align = elf.mf.flags.block_size,
4192 .node_align = node_block_align,
39224193 });
39234194 elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{
39244195 .name = ".rela.plt",
......@@ -3928,7 +4199,7 @@ fn initHeaders(
39284199 .info = (if (plt.got_plt != null) elf.shndx.got_plt else elf.shndx.plt).toSection().?,
39294200 .addralign = addr_align,
39304201 .entsize = rela_size,
3931 .node_align = elf.mf.flags.block_size,
4202 .node_align = node_block_align,
39324203 });
39334204 elf.shndx.dynamic = try elf.addSection(dynamic_ni, .{
39344205 .name = ".dynamic",
......@@ -3938,6 +4209,32 @@ fn initHeaders(
39384209 .entsize = @intCast(addr_align.toByteUnits() * 2),
39394210 .node_align = addr_align,
39404211 });
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
39414238 switch (machine) {
39424239 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
39434240 .X86_64 => {
......@@ -4015,15 +4312,6 @@ fn initHeaders(
40154312 .SPARCV9 => {},
40164313 }
40174314 }
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
40284316 // Populate reserved GOT words.
40294317 switch (machine) {
......@@ -4199,7 +4487,7 @@ fn initHeaders(
41994487 if (comp.config.any_non_single_threaded) elf.shndx.tdata = try elf.addSection(elf.ni.tls, .{
42004488 .name = ".tdata",
42014489 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
4202 .addralign = elf.mf.flags.block_size,
4490 .node_align = node_block_align,
42034491 });
42044492
42054493 assert(elf.nodes.len == expected_nodes_len);
......@@ -4465,6 +4753,28 @@ fn ehdrType(elf: *const Elf) EhdrType {
44654753fn targetPtrSize(elf: *const Elf) u8 {
44664754 return elf.identClass().size();
44674755}
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}
44684778fn targetEndian(elf: *const Elf) std.lang.Endian {
44694779 const ident_data: std.elf.DATA = @fromBackingInt(elf.ni.elf.sliceConst(&elf.mf)[std.elf.EI.DATA]);
44704780 return ident_data.endian();
......@@ -4531,6 +4841,30 @@ const PltInfo = struct {
45314841fn targetPltInfo(elf: *const Elf) PltInfo {
45324842 return .fromMachine(elf.ehdrMachine());
45334843}
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}
45344868fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
45354869 const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer;
45364870 const Child = pointer_ty.child;
......@@ -4586,14 +4920,11 @@ const PhdrSlice = union(std.elf.CLASS) {
45864920};
45874921fn phdrSlice(elf: *Elf) PhdrSlice {
45884922 assert(elf.ehdrType() != .REL);
4589 const slice = elf.ni.phdr.slice(&elf.mf);
45904923 return switch (elf.identClass()) {
45914924 .NONE, _ => unreachable,
4592 inline else => |class| @unionInit(
4593 PhdrSlice,
4594 @tagName(class),
4595 @ptrCast(@alignCast(slice)),
4596 ),
4925 inline else => |class| @unionInit(PhdrSlice, @tagName(class), @ptrCast(@alignCast(
4926 elf.ni.phdr.slice(&elf.mf)[0 .. elf.phdrs.items.len * @sizeOf(class.ElfN().Phdr)],
4927 ))),
45974928 };
45984929}
45994930
......@@ -4607,7 +4938,9 @@ fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr {
46074938 switch (elf.identClass()) {
46084939 .NONE, _ => unreachable,
46094940 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 ));
46114944 const shdr_ptr = &shdr_slice[@backingInt(shndx)];
46124945 return @unionInit(ShdrPtr, @tagName(class), shdr_ptr);
46134946 },
......@@ -4662,7 +4995,6 @@ fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT {
46624995fn mapInputSection(elf: *Elf, opts: struct {
46634996 name: []const u8,
46644997 flags: std.elf.SHF,
4665 addralign: std.elf.Xword,
46664998 entsize: std.elf.Xword,
46674999}) (Error || error{
46685000 UnsupportedSectionFlags,
......@@ -4734,16 +5066,12 @@ fn mapInputSection(elf: *Elf, opts: struct {
47345066 flags.COMPRESSED = false;
47355067 break :flags flags;
47365068 },
4737 .node_align = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
4738 usize,
4739 @intCast(@max(opts.addralign, 1)),
4740 )),
47415069 .entsize = std.math.lossyCast(u32, opts.entsize),
47425070 });
47435071 };
4744 // Validate that the input is compatible with this section...
47455072 switch (elf.shdrPtr(existing_shndx)) {
47465073 inline else => |shdr| {
5074 // Validate that the input is compatible with this section
47475075 const cur_flags = elf.targetLoad(&shdr.flags).shf;
47485076 if (cur_flags.EXECINSTR != opts.flags.EXECINSTR or
47495077 cur_flags.WRITE != opts.flags.WRITE or
......@@ -4756,20 +5084,8 @@ fn mapInputSection(elf: *Elf, opts: struct {
47565084 .NULL, .PROGBITS => {},
47575085 else => return error.SectionTypeConflict,
47585086 }
4759 },
4760 }
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;
5087
5088 // All okay, combine the section flags
47735089 elf.targetStore(&shdr.flags, .{ .shf = .{
47745090 .EXECINSTR = cur_flags.EXECINSTR,
47755091 .WRITE = cur_flags.WRITE,
......@@ -4778,11 +5094,6 @@ fn mapInputSection(elf: *Elf, opts: struct {
47785094 .STRINGS = cur_flags.STRINGS and opts.flags.STRINGS,
47795095 .MERGE = cur_flags.MERGE and opts.flags.MERGE,
47805096 } });
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 }
47865097 },
47875098 }
47885099 return existing_shndx;
......@@ -4810,7 +5121,6 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
48105121 .TLS = elf.base.comp.config.any_non_single_threaded and
48115122 nav.resolved.?.@"threadlocal",
48125123 },
4813 .addralign = 1,
48145124 .entsize = 0,
48155125 })) |shndx| {
48165126 break :section shndx;
......@@ -4856,6 +5166,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
48565166 else => |a| a,
48575167 },
48585168 };
5169 try shndx.ensureAligned(elf, alignment.toStdMem());
48595170 const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{
48605171 .alignment = alignment.toStdMem(),
48615172 });
......@@ -4899,6 +5210,7 @@ fn uavMapIndex(
48995210 const umi: Node.UavMapIndex = @fromBackingInt(@intCast(uav_gop.index));
49005211 if (!uav_gop.found_existing) {
49015212 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());
49025214 const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{
49035215 .moved = true, // see assert at end of `genUav`
49045216 .alignment = resolved_align.toStdMem(),
......@@ -4925,6 +5237,8 @@ fn uavMapIndex(
49255237 elf.pending_uavs.appendAssumeCapacity(umi);
49265238 } else {
49275239 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());
49285242 if (resolved_align.toStdMem().order(node.alignment(&elf.mf)).compare(.gt)) {
49295243 try node.realign(&elf.mf, gpa, resolved_align.toStdMem(), .{});
49305244 }
......@@ -5225,7 +5539,6 @@ fn loadObject(
52255539 const shndx = elf.mapInputSection(.{
52265540 .name = name,
52275541 .flags = section.shdr.flags.shf,
5228 .addralign = section.shdr.addralign,
52295542 .entsize = section.shdr.entsize,
52305543 }) catch |err| switch (err) {
52315544 error.StripSection => continue,
......@@ -5313,7 +5626,7 @@ fn loadObject(
53135626 const old_size = elf.targetLoad(&shdr.size);
53145627 const new_size = old_size + section.shdr.size;
53155628 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);
53175630 },
53185631 }
53195632 break :shndx shndx.*;
......@@ -5324,12 +5637,13 @@ fn loadObject(
53245637 .node_fixed = true,
53255638 },
53265639 };
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);
53275644 const ni = try elf.mf.addLastChildNode(gpa, opts.shndx.get(elf).ni, .{
53285645 .size = section.shdr.size,
5329 .alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
5330 usize,
5331 @intCast(@max(section.shdr.addralign, 1)),
5332 )),
5646 .alignment = need_align,
53335647 .moved = true, // see assert at end of `flushInputSection`
53345648 .fixed = opts.node_fixed,
53355649 });
......@@ -5699,6 +6013,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars
56996013 if (elf.copied_globals.get(name)) |copied_global| {
57006014 // We have a copy relocation for this global, but the amount of space we
57016015 // reserved for it could be too small or underaligned!
6016 try Section.Index.data.ensureAligned(elf, gop.value_ptr.alignment);
57026017 try copied_global.node.resize(&elf.mf, gpa, gop.value_ptr.size);
57036018 try copied_global.node.realign(&elf.mf, gpa, gop.value_ptr.alignment, .{});
57046019 const global_ptr = elf.globalByName(name).?;
......@@ -5878,19 +6193,7 @@ fn updateInitFiniArraySectionSize(
58786193 elf: *Elf,
58796194 shndx: Section.Index,
58806195 comptime name: []const u8,
5881 @"type": std.elf.SHT,
5882 new_size: u64,
58836196) 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
58946197 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {
58956198 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),
58966199 };
......@@ -5955,7 +6258,7 @@ fn prepareDynamic(elf: *Elf) Error!void {
59556258 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +
59566259 @as(usize, @intFromBool(use_plt)) * 4 +
59576260 @intFromBool(comp.config.output_mode == .Exe) +
5958 @intFromBool(elf.textrel_count > 0) + 8;
6261 @intFromBool(elf.textrel_count > 0) + 9;
59596262
59606263 const dynamic_size = dynamic_len * 2 * elf.targetPtrSize();
59616264
......@@ -6055,7 +6358,7 @@ fn flushDynamic(elf: *Elf) void {
60556358 dynamic_index += 4;
60566359 }
60576360
6058 dynamic_entries[dynamic_index..][0..8].* = .{
6361 dynamic_entries[dynamic_index..][0..9].* = .{
60596362 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },
60606363 .{ std.elf.DT_RELASZ, @intCast(elf.shndx.rela_dyn.size(elf)) },
60616364 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
......@@ -6063,9 +6366,10 @@ fn flushDynamic(elf: *Elf) void {
60636366 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },
60646367 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },
60656368 .{ std.elf.DT_STRSZ, @intCast(elf.shndx.dynstr.size(elf)) },
6369 .{ std.elf.DT_HASH, @intCast(elf.shndx.hash.vaddr(elf)) },
60666370 .{ std.elf.DT_NULL, 0 },
60676371 };
6068 dynamic_index += 8;
6372 dynamic_index += 9;
60696373
60706374 assert(dynamic_index == dynamic_entries.len);
60716375 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 {
60926396 else => {},
60936397 }
60946398 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);
60966401 }
60976402 const gpa = elf.base.comp.gpa;
60986403 try elf.nodes.ensureUnusedCapacity(gpa, 1);
......@@ -6427,7 +6732,7 @@ fn addRelocAssumeCapacity(
64276732
64286733 .WDISP30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" })),
64296734 .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" })),
64316736 .H44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:0]", .cast = .unsigned, .shift = .@"22" })),
64326737 .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(
64576762
64586763 // The following relocations are all represented by the ABI as writing to a 13 bit
64596764 // 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 model
6461 // the relocation as only writing to a smaller 10--12 bit field.
6462 // TODO: because we flush input sections lazily, we can't actually write these bits
6463 // immediately---we'll instead have to queue the writes somehow.
6765 // for applying relocations, we split this action up: we create a relocation writing
6766 // to the 10--12 bit long field which is actually variable, and queue a one-shot
6767 // task to set the constant bits. We can't just write the bits now unfortunately
6768 // because they may be in an input section which has not yet been loaded.
64646769 .PC10 => {
6465 // TODO: 32[12:10] = 0b000
6770 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
64666771 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
64676772 },
64686773 .L44 => {
6469 // TODO: 32[12:12] = 0b0
6774 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:12] = 0b0" });
64706775 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[11:0]", .cast = .trunc, .shift = .@"0" }));
64716776 },
64726777 .TLS_LDO_LOX10 => {
6473 // TODO: 32[12:10] = 0b000
6778 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
64746779 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
64756780 },
64766781 .TLS_LE_LOX10 => {
6477 // TODO: 32[12:10] = 0b111
6782 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b111" });
64786783 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
64796784 },
64806785 .GOT10 => {
6481 // TODO: 32[12:10] = 0b000
6786 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
64826787 elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
64836788 },
64846789 .TLS_GD_LO10 => {
6485 // TODO: 32[12:10] = 0b000
6790 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
64866791 elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
64876792 },
64886793 .TLS_LDM_LO10 => {
6489 // TODO: 32[12:10] = 0b000
6794 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
64906795 elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
64916796 },
64926797 },
......@@ -6887,7 +7192,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
68877192 const entry_ptr: *class.ElfN().Addr = @ptrCast(@alignCast(
68887193 elf.shndx.got.get(elf).ni.slice(&elf.mf)[offset..][0..addr_size],
68897194 ));
6890 entry_ptr.* = switch (entry_value) {
7195 elf.targetStore(entry_ptr, switch (entry_value) {
68917196 .unsigned => |x| @intCast(x),
68927197 .signed => |x| switch (class) {
68937198 .NONE, _ => comptime unreachable,
......@@ -6895,7 +7200,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
68957200 .@"64" => @bitCast(x),
68967201 },
68977202 .reloc => 0,
6898 };
7203 });
68997204 break :got_entry_addr elf.targetLoad(&got_shdr.addr) + offset;
69007205 },
69017206 };
......@@ -6950,6 +7255,9 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye
69507255fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
69517256 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
69537261 const gpa = elf.base.comp.gpa;
69547262
69557263 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 {
69577265
69587266 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
69647268 const dso_global = elf.dso_globals.get(global_name) orelse {
69657269 // We do not have a definition to provide the correct size for the symbol. If a definition
69667270 // 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 {
69747278 if (gop.found_existing) return true;
69757279 errdefer assert(elf.copied_globals.pop().?.key == global_name);
69767280
7281 try Section.Index.data.ensureAligned(elf, dso_global.alignment);
7282
69777283 try elf.nodes.ensureUnusedCapacity(gpa, 1);
69787284 const node = try elf.mf.addLastChildNode(gpa, Section.Index.data.get(elf).ni, .{
69797285 .size = dso_global.size,
......@@ -7234,6 +7540,25 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
72347540 };
72357541 break :task;
72367542 }
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 }
72377562 if (elf.changed_symtab_index.pop()) |kv| {
72387563 const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);
72397564 defer sub_prog_node.end();
......@@ -7324,6 +7649,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
73247649 }
73257650 }
73267651 if (elf.input_sections.items.len > elf.input_section_pending_index) return true;
7652 if (elf.one_shot_fixups.items.len > 0) return true;
73277653 if (elf.changed_symtab_index.count() > 0) return true;
73287654 if (elf.mf.updates.items.len > 0) return true;
73297655 return false;
......@@ -7586,17 +7912,22 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
75867912 const ph = &phdr[phndx];
75877913 switch (elf.targetLoad(&ph.type)) {
75887914 else => unreachable,
7589 .NULL, .LOAD => return,
7915
7916 .NULL, .LOAD => {
7917 try elf.allocateSegmentLoadAddress(phndx);
7918 },
75907919
75917920 .DYNAMIC,
75927921 .INTERP,
75937922 .PHDR,
75947923 .TLS,
75957924 .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 },
75977930 }
7598 elf.targetStore(&ph.vaddr, @intCast(elf.computeNodeVAddr(ni)));
7599 ph.paddr = ph.vaddr;
76007931 },
76017932 }
76027933 },
......@@ -7648,8 +7979,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
76487979 elf.flushMovedPltSection(.got_plt, old_addr, addr);
76497980 } else if (shndx == elf.shndx.plt_sec) {
76507981 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);
76537982 }
76547983 },
76557984 .input_member => {},
......@@ -7739,6 +8068,114 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
77398068 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
77408069}
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
77428179fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {
77438180 const trace = tracy.trace(@src());
77448181 defer trace.end();
......@@ -7766,68 +8203,40 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
77668203 assert(elf.phdrs.items[phndx] == ni);
77678204 const ph = &phdr[phndx];
77688205 elf.targetStore(&ph.filesz, @intCast(size));
7769 if (size > elf.targetLoad(&ph.memsz)) {
7770 switch (elf.targetLoad(&ph.type)) {
7771 else => unreachable,
7772 .NULL => if (size > 0) elf.targetStore(&ph.type, .LOAD),
7773 .LOAD => if (size == 0) elf.targetStore(&ph.type, .NULL),
7774 .DYNAMIC, .INTERP, .PHDR, std.elf.PT.GNU_RELRO => {
7775 elf.targetStore(&ph.memsz, @intCast(size));
7776 return;
7777 },
7778 .TLS => {
7779 elf.targetStore(&ph.memsz, @intCast(size));
7780 // TPOFF relocations care about the size of the TLS segment. Re-apply
7781 // those, and also update any GOT entries from GOTTPOFF relocations.
7782 for (elf.tls_size_symbol_relocs.keys()) |reloc| {
7783 reloc.get(elf).apply(elf);
7784 }
7785 for (elf.got.keys(), 0..) |got_key, got_index| {
7786 switch (got_key) {
7787 .reserved,
7788 .symbol,
7789 .tlsld0,
7790 .tlsld1,
7791 .tlsgd0,
7792 .tlsgd1,
7793 => {
7794 @branchHint(.likely);
7795 continue;
7796 },
8206 switch (elf.targetLoad(&ph.type)) {
8207 else => unreachable,
8208 .NULL, .LOAD => {
8209 elf.targetStore(&ph.type, if (size > 0) .LOAD else .NULL);
8210 try elf.allocateSegmentLoadAddress(phndx);
8211 },
8212 .DYNAMIC, .INTERP, .PHDR, std.elf.PT.GNU_RELRO => {
8213 elf.targetStore(&ph.memsz, @intCast(size));
8214 },
8215 .TLS => {
8216 elf.targetStore(&ph.memsz, @intCast(size));
8217 // TPOFF relocations care about the size of the TLS segment. Re-apply
8218 // those, and also update any GOT entries from GOTTPOFF relocations.
8219 for (elf.tls_size_symbol_relocs.keys()) |reloc| {
8220 reloc.get(elf).apply(elf);
8221 }
8222 for (elf.got.keys(), 0..) |got_key, got_index| {
8223 switch (got_key) {
8224 .reserved,
8225 .symbol,
8226 .tlsld0,
8227 .tlsld1,
8228 .tlsgd0,
8229 .tlsgd1,
8230 => {
8231 @branchHint(.likely);
8232 continue;
8233 },
77978234
7798 .tpoff => elf.updateGotEntry(got_index),
7799 }
8235 .tpoff => elf.updateGotEntry(got_index),
78008236 }
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,
78138237 }
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 };
78298238 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
7830 }
8239 },
78318240 }
78328241 },
78338242 },
......@@ -7848,6 +8257,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
78488257 .REL,
78498258 .RELA,
78508259 .DYNSYM,
8260 .HASH,
78518261 => return,
78528262 }
78538263 if (shndx != elf.shndx.plt and
......@@ -7940,21 +8350,6 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!
79408350 }
79418351}
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}
79588353fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void {
79598354 const target_endian = elf.targetEndian();
79608355
......@@ -8126,9 +8521,9 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
81268521 const plt_slice: []Inst = @ptrCast(@alignCast(plt_ni.slice(&elf.mf)[@intCast(got_plt_offset)..][0..32]));
81278522 @memcpy(plt_slice, &[8]Inst{
81288523 // sethi (. - .plt[0]), %g1
8129 .{ .imm22 = .{ .imm = @truncate(got_plt_offset), .op = 0b0000000011 } },
8524 .{ .imm22 = .{ .imm = @truncate(got_plt_offset), .op = 0b0000001100 } },
81308525 // 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 } },
81328527 // nop
81338528 .{ .raw = 0x0100_0000 },
81348529 // nop
......@@ -8142,6 +8537,9 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
81428537 // nop
81438538 .{ .raw = 0x0100_0000 },
81448539 });
8540 if (elf.targetEndian() != std.lang.Endian.native) {
8541 std.mem.byteSwapAllElements(Inst, plt_slice);
8542 }
81458543 },
81468544 }
81478545 },
......@@ -8212,6 +8610,13 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
82128610 .LOONGARCH => {
82138611 switch (which) {
82148612 .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 }
82158620 // We also need to update all of the references from `.plt` to `.got.plt`.
82168621 // However, if there's also a flush pending for `.got.plt`, don't bother doing
82178622 // 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
82728677 },
82738678 .SPARCV9 => switch (which) {
82748679 .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 }
82758687 // Update the offsets of the relocation entries in `.rela.plt`.
82768688 const rela_plt_shndx = elf.shndx.rela_plt;
82778689 for (0..elf.plt.count()) |plt_index| {
......@@ -8315,31 +8727,31 @@ fn updateExportInner(
83158727 }),
83168728 }
83178729 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);
8318 const exported_lsi: Symbol.LocalIndex, const @"type": std.elf.STT = switch (@"export".exported) {
8319 .nav => |nav| .{
8320 (try elf.navMapIndex(zcu, nav)).symbol(elf),
8321 elf.navType(ip.getNav(nav).resolved.?),
8322 },
8323 .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT },
8730 const exported_lsi: Symbol.LocalIndex = switch (@"export".exported) {
8731 .nav => |nav| (try elf.navMapIndex(zcu, nav)).symbol(elf),
8732 .uav => |uav| (try elf.uavMapIndex(uav, .none)).symbol(elf),
83248733 };
83258734
83268735 try elf.ensureElfNodeSize();
8327 while (try elf.idle(pt.tid)) {}
83288736
8329 const value: u64 = Symbol.Id.local(exported_lsi).value(elf);
8330 const size: u64, const shndx: Section.Index = switch (elf.symPtr(exported_lsi.index())) {
8737 // Initialize the global symbol with the same values that the local one currently has. If the
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())) {
83318741 inline else => |exported_sym| .{
8742 elf.targetLoad(&exported_sym.value),
83328743 elf.targetLoad(&exported_sym.size),
8744 elf.targetLoad(&exported_sym.info).type,
83338745 .fromSection(elf.targetLoad(&exported_sym.shndx)),
83348746 },
83358747 };
83368748
83378749 const name = @"export".opts.name.toSlice(ip);
83388750 _ = elf.addGlobalSymbolAssumeCapacity(.{
8339 .node = .none,
8751 .node = exported_lsi.index().ptr(elf).node,
83408752 .name = try .string(elf, name),
8341 .value = value,
8342 .size = @intCast(size),
8753 .value = cur_value,
8754 .size = cur_size,
83438755 .type = @"type",
83448756 .bind = switch (@"export".opts.linkage) {
83458757 .strong => .strong,
......@@ -8501,6 +8913,46 @@ pub fn printNode(
85018913 }
85028914}
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
85048956/// Must be called deterministically after any call to `MappedFile.Node.Index.resize`
85058957/// (of `elf.ni.elf` or one of its children) before any possible calls to `idle`.
85068958fn ensureElfNodeSize(elf: *Elf) MappedFile.Error!void {
src/link/MappedFile.zig+19-14
......@@ -856,19 +856,20 @@ fn resizeNode(
856856 ni.setLocationAssumeCapacity(mf, old_offset, new_size);
857857 return;
858858 }
859 if (is_linux and !mf.flags.fallocate_insert_range_unsupported and
860 node.flags.alignment.order(mf.flags.block_size).compare(.gte))
861859 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.
862867 const range_file_offset = ni.fileLocation(mf, false).offset + old_size;
863868 const range_size = node.flags.alignment.forward(
864869 @intCast(requested_size +| requested_size / growth_factor),
865870 ) - old_size;
866
867 // If this node is being realigned, its current state might not
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;
871 if (!mf.flags.block_size.check(@intCast(range_file_offset))) break :insert_range;
872 if (!mf.flags.block_size.check(@intCast(range_size))) break :insert_range;
872873
873874 mf.memory_map.write(io) catch |err| switch (err) {
874875 error.WouldBlock => return error.Unexpected, // file was not opened as non-blocking
......@@ -1022,9 +1023,10 @@ fn resizeNode(
10221023 }
10231024 try mf.ensureCapacityForSetLocation(gpa);
10241025 if (parent.last != first_floating_ni) {
1025 first_floating.prev = parent.last;
1026 const old_last = parent.last;
1027 first_floating.prev = old_last;
10261028 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);
10281030 try last_fixed_ni.setNext(gpa, first_floating.next, mf);
10291031 switch (first_floating.next) {
10301032 .none => {},
......@@ -1208,9 +1210,11 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size:
12081210 // make a copy of this node at the new location
12091211 try mf.copyRange(old_file_offset, new_file_offset, size);
12101212 // delete the copy of this node at the old location
1211 if (is_linux and !mf.flags.fallocate_punch_hole_unsupported and
1212 size >= mf.flags.block_size.toByteUnits() * 2 - 1) while (true)
1213 switch (linux.errno(linux.fallocate(
1213 if (is_linux and
1214 !mf.flags.fallocate_punch_hole_unsupported and
1215 size >= mf.flags.block_size.toByteUnits() * 2 - 1)
1216 {
1217 while (true) switch (linux.errno(linux.fallocate(
12141218 mf.memory_map.file.handle,
12151219 linux.FALLOC.FL_PUNCH_HOLE | linux.FALLOC.FL_KEEP_SIZE,
12161220 @intCast(old_file_offset),
......@@ -1224,13 +1228,14 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size:
12241228 .NOSPC => return error.NoSpaceLeft,
12251229 .NOSYS, .OPNOTSUPP => {
12261230 mf.flags.fallocate_punch_hole_unsupported = true;
1227 break;
1231 break; // fall back to slow path
12281232 },
12291233 .PERM => return error.PermissionDenied,
12301234 .SPIPE => return error.Unseekable,
12311235 .TXTBSY => return error.FileBusy,
12321236 else => |e| return std.posix.unexpectedErrno(e),
12331237 };
1238 }
12341239 @memset(mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)], 0);
12351240}
12361241
test/tests.zig+36-2
......@@ -26,6 +26,7 @@ const ModuleTestTarget = struct {
2626 single_threaded: ?bool = null,
2727 use_llvm: ?bool = null,
2828 use_lld: ?bool = null,
29 new_linker: ?bool = null,
2930 pic: ?bool = null,
3031 strip: ?bool = null,
3132 function_sections: ?bool = null,
......@@ -1288,6 +1289,34 @@ const module_test_targets = blk: {
12881289 },
12891290 .link_libc = true,
12901291 },
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
12921321 // Darwin Targets
12931322
......@@ -2821,6 +2850,7 @@ fn addOneModuleTest(
28212850 .zig_lib_dir = b.path("lib"),
28222851 });
28232852 these_tests.linkage = test_target.linkage;
2853 these_tests.use_new_linker = test_target.new_linker;
28242854 // https://codeberg.org/ziglang/zig/issues/31701
28252855 if (!(mem.eql(u8, options.name, "compiler-rt") or mem.eql(u8, options.name, "libc"))) {
28262856 if (options.no_builtin) these_tests.root_module.no_builtin = true;
......@@ -2847,7 +2877,11 @@ fn addOneModuleTest(
28472877 "-selfhosted"
28482878 else
28492879 "";
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 };
28512885 const linkage_name = if (test_target.linkage) |linkage| switch (linkage) {
28522886 inline else => |t| "-" ++ @tagName(t),
28532887 } else "";
......@@ -2863,7 +2897,7 @@ fn addOneModuleTest(
28632897 libc_suffix,
28642898 single_threaded_suffix,
28652899 backend_suffix,
2866 use_lld,
2900 linker_suffix,
28672901 linkage_name,
28682902 use_pic,
28692903 });