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;...@@ -21,6 +21,15 @@ const Alignment = MappedFile.Alignment;
21base: link.File,21base: link.File,
22options: link.File.OpenOptions,22options: link.File.OpenOptions,
23mf: MappedFile,23mf: 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
24ni: struct {33ni: struct {
25 elf: MappedFile.Node.Index,34 elf: MappedFile.Node.Index,
26 ehdr: MappedFile.Node.Index,35 ehdr: MappedFile.Node.Index,
...@@ -208,29 +217,24 @@ navs: std.array_hash_map.Auto(InternPool.Nav.Index, struct {...@@ -208,29 +217,24 @@ navs: std.array_hash_map.Auto(InternPool.Nav.Index, struct {
208 lsi: Symbol.LocalIndex,217 lsi: Symbol.LocalIndex,
209 /// The start index of the contiguous sequence of symbol relocations in this NAV.218 /// The start index of the contiguous sequence of symbol relocations in this NAV.
210 first_symbol_reloc: SymbolReloc.Index,219 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,
213}),220}),
214uavs: std.array_hash_map.Auto(InternPool.Index, struct {221uavs: std.array_hash_map.Auto(InternPool.Index, struct {
215 lsi: Symbol.LocalIndex,222 lsi: Symbol.LocalIndex,
216 /// The start index of the contiguous sequence of symbol relocations in this UAV.223 /// The start index of the contiguous sequence of symbol relocations in this UAV.
217 first_symbol_reloc: SymbolReloc.Index,224 first_symbol_reloc: SymbolReloc.Index,
218 // No `first_got_reloc` field because a UAV never contains GOT relocations.
219}),225}),
220lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {226lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
221 map: std.array_hash_map.Auto(InternPool.Index, struct {227 map: std.array_hash_map.Auto(InternPool.Index, struct {
222 lsi: Symbol.LocalIndex,228 lsi: Symbol.LocalIndex,
223 /// The start index of the contiguous sequence of symbol relocations in this lazy code/data.229 /// The start index of the contiguous sequence of symbol relocations in this lazy code/data.
224 first_symbol_reloc: SymbolReloc.Index,230 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,
227 }),231 }),
228 pending_index: u32,232 pending_index: u32,
229}),233}),
230pending_uavs: std.ArrayList(Node.UavMapIndex),234pending_uavs: std.ArrayList(Node.UavMapIndex),
231symbol_relocs: std.ArrayList(SymbolReloc),235symbol_relocs: std.ArrayList(SymbolReloc),
232node_relocs: std.ArrayList(NodeReloc),236node_relocs: std.ArrayList(NodeReloc),
233got_relocs: std.ArrayList(GotReloc),237got_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),
234/// Set of relocations which must be re-applied if the size of the TLS segment changes.238/// Set of relocations which must be re-applied if the size of the TLS segment changes.
235tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),239tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),
236/// Index matches the index into `shdrs`. Like `shdrs`, this map excludes `SHN_UNDEF`.240/// Index matches the index into `shdrs`. Like `shdrs`, this map excludes `SHN_UNDEF`.
...@@ -388,9 +392,6 @@ const Node = union(enum) {...@@ -388,9 +392,6 @@ const Node = union(enum) {
388 fn firstSymbolReloc(nmi: NavMapIndex, elf: *const Elf) SymbolReloc.Index {392 fn firstSymbolReloc(nmi: NavMapIndex, elf: *const Elf) SymbolReloc.Index {
389 return elf.navs.values()[@backingInt(nmi)].first_symbol_reloc;393 return elf.navs.values()[@backingInt(nmi)].first_symbol_reloc;
390 }394 }
391 fn firstGotReloc(nmi: NavMapIndex, elf: *const Elf) GotReloc.Index {
392 return elf.navs.values()[@backingInt(nmi)].first_got_reloc;
393 }
394 };395 };
395396
396 pub const UavMapIndex = enum(u32) {397 pub const UavMapIndex = enum(u32) {
...@@ -407,11 +408,6 @@ const Node = union(enum) {...@@ -407,11 +408,6 @@ const Node = union(enum) {
407 fn firstSymbolReloc(umi: UavMapIndex, elf: *const Elf) SymbolReloc.Index {408 fn firstSymbolReloc(umi: UavMapIndex, elf: *const Elf) SymbolReloc.Index {
408 return elf.uavs.values()[@backingInt(umi)].first_symbol_reloc;409 return elf.uavs.values()[@backingInt(umi)].first_symbol_reloc;
409 }410 }
410 fn firstGotReloc(umi: UavMapIndex, elf: *const Elf) GotReloc.Index {
411 _ = umi;
412 _ = elf;
413 return .none;
414 }
415 };411 };
416412
417 pub const LazyMapRef = struct {413 pub const LazyMapRef = struct {
...@@ -437,9 +433,6 @@ const Node = union(enum) {...@@ -437,9 +433,6 @@ const Node = union(enum) {
437 fn firstSymbolReloc(lmi: @This(), elf: *const Elf) SymbolReloc.Index {433 fn firstSymbolReloc(lmi: @This(), elf: *const Elf) SymbolReloc.Index {
438 return elf.lazy.getPtrConst(kind).map.values()[@backingInt(lmi)].first_symbol_reloc;434 return elf.lazy.getPtrConst(kind).map.values()[@backingInt(lmi)].first_symbol_reloc;
439 }435 }
440 fn firstGotReloc(lmi: @This(), elf: *const Elf) GotReloc.Index {
441 return elf.lazy.getPtrConst(kind).map.values()[@backingInt(lmi)].first_got_reloc;
442 }
443 };436 };
444 }437 }
445438
...@@ -603,8 +596,6 @@ const InputSection = struct {...@@ -603,8 +596,6 @@ const InputSection = struct {
603 node: MappedFile.Node.Index,596 node: MappedFile.Node.Index,
604 /// The start index of the contiguous sequence of symbol relocations in this input section.597 /// The start index of the contiguous sequence of symbol relocations in this input section.
605 first_symbol_reloc: SymbolReloc.Index,598 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
609 const Index = enum(u32) {600 const Index = enum(u32) {
610 _,601 _,
...@@ -1387,14 +1378,28 @@ const SymbolReloc = struct {...@@ -1387,14 +1378,28 @@ const SymbolReloc = struct {
1387 node: MappedFile.Node.Index.Optional,1378 node: MappedFile.Node.Index.Optional,
1388 /// The offset of the relocation inside of `node`.1379 /// The offset of the relocation inside of `node`.
1389 offset: u64,1380 offset: u64,
1390 /// A symbol 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"`.
1391 target: Symbol.Id,1382 target_symbol: Symbol.Id,
1392 /// A signed constant used to compute the relocated value. Precise meaning depends on `@"type"`.1383 /// A signed constant used to compute the relocated value. Precise meaning depends on `bits.@"type"`.
1393 addend: SymbolReloc.Addend,1384 addend: SymbolReloc.Addend,
1394 /// Specifies how to apply the relocation.1385 bits: packed struct(u32) {
1395 ///1386 /// Specifies how to apply the relocation.
1396 /// When emitting a relocatable, this field is `undefined`.1387 ///
1397 type: SymbolReloc.Type,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 },
1398 /// Forms a linked list of all symbol relocations with the same `target`. This list exists so1403 /// Forms a linked list of all symbol relocations with the same `target`. This list exists so
1399 /// that all relocations targeting a particular symbol can be re-applied if that symbol moves.1404 /// that all relocations targeting a particular symbol can be re-applied if that symbol moves.
1400 /// Doubly-linked so that relocations can be removed.1405 /// Doubly-linked so that relocations can be removed.
...@@ -1412,13 +1417,6 @@ const SymbolReloc = struct {...@@ -1412,13 +1417,6 @@ const SymbolReloc = struct {
1412 /// relocation entry. The entry will be removed if we discover a definition which allows us to1417 /// relocation entry. The entry will be removed if we discover a definition which allows us to
1413 /// statically resolve the relocation.1418 /// statically resolve the relocation.
1414 rela_index: Section.RelaIndex.Optional,1419 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
1423 /// Determines the section in which this relocation will be placed if it is outstanding.1421 /// Determines the section in which this relocation will be placed if it is outstanding.
1424 ///1422 ///
...@@ -1449,6 +1447,19 @@ const SymbolReloc = struct {...@@ -1449,6 +1447,19 @@ const SymbolReloc = struct {
1449 },1447 },
1450 };1448 };
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
1452 /// Instead of using the ELF relocation enums, we have our own internal representation for1463 /// Instead of using the ELF relocation enums, we have our own internal representation for
1453 /// relocation types. This representation is more compact (requiring only 16 bits), and allows1464 /// relocation types. This representation is more compact (requiring only 16 bits), and allows
1454 /// sharing a lot of relocation handling between multiple relocs and target architectures.1465 /// sharing a lot of relocation handling between multiple relocs and target architectures.
...@@ -1464,7 +1475,7 @@ const SymbolReloc = struct {...@@ -1464,7 +1475,7 @@ const SymbolReloc = struct {
1464 /// relocations which write multiple sequences of bits or which need to do unusual arithmetic on1475 /// relocations which write multiple sequences of bits or which need to do unusual arithmetic on
1465 /// a symbol value. The representation is simply a big enum containing all of these exceptional1476 /// a symbol value. The representation is simply a big enum containing all of these exceptional
1466 /// cases---see `Special`. This representation is in use when `Type.target == .special`.1477 /// cases---see `Special`. This representation is in use when `Type.target == .special`.
1467 const Type = packed struct(u16) {1478 const Type = packed struct(u18) {
1468 /// Helper function for constructing a "simple" relocation type. This mainly exists to1479 /// Helper function for constructing a "simple" relocation type. This mainly exists to
1469 /// improve readability in the relocation lowering logic in `addRelocAssumeCapacity`.1480 /// improve readability in the relocation lowering logic in `addRelocAssumeCapacity`.
1470 fn simple(target: Target, action: Simple) SymbolReloc.Type {1481 fn simple(target: Target, action: Simple) SymbolReloc.Type {
...@@ -1493,7 +1504,7 @@ const SymbolReloc = struct {...@@ -1493,7 +1504,7 @@ const SymbolReloc = struct {
1493 /// Otherwise (for "simple" relocations), `Target` indicates the first step in computing the1504 /// Otherwise (for "simple" relocations), `Target` indicates the first step in computing the
1494 /// relocation---whether we care about the target symbol's absolute address, its PC-relative1505 /// relocation---whether we care about the target symbol's absolute address, its PC-relative
1495 /// address, its PLT entry, etc.1506 /// address, its PLT entry, etc.
1496 const Target = enum(u3) {1507 const Target = enum(u5) {
1497 /// This is a "special" relocation whose specific type is in the `action.special` field.1508 /// This is a "special" relocation whose specific type is in the `action.special` field.
1498 special,1509 special,
14991510
...@@ -1501,6 +1512,7 @@ const SymbolReloc = struct {...@@ -1501,6 +1512,7 @@ const SymbolReloc = struct {
1501 abs,1512 abs,
1502 /// Offset from the relocation itself to the target symbol ("PC-relative").1513 /// Offset from the relocation itself to the target symbol ("PC-relative").
1503 rel,1514 rel,
1515
1504 /// Address of the target symbol's PLT entry.1516 /// Address of the target symbol's PLT entry.
1505 ///1517 ///
1506 /// If the target symbol does not have a PLT entry, equivalent to `.abs`.1518 /// If the target symbol does not have a PLT entry, equivalent to `.abs`.
...@@ -1509,12 +1521,35 @@ const SymbolReloc = struct {...@@ -1509,12 +1521,35 @@ const SymbolReloc = struct {
1509 ///1521 ///
1510 /// If the target symbol does not have a PLT entry, equivalent to `.rel`.1522 /// If the target symbol does not have a PLT entry, equivalent to `.rel`.
1511 pltrel,1523 pltrel,
1524
1512 /// Offset of the target TLS symbol from the base of this DSO's own TLS region.1525 /// Offset of the target TLS symbol from the base of this DSO's own TLS region.
1513 dtpoff,1526 dtpoff,
1514 /// Offset of the target TLS symbol from the raw thread pointer.1527 /// Offset of the target TLS symbol from the raw thread pointer.
1515 tpoff,1528 tpoff,
1529
1516 /// Size of the target symbol.1530 /// Size of the target symbol.
1517 size,1531 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,
1518 };1553 };
15191554
1520 /// For a "simple" relocation, after the initial value is computed according to `Target`, a1555 /// For a "simple" relocation, after the initial value is computed according to `Target`, a
...@@ -1701,17 +1736,22 @@ const SymbolReloc = struct {...@@ -1701,17 +1736,22 @@ const SymbolReloc = struct {
1701 larch_b21,1736 larch_b21,
1702 larch_b26,1737 larch_b26,
1703 larch_call36,1738 larch_call36,
1739 larch_got_pcala_hi20,
1740 larch_got_pcala64_lo20,
1741 larch_got_pcala64_hi12,
17041742
1705 sparc_olo10,1743 sparc_olo10,
1706 sparc_hix22,1744 sparc_hix22,
1707 sparc_le_hix22,1745 sparc_le_hix22,
1708 sparc_wdisp10,1746 sparc_wdisp10,
1709 sparc_wdisp16,1747 sparc_wdisp16,
1748 sparc_got_op_lox10,
1749 sparc_got_op_hix22,
17101750
1711 fn applyInner(1751 fn applyInner(
1712 s: Special,1752 s: Special,
1713 elf: *Elf,1753 elf: *Elf,
1714 target: Symbol.Id,1754 target_sym: Symbol.Id,
1715 addend: SymbolReloc.Addend,1755 addend: SymbolReloc.Addend,
1716 dest_vaddr: u64,1756 dest_vaddr: u64,
1717 dest_slice: []u8,1757 dest_slice: []u8,
...@@ -1719,7 +1759,7 @@ const SymbolReloc = struct {...@@ -1719,7 +1759,7 @@ const SymbolReloc = struct {
1719 const normal_addend: u64 = @bitCast(addend.normal);1759 const normal_addend: u64 = @bitCast(addend.normal);
1720 switch (s) {1760 switch (s) {
1721 .larch_pcala_hi20 => {1761 .larch_pcala_hi20 => {
1722 const val = target.value(elf) +% normal_addend;1762 const val = target_sym.value(elf) +% normal_addend;
1723 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);1763 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
1724 elf.targetStore(inst, .{1764 elf.targetStore(inst, .{
1725 .b0_4 = elf.targetLoad(inst).b0_4,1765 .b0_4 = elf.targetLoad(inst).b0_4,
...@@ -1728,7 +1768,7 @@ const SymbolReloc = struct {...@@ -1728,7 +1768,7 @@ const SymbolReloc = struct {
1728 });1768 });
1729 },1769 },
1730 .larch_pcala64_lo20 => {1770 .larch_pcala64_lo20 => {
1731 const val = target.value(elf) +% normal_addend;1771 const val = target_sym.value(elf) +% normal_addend;
1732 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);1772 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
1733 elf.targetStore(inst, .{1773 elf.targetStore(inst, .{
1734 .b0_4 = elf.targetLoad(inst).b0_4,1774 .b0_4 = elf.targetLoad(inst).b0_4,
...@@ -1737,7 +1777,7 @@ const SymbolReloc = struct {...@@ -1737,7 +1777,7 @@ const SymbolReloc = struct {
1737 });1777 });
1738 },1778 },
1739 .larch_pcala64_hi12 => {1779 .larch_pcala64_hi12 => {
1740 const val = target.value(elf) +% normal_addend;1780 const val = target_sym.value(elf) +% normal_addend;
1741 const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]);1781 const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]);
1742 elf.targetStore(inst, .{1782 elf.targetStore(inst, .{
1743 .b0_9 = elf.targetLoad(inst).b0_9,1783 .b0_9 = elf.targetLoad(inst).b0_9,
...@@ -1746,7 +1786,7 @@ const SymbolReloc = struct {...@@ -1746,7 +1786,7 @@ const SymbolReloc = struct {
1746 });1786 });
1747 },1787 },
1748 .larch_b21, .larch_b26, .larch_call36 => {1788 .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);
1750 const jump_offset: i64 = @bitCast(target_vaddr +% normal_addend -% dest_vaddr);1790 const jump_offset: i64 = @bitCast(target_vaddr +% normal_addend -% dest_vaddr);
1751 if ((jump_offset >> 2) << 2 != jump_offset) {1791 if ((jump_offset >> 2) << 2 != jump_offset) {
1752 return error.RelocationMisaligned;1792 return error.RelocationMisaligned;
...@@ -1806,9 +1846,43 @@ const SymbolReloc = struct {...@@ -1806,9 +1846,43 @@ const SymbolReloc = struct {
1806 }1846 }
1807 },1847 },
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
1809 .sparc_olo10 => {1883 .sparc_olo10 => {
1810 const addends = addend.sparc_olo10;1884 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));
1812 const simm13 = std.math.cast(i13, lo10 + addends.o) orelse return error.RelocationOverflow;1886 const simm13 = std.math.cast(i13, lo10 + addends.o) orelse return error.RelocationOverflow;
1813 const dest_ptr: *align(1) packed struct(u32) {1887 const dest_ptr: *align(1) packed struct(u32) {
1814 simm13: u13,1888 simm13: u13,
...@@ -1825,7 +1899,7 @@ const SymbolReloc = struct {...@@ -1825,7 +1899,7 @@ const SymbolReloc = struct {
1825 b22_31: u10,1899 b22_31: u10,
1826 } = @ptrCast(dest_slice);1900 } = @ptrCast(dest_slice);
1827 elf.targetStore(dest_ptr, .{1901 elf.targetStore(dest_ptr, .{
1828 .imm22 = @truncate(~(target.value(elf) +% normal_addend) >> 10),1902 .imm22 = @truncate(~(target_sym.value(elf) +% normal_addend) >> 10),
1829 .b22_31 = elf.targetLoad(dest_ptr).b22_31,1903 .b22_31 = elf.targetLoad(dest_ptr).b22_31,
1830 });1904 });
1831 },1905 },
...@@ -1842,12 +1916,12 @@ const SymbolReloc = struct {...@@ -1842,12 +1916,12 @@ const SymbolReloc = struct {
1842 b22_31: u10,1916 b22_31: u10,
1843 } = @ptrCast(dest_slice);1917 } = @ptrCast(dest_slice);
1844 elf.targetStore(dest_ptr, .{1918 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),
1846 .b22_31 = elf.targetLoad(dest_ptr).b22_31,1920 .b22_31 = elf.targetLoad(dest_ptr).b22_31,
1847 });1921 });
1848 },1922 },
1849 .sparc_wdisp16, .sparc_wdisp10 => {1923 .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);
1851 const jump_offset: i64 = @bitCast(target_vaddr +% normal_addend -% dest_vaddr);1925 const jump_offset: i64 = @bitCast(target_vaddr +% normal_addend -% dest_vaddr);
1852 if ((jump_offset >> 2) << 2 != jump_offset) {1926 if ((jump_offset >> 2) << 2 != jump_offset) {
1853 return error.RelocationMisaligned;1927 return error.RelocationMisaligned;
...@@ -1897,10 +1971,120 @@ const SymbolReloc = struct {...@@ -1897,10 +1971,120 @@ const SymbolReloc = struct {
1897 else => unreachable,1971 else => unreachable,
1898 }1972 }
1899 },1973 },
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 },
1900 }1997 }
1901 }1998 }
1902 };1999 };
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
1904 fn dependsOnTlsSize(t: SymbolReloc.Type, elf: *const Elf) bool {2088 fn dependsOnTlsSize(t: SymbolReloc.Type, elf: *const Elf) bool {
1905 return switch (elf.targetTlsVariant()) {2089 return switch (elf.targetTlsVariant()) {
1906 // In TLS variant I, the executable's TLS block starts at a fixed offset from the2090 // In TLS variant I, the executable's TLS block starts at a fixed offset from the
...@@ -1917,6 +2101,15 @@ const SymbolReloc = struct {...@@ -1917,6 +2101,15 @@ const SymbolReloc = struct {
1917 .pltrel,2101 .pltrel,
1918 .dtpoff,2102 .dtpoff,
1919 .size,2103 .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,
1920 => false,2113 => false,
19212114
1922 .tpoff => true,2115 .tpoff => true,
...@@ -1931,11 +2124,16 @@ const SymbolReloc = struct {...@@ -1931,11 +2124,16 @@ const SymbolReloc = struct {
1931 .larch_b21,2124 .larch_b21,
1932 .larch_b26,2125 .larch_b26,
1933 .larch_call36,2126 .larch_call36,
2127 .larch_got_pcala_hi20,
2128 .larch_got_pcala64_lo20,
2129 .larch_got_pcala64_hi12,
19342130
1935 .sparc_olo10,2131 .sparc_olo10,
1936 .sparc_hix22,2132 .sparc_hix22,
1937 .sparc_wdisp10,2133 .sparc_wdisp10,
1938 .sparc_wdisp16,2134 .sparc_wdisp16,
2135 .sparc_got_op_lox10,
2136 .sparc_got_op_hix22,
1939 => false,2137 => false,
1940 },2138 },
1941 },2139 },
...@@ -1954,7 +2152,7 @@ const SymbolReloc = struct {...@@ -1954,7 +2152,7 @@ const SymbolReloc = struct {
19542152
1955 fn apply(reloc: *SymbolReloc, elf: *Elf) void {2153 fn apply(reloc: *SymbolReloc, elf: *Elf) void {
1956 const node = reloc.node.unwrap() orelse return; // deleted2154 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)) {
1958 // There's no point applying the relocation now, because it will be re-applied by2156 // There's no point applying the relocation now, because it will be re-applied by
1959 // `flushMoved` at some point anyway.2157 // `flushMoved` at some point anyway.
1960 return;2158 return;
...@@ -1966,7 +2164,7 @@ const SymbolReloc = struct {...@@ -1966,7 +2164,7 @@ const SymbolReloc = struct {
1966 reloc.relaSection(elf).relaSetOffset(elf, rela_index, reloc_addr);2164 reloc.relaSection(elf).relaSetOffset(elf, rela_index, reloc_addr);
1967 return;2165 return;
1968 }2166 }
1969 switch (reloc.result) {2167 switch (reloc.bits.result) {
1970 .ok => {},2168 .ok => {},
1971 .overflowed => elf.overflowed_reloc_count -= 1,2169 .overflowed => elf.overflowed_reloc_count -= 1,
1972 .misaligned => elf.misaligned_reloc_count -= 1,2170 .misaligned => elf.misaligned_reloc_count -= 1,
...@@ -1977,14 +2175,14 @@ const SymbolReloc = struct {...@@ -1977,14 +2175,14 @@ const SymbolReloc = struct {
1977 }2175 }
1978 if (reloc.applyInner(elf)) {2176 if (reloc.applyInner(elf)) {
1979 @branchHint(.likely);2177 @branchHint(.likely);
1980 reloc.result = .ok;2178 reloc.bits.result = .ok;
1981 } else |err| switch (err) {2179 } else |err| switch (err) {
1982 error.RelocationOverflow => {2180 error.RelocationOverflow => {
1983 reloc.result = .overflowed;2181 reloc.bits.result = .overflowed;
1984 elf.overflowed_reloc_count += 1;2182 elf.overflowed_reloc_count += 1;
1985 },2183 },
1986 error.RelocationMisaligned => {2184 error.RelocationMisaligned => {
1987 reloc.result = .misaligned;2185 reloc.bits.result = .misaligned;
1988 elf.misaligned_reloc_count += 1;2186 elf.misaligned_reloc_count += 1;
1989 },2187 },
1990 }2188 }
...@@ -1995,21 +2193,21 @@ const SymbolReloc = struct {...@@ -1995,21 +2193,21 @@ const SymbolReloc = struct {
1995 const dest_slice = node.slice(&elf.mf)[@intCast(reloc.offset)..];2193 const dest_slice = node.slice(&elf.mf)[@intCast(reloc.offset)..];
19962194
1997 const addend: u64 = @bitCast(reloc.addend);2195 const addend: u64 = @bitCast(reloc.addend);
1998 const target_val: u64 = type: switch (reloc.type.target) {2196 const target_val: u64 = type: switch (reloc.bits.type.target) {
1999 .abs => reloc.target.value(elf) +% addend,2197 .abs => reloc.target_symbol.value(elf) +% addend,
2000 .rel => reloc.target.value(elf) +% addend -% dest_vaddr,2198 .rel => reloc.target_symbol.value(elf) +% addend -% dest_vaddr,
2001 .pltabs => {2199 .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;
2003 break :type plt_entry_addr +% addend;2201 break :type plt_entry_addr +% addend;
2004 },2202 },
2005 .pltrel => {2203 .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;
2007 break :type plt_entry_addr +% addend -% dest_vaddr;2205 break :type plt_entry_addr +% addend -% dest_vaddr;
2008 },2206 },
2009 .dtpoff => reloc.target.value(elf) +% addend,2207 .dtpoff => reloc.target_symbol.value(elf) +% addend,
2010 .tpoff => switch (elf.targetTlsVariant()) {2208 .tpoff => switch (elf.targetTlsVariant()) {
2011 .I_original => |tls| tls.tcb_size +% reloc.target.value(elf) +% addend,2209 .I_original => |tls| tls.tcb_size +% reloc.target_symbol.value(elf) +% addend,
2012 .I_modified => |tls| 0 -% tls.tp_off +% reloc.target.value(elf) +% addend,2210 .I_modified => |tls| 0 -% tls.tp_off +% reloc.target_symbol.value(elf) +% addend,
2013 .II => {2211 .II => {
2014 const tls_phndx = elf.getNode(elf.ni.tls.unwrap().?).segment;2212 const tls_phndx = elf.getNode(elf.ni.tls.unwrap().?).segment;
2015 const tls_size: u64 = switch (elf.phdrSlice()) {2213 const tls_size: u64 = switch (elf.phdrSlice()) {
...@@ -2018,13 +2216,57 @@ const SymbolReloc = struct {...@@ -2018,13 +2216,57 @@ const SymbolReloc = struct {
2018 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);2216 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
2019 },2217 },
2020 };2218 };
2021 break :type reloc.target.value(elf) +% addend -% tls_size;2219 break :type reloc.target_symbol.value(elf) +% addend -% tls_size;
2022 },2220 },
2023 },2221 },
2024 .size => reloc.target.size(elf),2222 .size => reloc.target_symbol.size(elf),
2025 .special => return reloc.type.action.special.applyInner(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(
2026 elf,2268 elf,
2027 reloc.target,2269 reloc.target_symbol,
2028 reloc.addend,2270 reloc.addend,
2029 dest_vaddr,2271 dest_vaddr,
2030 dest_slice,2272 dest_slice,
...@@ -2033,7 +2275,7 @@ const SymbolReloc = struct {...@@ -2033,7 +2275,7 @@ const SymbolReloc = struct {
20332275
2034 // Check for the `R_*_RELATIVE` case now, because it is possible only when no shift or cast2276 // Check for the `R_*_RELATIVE` case now, because it is possible only when no shift or cast
2035 // is required, meaning we can handle it now and return early.2277 // 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)) {
2037 .static => unreachable,2279 .static => unreachable,
2038 .dynamic => {2280 .dynamic => {
2039 // The relocation happens at runtime; we just need to update its offset.2281 // The relocation happens at runtime; we just need to update its offset.
...@@ -2046,31 +2288,34 @@ const SymbolReloc = struct {...@@ -2046,31 +2288,34 @@ const SymbolReloc = struct {
2046 // destination slice, we actually want to write it to the runtime relocation entry.2288 // destination slice, we actually want to write it to the runtime relocation entry.
2047 switch (elf.identClass()) {2289 switch (elf.identClass()) {
2048 .NONE, _ => unreachable,2290 .NONE, _ => unreachable,
2049 .@"32" => assert(reloc.type.action.simple.dest == .@"32"),2291 .@"32" => assert(reloc.bits.type.action.simple.dest == .@"32"),
2050 .@"64" => assert(reloc.type.action.simple.dest == .@"64"),2292 .@"64" => assert(reloc.bits.type.action.simple.dest == .@"64"),
2051 }2293 }
2052 assert(reloc.type.action.simple.cast == .unsigned);2294 assert(reloc.bits.type.action.simple.cast == .unsigned);
2053 assert(reloc.type.action.simple.shift == .@"0");2295 assert(reloc.bits.type.action.simple.shift == .@"0");
2054 elf.shndx.rela_dyn.relaSetOffset(elf, rela_index, dest_vaddr);2296 elf.shndx.rela_dyn.relaSetOffset(elf, rela_index, dest_vaddr);
2055 elf.shndx.rela_dyn.relaSetAddend(elf, rela_index, target_val);2297 elf.shndx.rela_dyn.relaSetAddend(elf, rela_index, target_val);
2056 return;2298 return;
2057 },2299 },
2058 };2300 };
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());
2061 }2303 }
20622304
2063 fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void {2305 fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void {
2064 assert(index.get(elf) == reloc);2306 assert(index.get(elf) == reloc);
20652307
2066 reloc.deleteOutputRel(elf);2308 reloc.deleteOutputRel(elf);
2067 if (reloc.type.dependsOnTlsSize(elf)) {2309 if (reloc.bits.type.dependsOnTlsSize(elf)) {
2068 assert(elf.tls_size_symbol_relocs.swapRemove(index));2310 assert(elf.tls_size_symbol_relocs.swapRemove(index));
2069 }2311 }
2312 if (reloc.bits.type.dependsOnGotAddress()) {
2313 assert(elf.got_relocs.swapRemove(index));
2314 }
20702315
2071 switch (reloc.prev) {2316 switch (reloc.prev) {
2072 .none => {2317 .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;
2074 assert(first_target_reloc.* == index);2319 assert(first_target_reloc.* == index);
2075 first_target_reloc.* = reloc.next;2320 first_target_reloc.* = reloc.next;
2076 },2321 },
...@@ -2080,7 +2325,7 @@ const SymbolReloc = struct {...@@ -2080,7 +2325,7 @@ const SymbolReloc = struct {
2080 .none => {},2325 .none => {},
2081 else => |next| next.get(elf).prev = reloc.prev,2326 else => |next| next.get(elf).prev = reloc.prev,
2082 }2327 }
2083 switch (reloc.result) {2328 switch (reloc.bits.result) {
2084 .ok => {},2329 .ok => {},
2085 .overflowed => elf.overflowed_reloc_count -= 1,2330 .overflowed => elf.overflowed_reloc_count -= 1,
2086 .misaligned => elf.misaligned_reloc_count -= 1,2331 .misaligned => elf.misaligned_reloc_count -= 1,
...@@ -2108,30 +2353,58 @@ const SymbolReloc = struct {...@@ -2108,30 +2353,58 @@ const SymbolReloc = struct {
2108 reloc.rela_index = .none;2353 reloc.rela_index = .none;
2109 }2354 }
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 {
2112 assert(elf.ehdrType() != .REL);2358 assert(elf.ehdrType() != .REL);
2113 const reloc_node = reloc.node.unwrap().?; // deleted2359 const reloc_node = reloc.node.unwrap().?; // deleted
21142360
2361 const trace = tracy.trace(@src());
2362 defer trace.end();
2363
2115 // Delete any current output relocation or error.2364 // Delete any current output relocation or error.
2116 switch (reloc.result) {2365 switch (reloc.bits.result) {
2117 .ok, .overflowed, .misaligned => {2366 .ok, .overflowed, .misaligned => {
2118 reloc.deleteOutputRel(elf);2367 reloc.deleteOutputRel(elf);
2119 },2368 },
2120 .illegal_dynamic_type => {2369 .illegal_dynamic_type => {
2121 elf.illegal_dynamic_type_reloc_count -= 1;2370 elf.illegal_dynamic_type_reloc_count -= 1;
2122 reloc.result = .ok;2371 reloc.bits.result = .ok;
2123 },2372 },
2124 .illegal_dynamic_section => {2373 .illegal_dynamic_section => {
2125 elf.illegal_dynamic_section_reloc_count -= 1;2374 elf.illegal_dynamic_section_reloc_count -= 1;
2126 reloc.result = .ok;2375 reloc.bits.result = .ok;
2127 },2376 },
2128 }2377 }
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
2130 assert(reloc.rela_index == .none);2403 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()) {
2135 .local => {},2408 .local => {},
2136 .global => |gsi| switch (symbol_class) {2409 .global => |gsi| switch (symbol_class) {
2137 .static => gsi.ptr(elf).status.any_static_target_relocs = true,2410 .static => gsi.ptr(elf).status.any_static_target_relocs = true,
...@@ -2152,25 +2425,36 @@ const SymbolReloc = struct {...@@ -2152,25 +2425,36 @@ const SymbolReloc = struct {
2152 } = switch (symbol_class) {2425 } = switch (symbol_class) {
2153 .static => return,2426 .static => return,
21542427
2155 .static_relative => switch (reloc.type.target) {2428 .static_relative => switch (reloc.bits.type.target) {
2156 // We need a runtime relocation if the relocation depends on our load address, which2429 // We need a runtime relocation if the relocation depends on our load address, which
2157 // is the case iff the relocation resolves to the absolute value of a symbol. Relocs2430 // is the case iff the relocation resolves to the absolute value of a symbol. Relocs
2158 // which are PC-relative, or which resolve to something other than a symbol address2431 // which are PC-relative, or which resolve to something other than a symbol address
2159 // (e.g. a symbol's size), are fine.2432 // (e.g. a symbol's size), are fine.
21602433
2161 .pltrel,2434 .pltrel,
2435 .gotrel_symbol,
2436 .gotoff_symbol,
2437 .gotrel_tpoff,
2438 .gotoff_tpoff,
2439 .gotrel_tlsgd,
2440 .gotoff_tlsgd,
2162 .rel,2441 .rel,
2163 .size,2442 .size,
2164 .dtpoff,2443 .dtpoff,
2165 .tpoff,2444 .tpoff,
2166 => return,2445 => 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)) {
2169 true => .relative,2453 true => .relative,
2170 false => .illegal,2454 false => .illegal,
2171 },2455 },
21722456
2173 .special => switch (reloc.type.action.special) {2457 .special => switch (reloc.bits.type.action.special) {
2174 .larch_pcala_hi20,2458 .larch_pcala_hi20,
2175 .larch_pcala64_lo20,2459 .larch_pcala64_lo20,
2176 .larch_pcala64_hi12,2460 .larch_pcala64_hi12,
...@@ -2180,6 +2464,11 @@ const SymbolReloc = struct {...@@ -2180,6 +2464,11 @@ const SymbolReloc = struct {
2180 .sparc_wdisp10,2464 .sparc_wdisp10,
2181 .sparc_wdisp16,2465 .sparc_wdisp16,
2182 .sparc_le_hix22,2466 .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,
2183 => return,2472 => return,
21842473
2185 .sparc_olo10,2474 .sparc_olo10,
...@@ -2188,44 +2477,60 @@ const SymbolReloc = struct {...@@ -2188,44 +2477,60 @@ const SymbolReloc = struct {
2188 },2477 },
2189 },2478 },
21902479
2191 .dynamic => switch (reloc.type.target) {2480 .dynamic => switch (reloc.bits.type.target) {
2192 // PLT relocations targeting dynamic symbols actually target that symbol's PLT2481 // PLT/GOT relocations targeting dynamic symbols actually target that symbol's PLT
2193 // entry, so we should emit an `R_*_RELATIVE` relocation instead.2482 // or GOT entry, so we should emit an `R_*_RELATIVE` relocation instead.
2194 .pltabs => switch (reloc.type.action.simple.dest.isAddr(elf)) {2483 .pltabs,
2484 .gotabs_symbol,
2485 .gotabs_tpoff,
2486 .gotabs_tlsgd,
2487 => switch (reloc.bits.type.action.simple.dest.isAddr(elf)) {
2195 true => .relative,2488 true => .relative,
2196 false => .illegal,2489 false => .illegal,
2197 },2490 },
2198 // ...although PC-relative PLT relocations don't even need that that!2491 // ...although PC-relative PLT/GOT relocations don't even need that that!
2199 .pltrel => return,2492 .pltrel,
2493 .gotrel_symbol,
2494 .gotoff_symbol,
2495 .gotrel_tpoff,
2496 .gotoff_tpoff,
2497 .gotrel_tlsgd,
2498 .gotoff_tlsgd,
2499 => return,
2200 // PC-relative relocations are not valid dynamic relocations.2500 // PC-relative relocations are not valid dynamic relocations.
2201 .rel => .illegal,2501 .rel => .illegal,
2202 // On the few targets supporting size relocations, they are valid at runtime.2502 // 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) {
2204 .@"32" => .size32,2504 .@"32" => .size32,
2205 .@"64" => .size64,2505 .@"64" => .size64,
2206 else => .illegal,2506 else => .illegal,
2207 },2507 },
2208 // Absolute addresses and TLS offsets can be lowered at runtime provided they2508 // Absolute addresses and TLS offsets can be lowered at runtime provided they
2209 // are address-sized.2509 // are address-sized.
2210 .dtpoff => switch (reloc.type.action.simple.dest.isAddr(elf)) {2510 .dtpoff => switch (reloc.bits.type.action.simple.dest.isAddr(elf)) {
2211 true => .dtp_off,2511 true => .dtp_off,
2212 false => .illegal,2512 false => .illegal,
2213 },2513 },
2214 .tpoff => switch (reloc.type.action.simple.dest.isAddr(elf)) {2514 .tpoff => switch (reloc.bits.type.action.simple.dest.isAddr(elf)) {
2215 true => .tp_off,2515 true => .tp_off,
2216 false => .illegal,2516 false => .illegal,
2217 },2517 },
2218 .abs => switch (reloc.type.action.simple.dest.isAddr(elf)) {2518 .abs => switch (reloc.bits.type.action.simple.dest.isAddr(elf)) {
2219 true => .abs_addr,2519 true => .abs_addr,
2220 false => .illegal,2520 false => .illegal,
2221 },2521 },
22222522
2223 .special => switch (reloc.type.action.special) {2523 .special => switch (reloc.bits.type.action.special) {
2224 .larch_b21,2524 .larch_b21,
2225 .larch_b26,2525 .larch_b26,
2226 .larch_call36,2526 .larch_call36,
2227 .sparc_wdisp10,2527 .sparc_wdisp10,
2228 .sparc_wdisp16,2528 .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,
2229 => return,2534 => return,
22302535
2231 .larch_pcala_hi20,2536 .larch_pcala_hi20,
...@@ -2255,7 +2560,7 @@ const SymbolReloc = struct {...@@ -2255,7 +2560,7 @@ const SymbolReloc = struct {
2255 .abs_addr,2560 .abs_addr,
2256 .dtp_off,2561 .dtp_off,
2257 .tp_off,2562 .tp_off,
2258 => switch (reloc.target.unwrap()) {2563 => switch (reloc.target_symbol.unwrap()) {
2259 .local => true,2564 .local => true,
2260 .global => |gsi| gsi.resolveAlias(elf).defined(elf),2565 .global => |gsi| gsi.resolveAlias(elf).defined(elf),
2261 },2566 },
...@@ -2267,7 +2572,7 @@ const SymbolReloc = struct {...@@ -2267,7 +2572,7 @@ const SymbolReloc = struct {
2267 };2572 };
2268 if (!allow) {2573 if (!allow) {
2269 // ...but in this case we actually do need to trigger an error.2574 // ...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;
2271 elf.illegal_dynamic_section_reloc_count += 1;2576 elf.illegal_dynamic_section_reloc_count += 1;
2272 }2577 }
2273 return;2578 return;
...@@ -2278,7 +2583,7 @@ const SymbolReloc = struct {...@@ -2278,7 +2583,7 @@ const SymbolReloc = struct {
22782583
2279 const rel_type: MachineRelocType = rel: switch (want_dynamic_reloc) {2584 const rel_type: MachineRelocType = rel: switch (want_dynamic_reloc) {
2280 .illegal => {2585 .illegal => {
2281 reloc.result = .illegal_dynamic_type;2586 reloc.bits.result = .illegal_dynamic_type;
2282 elf.illegal_dynamic_type_reloc_count += 1;2587 elf.illegal_dynamic_type_reloc_count += 1;
2283 return;2588 return;
2284 },2589 },
...@@ -2292,7 +2597,7 @@ const SymbolReloc = struct {...@@ -2292,7 +2597,7 @@ const SymbolReloc = struct {
2292 const target_dynsym: u32 = switch (want_dynamic_reloc) {2597 const target_dynsym: u32 = switch (want_dynamic_reloc) {
2293 .illegal => unreachable, // handled above2598 .illegal => unreachable, // handled above
2294 .relative => 0,2599 .relative => 0,
2295 else => reloc.target.unwrap().global.dynsymIndex(elf).?,2600 else => reloc.target_symbol.unwrap().global.dynsymIndex(elf).?,
2296 };2601 };
22972602
2298 try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, 1);2603 try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, 1);
...@@ -2435,305 +2740,98 @@ const GotKey = union(enum) {...@@ -2435,305 +2740,98 @@ const GotKey = union(enum) {
2435 /// Value is the signed offset of the given symbol from the TLS pointer.2740 /// Value is the signed offset of the given symbol from the TLS pointer.
2436 tpoff: Symbol.Id,2741 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).
2439 ///2745 ///
2440 /// Used for the first of the two GOT entries generated by a TLSLD relocation.2746 /// Alternative
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.
2448 ///2747 ///
2449 /// Used for the first of the two GOT entries generated by a TLSGD relocation.2748 /// Used for the first of the two GOT entries generated by a TLSGD relocation.
2450 tlsgd0: Symbol.Id,2749 tlsgd0: Symbol.Id,
2451 /// Value is the offset of the given STT_TLS symbol from the base of the per-module TLS area.2750 /// 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).
2452 ///2752 ///
2453 /// Used for the second of the two GOT entries generated by a TLSGD relocation.2753 /// Used for the second of the two GOT entries generated by a TLSGD relocation.
2454 tlsgd1: Symbol.Id,2754 tlsgd1: Symbol.Id,
2455};2755};
24562756
2457/// A relocation targeting a particular GOT entry.2757/// Records all global symbols defined in any DSO we depend on. For each symbol, its name, version,
2458const GotReloc = struct {2758/// type, size, and alignment are all stored, as well as the soname of the DSO defining it. If one
2459 /// The node containing this relocation. Possible values are:2759/// of these symbols ends up being referenced by the ELF module we are producing, we need this
2460 /// * An input section2760/// information for a few reasons:
2461 /// * A section2761///
2462 /// * A NAV, UAV, or lazy code/data2762/// * If an undefined symbol has an external definition with type `STT_FUNC`, we know to create a
2463 /// * `.none`, if this relocation was deleted (in which case it should be ignored)2763/// PLT entry for it.
2464 node: MappedFile.Node.Index.Optional,2764///
2465 /// The offset of the relocation inside of `node`.2765/// * If an undefined symbol has an external definition with type `STT_OBJECT`, we can create a copy
2466 offset: u64,2766/// relocation for it if necessary.
2467 target: GotKey,2767///
2468 addend: i64,2768/// * If an undefined versioned symbol has an external definition, we can add the required entry to
2469 type: GotReloc.Type,2769/// `.gnu.version_r` because we know the soname of the DSO defining it.
2470 result: enum(u8) { ok, overflowed, misaligned },2770///
24712771/// * Similarly, if an undefined unversioned symbol matches an external definition of a default
2472 /// `GotReloc.Type` has the same structure as `SymbolReloc.Type`, just with different `Target`2772/// symbol version, we know to emit the default version for the dynamic symbol table entry even
2473 /// and `Special` enums---consult doc comments on `SymbolReloc.Type` for an overview.2773/// though that symbol version was not explicitly requested.
2474 const Type = packed struct(u16) {2774///
2475 fn simple(target: Target, action: Simple) GotReloc.Type {2775/// * If an undefined symbol does *not* have any external definition, when emitting a dynamic
2476 assert(target != .special);2776/// executable, we can emit an "undefined global symbol" link error for it.
2477 return .{ .target = target, .action = .{ .simple = action } };2777const DsoGlobals = struct {
2478 }2778 string_bytes: std.ArrayList(u8),
2479
2480 fn special(s: Special) GotReloc.Type {
2481 return .{ .target = .special, .action = .{ .special = s } };
2482 }
24832779
2484 target: Target,2780 /// Contains all global symbols defined in any needed DSO.
2485 action: packed union {2781 ///
2486 simple: Simple,2782 /// Accessed using `DsoGlobals.Symbol.Adapter`. Adapted key type is `DsoGlobals.Symbol.Key`.
2487 special: Special,2783 symbols: std.array_hash_map.Custom(DsoGlobals.Symbol, void, void, true),
2488 },
24892784
2490 /// Like `SymbolReloc.Target`, but for GOT relocations. There are fewer tags because there2785 /// Contains all default-version global symbols defined in any needed DSO.
2491 /// are fewer different kinds of GOT relocation.2786 ///
2492 const Target = enum(u3) {2787 /// Accessed using `DsoGlobals.Symbol.DefaultVersionAdapter`. Adapted key type is `[]const u8`.
2493 /// This is a "special" relocation whose specific type is in the `action.special` field.2788 ///
2494 special,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.2792 /// Index into `DsoGlobals.string_bytes`. This is a different type than `Elf.String` because
2497 abs,2793 /// these string bytes are in a `std.ArrayList` rather than a section of the output binary.
2498 /// Offset from the relocation itself to the GOT entry ("PC-relative").2794 const String = enum(u32) {
2499 rel,2795 _,
2500 /// Offset from the base of the GOT to the GOT entry.2796 fn slice(s: DsoGlobals.String, dso_globals: *const DsoGlobals) [:0]const u8 {
2501 offset,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 }
2502 };2812 };
2813 };
25032814
2504 const Simple = SymbolReloc.Type.Simple;2815 const Symbol = struct {
25052816 name: DsoGlobals.String,
2506 /// Like `SymbolReloc.Special`, but for GOT relocations.2817 /// `.none` if the symbol is unversioned.
2507 const Special = enum(u13) {2818 version: DsoGlobals.String.Optional,
2508 larch_pcala_hi20,2819 type: std.elf.STT,
2509 larch_pcala64_lo20,2820 soname: Elf.String(.dynstr),
2510 larch_pcala64_hi12,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,2831 const Key = struct {
2513 sparc_op_hix22,2832 name: []const u8,
25142833 version: ?[]const u8,
2515 fn applyInner(2834 };
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 };
27372835
2738 const Adapter = struct {2836 const Adapter = struct {
2739 dso_globals: *const DsoGlobals,2837 dso_globals: *const DsoGlobals,
...@@ -3000,6 +3098,12 @@ const AddLocalSymbolOptions = struct {...@@ -3000,6 +3098,12 @@ const AddLocalSymbolOptions = struct {
3000fn addLocalSymbol(elf: *Elf, opts: AddLocalSymbolOptions) Error!Symbol.LocalIndex {3098fn addLocalSymbol(elf: *Elf, opts: AddLocalSymbolOptions) Error!Symbol.LocalIndex {
3001 const gpa = elf.base.comp.gpa;3099 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
3003 try elf.symtab.ensureUnusedCapacity(gpa, 1);3107 try elf.symtab.ensureUnusedCapacity(gpa, 1);
3004 try elf.changed_symtab_index.ensureUnusedCapacity(gpa, 1);3108 try elf.changed_symtab_index.ensureUnusedCapacity(gpa, 1);
3005 try Section.Index.symtab.node(elf).ensureMinimumSize(gpa, &elf.mf, switch (elf.shdrPtr(.symtab)) {3109 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{...@@ -3074,6 +3178,10 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{
3074} || Error)!Symbol.Global.Index {3178} || Error)!Symbol.Global.Index {
3075 _ = opts.lib_name; // TODO3179 _ = opts.lib_name; // TODO
30763180
3181 const trace = tracy.trace(@src());
3182 defer trace.end();
3183 trace.addText(opts.name);
3184
3077 log.debug("addGlobalSymbol({q})", .{opts.name});3185 log.debug("addGlobalSymbol({q})", .{opts.name});
30783186
3079 const gpa = elf.base.comp.gpa;3187 const gpa = elf.base.comp.gpa;
...@@ -4259,6 +4367,9 @@ const Symbol = struct {...@@ -4259,6 +4367,9 @@ const Symbol = struct {
4259 /// Returns `true` if any relocations were updated, in which case relocations targeting4367 /// Returns `true` if any relocations were updated, in which case relocations targeting
4260 /// `gsi` must be re-applied by the caller.4368 /// `gsi` must be re-applied by the caller.
4261 fn updateDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) Error!bool {4369 fn updateDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) Error!bool {
4370 const trace = tracy.trace(@src());
4371 defer trace.end();
4372
4262 if (elf.shndx.dynamic == .UNDEF) return false;4373 if (elf.shndx.dynamic == .UNDEF) return false;
4263 const sym_id: Symbol.Id = .global(gsi);4374 const sym_id: Symbol.Id = .global(gsi);
42644375
...@@ -4293,8 +4404,8 @@ const Symbol = struct {...@@ -4293,8 +4404,8 @@ const Symbol = struct {
4293 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;4404 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
4294 while (ri != .none) {4405 while (ri != .none) {
4295 const reloc = ri.get(elf);4406 const reloc = ri.get(elf);
4296 assert(reloc.target == sym_id);4407 assert(reloc.target_symbol == sym_id);
4297 try reloc.updateDynamicReloc(elf);4408 try reloc.updateDynamicReloc(elf, ri);
4298 ri = reloc.next;4409 ri = reloc.next;
4299 }4410 }
43004411
...@@ -4429,20 +4540,8 @@ const Symbol = struct {...@@ -4429,20 +4540,8 @@ const Symbol = struct {
4429 }4540 }
44304541
4431 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {4542 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {
4432 const trace = tracy.trace(@src());
4433 defer trace.end();
4434
4435 const sym_index = sym_id.index(elf);4543 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
4446 switch (elf.symPtr(sym_index)) {4545 switch (elf.symPtr(sym_index)) {
4447 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),4546 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
4448 }4547 }
...@@ -4467,10 +4566,13 @@ const Symbol = struct {...@@ -4467,10 +4566,13 @@ const Symbol = struct {
4467 }4566 }
44684567
4469 fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {4568 fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
4569 const trace = tracy.trace(@src());
4570 defer trace.end();
4571
4470 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;4572 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
4471 while (ri != .none) {4573 while (ri != .none) {
4472 const reloc = ri.get(elf);4574 const reloc = ri.get(elf);
4473 assert(reloc.target == sym_id);4575 assert(reloc.target_symbol == sym_id);
4474 reloc.apply(elf);4576 reloc.apply(elf);
4475 ri = reloc.next;4577 ri = reloc.next;
4476 }4578 }
...@@ -4563,7 +4665,7 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {...@@ -4563,7 +4665,7 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
45634665
4564 const shndx: Section.Index = switch (resolved_sym.unwrap()) {4666 const shndx: Section.Index = switch (resolved_sym.unwrap()) {
4565 .local => shndx: {4667 .local => shndx: {
4566 assert(symtab_shndx != .UNDEF);4668 assert(sym == Symbol.Id.null or symtab_shndx != .UNDEF);
4567 assert(visibility == .DEFAULT);4669 assert(visibility == .DEFAULT);
4568 break :shndx symtab_shndx;4670 break :shndx symtab_shndx;
4569 },4671 },
...@@ -4683,7 +4785,6 @@ fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.Symbol...@@ -4683,7 +4785,6 @@ fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.Symbol
4683 .shndx = shndx,4785 .shndx = shndx,
4684 }),4786 }),
4685 .first_symbol_reloc = .none,4787 .first_symbol_reloc = .none,
4686 .first_got_reloc = .none,
4687 };4788 };
4688 elf.base.comp.link_prog_node.increaseEstimatedTotalItems(1);4789 elf.base.comp.link_prog_node.increaseEstimatedTotalItems(1);
4689 }4790 }
...@@ -4871,31 +4972,20 @@ fn stringExisting(elf: *Elf, comptime section: StringSection, key: []const u8) S...@@ -4871,31 +4972,20 @@ fn stringExisting(elf: *Elf, comptime section: StringSection, key: []const u8) S
4871}4972}
48724973
4873const StringTable = struct {4974const StringTable = struct {
4874 map: std.HashMapUnmanaged(u32, void, StringTable.Context, std.hash_map.default_max_load_percentage),4975 map: std.array_hash_map.Custom(u32, void, void, true),
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 };
48874976
4888 const Adapter = struct {4977 const Adapter = struct {
4889 slice: []const u8,4978 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;
4892 return std.mem.startsWith(u8, adapter.slice[rhs_key..], lhs_key) and4982 return std.mem.startsWith(u8, adapter.slice[rhs_key..], lhs_key) and
4893 adapter.slice[rhs_key + lhs_key.len] == 0;4983 adapter.slice[rhs_key + lhs_key.len] == 0;
4894 }4984 }
48954985
4896 pub fn hash(_: Adapter, key: []const u8) u64 {4986 pub fn hash(_: Adapter, key: []const u8) u32 {
4897 assert(std.mem.findScalar(u8, key, 0) == null);4987 assert(std.mem.findScalar(u8, key, 0) == null);
4898 return std.hash_map.hashString(key);4988 return std.array_hash_map.hashString(key);
4899 }4989 }
4900 };4990 };
49014991
...@@ -4909,8 +4999,7 @@ const StringTable = struct {...@@ -4909,8 +4999,7 @@ const StringTable = struct {
4909 fn ensureAdditionalCapacity(st: *StringTable, elf: *Elf, shndx: Section.Index, string_len: usize) Error!void {4999 fn ensureAdditionalCapacity(st: *StringTable, elf: *Elf, shndx: Section.Index, string_len: usize) Error!void {
4910 const gpa = elf.base.comp.gpa;5000 const gpa = elf.base.comp.gpa;
4911 const ni = shndx.node(elf);5001 const ni = shndx.node(elf);
4912 const slice_const = ni.sliceConst(&elf.mf);5002 try st.map.ensureUnusedCapacity(gpa, 1);
4913 try st.map.ensureUnusedCapacityContext(gpa, 1, .{ .slice = slice_const });
4914 const size: u64 = switch (elf.shdrPtr(shndx)) {5003 const size: u64 = switch (elf.shdrPtr(shndx)) {
4915 inline else => |shdr| elf.targetLoad(&shdr.size),5004 inline else => |shdr| elf.targetLoad(&shdr.size),
4916 };5005 };
...@@ -5035,6 +5124,11 @@ fn create(...@@ -5035,6 +5124,11 @@ fn create(
5035 },5124 },
5036 .options = options,5125 .options = options,
5037 .mf = try .init(file, comp.gpa, io),5126 .mf = try .init(file, comp.gpa, io),
5127 .ehdr_fields = .{
5128 .class = class,
5129 .type = @"type",
5130 .machine = machine,
5131 },
5038 .ni = .{5132 .ni = .{
5039 .elf = undefined,5133 .elf = undefined,
5040 .ehdr = undefined,5134 .ehdr = undefined,
...@@ -6787,7 +6881,6 @@ pub fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {...@@ -6787,7 +6881,6 @@ pub fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
6787 skip_symbol_relocs: MappedFile.Node.Index.Optional = .none,6881 skip_symbol_relocs: MappedFile.Node.Index.Optional = .none,
6788 first_node_reloc: ?*NodeReloc.Index = null,6882 first_node_reloc: ?*NodeReloc.Index = null,
6789 skip_node_relocs: MappedFile.Node.Index.Optional = .none,6883 skip_node_relocs: MappedFile.Node.Index.Optional = .none,
6790 first_got_reloc: ?*GotReloc.Index = null,
6791 } = switch (elf.getNode(ni)) {6884 } = switch (elf.getNode(ni)) {
6792 .deleted,6885 .deleted,
6793 .archive,6886 .archive,
...@@ -6814,18 +6907,15 @@ pub fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {...@@ -6814,18 +6907,15 @@ pub fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
6814 => unreachable, // cannot contain relocs (.plt, .dynamic, and .eh_frame_hdr unsupported)6907 => unreachable, // cannot contain relocs (.plt, .dynamic, and .eh_frame_hdr unsupported)
6815 .input_section => |isi| .{6908 .input_section => |isi| .{
6816 .first_symbol_reloc = &elf.input_sections.items[@backingInt(isi)].first_symbol_reloc,6909 .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,
6818 },6910 },
6819 .nav => |nmi| .{6911 .nav => |nmi| .{
6820 .first_symbol_reloc = &elf.navs.values()[@backingInt(nmi)].first_symbol_reloc,6912 .first_symbol_reloc = &elf.navs.values()[@backingInt(nmi)].first_symbol_reloc,
6821 .first_got_reloc = &elf.navs.values()[@backingInt(nmi)].first_got_reloc,
6822 },6913 },
6823 .uav => |umi| .{6914 .uav => |umi| .{
6824 .first_symbol_reloc = &elf.uavs.values()[@backingInt(umi)].first_symbol_reloc,6915 .first_symbol_reloc = &elf.uavs.values()[@backingInt(umi)].first_symbol_reloc,
6825 },6916 },
6826 inline .lazy_code, .lazy_const_data => |lmi| .{6917 inline .lazy_code, .lazy_const_data => |lmi| .{
6827 .first_symbol_reloc = &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_symbol_reloc,6918 .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,
6829 },6919 },
6830 .unit_debug_info_header => |ui| .{6920 .unit_debug_info_header => |ui| .{
6831 .first_node_reloc = &elf.dwarf_units[@backingInt(ui)].debug_info_header_first_node_reloc,6921 .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 {...@@ -6893,19 +6983,6 @@ pub fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
6893 }6983 }
6894 ptr.* = @fromBackingInt(@intCast(elf.node_relocs.items.len));6984 ptr.* = @fromBackingInt(@intCast(elf.node_relocs.items.len));
6895 }6985 }
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 }
6909}6986}
69106987
6911/// Given that `node` has moved, updates all relocations in `node` as needed. In relocatables, this6988/// Given that `node` has moved, updates all relocations in `node` as needed. In relocatables, this
...@@ -6918,7 +6995,6 @@ fn flushMovedNodeRelocs(...@@ -6918,7 +6995,6 @@ fn flushMovedNodeRelocs(
6918 skip_symbol_relocs: MappedFile.Node.Index.Optional = .none,6995 skip_symbol_relocs: MappedFile.Node.Index.Optional = .none,
6919 first_node_reloc: NodeReloc.Index = .none,6996 first_node_reloc: NodeReloc.Index = .none,
6920 skip_node_relocs: MappedFile.Node.Index.Optional = .none,6997 skip_node_relocs: MappedFile.Node.Index.Optional = .none,
6921 first_got_reloc: GotReloc.Index = .none,
6922 },6998 },
6923) void {6999) void {
6924 if (opts.first_symbol_reloc != .none) {7000 if (opts.first_symbol_reloc != .none) {
...@@ -6942,20 +7018,12 @@ fn flushMovedNodeRelocs(...@@ -6942,20 +7018,12 @@ fn flushMovedNodeRelocs(
6942 reloc.apply(elf);7018 reloc.apply(elf);
6943 }7019 }
6944 }7020 }
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 }
6955}7021}
69567022
6957fn identClass(elf: *const Elf) std.elf.CLASS {7023fn 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;
6959}7027}
69607028
6961/// Like `std.elf.ET`, but only includes the ELF machine architectures we support, so that we can7029/// 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) {...@@ -6987,24 +7055,14 @@ const EhdrType = enum(u16) {
6987 }7055 }
6988};7056};
6989fn ehdrMachine(elf: *const Elf) EhdrMachine {7057fn ehdrMachine(elf: *const Elf) EhdrMachine {
6990 const ehdr_slice = elf.ni.ehdr.sliceConst(&elf.mf);7058 // We used to get this value from `elf.ni.ehdr`, but it turns out this function is called often
6991 switch (elf.identClass()) {7059 // enough to see measurable performance gains from memoizing its result on `Elf`.
6992 .NONE, _ => unreachable,7060 return elf.ehdr_fields.machine;
6993 inline else => |class| {
6994 const ehdr: *const class.ElfN().Ehdr = @ptrCast(@alignCast(ehdr_slice));
6995 return @bitCast(elf.targetLoad(&ehdr.machine));
6996 },
6997 }
6998}7061}
6999fn ehdrType(elf: *const Elf) EhdrType {7062fn ehdrType(elf: *const Elf) EhdrType {
7000 const ehdr_slice = elf.ni.ehdr.sliceConst(&elf.mf);7063 // We used to get this value from `elf.ni.ehdr`, but it turns out this function is called often
7001 switch (elf.identClass()) {7064 // enough to see measurable performance gains from memoizing its result on `Elf`.
7002 .NONE, _ => unreachable,7065 return elf.ehdr_fields.type;
7003 inline else => |class| {
7004 const ehdr: *const class.ElfN().Ehdr = @ptrCast(@alignCast(ehdr_slice));
7005 return @bitCast(elf.targetLoad(&ehdr.type));
7006 },
7007 }
7008}7066}
70097067
7010fn baseLoadableSegmentVAddr(elf: *const Elf) u64 {7068fn baseLoadableSegmentVAddr(elf: *const Elf) u64 {
...@@ -7227,11 +7285,17 @@ const SymPtr = union(std.elf.CLASS) {...@@ -7227,11 +7285,17 @@ const SymPtr = union(std.elf.CLASS) {
7227 @"32": *std.elf.Elf32.Sym,7285 @"32": *std.elf.Elf32.Sym,
7228 @"64": *std.elf.Elf64.Sym,7286 @"64": *std.elf.Elf64.Sym,
7229};7287};
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 {
7231 const raw_slice = Section.Index.symtab.node(elf).slice(&elf.mf);7292 const raw_slice = Section.Index.symtab.node(elf).slice(&elf.mf);
7232 switch (elf.shdrPtr(.symtab)) {7293 // Avoid calling `shdrPtr` here: this function is extremely hot, so having to locate the shdr
7233 inline else => |shdr, class| {7294 // actually noticably affects performance!
7234 const size = elf.targetLoad(&shdr.size);7295 switch (elf.identClass()) {
7296 .NONE, _ => unreachable,
7297 inline else => |class| {
7298 const size = elf.symtab.items.len * @sizeOf(class.ElfN().Sym);
7235 const slice: []class.ElfN().Sym = @ptrCast(@alignCast(raw_slice[0..@intCast(size)]));7299 const slice: []class.ElfN().Sym = @ptrCast(@alignCast(raw_slice[0..@intCast(size)]));
7236 return @unionInit(SymPtr, @tagName(class), &slice[@backingInt(index)]);7300 return @unionInit(SymPtr, @tagName(class), &slice[@backingInt(index)]);
7237 },7301 },
...@@ -7496,7 +7560,6 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node...@@ -7496,7 +7560,6 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node
7496 .shndx = shndx,7560 .shndx = shndx,
7497 }),7561 }),
7498 .first_symbol_reloc = .none,7562 .first_symbol_reloc = .none,
7499 .first_got_reloc = .none,
7500 };7563 };
7501 }7564 }
7502 return nmi;7565 return nmi;
...@@ -7651,6 +7714,10 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void...@@ -7651,6 +7714,10 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void
7651 }7714 }
7652}7715}
7653fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void {7716fn 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
7654 const comp = elf.base.comp;7721 const comp = elf.base.comp;
7655 const gpa = comp.gpa;7722 const gpa = comp.gpa;
7656 const diags = &comp.link_diags;7723 const diags = &comp.link_diags;
...@@ -7730,6 +7797,10 @@ fn loadObject(...@@ -7730,6 +7797,10 @@ fn loadObject(
7730 fr: *Io.File.Reader,7797 fr: *Io.File.Reader,
7731 fl: MappedFile.Node.FileLocation,7798 fl: MappedFile.Node.FileLocation,
7732) LoadParseInputError!void {7799) LoadParseInputError!void {
7800 const trace = tracy.trace(@src());
7801 defer trace.end();
7802 trace.addTextFmt("{f}{f}", .{ path.fmtEscapeString(), fmtMemberString(member) });
7803
7733 const comp = elf.base.comp;7804 const comp = elf.base.comp;
7734 const gpa = comp.gpa;7805 const gpa = comp.gpa;
7735 const diags = &comp.link_diags;7806 const diags = &comp.link_diags;
...@@ -7881,440 +7952,492 @@ fn loadObject(...@@ -7881,440 +7952,492 @@ fn loadObject(
7881 defer gpa.free(shstrtab);7952 defer gpa.free(shstrtab);
7882 try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1);7953 try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
7883 try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1);7954 try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
7884 for (sections[1..]) |*section| {7955
7885 if (section.shdr.name >= shstrtab.len) continue;7956 {
7886 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);7957 const sections_trace = tracy.traceNamed(@src(), "scan input sections");
7887 if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue;7958 defer sections_trace.end();
7888 const sect_type = section.shdr.type;7959 for (sections[1..]) |*section| {
7889 const opts: struct {7960 const section_trace = tracy.traceNamed(@src(), "input section");
7890 shndx: Section.Index,7961 defer section_trace.end();
7891 node_fixed: bool,7962 if (section.shdr.name >= shstrtab.len) continue;
7892 } = switch (sect_type) {7963 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
7893 else => continue,7964 section_trace.addText(name);
7894 .PROGBITS, .NOBITS, .X86_64_UNWIND => opts: {7965 if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue;
7895 const shndx = elf.mapInputSection(.{7966 const sect_type = section.shdr.type;
7896 .name = name,7967 const opts: struct {
7897 .flags = section.shdr.flags.shf,7968 shndx: Section.Index,
7898 .entsize = section.shdr.entsize,7969 node_fixed: bool,
7899 .type = section.shdr.type,7970 } = switch (sect_type) {
7900 }) catch |err| switch (err) {7971 else => continue,
7901 else => |e| return e,7972 .PROGBITS, .NOBITS, .X86_64_UNWIND => opts: {
7902 error.StripSection => continue,7973 const shndx = elf.mapInputSection(.{
7903 error.TlsSectionUnavailable => {7974 .name = name,
7904 if (section.shdr.size == 0) continue;7975 .flags = section.shdr.flags.shf,
79057976 .entsize = section.shdr.entsize,
7906 return diags.failParse(7977 .type = section.shdr.type,
7907 path,7978 }) catch |err| switch (err) {
7908 "thread-local storage section '{s}' is incompatible with '-fsingle-threaded'",7979 else => |e| return e,
7909 .{name},7980 error.StripSection => continue,
7910 );7981 error.TlsSectionUnavailable => {
7911 },7982 if (section.shdr.size == 0) continue;
7912 error.UnsupportedSectionFlags => if (!section.shdr.flags.shf.ALLOC) {7983
7913 // It probably doesn't matter, just skip this section.7984 return diags.failParse(
7914 continue;7985 path,
7915 } else return diags.failParse(7986 "thread-local storage section '{s}' is incompatible with '-fsingle-threaded'",
7916 path,7987 .{name},
7917 "unsupported flags for section '{s}'",7988 );
7918 .{name},7989 },
7919 ),7990 error.UnsupportedSectionFlags => if (!section.shdr.flags.shf.ALLOC) {
7920 error.SectionTypeConflict => if (!section.shdr.flags.shf.ALLOC) {7991 // It probably doesn't matter, just skip this section.
7921 // It probably doesn't matter, just skip this section.7992 continue;
7922 continue;7993 } else return diags.failParse(
7923 } else return diags.failParse(7994 path,
7924 path,7995 "unsupported flags for section '{s}'",
7925 "type of section '{s}' conflicts with other inputs",7996 .{name},
7926 .{name},7997 ),
7927 ),7998 error.SectionTypeConflict => if (!section.shdr.flags.shf.ALLOC) {
7928 error.SectionFlagsConflict => if (!section.shdr.flags.shf.ALLOC) {7999 // It probably doesn't matter, just skip this section.
7929 // 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.
7930 continue;8031 continue;
7931 } else return diags.failParse(8032 }
7932 path,8033 break :opts .{
7933 "flags of section '{s}' conflict with other inputs",8034 .shndx = shndx,
7934 .{name},8035 // For well-known sections, we know that it's fine to have e.g. random
7935 ),8036 // padding, so there's no need to make the sections fixed. For custom
7936 error.UnsupportedNobitsSection => return diags.failParse(8037 // sections, however, we do want fixed nodes to avoid padding.
7937 path,8038 .node_fixed = shndx != .text and
7938 "unsupported section type SHT_NOBITS for non-bss section '{s}'",8039 shndx != .rodata and
7939 .{name},8040 shndx != .data and
7940 ),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 },
7941 };8097 };
7942 if (section.shdr.flags.shf.COMPRESSED) {8098
7943 // SHF_COMPRESSED is only allowed on non-alloc sections.8099 section.placement = .{ .nobits = .{
7944 if (section.shdr.flags.shf.ALLOC) return diags.failParse(8100 .shndx = opts.shndx,
7945 path,8101 .offset = offset,
7946 "section '{s}' has conflicting flags SHF_ALLOC and SHF_COMPRESSED",8102 } };
7947 .{name},8103 if (opts.shndx == elf.shndx.bss) {
7948 );8104 input_has_bss = true;
7949 // TODO: handle compressed input sections. We'll need to set a flag to8105 } else if (opts.shndx == elf.shndx.tbss) {
7950 // indicate that `flushInputSection` needs to decompress the section.8106 input_has_tbss = true;
7951 // But because this section isn't SHF_ALLOC, it's probably okay to just8107 } else {
7952 // skip it for now.8108 assert(elf.ehdrType() == .REL);
7953 continue;
7954 }8109 }
7955 break :opts .{8110
7956 .shndx = shndx,8111 continue;
7957 // For well-known sections, we know that it's fine to have e.g. random8112 }
7958 // padding, so there's no need to make the sections fixed. For custom8113
7959 // sections, however, we do want fixed nodes to avoid padding.8114 const add_node_opts: MappedFile.Node.AddOptions = .{
7960 .node_fixed = shndx != .text and8115 .size = need_align.forward(section.shdr.size),
7961 shndx != .rodata and8116 .alignment = need_align,
7962 shndx != .data and8117 .moved = true, // see assert at end of `flushInputSection`
7963 shndx != .data_rel_ro and8118 };
7964 shndx != elf.shndx.tdata,8119 const ni = elf.addNodeAssumeCapacity(
7965 };8120 if (opts.node_fixed) ni: {
7966 },8121 const shndx_ni = opts.shndx.node(elf);
7967 inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{8122 const after_oni: MappedFile.Node.Index.Optional = after: {
7968 .shndx = shndx: {8123 var node_it = shndx_ni.last(&elf.mf);
7969 // TODO: the input section name may include a "priority" value between 18124 while (node_it.unwrap()) |cur_ni| {
7970 // and 65535 which should affect the order we assemble input sections in8125 switch (cur_ni.position(&elf.mf)) {
7971 const init_fini_section_name: []const u8 = switch (@"type") {8126 .header => break :after .wrap(cur_ni), // last header
7972 .INIT_ARRAY => "init_array",8127 .footer, .floating => node_it = cur_ni.prev(&elf.mf),
7973 .FINI_ARRAY => "fini_array",8128 }
7974 .PREINIT_ARRAY => "preinit_array",8129 } else {
7975 else => comptime unreachable,8130 break :after .none;
7976 };8131 }
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,
7982 };8132 };
7983 if (section.shdr.addralign != need_addralign) {8133 break :ni try shndx_ni.addHeaderChildAfter(gpa, &elf.mf, after_oni, add_node_opts);
7984 return diags.failParse(path, "bad addralign on {t} shdr", .{@"type"});8134 } else try opts.shndx.node(elf).addFloatingChild(gpa, &elf.mf, add_node_opts),
7985 }8135 .{ .input_section = @fromBackingInt(@intCast(elf.input_sections.items.len)) },
7986 if (shndx.* == .UNDEF) {8136 );
7987 try elf.createInitFiniArraySection(shndx, init_fini_section_name, @"type");8137 section.placement = .{ .progbits = @fromBackingInt(@intCast(elf.input_sections.items.len)) };
7988 }8138 elf.input_sections.addOneAssumeCapacity().* = .{
7989 switch (elf.shdrPtr(shndx.*)) {8139 .input = input_index,
7990 inline else => |shdr| {8140 .file_location = .{
7991 const old_size = elf.targetLoad(&shdr.size);8141 .offset = fl.offset + section.shdr.offset,
7992 const new_size = old_size + section.shdr.size;8142 .size = if (section.shdr.type == .NOBITS) 0 else 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.*;
7998 },8143 },
7999 // This node must be fixed to prevent padding from being added between different8144 // The section vaddr is initially 0, because the symbol addresses are
8000 // INIT_ARRAY/FINI_ARRAY/PREINIT_ARRAY input sections.8145 // zero-based. This will eventually be updated by `flushMoved`.
8001 .node_fixed = true,8146 .vaddr = 0,
8002 },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;
8003 };8188 };
8004 const need_align: Alignment = .fromByteUnits(8189 defer gpa.free(strtab);
8005 std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))),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 },
8006 );8199 );
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) {8221 if (input_sym.shndx == std.elf.SHN_UNDEF) switch (input_sym.info.bind) {
8011 const offset: u64 = switch (elf.shdrPtr(opts.shndx)) {8222 else => |bind| return diags.failParse(
8012 inline else => |shdr| offset: {8223 path,
8013 // Bump our BSS output section size8224 "symbol '{s}' has unsupported binding (0x{x})",
8014 const old_size: u64 = elf.targetLoad(&shdr.size);8225 .{ name, bind },
8015 const offset = need_align.forward(old_size);8226 ),
8016 elf.targetStore(&shdr.size, @intCast(offset + section.shdr.size));8227 .LOCAL => continue,
8017 break :offset offset;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;
8018 },8253 },
8019 };8254 };
80208255
8021 section.placement = .{ .nobits = .{8256 const symbol_oni: MappedFile.Node.Index.Optional, const output_shndx: Section.Index, const symbol_value: u64 = switch (sections[input_sym.shndx].placement) {
8022 .shndx = opts.shndx,8257 .none => continue,
8023 .offset = offset,8258 .progbits => |isi| .{
8024 } };8259 .wrap(isi.node(elf)),
8025 if (opts.shndx == elf.shndx.bss) {8260 elf.getNodeShndx(isi.node(elf)),
8026 input_has_bss = true;8261 input_sym.value,
8027 } else if (opts.shndx == elf.shndx.tbss) {8262 },
8028 input_has_tbss = true;8263 .nobits => |nobits| sym: {
8029 } else {8264 const section_base: u64 = switch (elf.ehdrType()) {
8030 assert(elf.ehdrType() == .REL);8265 .REL => 0,
8031 }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 }
8034 }8331 }
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);
8070 }8332 }
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) {8334 {
8118 else => |bind| return diags.failParse(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(
8119 path,8351 path,
8120 "symbol '{s}' has unsupported binding (0x{x})",8352 "relocation section 'sh_info' refers to invalid section index '{d}'",
8121 .{ name, bind },8353 .{rel_sec.shdr.info},
8122 ),8354 );
8123 .LOCAL => continue,8355 }
8124 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {8356 const Rel = switch (sht) {
8125 si.* = .global(elf.addGlobalSymbol(.{8357 else => comptime unreachable,
8126 .node = .none,8358 .REL => ElfN.Rel,
8127 .name = name,8359 .RELA => ElfN.Rela,
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 },
8150 };8360 };
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) {
8153 .none => continue,8367 .none => continue,
8154 .progbits => |isi| .{8368 .progbits => |isi| isi.node(elf),
8155 .wrap(isi.node(elf)),8369 .nobits => return diags.failParse(
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(
8182 path,8370 path,
8183 "symbol '{s}' has unsupported binding (0x{x})",8371 "cannot apply relocation to SHT_NOBITS section '{s}'",
8184 .{ name, bind },8372 .{std.mem.sliceTo(shstrtab[loc_sec.shdr.name..], 0)},
8185 ),8373 ),
8186 .LOCAL => {8374 };
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 };
82518375
8252 elf.resetNodeRelocs(loc_node);8376 elf.resetNodeRelocs(loc_node);
82538377
8254 const relnum = std.math.divExact(8378 const relnum = std.math.divExact(
8255 u32,8379 u32,
8256 @intCast(rel_sec.shdr.size),8380 @intCast(rel_sec.shdr.size),
8257 @intCast(rel_sec.shdr.entsize),8381 @intCast(rel_sec.shdr.entsize),
8258 ) catch return diags.failParse(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(
8259 path,8394 path,
8260 "relocation section size (0x{x}) is not a multiple of entsize (0x{x})",8395 "relocation target symbol index {d} exceeds symtab size",
8261 .{ rel_sec.shdr.size, rel_sec.shdr.entsize },8396 .{rel.info.sym},
8262 );8397 );
8263 try elf.ensureUnusedRelocCapacity(loc_node, relnum);8398 const target = symmap.items[rel.info.sym - 1];
8264 try fr.seekTo(fl.offset + rel_sec.shdr.offset);8399 if (target == Symbol.Id.null) {
8265 for (0..relnum) |_| {8400 // If this is not an SHF_ALLOC section, then let's not report
8266 const rel = try r.peekStruct(Rel, target_endian);8401 // this for now, because it probably doesn't affect the final
8267 try r.discardAll64(rel_sec.shdr.entsize);8402 // binary's functionality for this section to be a bit broken.
8268 if (rel.info.sym == 0) continue;8403 if (loc_sec.shdr.flags.shf.ALLOC) {
8269 if (rel.info.sym > symnum) return diags.failParse(8404 diags.addParseError(
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(
8303 path,8405 path,
8304 "non-static relocation type '{f}'",8406 "unsupported symbol at index {d} required for relocation",
8305 .{rt.fmt(elf)},8407 .{rel.info.sym},
8306 ),8408 );
8307 error.UnimplementedRelocation => diags.addParseError(8409 }
8308 path,8410 continue;
8309 "TODO(Elf2): unimplemented relocation type '{f}'",
8310 .{rt.fmt(elf)},
8311 ),
8312 };
8313 }8411 }
8314 },8412 const rt: MachineRelocType = .wrap(rel.info.type, elf);
8315 };8413 elf.addRelocAssumeCapacity(
8316 },8414 loc_node,
8317 };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
8319 if (input_has_bss) {8442 if (input_has_bss) {
8320 try elf.bss_inputs.append(gpa, input_index);8443 try elf.bss_inputs.append(gpa, input_index);
...@@ -8374,6 +8497,10 @@ fn loadDso(...@@ -8374,6 +8497,10 @@ fn loadDso(
8374 fallback_soname: link.Input.Dso.FallbackSoname,8497 fallback_soname: link.Input.Dso.FallbackSoname,
8375 fr: *Io.File.Reader,8498 fr: *Io.File.Reader,
8376) (LoadParseInputError || error{BadMagic})!void {8499) (LoadParseInputError || error{BadMagic})!void {
8500 const trace = tracy.trace(@src());
8501 defer trace.end();
8502 trace.addTextFmt("{f}", .{path.fmtEscapeString()});
8503
8377 const comp = elf.base.comp;8504 const comp = elf.base.comp;
8378 const gpa = comp.gpa;8505 const gpa = comp.gpa;
8379 const diags = &comp.link_diags;8506 const diags = &comp.link_diags;
...@@ -8519,6 +8646,9 @@ fn loadDso(...@@ -8519,6 +8646,9 @@ fn loadDso(
8519 const verdef: []DsoGlobals.String.Optional = verdef: {8646 const verdef: []DsoGlobals.String.Optional = verdef: {
8520 const verdef_sh = shdr.verdef orelse break :verdef &.{};8647 const verdef_sh = shdr.verdef orelse break :verdef &.{};
85218648
8649 const versions_trace = tracy.traceNamed(@src(), "dso versions");
8650 defer versions_trace.end();
8651
8522 // This parsing is a bit of a mess because it appears that at this moment in history8652 // This parsing is a bit of a mess because it appears that at this moment in history
8523 // nobody at GNU was yet aware of the concept of an "array".8653 // nobody at GNU was yet aware of the concept of an "array".
85248654
...@@ -8602,6 +8732,9 @@ fn loadDso(...@@ -8602,6 +8732,9 @@ fn loadDso(
8602 const soname = try elf.string(.dynstr, soname_slice);8732 const soname = try elf.string(.dynstr, soname_slice);
8603 try elf.needed.put(gpa, soname, {});8733 try elf.needed.put(gpa, soname, {});
86048734
8735 const symbols_trace = tracy.traceNamed(@src(), "dso symbols");
8736 defer symbols_trace.end();
8737
8605 // Scan the symbol table and populate `elf.dso_globals`.8738 // Scan the symbol table and populate `elf.dso_globals`.
8606 const first_global = @min(shdr.dynsym.info, symnum);8739 const first_global = @min(shdr.dynsym.info, symnum);
8607 try elf.dso_globals.symbols.ensureUnusedCapacity(gpa, symnum - first_global);8740 try elf.dso_globals.symbols.ensureUnusedCapacity(gpa, symnum - first_global);
...@@ -9422,7 +9555,6 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)...@@ -9422,7 +9555,6 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)
9422 const gpa = elf.base.comp.gpa;9555 const gpa = elf.base.comp.gpa;
9423 try elf.symbol_relocs.ensureUnusedCapacity(gpa, len);9556 try elf.symbol_relocs.ensureUnusedCapacity(gpa, len);
9424 try elf.node_relocs.ensureUnusedCapacity(gpa, len);9557 try elf.node_relocs.ensureUnusedCapacity(gpa, len);
9425 try elf.got_relocs.ensureUnusedCapacity(gpa, len);
9426 const class = elf.identClass();9558 const class = elf.identClass();
9427 switch (elf.ehdrType()) {9559 switch (elf.ehdrType()) {
9428 .REL => {9560 .REL => {
...@@ -9460,7 +9592,6 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)...@@ -9460,7 +9592,6 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)
9460 try shndx.get(elf).rela.shndx.relaEnsureAdditionalCapacity(elf, len);9592 try shndx.get(elf).rela.shndx.relaEnsureAdditionalCapacity(elf, len);
9461 },9593 },
9462 .EXEC, .DYN => {9594 .EXEC, .DYN => {
9463 try elf.tls_size_symbol_relocs.ensureUnusedCapacity(gpa, len);
9464 const new_got_entries = len * 2; // at worst, every reloc is a new TLSGD9595 const new_got_entries = len * 2; // at worst, every reloc is a new TLSGD
9465 try elf.got.ensureUnusedCapacity(gpa, new_got_entries);9596 try elf.got.ensureUnusedCapacity(gpa, new_got_entries);
9466 const need_got_size = switch (class) {9597 const need_got_size = switch (class) {
...@@ -9485,6 +9616,9 @@ fn addRelocAssumeCapacity(...@@ -9485,6 +9616,9 @@ fn addRelocAssumeCapacity(
9485 addend: i64,9616 addend: i64,
9486 @"type": MachineRelocType,9617 @"type": MachineRelocType,
9487) (Error || error{ UnknownRelocation, NonStaticRelocation, UnimplementedRelocation })!void {9618) (Error || error{ UnknownRelocation, NonStaticRelocation, UnimplementedRelocation })!void {
9619 const trace = tracy.trace(@src());
9620 defer trace.end();
9621
9488 switch (elf.ehdrType()) {9622 switch (elf.ehdrType()) {
9489 .REL => {9623 .REL => {
9490 const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx;9624 const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx;
...@@ -9506,13 +9640,12 @@ fn addRelocAssumeCapacity(...@@ -9506,13 +9640,12 @@ fn addRelocAssumeCapacity(
9506 elf.symbol_relocs.appendAssumeCapacity(.{9640 elf.symbol_relocs.appendAssumeCapacity(.{
9507 .node = node.toOptional(),9641 .node = node.toOptional(),
9508 .offset = offset,9642 .offset = offset,
9509 .type = undefined,9643 .bits = undefined,
9510 .target = target,9644 .target_symbol = target,
9511 .addend = .{ .normal = addend },9645 .addend = .{ .normal = addend },
9512 .next = next,9646 .next = next,
9513 .prev = .none,9647 .prev = .none,
9514 .rela_index = rela_index.toOptional(),9648 .rela_index = rela_index.toOptional(),
9515 .result = .ok,
9516 });9649 });
9517 ri.get(elf).apply(elf);9650 ri.get(elf).apply(elf);
9518 },9651 },
...@@ -9567,14 +9700,14 @@ fn addRelocAssumeCapacity(...@@ -9567,14 +9700,14 @@ fn addRelocAssumeCapacity(
9567 .TLS_LE64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })),9700 .TLS_LE64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })),
9568 .TLS_LE64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })),9701 .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" })),9703 .GOT_PC_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotabs_symbol, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
9571 .GOT_PC_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala_hi20)),9704 .GOT_PC_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_got_pcala_hi20)),
9572 .GOT64_PC_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala64_lo20)),9705 .GOT64_PC_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_got_pcala64_lo20)),
9573 .GOT64_PC_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala64_hi12)),9706 .GOT64_PC_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_got_pcala64_hi12)),
9574 .GOT_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),9707 .GOT_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotabs_symbol, .{ .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" })),9708 .GOT_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotabs_symbol, .{ .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" })),9709 .GOT64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotabs_symbol, .{ .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" })),9710 .GOT64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotabs_symbol, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })),
9578 // zig fmt: on9711 // zig fmt: on
9579 },9712 },
9580 .PPC64 => switch (@"type".PPC64) {9713 .PPC64 => switch (@"type".PPC64) {
...@@ -9678,13 +9811,13 @@ fn addRelocAssumeCapacity(...@@ -9678,13 +9811,13 @@ fn addRelocAssumeCapacity(
9678 .TLS_DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),9811 .TLS_DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
9679 .TLS_DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),9812 .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" })),9814 .GOT13 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_symbol, .{ .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" })),9815 .GOT22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_symbol, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
9683 .GOTDATA_OP_LOX10 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.sparc_op_lox10)),9816 .GOTDATA_OP_LOX10 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.sparc_got_op_lox10)),
9684 .GOTDATA_OP_HIX22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.sparc_op_hix22)),9817 .GOTDATA_OP_HIX22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.sparc_got_op_hix22)),
9685 .TLS_GD_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),9818 .TLS_GD_HI22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_tlsgd, .{ .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" })),9819 .TLS_LDM_HI22 => try elf.addSymbolRelocAssumeCapacity(node, offset, .null, addend, .simple(.gotoff_tlsgd, .{ .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" })),9820 .TLS_IE_HI22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_tpoff, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
9688 // zig fmt: on9821 // zig fmt: on
96899822
9690 .TLS_GD_CALL, .TLS_LDM_CALL => {9823 .TLS_GD_CALL, .TLS_LDM_CALL => {
...@@ -9750,19 +9883,19 @@ fn addRelocAssumeCapacity(...@@ -9750,19 +9883,19 @@ fn addRelocAssumeCapacity(
9750 },9883 },
9751 .GOT10 => {9884 .GOT10 => {
9752 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });9885 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" }));
9754 },9887 },
9755 .TLS_GD_LO10 => {9888 .TLS_GD_LO10 => {
9756 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });9889 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" }));
9758 },9891 },
9759 .TLS_LDM_LO10 => {9892 .TLS_LDM_LO10 => {
9760 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });9893 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" }));
9762 },9895 },
9763 .TLS_IE_LO10 => {9896 .TLS_IE_LO10 => {
9764 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });9897 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" }));
9766 },9899 },
9767 },9900 },
9768 .X86_64 => rel_type: switch (@"type".X86_64) {9901 .X86_64 => rel_type: switch (@"type".X86_64) {
...@@ -9790,12 +9923,7 @@ fn addRelocAssumeCapacity(...@@ -9790,12 +9923,7 @@ fn addRelocAssumeCapacity(
9790 .GOTOFF64 => return error.UnimplementedRelocation, // offset of symbol from GOT base9923 .GOTOFF64 => return error.UnimplementedRelocation, // offset of symbol from GOT base
9791 .PLTOFF64 => return error.UnimplementedRelocation, // offset of PLT entry from GOT base (yes, I know, the name is stupid)9924 .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`9926 // TODO: figure out how to do relaxations
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.
97999927
9800 // Relaxable versions of other relocations. Since we don't yet implement relaxation,9928 // Relaxable versions of other relocations. Since we don't yet implement relaxation,
9801 // just use the handling for the non-relaxable versions.9929 // just use the handling for the non-relaxable versions.
...@@ -9831,13 +9959,13 @@ fn addRelocAssumeCapacity(...@@ -9831,13 +9959,13 @@ fn addRelocAssumeCapacity(
9831 .TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),9959 .TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
9832 .TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),9960 .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" })),9962 .GOT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_symbol, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
9835 .GOT64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),9963 .GOT64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_symbol, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
9836 .GOTPCREL => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),9964 .GOTPCREL => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotrel_symbol, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
9837 .GOTPCREL64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),9965 .GOTPCREL64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotrel_symbol, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
9838 .TLSGD => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),9966 .TLSGD => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotrel_tlsgd, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
9839 .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),9967 .TLSLD => try elf.addSymbolRelocAssumeCapacity(node, offset, .null, addend, .simple(.gotrel_tlsgd, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
9840 .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),9968 .GOTTPOFF => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotrel_tpoff, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
9841 // zig fmt: on9969 // zig fmt: on
98429970
9843 .GOTPC64 => {9971 .GOTPC64 => {
...@@ -9872,6 +10000,11 @@ fn addSymbolRelocAssumeCapacityInner(...@@ -9872,6 +10000,11 @@ fn addSymbolRelocAssumeCapacityInner(
9872) Error!void {10000) Error!void {
9873 assert(elf.ehdrType() != .REL);10001 assert(elf.ehdrType() != .REL);
987410002
10003 const trace = tracy.trace(@src());
10004 defer trace.end();
10005
10006 const gpa = elf.base.comp.gpa;
10007
9875 request_static_target: {10008 request_static_target: {
9876 if (elf.shndx.dynamic == .UNDEF) break :request_static_target;10009 if (elf.shndx.dynamic == .UNDEF) break :request_static_target;
9877 const gsi: Symbol.Global.Index = switch (target.unwrap()) {10010 const gsi: Symbol.Global.Index = switch (target.unwrap()) {
...@@ -9894,6 +10027,15 @@ fn addSymbolRelocAssumeCapacityInner(...@@ -9894,6 +10027,15 @@ fn addSymbolRelocAssumeCapacityInner(
9894 .dtpoff,10027 .dtpoff,
9895 .tpoff,10028 .tpoff,
9896 .size,10029 .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,
9897 => false,10039 => false,
989810040
9899 .special => switch (@"type".action.special) {10041 .special => switch (@"type".action.special) {
...@@ -9908,10 +10050,15 @@ fn addSymbolRelocAssumeCapacityInner(...@@ -9908,10 +10050,15 @@ fn addSymbolRelocAssumeCapacityInner(
9908 .larch_b21,10050 .larch_b21,
9909 .larch_b26,10051 .larch_b26,
9910 .larch_call36,10052 .larch_call36,
10053 .larch_got_pcala_hi20,
10054 .larch_got_pcala64_lo20,
10055 .larch_got_pcala64_hi12,
991110056
9912 .sparc_le_hix22,10057 .sparc_le_hix22,
9913 .sparc_wdisp10,10058 .sparc_wdisp10,
9914 .sparc_wdisp16,10059 .sparc_wdisp16,
10060 .sparc_got_op_lox10,
10061 .sparc_got_op_hix22,
9915 => false,10062 => false,
9916 },10063 },
9917 };10064 };
...@@ -9923,6 +10070,42 @@ fn addSymbolRelocAssumeCapacityInner(...@@ -9923,6 +10070,42 @@ fn addSymbolRelocAssumeCapacityInner(
9923 }10070 }
9924 }10071 }
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
9926 const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));10109 const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));
9927 const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc;10110 const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc;
9928 const next = first_target_reloc.*;10111 const next = first_target_reloc.*;
...@@ -9931,18 +10114,24 @@ fn addSymbolRelocAssumeCapacityInner(...@@ -9931,18 +10114,24 @@ fn addSymbolRelocAssumeCapacityInner(
9931 elf.symbol_relocs.appendAssumeCapacity(.{10114 elf.symbol_relocs.appendAssumeCapacity(.{
9932 .node = node.toOptional(),10115 .node = node.toOptional(),
9933 .offset = offset,10116 .offset = offset,
9934 .target = target,10117 .target_symbol = target,
9935 .addend = addend,10118 .addend = addend,
9936 .type = @"type",10119 .bits = .{
10120 .type = @"type",
10121 .result = .ok,
10122 .relaxation = .none,
10123 },
9937 .next = next,10124 .next = next,
9938 .prev = .none,10125 .prev = .none,
9939 .rela_index = .none,10126 .rela_index = .none,
9940 .result = .ok,
9941 });10127 });
9942 if (@"type".dependsOnTlsSize(elf)) {10128 if (@"type".dependsOnTlsSize(elf)) {
9943 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});10129 try elf.tls_size_symbol_relocs.putNoClobber(gpa, ri, {});
9944 }10130 }
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
9947 // Actually apply the new relocation!10136 // Actually apply the new relocation!
9948 ri.get(elf).apply(elf);10137 ri.get(elf).apply(elf);
...@@ -10046,95 +10235,6 @@ fn addNodeRelocAssumeCapacity(...@@ -10046,95 +10235,6 @@ fn addNodeRelocAssumeCapacity(
10046 },10235 },
10047 }10236 }
10048}10237}
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}
10138fn updateGotEntry(elf: *Elf, got_index: usize) void {10238fn updateGotEntry(elf: *Elf, got_index: usize) void {
10139 assert(elf.ehdrType() != .REL);10239 assert(elf.ehdrType() != .REL);
10140 const entry_value: union(enum) {10240 const entry_value: union(enum) {
...@@ -10193,15 +10293,6 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -10193,15 +10293,6 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
10193 .addend = 0,10293 .addend = 0,
10194 } },10294 } },
10195 },10295 },
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 },
10205 .tlsgd0 => |sym| switch (elf.base.comp.config.link_mode) {10296 .tlsgd0 => |sym| switch (elf.base.comp.config.link_mode) {
10206 .static => val: {10297 .static => val: {
10207 assert(elf.base.comp.config.output_mode == .Exe); // static libraries don't have GOTs10298 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 {...@@ -10209,25 +10300,28 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
10209 },10300 },
10210 .dynamic => .{ .reloc = .{10301 .dynamic => .{ .reloc = .{
10211 .type = .dtpMod(elf),10302 .type = .dtpMod(elf),
10212 .dynsym_index = switch (elf.classifySymbolValue(sym)) {10303 .dynsym_index = switch (sym) {
10213 .static, .static_relative => 0,10304 .null => 0,
10214 .dynamic => sym.unwrap().global.dynsymIndex(elf).?,10305 else => switch (elf.classifySymbolValue(sym)) {
10306 .static, .static_relative => 0,
10307 .dynamic => sym.unwrap().global.dynsymIndex(elf).?,
10308 },
10215 },10309 },
10216 .addend = 0,10310 .addend = 0,
10217 } },10311 } },
10218 },10312 },
10219 .tlsld0 => switch (elf.base.comp.config.link_mode) {10313 .tlsgd1 => |sym| switch (sym) {
10220 .static => val: {10314 .null => .{ .unsigned = 0 },
10221 assert(elf.base.comp.config.output_mode == .Exe); // static libraries don't have GOTs10315 else => switch (elf.classifySymbolValue(sym)) {
10222 break :val .{ .unsigned = 1 }; // TLS module ID for executable10316 .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 } },
10223 },10323 },
10224 .dynamic => .{ .reloc = .{
10225 .type = .dtpMod(elf),
10226 .dynsym_index = 0,
10227 .addend = 0,
10228 } },
10229 },10324 },
10230 .tlsld1 => .{ .unsigned = 0 },
10231 };10325 };
1023210326
10233 // First, write to the GOT itself. If we're planning to use a relocation, we'll just write zeroes.10327 // 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 {...@@ -10994,7 +11088,7 @@ pub fn idle(elf: *Elf) link.Error!bool {
10994 var ri = symtab_index.ptr(elf).first_target_reloc;11088 var ri = symtab_index.ptr(elf).first_target_reloc;
10995 while (ri != .none) {11089 while (ri != .none) {
10996 const reloc = ri.get(elf);11090 const reloc = ri.get(elf);
10997 assert(reloc.target == sym_id);11091 assert(reloc.target_symbol == sym_id);
10998 // In relocatables, every symbol relocation has an output relocation.11092 // In relocatables, every symbol relocation has an output relocation.
10999 const rela_index = reloc.rela_index.unwrap().?;11093 const rela_index = reloc.rela_index.unwrap().?;
11000 reloc.relaSection(elf).relaUpdateSym(elf, rela_index, @backingInt(symtab_index));11094 reloc.relaSection(elf).relaUpdateSym(elf, rela_index, @backingInt(symtab_index));
...@@ -11024,7 +11118,7 @@ pub fn idle(elf: *Elf) link.Error!bool {...@@ -11024,7 +11118,7 @@ pub fn idle(elf: *Elf) link.Error!bool {
11024 var ri = symtab_index.ptr(elf).first_target_reloc;11118 var ri = symtab_index.ptr(elf).first_target_reloc;
11025 while (ri != .none) {11119 while (ri != .none) {
11026 const reloc = ri.get(elf);11120 const reloc = ri.get(elf);
11027 assert(reloc.target == sym_id);11121 assert(reloc.target_symbol == sym_id);
11028 // There may or may not be a runtime relocation for this symbol reloc.11122 // There may or may not be a runtime relocation for this symbol reloc.
11029 if (reloc.rela_index.unwrap()) |rela_index| {11123 if (reloc.rela_index.unwrap()) |rela_index| {
11030 elf.shndx.rela_dyn.relaUpdateSym(elf, rela_index, dynsym_index);11124 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...@@ -11615,8 +11709,8 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
11615 const rela_index = opt_rela_index.unwrap() orelse continue;11709 const rela_index = opt_rela_index.unwrap() orelse continue;
11616 rela_dyn_shndx.relaAdjustOffset(elf, rela_index, old_addr, addr);11710 rela_dyn_shndx.relaAdjustOffset(elf, rela_index, old_addr, addr);
11617 }11711 }
11618 for (elf.got_relocs.items) |*reloc| {11712 for (elf.got_relocs.keys()) |ri| {
11619 reloc.apply(elf);11713 ri.get(elf).apply(elf);
11620 }11714 }
11621 } else if (shndx == elf.shndx.plt) {11715 } else if (shndx == elf.shndx.plt) {
11622 elf.flushMovedNodeRelocs(ni, .{11716 elf.flushMovedNodeRelocs(ni, .{
...@@ -11683,7 +11777,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -11683,7 +11777,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1168311777
11684 elf.flushMovedNodeRelocs(ni, .{11778 elf.flushMovedNodeRelocs(ni, .{
11685 .first_symbol_reloc = isi.ptrConst(elf).first_symbol_reloc,11779 .first_symbol_reloc = isi.ptrConst(elf).first_symbol_reloc,
11686 .first_got_reloc = isi.ptrConst(elf).first_got_reloc,
11687 });11780 });
11688 },11781 },
11689 .copied_global => |global_name| {11782 .copied_global => |global_name| {
...@@ -11714,7 +11807,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -11714,7 +11807,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
11714 .none,11807 .none,
11715 .uav, .lazy_code, .lazy_const_data => .none,11808 .uav, .lazy_code, .lazy_const_data => .none,
11716 },11809 },
11717 .first_got_reloc = mi.firstGotReloc(elf),
11718 });11810 });
11719 },11811 },
11720 .debug_shared => |ss| {11812 .debug_shared => |ss| {
...@@ -12200,8 +12292,6 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo...@@ -12200,8 +12292,6 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
12200 switch (got_key) {12292 switch (got_key) {
12201 .reserved,12293 .reserved,
12202 .symbol,12294 .symbol,
12203 .tlsld0,
12204 .tlsld1,
12205 .tlsgd0,12295 .tlsgd0,
12206 .tlsgd1,12296 .tlsgd1,
12207 => {12297 => {
src/link/MappedFile.zig+34
...@@ -11,6 +11,8 @@ const assert = std.debug.assert;...@@ -11,6 +11,8 @@ const assert = std.debug.assert;
11const linux = std.os.linux;11const linux = std.os.linux;
12const windows = std.os.windows;12const windows = std.os.windows;
1313
14const tracy = @import("../tracy.zig");
15
14io: Io,16io: Io,
15flags: packed struct {17flags: packed struct {
16 block_size: Alignment,18 block_size: Alignment,
...@@ -892,6 +894,9 @@ fn addNode(mf: *MappedFile, gpa: Allocator, opts: struct {...@@ -892,6 +894,9 @@ fn addNode(mf: *MappedFile, gpa: Allocator, opts: struct {
892}) Error!Node.Index {894}) Error!Node.Index {
893 mf.nodes_lock.assertUnlocked();895 mf.nodes_lock.assertUnlocked();
894896
897 const trace = tracy.trace(@src());
898 defer trace.end();
899
895 try mf.nodes.ensureUnusedCapacity(gpa, 1);900 try mf.nodes.ensureUnusedCapacity(gpa, 1);
896 try mf.large.ensureUnusedCapacity(gpa, 2);901 try mf.large.ensureUnusedCapacity(gpa, 2);
897902
...@@ -1014,6 +1019,9 @@ fn shrinkLeafNode(...@@ -1014,6 +1019,9 @@ fn shrinkLeafNode(
1014) Error!void {1019) Error!void {
1015 mf.nodes_lock.assertUnlocked();1020 mf.nodes_lock.assertUnlocked();
10161021
1022 const trace = tracy.trace(@src());
1023 defer trace.end();
1024
1017 const old_offset, const old_size = ni.location(mf).resolve(mf);1025 const old_offset, const old_size = ni.location(mf).resolve(mf);
10181026
1019 assert(new_size < old_size);1027 assert(new_size < old_size);
...@@ -1152,6 +1160,9 @@ fn growNode(...@@ -1152,6 +1160,9 @@ fn growNode(
1152) Error!void {1160) Error!void {
1153 mf.nodes_lock.assertUnlocked();1161 mf.nodes_lock.assertUnlocked();
11541162
1163 const trace = tracy.trace(@src());
1164 defer trace.end();
1165
1155 const node = ni.get(mf);1166 const node = ni.get(mf);
11561167
1157 const old_offset, const old_size = node.location().resolve(mf);1168 const old_offset, const old_size = node.location().resolve(mf);
...@@ -1569,6 +1580,9 @@ fn growFloatingNodeWithAlignment(...@@ -1569,6 +1580,9 @@ fn growFloatingNodeWithAlignment(
1569) Error!void {1580) Error!void {
1570 mf.nodes_lock.assertUnlocked();1581 mf.nodes_lock.assertUnlocked();
15711582
1583 const trace = tracy.trace(@src());
1584 defer trace.end();
1585
1572 const parent_ni = ni.parent(mf).unwrap().?; // `ni` cannot be `.root`1586 const parent_ni = ni.parent(mf).unwrap().?; // `ni` cannot be `.root`
1573 const old_offset, const old_size = ni.location(mf).resolve(mf);1587 const old_offset, const old_size = ni.location(mf).resolve(mf);
15741588
...@@ -1791,6 +1805,9 @@ fn growNodeViaInsertRange(...@@ -1791,6 +1805,9 @@ fn growNodeViaInsertRange(
1791 new_size: u64,1805 new_size: u64,
1792 grow_options: GrowOptions,1806 grow_options: GrowOptions,
1793) Error!bool {1807) Error!bool {
1808 const trace = tracy.trace(@src());
1809 defer trace.end();
1810
1794 if (!is_linux or mf.flags.fallocate_insert_range_unsupported) {1811 if (!is_linux or mf.flags.fallocate_insert_range_unsupported) {
1795 return false;1812 return false;
1796 }1813 }
...@@ -1999,6 +2016,9 @@ fn ensureAdditionalHeaderCapacity(...@@ -1999,6 +2016,9 @@ fn ensureAdditionalHeaderCapacity(
1999 parent_ni: Node.Index,2016 parent_ni: Node.Index,
2000 extra_capacity: u64,2017 extra_capacity: u64,
2001) Error!void {2018) Error!void {
2019 const trace = tracy.trace(@src());
2020 defer trace.end();
2021
2002 _, const parent_size = parent_ni.location(mf).resolve(mf);2022 _, const parent_size = parent_ni.location(mf).resolve(mf);
20032023
2004 const last_header_oni = parent_ni.lastHeader(mf);2024 const last_header_oni = parent_ni.lastHeader(mf);
...@@ -2274,6 +2294,9 @@ fn realignNode(...@@ -2274,6 +2294,9 @@ fn realignNode(
2274) Error!void {2294) Error!void {
2275 mf.nodes_lock.assertUnlocked();2295 mf.nodes_lock.assertUnlocked();
22762296
2297 const trace = tracy.trace(@src());
2298 defer trace.end();
2299
2277 const old_offset, const old_size = ni.location(mf).resolve(mf);2300 const old_offset, const old_size = ni.location(mf).resolve(mf);
22782301
2279 if (ni == .root or ni.position(mf) != .floating) {2302 if (ni == .root or ni.position(mf) != .floating) {
...@@ -2310,6 +2333,10 @@ fn updateWriters(mf: *MappedFile) void {...@@ -2310,6 +2333,10 @@ fn updateWriters(mf: *MappedFile) void {
2310fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) Error!void {2333fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) Error!void {
2311 if (old_file_offset == new_file_offset) return;2334 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
2313 if (old_file_offset >= new_file_offset + size or2340 if (old_file_offset >= new_file_offset + size or
2314 new_file_offset >= old_file_offset + size)2341 new_file_offset >= old_file_offset + size)
2315 {2342 {
...@@ -2348,6 +2375,10 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size:...@@ -2348,6 +2375,10 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size:
2348 }2375 }
2349}2376}
2350fn zeroRange(mf: *MappedFile, file_offset: u64, size: u64) Error!void {2377fn 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
2351 if (is_linux and2382 if (is_linux and
2352 !mf.flags.fallocate_punch_hole_unsupported and2383 !mf.flags.fallocate_punch_hole_unsupported and
2353 size >= mf.flags.block_size.toByteUnits() * 2 - 1)2384 size >= mf.flags.block_size.toByteUnits() * 2 - 1)
...@@ -2395,6 +2426,9 @@ fn copyFileRange(...@@ -2395,6 +2426,9 @@ fn copyFileRange(
2395 return 0;2426 return 0;
2396 }2427 }
23972428
2429 const trace = tracy.trace(@src());
2430 defer trace.end();
2431
2398 const min_size = mf.flags.block_size.toByteUnits() * 2 - 1;2432 const min_size = mf.flags.block_size.toByteUnits() * 2 - 1;
2399 if (size < min_size) return 0;2433 if (size < min_size) return 0;
24002434