| ... | @@ -1794,66 +1794,74 @@ fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void { | ... | @@ -1794,66 +1794,74 @@ fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void { |
| 1794 | )), | 1794 | )), |
| 1795 | }; | 1795 | }; |
| 1796 | | 1796 | |
| 1797 | { | 1797 | switch (elf.targetDynsymHashInfo()) { |
| 1798 | const section_slice: []align(4) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf)); | 1798 | inline else => |info| { |
| 1799 | const header: *std.elf.hash.Header = @ptrCast(section_slice[0..@sizeOf(std.elf.hash.Header)]); | 1799 | { |
| 1800 | assert(elf.targetLoad(&header.nchain) == cur_dynsym_count); | 1800 | const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf)); |
| 1801 | const nbucket = elf.targetLoad(&header.nbucket); | 1801 | const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]); |
| 1802 | if (nbucket >= min_buckets) { | 1802 | assert(elf.targetLoad(&header.nchain) == cur_dynsym_count); |
| 1803 | // We don't need to add any buckets, but we still need to make sure the section is large | 1803 | const nbucket = elf.targetLoad(&header.nbucket); |
| 1804 | // enough to fit `max_dynsym_count` chains. | 1804 | if (nbucket >= min_buckets) { |
| 1805 | const need_size = @sizeOf(std.elf.hash.Header) + (nbucket + max_dynsym_count) * 4; | 1805 | // We don't need to add any buckets, but we still need to make sure the section is large |
| 1806 | try elf.ensureNodeSize(elf.shndx.hash.get(elf).ni, need_size); | 1806 | // enough to fit `max_dynsym_count` chains. |
| 1807 | return; | 1807 | const need_size = @sizeOf(info.Header()) + (nbucket + max_dynsym_count) * 4; |
| 1808 | } | 1808 | try elf.ensureNodeSize(elf.shndx.hash.get(elf).ni, need_size); |
| 1809 | // We need more buckets, so we'll have to rebuild the hash table. | 1809 | return; |
| 1810 | } | 1810 | } |
| 1811 | | 1811 | // We need more buckets, so we'll have to rebuild the hash table. |
| 1812 | // Rebuilding the hash table is quite expensive, so to avoid doing it too often we use a large | 1812 | } |
| 1813 | // growth factor (* 2) for `nbucket`. | | |
| 1814 | const new_nbucket = min_buckets * 2; | | |
| 1815 | | 1813 | |
| 1816 | { | 1814 | // Rebuilding the hash table is quite expensive, so to avoid doing it too often we use a large |
| 1817 | const need_size = @sizeOf(std.elf.hash.Header) + (new_nbucket + max_dynsym_count) * 4; | 1815 | // growth factor (* 2) for `nbucket`. |
| 1818 | try elf.ensureNodeSize(elf.shndx.hash.get(elf).ni, need_size); | 1816 | const new_nbucket = min_buckets * 2; |
| 1819 | } | | |
| 1820 | | 1817 | |
| 1821 | elf.mf.nodes_lock.lock(); | 1818 | { |
| 1822 | defer elf.mf.nodes_lock.unlock(); | 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 | } |
| 1823 | | 1822 | |
| 1824 | const section_slice: []align(4) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf)); | 1823 | elf.mf.nodes_lock.lock(); |
| 1825 | const header: *std.elf.hash.Header = @ptrCast(section_slice[0..@sizeOf(std.elf.hash.Header)]); | 1824 | defer elf.mf.nodes_lock.unlock(); |
| 1826 | const trailing: []u32 = @ptrCast(section_slice[@sizeOf(std.elf.hash.Header)..]); | | |
| 1827 | | 1825 | |
| 1828 | header.* = .{ .nbucket = new_nbucket, .nchain = cur_dynsym_count }; | 1826 | const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf)); |
| 1829 | if (elf.targetEndian() != std.lang.Endian.native) { | 1827 | const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]); |
| 1830 | std.mem.byteSwapAllFields(std.elf.hash.Header, header); | 1828 | const trailing: []info.Int() = @ptrCast(section_slice[@sizeOf(info.Header())..]); |
| 1831 | } | | |
| 1832 | const buckets: []u32 = trailing[0..elf.targetLoad(&header.nbucket)]; | | |
| 1833 | const chains: []u32 = trailing[elf.targetLoad(&header.nbucket)..][0..elf.targetLoad(&header.nchain)]; | | |
| 1834 | | 1829 | |
| 1835 | @memset(buckets, 0); | 1830 | header.* = .{ .nbucket = new_nbucket, .nchain = cur_dynsym_count }; |
| 1836 | chains[0] = 0; | 1831 | if (elf.targetEndian() != std.lang.Endian.native) { |
| 1837 | for (1..cur_dynsym_count, chains[1..]) |dynsym_index_usize, *chain| { | 1832 | std.mem.byteSwapAllFields(info.Header(), header); |
| 1838 | const dynsym_index: u32 = @intCast(dynsym_index_usize); | 1833 | } |
| 1839 | const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) { | 1834 | const buckets: []info.Int() = trailing[0..elf.targetLoad(&header.nbucket)]; |
| 1840 | inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)), | 1835 | const chains: []info.Int() = trailing[elf.targetLoad(&header.nbucket)..][0..elf.targetLoad(&header.nchain)]; |
| 1841 | }; | 1836 | |
| 1842 | const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len; | 1837 | @memset(buckets, 0); |
| 1843 | // Make this symbol the head of that bucket, and chain to the old head. | 1838 | chains[0] = 0; |
| 1844 | chain.* = buckets[b]; | 1839 | for (1..cur_dynsym_count, chains[1..]) |dynsym_index_usize, *chain| { |
| 1845 | elf.targetStore(&buckets[b], dynsym_index); | 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 | }, |
| 1846 | } | 1850 | } |
| 1847 | } | 1851 | } |
| 1848 | | 1852 | |
| 1849 | fn appendDynsymHashEntry(elf: *Elf, dynsym_index: u32) void { | 1853 | fn appendDynsymHashEntry(elf: *Elf, dynsym_index: u32) void { |
| 1850 | const section_slice: []align(4) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf)); | 1854 | switch (elf.targetDynsymHashInfo()) { |
| 1851 | const header: *std.elf.hash.Header = @ptrCast(section_slice[0..@sizeOf(std.elf.hash.Header)]); | 1855 | inline else => |info| { |
| 1852 | assert(elf.targetLoad(&header.nchain) == dynsym_index); | 1856 | const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf)); |
| 1853 | elf.targetStore(&header.nchain, dynsym_index + 1); | 1857 | const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]); |
| 1854 | | 1858 | assert(elf.targetLoad(&header.nchain) == dynsym_index); |
| 1855 | switch (elf.shdrPtr(elf.shndx.hash)) { | 1859 | elf.targetStore(&header.nchain, dynsym_index + 1); |
| 1856 | inline else => |shdr| elf.targetStore(&shdr.size, elf.targetLoad(&shdr.size) + 4), | 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 | }, |
| 1857 | } | 1865 | } |
| 1858 | | 1866 | |
| 1859 | elf.populateDynsymHashEntry(dynsym_index); | 1867 | elf.populateDynsymHashEntry(dynsym_index); |
| ... | @@ -1864,31 +1872,39 @@ fn populateDynsymHashEntry(elf: *Elf, dynsym_index: u32) void { | ... | @@ -1864,31 +1872,39 @@ fn populateDynsymHashEntry(elf: *Elf, dynsym_index: u32) void { |
| 1864 | | 1872 | |
| 1865 | assert(dynsym_index != 0); | 1873 | assert(dynsym_index != 0); |
| 1866 | | 1874 | |
| 1867 | const section_slice: []align(4) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf)); | 1875 | switch (elf.targetDynsymHashInfo()) { |
| 1868 | const header: *std.elf.hash.Header = @ptrCast(section_slice[0..@sizeOf(std.elf.hash.Header)]); | 1876 | inline else => |info| { |
| 1869 | const trailing: []u32 = @ptrCast(section_slice[@sizeOf(std.elf.hash.Header)..]); | 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())..]); |
| 1870 | | 1880 | |
| 1871 | const buckets: []u32 = trailing[0..elf.targetLoad(&header.nbucket)]; | 1881 | const buckets: []info.Int() = trailing[0..elf.targetLoad(&header.nbucket)]; |
| 1872 | const chains: []u32 = trailing[elf.targetLoad(&header.nbucket)..][0..elf.targetLoad(&header.nchain)]; | 1882 | const chains: []info.Int() = trailing[elf.targetLoad(&header.nbucket)..][0..elf.targetLoad(&header.nchain)]; |
| 1873 | | 1883 | |
| 1874 | const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) { | 1884 | const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) { |
| 1875 | inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)), | 1885 | inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)), |
| 1876 | }; | 1886 | }; |
| 1877 | const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len; | 1887 | const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len; |
| 1878 | // Make this symbol the head of that bucket, and chain to the old head. | 1888 | // Make this symbol the head of that bucket, and chain to the old head. |
| 1879 | chains[dynsym_index] = buckets[b]; | 1889 | chains[dynsym_index] = buckets[b]; |
| 1880 | elf.targetStore(&buckets[b], dynsym_index); | 1890 | elf.targetStore(&buckets[b], dynsym_index); |
| | 1891 | }, |
| | 1892 | } |
| 1881 | } | 1893 | } |
| 1882 | fn popDynsymHashEntry(elf: *Elf, dynsym_index: u32) void { | 1894 | fn popDynsymHashEntry(elf: *Elf, dynsym_index: u32) void { |
| 1883 | elf.clearDynsymHashEntry(dynsym_index); | 1895 | elf.clearDynsymHashEntry(dynsym_index); |
| 1884 | | 1896 | |
| 1885 | const section_slice: []align(4) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf)); | 1897 | switch (elf.targetDynsymHashInfo()) { |
| 1886 | const header: *std.elf.hash.Header = @ptrCast(section_slice[0..@sizeOf(std.elf.hash.Header)]); | 1898 | inline else => |info| { |
| 1887 | assert(elf.targetLoad(&header.nchain) == dynsym_index + 1); | 1899 | const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf)); |
| 1888 | elf.targetStore(&header.nchain, dynsym_index); | 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); |
| 1889 | | 1903 | |
| 1890 | switch (elf.shdrPtr(elf.shndx.hash)) { | 1904 | switch (elf.shdrPtr(elf.shndx.hash)) { |
| 1891 | inline else => |shdr| elf.targetStore(&shdr.size, elf.targetLoad(&shdr.size) - 4), | 1905 | inline else => |shdr| elf.targetStore(&shdr.size, elf.targetLoad(&shdr.size) - @sizeOf(info.Int())), |
| | 1906 | } |
| | 1907 | }, |
| 1892 | } | 1908 | } |
| 1893 | } | 1909 | } |
| 1894 | fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void { | 1910 | fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void { |
| ... | @@ -1897,34 +1913,38 @@ fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void { | ... | @@ -1897,34 +1913,38 @@ fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void { |
| 1897 | | 1913 | |
| 1898 | assert(dynsym_index != 0); | 1914 | assert(dynsym_index != 0); |
| 1899 | | 1915 | |
| 1900 | const section_slice: []align(4) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf)); | 1916 | switch (elf.targetDynsymHashInfo()) { |
| 1901 | const header: *std.elf.hash.Header = @ptrCast(section_slice[0..@sizeOf(std.elf.hash.Header)]); | 1917 | inline else => |info| { |
| 1902 | const trailing: []u32 = @ptrCast(section_slice[@sizeOf(std.elf.hash.Header)..]); | 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())..]); |
| 1903 | | 1921 | |
| 1904 | const buckets: []u32 = trailing[0..elf.targetLoad(&header.nbucket)]; | 1922 | const buckets: []info.Int() = trailing[0..elf.targetLoad(&header.nbucket)]; |
| 1905 | const chains: []u32 = trailing[elf.targetLoad(&header.nbucket)..][0..elf.targetLoad(&header.nchain)]; | 1923 | const chains: []info.Int() = trailing[elf.targetLoad(&header.nbucket)..][0..elf.targetLoad(&header.nchain)]; |
| 1906 | | 1924 | |
| 1907 | const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) { | 1925 | const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) { |
| 1908 | inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)), | 1926 | inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)), |
| 1909 | }; | 1927 | }; |
| 1910 | const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len; | 1928 | const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len; |
| 1911 | | 1929 | |
| 1912 | const next_dynsym_index = elf.targetLoad(&chains[dynsym_index]); | 1930 | const next_dynsym_index = elf.targetLoad(&chains[dynsym_index]); |
| 1913 | elf.targetStore(&chains[dynsym_index], 0); | 1931 | elf.targetStore(&chains[dynsym_index], 0); |
| 1914 | | 1932 | |
| 1915 | // To remove `dynsym_index` from the singly-linked list, we need to iterate the chain to find | 1933 | // To remove `dynsym_index` from the singly-linked list, we need to iterate the chain to find |
| 1916 | // and replace it. But since this is, well, a hash table, that's actually fine. | 1934 | // and replace it. But since this is, well, a hash table, that's actually fine. |
| 1917 | if (elf.targetLoad(&buckets[b]) == dynsym_index) { | 1935 | if (elf.targetLoad(&buckets[b]) == dynsym_index) { |
| 1918 | elf.targetStore(&buckets[b], next_dynsym_index); | 1936 | elf.targetStore(&buckets[b], next_dynsym_index); |
| 1919 | } else { | 1937 | } else { |
| 1920 | var cur = elf.targetLoad(&buckets[b]); | 1938 | var cur = elf.targetLoad(&buckets[b]); |
| 1921 | while (true) { | 1939 | while (true) { |
| 1922 | assert(cur != 0); // `dynsym_index` is definitely somewhere in the chain | 1940 | assert(cur != 0); // `dynsym_index` is definitely somewhere in the chain |
| 1923 | if (elf.targetLoad(&chains[cur]) == dynsym_index) break; | 1941 | if (elf.targetLoad(&chains[cur]) == dynsym_index) break; |
| 1924 | cur = elf.targetLoad(&chains[cur]); | 1942 | cur = elf.targetLoad(&chains[cur]); |
| 1925 | } | 1943 | } |
| 1926 | // We found `dynsym_index`; replace it with `next_dynsym_index`. | 1944 | // We found `dynsym_index`; replace it with `next_dynsym_index`. |
| 1927 | elf.targetStore(&chains[cur], next_dynsym_index); | 1945 | elf.targetStore(&chains[cur], next_dynsym_index); |
| | 1946 | } |
| | 1947 | }, |
| 1928 | } | 1948 | } |
| 1929 | } | 1949 | } |
| 1930 | | 1950 | |
| ... | @@ -4169,24 +4189,30 @@ fn initHeaders( | ... | @@ -4169,24 +4189,30 @@ fn initHeaders( |
| 4169 | .entsize = @intCast(addr_align.toByteUnits() * 2), | 4189 | .entsize = @intCast(addr_align.toByteUnits() * 2), |
| 4170 | .node_align = addr_align, | 4190 | .node_align = addr_align, |
| 4171 | }); | 4191 | }); |
| 4172 | elf.shndx.hash = try elf.addSection(elf.ni.rodata, .{ | 4192 | switch (elf.targetDynsymHashInfo()) { |
| 4173 | .name = ".hash", | 4193 | inline else => |info| { |
| 4174 | .type = .HASH, | 4194 | elf.shndx.hash = try elf.addSection(elf.ni.rodata, .{ |
| 4175 | .flags = .{ .ALLOC = true }, | 4195 | .name = ".hash", |
| 4176 | .link = elf.shndx.dynsym.toSection().?, | 4196 | .type = .HASH, |
| 4177 | .addralign = .@"4", | 4197 | .flags = .{ .ALLOC = true }, |
| 4178 | // initially: nbucket = 8, nchain = 1 | 4198 | .link = elf.shndx.dynsym.toSection().?, |
| 4179 | .size = @sizeOf(std.elf.hash.Header) + (8 + 1) * 4, | 4199 | // It's unclear what value is correct for the alignment. binutils uses 8 everywhere, |
| 4180 | }); | 4200 | // while lld uses 4 everywhere (but lld lacks support for the alpha/s390x special |
| 4181 | { | 4201 | // case). Matching the hash word (= entry) size seems like the actually sane choice, |
| 4182 | const hash_slice: []align(4) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf)); | 4202 | // and is what mold does too. |
| 4183 | const header: *std.elf.hash.Header = @ptrCast(hash_slice[0..@sizeOf(std.elf.hash.Header)]); | 4203 | .addralign = .fromByteUnits(@sizeOf(info.Int())), |
| 4184 | header.* = .{ .nbucket = 8, .nchain = 1 }; | 4204 | // initially: nbucket = 8 + nchain = 1 |
| 4185 | if (elf.targetEndian() != std.lang.Endian.native) { | 4205 | .size = @sizeOf(info.Header()) + @sizeOf(info.Int()) * (8 + 1), |
| 4186 | std.mem.byteSwapAllFields(std.elf.hash.Header, header); | 4206 | }); |
| 4187 | } | 4207 | const hash_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf)); |
| 4188 | // The initial bucket and chain values are all 0, but `MappedFile` initialized the | 4208 | const header: *info.Header() = @ptrCast(hash_slice[0..@sizeOf(info.Header())]); |
| 4189 | // node with zeroes anyway, so no need to memset. | 4209 | header.* = .{ .nbucket = 8, .nchain = 1 }; |
| | 4210 | if (elf.targetEndian() != std.lang.Endian.native) { |
| | 4211 | std.mem.byteSwapAllFields(info.Header(), header); |
| | 4212 | } |
| | 4213 | // The initial bucket and chain values are all 0, but `MappedFile` initialized |
| | 4214 | // the node with zeroes anyway, so no need to memset. |
| | 4215 | }, |
| 4190 | } | 4216 | } |
| 4191 | | 4217 | |
| 4192 | switch (machine) { | 4218 | switch (machine) { |
| ... | @@ -4795,6 +4821,30 @@ const PltInfo = struct { | ... | @@ -4795,6 +4821,30 @@ const PltInfo = struct { |
| 4795 | fn targetPltInfo(elf: *const Elf) PltInfo { | 4821 | fn targetPltInfo(elf: *const Elf) PltInfo { |
| 4796 | return .fromMachine(elf.ehdrMachine()); | 4822 | return .fromMachine(elf.ehdrMachine()); |
| 4797 | } | 4823 | } |
| | 4824 | const DynsymHashInfo = enum(u32) { |
| | 4825 | @"4" = 4, |
| | 4826 | @"8" = 8, |
| | 4827 | |
| | 4828 | fn Int(comptime self: DynsymHashInfo) type { |
| | 4829 | return switch (self) { |
| | 4830 | .@"4" => u32, |
| | 4831 | .@"8" => u64, |
| | 4832 | }; |
| | 4833 | } |
| | 4834 | |
| | 4835 | fn Header(comptime self: DynsymHashInfo) type { |
| | 4836 | return switch (self) { |
| | 4837 | .@"4" => std.elf.hash.Header32, |
| | 4838 | .@"8" => std.elf.hash.Header64, |
| | 4839 | }; |
| | 4840 | } |
| | 4841 | }; |
| | 4842 | fn targetDynsymHashInfo(elf: *const Elf) DynsymHashInfo { |
| | 4843 | return switch (elf.ehdrMachine()) { |
| | 4844 | else => .@"4", |
| | 4845 | // TODO: Alpha and S390x will need to use either `."@4"` or `.@"8"` depending on `elf.identClass()`. |
| | 4846 | }; |
| | 4847 | } |
| 4798 | fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child { | 4848 | fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child { |
| 4799 | const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer; | 4849 | const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer; |
| 4800 | const Child = pointer_ty.child; | 4850 | const Child = pointer_ty.child; |