| ... | ... | @@ -36,6 +36,7 @@ shndx: struct { |
| 36 | 36 | dynsym: Section.Index, |
| 37 | 37 | dynstr: Section.Index, |
| 38 | 38 | dynamic: Section.Index, |
| 39 | hash: Section.Index, |
| 39 | 40 | tdata: Section.Index, |
| 40 | 41 | rela_dyn: Section.Index, |
| 41 | 42 | rela_plt: Section.Index, |
| ... | ... | @@ -119,8 +120,6 @@ got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional), |
| 119 | 120 | plt: std.array_hash_map.Auto(String(.strtab), void), |
| 120 | 121 | /// The `.plt` section contains zero or more symbol relocations starting at this index. |
| 121 | 122 | plt_first_symbol_reloc: SymbolReloc.Index, |
| 122 | | /// The `.dynamic` section contains zero or more symbol relocations starting at this index. |
| 123 | | dynamic_first_symbol_reloc: SymbolReloc.Index, |
| 124 | 123 | |
| 125 | 124 | needed: std.array_hash_map.Auto(String(.dynstr), void), |
| 126 | 125 | inputs: std.ArrayList(struct { |
| ... | ... | @@ -136,6 +135,18 @@ inputs: std.ArrayList(struct { |
| 136 | 135 | input_pending_index: u32, |
| 137 | 136 | input_sections: std.ArrayList(InputSection), |
| 138 | 137 | input_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`. |
| 140 | one_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 | }), |
| 139 | 150 | navs: std.array_hash_map.Auto(InternPool.Nav.Index, struct { |
| 140 | 151 | lsi: Symbol.LocalIndex, |
| 141 | 152 | /// The start index of the contiguous sequence of symbol relocations in this NAV. |
| ... | ... | @@ -195,8 +206,6 @@ const Node = union(enum) { |
| 195 | 206 | shdr, |
| 196 | 207 | segment: u32, |
| 197 | 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 | 209 | section: Section.Index, |
| 201 | 210 | /// Only valid for static libraries, represents one non-zcu archive member. |
| 202 | 211 | input_member: InputIndex, |
| ... | ... | @@ -530,6 +539,26 @@ const Section = struct { |
| 530 | 539 | } |
| 531 | 540 | } |
| 532 | 541 | |
| 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 | 562 | /// Asserts that `rela_shndx` is a `SHT_RELA` section and ensures that its node has enough |
| 534 | 563 | /// unused space to hold `n` additional `ElfN.Rela` entries. |
| 535 | 564 | fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) Error!void { |
| ... | ... | @@ -634,11 +663,6 @@ const Section = struct { |
| 634 | 663 | const old_size = elf.targetLoad(&shdr.size); |
| 635 | 664 | const new_size = old_size + ent_size; |
| 636 | 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 | 666 | break :new_index @fromBackingInt(@intCast(@divExact(old_size, ent_size))); |
| 643 | 667 | }; |
| 644 | 668 | const relas: []class.ElfN().Rela = @ptrCast(@alignCast( |
| ... | ... | @@ -1378,7 +1402,7 @@ const SymbolReloc = struct { |
| 1378 | 1402 | const shift: u6, const shift_exact: bool = switch (s.shift) { |
| 1379 | 1403 | .@"0" => .{ 0, false }, |
| 1380 | 1404 | .@"2_exact" => .{ 2, true }, |
| 1381 | | .@"10" => .{ 10, true }, |
| 1405 | .@"10" => .{ 10, false }, |
| 1382 | 1406 | .@"12" => .{ 12, false }, |
| 1383 | 1407 | .@"22" => .{ 22, false }, |
| 1384 | 1408 | .@"32" => .{ 32, false }, |
| ... | ... | @@ -1760,6 +1784,170 @@ const SymbolReloc = struct { |
| 1760 | 1784 | } |
| 1761 | 1785 | }; |
| 1762 | 1786 | |
| 1787 | fn 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 | |
| 1853 | fn 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 | } |
| 1869 | fn 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 | } |
| 1894 | fn 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 | } |
| 1910 | fn 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 | |
| 1763 | 1951 | fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) Error!void { |
| 1764 | 1952 | const gpa = elf.base.comp.gpa; |
| 1765 | 1953 | |
| ... | ... | @@ -1789,12 +1977,19 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe |
| 1789 | 1977 | try elf.node_global_symbols.ensureUnusedCapacity(gpa, len); |
| 1790 | 1978 | |
| 1791 | 1979 | 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 | }, |
| 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 | 1989 | try elf.ensureNodeSize(elf.shndx.dynsym.get(elf).ni, dynsym_need_size); |
| 1797 | 1990 | |
| 1991 | try elf.ensureDynsymHashCapacity(dynsym_cur_len + len); |
| 1992 | |
| 1798 | 1993 | try elf.ensureUnusedPltCapacity(len); |
| 1799 | 1994 | } |
| 1800 | 1995 | }, |
| ... | ... | @@ -2131,6 +2326,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{ |
| 2131 | 2326 | if (elf.targetEndian() != native_endian) { |
| 2132 | 2327 | std.mem.byteSwapAllFields(Sym, sym); |
| 2133 | 2328 | } |
| 2329 | elf.appendDynsymHashEntry(dynsym_index); |
| 2134 | 2330 | break :dynsym_index dynsym_index; |
| 2135 | 2331 | }, |
| 2136 | 2332 | } |
| ... | ... | @@ -2279,8 +2475,9 @@ fn setGlobalSymbolValue( |
| 2279 | 2475 | } |
| 2280 | 2476 | |
| 2281 | 2477 | // 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) { |
| 2284 | 2481 | Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf); |
| 2285 | 2482 | } |
| 2286 | 2483 | |
| ... | ... | @@ -2404,6 +2601,8 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void { |
| 2404 | 2601 | const new_size = old_size - ent_size; |
| 2405 | 2602 | const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size)); |
| 2406 | 2603 | |
| 2604 | elf.popDynsymHashEntry(remove_dynsym_index); |
| 2605 | |
| 2407 | 2606 | const free_dynsym_index = global_ptr.dynsym_index; |
| 2408 | 2607 | global_ptr.dynsym_index = 0; |
| 2409 | 2608 | |
| ... | ... | @@ -2411,6 +2610,8 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void { |
| 2411 | 2610 | // The demoted global wasn't the last entry, so move whatever entry we just |
| 2412 | 2611 | // truncated out of dynsym into its place. |
| 2413 | 2612 | |
| 2613 | elf.clearDynsymHashEntry(free_dynsym_index); |
| 2614 | |
| 2414 | 2615 | const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class)); |
| 2415 | 2616 | const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class)); |
| 2416 | 2617 | |
| ... | ... | @@ -2423,6 +2624,8 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void { |
| 2423 | 2624 | assert(moved_global_ptr.dynsym_index == remove_dynsym_index); |
| 2424 | 2625 | moved_global_ptr.dynsym_index = free_dynsym_index; |
| 2425 | 2626 | |
| 2627 | elf.populateDynsymHashEntry(free_dynsym_index); |
| 2628 | |
| 2426 | 2629 | // Since that symbol's dynsym index has changed, we'll have to update any |
| 2427 | 2630 | // relocation entries targeting it. |
| 2428 | 2631 | elf.changed_symtab_index.putAssumeCapacity(moved_name, {}); |
| ... | ... | @@ -3045,9 +3248,6 @@ const StringTable = struct { |
| 3045 | 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 | 3251 | try elf.ensureNodeSize(ni, new_size); |
| 3052 | 3252 | const slice = ni.slice(&elf.mf)[old_size..]; |
| 3053 | 3253 | @memcpy(slice[0..key.len], key); |
| ... | ... | @@ -3172,6 +3372,7 @@ fn create( |
| 3172 | 3372 | .dynsym = .UNDEF, |
| 3173 | 3373 | .dynstr = .UNDEF, |
| 3174 | 3374 | .dynamic = .UNDEF, |
| 3375 | .hash = .UNDEF, |
| 3175 | 3376 | .tdata = .UNDEF, |
| 3176 | 3377 | .rela_dyn = .UNDEF, |
| 3177 | 3378 | .rela_plt = .UNDEF, |
| ... | ... | @@ -3202,12 +3403,12 @@ fn create( |
| 3202 | 3403 | .got = .empty, |
| 3203 | 3404 | .plt = .empty, |
| 3204 | 3405 | .plt_first_symbol_reloc = .none, |
| 3205 | | .dynamic_first_symbol_reloc = .none, |
| 3206 | 3406 | .needed = .empty, |
| 3207 | 3407 | .inputs = .empty, |
| 3208 | 3408 | .input_pending_index = 0, |
| 3209 | 3409 | .input_sections = .empty, |
| 3210 | 3410 | .input_section_pending_index = 0, |
| 3411 | .one_shot_fixups = .empty, |
| 3211 | 3412 | .navs = .empty, |
| 3212 | 3413 | .uavs = .empty, |
| 3213 | 3414 | .lazy = comptime .initFill(.{ |
| ... | ... | @@ -3257,6 +3458,7 @@ pub fn deinit(elf: *Elf) void { |
| 3257 | 3458 | for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m); |
| 3258 | 3459 | elf.inputs.deinit(gpa); |
| 3259 | 3460 | elf.input_sections.deinit(gpa); |
| 3461 | elf.one_shot_fixups.deinit(gpa); |
| 3260 | 3462 | elf.navs.deinit(gpa); |
| 3261 | 3463 | elf.uavs.deinit(gpa); |
| 3262 | 3464 | for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa); |
| ... | ... | @@ -3293,6 +3495,16 @@ fn initHeaders( |
| 3293 | 3495 | .@"64" => .@"8", |
| 3294 | 3496 | }; |
| 3295 | 3497 | |
| 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 | 3508 | const plt: PltInfo = .fromMachine(machine); |
| 3297 | 3509 | |
| 3298 | 3510 | const shnum: u32 = shnum: { |
| ... | ... | @@ -3310,6 +3522,7 @@ fn initHeaders( |
| 3310 | 3522 | shnum += 1; // .dynamic |
| 3311 | 3523 | shnum += 1; // .dynstr |
| 3312 | 3524 | shnum += 1; // .dynsym |
| 3525 | shnum += 1; // .hash |
| 3313 | 3526 | shnum += 1; // .rela.dyn |
| 3314 | 3527 | shnum += 1; // .rela.plt |
| 3315 | 3528 | } |
| ... | ... | @@ -3328,6 +3541,10 @@ fn initHeaders( |
| 3328 | 3541 | rodata: u32, |
| 3329 | 3542 | text: u32, |
| 3330 | 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 | 3548 | tls: u32, |
| 3332 | 3549 | dynamic: u32, |
| 3333 | 3550 | relro: u32, |
| ... | ... | @@ -3359,6 +3576,10 @@ fn initHeaders( |
| 3359 | 3576 | defer phnum += 1; |
| 3360 | 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 | 3583 | .tls = if (comp.config.any_non_single_threaded) phndx: { |
| 3363 | 3584 | defer phnum += 1; |
| 3364 | 3585 | break :phndx phnum; |
| ... | ... | @@ -3414,7 +3635,7 @@ fn initHeaders( |
| 3414 | 3635 | |
| 3415 | 3636 | elf.nodes.appendAssumeCapacity(.archive_header); |
| 3416 | 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 | 3639 | .next_moved = true, |
| 3419 | 3640 | .bubbles_moved = false, |
| 3420 | 3641 | .enable_next_moved = true, |
| ... | ... | @@ -3424,9 +3645,98 @@ fn initHeaders( |
| 3424 | 3645 | |
| 3425 | 3646 | const entsize: struct { ph: u32, sh: u32 } = switch (class) { |
| 3426 | 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 | 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 | 3740 | .size = @sizeOf(ElfN.Ehdr), |
| 3431 | 3741 | .alignment = addr_align, |
| 3432 | 3742 | .fixed = true, |
| ... | ... | @@ -3478,106 +3788,58 @@ fn initHeaders( |
| 3478 | 3788 | ehdr.shnum = 1; // Only the null shdr initially---will be incremented by `addSection` |
| 3479 | 3789 | ehdr.shstrndx = std.elf.SHN_UNDEF; |
| 3480 | 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 | } |
| 3485 | 3793 | |
| 3486 | 3794 | elf.ni.shdr = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{ |
| 3487 | 3795 | .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 | 3797 | .moved = true, |
| 3490 | 3798 | .resized = true, |
| 3491 | 3799 | }); |
| 3492 | 3800 | elf.nodes.appendAssumeCapacity(.shdr); |
| 3493 | 3801 | |
| 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 | 3802 | switch (class) { |
| 3574 | 3803 | .NONE, _ => unreachable, |
| 3575 | 3804 | inline else => |ct_class| { |
| 3576 | 3805 | const ElfN = ct_class.ElfN(); |
| 3577 | 3806 | const target_endian = elf.targetEndian(); |
| 3578 | 3807 | |
| 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 | |
| 3581 | 3843 | const ph_phdr = &phdr[phndx.phdr]; |
| 3582 | 3844 | ph_phdr.* = .{ |
| 3583 | 3845 | .type = .PHDR, |
| ... | ... | @@ -3589,7 +3851,6 @@ fn initHeaders( |
| 3589 | 3851 | .flags = .{ .R = true }, |
| 3590 | 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); |
| 3593 | 3854 | |
| 3594 | 3855 | if (maybe_interp) |_| { |
| 3595 | 3856 | const ph_interp = &phdr[phndx.interp]; |
| ... | ... | @@ -3603,53 +3864,57 @@ fn initHeaders( |
| 3603 | 3864 | .flags = .{ .R = true }, |
| 3604 | 3865 | .@"align" = 1, |
| 3605 | 3866 | }; |
| 3606 | | if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_interp); |
| 3607 | 3867 | } |
| 3608 | 3868 | |
| 3609 | | _, const rodata_size = elf.ni.rodata.location(&elf.mf).resolve(&elf.mf); |
| 3610 | 3869 | const ph_rodata = &phdr[phndx.rodata]; |
| 3611 | 3870 | ph_rodata.* = .{ |
| 3612 | | .type = if (rodata_size == 0) .NULL else .LOAD, |
| 3871 | .type = .NULL, |
| 3613 | 3872 | .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, |
| 3618 | 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); |
| 3623 | 3880 | |
| 3624 | | _, const text_size = elf.ni.text.location(&elf.mf).resolve(&elf.mf); |
| 3625 | 3881 | const ph_text = &phdr[phndx.text]; |
| 3626 | 3882 | ph_text.* = .{ |
| 3627 | | .type = if (text_size == 0) .NULL else .LOAD, |
| 3883 | .type = .NULL, |
| 3628 | 3884 | .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, |
| 3633 | 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); |
| 3638 | 3892 | |
| 3639 | | _, const data_size = elf.ni.data.location(&elf.mf).resolve(&elf.mf); |
| 3640 | 3893 | const ph_data = &phdr[phndx.data]; |
| 3641 | 3894 | ph_data.* = .{ |
| 3642 | | .type = if (data_size == 0) .NULL else .LOAD, |
| 3895 | .type = .NULL, |
| 3643 | 3896 | .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, |
| 3648 | 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); |
| 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 | } |
| 3653 | 3918 | |
| 3654 | 3919 | if (comp.config.any_non_single_threaded) { |
| 3655 | 3920 | const ph_tls = &phdr[phndx.tls]; |
| ... | ... | @@ -3661,9 +3926,8 @@ fn initHeaders( |
| 3661 | 3926 | .filesz = 0, |
| 3662 | 3927 | .memsz = 0, |
| 3663 | 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 | } |
| 3668 | 3932 | |
| 3669 | 3933 | if (have_dynamic_section) { |
| ... | ... | @@ -3678,7 +3942,6 @@ fn initHeaders( |
| 3678 | 3942 | .flags = .{ .R = true, .W = true }, |
| 3679 | 3943 | .@"align" = @intCast(addr_align.toByteUnits()), |
| 3680 | 3944 | }; |
| 3681 | | if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Phdr, ph_dynamic); |
| 3682 | 3945 | } |
| 3683 | 3946 | |
| 3684 | 3947 | const ph_relro = &phdr[phndx.relro]; |
| ... | ... | @@ -3690,9 +3953,8 @@ fn initHeaders( |
| 3690 | 3953 | .filesz = 0, |
| 3691 | 3954 | .memsz = 0, |
| 3692 | 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); |
| 3696 | 3958 | |
| 3697 | 3959 | const ph_gnu_stack = &phdr[phndx.gnu_stack]; |
| 3698 | 3960 | ph_gnu_stack.* = .{ |
| ... | ... | @@ -3705,7 +3967,10 @@ fn initHeaders( |
| 3705 | 3967 | .flags = .{ .R = true, .W = true }, |
| 3706 | 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 | } |
| 3710 | 3975 | |
| 3711 | 3976 | const sh_undef: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf))); |
| ... | ... | @@ -3733,7 +3998,7 @@ fn initHeaders( |
| 3733 | 3998 | .size = @sizeOf(ElfN.Sym) * 1, |
| 3734 | 3999 | .addralign = addr_align, |
| 3735 | 4000 | .entsize = @sizeOf(ElfN.Sym), |
| 3736 | | .node_align = elf.mf.flags.block_size, |
| 4001 | .node_align = node_block_align, |
| 3737 | 4002 | .info = 1, // index of first non-local symbol |
| 3738 | 4003 | })); |
| 3739 | 4004 | const symtab_null = @field(elf.symPtr(.null), @tagName(ct_class)); |
| ... | ... | @@ -3755,7 +4020,7 @@ fn initHeaders( |
| 3755 | 4020 | .type = .STRTAB, |
| 3756 | 4021 | .size = 1, |
| 3757 | 4022 | .entsize = 1, |
| 3758 | | .node_align = elf.mf.flags.block_size, |
| 4023 | .node_align = node_block_align, |
| 3759 | 4024 | })); |
| 3760 | 4025 | Section.Index.get(.shstrtab, elf).ni.slice(&elf.mf)[0] = 0; |
| 3761 | 4026 | |
| ... | ... | @@ -3767,7 +4032,7 @@ fn initHeaders( |
| 3767 | 4032 | .type = .STRTAB, |
| 3768 | 4033 | .size = 1, |
| 3769 | 4034 | .entsize = 1, |
| 3770 | | .node_align = elf.mf.flags.block_size, |
| 4035 | .node_align = node_block_align, |
| 3771 | 4036 | })); |
| 3772 | 4037 | Section.Index.get(.strtab, elf).ni.slice(&elf.mf)[0] = 0; |
| 3773 | 4038 | switch (elf.shdrPtr(.symtab)) { |
| ... | ... | @@ -3777,22 +4042,22 @@ fn initHeaders( |
| 3777 | 4042 | assert(.rodata == try elf.addSection(elf.ni.rodata, .{ |
| 3778 | 4043 | .name = ".rodata", |
| 3779 | 4044 | .flags = .{ .ALLOC = true }, |
| 3780 | | .addralign = elf.mf.flags.block_size, |
| 4045 | .node_align = node_block_align, |
| 3781 | 4046 | })); |
| 3782 | 4047 | assert(.text == try elf.addSection(elf.ni.text, .{ |
| 3783 | 4048 | .name = ".text", |
| 3784 | 4049 | .flags = .{ .ALLOC = true, .EXECINSTR = true }, |
| 3785 | | .addralign = elf.mf.flags.block_size, |
| 4050 | .node_align = node_block_align, |
| 3786 | 4051 | })); |
| 3787 | 4052 | assert(.data == try elf.addSection(elf.ni.data, .{ |
| 3788 | 4053 | .name = ".data", |
| 3789 | 4054 | .flags = .{ .WRITE = true, .ALLOC = true }, |
| 3790 | | .addralign = elf.mf.flags.block_size, |
| 4055 | .node_align = node_block_align, |
| 3791 | 4056 | })); |
| 3792 | 4057 | assert(.data_rel_ro == try elf.addSection(elf.ni.data_rel_ro, .{ |
| 3793 | 4058 | .name = ".data.rel.ro", |
| 3794 | 4059 | .flags = .{ .WRITE = true, .ALLOC = true }, |
| 3795 | | .addralign = elf.mf.flags.block_size, |
| 4060 | .node_align = node_block_align, |
| 3796 | 4061 | })); |
| 3797 | 4062 | if (@"type" != .REL) { |
| 3798 | 4063 | elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{ |
| ... | ... | @@ -3808,34 +4073,39 @@ fn initHeaders( |
| 3808 | 4073 | .addralign = addr_align, |
| 3809 | 4074 | .entsize = @intCast(addr_align.toByteUnits()), |
| 3810 | 4075 | }); |
| 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, .{ |
| 3814 | 4079 | .name = ".got.plt", |
| 3815 | 4080 | .type = .PROGBITS, |
| 3816 | 4081 | .flags = .{ .WRITE = true, .ALLOC = true }, |
| 3817 | 4082 | .size = got_plt.header_entries * elf.targetPtrSize(), |
| 3818 | 4083 | .addralign = addr_align, |
| 3819 | 4084 | .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 | } |
| 3834 | 4104 | if (plt.plt_sec != null) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{ |
| 3835 | 4105 | .name = ".plt.sec", |
| 3836 | 4106 | .flags = .{ .ALLOC = true, .EXECINSTR = true }, |
| 3837 | 4107 | .addralign = plt.@"align", |
| 3838 | | .node_align = elf.mf.flags.block_size, |
| 4108 | .node_align = node_block_align, |
| 3839 | 4109 | }); |
| 3840 | 4110 | if (maybe_interp) |interp| { |
| 3841 | 4111 | const interp_ni = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{ |
| ... | ... | @@ -3858,6 +4128,7 @@ fn initHeaders( |
| 3858 | 4128 | sec_interp[interp.len] = 0; |
| 3859 | 4129 | } |
| 3860 | 4130 | if (have_dynamic_section) { |
| 4131 | assert(elf.ni.data_rel_ro.alignment(&elf.mf).compare(.gte, addr_align)); |
| 3861 | 4132 | const dynamic_ni = try elf.mf.addLastChildNode(gpa, elf.ni.data_rel_ro, .{ |
| 3862 | 4133 | .alignment = addr_align, |
| 3863 | 4134 | .moved = true, |
| ... | ... | @@ -3872,7 +4143,7 @@ fn initHeaders( |
| 3872 | 4143 | .flags = .{ .ALLOC = true }, |
| 3873 | 4144 | .size = 1, |
| 3874 | 4145 | .entsize = 1, |
| 3875 | | .node_align = elf.mf.flags.block_size, |
| 4146 | .node_align = node_block_align, |
| 3876 | 4147 | }); |
| 3877 | 4148 | dynstr_shndx.get(elf).ni.slice(&elf.mf)[0] = 0; |
| 3878 | 4149 | elf.shndx.dynstr = dynstr_shndx; |
| ... | ... | @@ -3890,7 +4161,7 @@ fn initHeaders( |
| 3890 | 4161 | .info = 1, |
| 3891 | 4162 | .addralign = addr_align, |
| 3892 | 4163 | .entsize = @sizeOf(Sym), |
| 3893 | | .node_align = elf.mf.flags.block_size, |
| 4164 | .node_align = node_block_align, |
| 3894 | 4165 | }); |
| 3895 | 4166 | const dynsym_null = @field(elf.dynsymPtr(0), @tagName(ct_class)); |
| 3896 | 4167 | dynsym_null.* = .{ |
| ... | ... | @@ -3918,7 +4189,7 @@ fn initHeaders( |
| 3918 | 4189 | .link = elf.shndx.dynsym.toSection().?, |
| 3919 | 4190 | .addralign = addr_align, |
| 3920 | 4191 | .entsize = rela_size, |
| 3921 | | .node_align = elf.mf.flags.block_size, |
| 4192 | .node_align = node_block_align, |
| 3922 | 4193 | }); |
| 3923 | 4194 | elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{ |
| 3924 | 4195 | .name = ".rela.plt", |
| ... | ... | @@ -3928,7 +4199,7 @@ fn initHeaders( |
| 3928 | 4199 | .info = (if (plt.got_plt != null) elf.shndx.got_plt else elf.shndx.plt).toSection().?, |
| 3929 | 4200 | .addralign = addr_align, |
| 3930 | 4201 | .entsize = rela_size, |
| 3931 | | .node_align = elf.mf.flags.block_size, |
| 4202 | .node_align = node_block_align, |
| 3932 | 4203 | }); |
| 3933 | 4204 | elf.shndx.dynamic = try elf.addSection(dynamic_ni, .{ |
| 3934 | 4205 | .name = ".dynamic", |
| ... | ... | @@ -3938,6 +4209,32 @@ fn initHeaders( |
| 3938 | 4209 | .entsize = @intCast(addr_align.toByteUnits() * 2), |
| 3939 | 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 | 4238 | switch (machine) { |
| 3942 | 4239 | .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)), |
| 3943 | 4240 | .X86_64 => { |
| ... | ... | @@ -4015,15 +4312,6 @@ fn initHeaders( |
| 4015 | 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 | | } |
| 4027 | 4315 | |
| 4028 | 4316 | // Populate reserved GOT words. |
| 4029 | 4317 | switch (machine) { |
| ... | ... | @@ -4199,7 +4487,7 @@ fn initHeaders( |
| 4199 | 4487 | if (comp.config.any_non_single_threaded) elf.shndx.tdata = try elf.addSection(elf.ni.tls, .{ |
| 4200 | 4488 | .name = ".tdata", |
| 4201 | 4489 | .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true }, |
| 4202 | | .addralign = elf.mf.flags.block_size, |
| 4490 | .node_align = node_block_align, |
| 4203 | 4491 | }); |
| 4204 | 4492 | |
| 4205 | 4493 | assert(elf.nodes.len == expected_nodes_len); |
| ... | ... | @@ -4465,6 +4753,28 @@ fn ehdrType(elf: *const Elf) EhdrType { |
| 4465 | 4753 | fn targetPtrSize(elf: *const Elf) u8 { |
| 4466 | 4754 | return elf.identClass().size(); |
| 4467 | 4755 | } |
| 4756 | fn 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 | } |
| 4468 | 4778 | fn targetEndian(elf: *const Elf) std.lang.Endian { |
| 4469 | 4779 | const ident_data: std.elf.DATA = @fromBackingInt(elf.ni.elf.sliceConst(&elf.mf)[std.elf.EI.DATA]); |
| 4470 | 4780 | return ident_data.endian(); |
| ... | ... | @@ -4531,6 +4841,30 @@ const PltInfo = struct { |
| 4531 | 4841 | fn targetPltInfo(elf: *const Elf) PltInfo { |
| 4532 | 4842 | return .fromMachine(elf.ehdrMachine()); |
| 4533 | 4843 | } |
| 4844 | const 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 | }; |
| 4862 | fn 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 | } |
| 4534 | 4868 | fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child { |
| 4535 | 4869 | const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer; |
| 4536 | 4870 | const Child = pointer_ty.child; |
| ... | ... | @@ -4586,14 +4920,11 @@ const PhdrSlice = union(std.elf.CLASS) { |
| 4586 | 4920 | }; |
| 4587 | 4921 | fn phdrSlice(elf: *Elf) PhdrSlice { |
| 4588 | 4922 | assert(elf.ehdrType() != .REL); |
| 4589 | | const slice = elf.ni.phdr.slice(&elf.mf); |
| 4590 | 4923 | return switch (elf.identClass()) { |
| 4591 | 4924 | .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 | ))), |
| 4597 | 4928 | }; |
| 4598 | 4929 | } |
| 4599 | 4930 | |
| ... | ... | @@ -4607,7 +4938,9 @@ fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr { |
| 4607 | 4938 | switch (elf.identClass()) { |
| 4608 | 4939 | .NONE, _ => unreachable, |
| 4609 | 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 | 4944 | const shdr_ptr = &shdr_slice[@backingInt(shndx)]; |
| 4612 | 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 | 4995 | fn mapInputSection(elf: *Elf, opts: struct { |
| 4663 | 4996 | name: []const u8, |
| 4664 | 4997 | flags: std.elf.SHF, |
| 4665 | | addralign: std.elf.Xword, |
| 4666 | 4998 | entsize: std.elf.Xword, |
| 4667 | 4999 | }) (Error || error{ |
| 4668 | 5000 | UnsupportedSectionFlags, |
| ... | ... | @@ -4734,16 +5066,12 @@ fn mapInputSection(elf: *Elf, opts: struct { |
| 4734 | 5066 | flags.COMPRESSED = false; |
| 4735 | 5067 | break :flags flags; |
| 4736 | 5068 | }, |
| 4737 | | .node_align = .fromByteUnits(std.math.ceilPowerOfTwoAssert( |
| 4738 | | usize, |
| 4739 | | @intCast(@max(opts.addralign, 1)), |
| 4740 | | )), |
| 4741 | 5069 | .entsize = std.math.lossyCast(u32, opts.entsize), |
| 4742 | 5070 | }); |
| 4743 | 5071 | }; |
| 4744 | | // Validate that the input is compatible with this section... |
| 4745 | 5072 | switch (elf.shdrPtr(existing_shndx)) { |
| 4746 | 5073 | inline else => |shdr| { |
| 5074 | // Validate that the input is compatible with this section |
| 4747 | 5075 | const cur_flags = elf.targetLoad(&shdr.flags).shf; |
| 4748 | 5076 | if (cur_flags.EXECINSTR != opts.flags.EXECINSTR or |
| 4749 | 5077 | cur_flags.WRITE != opts.flags.WRITE or |
| ... | ... | @@ -4756,20 +5084,8 @@ fn mapInputSection(elf: *Elf, opts: struct { |
| 4756 | 5084 | .NULL, .PROGBITS => {}, |
| 4757 | 5085 | else => return error.SectionTypeConflict, |
| 4758 | 5086 | } |
| 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 |
| 4773 | 5089 | elf.targetStore(&shdr.flags, .{ .shf = .{ |
| 4774 | 5090 | .EXECINSTR = cur_flags.EXECINSTR, |
| 4775 | 5091 | .WRITE = cur_flags.WRITE, |
| ... | ... | @@ -4778,11 +5094,6 @@ fn mapInputSection(elf: *Elf, opts: struct { |
| 4778 | 5094 | .STRINGS = cur_flags.STRINGS and opts.flags.STRINGS, |
| 4779 | 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 | 5099 | return existing_shndx; |
| ... | ... | @@ -4810,7 +5121,6 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node |
| 4810 | 5121 | .TLS = elf.base.comp.config.any_non_single_threaded and |
| 4811 | 5122 | nav.resolved.?.@"threadlocal", |
| 4812 | 5123 | }, |
| 4813 | | .addralign = 1, |
| 4814 | 5124 | .entsize = 0, |
| 4815 | 5125 | })) |shndx| { |
| 4816 | 5126 | break :section shndx; |
| ... | ... | @@ -4856,6 +5166,7 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node |
| 4856 | 5166 | else => |a| a, |
| 4857 | 5167 | }, |
| 4858 | 5168 | }; |
| 5169 | try shndx.ensureAligned(elf, alignment.toStdMem()); |
| 4859 | 5170 | const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{ |
| 4860 | 5171 | .alignment = alignment.toStdMem(), |
| 4861 | 5172 | }); |
| ... | ... | @@ -4899,6 +5210,7 @@ fn uavMapIndex( |
| 4899 | 5210 | const umi: Node.UavMapIndex = @fromBackingInt(@intCast(uav_gop.index)); |
| 4900 | 5211 | if (!uav_gop.found_existing) { |
| 4901 | 5212 | 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 | 5214 | const node = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{ |
| 4903 | 5215 | .moved = true, // see assert at end of `genUav` |
| 4904 | 5216 | .alignment = resolved_align.toStdMem(), |
| ... | ... | @@ -4925,6 +5237,8 @@ fn uavMapIndex( |
| 4925 | 5237 | elf.pending_uavs.appendAssumeCapacity(umi); |
| 4926 | 5238 | } else { |
| 4927 | 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 | 5242 | if (resolved_align.toStdMem().order(node.alignment(&elf.mf)).compare(.gt)) { |
| 4929 | 5243 | try node.realign(&elf.mf, gpa, resolved_align.toStdMem(), .{}); |
| 4930 | 5244 | } |
| ... | ... | @@ -5225,7 +5539,6 @@ fn loadObject( |
| 5225 | 5539 | const shndx = elf.mapInputSection(.{ |
| 5226 | 5540 | .name = name, |
| 5227 | 5541 | .flags = section.shdr.flags.shf, |
| 5228 | | .addralign = section.shdr.addralign, |
| 5229 | 5542 | .entsize = section.shdr.entsize, |
| 5230 | 5543 | }) catch |err| switch (err) { |
| 5231 | 5544 | error.StripSection => continue, |
| ... | ... | @@ -5313,7 +5626,7 @@ fn loadObject( |
| 5313 | 5626 | const old_size = elf.targetLoad(&shdr.size); |
| 5314 | 5627 | const new_size = old_size + section.shdr.size; |
| 5315 | 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 | 5632 | break :shndx shndx.*; |
| ... | ... | @@ -5324,12 +5637,13 @@ fn loadObject( |
| 5324 | 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 | 5644 | const ni = try elf.mf.addLastChildNode(gpa, opts.shndx.get(elf).ni, .{ |
| 5328 | 5645 | .size = section.shdr.size, |
| 5329 | | .alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert( |
| 5330 | | usize, |
| 5331 | | @intCast(@max(section.shdr.addralign, 1)), |
| 5332 | | )), |
| 5646 | .alignment = need_align, |
| 5333 | 5647 | .moved = true, // see assert at end of `flushInputSection` |
| 5334 | 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 | 6013 | if (elf.copied_globals.get(name)) |copied_global| { |
| 5700 | 6014 | // We have a copy relocation for this global, but the amount of space we |
| 5701 | 6015 | // reserved for it could be too small or underaligned! |
| 6016 | try Section.Index.data.ensureAligned(elf, gop.value_ptr.alignment); |
| 5702 | 6017 | try copied_global.node.resize(&elf.mf, gpa, gop.value_ptr.size); |
| 5703 | 6018 | try copied_global.node.realign(&elf.mf, gpa, gop.value_ptr.alignment, .{}); |
| 5704 | 6019 | const global_ptr = elf.globalByName(name).?; |
| ... | ... | @@ -5878,19 +6193,7 @@ fn updateInitFiniArraySectionSize( |
| 5878 | 6193 | elf: *Elf, |
| 5879 | 6194 | shndx: Section.Index, |
| 5880 | 6195 | comptime name: []const u8, |
| 5881 | | @"type": std.elf.SHT, |
| 5882 | | new_size: u64, |
| 5883 | 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 | 6197 | const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) { |
| 5895 | 6198 | inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size), |
| 5896 | 6199 | }; |
| ... | ... | @@ -5955,7 +6258,7 @@ fn prepareDynamic(elf: *Elf) Error!void { |
| 5955 | 6258 | @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 + |
| 5956 | 6259 | @as(usize, @intFromBool(use_plt)) * 4 + |
| 5957 | 6260 | @intFromBool(comp.config.output_mode == .Exe) + |
| 5958 | | @intFromBool(elf.textrel_count > 0) + 8; |
| 6261 | @intFromBool(elf.textrel_count > 0) + 9; |
| 5959 | 6262 | |
| 5960 | 6263 | const dynamic_size = dynamic_len * 2 * elf.targetPtrSize(); |
| 5961 | 6264 | |
| ... | ... | @@ -6055,7 +6358,7 @@ fn flushDynamic(elf: *Elf) void { |
| 6055 | 6358 | dynamic_index += 4; |
| 6056 | 6359 | } |
| 6057 | 6360 | |
| 6058 | | dynamic_entries[dynamic_index..][0..8].* = .{ |
| 6361 | dynamic_entries[dynamic_index..][0..9].* = .{ |
| 6059 | 6362 | .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) }, |
| 6060 | 6363 | .{ std.elf.DT_RELASZ, @intCast(elf.shndx.rela_dyn.size(elf)) }, |
| 6061 | 6364 | .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) }, |
| ... | ... | @@ -6063,9 +6366,10 @@ fn flushDynamic(elf: *Elf) void { |
| 6063 | 6366 | .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) }, |
| 6064 | 6367 | .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) }, |
| 6065 | 6368 | .{ std.elf.DT_STRSZ, @intCast(elf.shndx.dynstr.size(elf)) }, |
| 6369 | .{ std.elf.DT_HASH, @intCast(elf.shndx.hash.vaddr(elf)) }, |
| 6066 | 6370 | .{ std.elf.DT_NULL, 0 }, |
| 6067 | 6371 | }; |
| 6068 | | dynamic_index += 8; |
| 6372 | dynamic_index += 9; |
| 6069 | 6373 | |
| 6070 | 6374 | assert(dynamic_index == dynamic_entries.len); |
| 6071 | 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 | 6396 | else => {}, |
| 6093 | 6397 | } |
| 6094 | 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 | 6402 | const gpa = elf.base.comp.gpa; |
| 6098 | 6403 | try elf.nodes.ensureUnusedCapacity(gpa, 1); |
| ... | ... | @@ -6427,7 +6732,7 @@ fn addRelocAssumeCapacity( |
| 6427 | 6732 | |
| 6428 | 6733 | .WDISP30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" })), |
| 6429 | 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 | 6736 | .H44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:0]", .cast = .unsigned, .shift = .@"22" })), |
| 6432 | 6737 | .M44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"12" })), |
| 6433 | 6738 | |
| ... | ... | @@ -6457,36 +6762,36 @@ fn addRelocAssumeCapacity( |
| 6457 | 6762 | |
| 6458 | 6763 | // The following relocations are all represented by the ABI as writing to a 13 bit |
| 6459 | 6764 | // 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. |
| 6464 | 6769 | .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" }); |
| 6466 | 6771 | try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" })); |
| 6467 | 6772 | }, |
| 6468 | 6773 | .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" }); |
| 6470 | 6775 | try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[11:0]", .cast = .trunc, .shift = .@"0" })); |
| 6471 | 6776 | }, |
| 6472 | 6777 | .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" }); |
| 6474 | 6779 | try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" })); |
| 6475 | 6780 | }, |
| 6476 | 6781 | .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" }); |
| 6478 | 6783 | try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" })); |
| 6479 | 6784 | }, |
| 6480 | 6785 | .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" }); |
| 6482 | 6787 | elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" })); |
| 6483 | 6788 | }, |
| 6484 | 6789 | .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" }); |
| 6486 | 6791 | elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" })); |
| 6487 | 6792 | }, |
| 6488 | 6793 | .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" }); |
| 6490 | 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 | 7192 | const entry_ptr: *class.ElfN().Addr = @ptrCast(@alignCast( |
| 6888 | 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 | 7196 | .unsigned => |x| @intCast(x), |
| 6892 | 7197 | .signed => |x| switch (class) { |
| 6893 | 7198 | .NONE, _ => comptime unreachable, |
| ... | ... | @@ -6895,7 +7200,7 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 6895 | 7200 | .@"64" => @bitCast(x), |
| 6896 | 7201 | }, |
| 6897 | 7202 | .reloc => 0, |
| 6898 | | }; |
| 7203 | }); |
| 6899 | 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 | 7255 | fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool { |
| 6951 | 7256 | assert(elf.shndx.dynamic != .UNDEF); |
| 6952 | 7257 | |
| 7258 | // Only dynamic executables may contain `R_*_COPY` relocations. |
| 7259 | if (elf.base.comp.config.output_mode != .Exe) return false; |
| 7260 | |
| 6953 | 7261 | const gpa = elf.base.comp.gpa; |
| 6954 | 7262 | |
| 6955 | 7263 | 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 | 7265 | |
| 6958 | 7266 | assert(global_ptr.dynsym_index != 0); |
| 6959 | 7267 | |
| 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 | 7268 | const dso_global = elf.dso_globals.get(global_name) orelse { |
| 6965 | 7269 | // We do not have a definition to provide the correct size for the symbol. If a definition |
| 6966 | 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 | 7278 | if (gop.found_existing) return true; |
| 6975 | 7279 | errdefer assert(elf.copied_globals.pop().?.key == global_name); |
| 6976 | 7280 | |
| 7281 | try Section.Index.data.ensureAligned(elf, dso_global.alignment); |
| 7282 | |
| 6977 | 7283 | try elf.nodes.ensureUnusedCapacity(gpa, 1); |
| 6978 | 7284 | const node = try elf.mf.addLastChildNode(gpa, Section.Index.data.get(elf).ni, .{ |
| 6979 | 7285 | .size = dso_global.size, |
| ... | ... | @@ -7234,6 +7540,25 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { |
| 7234 | 7540 | }; |
| 7235 | 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 | 7562 | if (elf.changed_symtab_index.pop()) |kv| { |
| 7238 | 7563 | const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0); |
| 7239 | 7564 | defer sub_prog_node.end(); |
| ... | ... | @@ -7324,6 +7649,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool { |
| 7324 | 7649 | } |
| 7325 | 7650 | } |
| 7326 | 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 | 7653 | if (elf.changed_symtab_index.count() > 0) return true; |
| 7328 | 7654 | if (elf.mf.updates.items.len > 0) return true; |
| 7329 | 7655 | return false; |
| ... | ... | @@ -7586,17 +7912,22 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 7586 | 7912 | const ph = &phdr[phndx]; |
| 7587 | 7913 | switch (elf.targetLoad(&ph.type)) { |
| 7588 | 7914 | else => unreachable, |
| 7589 | | .NULL, .LOAD => return, |
| 7915 | |
| 7916 | .NULL, .LOAD => { |
| 7917 | try elf.allocateSegmentLoadAddress(phndx); |
| 7918 | }, |
| 7590 | 7919 | |
| 7591 | 7920 | .DYNAMIC, |
| 7592 | 7921 | .INTERP, |
| 7593 | 7922 | .PHDR, |
| 7594 | 7923 | .TLS, |
| 7595 | 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 | 7979 | elf.flushMovedPltSection(.got_plt, old_addr, addr); |
| 7649 | 7980 | } else if (shndx == elf.shndx.plt_sec) { |
| 7650 | 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 | 7984 | .input_member => {}, |
| ... | ... | @@ -7739,6 +8068,114 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 7739 | 8068 | try ni.childrenMoved(elf.base.comp.gpa, &elf.mf); |
| 7740 | 8069 | } |
| 7741 | 8070 | |
| 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. |
| 8078 | fn 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 | |
| 7742 | 8179 | fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void { |
| 7743 | 8180 | const trace = tracy.trace(@src()); |
| 7744 | 8181 | defer trace.end(); |
| ... | ... | @@ -7766,68 +8203,40 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo |
| 7766 | 8203 | assert(elf.phdrs.items[phndx] == ni); |
| 7767 | 8204 | const ph = &phdr[phndx]; |
| 7768 | 8205 | 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 | }, |
| 7797 | 8234 | |
| 7798 | | .tpoff => elf.updateGotEntry(got_index), |
| 7799 | | } |
| 8235 | .tpoff => elf.updateGotEntry(got_index), |
| 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 | 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 | 8257 | .REL, |
| 7849 | 8258 | .RELA, |
| 7850 | 8259 | .DYNSYM, |
| 8260 | .HASH, |
| 7851 | 8261 | => return, |
| 7852 | 8262 | } |
| 7853 | 8263 | if (shndx != elf.shndx.plt and |
| ... | ... | @@ -7940,21 +8350,6 @@ fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error! |
| 7940 | 8350 | } |
| 7941 | 8351 | } |
| 7942 | 8352 | |
| 7943 | | fn 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 | | } |
| 7958 | 8353 | fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void { |
| 7959 | 8354 | const target_endian = elf.targetEndian(); |
| 7960 | 8355 | |
| ... | ... | @@ -8126,9 +8521,9 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void |
| 8126 | 8521 | const plt_slice: []Inst = @ptrCast(@alignCast(plt_ni.slice(&elf.mf)[@intCast(got_plt_offset)..][0..32])); |
| 8127 | 8522 | @memcpy(plt_slice, &[8]Inst{ |
| 8128 | 8523 | // sethi (. - .plt[0]), %g1 |
| 8129 | | .{ .imm22 = .{ .imm = @truncate(got_plt_offset), .op = 0b0000000011 } }, |
| 8524 | .{ .imm22 = .{ .imm = @truncate(got_plt_offset), .op = 0b0000001100 } }, |
| 8130 | 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 | 8527 | // nop |
| 8133 | 8528 | .{ .raw = 0x0100_0000 }, |
| 8134 | 8529 | // nop |
| ... | ... | @@ -8142,6 +8537,9 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void |
| 8142 | 8537 | // nop |
| 8143 | 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 | 8610 | .LOONGARCH => { |
| 8213 | 8611 | switch (which) { |
| 8214 | 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 | 8620 | // We also need to update all of the references from `.plt` to `.got.plt`. |
| 8216 | 8621 | // However, if there's also a flush pending for `.got.plt`, don't bother doing |
| 8217 | 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 | 8677 | }, |
| 8273 | 8678 | .SPARCV9 => switch (which) { |
| 8274 | 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 | 8687 | // Update the offsets of the relocation entries in `.rela.plt`. |
| 8276 | 8688 | const rela_plt_shndx = elf.shndx.rela_plt; |
| 8277 | 8689 | for (0..elf.plt.count()) |plt_index| { |
| ... | ... | @@ -8315,31 +8727,31 @@ fn updateExportInner( |
| 8315 | 8727 | }), |
| 8316 | 8728 | } |
| 8317 | 8729 | 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), |
| 8324 | 8733 | }; |
| 8325 | 8734 | |
| 8326 | 8735 | try elf.ensureElfNodeSize(); |
| 8327 | | while (try elf.idle(pt.tid)) {} |
| 8328 | 8736 | |
| 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())) { |
| 8331 | 8741 | inline else => |exported_sym| .{ |
| 8742 | elf.targetLoad(&exported_sym.value), |
| 8332 | 8743 | elf.targetLoad(&exported_sym.size), |
| 8744 | elf.targetLoad(&exported_sym.info).type, |
| 8333 | 8745 | .fromSection(elf.targetLoad(&exported_sym.shndx)), |
| 8334 | 8746 | }, |
| 8335 | 8747 | }; |
| 8336 | 8748 | |
| 8337 | 8749 | const name = @"export".opts.name.toSlice(ip); |
| 8338 | 8750 | _ = elf.addGlobalSymbolAssumeCapacity(.{ |
| 8339 | | .node = .none, |
| 8751 | .node = exported_lsi.index().ptr(elf).node, |
| 8340 | 8752 | .name = try .string(elf, name), |
| 8341 | | .value = value, |
| 8342 | | .size = @intCast(size), |
| 8753 | .value = cur_value, |
| 8754 | .size = cur_size, |
| 8343 | 8755 | .type = @"type", |
| 8344 | 8756 | .bind = switch (@"export".opts.linkage) { |
| 8345 | 8757 | .strong => .strong, |
| ... | ... | @@ -8501,6 +8913,46 @@ pub fn printNode( |
| 8501 | 8913 | } |
| 8502 | 8914 | } |
| 8503 | 8915 | |
| 8916 | fn 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 | 8956 | /// Must be called deterministically after any call to `MappedFile.Node.Index.resize` |
| 8505 | 8957 | /// (of `elf.ni.elf` or one of its children) before any possible calls to `idle`. |
| 8506 | 8958 | fn ensureElfNodeSize(elf: *Elf) MappedFile.Error!void { |