authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-25 11:14:37+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-25 11:14:37+01:00
logb7f2c6d8fefdd2747e39e930bbe2c9450ec8f07c
treec3543b991b03c844856933d7d00b9e716d58628d
parent2f90fcd8393b493eafa3879b9ca6b2d644721f83
parentfac41b3ec9237583f4d6bcb73c339464eb9a1edc

Merge pull request 'Elf2: performance improvements & small refactors' (#37400) from mlugg/elf2-perf into master

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

2 files changed, 1153 insertions(+), 1029 deletions(-)

src/link/Elf2.zig+1119-1029
......@@ -21,6 +21,15 @@ const Alignment = MappedFile.Alignment;
2121base: link.File,
2222options: link.File.OpenOptions,
2323mf: MappedFile,
24
25/// These fields could simply be read from the ehdr node when needed, but it turns out that they are
26/// accessed frequently enough that we see measurable performance gains from caching them here.
27ehdr_fields: struct {
28 class: std.elf.CLASS,
29 type: EhdrType,
30 machine: EhdrMachine,
31},
32
2433ni: struct {
2534 elf: MappedFile.Node.Index,
2635 ehdr: MappedFile.Node.Index,
......@@ -208,29 +217,24 @@ navs: std.array_hash_map.Auto(InternPool.Nav.Index, struct {
208217 lsi: Symbol.LocalIndex,
209218 /// The start index of the contiguous sequence of symbol relocations in this NAV.
210219 first_symbol_reloc: SymbolReloc.Index,
211 /// The start index of the contiguous sequence of GOT relocations in this NAV.
212 first_got_reloc: GotReloc.Index,
213220}),
214221uavs: std.array_hash_map.Auto(InternPool.Index, struct {
215222 lsi: Symbol.LocalIndex,
216223 /// The start index of the contiguous sequence of symbol relocations in this UAV.
217224 first_symbol_reloc: SymbolReloc.Index,
218 // No `first_got_reloc` field because a UAV never contains GOT relocations.
219225}),
220226lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
221227 map: std.array_hash_map.Auto(InternPool.Index, struct {
222228 lsi: Symbol.LocalIndex,
223229 /// The start index of the contiguous sequence of symbol relocations in this lazy code/data.
224230 first_symbol_reloc: SymbolReloc.Index,
225 /// The start index of the contiguous sequence of GOT relocations in this lazy code/data.
226 first_got_reloc: GotReloc.Index,
227231 }),
228232 pending_index: u32,
229233}),
230234pending_uavs: std.ArrayList(Node.UavMapIndex),
231235symbol_relocs: std.ArrayList(SymbolReloc),
232236node_relocs: std.ArrayList(NodeReloc),
233got_relocs: std.ArrayList(GotReloc),
237got_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),
234238/// Set of relocations which must be re-applied if the size of the TLS segment changes.
235239tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),
236240/// Index matches the index into `shdrs`. Like `shdrs`, this map excludes `SHN_UNDEF`.
......@@ -388,9 +392,6 @@ const Node = union(enum) {
388392 fn firstSymbolReloc(nmi: NavMapIndex, elf: *const Elf) SymbolReloc.Index {
389393 return elf.navs.values()[@backingInt(nmi)].first_symbol_reloc;
390394 }
391 fn firstGotReloc(nmi: NavMapIndex, elf: *const Elf) GotReloc.Index {
392 return elf.navs.values()[@backingInt(nmi)].first_got_reloc;
393 }
394395 };
395396
396397 pub const UavMapIndex = enum(u32) {
......@@ -407,11 +408,6 @@ const Node = union(enum) {
407408 fn firstSymbolReloc(umi: UavMapIndex, elf: *const Elf) SymbolReloc.Index {
408409 return elf.uavs.values()[@backingInt(umi)].first_symbol_reloc;
409410 }
410 fn firstGotReloc(umi: UavMapIndex, elf: *const Elf) GotReloc.Index {
411 _ = umi;
412 _ = elf;
413 return .none;
414 }
415411 };
416412
417413 pub const LazyMapRef = struct {
......@@ -437,9 +433,6 @@ const Node = union(enum) {
437433 fn firstSymbolReloc(lmi: @This(), elf: *const Elf) SymbolReloc.Index {
438434 return elf.lazy.getPtrConst(kind).map.values()[@backingInt(lmi)].first_symbol_reloc;
439435 }
440 fn firstGotReloc(lmi: @This(), elf: *const Elf) GotReloc.Index {
441 return elf.lazy.getPtrConst(kind).map.values()[@backingInt(lmi)].first_got_reloc;
442 }
443436 };
444437 }
445438
......@@ -603,8 +596,6 @@ const InputSection = struct {
603596 node: MappedFile.Node.Index,
604597 /// The start index of the contiguous sequence of symbol relocations in this input section.
605598 first_symbol_reloc: SymbolReloc.Index,
606 /// The start index of the contiguous sequence of GOT relocations in this input section.
607 first_got_reloc: GotReloc.Index,
608599
609600 const Index = enum(u32) {
610601 _,
......@@ -1387,14 +1378,28 @@ const SymbolReloc = struct {
13871378 node: MappedFile.Node.Index.Optional,
13881379 /// The offset of the relocation inside of `node`.
13891380 offset: u64,
1390 /// A symbol used to compute the relocated value. Precise meaning depends on `@"type"`.
1391 target: Symbol.Id,
1392 /// A signed constant used to compute the relocated value. Precise meaning depends on `@"type"`.
1381 /// A symbol used to compute the relocated value. Precise meaning depends on `bits.@"type"`.
1382 target_symbol: Symbol.Id,
1383 /// A signed constant used to compute the relocated value. Precise meaning depends on `bits.@"type"`.
13931384 addend: SymbolReloc.Addend,
1394 /// Specifies how to apply the relocation.
1395 ///
1396 /// When emitting a relocatable, this field is `undefined`.
1397 type: SymbolReloc.Type,
1385 bits: packed struct(u32) {
1386 /// Specifies how to apply the relocation.
1387 ///
1388 /// When emitting a relocatable, this field is `undefined`.
1389 type: SymbolReloc.Type,
1390
1391 result: enum(u3) {
1392 ok,
1393 overflowed,
1394 misaligned,
1395 illegal_dynamic_type,
1396 illegal_dynamic_section,
1397 },
1398
1399 relaxation: Relaxation,
1400
1401 _: u10 = 0,
1402 },
13981403 /// Forms a linked list of all symbol relocations with the same `target`. This list exists so
13991404 /// that all relocations targeting a particular symbol can be re-applied if that symbol moves.
14001405 /// Doubly-linked so that relocations can be removed.
......@@ -1412,13 +1417,6 @@ const SymbolReloc = struct {
14121417 /// relocation entry. The entry will be removed if we discover a definition which allows us to
14131418 /// statically resolve the relocation.
14141419 rela_index: Section.RelaIndex.Optional,
1415 result: enum(u8) {
1416 ok,
1417 overflowed,
1418 misaligned,
1419 illegal_dynamic_type,
1420 illegal_dynamic_section,
1421 },
14221420
14231421 /// Determines the section in which this relocation will be placed if it is outstanding.
14241422 ///
......@@ -1449,6 +1447,19 @@ const SymbolReloc = struct {
14491447 },
14501448 };
14511449
1450 const Relaxation = enum(u1) {
1451 none,
1452 };
1453 /// Updates `reloc.type` and `reloc.offset` as needed based on `reloc.bits.relaxation`. It is
1454 /// possible that `reloc` is already relaxed, and may need to be un-relaxed due to a change in
1455 /// the symbol definition.
1456 fn processRelaxation(reloc: *SymbolReloc, elf: *Elf) void {
1457 _ = elf;
1458 switch (reloc.bits.relaxation) {
1459 .none => return,
1460 }
1461 }
1462
14521463 /// Instead of using the ELF relocation enums, we have our own internal representation for
14531464 /// relocation types. This representation is more compact (requiring only 16 bits), and allows
14541465 /// sharing a lot of relocation handling between multiple relocs and target architectures.
......@@ -1464,7 +1475,7 @@ const SymbolReloc = struct {
14641475 /// relocations which write multiple sequences of bits or which need to do unusual arithmetic on
14651476 /// a symbol value. The representation is simply a big enum containing all of these exceptional
14661477 /// cases---see `Special`. This representation is in use when `Type.target == .special`.
1467 const Type = packed struct(u16) {
1478 const Type = packed struct(u18) {
14681479 /// Helper function for constructing a "simple" relocation type. This mainly exists to
14691480 /// improve readability in the relocation lowering logic in `addRelocAssumeCapacity`.
14701481 fn simple(target: Target, action: Simple) SymbolReloc.Type {
......@@ -1493,7 +1504,7 @@ const SymbolReloc = struct {
14931504 /// Otherwise (for "simple" relocations), `Target` indicates the first step in computing the
14941505 /// relocation---whether we care about the target symbol's absolute address, its PC-relative
14951506 /// address, its PLT entry, etc.
1496 const Target = enum(u3) {
1507 const Target = enum(u5) {
14971508 /// This is a "special" relocation whose specific type is in the `action.special` field.
14981509 special,
14991510
......@@ -1501,6 +1512,7 @@ const SymbolReloc = struct {
15011512 abs,
15021513 /// Offset from the relocation itself to the target symbol ("PC-relative").
15031514 rel,
1515
15041516 /// Address of the target symbol's PLT entry.
15051517 ///
15061518 /// If the target symbol does not have a PLT entry, equivalent to `.abs`.
......@@ -1509,12 +1521,35 @@ const SymbolReloc = struct {
15091521 ///
15101522 /// If the target symbol does not have a PLT entry, equivalent to `.rel`.
15111523 pltrel,
1524
15121525 /// Offset of the target TLS symbol from the base of this DSO's own TLS region.
15131526 dtpoff,
15141527 /// Offset of the target TLS symbol from the raw thread pointer.
15151528 tpoff,
1529
15161530 /// Size of the target symbol.
15171531 size,
1532
1533 /// Address of the GOT entry with key `.{ .symbol = target_symbol }`.
1534 gotabs_symbol,
1535 /// Offset from the relocation to the GOT entry with key `.{ .symbol = target_symbol }`.
1536 gotrel_symbol,
1537 /// Offset from the GOT base to the GOT entry with key `.{ .symbol = target_symbol }`.
1538 gotoff_symbol,
1539
1540 /// Address of the GOT entry with key `.{ .tpoff = target_symbol }`.
1541 gotabs_tpoff,
1542 /// Offset from the relocation to the GOT entry with key `.{ .tpoff = target_symbol }`.
1543 gotrel_tpoff,
1544 /// Offset from the GOT base to the GOT entry with key `.{ .tpoff = target_symbol }`.
1545 gotoff_tpoff,
1546
1547 /// Address of the GOT entry with key `.{ .tlsgd0 = target_symbol }`.
1548 gotabs_tlsgd,
1549 /// Offset from the relocation to the GOT entry with key `.{ .tlsgd0 = target_symbol }`.
1550 gotrel_tlsgd,
1551 /// Offset from the GOT base to the GOT entry with key `.{ .tlsgd0 = target_symbol }`.
1552 gotoff_tlsgd,
15181553 };
15191554
15201555 /// For a "simple" relocation, after the initial value is computed according to `Target`, a
......@@ -1701,17 +1736,22 @@ const SymbolReloc = struct {
17011736 larch_b21,
17021737 larch_b26,
17031738 larch_call36,
1739 larch_got_pcala_hi20,
1740 larch_got_pcala64_lo20,
1741 larch_got_pcala64_hi12,
17041742
17051743 sparc_olo10,
17061744 sparc_hix22,
17071745 sparc_le_hix22,
17081746 sparc_wdisp10,
17091747 sparc_wdisp16,
1748 sparc_got_op_lox10,
1749 sparc_got_op_hix22,
17101750
17111751 fn applyInner(
17121752 s: Special,
17131753 elf: *Elf,
1714 target: Symbol.Id,
1754 target_sym: Symbol.Id,
17151755 addend: SymbolReloc.Addend,
17161756 dest_vaddr: u64,
17171757 dest_slice: []u8,
......@@ -1719,7 +1759,7 @@ const SymbolReloc = struct {
17191759 const normal_addend: u64 = @bitCast(addend.normal);
17201760 switch (s) {
17211761 .larch_pcala_hi20 => {
1722 const val = target.value(elf) +% normal_addend;
1762 const val = target_sym.value(elf) +% normal_addend;
17231763 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
17241764 elf.targetStore(inst, .{
17251765 .b0_4 = elf.targetLoad(inst).b0_4,
......@@ -1728,7 +1768,7 @@ const SymbolReloc = struct {
17281768 });
17291769 },
17301770 .larch_pcala64_lo20 => {
1731 const val = target.value(elf) +% normal_addend;
1771 const val = target_sym.value(elf) +% normal_addend;
17321772 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
17331773 elf.targetStore(inst, .{
17341774 .b0_4 = elf.targetLoad(inst).b0_4,
......@@ -1737,7 +1777,7 @@ const SymbolReloc = struct {
17371777 });
17381778 },
17391779 .larch_pcala64_hi12 => {
1740 const val = target.value(elf) +% normal_addend;
1780 const val = target_sym.value(elf) +% normal_addend;
17411781 const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]);
17421782 elf.targetStore(inst, .{
17431783 .b0_9 = elf.targetLoad(inst).b0_9,
......@@ -1746,7 +1786,7 @@ const SymbolReloc = struct {
17461786 });
17471787 },
17481788 .larch_b21, .larch_b26, .larch_call36 => {
1749 const target_vaddr: u64 = elf.pltEntryTargetAddr(target) orelse target.value(elf);
1789 const target_vaddr: u64 = elf.pltEntryTargetAddr(target_sym) orelse target_sym.value(elf);
17501790 const jump_offset: i64 = @bitCast(target_vaddr +% normal_addend -% dest_vaddr);
17511791 if ((jump_offset >> 2) << 2 != jump_offset) {
17521792 return error.RelocationMisaligned;
......@@ -1806,9 +1846,43 @@ const SymbolReloc = struct {
18061846 }
18071847 },
18081848
1849 .larch_got_pcala_hi20 => {
1850 const got_vaddr = elf.shndx.got.vaddr(elf);
1851 const got_offset = elf.got.getIndex(.{ .symbol = target_sym }).? * elf.targetPtrSize();
1852 const val = got_vaddr +% got_offset +% normal_addend;
1853 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
1854 elf.targetStore(inst, .{
1855 .b0_4 = elf.targetLoad(inst).b0_4,
1856 .j20 = link.loongarch.pcalaHi20(val, dest_vaddr),
1857 .b25_31 = elf.targetLoad(inst).b25_31,
1858 });
1859 },
1860 .larch_got_pcala64_lo20 => {
1861 const got_vaddr = elf.shndx.got.vaddr(elf);
1862 const got_offset = elf.got.getIndex(.{ .symbol = target_sym }).? * elf.targetPtrSize();
1863 const val = got_vaddr +% got_offset +% normal_addend;
1864 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
1865 elf.targetStore(inst, .{
1866 .b0_4 = elf.targetLoad(inst).b0_4,
1867 .j20 = link.loongarch.pcala64Lo20(val, dest_vaddr),
1868 .b25_31 = elf.targetLoad(inst).b25_31,
1869 });
1870 },
1871 .larch_got_pcala64_hi12 => {
1872 const got_vaddr = elf.shndx.got.vaddr(elf);
1873 const got_offset = elf.got.getIndex(.{ .symbol = target_sym }).? * elf.targetPtrSize();
1874 const val = got_vaddr +% got_offset +% normal_addend;
1875 const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]);
1876 elf.targetStore(inst, .{
1877 .b0_9 = elf.targetLoad(inst).b0_9,
1878 .k12 = link.loongarch.pcala64Hi12(val, dest_vaddr),
1879 .b22_31 = elf.targetLoad(inst).b22_31,
1880 });
1881 },
1882
18091883 .sparc_olo10 => {
18101884 const addends = addend.sparc_olo10;
1811 const lo10: i64 = @as(u10, @truncate(target.value(elf) +% addends.a));
1885 const lo10: i64 = @as(u10, @truncate(target_sym.value(elf) +% addends.a));
18121886 const simm13 = std.math.cast(i13, lo10 + addends.o) orelse return error.RelocationOverflow;
18131887 const dest_ptr: *align(1) packed struct(u32) {
18141888 simm13: u13,
......@@ -1825,7 +1899,7 @@ const SymbolReloc = struct {
18251899 b22_31: u10,
18261900 } = @ptrCast(dest_slice);
18271901 elf.targetStore(dest_ptr, .{
1828 .imm22 = @truncate(~(target.value(elf) +% normal_addend) >> 10),
1902 .imm22 = @truncate(~(target_sym.value(elf) +% normal_addend) >> 10),
18291903 .b22_31 = elf.targetLoad(dest_ptr).b22_31,
18301904 });
18311905 },
......@@ -1842,12 +1916,12 @@ const SymbolReloc = struct {
18421916 b22_31: u10,
18431917 } = @ptrCast(dest_slice);
18441918 elf.targetStore(dest_ptr, .{
1845 .imm22 = @truncate(~(target.value(elf) +% normal_addend -% tls_size) >> 10),
1919 .imm22 = @truncate(~(target_sym.value(elf) +% normal_addend -% tls_size) >> 10),
18461920 .b22_31 = elf.targetLoad(dest_ptr).b22_31,
18471921 });
18481922 },
18491923 .sparc_wdisp16, .sparc_wdisp10 => {
1850 const target_vaddr: u64 = elf.pltEntryTargetAddr(target) orelse target.value(elf);
1924 const target_vaddr: u64 = elf.pltEntryTargetAddr(target_sym) orelse target_sym.value(elf);
18511925 const jump_offset: i64 = @bitCast(target_vaddr +% normal_addend -% dest_vaddr);
18521926 if ((jump_offset >> 2) << 2 != jump_offset) {
18531927 return error.RelocationMisaligned;
......@@ -1897,10 +1971,120 @@ const SymbolReloc = struct {
18971971 else => unreachable,
18981972 }
18991973 },
1974
1975 .sparc_got_op_lox10 => {
1976 const got_offset = elf.got.getIndex(.{ .symbol = target_sym }).? * elf.targetPtrSize();
1977 const dest_ptr: *align(1) packed struct(u32) {
1978 imm13: u13,
1979 b13_31: u19,
1980 } = @ptrCast(dest_slice);
1981 elf.targetStore(dest_ptr, .{
1982 .imm13 = @as(u10, @truncate(got_offset)),
1983 .b13_31 = elf.targetLoad(dest_ptr).b13_31,
1984 });
1985 },
1986 .sparc_got_op_hix22 => {
1987 const got_offset = elf.got.getIndex(.{ .symbol = target_sym }).? * elf.targetPtrSize();
1988 const dest_ptr: *align(1) packed struct(u32) {
1989 imm22: u22,
1990 b22_31: u10,
1991 } = @ptrCast(dest_slice);
1992 elf.targetStore(dest_ptr, .{
1993 .imm22 = @truncate(got_offset >> 10),
1994 .b22_31 = elf.targetLoad(dest_ptr).b22_31,
1995 });
1996 },
19001997 }
19011998 }
19021999 };
19032000
2001 fn needsGotEntry(t: SymbolReloc.Type) ?enum { symbol, tpoff, tlsgd } {
2002 return switch (t.target) {
2003 .abs,
2004 .rel,
2005 .pltabs,
2006 .pltrel,
2007 .dtpoff,
2008 .tpoff,
2009 .size,
2010 => null,
2011
2012 .gotabs_symbol, .gotrel_symbol, .gotoff_symbol => .symbol,
2013 .gotabs_tpoff, .gotrel_tpoff, .gotoff_tpoff => .tpoff,
2014 .gotabs_tlsgd, .gotrel_tlsgd, .gotoff_tlsgd => .tlsgd,
2015
2016 .special => switch (t.action.special) {
2017 .larch_pcala_hi20,
2018 .larch_pcala64_lo20,
2019 .larch_pcala64_hi12,
2020 .larch_b21,
2021 .larch_b26,
2022 .larch_call36,
2023 .sparc_olo10,
2024 .sparc_hix22,
2025 .sparc_le_hix22,
2026 .sparc_wdisp10,
2027 .sparc_wdisp16,
2028 => null,
2029
2030 .larch_got_pcala_hi20,
2031 .larch_got_pcala64_lo20,
2032 .larch_got_pcala64_hi12,
2033 .sparc_got_op_lox10,
2034 .sparc_got_op_hix22,
2035 => .symbol,
2036 },
2037 };
2038 }
2039
2040 fn dependsOnGotAddress(t: SymbolReloc.Type) bool {
2041 return switch (t.target) {
2042 .abs,
2043 .rel,
2044 .pltabs,
2045 .pltrel,
2046 .dtpoff,
2047 .tpoff,
2048 .size,
2049 => false,
2050
2051 .gotabs_symbol,
2052 .gotrel_symbol,
2053 .gotabs_tpoff,
2054 .gotrel_tpoff,
2055 .gotabs_tlsgd,
2056 .gotrel_tlsgd,
2057 => true,
2058
2059 .gotoff_symbol,
2060 .gotoff_tpoff,
2061 .gotoff_tlsgd,
2062 => false,
2063
2064 .special => switch (t.action.special) {
2065 .larch_pcala_hi20,
2066 .larch_pcala64_lo20,
2067 .larch_pcala64_hi12,
2068 .larch_b21,
2069 .larch_b26,
2070 .larch_call36,
2071 .sparc_olo10,
2072 .sparc_hix22,
2073 .sparc_le_hix22,
2074 .sparc_wdisp10,
2075 .sparc_wdisp16,
2076 => false,
2077
2078 .larch_got_pcala_hi20,
2079 .larch_got_pcala64_lo20,
2080 .larch_got_pcala64_hi12,
2081 .sparc_got_op_lox10,
2082 .sparc_got_op_hix22,
2083 => true,
2084 },
2085 };
2086 }
2087
19042088 fn dependsOnTlsSize(t: SymbolReloc.Type, elf: *const Elf) bool {
19052089 return switch (elf.targetTlsVariant()) {
19062090 // In TLS variant I, the executable's TLS block starts at a fixed offset from the
......@@ -1917,6 +2101,15 @@ const SymbolReloc = struct {
19172101 .pltrel,
19182102 .dtpoff,
19192103 .size,
2104 .gotabs_symbol,
2105 .gotrel_symbol,
2106 .gotoff_symbol,
2107 .gotabs_tpoff,
2108 .gotrel_tpoff,
2109 .gotoff_tpoff,
2110 .gotabs_tlsgd,
2111 .gotrel_tlsgd,
2112 .gotoff_tlsgd,
19202113 => false,
19212114
19222115 .tpoff => true,
......@@ -1931,11 +2124,16 @@ const SymbolReloc = struct {
19312124 .larch_b21,
19322125 .larch_b26,
19332126 .larch_call36,
2127 .larch_got_pcala_hi20,
2128 .larch_got_pcala64_lo20,
2129 .larch_got_pcala64_hi12,
19342130
19352131 .sparc_olo10,
19362132 .sparc_hix22,
19372133 .sparc_wdisp10,
19382134 .sparc_wdisp16,
2135 .sparc_got_op_lox10,
2136 .sparc_got_op_hix22,
19392137 => false,
19402138 },
19412139 },
......@@ -1954,7 +2152,7 @@ const SymbolReloc = struct {
19542152
19552153 fn apply(reloc: *SymbolReloc, elf: *Elf) void {
19562154 const node = reloc.node.unwrap() orelse return; // deleted
1957 if (node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {
2155 if (node.hasMoved(&elf.mf) or reloc.target_symbol.hasMoved(elf)) {
19582156 // There's no point applying the relocation now, because it will be re-applied by
19592157 // `flushMoved` at some point anyway.
19602158 return;
......@@ -1966,7 +2164,7 @@ const SymbolReloc = struct {
19662164 reloc.relaSection(elf).relaSetOffset(elf, rela_index, reloc_addr);
19672165 return;
19682166 }
1969 switch (reloc.result) {
2167 switch (reloc.bits.result) {
19702168 .ok => {},
19712169 .overflowed => elf.overflowed_reloc_count -= 1,
19722170 .misaligned => elf.misaligned_reloc_count -= 1,
......@@ -1977,14 +2175,14 @@ const SymbolReloc = struct {
19772175 }
19782176 if (reloc.applyInner(elf)) {
19792177 @branchHint(.likely);
1980 reloc.result = .ok;
2178 reloc.bits.result = .ok;
19812179 } else |err| switch (err) {
19822180 error.RelocationOverflow => {
1983 reloc.result = .overflowed;
2181 reloc.bits.result = .overflowed;
19842182 elf.overflowed_reloc_count += 1;
19852183 },
19862184 error.RelocationMisaligned => {
1987 reloc.result = .misaligned;
2185 reloc.bits.result = .misaligned;
19882186 elf.misaligned_reloc_count += 1;
19892187 },
19902188 }
......@@ -1995,21 +2193,21 @@ const SymbolReloc = struct {
19952193 const dest_slice = node.slice(&elf.mf)[@intCast(reloc.offset)..];
19962194
19972195 const addend: u64 = @bitCast(reloc.addend);
1998 const target_val: u64 = type: switch (reloc.type.target) {
1999 .abs => reloc.target.value(elf) +% addend,
2000 .rel => reloc.target.value(elf) +% addend -% dest_vaddr,
2196 const target_val: u64 = type: switch (reloc.bits.type.target) {
2197 .abs => reloc.target_symbol.value(elf) +% addend,
2198 .rel => reloc.target_symbol.value(elf) +% addend -% dest_vaddr,
20012199 .pltabs => {
2002 const plt_entry_addr = elf.pltEntryTargetAddr(reloc.target) orelse continue :type .abs;
2200 const plt_entry_addr = elf.pltEntryTargetAddr(reloc.target_symbol) orelse continue :type .abs;
20032201 break :type plt_entry_addr +% addend;
20042202 },
20052203 .pltrel => {
2006 const plt_entry_addr = elf.pltEntryTargetAddr(reloc.target) orelse continue :type .rel;
2204 const plt_entry_addr = elf.pltEntryTargetAddr(reloc.target_symbol) orelse continue :type .rel;
20072205 break :type plt_entry_addr +% addend -% dest_vaddr;
20082206 },
2009 .dtpoff => reloc.target.value(elf) +% addend,
2207 .dtpoff => reloc.target_symbol.value(elf) +% addend,
20102208 .tpoff => switch (elf.targetTlsVariant()) {
2011 .I_original => |tls| tls.tcb_size +% reloc.target.value(elf) +% addend,
2012 .I_modified => |tls| 0 -% tls.tp_off +% reloc.target.value(elf) +% addend,
2209 .I_original => |tls| tls.tcb_size +% reloc.target_symbol.value(elf) +% addend,
2210 .I_modified => |tls| 0 -% tls.tp_off +% reloc.target_symbol.value(elf) +% addend,
20132211 .II => {
20142212 const tls_phndx = elf.getNode(elf.ni.tls.unwrap().?).segment;
20152213 const tls_size: u64 = switch (elf.phdrSlice()) {
......@@ -2018,13 +2216,57 @@ const SymbolReloc = struct {
20182216 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
20192217 },
20202218 };
2021 break :type reloc.target.value(elf) +% addend -% tls_size;
2219 break :type reloc.target_symbol.value(elf) +% addend -% tls_size;
20222220 },
20232221 },
2024 .size => reloc.target.size(elf),
2025 .special => return reloc.type.action.special.applyInner(
2222 .size => reloc.target_symbol.size(elf),
2223
2224 .gotabs_symbol => {
2225 const got_vaddr = elf.shndx.got.vaddr(elf);
2226 const got_offset = elf.got.getIndex(.{ .symbol = reloc.target_symbol }).? * elf.targetPtrSize();
2227 break :type (got_vaddr + got_offset) +% addend;
2228 },
2229 .gotrel_symbol => {
2230 const got_vaddr = elf.shndx.got.vaddr(elf);
2231 const got_offset = elf.got.getIndex(.{ .symbol = reloc.target_symbol }).? * elf.targetPtrSize();
2232 break :type (got_vaddr + got_offset) +% addend -% dest_vaddr;
2233 },
2234 .gotoff_symbol => {
2235 const got_offset = elf.got.getIndex(.{ .symbol = reloc.target_symbol }).? * elf.targetPtrSize();
2236 break :type got_offset +% addend;
2237 },
2238 .gotabs_tpoff => {
2239 const got_vaddr = elf.shndx.got.vaddr(elf);
2240 const got_offset = elf.got.getIndex(.{ .tpoff = reloc.target_symbol }).? * elf.targetPtrSize();
2241 break :type (got_vaddr + got_offset) +% addend;
2242 },
2243 .gotrel_tpoff => {
2244 const got_vaddr = elf.shndx.got.vaddr(elf);
2245 const got_offset = elf.got.getIndex(.{ .tpoff = reloc.target_symbol }).? * elf.targetPtrSize();
2246 break :type (got_vaddr + got_offset) +% addend -% dest_vaddr;
2247 },
2248 .gotoff_tpoff => {
2249 const got_offset = elf.got.getIndex(.{ .tpoff = reloc.target_symbol }).? * elf.targetPtrSize();
2250 break :type got_offset +% addend;
2251 },
2252 .gotabs_tlsgd => {
2253 const got_vaddr = elf.shndx.got.vaddr(elf);
2254 const got_offset = elf.got.getIndex(.{ .tlsgd0 = reloc.target_symbol }).? * elf.targetPtrSize();
2255 break :type (got_vaddr + got_offset) +% addend;
2256 },
2257 .gotrel_tlsgd => {
2258 const got_vaddr = elf.shndx.got.vaddr(elf);
2259 const got_offset = elf.got.getIndex(.{ .tlsgd0 = reloc.target_symbol }).? * elf.targetPtrSize();
2260 break :type (got_vaddr + got_offset) +% addend -% dest_vaddr;
2261 },
2262 .gotoff_tlsgd => {
2263 const got_offset = elf.got.getIndex(.{ .tlsgd0 = reloc.target_symbol }).? * elf.targetPtrSize();
2264 break :type got_offset +% addend;
2265 },
2266
2267 .special => return reloc.bits.type.action.special.applyInner(
20262268 elf,
2027 reloc.target,
2269 reloc.target_symbol,
20282270 reloc.addend,
20292271 dest_vaddr,
20302272 dest_slice,
......@@ -2033,7 +2275,7 @@ const SymbolReloc = struct {
20332275
20342276 // Check for the `R_*_RELATIVE` case now, because it is possible only when no shift or cast
20352277 // is required, meaning we can handle it now and return early.
2036 if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) {
2278 if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target_symbol)) {
20372279 .static => unreachable,
20382280 .dynamic => {
20392281 // The relocation happens at runtime; we just need to update its offset.
......@@ -2046,31 +2288,34 @@ const SymbolReloc = struct {
20462288 // destination slice, we actually want to write it to the runtime relocation entry.
20472289 switch (elf.identClass()) {
20482290 .NONE, _ => unreachable,
2049 .@"32" => assert(reloc.type.action.simple.dest == .@"32"),
2050 .@"64" => assert(reloc.type.action.simple.dest == .@"64"),
2291 .@"32" => assert(reloc.bits.type.action.simple.dest == .@"32"),
2292 .@"64" => assert(reloc.bits.type.action.simple.dest == .@"64"),
20512293 }
2052 assert(reloc.type.action.simple.cast == .unsigned);
2053 assert(reloc.type.action.simple.shift == .@"0");
2294 assert(reloc.bits.type.action.simple.cast == .unsigned);
2295 assert(reloc.bits.type.action.simple.shift == .@"0");
20542296 elf.shndx.rela_dyn.relaSetOffset(elf, rela_index, dest_vaddr);
20552297 elf.shndx.rela_dyn.relaSetAddend(elf, rela_index, target_val);
20562298 return;
20572299 },
20582300 };
20592301
2060 try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian());
2302 try reloc.bits.type.action.simple.write(target_val, dest_slice, elf.targetEndian());
20612303 }
20622304
20632305 fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void {
20642306 assert(index.get(elf) == reloc);
20652307
20662308 reloc.deleteOutputRel(elf);
2067 if (reloc.type.dependsOnTlsSize(elf)) {
2309 if (reloc.bits.type.dependsOnTlsSize(elf)) {
20682310 assert(elf.tls_size_symbol_relocs.swapRemove(index));
20692311 }
2312 if (reloc.bits.type.dependsOnGotAddress()) {
2313 assert(elf.got_relocs.swapRemove(index));
2314 }
20702315
20712316 switch (reloc.prev) {
20722317 .none => {
2073 const first_target_reloc = &reloc.target.index(elf).ptr(elf).first_target_reloc;
2318 const first_target_reloc = &reloc.target_symbol.index(elf).ptr(elf).first_target_reloc;
20742319 assert(first_target_reloc.* == index);
20752320 first_target_reloc.* = reloc.next;
20762321 },
......@@ -2080,7 +2325,7 @@ const SymbolReloc = struct {
20802325 .none => {},
20812326 else => |next| next.get(elf).prev = reloc.prev,
20822327 }
2083 switch (reloc.result) {
2328 switch (reloc.bits.result) {
20842329 .ok => {},
20852330 .overflowed => elf.overflowed_reloc_count -= 1,
20862331 .misaligned => elf.misaligned_reloc_count -= 1,
......@@ -2108,30 +2353,58 @@ const SymbolReloc = struct {
21082353 reloc.rela_index = .none;
21092354 }
21102355
2111 fn updateDynamicReloc(reloc: *SymbolReloc, elf: *Elf) Error!void {
2356 /// Called when the classification of the target symbol changes.
2357 fn updateDynamicReloc(reloc: *SymbolReloc, elf: *Elf, ri: SymbolReloc.Index) Error!void {
21122358 assert(elf.ehdrType() != .REL);
21132359 const reloc_node = reloc.node.unwrap().?; // deleted
21142360
2361 const trace = tracy.trace(@src());
2362 defer trace.end();
2363
21152364 // Delete any current output relocation or error.
2116 switch (reloc.result) {
2365 switch (reloc.bits.result) {
21172366 .ok, .overflowed, .misaligned => {
21182367 reloc.deleteOutputRel(elf);
21192368 },
21202369 .illegal_dynamic_type => {
21212370 elf.illegal_dynamic_type_reloc_count -= 1;
2122 reloc.result = .ok;
2371 reloc.bits.result = .ok;
21232372 },
21242373 .illegal_dynamic_section => {
21252374 elf.illegal_dynamic_section_reloc_count -= 1;
2126 reloc.result = .ok;
2375 reloc.bits.result = .ok;
21272376 },
21282377 }
21292378
2379 {
2380 const gpa = elf.base.comp.gpa;
2381
2382 const old_depends_on_got_address = reloc.bits.type.dependsOnGotAddress();
2383 const old_depends_on_tls_size = reloc.bits.type.dependsOnTlsSize(elf);
2384
2385 reloc.processRelaxation(elf);
2386
2387 const new_depends_on_got_address = reloc.bits.type.dependsOnGotAddress();
2388 const new_depends_on_tls_size = reloc.bits.type.dependsOnTlsSize(elf);
2389
2390 if (old_depends_on_got_address and !new_depends_on_got_address) {
2391 assert(elf.got_relocs.swapRemove(ri));
2392 } else if (!old_depends_on_got_address and new_depends_on_got_address) {
2393 try elf.got_relocs.putNoClobber(gpa, ri, {});
2394 }
2395
2396 if (old_depends_on_tls_size and !new_depends_on_tls_size) {
2397 assert(elf.tls_size_symbol_relocs.swapRemove(ri));
2398 } else if (!old_depends_on_tls_size and new_depends_on_tls_size) {
2399 try elf.tls_size_symbol_relocs.putNoClobber(gpa, ri, {});
2400 }
2401 }
2402
21302403 assert(reloc.rela_index == .none);
21312404
2132 const symbol_class = elf.classifySymbolValue(reloc.target);
2405 const symbol_class = elf.classifySymbolValue(reloc.target_symbol);
21332406
2134 switch (reloc.target.unwrap()) {
2407 switch (reloc.target_symbol.unwrap()) {
21352408 .local => {},
21362409 .global => |gsi| switch (symbol_class) {
21372410 .static => gsi.ptr(elf).status.any_static_target_relocs = true,
......@@ -2152,25 +2425,36 @@ const SymbolReloc = struct {
21522425 } = switch (symbol_class) {
21532426 .static => return,
21542427
2155 .static_relative => switch (reloc.type.target) {
2428 .static_relative => switch (reloc.bits.type.target) {
21562429 // We need a runtime relocation if the relocation depends on our load address, which
21572430 // is the case iff the relocation resolves to the absolute value of a symbol. Relocs
21582431 // which are PC-relative, or which resolve to something other than a symbol address
21592432 // (e.g. a symbol's size), are fine.
21602433
21612434 .pltrel,
2435 .gotrel_symbol,
2436 .gotoff_symbol,
2437 .gotrel_tpoff,
2438 .gotoff_tpoff,
2439 .gotrel_tlsgd,
2440 .gotoff_tlsgd,
21622441 .rel,
21632442 .size,
21642443 .dtpoff,
21652444 .tpoff,
21662445 => return,
21672446
2168 .abs, .pltabs => switch (reloc.type.action.simple.dest.isAddr(elf)) {
2447 .abs,
2448 .pltabs,
2449 .gotabs_symbol,
2450 .gotabs_tpoff,
2451 .gotabs_tlsgd,
2452 => switch (reloc.bits.type.action.simple.dest.isAddr(elf)) {
21692453 true => .relative,
21702454 false => .illegal,
21712455 },
21722456
2173 .special => switch (reloc.type.action.special) {
2457 .special => switch (reloc.bits.type.action.special) {
21742458 .larch_pcala_hi20,
21752459 .larch_pcala64_lo20,
21762460 .larch_pcala64_hi12,
......@@ -2180,6 +2464,11 @@ const SymbolReloc = struct {
21802464 .sparc_wdisp10,
21812465 .sparc_wdisp16,
21822466 .sparc_le_hix22,
2467 .larch_got_pcala_hi20,
2468 .larch_got_pcala64_lo20,
2469 .larch_got_pcala64_hi12,
2470 .sparc_got_op_lox10,
2471 .sparc_got_op_hix22,
21832472 => return,
21842473
21852474 .sparc_olo10,
......@@ -2188,44 +2477,60 @@ const SymbolReloc = struct {
21882477 },
21892478 },
21902479
2191 .dynamic => switch (reloc.type.target) {
2192 // PLT relocations targeting dynamic symbols actually target that symbol's PLT
2193 // entry, so we should emit an `R_*_RELATIVE` relocation instead.
2194 .pltabs => switch (reloc.type.action.simple.dest.isAddr(elf)) {
2480 .dynamic => switch (reloc.bits.type.target) {
2481 // PLT/GOT relocations targeting dynamic symbols actually target that symbol's PLT
2482 // or GOT entry, so we should emit an `R_*_RELATIVE` relocation instead.
2483 .pltabs,
2484 .gotabs_symbol,
2485 .gotabs_tpoff,
2486 .gotabs_tlsgd,
2487 => switch (reloc.bits.type.action.simple.dest.isAddr(elf)) {
21952488 true => .relative,
21962489 false => .illegal,
21972490 },
2198 // ...although PC-relative PLT relocations don't even need that that!
2199 .pltrel => return,
2491 // ...although PC-relative PLT/GOT relocations don't even need that that!
2492 .pltrel,
2493 .gotrel_symbol,
2494 .gotoff_symbol,
2495 .gotrel_tpoff,
2496 .gotoff_tpoff,
2497 .gotrel_tlsgd,
2498 .gotoff_tlsgd,
2499 => return,
22002500 // PC-relative relocations are not valid dynamic relocations.
22012501 .rel => .illegal,
22022502 // On the few targets supporting size relocations, they are valid at runtime.
2203 .size => switch (reloc.type.action.simple.dest) {
2503 .size => switch (reloc.bits.type.action.simple.dest) {
22042504 .@"32" => .size32,
22052505 .@"64" => .size64,
22062506 else => .illegal,
22072507 },
22082508 // Absolute addresses and TLS offsets can be lowered at runtime provided they
22092509 // are address-sized.
2210 .dtpoff => switch (reloc.type.action.simple.dest.isAddr(elf)) {
2510 .dtpoff => switch (reloc.bits.type.action.simple.dest.isAddr(elf)) {
22112511 true => .dtp_off,
22122512 false => .illegal,
22132513 },
2214 .tpoff => switch (reloc.type.action.simple.dest.isAddr(elf)) {
2514 .tpoff => switch (reloc.bits.type.action.simple.dest.isAddr(elf)) {
22152515 true => .tp_off,
22162516 false => .illegal,
22172517 },
2218 .abs => switch (reloc.type.action.simple.dest.isAddr(elf)) {
2518 .abs => switch (reloc.bits.type.action.simple.dest.isAddr(elf)) {
22192519 true => .abs_addr,
22202520 false => .illegal,
22212521 },
22222522
2223 .special => switch (reloc.type.action.special) {
2523 .special => switch (reloc.bits.type.action.special) {
22242524 .larch_b21,
22252525 .larch_b26,
22262526 .larch_call36,
22272527 .sparc_wdisp10,
22282528 .sparc_wdisp16,
2529 .larch_got_pcala_hi20,
2530 .larch_got_pcala64_lo20,
2531 .larch_got_pcala64_hi12,
2532 .sparc_got_op_lox10,
2533 .sparc_got_op_hix22,
22292534 => return,
22302535
22312536 .larch_pcala_hi20,
......@@ -2255,7 +2560,7 @@ const SymbolReloc = struct {
22552560 .abs_addr,
22562561 .dtp_off,
22572562 .tp_off,
2258 => switch (reloc.target.unwrap()) {
2563 => switch (reloc.target_symbol.unwrap()) {
22592564 .local => true,
22602565 .global => |gsi| gsi.resolveAlias(elf).defined(elf),
22612566 },
......@@ -2267,7 +2572,7 @@ const SymbolReloc = struct {
22672572 };
22682573 if (!allow) {
22692574 // ...but in this case we actually do need to trigger an error.
2270 reloc.result = .illegal_dynamic_section;
2575 reloc.bits.result = .illegal_dynamic_section;
22712576 elf.illegal_dynamic_section_reloc_count += 1;
22722577 }
22732578 return;
......@@ -2278,7 +2583,7 @@ const SymbolReloc = struct {
22782583
22792584 const rel_type: MachineRelocType = rel: switch (want_dynamic_reloc) {
22802585 .illegal => {
2281 reloc.result = .illegal_dynamic_type;
2586 reloc.bits.result = .illegal_dynamic_type;
22822587 elf.illegal_dynamic_type_reloc_count += 1;
22832588 return;
22842589 },
......@@ -2292,7 +2597,7 @@ const SymbolReloc = struct {
22922597 const target_dynsym: u32 = switch (want_dynamic_reloc) {
22932598 .illegal => unreachable, // handled above
22942599 .relative => 0,
2295 else => reloc.target.unwrap().global.dynsymIndex(elf).?,
2600 else => reloc.target_symbol.unwrap().global.dynsymIndex(elf).?,
22962601 };
22972602
22982603 try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, 1);
......@@ -2435,305 +2740,98 @@ const GotKey = union(enum) {
24352740 /// Value is the signed offset of the given symbol from the TLS pointer.
24362741 tpoff: Symbol.Id,
24372742
2438 /// Value is the TLS module ID of the DSO we are creating.
2743 /// Value is the TLS module ID for the given STT_TLS symbol. Alternatively, it may be set to
2744 /// `Symbol.Id.null` to resolve to the TLS module ID of this module (for TLSLD relocations).
24392745 ///
2440 /// Used for the first of the two GOT entries generated by a TLSLD relocation.
2441 tlsld0,
2442 /// Value is always 0.
2443 ///
2444 /// Used for the second of the two GOT entries generated by a TLSLD relocation.
2445 tlsld1,
2446
2447 /// Value is the TLS module ID for the given STT_TLS symbol.
2746 /// Alternative
24482747 ///
24492748 /// Used for the first of the two GOT entries generated by a TLSGD relocation.
24502749 tlsgd0: Symbol.Id,
24512750 /// Value is the offset of the given STT_TLS symbol from the base of the per-module TLS area.
2751 /// Alternatively, it may be set to `Symbol.Id.null` to resolve to 0 (for TLSLD relocations).
24522752 ///
24532753 /// Used for the second of the two GOT entries generated by a TLSGD relocation.
24542754 tlsgd1: Symbol.Id,
24552755};
24562756
2457/// A relocation targeting a particular GOT entry.
2458const GotReloc = struct {
2459 /// The node containing this relocation. Possible values are:
2460 /// * An input section
2461 /// * A section
2462 /// * A NAV, UAV, or lazy code/data
2463 /// * `.none`, if this relocation was deleted (in which case it should be ignored)
2464 node: MappedFile.Node.Index.Optional,
2465 /// The offset of the relocation inside of `node`.
2466 offset: u64,
2467 target: GotKey,
2468 addend: i64,
2469 type: GotReloc.Type,
2470 result: enum(u8) { ok, overflowed, misaligned },
2471
2472 /// `GotReloc.Type` has the same structure as `SymbolReloc.Type`, just with different `Target`
2473 /// and `Special` enums---consult doc comments on `SymbolReloc.Type` for an overview.
2474 const Type = packed struct(u16) {
2475 fn simple(target: Target, action: Simple) GotReloc.Type {
2476 assert(target != .special);
2477 return .{ .target = target, .action = .{ .simple = action } };
2478 }
2479
2480 fn special(s: Special) GotReloc.Type {
2481 return .{ .target = .special, .action = .{ .special = s } };
2482 }
2757/// Records all global symbols defined in any DSO we depend on. For each symbol, its name, version,
2758/// type, size, and alignment are all stored, as well as the soname of the DSO defining it. If one
2759/// of these symbols ends up being referenced by the ELF module we are producing, we need this
2760/// information for a few reasons:
2761///
2762/// * If an undefined symbol has an external definition with type `STT_FUNC`, we know to create a
2763/// PLT entry for it.
2764///
2765/// * If an undefined symbol has an external definition with type `STT_OBJECT`, we can create a copy
2766/// relocation for it if necessary.
2767///
2768/// * If an undefined versioned symbol has an external definition, we can add the required entry to
2769/// `.gnu.version_r` because we know the soname of the DSO defining it.
2770///
2771/// * Similarly, if an undefined unversioned symbol matches an external definition of a default
2772/// symbol version, we know to emit the default version for the dynamic symbol table entry even
2773/// though that symbol version was not explicitly requested.
2774///
2775/// * If an undefined symbol does *not* have any external definition, when emitting a dynamic
2776/// executable, we can emit an "undefined global symbol" link error for it.
2777const DsoGlobals = struct {
2778 string_bytes: std.ArrayList(u8),
24832779
2484 target: Target,
2485 action: packed union {
2486 simple: Simple,
2487 special: Special,
2488 },
2780 /// Contains all global symbols defined in any needed DSO.
2781 ///
2782 /// Accessed using `DsoGlobals.Symbol.Adapter`. Adapted key type is `DsoGlobals.Symbol.Key`.
2783 symbols: std.array_hash_map.Custom(DsoGlobals.Symbol, void, void, true),
24892784
2490 /// Like `SymbolReloc.Target`, but for GOT relocations. There are fewer tags because there
2491 /// are fewer different kinds of GOT relocation.
2492 const Target = enum(u3) {
2493 /// This is a "special" relocation whose specific type is in the `action.special` field.
2494 special,
2785 /// Contains all default-version global symbols defined in any needed DSO.
2786 ///
2787 /// Accessed using `DsoGlobals.Symbol.DefaultVersionAdapter`. Adapted key type is `[]const u8`.
2788 ///
2789 /// Stored key is an index into `DsoGlobals.symbols`.
2790 default_sym_vers: std.array_hash_map.Custom(u32, void, void, true),
24952791
2496 /// Absolute address of the GOT entry.
2497 abs,
2498 /// Offset from the relocation itself to the GOT entry ("PC-relative").
2499 rel,
2500 /// Offset from the base of the GOT to the GOT entry.
2501 offset,
2792 /// Index into `DsoGlobals.string_bytes`. This is a different type than `Elf.String` because
2793 /// these string bytes are in a `std.ArrayList` rather than a section of the output binary.
2794 const String = enum(u32) {
2795 _,
2796 fn slice(s: DsoGlobals.String, dso_globals: *const DsoGlobals) [:0]const u8 {
2797 const overlong = dso_globals.string_bytes.items[@backingInt(s)..];
2798 return overlong[0..std.mem.findScalar(u8, overlong, 0).? :0];
2799 }
2800 const Optional = enum(u32) {
2801 none = std.math.maxInt(u32),
2802 _,
2803 fn unwrap(os: DsoGlobals.String.Optional) ?DsoGlobals.String {
2804 return switch (os) {
2805 .none => null,
2806 _ => @fromBackingInt(@backingInt(os)),
2807 };
2808 }
2809 fn wrap(s: DsoGlobals.String) DsoGlobals.String.Optional {
2810 return @bitCast(s);
2811 }
25022812 };
2813 };
25032814
2504 const Simple = SymbolReloc.Type.Simple;
2505
2506 /// Like `SymbolReloc.Special`, but for GOT relocations.
2507 const Special = enum(u13) {
2508 larch_pcala_hi20,
2509 larch_pcala64_lo20,
2510 larch_pcala64_hi12,
2815 const Symbol = struct {
2816 name: DsoGlobals.String,
2817 /// `.none` if the symbol is unversioned.
2818 version: DsoGlobals.String.Optional,
2819 type: std.elf.STT,
2820 soname: Elf.String(.dynstr),
2821 size: u64,
2822 /// This is usually unnecessary, but if a symbol is given a copy relocation (`R_*_COPY`) and
2823 /// so becomes a part of the executable's address space despite being defined by a different
2824 /// DSO, we need to know its alignment requirement so that we don't break other code. This
2825 /// isn't actually stored on the symbol---instead we compute a maximum alignment from the
2826 /// alignment of the section containing the symbol, and the symbol's offset within the
2827 /// section. I know this sounds like a terrible hack, but it is *genuinely* how you're
2828 /// supposed to do this. Copy relocations suck.
2829 alignment: Alignment,
25112830
2512 sparc_op_lox10,
2513 sparc_op_hix22,
2514
2515 fn applyInner(
2516 s: Special,
2517 elf: *Elf,
2518 got_vaddr: u64,
2519 got_offset: u64,
2520 addend: u64,
2521 dest_vaddr: u64,
2522 dest_slice: []u8,
2523 ) error{ RelocationMisaligned, RelocationOverflow }!void {
2524 switch (s) {
2525 .larch_pcala_hi20 => {
2526 const val = got_vaddr +% got_offset +% addend;
2527 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
2528 elf.targetStore(inst, .{
2529 .b0_4 = elf.targetLoad(inst).b0_4,
2530 .j20 = link.loongarch.pcalaHi20(val, dest_vaddr),
2531 .b25_31 = elf.targetLoad(inst).b25_31,
2532 });
2533 },
2534 .larch_pcala64_lo20 => {
2535 const val = got_vaddr +% got_offset +% addend;
2536 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
2537 elf.targetStore(inst, .{
2538 .b0_4 = elf.targetLoad(inst).b0_4,
2539 .j20 = link.loongarch.pcala64Lo20(val, dest_vaddr),
2540 .b25_31 = elf.targetLoad(inst).b25_31,
2541 });
2542 },
2543 .larch_pcala64_hi12 => {
2544 const val = got_vaddr +% got_offset +% addend;
2545 const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]);
2546 elf.targetStore(inst, .{
2547 .b0_9 = elf.targetLoad(inst).b0_9,
2548 .k12 = link.loongarch.pcala64Hi12(val, dest_vaddr),
2549 .b22_31 = elf.targetLoad(inst).b22_31,
2550 });
2551 },
2552 .sparc_op_lox10 => {
2553 const dest_ptr: *align(1) packed struct(u32) {
2554 imm13: u13,
2555 b13_31: u19,
2556 } = @ptrCast(dest_slice);
2557 elf.targetStore(dest_ptr, .{
2558 .imm13 = @as(u10, @truncate(got_offset)),
2559 .b13_31 = elf.targetLoad(dest_ptr).b13_31,
2560 });
2561 },
2562 .sparc_op_hix22 => {
2563 const dest_ptr: *align(1) packed struct(u32) {
2564 imm22: u22,
2565 b22_31: u10,
2566 } = @ptrCast(dest_slice);
2567 elf.targetStore(dest_ptr, .{
2568 .imm22 = @truncate(got_offset >> 10),
2569 .b22_31 = elf.targetLoad(dest_ptr).b22_31,
2570 });
2571 },
2572 }
2573 }
2574 };
2575 };
2576
2577 const Index = enum(u32) {
2578 none = std.math.maxInt(u32),
2579 _,
2580
2581 fn get(index: GotReloc.Index, elf: *Elf) *GotReloc {
2582 return &elf.got_relocs.items[@backingInt(index)];
2583 }
2584 };
2585
2586 fn apply(reloc: *GotReloc, elf: *Elf) void {
2587 assert(elf.ehdrType() != .REL);
2588 const node = reloc.node.unwrap() orelse return; // deleted
2589 if (node.hasMoved(&elf.mf) or elf.shndx.got.node(elf).hasMoved(&elf.mf)) {
2590 // There's no point applying the relocation now, because it will be re-applied by
2591 // `flushMoved` at some point anyway.
2592 return;
2593 }
2594 switch (reloc.result) {
2595 .ok => {},
2596 .overflowed => elf.overflowed_reloc_count -= 1,
2597 .misaligned => elf.misaligned_reloc_count -= 1,
2598 }
2599 if (reloc.applyInner(elf)) {
2600 @branchHint(.likely);
2601 reloc.result = .ok;
2602 } else |err| switch (err) {
2603 error.RelocationOverflow => {
2604 reloc.result = .overflowed;
2605 elf.overflowed_reloc_count += 1;
2606 },
2607 error.RelocationMisaligned => {
2608 reloc.result = .misaligned;
2609 elf.misaligned_reloc_count += 1;
2610 },
2611 }
2612 }
2613 fn applyInner(reloc: *const GotReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void {
2614 const node = reloc.node.unwrap().?;
2615 const dest_vaddr = elf.getNodeVAddr(node) + reloc.offset;
2616 const dest_slice = node.slice(&elf.mf)[@intCast(reloc.offset)..];
2617
2618 const got_vaddr = elf.shndx.got.vaddr(elf);
2619 const got_index: u64 = elf.got.getIndex(reloc.target).?;
2620 const got_offset: u64 = switch (elf.identClass()) {
2621 .NONE, _ => unreachable,
2622 inline else => |class| @sizeOf(class.ElfN().Addr) * got_index,
2623 };
2624 const addend: u64 = @bitCast(reloc.addend);
2625
2626 const target_val: u64 = switch (reloc.type.target) {
2627 .abs => got_vaddr +% got_offset +% addend,
2628 .rel => got_vaddr +% got_offset +% addend -% dest_vaddr,
2629 .offset => got_offset +% addend,
2630 .special => return reloc.type.action.special.applyInner(
2631 elf,
2632 got_vaddr,
2633 got_offset,
2634 addend,
2635 dest_vaddr,
2636 dest_slice,
2637 ),
2638 };
2639 try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian());
2640 }
2641
2642 fn delete(reloc: *GotReloc, elf: *Elf) void {
2643 switch (reloc.result) {
2644 .ok => {},
2645 .overflowed => elf.overflowed_reloc_count -= 1,
2646 .misaligned => elf.misaligned_reloc_count -= 1,
2647 }
2648 reloc.* = .{
2649 .node = .none,
2650 .offset = undefined,
2651 .target = undefined,
2652 .addend = undefined,
2653 .type = undefined,
2654 .result = undefined,
2655 };
2656 }
2657};
2658
2659/// Records all global symbols defined in any DSO we depend on. For each symbol, its name, version,
2660/// type, size, and alignment are all stored, as well as the soname of the DSO defining it. If one
2661/// of these symbols ends up being referenced by the ELF module we are producing, we need this
2662/// information for a few reasons:
2663///
2664/// * If an undefined symbol has an external definition with type `STT_FUNC`, we know to create a
2665/// PLT entry for it.
2666///
2667/// * If an undefined symbol has an external definition with type `STT_OBJECT`, we can create a copy
2668/// relocation for it if necessary.
2669///
2670/// * If an undefined versioned symbol has an external definition, we can add the required entry to
2671/// `.gnu.version_r` because we know the soname of the DSO defining it.
2672///
2673/// * Similarly, if an undefined unversioned symbol matches an external definition of a default
2674/// symbol version, we know to emit the default version for the dynamic symbol table entry even
2675/// though that symbol version was not explicitly requested.
2676///
2677/// * If an undefined symbol does *not* have any external definition, when emitting a dynamic
2678/// executable, we can emit an "undefined global symbol" link error for it.
2679const DsoGlobals = struct {
2680 string_bytes: std.ArrayList(u8),
2681
2682 /// Contains all global symbols defined in any needed DSO.
2683 ///
2684 /// Accessed using `DsoGlobals.Symbol.Adapter`. Adapted key type is `DsoGlobals.Symbol.Key`.
2685 symbols: std.array_hash_map.Custom(DsoGlobals.Symbol, void, void, true),
2686
2687 /// Contains all default-version global symbols defined in any needed DSO.
2688 ///
2689 /// Accessed using `DsoGlobals.Symbol.DefaultVersionAdapter`. Adapted key type is `[]const u8`.
2690 ///
2691 /// Stored key is an index into `DsoGlobals.symbols`.
2692 default_sym_vers: std.array_hash_map.Custom(u32, void, void, true),
2693
2694 /// Index into `DsoGlobals.string_bytes`. This is a different type than `Elf.String` because
2695 /// these string bytes are in a `std.ArrayList` rather than a section of the output binary.
2696 const String = enum(u32) {
2697 _,
2698 fn slice(s: DsoGlobals.String, dso_globals: *const DsoGlobals) [:0]const u8 {
2699 const overlong = dso_globals.string_bytes.items[@backingInt(s)..];
2700 return overlong[0..std.mem.findScalar(u8, overlong, 0).? :0];
2701 }
2702 const Optional = enum(u32) {
2703 none = std.math.maxInt(u32),
2704 _,
2705 fn unwrap(os: DsoGlobals.String.Optional) ?DsoGlobals.String {
2706 return switch (os) {
2707 .none => null,
2708 _ => @fromBackingInt(@backingInt(os)),
2709 };
2710 }
2711 fn wrap(s: DsoGlobals.String) DsoGlobals.String.Optional {
2712 return @bitCast(s);
2713 }
2714 };
2715 };
2716
2717 const Symbol = struct {
2718 name: DsoGlobals.String,
2719 /// `.none` if the symbol is unversioned.
2720 version: DsoGlobals.String.Optional,
2721 type: std.elf.STT,
2722 soname: Elf.String(.dynstr),
2723 size: u64,
2724 /// This is usually unnecessary, but if a symbol is given a copy relocation (`R_*_COPY`) and
2725 /// so becomes a part of the executable's address space despite being defined by a different
2726 /// DSO, we need to know its alignment requirement so that we don't break other code. This
2727 /// isn't actually stored on the symbol---instead we compute a maximum alignment from the
2728 /// alignment of the section containing the symbol, and the symbol's offset within the
2729 /// section. I know this sounds like a terrible hack, but it is *genuinely* how you're
2730 /// supposed to do this. Copy relocations suck.
2731 alignment: Alignment,
2732
2733 const Key = struct {
2734 name: []const u8,
2735 version: ?[]const u8,
2736 };
2831 const Key = struct {
2832 name: []const u8,
2833 version: ?[]const u8,
2834 };
27372835
27382836 const Adapter = struct {
27392837 dso_globals: *const DsoGlobals,
......@@ -3000,6 +3098,12 @@ const AddLocalSymbolOptions = struct {
30003098fn addLocalSymbol(elf: *Elf, opts: AddLocalSymbolOptions) Error!Symbol.LocalIndex {
30013099 const gpa = elf.base.comp.gpa;
30023100
3101 const trace = tracy.trace(@src());
3102 defer trace.end();
3103 trace.addText(opts.name);
3104
3105 assert(opts.shndx != .UNDEF);
3106
30033107 try elf.symtab.ensureUnusedCapacity(gpa, 1);
30043108 try elf.changed_symtab_index.ensureUnusedCapacity(gpa, 1);
30053109 try Section.Index.symtab.node(elf).ensureMinimumSize(gpa, &elf.mf, switch (elf.shdrPtr(.symtab)) {
......@@ -3074,6 +3178,10 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{
30743178} || Error)!Symbol.Global.Index {
30753179 _ = opts.lib_name; // TODO
30763180
3181 const trace = tracy.trace(@src());
3182 defer trace.end();
3183 trace.addText(opts.name);
3184
30773185 log.debug("addGlobalSymbol({q})", .{opts.name});
30783186
30793187 const gpa = elf.base.comp.gpa;
......@@ -4259,6 +4367,9 @@ const Symbol = struct {
42594367 /// Returns `true` if any relocations were updated, in which case relocations targeting
42604368 /// `gsi` must be re-applied by the caller.
42614369 fn updateDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) Error!bool {
4370 const trace = tracy.trace(@src());
4371 defer trace.end();
4372
42624373 if (elf.shndx.dynamic == .UNDEF) return false;
42634374 const sym_id: Symbol.Id = .global(gsi);
42644375
......@@ -4293,8 +4404,8 @@ const Symbol = struct {
42934404 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
42944405 while (ri != .none) {
42954406 const reloc = ri.get(elf);
4296 assert(reloc.target == sym_id);
4297 try reloc.updateDynamicReloc(elf);
4407 assert(reloc.target_symbol == sym_id);
4408 try reloc.updateDynamicReloc(elf, ri);
42984409 ri = reloc.next;
42994410 }
43004411
......@@ -4429,20 +4540,8 @@ const Symbol = struct {
44294540 }
44304541
44314542 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {
4432 const trace = tracy.trace(@src());
4433 defer trace.end();
4434
44354543 const sym_index = sym_id.index(elf);
44364544
4437 trace.addText(name: {
4438 // There isn't a general `name` method on `Symbol.Id` because it would be incorrect
4439 // to ever actually care about the name of a local symbol.
4440 const name: String(.strtab) = switch (elf.symPtr(sym_index)) {
4441 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
4442 };
4443 break :name name.slice(elf);
4444 });
4445
44464545 switch (elf.symPtr(sym_index)) {
44474546 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
44484547 }
......@@ -4467,10 +4566,13 @@ const Symbol = struct {
44674566 }
44684567
44694568 fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
4569 const trace = tracy.trace(@src());
4570 defer trace.end();
4571
44704572 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
44714573 while (ri != .none) {
44724574 const reloc = ri.get(elf);
4473 assert(reloc.target == sym_id);
4575 assert(reloc.target_symbol == sym_id);
44744576 reloc.apply(elf);
44754577 ri = reloc.next;
44764578 }
......@@ -4563,7 +4665,7 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
45634665
45644666 const shndx: Section.Index = switch (resolved_sym.unwrap()) {
45654667 .local => shndx: {
4566 assert(symtab_shndx != .UNDEF);
4668 assert(sym == Symbol.Id.null or symtab_shndx != .UNDEF);
45674669 assert(visibility == .DEFAULT);
45684670 break :shndx symtab_shndx;
45694671 },
......@@ -4683,7 +4785,6 @@ fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.Symbol
46834785 .shndx = shndx,
46844786 }),
46854787 .first_symbol_reloc = .none,
4686 .first_got_reloc = .none,
46874788 };
46884789 elf.base.comp.link_prog_node.increaseEstimatedTotalItems(1);
46894790 }
......@@ -4871,31 +4972,20 @@ fn stringExisting(elf: *Elf, comptime section: StringSection, key: []const u8) S
48714972}
48724973
48734974const StringTable = struct {
4874 map: std.HashMapUnmanaged(u32, void, StringTable.Context, std.hash_map.default_max_load_percentage),
4875
4876 const Context = struct {
4877 slice: []const u8,
4878
4879 pub fn eql(_: Context, lhs_key: u32, rhs_key: u32) bool {
4880 return lhs_key == rhs_key;
4881 }
4882
4883 pub fn hash(ctx: Context, key: u32) u64 {
4884 return std.hash_map.hashString(std.mem.sliceTo(ctx.slice[key..], 0));
4885 }
4886 };
4975 map: std.array_hash_map.Custom(u32, void, void, true),
48874976
48884977 const Adapter = struct {
48894978 slice: []const u8,
48904979
4891 pub fn eql(adapter: Adapter, lhs_key: []const u8, rhs_key: u32) bool {
4980 pub fn eql(adapter: Adapter, lhs_key: []const u8, rhs_key: u32, rhs_index: usize) bool {
4981 _ = rhs_index;
48924982 return std.mem.startsWith(u8, adapter.slice[rhs_key..], lhs_key) and
48934983 adapter.slice[rhs_key + lhs_key.len] == 0;
48944984 }
48954985
4896 pub fn hash(_: Adapter, key: []const u8) u64 {
4986 pub fn hash(_: Adapter, key: []const u8) u32 {
48974987 assert(std.mem.findScalar(u8, key, 0) == null);
4898 return std.hash_map.hashString(key);
4988 return std.array_hash_map.hashString(key);
48994989 }
49004990 };
49014991
......@@ -4909,8 +4999,7 @@ const StringTable = struct {
49094999 fn ensureAdditionalCapacity(st: *StringTable, elf: *Elf, shndx: Section.Index, string_len: usize) Error!void {
49105000 const gpa = elf.base.comp.gpa;
49115001 const ni = shndx.node(elf);
4912 const slice_const = ni.sliceConst(&elf.mf);
4913 try st.map.ensureUnusedCapacityContext(gpa, 1, .{ .slice = slice_const });
5002 try st.map.ensureUnusedCapacity(gpa, 1);
49145003 const size: u64 = switch (elf.shdrPtr(shndx)) {
49155004 inline else => |shdr| elf.targetLoad(&shdr.size),
49165005 };
......@@ -5035,6 +5124,11 @@ fn create(
50355124 },
50365125 .options = options,
50375126 .mf = try .init(file, comp.gpa, io),
5127 .ehdr_fields = .{
5128 .class = class,
5129 .type = @"type",
5130 .machine = machine,
5131 },
50385132 .ni = .{
50395133 .elf = undefined,
50405134 .ehdr = undefined,
......@@ -6787,7 +6881,6 @@ pub fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
67876881 skip_symbol_relocs: MappedFile.Node.Index.Optional = .none,
67886882 first_node_reloc: ?*NodeReloc.Index = null,
67896883 skip_node_relocs: MappedFile.Node.Index.Optional = .none,
6790 first_got_reloc: ?*GotReloc.Index = null,
67916884 } = switch (elf.getNode(ni)) {
67926885 .deleted,
67936886 .archive,
......@@ -6814,18 +6907,15 @@ pub fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
68146907 => unreachable, // cannot contain relocs (.plt, .dynamic, and .eh_frame_hdr unsupported)
68156908 .input_section => |isi| .{
68166909 .first_symbol_reloc = &elf.input_sections.items[@backingInt(isi)].first_symbol_reloc,
6817 .first_got_reloc = &elf.input_sections.items[@backingInt(isi)].first_got_reloc,
68186910 },
68196911 .nav => |nmi| .{
68206912 .first_symbol_reloc = &elf.navs.values()[@backingInt(nmi)].first_symbol_reloc,
6821 .first_got_reloc = &elf.navs.values()[@backingInt(nmi)].first_got_reloc,
68226913 },
68236914 .uav => |umi| .{
68246915 .first_symbol_reloc = &elf.uavs.values()[@backingInt(umi)].first_symbol_reloc,
68256916 },
68266917 inline .lazy_code, .lazy_const_data => |lmi| .{
68276918 .first_symbol_reloc = &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_symbol_reloc,
6828 .first_got_reloc = &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_got_reloc,
68296919 },
68306920 .unit_debug_info_header => |ui| .{
68316921 .first_node_reloc = &elf.dwarf_units[@backingInt(ui)].debug_info_header_first_node_reloc,
......@@ -6893,19 +6983,6 @@ pub fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
68936983 }
68946984 ptr.* = @fromBackingInt(@intCast(elf.node_relocs.items.len));
68956985 }
6896
6897 if (opts.first_got_reloc) |ptr| {
6898 if (ptr.* != .none) {
6899 for (elf.got_relocs.items[@backingInt(ptr.*)..]) |*reloc| {
6900 if (reloc.node != ni.toOptional()) {
6901 if (reloc.node == .none) continue;
6902 break;
6903 }
6904 reloc.delete(elf);
6905 }
6906 }
6907 ptr.* = @fromBackingInt(@intCast(elf.got_relocs.items.len));
6908 }
69096986}
69106987
69116988/// Given that `node` has moved, updates all relocations in `node` as needed. In relocatables, this
......@@ -6918,7 +6995,6 @@ fn flushMovedNodeRelocs(
69186995 skip_symbol_relocs: MappedFile.Node.Index.Optional = .none,
69196996 first_node_reloc: NodeReloc.Index = .none,
69206997 skip_node_relocs: MappedFile.Node.Index.Optional = .none,
6921 first_got_reloc: GotReloc.Index = .none,
69226998 },
69236999) void {
69247000 if (opts.first_symbol_reloc != .none) {
......@@ -6942,20 +7018,12 @@ fn flushMovedNodeRelocs(
69427018 reloc.apply(elf);
69437019 }
69447020 }
6945
6946 if (opts.first_got_reloc != .none) {
6947 for (elf.got_relocs.items[@backingInt(opts.first_got_reloc)..]) |*reloc| {
6948 if (reloc.node != node.toOptional()) {
6949 if (reloc.node == .none) continue;
6950 break;
6951 }
6952 reloc.apply(elf);
6953 }
6954 }
69557021}
69567022
69577023fn identClass(elf: *const Elf) std.elf.CLASS {
6958 return @fromBackingInt(elf.ni.elf.sliceConst(&elf.mf)[std.elf.EI.CLASS]);
7024 // We used to get this value from `elf.ni.ehdr`, but it turns out this function is called often
7025 // enough to see measurable performance gains from memoizing its result on `Elf`.
7026 return elf.ehdr_fields.class;
69597027}
69607028
69617029/// Like `std.elf.ET`, but only includes the ELF machine architectures we support, so that we can
......@@ -6987,24 +7055,14 @@ const EhdrType = enum(u16) {
69877055 }
69887056};
69897057fn ehdrMachine(elf: *const Elf) EhdrMachine {
6990 const ehdr_slice = elf.ni.ehdr.sliceConst(&elf.mf);
6991 switch (elf.identClass()) {
6992 .NONE, _ => unreachable,
6993 inline else => |class| {
6994 const ehdr: *const class.ElfN().Ehdr = @ptrCast(@alignCast(ehdr_slice));
6995 return @bitCast(elf.targetLoad(&ehdr.machine));
6996 },
6997 }
7058 // We used to get this value from `elf.ni.ehdr`, but it turns out this function is called often
7059 // enough to see measurable performance gains from memoizing its result on `Elf`.
7060 return elf.ehdr_fields.machine;
69987061}
69997062fn ehdrType(elf: *const Elf) EhdrType {
7000 const ehdr_slice = elf.ni.ehdr.sliceConst(&elf.mf);
7001 switch (elf.identClass()) {
7002 .NONE, _ => unreachable,
7003 inline else => |class| {
7004 const ehdr: *const class.ElfN().Ehdr = @ptrCast(@alignCast(ehdr_slice));
7005 return @bitCast(elf.targetLoad(&ehdr.type));
7006 },
7007 }
7063 // We used to get this value from `elf.ni.ehdr`, but it turns out this function is called often
7064 // enough to see measurable performance gains from memoizing its result on `Elf`.
7065 return elf.ehdr_fields.type;
70087066}
70097067
70107068fn baseLoadableSegmentVAddr(elf: *const Elf) u64 {
......@@ -7227,11 +7285,17 @@ const SymPtr = union(std.elf.CLASS) {
72277285 @"32": *std.elf.Elf32.Sym,
72287286 @"64": *std.elf.Elf64.Sym,
72297287};
7230fn symPtr(elf: *Elf, index: Symbol.Index) SymPtr {
7288/// This function is marked as `inline` because performance measurements (specifically, using this
7289/// linker to link the Zig compiler against a static build of LLVM 23) show a measurable performance
7290/// improvement from doing so---it seems that this is one of the hottest functions in this linker!
7291inline fn symPtr(elf: *Elf, index: Symbol.Index) SymPtr {
72317292 const raw_slice = Section.Index.symtab.node(elf).slice(&elf.mf);
7232 switch (elf.shdrPtr(.symtab)) {
7233 inline else => |shdr, class| {
7234 const size = elf.targetLoad(&shdr.size);
7293 // Avoid calling `shdrPtr` here: this function is extremely hot, so having to locate the shdr
7294 // actually noticably affects performance!
7295 switch (elf.identClass()) {
7296 .NONE, _ => unreachable,
7297 inline else => |class| {
7298 const size = elf.symtab.items.len * @sizeOf(class.ElfN().Sym);
72357299 const slice: []class.ElfN().Sym = @ptrCast(@alignCast(raw_slice[0..@intCast(size)]));
72367300 return @unionInit(SymPtr, @tagName(class), &slice[@backingInt(index)]);
72377301 },
......@@ -7496,7 +7560,6 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
74967560 .shndx = shndx,
74977561 }),
74987562 .first_symbol_reloc = .none,
7499 .first_got_reloc = .none,
75007563 };
75017564 }
75027565 return nmi;
......@@ -7651,6 +7714,10 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void
76517714 }
76527715}
76537716fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void {
7717 const trace = tracy.trace(@src());
7718 defer trace.end();
7719 trace.addTextFmt("{f}", .{path.fmtEscapeString()});
7720
76547721 const comp = elf.base.comp;
76557722 const gpa = comp.gpa;
76567723 const diags = &comp.link_diags;
......@@ -7730,6 +7797,10 @@ fn loadObject(
77307797 fr: *Io.File.Reader,
77317798 fl: MappedFile.Node.FileLocation,
77327799) LoadParseInputError!void {
7800 const trace = tracy.trace(@src());
7801 defer trace.end();
7802 trace.addTextFmt("{f}{f}", .{ path.fmtEscapeString(), fmtMemberString(member) });
7803
77337804 const comp = elf.base.comp;
77347805 const gpa = comp.gpa;
77357806 const diags = &comp.link_diags;
......@@ -7881,440 +7952,492 @@ fn loadObject(
78817952 defer gpa.free(shstrtab);
78827953 try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
78837954 try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
7884 for (sections[1..]) |*section| {
7885 if (section.shdr.name >= shstrtab.len) continue;
7886 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
7887 if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue;
7888 const sect_type = section.shdr.type;
7889 const opts: struct {
7890 shndx: Section.Index,
7891 node_fixed: bool,
7892 } = switch (sect_type) {
7893 else => continue,
7894 .PROGBITS, .NOBITS, .X86_64_UNWIND => opts: {
7895 const shndx = elf.mapInputSection(.{
7896 .name = name,
7897 .flags = section.shdr.flags.shf,
7898 .entsize = section.shdr.entsize,
7899 .type = section.shdr.type,
7900 }) catch |err| switch (err) {
7901 else => |e| return e,
7902 error.StripSection => continue,
7903 error.TlsSectionUnavailable => {
7904 if (section.shdr.size == 0) continue;
7905
7906 return diags.failParse(
7907 path,
7908 "thread-local storage section '{s}' is incompatible with '-fsingle-threaded'",
7909 .{name},
7910 );
7911 },
7912 error.UnsupportedSectionFlags => if (!section.shdr.flags.shf.ALLOC) {
7913 // It probably doesn't matter, just skip this section.
7914 continue;
7915 } else return diags.failParse(
7916 path,
7917 "unsupported flags for section '{s}'",
7918 .{name},
7919 ),
7920 error.SectionTypeConflict => if (!section.shdr.flags.shf.ALLOC) {
7921 // It probably doesn't matter, just skip this section.
7922 continue;
7923 } else return diags.failParse(
7924 path,
7925 "type of section '{s}' conflicts with other inputs",
7926 .{name},
7927 ),
7928 error.SectionFlagsConflict => if (!section.shdr.flags.shf.ALLOC) {
7929 // It probably doesn't matter, just skip this section.
7955
7956 {
7957 const sections_trace = tracy.traceNamed(@src(), "scan input sections");
7958 defer sections_trace.end();
7959 for (sections[1..]) |*section| {
7960 const section_trace = tracy.traceNamed(@src(), "input section");
7961 defer section_trace.end();
7962 if (section.shdr.name >= shstrtab.len) continue;
7963 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
7964 section_trace.addText(name);
7965 if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue;
7966 const sect_type = section.shdr.type;
7967 const opts: struct {
7968 shndx: Section.Index,
7969 node_fixed: bool,
7970 } = switch (sect_type) {
7971 else => continue,
7972 .PROGBITS, .NOBITS, .X86_64_UNWIND => opts: {
7973 const shndx = elf.mapInputSection(.{
7974 .name = name,
7975 .flags = section.shdr.flags.shf,
7976 .entsize = section.shdr.entsize,
7977 .type = section.shdr.type,
7978 }) catch |err| switch (err) {
7979 else => |e| return e,
7980 error.StripSection => continue,
7981 error.TlsSectionUnavailable => {
7982 if (section.shdr.size == 0) continue;
7983
7984 return diags.failParse(
7985 path,
7986 "thread-local storage section '{s}' is incompatible with '-fsingle-threaded'",
7987 .{name},
7988 );
7989 },
7990 error.UnsupportedSectionFlags => if (!section.shdr.flags.shf.ALLOC) {
7991 // It probably doesn't matter, just skip this section.
7992 continue;
7993 } else return diags.failParse(
7994 path,
7995 "unsupported flags for section '{s}'",
7996 .{name},
7997 ),
7998 error.SectionTypeConflict => if (!section.shdr.flags.shf.ALLOC) {
7999 // It probably doesn't matter, just skip this section.
8000 continue;
8001 } else return diags.failParse(
8002 path,
8003 "type of section '{s}' conflicts with other inputs",
8004 .{name},
8005 ),
8006 error.SectionFlagsConflict => if (!section.shdr.flags.shf.ALLOC) {
8007 // It probably doesn't matter, just skip this section.
8008 continue;
8009 } else return diags.failParse(
8010 path,
8011 "flags of section '{s}' conflict with other inputs",
8012 .{name},
8013 ),
8014 error.UnsupportedNobitsSection => return diags.failParse(
8015 path,
8016 "unsupported section type SHT_NOBITS for non-bss section '{s}'",
8017 .{name},
8018 ),
8019 };
8020 if (section.shdr.flags.shf.COMPRESSED) {
8021 // SHF_COMPRESSED is only allowed on non-alloc sections.
8022 if (section.shdr.flags.shf.ALLOC) return diags.failParse(
8023 path,
8024 "section '{s}' has conflicting flags SHF_ALLOC and SHF_COMPRESSED",
8025 .{name},
8026 );
8027 // TODO: handle compressed input sections. We'll need to set a flag to
8028 // indicate that `flushInputSection` needs to decompress the section.
8029 // But because this section isn't SHF_ALLOC, it's probably okay to just
8030 // skip it for now.
79308031 continue;
7931 } else return diags.failParse(
7932 path,
7933 "flags of section '{s}' conflict with other inputs",
7934 .{name},
7935 ),
7936 error.UnsupportedNobitsSection => return diags.failParse(
7937 path,
7938 "unsupported section type SHT_NOBITS for non-bss section '{s}'",
7939 .{name},
7940 ),
8032 }
8033 break :opts .{
8034 .shndx = shndx,
8035 // For well-known sections, we know that it's fine to have e.g. random
8036 // padding, so there's no need to make the sections fixed. For custom
8037 // sections, however, we do want fixed nodes to avoid padding.
8038 .node_fixed = shndx != .text and
8039 shndx != .rodata and
8040 shndx != .data and
8041 shndx != .data_rel_ro and
8042 shndx != elf.shndx.tdata,
8043 };
8044 },
8045 inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{
8046 .shndx = shndx: {
8047 // TODO: the input section name may include a "priority" value between 1
8048 // and 65535 which should affect the order we assemble input sections in
8049 const init_fini_section_name: []const u8 = switch (@"type") {
8050 .INIT_ARRAY => "init_array",
8051 .FINI_ARRAY => "fini_array",
8052 .PREINIT_ARRAY => "preinit_array",
8053 else => comptime unreachable,
8054 };
8055 const shndx: *Section.Index = &@field(elf.shndx, init_fini_section_name);
8056 const need_addralign: u8 = switch (class) {
8057 .NONE, _ => unreachable,
8058 .@"32" => 4,
8059 .@"64" => 8,
8060 };
8061 if (section.shdr.addralign != need_addralign) {
8062 return diags.failParse(path, "bad addralign on {t} shdr", .{@"type"});
8063 }
8064 if (shndx.* == .UNDEF) {
8065 try elf.createInitFiniArraySection(shndx, init_fini_section_name, @"type");
8066 }
8067 switch (elf.shdrPtr(shndx.*)) {
8068 inline else => |shdr| {
8069 const old_size = elf.targetLoad(&shdr.size);
8070 const new_size = old_size + section.shdr.size;
8071 elf.targetStore(&shdr.size, @intCast(new_size));
8072 elf.updateInitFiniArraySectionSize(shndx.*, init_fini_section_name);
8073 },
8074 }
8075 break :shndx shndx.*;
8076 },
8077 // This node must be fixed to prevent padding from being added between different
8078 // INIT_ARRAY/FINI_ARRAY/PREINIT_ARRAY input sections.
8079 .node_fixed = true,
8080 },
8081 };
8082 const need_align: Alignment = .fromByteUnits(
8083 std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))),
8084 );
8085
8086 try opts.shndx.ensureAligned(elf, need_align);
8087
8088 if (sect_type == .NOBITS) {
8089 const offset: u64 = switch (elf.shdrPtr(opts.shndx)) {
8090 inline else => |shdr| offset: {
8091 // Bump our BSS output section size
8092 const old_size: u64 = elf.targetLoad(&shdr.size);
8093 const offset = need_align.forward(old_size);
8094 elf.targetStore(&shdr.size, @intCast(offset + section.shdr.size));
8095 break :offset offset;
8096 },
79418097 };
7942 if (section.shdr.flags.shf.COMPRESSED) {
7943 // SHF_COMPRESSED is only allowed on non-alloc sections.
7944 if (section.shdr.flags.shf.ALLOC) return diags.failParse(
7945 path,
7946 "section '{s}' has conflicting flags SHF_ALLOC and SHF_COMPRESSED",
7947 .{name},
7948 );
7949 // TODO: handle compressed input sections. We'll need to set a flag to
7950 // indicate that `flushInputSection` needs to decompress the section.
7951 // But because this section isn't SHF_ALLOC, it's probably okay to just
7952 // skip it for now.
7953 continue;
8098
8099 section.placement = .{ .nobits = .{
8100 .shndx = opts.shndx,
8101 .offset = offset,
8102 } };
8103 if (opts.shndx == elf.shndx.bss) {
8104 input_has_bss = true;
8105 } else if (opts.shndx == elf.shndx.tbss) {
8106 input_has_tbss = true;
8107 } else {
8108 assert(elf.ehdrType() == .REL);
79548109 }
7955 break :opts .{
7956 .shndx = shndx,
7957 // For well-known sections, we know that it's fine to have e.g. random
7958 // padding, so there's no need to make the sections fixed. For custom
7959 // sections, however, we do want fixed nodes to avoid padding.
7960 .node_fixed = shndx != .text and
7961 shndx != .rodata and
7962 shndx != .data and
7963 shndx != .data_rel_ro and
7964 shndx != elf.shndx.tdata,
7965 };
7966 },
7967 inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{
7968 .shndx = shndx: {
7969 // TODO: the input section name may include a "priority" value between 1
7970 // and 65535 which should affect the order we assemble input sections in
7971 const init_fini_section_name: []const u8 = switch (@"type") {
7972 .INIT_ARRAY => "init_array",
7973 .FINI_ARRAY => "fini_array",
7974 .PREINIT_ARRAY => "preinit_array",
7975 else => comptime unreachable,
7976 };
7977 const shndx: *Section.Index = &@field(elf.shndx, init_fini_section_name);
7978 const need_addralign: u8 = switch (class) {
7979 .NONE, _ => unreachable,
7980 .@"32" => 4,
7981 .@"64" => 8,
8110
8111 continue;
8112 }
8113
8114 const add_node_opts: MappedFile.Node.AddOptions = .{
8115 .size = need_align.forward(section.shdr.size),
8116 .alignment = need_align,
8117 .moved = true, // see assert at end of `flushInputSection`
8118 };
8119 const ni = elf.addNodeAssumeCapacity(
8120 if (opts.node_fixed) ni: {
8121 const shndx_ni = opts.shndx.node(elf);
8122 const after_oni: MappedFile.Node.Index.Optional = after: {
8123 var node_it = shndx_ni.last(&elf.mf);
8124 while (node_it.unwrap()) |cur_ni| {
8125 switch (cur_ni.position(&elf.mf)) {
8126 .header => break :after .wrap(cur_ni), // last header
8127 .footer, .floating => node_it = cur_ni.prev(&elf.mf),
8128 }
8129 } else {
8130 break :after .none;
8131 }
79828132 };
7983 if (section.shdr.addralign != need_addralign) {
7984 return diags.failParse(path, "bad addralign on {t} shdr", .{@"type"});
7985 }
7986 if (shndx.* == .UNDEF) {
7987 try elf.createInitFiniArraySection(shndx, init_fini_section_name, @"type");
7988 }
7989 switch (elf.shdrPtr(shndx.*)) {
7990 inline else => |shdr| {
7991 const old_size = elf.targetLoad(&shdr.size);
7992 const new_size = old_size + section.shdr.size;
7993 elf.targetStore(&shdr.size, @intCast(new_size));
7994 elf.updateInitFiniArraySectionSize(shndx.*, init_fini_section_name);
7995 },
7996 }
7997 break :shndx shndx.*;
8133 break :ni try shndx_ni.addHeaderChildAfter(gpa, &elf.mf, after_oni, add_node_opts);
8134 } else try opts.shndx.node(elf).addFloatingChild(gpa, &elf.mf, add_node_opts),
8135 .{ .input_section = @fromBackingInt(@intCast(elf.input_sections.items.len)) },
8136 );
8137 section.placement = .{ .progbits = @fromBackingInt(@intCast(elf.input_sections.items.len)) };
8138 elf.input_sections.addOneAssumeCapacity().* = .{
8139 .input = input_index,
8140 .file_location = .{
8141 .offset = fl.offset + section.shdr.offset,
8142 .size = if (section.shdr.type == .NOBITS) 0 else section.shdr.size,
79988143 },
7999 // This node must be fixed to prevent padding from being added between different
8000 // INIT_ARRAY/FINI_ARRAY/PREINIT_ARRAY input sections.
8001 .node_fixed = true,
8002 },
8144 // The section vaddr is initially 0, because the symbol addresses are
8145 // zero-based. This will eventually be updated by `flushMoved`.
8146 .vaddr = 0,
8147 .node = ni,
8148 .first_symbol_reloc = .none,
8149 };
8150 elf.input_prog_node.increaseEstimatedTotalItems(1);
8151 }
8152 }
8153
8154 // This array will exclude the "null" symbol (symtab index 0).
8155 var symmap: std.ArrayList(Symbol.Id) = .empty;
8156 defer symmap.deinit(gpa);
8157
8158 const maybe_symtab_shndx: ?usize = for (sections[1..], 1..) |*symtab, symtab_shndx| {
8159 if (symtab.shdr.type == .SYMTAB) break symtab_shndx;
8160 } else null;
8161
8162 if (maybe_symtab_shndx) |symtab_shndx| {
8163 const symtab = &sections[symtab_shndx];
8164
8165 const symbols_trace = tracy.traceNamed(@src(), "scan input symbols");
8166 defer symbols_trace.end();
8167
8168 for (sections[symtab_shndx + 1 ..]) |*other_section| {
8169 if (other_section.shdr.type == .SYMTAB) {
8170 return diags.failParse(path, "multiple SHT_SYMTAB sections", .{});
8171 }
8172 }
8173
8174 if (symtab.shdr.entsize < @sizeOf(ElfN.Sym))
8175 return diags.failParse(path, "unsupported symtab entsize", .{});
8176
8177 const strtab = strtab: {
8178 if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum)
8179 return diags.failParse(path, "missing symbol names", .{});
8180 const shdr = &sections[symtab.shdr.link].shdr;
8181 if (shdr.type != .STRTAB)
8182 return diags.failParse(path, "invalid strtab type", .{});
8183 const strtab = try gpa.alloc(u8, @intCast(shdr.size));
8184 errdefer gpa.free(strtab);
8185 try fr.seekTo(fl.offset + shdr.offset);
8186 try r.readSliceAll(strtab);
8187 break :strtab strtab;
80038188 };
8004 const need_align: Alignment = .fromByteUnits(
8005 std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))),
8189 defer gpa.free(strtab);
8190
8191 const symnum = std.math.divExact(
8192 u32,
8193 @intCast(symtab.shdr.size),
8194 @intCast(symtab.shdr.entsize),
8195 ) catch return diags.failParse(
8196 path,
8197 "symtab section size (0x{x}) is not a multiple of entsize (0x{x})",
8198 .{ symtab.shdr.size, symtab.shdr.entsize },
80068199 );
80078200
8008 try opts.shndx.ensureAligned(elf, need_align);
8201 try symmap.resize(gpa, symnum -| 1); // -1 to exclude "null" symbol
8202 try fr.seekTo(fl.offset + symtab.shdr.offset + symtab.shdr.entsize); // +entsize to skip "null" symbol
8203 for (symmap.items) |*si| {
8204 si.* = .null;
8205 const input_sym = try r.peekStruct(ElfN.Sym, target_endian);
8206 try r.discardAll64(symtab.shdr.entsize);
8207 if (input_sym.name >= strtab.len or input_sym.shndx >= ehdr.shnum) continue;
8208
8209 const name = std.mem.sliceTo(strtab[input_sym.name..], 0);
8210
8211 const symbol_trace = tracy.traceNamed(@src(), "input symbol");
8212 defer symbol_trace.end();
8213 symbol_trace.addText(name);
8214
8215 const sym_type: std.elf.STT = switch (input_sym.info.type) {
8216 .NOTYPE, .OBJECT, .FUNC, .TLS => |t| t,
8217 .SECTION => .NOTYPE,
8218 .FILE, .COMMON, _ => continue,
8219 };
80098220
8010 if (sect_type == .NOBITS) {
8011 const offset: u64 = switch (elf.shdrPtr(opts.shndx)) {
8012 inline else => |shdr| offset: {
8013 // Bump our BSS output section size
8014 const old_size: u64 = elf.targetLoad(&shdr.size);
8015 const offset = need_align.forward(old_size);
8016 elf.targetStore(&shdr.size, @intCast(offset + section.shdr.size));
8017 break :offset offset;
8221 if (input_sym.shndx == std.elf.SHN_UNDEF) switch (input_sym.info.bind) {
8222 else => |bind| return diags.failParse(
8223 path,
8224 "symbol '{s}' has unsupported binding (0x{x})",
8225 .{ name, bind },
8226 ),
8227 .LOCAL => continue,
8228 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
8229 si.* = .global(elf.addGlobalSymbol(.{
8230 .node = .none,
8231 .name = name,
8232 .value = input_sym.value,
8233 .size = input_sym.size,
8234 .type = sym_type,
8235 .bind = switch (bind) {
8236 .WEAK, .GNU_UNIQUE => .weak,
8237 .GLOBAL => .strong,
8238 else => unreachable,
8239 },
8240 .visibility = input_sym.other.visibility,
8241 .shndx = .UNDEF,
8242 }) catch |err| switch (err) {
8243 error.MultipleDefinitions => unreachable, // shndx is .UNDEF
8244 error.MultipleDefaultVersions => unreachable, // shndx is .UNDEF
8245 error.UndefinedDefaultVersion => return diags.failParse(
8246 path,
8247 "symbol '{s}' specifies default version without defining it",
8248 .{name},
8249 ),
8250 else => |e| return e,
8251 });
8252 continue;
80188253 },
80198254 };
80208255
8021 section.placement = .{ .nobits = .{
8022 .shndx = opts.shndx,
8023 .offset = offset,
8024 } };
8025 if (opts.shndx == elf.shndx.bss) {
8026 input_has_bss = true;
8027 } else if (opts.shndx == elf.shndx.tbss) {
8028 input_has_tbss = true;
8029 } else {
8030 assert(elf.ehdrType() == .REL);
8031 }
8256 const symbol_oni: MappedFile.Node.Index.Optional, const output_shndx: Section.Index, const symbol_value: u64 = switch (sections[input_sym.shndx].placement) {
8257 .none => continue,
8258 .progbits => |isi| .{
8259 .wrap(isi.node(elf)),
8260 elf.getNodeShndx(isi.node(elf)),
8261 input_sym.value,
8262 },
8263 .nobits => |nobits| sym: {
8264 const section_base: u64 = switch (elf.ehdrType()) {
8265 .REL => 0,
8266 .EXEC, .DYN => base: {
8267 if (elf.ni.tls != .none and
8268 nobits.shndx == elf.shndx.tbss)
8269 {
8270 break :base elf.tbssOffset();
8271 }
8272 assert(nobits.shndx == elf.shndx.bss);
8273 break :base nobits.shndx.vaddr(elf);
8274 },
8275 };
8276 break :sym .{
8277 .none,
8278 nobits.shndx,
8279 section_base + nobits.offset + input_sym.value,
8280 };
8281 },
8282 };
80328283
8033 continue;
8284 switch (input_sym.info.bind) {
8285 else => |bind| return diags.failParse(
8286 path,
8287 "symbol '{s}' has unsupported binding (0x{x})",
8288 .{ name, bind },
8289 ),
8290 .LOCAL => {
8291 const lsi = try elf.addLocalSymbol(.{
8292 .node = symbol_oni,
8293 .name = name,
8294 .value = symbol_value,
8295 .size = input_sym.size,
8296 .type = sym_type,
8297 .shndx = output_shndx,
8298 });
8299 si.* = .local(lsi);
8300 },
8301 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
8302 si.* = .global(elf.addGlobalSymbol(.{
8303 .node = symbol_oni,
8304 .name = name,
8305 .value = symbol_value,
8306 .size = input_sym.size,
8307 .type = sym_type,
8308 .bind = switch (bind) {
8309 .WEAK, .GNU_UNIQUE => .weak,
8310 .GLOBAL => .strong,
8311 else => unreachable,
8312 },
8313 .visibility = input_sym.other.visibility,
8314 .shndx = output_shndx,
8315 }) catch |err| switch (err) {
8316 error.MultipleDefinitions => return diags.failParse(
8317 path,
8318 "multiple definitions of '{s}'",
8319 .{name},
8320 ),
8321 error.MultipleDefaultVersions => return diags.failParse(
8322 path,
8323 "multiple default versions of '{s}'",
8324 .{parseVersionedSymbolName(name).name},
8325 ),
8326 error.UndefinedDefaultVersion => unreachable, // shndx is not `.UNDEF` (i.e. the symbol is defined)
8327 else => |e| return e,
8328 });
8329 },
8330 }
80348331 }
8035
8036 const add_node_opts: MappedFile.Node.AddOptions = .{
8037 .size = need_align.forward(section.shdr.size),
8038 .alignment = need_align,
8039 .moved = true, // see assert at end of `flushInputSection`
8040 };
8041 const ni = elf.addNodeAssumeCapacity(
8042 if (opts.node_fixed) ni: {
8043 const shndx_ni = opts.shndx.node(elf);
8044 const after_oni: MappedFile.Node.Index.Optional = after: {
8045 const last_ni = shndx_ni.last(&elf.mf).unwrap() orelse break :after .none;
8046 break :after switch (last_ni.position(&elf.mf)) {
8047 .header => .wrap(last_ni),
8048 .footer, .floating => .none,
8049 };
8050 };
8051 break :ni try shndx_ni.addHeaderChildAfter(gpa, &elf.mf, after_oni, add_node_opts);
8052 } else try opts.shndx.node(elf).addFloatingChild(gpa, &elf.mf, add_node_opts),
8053 .{ .input_section = @fromBackingInt(@intCast(elf.input_sections.items.len)) },
8054 );
8055 section.placement = .{ .progbits = @fromBackingInt(@intCast(elf.input_sections.items.len)) };
8056 elf.input_sections.addOneAssumeCapacity().* = .{
8057 .input = input_index,
8058 .file_location = .{
8059 .offset = fl.offset + section.shdr.offset,
8060 .size = if (section.shdr.type == .NOBITS) 0 else section.shdr.size,
8061 },
8062 // The section vaddr is initially 0, because the symbol addresses are
8063 // zero-based. This will eventually be updated by `flushMoved`.
8064 .vaddr = 0,
8065 .node = ni,
8066 .first_symbol_reloc = .none,
8067 .first_got_reloc = .none,
8068 };
8069 elf.input_prog_node.increaseEstimatedTotalItems(1);
80708332 }
8071 var symmap: std.ArrayList(Symbol.Id) = .empty;
8072 defer symmap.deinit(gpa);
8073 for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) {
8074 else => {},
8075 .SYMTAB => {
8076 if (symtab.shdr.entsize < @sizeOf(ElfN.Sym))
8077 return diags.failParse(path, "unsupported symtab entsize", .{});
8078 const strtab = strtab: {
8079 if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum)
8080 return diags.failParse(path, "missing symbol names", .{});
8081 const shdr = &sections[symtab.shdr.link].shdr;
8082 if (shdr.type != .STRTAB)
8083 return diags.failParse(path, "invalid strtab type", .{});
8084 const strtab = try gpa.alloc(u8, @intCast(shdr.size));
8085 errdefer gpa.free(strtab);
8086 try fr.seekTo(fl.offset + shdr.offset);
8087 try r.readSliceAll(strtab);
8088 break :strtab strtab;
8089 };
8090 defer gpa.free(strtab);
8091 const symnum = std.math.sub(u32, std.math.divExact(
8092 u32,
8093 @intCast(symtab.shdr.size),
8094 @intCast(symtab.shdr.entsize),
8095 ) catch return diags.failParse(
8096 path,
8097 "symtab section size (0x{x}) is not a multiple of entsize (0x{x})",
8098 .{ symtab.shdr.size, symtab.shdr.entsize },
8099 ), 1) catch continue;
8100 symmap.clearRetainingCapacity();
8101 try symmap.resize(gpa, symnum);
8102 try fr.seekTo(fl.offset + symtab.shdr.offset + symtab.shdr.entsize);
8103 for (symmap.items) |*si| {
8104 si.* = .null;
8105 const input_sym = try r.peekStruct(ElfN.Sym, target_endian);
8106 try r.discardAll64(symtab.shdr.entsize);
8107 if (input_sym.name >= strtab.len or input_sym.shndx >= ehdr.shnum) continue;
8108
8109 const name = std.mem.sliceTo(strtab[input_sym.name..], 0);
8110
8111 const sym_type: std.elf.STT = switch (input_sym.info.type) {
8112 .NOTYPE, .OBJECT, .FUNC, .TLS => |t| t,
8113 .SECTION => .NOTYPE,
8114 .FILE, .COMMON, _ => continue,
8115 };
81168333
8117 if (input_sym.shndx == std.elf.SHN_UNDEF) switch (input_sym.info.bind) {
8118 else => |bind| return diags.failParse(
8334 {
8335 const all_relocs_trace = tracy.traceNamed(@src(), "scan input relocations");
8336 defer all_relocs_trace.end();
8337 for (sections[1..]) |*rel_sec| switch (rel_sec.shdr.type) {
8338 else => {},
8339 inline .REL, .RELA => |sht| {
8340 const relocs_trace = tracy.traceNamed(@src(), "input relocation section");
8341 defer relocs_trace.end();
8342
8343 const link_is_symtab: bool = if (maybe_symtab_shndx) |symtab_shndx| s: {
8344 break :s rel_sec.shdr.link == symtab_shndx;
8345 } else false;
8346 if (!link_is_symtab) {
8347 return diags.failParse(path, "relocation section does not link to SHT_SYMTAB section", .{});
8348 }
8349 if (rel_sec.shdr.info == std.elf.SHN_UNDEF or rel_sec.shdr.info >= ehdr.shnum) {
8350 return diags.failParse(
81198351 path,
8120 "symbol '{s}' has unsupported binding (0x{x})",
8121 .{ name, bind },
8122 ),
8123 .LOCAL => continue,
8124 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
8125 si.* = .global(elf.addGlobalSymbol(.{
8126 .node = .none,
8127 .name = name,
8128 .value = input_sym.value,
8129 .size = input_sym.size,
8130 .type = sym_type,
8131 .bind = switch (bind) {
8132 .WEAK, .GNU_UNIQUE => .weak,
8133 .GLOBAL => .strong,
8134 else => unreachable,
8135 },
8136 .visibility = input_sym.other.visibility,
8137 .shndx = .UNDEF,
8138 }) catch |err| switch (err) {
8139 error.MultipleDefinitions => unreachable, // shndx is .UNDEF
8140 error.MultipleDefaultVersions => unreachable, // shndx is .UNDEF
8141 error.UndefinedDefaultVersion => return diags.failParse(
8142 path,
8143 "symbol '{s}' specifies default version without defining it",
8144 .{name},
8145 ),
8146 else => |e| return e,
8147 });
8148 continue;
8149 },
8352 "relocation section 'sh_info' refers to invalid section index '{d}'",
8353 .{rel_sec.shdr.info},
8354 );
8355 }
8356 const Rel = switch (sht) {
8357 else => comptime unreachable,
8358 .REL => ElfN.Rel,
8359 .RELA => ElfN.Rela,
81508360 };
8361 if (rel_sec.shdr.entsize < @sizeOf(Rel))
8362 return diags.failParse(path, "unsupported rel entsize", .{});
81518363
8152 const symbol_oni: MappedFile.Node.Index.Optional, const output_shndx: Section.Index, const symbol_value: u64 = switch (sections[input_sym.shndx].placement) {
8364 const loc_sec = &sections[rel_sec.shdr.info];
8365 relocs_trace.addText(std.mem.sliceTo(shstrtab[loc_sec.shdr.name..], 0));
8366 const loc_node = switch (loc_sec.placement) {
81538367 .none => continue,
8154 .progbits => |isi| .{
8155 .wrap(isi.node(elf)),
8156 elf.getNodeShndx(isi.node(elf)),
8157 input_sym.value,
8158 },
8159 .nobits => |nobits| sym: {
8160 const section_base: u64 = switch (elf.ehdrType()) {
8161 .REL => 0,
8162 .EXEC, .DYN => base: {
8163 if (elf.ni.tls != .none and
8164 nobits.shndx == elf.shndx.tbss)
8165 {
8166 break :base elf.tbssOffset();
8167 }
8168 assert(nobits.shndx == elf.shndx.bss);
8169 break :base nobits.shndx.vaddr(elf);
8170 },
8171 };
8172 break :sym .{
8173 .none,
8174 nobits.shndx,
8175 section_base + nobits.offset + input_sym.value,
8176 };
8177 },
8178 };
8179
8180 switch (input_sym.info.bind) {
8181 else => |bind| return diags.failParse(
8368 .progbits => |isi| isi.node(elf),
8369 .nobits => return diags.failParse(
81828370 path,
8183 "symbol '{s}' has unsupported binding (0x{x})",
8184 .{ name, bind },
8371 "cannot apply relocation to SHT_NOBITS section '{s}'",
8372 .{std.mem.sliceTo(shstrtab[loc_sec.shdr.name..], 0)},
81858373 ),
8186 .LOCAL => {
8187 const lsi = try elf.addLocalSymbol(.{
8188 .node = symbol_oni,
8189 .name = name,
8190 .value = symbol_value,
8191 .size = input_sym.size,
8192 .type = sym_type,
8193 .shndx = output_shndx,
8194 });
8195 si.* = .local(lsi);
8196 },
8197 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
8198 si.* = .global(elf.addGlobalSymbol(.{
8199 .node = symbol_oni,
8200 .name = name,
8201 .value = symbol_value,
8202 .size = input_sym.size,
8203 .type = sym_type,
8204 .bind = switch (bind) {
8205 .WEAK, .GNU_UNIQUE => .weak,
8206 .GLOBAL => .strong,
8207 else => unreachable,
8208 },
8209 .visibility = input_sym.other.visibility,
8210 .shndx = output_shndx,
8211 }) catch |err| switch (err) {
8212 error.MultipleDefinitions => return diags.failParse(
8213 path,
8214 "multiple definitions of '{s}'",
8215 .{name},
8216 ),
8217 error.MultipleDefaultVersions => return diags.failParse(
8218 path,
8219 "multiple default versions of '{s}'",
8220 .{parseVersionedSymbolName(name).name},
8221 ),
8222 error.UndefinedDefaultVersion => unreachable, // shndx is not `.UNDEF` (i.e. the symbol is defined)
8223 else => |e| return e,
8224 });
8225 },
8226 }
8227 }
8228 for (sections[1..]) |*rel_sec| switch (rel_sec.shdr.type) {
8229 else => {},
8230 inline .REL, .RELA => |sht| {
8231 if (rel_sec.shdr.link != symtab_shndx or rel_sec.shdr.info == std.elf.SHN_UNDEF or
8232 rel_sec.shdr.info >= ehdr.shnum) continue;
8233 const Rel = switch (sht) {
8234 else => comptime unreachable,
8235 .REL => ElfN.Rel,
8236 .RELA => ElfN.Rela,
8237 };
8238 if (rel_sec.shdr.entsize < @sizeOf(Rel))
8239 return diags.failParse(path, "unsupported rel entsize", .{});
8240
8241 const loc_sec = &sections[rel_sec.shdr.info];
8242 const loc_node = switch (loc_sec.placement) {
8243 .none => continue,
8244 .progbits => |isi| isi.node(elf),
8245 .nobits => return diags.failParse(
8246 path,
8247 "cannot apply relocation to SHT_NOBITS section '{s}'",
8248 .{std.mem.sliceTo(shstrtab[loc_sec.shdr.name..], 0)},
8249 ),
8250 };
8374 };
82518375
8252 elf.resetNodeRelocs(loc_node);
8376 elf.resetNodeRelocs(loc_node);
82538377
8254 const relnum = std.math.divExact(
8255 u32,
8256 @intCast(rel_sec.shdr.size),
8257 @intCast(rel_sec.shdr.entsize),
8258 ) catch return diags.failParse(
8378 const relnum = std.math.divExact(
8379 u32,
8380 @intCast(rel_sec.shdr.size),
8381 @intCast(rel_sec.shdr.entsize),
8382 ) catch return diags.failParse(
8383 path,
8384 "relocation section size (0x{x}) is not a multiple of entsize (0x{x})",
8385 .{ rel_sec.shdr.size, rel_sec.shdr.entsize },
8386 );
8387 try elf.ensureUnusedRelocCapacity(loc_node, relnum);
8388 try fr.seekTo(fl.offset + rel_sec.shdr.offset);
8389 for (0..relnum) |_| {
8390 const rel = try r.peekStruct(Rel, target_endian);
8391 try r.discardAll64(rel_sec.shdr.entsize);
8392 if (rel.info.sym == 0) continue;
8393 if (rel.info.sym - 1 > symmap.items.len) return diags.failParse(
82598394 path,
8260 "relocation section size (0x{x}) is not a multiple of entsize (0x{x})",
8261 .{ rel_sec.shdr.size, rel_sec.shdr.entsize },
8395 "relocation target symbol index {d} exceeds symtab size",
8396 .{rel.info.sym},
82628397 );
8263 try elf.ensureUnusedRelocCapacity(loc_node, relnum);
8264 try fr.seekTo(fl.offset + rel_sec.shdr.offset);
8265 for (0..relnum) |_| {
8266 const rel = try r.peekStruct(Rel, target_endian);
8267 try r.discardAll64(rel_sec.shdr.entsize);
8268 if (rel.info.sym == 0) continue;
8269 if (rel.info.sym > symnum) return diags.failParse(
8270 path,
8271 "relocation target symbol index {d} exceeds symtab size",
8272 .{rel.info.sym},
8273 );
8274 const target = symmap.items[rel.info.sym - 1];
8275 if (target == Symbol.Id.null) {
8276 // If this is not an SHF_ALLOC section, then let's not report
8277 // this for now, because it probably doesn't affect the final
8278 // binary's functionality for this section to be a bit broken.
8279 if (loc_sec.shdr.flags.shf.ALLOC) {
8280 diags.addParseError(
8281 path,
8282 "unsupported symbol at index {d} required for relocation",
8283 .{rel.info.sym},
8284 );
8285 }
8286 continue;
8287 }
8288 const rt: MachineRelocType = .wrap(rel.info.type, elf);
8289 elf.addRelocAssumeCapacity(
8290 loc_node,
8291 rel.offset - loc_sec.shdr.addr,
8292 target,
8293 rel.addend,
8294 rt,
8295 ) catch |err| switch (err) {
8296 else => |e| return e,
8297 error.UnknownRelocation => diags.addParseError(
8298 path,
8299 "unknown relocation type '{f}'",
8300 .{rt.fmt(elf)},
8301 ),
8302 error.NonStaticRelocation => diags.addParseError(
8398 const target = symmap.items[rel.info.sym - 1];
8399 if (target == Symbol.Id.null) {
8400 // If this is not an SHF_ALLOC section, then let's not report
8401 // this for now, because it probably doesn't affect the final
8402 // binary's functionality for this section to be a bit broken.
8403 if (loc_sec.shdr.flags.shf.ALLOC) {
8404 diags.addParseError(
83038405 path,
8304 "non-static relocation type '{f}'",
8305 .{rt.fmt(elf)},
8306 ),
8307 error.UnimplementedRelocation => diags.addParseError(
8308 path,
8309 "TODO(Elf2): unimplemented relocation type '{f}'",
8310 .{rt.fmt(elf)},
8311 ),
8312 };
8406 "unsupported symbol at index {d} required for relocation",
8407 .{rel.info.sym},
8408 );
8409 }
8410 continue;
83138411 }
8314 },
8315 };
8316 },
8317 };
8412 const rt: MachineRelocType = .wrap(rel.info.type, elf);
8413 elf.addRelocAssumeCapacity(
8414 loc_node,
8415 rel.offset - loc_sec.shdr.addr,
8416 target,
8417 rel.addend,
8418 rt,
8419 ) catch |err| switch (err) {
8420 else => |e| return e,
8421 error.UnknownRelocation => diags.addParseError(
8422 path,
8423 "unknown relocation type '{f}'",
8424 .{rt.fmt(elf)},
8425 ),
8426 error.NonStaticRelocation => diags.addParseError(
8427 path,
8428 "non-static relocation type '{f}'",
8429 .{rt.fmt(elf)},
8430 ),
8431 error.UnimplementedRelocation => diags.addParseError(
8432 path,
8433 "TODO(Elf2): unimplemented relocation type '{f}'",
8434 .{rt.fmt(elf)},
8435 ),
8436 };
8437 }
8438 },
8439 };
8440 }
83188441
83198442 if (input_has_bss) {
83208443 try elf.bss_inputs.append(gpa, input_index);
......@@ -8374,6 +8497,10 @@ fn loadDso(
83748497 fallback_soname: link.Input.Dso.FallbackSoname,
83758498 fr: *Io.File.Reader,
83768499) (LoadParseInputError || error{BadMagic})!void {
8500 const trace = tracy.trace(@src());
8501 defer trace.end();
8502 trace.addTextFmt("{f}", .{path.fmtEscapeString()});
8503
83778504 const comp = elf.base.comp;
83788505 const gpa = comp.gpa;
83798506 const diags = &comp.link_diags;
......@@ -8519,6 +8646,9 @@ fn loadDso(
85198646 const verdef: []DsoGlobals.String.Optional = verdef: {
85208647 const verdef_sh = shdr.verdef orelse break :verdef &.{};
85218648
8649 const versions_trace = tracy.traceNamed(@src(), "dso versions");
8650 defer versions_trace.end();
8651
85228652 // This parsing is a bit of a mess because it appears that at this moment in history
85238653 // nobody at GNU was yet aware of the concept of an "array".
85248654
......@@ -8602,6 +8732,9 @@ fn loadDso(
86028732 const soname = try elf.string(.dynstr, soname_slice);
86038733 try elf.needed.put(gpa, soname, {});
86048734
8735 const symbols_trace = tracy.traceNamed(@src(), "dso symbols");
8736 defer symbols_trace.end();
8737
86058738 // Scan the symbol table and populate `elf.dso_globals`.
86068739 const first_global = @min(shdr.dynsym.info, symnum);
86078740 try elf.dso_globals.symbols.ensureUnusedCapacity(gpa, symnum - first_global);
......@@ -9422,7 +9555,6 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)
94229555 const gpa = elf.base.comp.gpa;
94239556 try elf.symbol_relocs.ensureUnusedCapacity(gpa, len);
94249557 try elf.node_relocs.ensureUnusedCapacity(gpa, len);
9425 try elf.got_relocs.ensureUnusedCapacity(gpa, len);
94269558 const class = elf.identClass();
94279559 switch (elf.ehdrType()) {
94289560 .REL => {
......@@ -9460,7 +9592,6 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)
94609592 try shndx.get(elf).rela.shndx.relaEnsureAdditionalCapacity(elf, len);
94619593 },
94629594 .EXEC, .DYN => {
9463 try elf.tls_size_symbol_relocs.ensureUnusedCapacity(gpa, len);
94649595 const new_got_entries = len * 2; // at worst, every reloc is a new TLSGD
94659596 try elf.got.ensureUnusedCapacity(gpa, new_got_entries);
94669597 const need_got_size = switch (class) {
......@@ -9485,6 +9616,9 @@ fn addRelocAssumeCapacity(
94859616 addend: i64,
94869617 @"type": MachineRelocType,
94879618) (Error || error{ UnknownRelocation, NonStaticRelocation, UnimplementedRelocation })!void {
9619 const trace = tracy.trace(@src());
9620 defer trace.end();
9621
94889622 switch (elf.ehdrType()) {
94899623 .REL => {
94909624 const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx;
......@@ -9506,13 +9640,12 @@ fn addRelocAssumeCapacity(
95069640 elf.symbol_relocs.appendAssumeCapacity(.{
95079641 .node = node.toOptional(),
95089642 .offset = offset,
9509 .type = undefined,
9510 .target = target,
9643 .bits = undefined,
9644 .target_symbol = target,
95119645 .addend = .{ .normal = addend },
95129646 .next = next,
95139647 .prev = .none,
95149648 .rela_index = rela_index.toOptional(),
9515 .result = .ok,
95169649 });
95179650 ri.get(elf).apply(elf);
95189651 },
......@@ -9567,14 +9700,14 @@ fn addRelocAssumeCapacity(
95679700 .TLS_LE64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })),
95689701 .TLS_LE64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })),
95699702
9570 .GOT_PC_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
9571 .GOT_PC_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala_hi20)),
9572 .GOT64_PC_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala64_lo20)),
9573 .GOT64_PC_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala64_hi12)),
9574 .GOT_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
9575 .GOT_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })),
9576 .GOT64_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })),
9577 .GOT64_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })),
9703 .GOT_PC_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotabs_symbol, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
9704 .GOT_PC_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_got_pcala_hi20)),
9705 .GOT64_PC_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_got_pcala64_lo20)),
9706 .GOT64_PC_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_got_pcala64_hi12)),
9707 .GOT_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotabs_symbol, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
9708 .GOT_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotabs_symbol, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })),
9709 .GOT64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotabs_symbol, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })),
9710 .GOT64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotabs_symbol, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })),
95789711 // zig fmt: on
95799712 },
95809713 .PPC64 => switch (@"type".PPC64) {
......@@ -9678,13 +9811,13 @@ fn addRelocAssumeCapacity(
96789811 .TLS_DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
96799812 .TLS_DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
96809813
9681 .GOT13 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[12:0]", .cast = .unsigned, .shift = .@"0" })),
9682 .GOT22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
9683 .GOTDATA_OP_LOX10 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.sparc_op_lox10)),
9684 .GOTDATA_OP_HIX22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.sparc_op_hix22)),
9685 .TLS_GD_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
9686 .TLS_LDM_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
9687 .TLS_IE_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
9814 .GOT13 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_symbol, .{ .dest = .@"32[12:0]", .cast = .unsigned, .shift = .@"0" })),
9815 .GOT22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_symbol, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
9816 .GOTDATA_OP_LOX10 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.sparc_got_op_lox10)),
9817 .GOTDATA_OP_HIX22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.sparc_got_op_hix22)),
9818 .TLS_GD_HI22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_tlsgd, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
9819 .TLS_LDM_HI22 => try elf.addSymbolRelocAssumeCapacity(node, offset, .null, addend, .simple(.gotoff_tlsgd, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
9820 .TLS_IE_HI22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_tpoff, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
96889821 // zig fmt: on
96899822
96909823 .TLS_GD_CALL, .TLS_LDM_CALL => {
......@@ -9750,19 +9883,19 @@ fn addRelocAssumeCapacity(
97509883 },
97519884 .GOT10 => {
97529885 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
9753 elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
9886 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_symbol, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
97549887 },
97559888 .TLS_GD_LO10 => {
97569889 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
9757 elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
9890 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_tlsgd, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
97589891 },
97599892 .TLS_LDM_LO10 => {
97609893 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
9761 elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
9894 try elf.addSymbolRelocAssumeCapacity(node, offset, .null, addend, .simple(.gotoff_tlsgd, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
97629895 },
97639896 .TLS_IE_LO10 => {
97649897 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
9765 elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
9898 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_tpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
97669899 },
97679900 },
97689901 .X86_64 => rel_type: switch (@"type".X86_64) {
......@@ -9790,12 +9923,7 @@ fn addRelocAssumeCapacity(
97909923 .GOTOFF64 => return error.UnimplementedRelocation, // offset of symbol from GOT base
97919924 .PLTOFF64 => return error.UnimplementedRelocation, // offset of PLT entry from GOT base (yes, I know, the name is stupid)
97929925
9793 // TODO: figure out how to do relaxations. Perhaps we want to remove a `GotReloc`
9794 // and replace it with a `SymbolReloc` when a relaxation becomes possible, but we'd
9795 // need to bear in mind whether incremental updates might make a relaxation
9796 // impossible again or something like that. Relaxations seem kind of hostile to
9797 // incremental compilation, so perhaps we just only support them in non-incremental
9798 // compilations and just apply them in flush or something.
9926 // TODO: figure out how to do relaxations
97999927
98009928 // Relaxable versions of other relocations. Since we don't yet implement relaxation,
98019929 // just use the handling for the non-relaxable versions.
......@@ -9831,13 +9959,13 @@ fn addRelocAssumeCapacity(
98319959 .TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
98329960 .TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
98339961
9834 .GOT32 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
9835 .GOT64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
9836 .GOTPCREL => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
9837 .GOTPCREL64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
9838 .TLSGD => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
9839 .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
9840 .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
9962 .GOT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_symbol, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
9963 .GOT64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_symbol, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
9964 .GOTPCREL => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotrel_symbol, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
9965 .GOTPCREL64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotrel_symbol, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
9966 .TLSGD => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotrel_tlsgd, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
9967 .TLSLD => try elf.addSymbolRelocAssumeCapacity(node, offset, .null, addend, .simple(.gotrel_tlsgd, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
9968 .GOTTPOFF => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotrel_tpoff, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
98419969 // zig fmt: on
98429970
98439971 .GOTPC64 => {
......@@ -9872,6 +10000,11 @@ fn addSymbolRelocAssumeCapacityInner(
987210000) Error!void {
987310001 assert(elf.ehdrType() != .REL);
987410002
10003 const trace = tracy.trace(@src());
10004 defer trace.end();
10005
10006 const gpa = elf.base.comp.gpa;
10007
987510008 request_static_target: {
987610009 if (elf.shndx.dynamic == .UNDEF) break :request_static_target;
987710010 const gsi: Symbol.Global.Index = switch (target.unwrap()) {
......@@ -9894,6 +10027,15 @@ fn addSymbolRelocAssumeCapacityInner(
989410027 .dtpoff,
989510028 .tpoff,
989610029 .size,
10030 .gotabs_symbol,
10031 .gotrel_symbol,
10032 .gotoff_symbol,
10033 .gotabs_tpoff,
10034 .gotrel_tpoff,
10035 .gotoff_tpoff,
10036 .gotabs_tlsgd,
10037 .gotrel_tlsgd,
10038 .gotoff_tlsgd,
989710039 => false,
989810040
989910041 .special => switch (@"type".action.special) {
......@@ -9908,10 +10050,15 @@ fn addSymbolRelocAssumeCapacityInner(
990810050 .larch_b21,
990910051 .larch_b26,
991010052 .larch_call36,
10053 .larch_got_pcala_hi20,
10054 .larch_got_pcala64_lo20,
10055 .larch_got_pcala64_hi12,
991110056
991210057 .sparc_le_hix22,
991310058 .sparc_wdisp10,
991410059 .sparc_wdisp16,
10060 .sparc_got_op_lox10,
10061 .sparc_got_op_hix22,
991510062 => false,
991610063 },
991710064 };
......@@ -9923,6 +10070,42 @@ fn addSymbolRelocAssumeCapacityInner(
992310070 }
992410071 }
992510072
10073 if (@"type".needsGotEntry()) |got_kind| {
10074 const new_index: usize, const new_len: u32 = switch (got_kind) {
10075 .symbol => new: {
10076 const gop = try elf.got.getOrPut(gpa, .{ .symbol = target });
10077 if (gop.found_existing) break :new .{ 0, 0 };
10078 gop.value_ptr.* = .none;
10079 break :new .{ gop.index, 1 };
10080 },
10081 .tpoff => new: {
10082 const gop = try elf.got.getOrPut(gpa, .{ .tpoff = target });
10083 if (gop.found_existing) break :new .{ 0, 0 };
10084 gop.value_ptr.* = .none;
10085 break :new .{ gop.index, 1 };
10086 },
10087 .tlsgd => new: {
10088 try elf.got.ensureUnusedCapacity(gpa, 2);
10089 const gop = elf.got.getOrPutAssumeCapacity(.{ .tlsgd0 = target });
10090 if (gop.found_existing) break :new .{ 0, 0 };
10091 gop.value_ptr.* = .none;
10092 elf.got.putAssumeCapacityNoClobber(.{ .tlsgd1 = target }, .none);
10093 break :new .{ gop.index, 1 };
10094 },
10095 };
10096
10097 switch (elf.shdrPtr(elf.shndx.got)) {
10098 inline else => |got_shdr, class| {
10099 const new_size = elf.targetLoad(&got_shdr.size) + new_len * @sizeOf(class.ElfN().Addr);
10100 elf.targetStore(&got_shdr.size, new_size);
10101 try elf.shndx.got.node(elf).ensureMinimumSize(gpa, &elf.mf, new_size);
10102 },
10103 }
10104 for (new_index..new_index + new_len) |got_index| {
10105 elf.updateGotEntry(got_index);
10106 }
10107 }
10108
992610109 const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));
992710110 const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc;
992810111 const next = first_target_reloc.*;
......@@ -9931,18 +10114,24 @@ fn addSymbolRelocAssumeCapacityInner(
993110114 elf.symbol_relocs.appendAssumeCapacity(.{
993210115 .node = node.toOptional(),
993310116 .offset = offset,
9934 .target = target,
10117 .target_symbol = target,
993510118 .addend = addend,
9936 .type = @"type",
10119 .bits = .{
10120 .type = @"type",
10121 .result = .ok,
10122 .relaxation = .none,
10123 },
993710124 .next = next,
993810125 .prev = .none,
993910126 .rela_index = .none,
9940 .result = .ok,
994110127 });
994210128 if (@"type".dependsOnTlsSize(elf)) {
9943 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});
10129 try elf.tls_size_symbol_relocs.putNoClobber(gpa, ri, {});
994410130 }
9945 try ri.get(elf).updateDynamicReloc(elf);
10131 if (@"type".dependsOnGotAddress()) {
10132 try elf.got_relocs.put(gpa, ri, {});
10133 }
10134 try ri.get(elf).updateDynamicReloc(elf, ri);
994610135
994710136 // Actually apply the new relocation!
994810137 ri.get(elf).apply(elf);
......@@ -10046,95 +10235,6 @@ fn addNodeRelocAssumeCapacity(
1004610235 },
1004710236 }
1004810237}
10049fn addGotRelocAssumeCapacity(
10050 elf: *Elf,
10051 node: MappedFile.Node.Index,
10052 offset: u64,
10053 target: GotKey,
10054 addend: i64,
10055 @"type": GotReloc.Type,
10056) void {
10057 assert(elf.ehdrType() != .REL);
10058 switch (elf.getNode(node)) {
10059 .deleted,
10060 .archive,
10061 .archive_header,
10062 .archive_input_member,
10063 .archive_elf_member_header,
10064 .elf,
10065 .ehdr,
10066 .shdr,
10067 .segment,
10068 .copied_global,
10069 .debug_shared,
10070 .debug_addr,
10071 .eh_frame_footer,
10072 .debug_str_offsets,
10073 .unit_padding,
10074 .unit_frame,
10075 .unit_frame_cie,
10076 .unit_debug_info,
10077 .unit_debug_info_header,
10078 .unit_debug_info_footer,
10079 .unit_debug_line,
10080 .unit_debug_line_header,
10081 .unit_debug_rnglists,
10082 .const_debug_info,
10083 .global_debug_info,
10084 .func_frame_fde,
10085 .func_debug_info,
10086 .func_debug_line,
10087 .decl_debug_info,
10088 => unreachable, // cannot contain relocs,
10089 .section,
10090 .section_manual_size,
10091 .uav,
10092 => unreachable, // cannot contain GOT relocs
10093 .input_section,
10094 .nav,
10095 .lazy_code,
10096 .lazy_const_data,
10097 => {},
10098 }
10099
10100 const gop = elf.got.getOrPutAssumeCapacity(target);
10101 if (!gop.found_existing) {
10102 gop.value_ptr.* = .none;
10103 const maybe_next_key: ?GotKey = switch (target) {
10104 .reserved => null,
10105 .tpoff => null,
10106 .symbol => null,
10107 .tlsld0 => .tlsld1,
10108 .tlsgd0 => |sym| .{ .tlsgd1 = sym },
10109 .tlsld1 => unreachable,
10110 .tlsgd1 => unreachable,
10111 };
10112 switch (elf.shdrPtr(elf.shndx.got)) {
10113 inline else => |got_shdr, class| {
10114 const Addr = class.ElfN().Addr;
10115 const old_size = elf.targetLoad(&got_shdr.size);
10116 const new_entry_count = @as(u32, 1) + @intFromBool(maybe_next_key != null);
10117 elf.targetStore(&got_shdr.size, @intCast(old_size + @sizeOf(Addr) * new_entry_count));
10118 },
10119 }
10120 if (maybe_next_key) |next_key| {
10121 elf.got.putAssumeCapacityNoClobber(next_key, .none);
10122 elf.updateGotEntry(gop.index);
10123 elf.updateGotEntry(gop.index + 1);
10124 } else {
10125 elf.updateGotEntry(gop.index);
10126 }
10127 }
10128
10129 elf.got_relocs.appendAssumeCapacity(.{
10130 .node = .wrap(node),
10131 .offset = offset,
10132 .target = target,
10133 .addend = addend,
10134 .type = @"type",
10135 .result = .ok,
10136 });
10137}
1013810238fn updateGotEntry(elf: *Elf, got_index: usize) void {
1013910239 assert(elf.ehdrType() != .REL);
1014010240 const entry_value: union(enum) {
......@@ -10193,15 +10293,6 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
1019310293 .addend = 0,
1019410294 } },
1019510295 },
10196 .tlsgd1 => |sym| switch (elf.classifySymbolValue(sym)) {
10197 .static => .{ .unsigned = sym.value(elf) },
10198 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`
10199 .dynamic => .{ .reloc = .{
10200 .type = .dtpOff(elf),
10201 .dynsym_index = sym.unwrap().global.dynsymIndex(elf).?,
10202 .addend = 0,
10203 } },
10204 },
1020510296 .tlsgd0 => |sym| switch (elf.base.comp.config.link_mode) {
1020610297 .static => val: {
1020710298 assert(elf.base.comp.config.output_mode == .Exe); // static libraries don't have GOTs
......@@ -10209,25 +10300,28 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
1020910300 },
1021010301 .dynamic => .{ .reloc = .{
1021110302 .type = .dtpMod(elf),
10212 .dynsym_index = switch (elf.classifySymbolValue(sym)) {
10213 .static, .static_relative => 0,
10214 .dynamic => sym.unwrap().global.dynsymIndex(elf).?,
10303 .dynsym_index = switch (sym) {
10304 .null => 0,
10305 else => switch (elf.classifySymbolValue(sym)) {
10306 .static, .static_relative => 0,
10307 .dynamic => sym.unwrap().global.dynsymIndex(elf).?,
10308 },
1021510309 },
1021610310 .addend = 0,
1021710311 } },
1021810312 },
10219 .tlsld0 => switch (elf.base.comp.config.link_mode) {
10220 .static => val: {
10221 assert(elf.base.comp.config.output_mode == .Exe); // static libraries don't have GOTs
10222 break :val .{ .unsigned = 1 }; // TLS module ID for executable
10313 .tlsgd1 => |sym| switch (sym) {
10314 .null => .{ .unsigned = 0 },
10315 else => switch (elf.classifySymbolValue(sym)) {
10316 .static => .{ .unsigned = sym.value(elf) },
10317 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`
10318 .dynamic => .{ .reloc = .{
10319 .type = .dtpOff(elf),
10320 .dynsym_index = sym.unwrap().global.dynsymIndex(elf).?,
10321 .addend = 0,
10322 } },
1022310323 },
10224 .dynamic => .{ .reloc = .{
10225 .type = .dtpMod(elf),
10226 .dynsym_index = 0,
10227 .addend = 0,
10228 } },
1022910324 },
10230 .tlsld1 => .{ .unsigned = 0 },
1023110325 };
1023210326
1023310327 // First, write to the GOT itself. If we're planning to use a relocation, we'll just write zeroes.
......@@ -10994,7 +11088,7 @@ pub fn idle(elf: *Elf) link.Error!bool {
1099411088 var ri = symtab_index.ptr(elf).first_target_reloc;
1099511089 while (ri != .none) {
1099611090 const reloc = ri.get(elf);
10997 assert(reloc.target == sym_id);
11091 assert(reloc.target_symbol == sym_id);
1099811092 // In relocatables, every symbol relocation has an output relocation.
1099911093 const rela_index = reloc.rela_index.unwrap().?;
1100011094 reloc.relaSection(elf).relaUpdateSym(elf, rela_index, @backingInt(symtab_index));
......@@ -11024,7 +11118,7 @@ pub fn idle(elf: *Elf) link.Error!bool {
1102411118 var ri = symtab_index.ptr(elf).first_target_reloc;
1102511119 while (ri != .none) {
1102611120 const reloc = ri.get(elf);
11027 assert(reloc.target == sym_id);
11121 assert(reloc.target_symbol == sym_id);
1102811122 // There may or may not be a runtime relocation for this symbol reloc.
1102911123 if (reloc.rela_index.unwrap()) |rela_index| {
1103011124 elf.shndx.rela_dyn.relaUpdateSym(elf, rela_index, dynsym_index);
......@@ -11615,8 +11709,8 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1161511709 const rela_index = opt_rela_index.unwrap() orelse continue;
1161611710 rela_dyn_shndx.relaAdjustOffset(elf, rela_index, old_addr, addr);
1161711711 }
11618 for (elf.got_relocs.items) |*reloc| {
11619 reloc.apply(elf);
11712 for (elf.got_relocs.keys()) |ri| {
11713 ri.get(elf).apply(elf);
1162011714 }
1162111715 } else if (shndx == elf.shndx.plt) {
1162211716 elf.flushMovedNodeRelocs(ni, .{
......@@ -11683,7 +11777,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1168311777
1168411778 elf.flushMovedNodeRelocs(ni, .{
1168511779 .first_symbol_reloc = isi.ptrConst(elf).first_symbol_reloc,
11686 .first_got_reloc = isi.ptrConst(elf).first_got_reloc,
1168711780 });
1168811781 },
1168911782 .copied_global => |global_name| {
......@@ -11714,7 +11807,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1171411807 .none,
1171511808 .uav, .lazy_code, .lazy_const_data => .none,
1171611809 },
11717 .first_got_reloc = mi.firstGotReloc(elf),
1171811810 });
1171911811 },
1172011812 .debug_shared => |ss| {
......@@ -12200,8 +12292,6 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
1220012292 switch (got_key) {
1220112293 .reserved,
1220212294 .symbol,
12203 .tlsld0,
12204 .tlsld1,
1220512295 .tlsgd0,
1220612296 .tlsgd1,
1220712297 => {
src/link/MappedFile.zig+34
......@@ -11,6 +11,8 @@ const assert = std.debug.assert;
1111const linux = std.os.linux;
1212const windows = std.os.windows;
1313
14const tracy = @import("../tracy.zig");
15
1416io: Io,
1517flags: packed struct {
1618 block_size: Alignment,
......@@ -892,6 +894,9 @@ fn addNode(mf: *MappedFile, gpa: Allocator, opts: struct {
892894}) Error!Node.Index {
893895 mf.nodes_lock.assertUnlocked();
894896
897 const trace = tracy.trace(@src());
898 defer trace.end();
899
895900 try mf.nodes.ensureUnusedCapacity(gpa, 1);
896901 try mf.large.ensureUnusedCapacity(gpa, 2);
897902
......@@ -1014,6 +1019,9 @@ fn shrinkLeafNode(
10141019) Error!void {
10151020 mf.nodes_lock.assertUnlocked();
10161021
1022 const trace = tracy.trace(@src());
1023 defer trace.end();
1024
10171025 const old_offset, const old_size = ni.location(mf).resolve(mf);
10181026
10191027 assert(new_size < old_size);
......@@ -1152,6 +1160,9 @@ fn growNode(
11521160) Error!void {
11531161 mf.nodes_lock.assertUnlocked();
11541162
1163 const trace = tracy.trace(@src());
1164 defer trace.end();
1165
11551166 const node = ni.get(mf);
11561167
11571168 const old_offset, const old_size = node.location().resolve(mf);
......@@ -1569,6 +1580,9 @@ fn growFloatingNodeWithAlignment(
15691580) Error!void {
15701581 mf.nodes_lock.assertUnlocked();
15711582
1583 const trace = tracy.trace(@src());
1584 defer trace.end();
1585
15721586 const parent_ni = ni.parent(mf).unwrap().?; // `ni` cannot be `.root`
15731587 const old_offset, const old_size = ni.location(mf).resolve(mf);
15741588
......@@ -1791,6 +1805,9 @@ fn growNodeViaInsertRange(
17911805 new_size: u64,
17921806 grow_options: GrowOptions,
17931807) Error!bool {
1808 const trace = tracy.trace(@src());
1809 defer trace.end();
1810
17941811 if (!is_linux or mf.flags.fallocate_insert_range_unsupported) {
17951812 return false;
17961813 }
......@@ -1999,6 +2016,9 @@ fn ensureAdditionalHeaderCapacity(
19992016 parent_ni: Node.Index,
20002017 extra_capacity: u64,
20012018) Error!void {
2019 const trace = tracy.trace(@src());
2020 defer trace.end();
2021
20022022 _, const parent_size = parent_ni.location(mf).resolve(mf);
20032023
20042024 const last_header_oni = parent_ni.lastHeader(mf);
......@@ -2274,6 +2294,9 @@ fn realignNode(
22742294) Error!void {
22752295 mf.nodes_lock.assertUnlocked();
22762296
2297 const trace = tracy.trace(@src());
2298 defer trace.end();
2299
22772300 const old_offset, const old_size = ni.location(mf).resolve(mf);
22782301
22792302 if (ni == .root or ni.position(mf) != .floating) {
......@@ -2310,6 +2333,10 @@ fn updateWriters(mf: *MappedFile) void {
23102333fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) Error!void {
23112334 if (old_file_offset == new_file_offset) return;
23122335
2336 const trace = tracy.trace(@src());
2337 defer trace.end();
2338 trace.addTextFmt("{Bi} (0x{X} -> 0x{X})", .{ size, old_file_offset, new_file_offset });
2339
23132340 if (old_file_offset >= new_file_offset + size or
23142341 new_file_offset >= old_file_offset + size)
23152342 {
......@@ -2348,6 +2375,10 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size:
23482375 }
23492376}
23502377fn zeroRange(mf: *MappedFile, file_offset: u64, size: u64) Error!void {
2378 const trace = tracy.trace(@src());
2379 defer trace.end();
2380 trace.addTextFmt("{Bi} (at 0x{X})", .{ size, file_offset });
2381
23512382 if (is_linux and
23522383 !mf.flags.fallocate_punch_hole_unsupported and
23532384 size >= mf.flags.block_size.toByteUnits() * 2 - 1)
......@@ -2395,6 +2426,9 @@ fn copyFileRange(
23952426 return 0;
23962427 }
23972428
2429 const trace = tracy.trace(@src());
2430 defer trace.end();
2431
23982432 const min_size = mf.flags.block_size.toByteUnits() * 2 - 1;
23992433 if (size < min_size) return 0;
24002434