| ... | ... | @@ -21,6 +21,15 @@ const Alignment = MappedFile.Alignment; |
| 21 | 21 | base: link.File, |
| 22 | 22 | options: link.File.OpenOptions, |
| 23 | 23 | mf: 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. |
| 27 | ehdr_fields: struct { |
| 28 | class: std.elf.CLASS, |
| 29 | type: EhdrType, |
| 30 | machine: EhdrMachine, |
| 31 | }, |
| 32 | |
| 24 | 33 | ni: struct { |
| 25 | 34 | elf: MappedFile.Node.Index, |
| 26 | 35 | ehdr: MappedFile.Node.Index, |
| ... | ... | @@ -208,29 +217,24 @@ navs: std.array_hash_map.Auto(InternPool.Nav.Index, struct { |
| 208 | 217 | lsi: Symbol.LocalIndex, |
| 209 | 218 | /// The start index of the contiguous sequence of symbol relocations in this NAV. |
| 210 | 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 | }), |
| 214 | 221 | uavs: std.array_hash_map.Auto(InternPool.Index, struct { |
| 215 | 222 | lsi: Symbol.LocalIndex, |
| 216 | 223 | /// The start index of the contiguous sequence of symbol relocations in this UAV. |
| 217 | 224 | first_symbol_reloc: SymbolReloc.Index, |
| 218 | | // No `first_got_reloc` field because a UAV never contains GOT relocations. |
| 219 | 225 | }), |
| 220 | 226 | lazy: std.EnumArray(link.File.LazySymbol.Kind, struct { |
| 221 | 227 | map: std.array_hash_map.Auto(InternPool.Index, struct { |
| 222 | 228 | lsi: Symbol.LocalIndex, |
| 223 | 229 | /// The start index of the contiguous sequence of symbol relocations in this lazy code/data. |
| 224 | 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 | 232 | pending_index: u32, |
| 229 | 233 | }), |
| 230 | 234 | pending_uavs: std.ArrayList(Node.UavMapIndex), |
| 231 | 235 | symbol_relocs: std.ArrayList(SymbolReloc), |
| 232 | 236 | node_relocs: std.ArrayList(NodeReloc), |
| 233 | | got_relocs: std.ArrayList(GotReloc), |
| 237 | got_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void), |
| 234 | 238 | /// Set of relocations which must be re-applied if the size of the TLS segment changes. |
| 235 | 239 | tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void), |
| 236 | 240 | /// Index matches the index into `shdrs`. Like `shdrs`, this map excludes `SHN_UNDEF`. |
| ... | ... | @@ -388,9 +392,6 @@ const Node = union(enum) { |
| 388 | 392 | fn firstSymbolReloc(nmi: NavMapIndex, elf: *const Elf) SymbolReloc.Index { |
| 389 | 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 | }; |
| 395 | 396 | |
| 396 | 397 | pub const UavMapIndex = enum(u32) { |
| ... | ... | @@ -407,11 +408,6 @@ const Node = union(enum) { |
| 407 | 408 | fn firstSymbolReloc(umi: UavMapIndex, elf: *const Elf) SymbolReloc.Index { |
| 408 | 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 | }; |
| 416 | 412 | |
| 417 | 413 | pub const LazyMapRef = struct { |
| ... | ... | @@ -437,9 +433,6 @@ const Node = union(enum) { |
| 437 | 433 | fn firstSymbolReloc(lmi: @This(), elf: *const Elf) SymbolReloc.Index { |
| 438 | 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 | } |
| 445 | 438 | |
| ... | ... | @@ -603,8 +596,6 @@ const InputSection = struct { |
| 603 | 596 | node: MappedFile.Node.Index, |
| 604 | 597 | /// The start index of the contiguous sequence of symbol relocations in this input section. |
| 605 | 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, |
| 608 | 599 | |
| 609 | 600 | const Index = enum(u32) { |
| 610 | 601 | _, |
| ... | ... | @@ -1387,14 +1378,28 @@ const SymbolReloc = struct { |
| 1387 | 1378 | node: MappedFile.Node.Index.Optional, |
| 1388 | 1379 | /// The offset of the relocation inside of `node`. |
| 1389 | 1380 | offset: u64, |
| 1390 | | /// A symbol used to compute the relocated value. Precise meaning depends on `@"type"`. |
| 1391 | | target: Symbol.Id, |
| 1392 | | /// A signed constant used to compute the relocated value. Precise meaning depends on `@"type"`. |
| 1381 | /// A symbol used to compute the relocated value. Precise meaning depends on `bits.@"type"`. |
| 1382 | target_symbol: Symbol.Id, |
| 1383 | /// A signed constant used to compute the relocated value. Precise meaning depends on `bits.@"type"`. |
| 1393 | 1384 | addend: SymbolReloc.Addend, |
| 1394 | | /// Specifies how to apply the relocation. |
| 1395 | | /// |
| 1396 | | /// When emitting a relocatable, this field is `undefined`. |
| 1397 | | type: SymbolReloc.Type, |
| 1385 | bits: packed struct(u32) { |
| 1386 | /// Specifies how to apply the relocation. |
| 1387 | /// |
| 1388 | /// When emitting a relocatable, this field is `undefined`. |
| 1389 | type: SymbolReloc.Type, |
| 1390 | |
| 1391 | result: enum(u3) { |
| 1392 | ok, |
| 1393 | overflowed, |
| 1394 | misaligned, |
| 1395 | illegal_dynamic_type, |
| 1396 | illegal_dynamic_section, |
| 1397 | }, |
| 1398 | |
| 1399 | relaxation: Relaxation, |
| 1400 | |
| 1401 | _: u10 = 0, |
| 1402 | }, |
| 1398 | 1403 | /// Forms a linked list of all symbol relocations with the same `target`. This list exists so |
| 1399 | 1404 | /// that all relocations targeting a particular symbol can be re-applied if that symbol moves. |
| 1400 | 1405 | /// Doubly-linked so that relocations can be removed. |
| ... | ... | @@ -1412,13 +1417,6 @@ const SymbolReloc = struct { |
| 1412 | 1417 | /// relocation entry. The entry will be removed if we discover a definition which allows us to |
| 1413 | 1418 | /// statically resolve the relocation. |
| 1414 | 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 | | }, |
| 1422 | 1420 | |
| 1423 | 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 | 1447 | }, |
| 1450 | 1448 | }; |
| 1451 | 1449 | |
| 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 | 1463 | /// Instead of using the ELF relocation enums, we have our own internal representation for |
| 1453 | 1464 | /// relocation types. This representation is more compact (requiring only 16 bits), and allows |
| 1454 | 1465 | /// sharing a lot of relocation handling between multiple relocs and target architectures. |
| ... | ... | @@ -1464,7 +1475,7 @@ const SymbolReloc = struct { |
| 1464 | 1475 | /// relocations which write multiple sequences of bits or which need to do unusual arithmetic on |
| 1465 | 1476 | /// a symbol value. The representation is simply a big enum containing all of these exceptional |
| 1466 | 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 | 1479 | /// Helper function for constructing a "simple" relocation type. This mainly exists to |
| 1469 | 1480 | /// improve readability in the relocation lowering logic in `addRelocAssumeCapacity`. |
| 1470 | 1481 | fn simple(target: Target, action: Simple) SymbolReloc.Type { |
| ... | ... | @@ -1493,7 +1504,7 @@ const SymbolReloc = struct { |
| 1493 | 1504 | /// Otherwise (for "simple" relocations), `Target` indicates the first step in computing the |
| 1494 | 1505 | /// relocation---whether we care about the target symbol's absolute address, its PC-relative |
| 1495 | 1506 | /// address, its PLT entry, etc. |
| 1496 | | const Target = enum(u3) { |
| 1507 | const Target = enum(u5) { |
| 1497 | 1508 | /// This is a "special" relocation whose specific type is in the `action.special` field. |
| 1498 | 1509 | special, |
| 1499 | 1510 | |
| ... | ... | @@ -1501,6 +1512,7 @@ const SymbolReloc = struct { |
| 1501 | 1512 | abs, |
| 1502 | 1513 | /// Offset from the relocation itself to the target symbol ("PC-relative"). |
| 1503 | 1514 | rel, |
| 1515 | |
| 1504 | 1516 | /// Address of the target symbol's PLT entry. |
| 1505 | 1517 | /// |
| 1506 | 1518 | /// If the target symbol does not have a PLT entry, equivalent to `.abs`. |
| ... | ... | @@ -1509,12 +1521,35 @@ const SymbolReloc = struct { |
| 1509 | 1521 | /// |
| 1510 | 1522 | /// If the target symbol does not have a PLT entry, equivalent to `.rel`. |
| 1511 | 1523 | pltrel, |
| 1524 | |
| 1512 | 1525 | /// Offset of the target TLS symbol from the base of this DSO's own TLS region. |
| 1513 | 1526 | dtpoff, |
| 1514 | 1527 | /// Offset of the target TLS symbol from the raw thread pointer. |
| 1515 | 1528 | tpoff, |
| 1529 | |
| 1516 | 1530 | /// Size of the target symbol. |
| 1517 | 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 | }; |
| 1519 | 1554 | |
| 1520 | 1555 | /// For a "simple" relocation, after the initial value is computed according to `Target`, a |
| ... | ... | @@ -1701,17 +1736,22 @@ const SymbolReloc = struct { |
| 1701 | 1736 | larch_b21, |
| 1702 | 1737 | larch_b26, |
| 1703 | 1738 | larch_call36, |
| 1739 | larch_got_pcala_hi20, |
| 1740 | larch_got_pcala64_lo20, |
| 1741 | larch_got_pcala64_hi12, |
| 1704 | 1742 | |
| 1705 | 1743 | sparc_olo10, |
| 1706 | 1744 | sparc_hix22, |
| 1707 | 1745 | sparc_le_hix22, |
| 1708 | 1746 | sparc_wdisp10, |
| 1709 | 1747 | sparc_wdisp16, |
| 1748 | sparc_got_op_lox10, |
| 1749 | sparc_got_op_hix22, |
| 1710 | 1750 | |
| 1711 | 1751 | fn applyInner( |
| 1712 | 1752 | s: Special, |
| 1713 | 1753 | elf: *Elf, |
| 1714 | | target: Symbol.Id, |
| 1754 | target_sym: Symbol.Id, |
| 1715 | 1755 | addend: SymbolReloc.Addend, |
| 1716 | 1756 | dest_vaddr: u64, |
| 1717 | 1757 | dest_slice: []u8, |
| ... | ... | @@ -1719,7 +1759,7 @@ const SymbolReloc = struct { |
| 1719 | 1759 | const normal_addend: u64 = @bitCast(addend.normal); |
| 1720 | 1760 | switch (s) { |
| 1721 | 1761 | .larch_pcala_hi20 => { |
| 1722 | | const val = target.value(elf) +% normal_addend; |
| 1762 | const val = target_sym.value(elf) +% normal_addend; |
| 1723 | 1763 | const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]); |
| 1724 | 1764 | elf.targetStore(inst, .{ |
| 1725 | 1765 | .b0_4 = elf.targetLoad(inst).b0_4, |
| ... | ... | @@ -1728,7 +1768,7 @@ const SymbolReloc = struct { |
| 1728 | 1768 | }); |
| 1729 | 1769 | }, |
| 1730 | 1770 | .larch_pcala64_lo20 => { |
| 1731 | | const val = target.value(elf) +% normal_addend; |
| 1771 | const val = target_sym.value(elf) +% normal_addend; |
| 1732 | 1772 | const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]); |
| 1733 | 1773 | elf.targetStore(inst, .{ |
| 1734 | 1774 | .b0_4 = elf.targetLoad(inst).b0_4, |
| ... | ... | @@ -1737,7 +1777,7 @@ const SymbolReloc = struct { |
| 1737 | 1777 | }); |
| 1738 | 1778 | }, |
| 1739 | 1779 | .larch_pcala64_hi12 => { |
| 1740 | | const val = target.value(elf) +% normal_addend; |
| 1780 | const val = target_sym.value(elf) +% normal_addend; |
| 1741 | 1781 | const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]); |
| 1742 | 1782 | elf.targetStore(inst, .{ |
| 1743 | 1783 | .b0_9 = elf.targetLoad(inst).b0_9, |
| ... | ... | @@ -1746,7 +1786,7 @@ const SymbolReloc = struct { |
| 1746 | 1786 | }); |
| 1747 | 1787 | }, |
| 1748 | 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 | 1790 | const jump_offset: i64 = @bitCast(target_vaddr +% normal_addend -% dest_vaddr); |
| 1751 | 1791 | if ((jump_offset >> 2) << 2 != jump_offset) { |
| 1752 | 1792 | return error.RelocationMisaligned; |
| ... | ... | @@ -1806,9 +1846,43 @@ const SymbolReloc = struct { |
| 1806 | 1846 | } |
| 1807 | 1847 | }, |
| 1808 | 1848 | |
| 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 | 1883 | .sparc_olo10 => { |
| 1810 | 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 | 1886 | const simm13 = std.math.cast(i13, lo10 + addends.o) orelse return error.RelocationOverflow; |
| 1813 | 1887 | const dest_ptr: *align(1) packed struct(u32) { |
| 1814 | 1888 | simm13: u13, |
| ... | ... | @@ -1825,7 +1899,7 @@ const SymbolReloc = struct { |
| 1825 | 1899 | b22_31: u10, |
| 1826 | 1900 | } = @ptrCast(dest_slice); |
| 1827 | 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 | 1903 | .b22_31 = elf.targetLoad(dest_ptr).b22_31, |
| 1830 | 1904 | }); |
| 1831 | 1905 | }, |
| ... | ... | @@ -1842,12 +1916,12 @@ const SymbolReloc = struct { |
| 1842 | 1916 | b22_31: u10, |
| 1843 | 1917 | } = @ptrCast(dest_slice); |
| 1844 | 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 | 1920 | .b22_31 = elf.targetLoad(dest_ptr).b22_31, |
| 1847 | 1921 | }); |
| 1848 | 1922 | }, |
| 1849 | 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 | 1925 | const jump_offset: i64 = @bitCast(target_vaddr +% normal_addend -% dest_vaddr); |
| 1852 | 1926 | if ((jump_offset >> 2) << 2 != jump_offset) { |
| 1853 | 1927 | return error.RelocationMisaligned; |
| ... | ... | @@ -1897,10 +1971,120 @@ const SymbolReloc = struct { |
| 1897 | 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 | }; |
| 1903 | 2000 | |
| 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 | 2088 | fn dependsOnTlsSize(t: SymbolReloc.Type, elf: *const Elf) bool { |
| 1905 | 2089 | return switch (elf.targetTlsVariant()) { |
| 1906 | 2090 | // In TLS variant I, the executable's TLS block starts at a fixed offset from the |
| ... | ... | @@ -1917,6 +2101,15 @@ const SymbolReloc = struct { |
| 1917 | 2101 | .pltrel, |
| 1918 | 2102 | .dtpoff, |
| 1919 | 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 | 2113 | => false, |
| 1921 | 2114 | |
| 1922 | 2115 | .tpoff => true, |
| ... | ... | @@ -1931,11 +2124,16 @@ const SymbolReloc = struct { |
| 1931 | 2124 | .larch_b21, |
| 1932 | 2125 | .larch_b26, |
| 1933 | 2126 | .larch_call36, |
| 2127 | .larch_got_pcala_hi20, |
| 2128 | .larch_got_pcala64_lo20, |
| 2129 | .larch_got_pcala64_hi12, |
| 1934 | 2130 | |
| 1935 | 2131 | .sparc_olo10, |
| 1936 | 2132 | .sparc_hix22, |
| 1937 | 2133 | .sparc_wdisp10, |
| 1938 | 2134 | .sparc_wdisp16, |
| 2135 | .sparc_got_op_lox10, |
| 2136 | .sparc_got_op_hix22, |
| 1939 | 2137 | => false, |
| 1940 | 2138 | }, |
| 1941 | 2139 | }, |
| ... | ... | @@ -1954,7 +2152,7 @@ const SymbolReloc = struct { |
| 1954 | 2152 | |
| 1955 | 2153 | fn apply(reloc: *SymbolReloc, elf: *Elf) void { |
| 1956 | 2154 | 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 | 2156 | // There's no point applying the relocation now, because it will be re-applied by |
| 1959 | 2157 | // `flushMoved` at some point anyway. |
| 1960 | 2158 | return; |
| ... | ... | @@ -1966,7 +2164,7 @@ const SymbolReloc = struct { |
| 1966 | 2164 | reloc.relaSection(elf).relaSetOffset(elf, rela_index, reloc_addr); |
| 1967 | 2165 | return; |
| 1968 | 2166 | } |
| 1969 | | switch (reloc.result) { |
| 2167 | switch (reloc.bits.result) { |
| 1970 | 2168 | .ok => {}, |
| 1971 | 2169 | .overflowed => elf.overflowed_reloc_count -= 1, |
| 1972 | 2170 | .misaligned => elf.misaligned_reloc_count -= 1, |
| ... | ... | @@ -1977,14 +2175,14 @@ const SymbolReloc = struct { |
| 1977 | 2175 | } |
| 1978 | 2176 | if (reloc.applyInner(elf)) { |
| 1979 | 2177 | @branchHint(.likely); |
| 1980 | | reloc.result = .ok; |
| 2178 | reloc.bits.result = .ok; |
| 1981 | 2179 | } else |err| switch (err) { |
| 1982 | 2180 | error.RelocationOverflow => { |
| 1983 | | reloc.result = .overflowed; |
| 2181 | reloc.bits.result = .overflowed; |
| 1984 | 2182 | elf.overflowed_reloc_count += 1; |
| 1985 | 2183 | }, |
| 1986 | 2184 | error.RelocationMisaligned => { |
| 1987 | | reloc.result = .misaligned; |
| 2185 | reloc.bits.result = .misaligned; |
| 1988 | 2186 | elf.misaligned_reloc_count += 1; |
| 1989 | 2187 | }, |
| 1990 | 2188 | } |
| ... | ... | @@ -1995,21 +2193,21 @@ const SymbolReloc = struct { |
| 1995 | 2193 | const dest_slice = node.slice(&elf.mf)[@intCast(reloc.offset)..]; |
| 1996 | 2194 | |
| 1997 | 2195 | const addend: u64 = @bitCast(reloc.addend); |
| 1998 | | const target_val: u64 = type: switch (reloc.type.target) { |
| 1999 | | .abs => reloc.target.value(elf) +% addend, |
| 2000 | | .rel => reloc.target.value(elf) +% addend -% dest_vaddr, |
| 2196 | const target_val: u64 = type: switch (reloc.bits.type.target) { |
| 2197 | .abs => reloc.target_symbol.value(elf) +% addend, |
| 2198 | .rel => reloc.target_symbol.value(elf) +% addend -% dest_vaddr, |
| 2001 | 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 | 2201 | break :type plt_entry_addr +% addend; |
| 2004 | 2202 | }, |
| 2005 | 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 | 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 | 2208 | .tpoff => switch (elf.targetTlsVariant()) { |
| 2011 | | .I_original => |tls| tls.tcb_size +% reloc.target.value(elf) +% addend, |
| 2012 | | .I_modified => |tls| 0 -% tls.tp_off +% reloc.target.value(elf) +% addend, |
| 2209 | .I_original => |tls| tls.tcb_size +% reloc.target_symbol.value(elf) +% addend, |
| 2210 | .I_modified => |tls| 0 -% tls.tp_off +% reloc.target_symbol.value(elf) +% addend, |
| 2013 | 2211 | .II => { |
| 2014 | 2212 | const tls_phndx = elf.getNode(elf.ni.tls.unwrap().?).segment; |
| 2015 | 2213 | const tls_size: u64 = switch (elf.phdrSlice()) { |
| ... | ... | @@ -2018,13 +2216,57 @@ const SymbolReloc = struct { |
| 2018 | 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), |
| 2025 | | .special => return reloc.type.action.special.applyInner( |
| 2222 | .size => reloc.target_symbol.size(elf), |
| 2223 | |
| 2224 | .gotabs_symbol => { |
| 2225 | const got_vaddr = elf.shndx.got.vaddr(elf); |
| 2226 | const got_offset = elf.got.getIndex(.{ .symbol = reloc.target_symbol }).? * elf.targetPtrSize(); |
| 2227 | break :type (got_vaddr + got_offset) +% addend; |
| 2228 | }, |
| 2229 | .gotrel_symbol => { |
| 2230 | const got_vaddr = elf.shndx.got.vaddr(elf); |
| 2231 | const got_offset = elf.got.getIndex(.{ .symbol = reloc.target_symbol }).? * elf.targetPtrSize(); |
| 2232 | break :type (got_vaddr + got_offset) +% addend -% dest_vaddr; |
| 2233 | }, |
| 2234 | .gotoff_symbol => { |
| 2235 | const got_offset = elf.got.getIndex(.{ .symbol = reloc.target_symbol }).? * elf.targetPtrSize(); |
| 2236 | break :type got_offset +% addend; |
| 2237 | }, |
| 2238 | .gotabs_tpoff => { |
| 2239 | const got_vaddr = elf.shndx.got.vaddr(elf); |
| 2240 | const got_offset = elf.got.getIndex(.{ .tpoff = reloc.target_symbol }).? * elf.targetPtrSize(); |
| 2241 | break :type (got_vaddr + got_offset) +% addend; |
| 2242 | }, |
| 2243 | .gotrel_tpoff => { |
| 2244 | const got_vaddr = elf.shndx.got.vaddr(elf); |
| 2245 | const got_offset = elf.got.getIndex(.{ .tpoff = reloc.target_symbol }).? * elf.targetPtrSize(); |
| 2246 | break :type (got_vaddr + got_offset) +% addend -% dest_vaddr; |
| 2247 | }, |
| 2248 | .gotoff_tpoff => { |
| 2249 | const got_offset = elf.got.getIndex(.{ .tpoff = reloc.target_symbol }).? * elf.targetPtrSize(); |
| 2250 | break :type got_offset +% addend; |
| 2251 | }, |
| 2252 | .gotabs_tlsgd => { |
| 2253 | const got_vaddr = elf.shndx.got.vaddr(elf); |
| 2254 | const got_offset = elf.got.getIndex(.{ .tlsgd0 = reloc.target_symbol }).? * elf.targetPtrSize(); |
| 2255 | break :type (got_vaddr + got_offset) +% addend; |
| 2256 | }, |
| 2257 | .gotrel_tlsgd => { |
| 2258 | const got_vaddr = elf.shndx.got.vaddr(elf); |
| 2259 | const got_offset = elf.got.getIndex(.{ .tlsgd0 = reloc.target_symbol }).? * elf.targetPtrSize(); |
| 2260 | break :type (got_vaddr + got_offset) +% addend -% dest_vaddr; |
| 2261 | }, |
| 2262 | .gotoff_tlsgd => { |
| 2263 | const got_offset = elf.got.getIndex(.{ .tlsgd0 = reloc.target_symbol }).? * elf.targetPtrSize(); |
| 2264 | break :type got_offset +% addend; |
| 2265 | }, |
| 2266 | |
| 2267 | .special => return reloc.bits.type.action.special.applyInner( |
| 2026 | 2268 | elf, |
| 2027 | | reloc.target, |
| 2269 | reloc.target_symbol, |
| 2028 | 2270 | reloc.addend, |
| 2029 | 2271 | dest_vaddr, |
| 2030 | 2272 | dest_slice, |
| ... | ... | @@ -2033,7 +2275,7 @@ const SymbolReloc = struct { |
| 2033 | 2275 | |
| 2034 | 2276 | // Check for the `R_*_RELATIVE` case now, because it is possible only when no shift or cast |
| 2035 | 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 | 2279 | .static => unreachable, |
| 2038 | 2280 | .dynamic => { |
| 2039 | 2281 | // The relocation happens at runtime; we just need to update its offset. |
| ... | ... | @@ -2046,31 +2288,34 @@ const SymbolReloc = struct { |
| 2046 | 2288 | // destination slice, we actually want to write it to the runtime relocation entry. |
| 2047 | 2289 | switch (elf.identClass()) { |
| 2048 | 2290 | .NONE, _ => unreachable, |
| 2049 | | .@"32" => assert(reloc.type.action.simple.dest == .@"32"), |
| 2050 | | .@"64" => assert(reloc.type.action.simple.dest == .@"64"), |
| 2291 | .@"32" => assert(reloc.bits.type.action.simple.dest == .@"32"), |
| 2292 | .@"64" => assert(reloc.bits.type.action.simple.dest == .@"64"), |
| 2051 | 2293 | } |
| 2052 | | assert(reloc.type.action.simple.cast == .unsigned); |
| 2053 | | assert(reloc.type.action.simple.shift == .@"0"); |
| 2294 | assert(reloc.bits.type.action.simple.cast == .unsigned); |
| 2295 | assert(reloc.bits.type.action.simple.shift == .@"0"); |
| 2054 | 2296 | elf.shndx.rela_dyn.relaSetOffset(elf, rela_index, dest_vaddr); |
| 2055 | 2297 | elf.shndx.rela_dyn.relaSetAddend(elf, rela_index, target_val); |
| 2056 | 2298 | return; |
| 2057 | 2299 | }, |
| 2058 | 2300 | }; |
| 2059 | 2301 | |
| 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 | } |
| 2062 | 2304 | |
| 2063 | 2305 | fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void { |
| 2064 | 2306 | assert(index.get(elf) == reloc); |
| 2065 | 2307 | |
| 2066 | 2308 | reloc.deleteOutputRel(elf); |
| 2067 | | if (reloc.type.dependsOnTlsSize(elf)) { |
| 2309 | if (reloc.bits.type.dependsOnTlsSize(elf)) { |
| 2068 | 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 | } |
| 2070 | 2315 | |
| 2071 | 2316 | switch (reloc.prev) { |
| 2072 | 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 | 2319 | assert(first_target_reloc.* == index); |
| 2075 | 2320 | first_target_reloc.* = reloc.next; |
| 2076 | 2321 | }, |
| ... | ... | @@ -2080,7 +2325,7 @@ const SymbolReloc = struct { |
| 2080 | 2325 | .none => {}, |
| 2081 | 2326 | else => |next| next.get(elf).prev = reloc.prev, |
| 2082 | 2327 | } |
| 2083 | | switch (reloc.result) { |
| 2328 | switch (reloc.bits.result) { |
| 2084 | 2329 | .ok => {}, |
| 2085 | 2330 | .overflowed => elf.overflowed_reloc_count -= 1, |
| 2086 | 2331 | .misaligned => elf.misaligned_reloc_count -= 1, |
| ... | ... | @@ -2108,30 +2353,58 @@ const SymbolReloc = struct { |
| 2108 | 2353 | reloc.rela_index = .none; |
| 2109 | 2354 | } |
| 2110 | 2355 | |
| 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 | 2358 | assert(elf.ehdrType() != .REL); |
| 2113 | 2359 | const reloc_node = reloc.node.unwrap().?; // deleted |
| 2114 | 2360 | |
| 2361 | const trace = tracy.trace(@src()); |
| 2362 | defer trace.end(); |
| 2363 | |
| 2115 | 2364 | // Delete any current output relocation or error. |
| 2116 | | switch (reloc.result) { |
| 2365 | switch (reloc.bits.result) { |
| 2117 | 2366 | .ok, .overflowed, .misaligned => { |
| 2118 | 2367 | reloc.deleteOutputRel(elf); |
| 2119 | 2368 | }, |
| 2120 | 2369 | .illegal_dynamic_type => { |
| 2121 | 2370 | elf.illegal_dynamic_type_reloc_count -= 1; |
| 2122 | | reloc.result = .ok; |
| 2371 | reloc.bits.result = .ok; |
| 2123 | 2372 | }, |
| 2124 | 2373 | .illegal_dynamic_section => { |
| 2125 | 2374 | elf.illegal_dynamic_section_reloc_count -= 1; |
| 2126 | | reloc.result = .ok; |
| 2375 | reloc.bits.result = .ok; |
| 2127 | 2376 | }, |
| 2128 | 2377 | } |
| 2129 | 2378 | |
| 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 | 2403 | assert(reloc.rela_index == .none); |
| 2131 | 2404 | |
| 2132 | | const symbol_class = elf.classifySymbolValue(reloc.target); |
| 2405 | const symbol_class = elf.classifySymbolValue(reloc.target_symbol); |
| 2133 | 2406 | |
| 2134 | | switch (reloc.target.unwrap()) { |
| 2407 | switch (reloc.target_symbol.unwrap()) { |
| 2135 | 2408 | .local => {}, |
| 2136 | 2409 | .global => |gsi| switch (symbol_class) { |
| 2137 | 2410 | .static => gsi.ptr(elf).status.any_static_target_relocs = true, |
| ... | ... | @@ -2152,25 +2425,36 @@ const SymbolReloc = struct { |
| 2152 | 2425 | } = switch (symbol_class) { |
| 2153 | 2426 | .static => return, |
| 2154 | 2427 | |
| 2155 | | .static_relative => switch (reloc.type.target) { |
| 2428 | .static_relative => switch (reloc.bits.type.target) { |
| 2156 | 2429 | // We need a runtime relocation if the relocation depends on our load address, which |
| 2157 | 2430 | // is the case iff the relocation resolves to the absolute value of a symbol. Relocs |
| 2158 | 2431 | // which are PC-relative, or which resolve to something other than a symbol address |
| 2159 | 2432 | // (e.g. a symbol's size), are fine. |
| 2160 | 2433 | |
| 2161 | 2434 | .pltrel, |
| 2435 | .gotrel_symbol, |
| 2436 | .gotoff_symbol, |
| 2437 | .gotrel_tpoff, |
| 2438 | .gotoff_tpoff, |
| 2439 | .gotrel_tlsgd, |
| 2440 | .gotoff_tlsgd, |
| 2162 | 2441 | .rel, |
| 2163 | 2442 | .size, |
| 2164 | 2443 | .dtpoff, |
| 2165 | 2444 | .tpoff, |
| 2166 | 2445 | => return, |
| 2167 | 2446 | |
| 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 | 2453 | true => .relative, |
| 2170 | 2454 | false => .illegal, |
| 2171 | 2455 | }, |
| 2172 | 2456 | |
| 2173 | | .special => switch (reloc.type.action.special) { |
| 2457 | .special => switch (reloc.bits.type.action.special) { |
| 2174 | 2458 | .larch_pcala_hi20, |
| 2175 | 2459 | .larch_pcala64_lo20, |
| 2176 | 2460 | .larch_pcala64_hi12, |
| ... | ... | @@ -2180,6 +2464,11 @@ const SymbolReloc = struct { |
| 2180 | 2464 | .sparc_wdisp10, |
| 2181 | 2465 | .sparc_wdisp16, |
| 2182 | 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 | 2472 | => return, |
| 2184 | 2473 | |
| 2185 | 2474 | .sparc_olo10, |
| ... | ... | @@ -2188,44 +2477,60 @@ const SymbolReloc = struct { |
| 2188 | 2477 | }, |
| 2189 | 2478 | }, |
| 2190 | 2479 | |
| 2191 | | .dynamic => switch (reloc.type.target) { |
| 2192 | | // PLT relocations targeting dynamic symbols actually target that symbol's PLT |
| 2193 | | // entry, so we should emit an `R_*_RELATIVE` relocation instead. |
| 2194 | | .pltabs => switch (reloc.type.action.simple.dest.isAddr(elf)) { |
| 2480 | .dynamic => switch (reloc.bits.type.target) { |
| 2481 | // PLT/GOT relocations targeting dynamic symbols actually target that symbol's PLT |
| 2482 | // or GOT entry, so we should emit an `R_*_RELATIVE` relocation instead. |
| 2483 | .pltabs, |
| 2484 | .gotabs_symbol, |
| 2485 | .gotabs_tpoff, |
| 2486 | .gotabs_tlsgd, |
| 2487 | => switch (reloc.bits.type.action.simple.dest.isAddr(elf)) { |
| 2195 | 2488 | true => .relative, |
| 2196 | 2489 | false => .illegal, |
| 2197 | 2490 | }, |
| 2198 | | // ...although PC-relative PLT relocations don't even need that that! |
| 2199 | | .pltrel => return, |
| 2491 | // ...although PC-relative PLT/GOT relocations don't even need that that! |
| 2492 | .pltrel, |
| 2493 | .gotrel_symbol, |
| 2494 | .gotoff_symbol, |
| 2495 | .gotrel_tpoff, |
| 2496 | .gotoff_tpoff, |
| 2497 | .gotrel_tlsgd, |
| 2498 | .gotoff_tlsgd, |
| 2499 | => return, |
| 2200 | 2500 | // PC-relative relocations are not valid dynamic relocations. |
| 2201 | 2501 | .rel => .illegal, |
| 2202 | 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 | 2504 | .@"32" => .size32, |
| 2205 | 2505 | .@"64" => .size64, |
| 2206 | 2506 | else => .illegal, |
| 2207 | 2507 | }, |
| 2208 | 2508 | // Absolute addresses and TLS offsets can be lowered at runtime provided they |
| 2209 | 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 | 2511 | true => .dtp_off, |
| 2212 | 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 | 2515 | true => .tp_off, |
| 2216 | 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 | 2519 | true => .abs_addr, |
| 2220 | 2520 | false => .illegal, |
| 2221 | 2521 | }, |
| 2222 | 2522 | |
| 2223 | | .special => switch (reloc.type.action.special) { |
| 2523 | .special => switch (reloc.bits.type.action.special) { |
| 2224 | 2524 | .larch_b21, |
| 2225 | 2525 | .larch_b26, |
| 2226 | 2526 | .larch_call36, |
| 2227 | 2527 | .sparc_wdisp10, |
| 2228 | 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 | 2534 | => return, |
| 2230 | 2535 | |
| 2231 | 2536 | .larch_pcala_hi20, |
| ... | ... | @@ -2255,7 +2560,7 @@ const SymbolReloc = struct { |
| 2255 | 2560 | .abs_addr, |
| 2256 | 2561 | .dtp_off, |
| 2257 | 2562 | .tp_off, |
| 2258 | | => switch (reloc.target.unwrap()) { |
| 2563 | => switch (reloc.target_symbol.unwrap()) { |
| 2259 | 2564 | .local => true, |
| 2260 | 2565 | .global => |gsi| gsi.resolveAlias(elf).defined(elf), |
| 2261 | 2566 | }, |
| ... | ... | @@ -2267,7 +2572,7 @@ const SymbolReloc = struct { |
| 2267 | 2572 | }; |
| 2268 | 2573 | if (!allow) { |
| 2269 | 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 | 2576 | elf.illegal_dynamic_section_reloc_count += 1; |
| 2272 | 2577 | } |
| 2273 | 2578 | return; |
| ... | ... | @@ -2278,7 +2583,7 @@ const SymbolReloc = struct { |
| 2278 | 2583 | |
| 2279 | 2584 | const rel_type: MachineRelocType = rel: switch (want_dynamic_reloc) { |
| 2280 | 2585 | .illegal => { |
| 2281 | | reloc.result = .illegal_dynamic_type; |
| 2586 | reloc.bits.result = .illegal_dynamic_type; |
| 2282 | 2587 | elf.illegal_dynamic_type_reloc_count += 1; |
| 2283 | 2588 | return; |
| 2284 | 2589 | }, |
| ... | ... | @@ -2292,7 +2597,7 @@ const SymbolReloc = struct { |
| 2292 | 2597 | const target_dynsym: u32 = switch (want_dynamic_reloc) { |
| 2293 | 2598 | .illegal => unreachable, // handled above |
| 2294 | 2599 | .relative => 0, |
| 2295 | | else => reloc.target.unwrap().global.dynsymIndex(elf).?, |
| 2600 | else => reloc.target_symbol.unwrap().global.dynsymIndex(elf).?, |
| 2296 | 2601 | }; |
| 2297 | 2602 | |
| 2298 | 2603 | try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, 1); |
| ... | ... | @@ -2435,305 +2740,98 @@ const GotKey = union(enum) { |
| 2435 | 2740 | /// Value is the signed offset of the given symbol from the TLS pointer. |
| 2436 | 2741 | tpoff: Symbol.Id, |
| 2437 | 2742 | |
| 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. |
| 2441 | | tlsld0, |
| 2442 | | /// Value is always 0. |
| 2443 | | /// |
| 2444 | | /// Used for the second of the two GOT entries generated by a TLSLD relocation. |
| 2445 | | tlsld1, |
| 2446 | | |
| 2447 | | /// Value is the TLS module ID for the given STT_TLS symbol. |
| 2746 | /// Alternative |
| 2448 | 2747 | /// |
| 2449 | 2748 | /// Used for the first of the two GOT entries generated by a TLSGD relocation. |
| 2450 | 2749 | tlsgd0: Symbol.Id, |
| 2451 | 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 | 2753 | /// Used for the second of the two GOT entries generated by a TLSGD relocation. |
| 2454 | 2754 | tlsgd1: Symbol.Id, |
| 2455 | 2755 | }; |
| 2456 | 2756 | |
| 2457 | | /// A relocation targeting a particular GOT entry. |
| 2458 | | const GotReloc = struct { |
| 2459 | | /// The node containing this relocation. Possible values are: |
| 2460 | | /// * An input section |
| 2461 | | /// * A section |
| 2462 | | /// * A NAV, UAV, or lazy code/data |
| 2463 | | /// * `.none`, if this relocation was deleted (in which case it should be ignored) |
| 2464 | | node: MappedFile.Node.Index.Optional, |
| 2465 | | /// The offset of the relocation inside of `node`. |
| 2466 | | offset: u64, |
| 2467 | | target: GotKey, |
| 2468 | | addend: i64, |
| 2469 | | type: GotReloc.Type, |
| 2470 | | result: enum(u8) { ok, overflowed, misaligned }, |
| 2471 | | |
| 2472 | | /// `GotReloc.Type` has the same structure as `SymbolReloc.Type`, just with different `Target` |
| 2473 | | /// and `Special` enums---consult doc comments on `SymbolReloc.Type` for an overview. |
| 2474 | | const Type = packed struct(u16) { |
| 2475 | | fn simple(target: Target, action: Simple) GotReloc.Type { |
| 2476 | | assert(target != .special); |
| 2477 | | return .{ .target = target, .action = .{ .simple = action } }; |
| 2478 | | } |
| 2479 | | |
| 2480 | | fn special(s: Special) GotReloc.Type { |
| 2481 | | return .{ .target = .special, .action = .{ .special = s } }; |
| 2482 | | } |
| 2757 | /// Records all global symbols defined in any DSO we depend on. For each symbol, its name, version, |
| 2758 | /// type, size, and alignment are all stored, as well as the soname of the DSO defining it. If one |
| 2759 | /// of these symbols ends up being referenced by the ELF module we are producing, we need this |
| 2760 | /// information for a few reasons: |
| 2761 | /// |
| 2762 | /// * If an undefined symbol has an external definition with type `STT_FUNC`, we know to create a |
| 2763 | /// PLT entry for it. |
| 2764 | /// |
| 2765 | /// * If an undefined symbol has an external definition with type `STT_OBJECT`, we can create a copy |
| 2766 | /// relocation for it if necessary. |
| 2767 | /// |
| 2768 | /// * If an undefined versioned symbol has an external definition, we can add the required entry to |
| 2769 | /// `.gnu.version_r` because we know the soname of the DSO defining it. |
| 2770 | /// |
| 2771 | /// * Similarly, if an undefined unversioned symbol matches an external definition of a default |
| 2772 | /// symbol version, we know to emit the default version for the dynamic symbol table entry even |
| 2773 | /// though that symbol version was not explicitly requested. |
| 2774 | /// |
| 2775 | /// * If an undefined symbol does *not* have any external definition, when emitting a dynamic |
| 2776 | /// executable, we can emit an "undefined global symbol" link error for it. |
| 2777 | const DsoGlobals = struct { |
| 2778 | string_bytes: std.ArrayList(u8), |
| 2483 | 2779 | |
| 2484 | | target: Target, |
| 2485 | | action: packed union { |
| 2486 | | simple: Simple, |
| 2487 | | special: Special, |
| 2488 | | }, |
| 2780 | /// Contains all global symbols defined in any needed DSO. |
| 2781 | /// |
| 2782 | /// Accessed using `DsoGlobals.Symbol.Adapter`. Adapted key type is `DsoGlobals.Symbol.Key`. |
| 2783 | symbols: std.array_hash_map.Custom(DsoGlobals.Symbol, void, void, true), |
| 2489 | 2784 | |
| 2490 | | /// Like `SymbolReloc.Target`, but for GOT relocations. There are fewer tags because there |
| 2491 | | /// are fewer different kinds of GOT relocation. |
| 2492 | | const Target = enum(u3) { |
| 2493 | | /// This is a "special" relocation whose specific type is in the `action.special` field. |
| 2494 | | special, |
| 2785 | /// Contains all default-version global symbols defined in any needed DSO. |
| 2786 | /// |
| 2787 | /// Accessed using `DsoGlobals.Symbol.DefaultVersionAdapter`. Adapted key type is `[]const u8`. |
| 2788 | /// |
| 2789 | /// Stored key is an index into `DsoGlobals.symbols`. |
| 2790 | default_sym_vers: std.array_hash_map.Custom(u32, void, void, true), |
| 2495 | 2791 | |
| 2496 | | /// Absolute address of the GOT entry. |
| 2497 | | abs, |
| 2498 | | /// Offset from the relocation itself to the GOT entry ("PC-relative"). |
| 2499 | | rel, |
| 2500 | | /// Offset from the base of the GOT to the GOT entry. |
| 2501 | | offset, |
| 2792 | /// Index into `DsoGlobals.string_bytes`. This is a different type than `Elf.String` because |
| 2793 | /// these string bytes are in a `std.ArrayList` rather than a section of the output binary. |
| 2794 | const String = enum(u32) { |
| 2795 | _, |
| 2796 | fn slice(s: DsoGlobals.String, dso_globals: *const DsoGlobals) [:0]const u8 { |
| 2797 | const overlong = dso_globals.string_bytes.items[@backingInt(s)..]; |
| 2798 | return overlong[0..std.mem.findScalar(u8, overlong, 0).? :0]; |
| 2799 | } |
| 2800 | const Optional = enum(u32) { |
| 2801 | none = std.math.maxInt(u32), |
| 2802 | _, |
| 2803 | fn unwrap(os: DsoGlobals.String.Optional) ?DsoGlobals.String { |
| 2804 | return switch (os) { |
| 2805 | .none => null, |
| 2806 | _ => @fromBackingInt(@backingInt(os)), |
| 2807 | }; |
| 2808 | } |
| 2809 | fn wrap(s: DsoGlobals.String) DsoGlobals.String.Optional { |
| 2810 | return @bitCast(s); |
| 2811 | } |
| 2502 | 2812 | }; |
| 2813 | }; |
| 2503 | 2814 | |
| 2504 | | const Simple = SymbolReloc.Type.Simple; |
| 2505 | | |
| 2506 | | /// Like `SymbolReloc.Special`, but for GOT relocations. |
| 2507 | | const Special = enum(u13) { |
| 2508 | | larch_pcala_hi20, |
| 2509 | | larch_pcala64_lo20, |
| 2510 | | larch_pcala64_hi12, |
| 2815 | const Symbol = struct { |
| 2816 | name: DsoGlobals.String, |
| 2817 | /// `.none` if the symbol is unversioned. |
| 2818 | version: DsoGlobals.String.Optional, |
| 2819 | type: std.elf.STT, |
| 2820 | soname: Elf.String(.dynstr), |
| 2821 | size: u64, |
| 2822 | /// This is usually unnecessary, but if a symbol is given a copy relocation (`R_*_COPY`) and |
| 2823 | /// so becomes a part of the executable's address space despite being defined by a different |
| 2824 | /// DSO, we need to know its alignment requirement so that we don't break other code. This |
| 2825 | /// isn't actually stored on the symbol---instead we compute a maximum alignment from the |
| 2826 | /// alignment of the section containing the symbol, and the symbol's offset within the |
| 2827 | /// section. I know this sounds like a terrible hack, but it is *genuinely* how you're |
| 2828 | /// supposed to do this. Copy relocations suck. |
| 2829 | alignment: Alignment, |
| 2511 | 2830 | |
| 2512 | | sparc_op_lox10, |
| 2513 | | sparc_op_hix22, |
| 2514 | | |
| 2515 | | fn applyInner( |
| 2516 | | s: Special, |
| 2517 | | elf: *Elf, |
| 2518 | | got_vaddr: u64, |
| 2519 | | got_offset: u64, |
| 2520 | | addend: u64, |
| 2521 | | dest_vaddr: u64, |
| 2522 | | dest_slice: []u8, |
| 2523 | | ) error{ RelocationMisaligned, RelocationOverflow }!void { |
| 2524 | | switch (s) { |
| 2525 | | .larch_pcala_hi20 => { |
| 2526 | | const val = got_vaddr +% got_offset +% addend; |
| 2527 | | const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]); |
| 2528 | | elf.targetStore(inst, .{ |
| 2529 | | .b0_4 = elf.targetLoad(inst).b0_4, |
| 2530 | | .j20 = link.loongarch.pcalaHi20(val, dest_vaddr), |
| 2531 | | .b25_31 = elf.targetLoad(inst).b25_31, |
| 2532 | | }); |
| 2533 | | }, |
| 2534 | | .larch_pcala64_lo20 => { |
| 2535 | | const val = got_vaddr +% got_offset +% addend; |
| 2536 | | const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]); |
| 2537 | | elf.targetStore(inst, .{ |
| 2538 | | .b0_4 = elf.targetLoad(inst).b0_4, |
| 2539 | | .j20 = link.loongarch.pcala64Lo20(val, dest_vaddr), |
| 2540 | | .b25_31 = elf.targetLoad(inst).b25_31, |
| 2541 | | }); |
| 2542 | | }, |
| 2543 | | .larch_pcala64_hi12 => { |
| 2544 | | const val = got_vaddr +% got_offset +% addend; |
| 2545 | | const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]); |
| 2546 | | elf.targetStore(inst, .{ |
| 2547 | | .b0_9 = elf.targetLoad(inst).b0_9, |
| 2548 | | .k12 = link.loongarch.pcala64Hi12(val, dest_vaddr), |
| 2549 | | .b22_31 = elf.targetLoad(inst).b22_31, |
| 2550 | | }); |
| 2551 | | }, |
| 2552 | | .sparc_op_lox10 => { |
| 2553 | | const dest_ptr: *align(1) packed struct(u32) { |
| 2554 | | imm13: u13, |
| 2555 | | b13_31: u19, |
| 2556 | | } = @ptrCast(dest_slice); |
| 2557 | | elf.targetStore(dest_ptr, .{ |
| 2558 | | .imm13 = @as(u10, @truncate(got_offset)), |
| 2559 | | .b13_31 = elf.targetLoad(dest_ptr).b13_31, |
| 2560 | | }); |
| 2561 | | }, |
| 2562 | | .sparc_op_hix22 => { |
| 2563 | | const dest_ptr: *align(1) packed struct(u32) { |
| 2564 | | imm22: u22, |
| 2565 | | b22_31: u10, |
| 2566 | | } = @ptrCast(dest_slice); |
| 2567 | | elf.targetStore(dest_ptr, .{ |
| 2568 | | .imm22 = @truncate(got_offset >> 10), |
| 2569 | | .b22_31 = elf.targetLoad(dest_ptr).b22_31, |
| 2570 | | }); |
| 2571 | | }, |
| 2572 | | } |
| 2573 | | } |
| 2574 | | }; |
| 2575 | | }; |
| 2576 | | |
| 2577 | | const Index = enum(u32) { |
| 2578 | | none = std.math.maxInt(u32), |
| 2579 | | _, |
| 2580 | | |
| 2581 | | fn get(index: GotReloc.Index, elf: *Elf) *GotReloc { |
| 2582 | | return &elf.got_relocs.items[@backingInt(index)]; |
| 2583 | | } |
| 2584 | | }; |
| 2585 | | |
| 2586 | | fn apply(reloc: *GotReloc, elf: *Elf) void { |
| 2587 | | assert(elf.ehdrType() != .REL); |
| 2588 | | const node = reloc.node.unwrap() orelse return; // deleted |
| 2589 | | if (node.hasMoved(&elf.mf) or elf.shndx.got.node(elf).hasMoved(&elf.mf)) { |
| 2590 | | // There's no point applying the relocation now, because it will be re-applied by |
| 2591 | | // `flushMoved` at some point anyway. |
| 2592 | | return; |
| 2593 | | } |
| 2594 | | switch (reloc.result) { |
| 2595 | | .ok => {}, |
| 2596 | | .overflowed => elf.overflowed_reloc_count -= 1, |
| 2597 | | .misaligned => elf.misaligned_reloc_count -= 1, |
| 2598 | | } |
| 2599 | | if (reloc.applyInner(elf)) { |
| 2600 | | @branchHint(.likely); |
| 2601 | | reloc.result = .ok; |
| 2602 | | } else |err| switch (err) { |
| 2603 | | error.RelocationOverflow => { |
| 2604 | | reloc.result = .overflowed; |
| 2605 | | elf.overflowed_reloc_count += 1; |
| 2606 | | }, |
| 2607 | | error.RelocationMisaligned => { |
| 2608 | | reloc.result = .misaligned; |
| 2609 | | elf.misaligned_reloc_count += 1; |
| 2610 | | }, |
| 2611 | | } |
| 2612 | | } |
| 2613 | | fn applyInner(reloc: *const GotReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void { |
| 2614 | | const node = reloc.node.unwrap().?; |
| 2615 | | const dest_vaddr = elf.getNodeVAddr(node) + reloc.offset; |
| 2616 | | const dest_slice = node.slice(&elf.mf)[@intCast(reloc.offset)..]; |
| 2617 | | |
| 2618 | | const got_vaddr = elf.shndx.got.vaddr(elf); |
| 2619 | | const got_index: u64 = elf.got.getIndex(reloc.target).?; |
| 2620 | | const got_offset: u64 = switch (elf.identClass()) { |
| 2621 | | .NONE, _ => unreachable, |
| 2622 | | inline else => |class| @sizeOf(class.ElfN().Addr) * got_index, |
| 2623 | | }; |
| 2624 | | const addend: u64 = @bitCast(reloc.addend); |
| 2625 | | |
| 2626 | | const target_val: u64 = switch (reloc.type.target) { |
| 2627 | | .abs => got_vaddr +% got_offset +% addend, |
| 2628 | | .rel => got_vaddr +% got_offset +% addend -% dest_vaddr, |
| 2629 | | .offset => got_offset +% addend, |
| 2630 | | .special => return reloc.type.action.special.applyInner( |
| 2631 | | elf, |
| 2632 | | got_vaddr, |
| 2633 | | got_offset, |
| 2634 | | addend, |
| 2635 | | dest_vaddr, |
| 2636 | | dest_slice, |
| 2637 | | ), |
| 2638 | | }; |
| 2639 | | try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian()); |
| 2640 | | } |
| 2641 | | |
| 2642 | | fn delete(reloc: *GotReloc, elf: *Elf) void { |
| 2643 | | switch (reloc.result) { |
| 2644 | | .ok => {}, |
| 2645 | | .overflowed => elf.overflowed_reloc_count -= 1, |
| 2646 | | .misaligned => elf.misaligned_reloc_count -= 1, |
| 2647 | | } |
| 2648 | | reloc.* = .{ |
| 2649 | | .node = .none, |
| 2650 | | .offset = undefined, |
| 2651 | | .target = undefined, |
| 2652 | | .addend = undefined, |
| 2653 | | .type = undefined, |
| 2654 | | .result = undefined, |
| 2655 | | }; |
| 2656 | | } |
| 2657 | | }; |
| 2658 | | |
| 2659 | | /// Records all global symbols defined in any DSO we depend on. For each symbol, its name, version, |
| 2660 | | /// type, size, and alignment are all stored, as well as the soname of the DSO defining it. If one |
| 2661 | | /// of these symbols ends up being referenced by the ELF module we are producing, we need this |
| 2662 | | /// information for a few reasons: |
| 2663 | | /// |
| 2664 | | /// * If an undefined symbol has an external definition with type `STT_FUNC`, we know to create a |
| 2665 | | /// PLT entry for it. |
| 2666 | | /// |
| 2667 | | /// * If an undefined symbol has an external definition with type `STT_OBJECT`, we can create a copy |
| 2668 | | /// relocation for it if necessary. |
| 2669 | | /// |
| 2670 | | /// * If an undefined versioned symbol has an external definition, we can add the required entry to |
| 2671 | | /// `.gnu.version_r` because we know the soname of the DSO defining it. |
| 2672 | | /// |
| 2673 | | /// * Similarly, if an undefined unversioned symbol matches an external definition of a default |
| 2674 | | /// symbol version, we know to emit the default version for the dynamic symbol table entry even |
| 2675 | | /// though that symbol version was not explicitly requested. |
| 2676 | | /// |
| 2677 | | /// * If an undefined symbol does *not* have any external definition, when emitting a dynamic |
| 2678 | | /// executable, we can emit an "undefined global symbol" link error for it. |
| 2679 | | const DsoGlobals = struct { |
| 2680 | | string_bytes: std.ArrayList(u8), |
| 2681 | | |
| 2682 | | /// Contains all global symbols defined in any needed DSO. |
| 2683 | | /// |
| 2684 | | /// Accessed using `DsoGlobals.Symbol.Adapter`. Adapted key type is `DsoGlobals.Symbol.Key`. |
| 2685 | | symbols: std.array_hash_map.Custom(DsoGlobals.Symbol, void, void, true), |
| 2686 | | |
| 2687 | | /// Contains all default-version global symbols defined in any needed DSO. |
| 2688 | | /// |
| 2689 | | /// Accessed using `DsoGlobals.Symbol.DefaultVersionAdapter`. Adapted key type is `[]const u8`. |
| 2690 | | /// |
| 2691 | | /// Stored key is an index into `DsoGlobals.symbols`. |
| 2692 | | default_sym_vers: std.array_hash_map.Custom(u32, void, void, true), |
| 2693 | | |
| 2694 | | /// Index into `DsoGlobals.string_bytes`. This is a different type than `Elf.String` because |
| 2695 | | /// these string bytes are in a `std.ArrayList` rather than a section of the output binary. |
| 2696 | | const String = enum(u32) { |
| 2697 | | _, |
| 2698 | | fn slice(s: DsoGlobals.String, dso_globals: *const DsoGlobals) [:0]const u8 { |
| 2699 | | const overlong = dso_globals.string_bytes.items[@backingInt(s)..]; |
| 2700 | | return overlong[0..std.mem.findScalar(u8, overlong, 0).? :0]; |
| 2701 | | } |
| 2702 | | const Optional = enum(u32) { |
| 2703 | | none = std.math.maxInt(u32), |
| 2704 | | _, |
| 2705 | | fn unwrap(os: DsoGlobals.String.Optional) ?DsoGlobals.String { |
| 2706 | | return switch (os) { |
| 2707 | | .none => null, |
| 2708 | | _ => @fromBackingInt(@backingInt(os)), |
| 2709 | | }; |
| 2710 | | } |
| 2711 | | fn wrap(s: DsoGlobals.String) DsoGlobals.String.Optional { |
| 2712 | | return @bitCast(s); |
| 2713 | | } |
| 2714 | | }; |
| 2715 | | }; |
| 2716 | | |
| 2717 | | const Symbol = struct { |
| 2718 | | name: DsoGlobals.String, |
| 2719 | | /// `.none` if the symbol is unversioned. |
| 2720 | | version: DsoGlobals.String.Optional, |
| 2721 | | type: std.elf.STT, |
| 2722 | | soname: Elf.String(.dynstr), |
| 2723 | | size: u64, |
| 2724 | | /// This is usually unnecessary, but if a symbol is given a copy relocation (`R_*_COPY`) and |
| 2725 | | /// so becomes a part of the executable's address space despite being defined by a different |
| 2726 | | /// DSO, we need to know its alignment requirement so that we don't break other code. This |
| 2727 | | /// isn't actually stored on the symbol---instead we compute a maximum alignment from the |
| 2728 | | /// alignment of the section containing the symbol, and the symbol's offset within the |
| 2729 | | /// section. I know this sounds like a terrible hack, but it is *genuinely* how you're |
| 2730 | | /// supposed to do this. Copy relocations suck. |
| 2731 | | alignment: Alignment, |
| 2732 | | |
| 2733 | | const Key = struct { |
| 2734 | | name: []const u8, |
| 2735 | | version: ?[]const u8, |
| 2736 | | }; |
| 2831 | const Key = struct { |
| 2832 | name: []const u8, |
| 2833 | version: ?[]const u8, |
| 2834 | }; |
| 2737 | 2835 | |
| 2738 | 2836 | const Adapter = struct { |
| 2739 | 2837 | dso_globals: *const DsoGlobals, |
| ... | ... | @@ -3000,6 +3098,12 @@ const AddLocalSymbolOptions = struct { |
| 3000 | 3098 | fn addLocalSymbol(elf: *Elf, opts: AddLocalSymbolOptions) Error!Symbol.LocalIndex { |
| 3001 | 3099 | const gpa = elf.base.comp.gpa; |
| 3002 | 3100 | |
| 3101 | const trace = tracy.trace(@src()); |
| 3102 | defer trace.end(); |
| 3103 | trace.addText(opts.name); |
| 3104 | |
| 3105 | assert(opts.shndx != .UNDEF); |
| 3106 | |
| 3003 | 3107 | try elf.symtab.ensureUnusedCapacity(gpa, 1); |
| 3004 | 3108 | try elf.changed_symtab_index.ensureUnusedCapacity(gpa, 1); |
| 3005 | 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 | 3178 | } || Error)!Symbol.Global.Index { |
| 3075 | 3179 | _ = opts.lib_name; // TODO |
| 3076 | 3180 | |
| 3181 | const trace = tracy.trace(@src()); |
| 3182 | defer trace.end(); |
| 3183 | trace.addText(opts.name); |
| 3184 | |
| 3077 | 3185 | log.debug("addGlobalSymbol({q})", .{opts.name}); |
| 3078 | 3186 | |
| 3079 | 3187 | const gpa = elf.base.comp.gpa; |
| ... | ... | @@ -4259,6 +4367,9 @@ const Symbol = struct { |
| 4259 | 4367 | /// Returns `true` if any relocations were updated, in which case relocations targeting |
| 4260 | 4368 | /// `gsi` must be re-applied by the caller. |
| 4261 | 4369 | fn updateDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) Error!bool { |
| 4370 | const trace = tracy.trace(@src()); |
| 4371 | defer trace.end(); |
| 4372 | |
| 4262 | 4373 | if (elf.shndx.dynamic == .UNDEF) return false; |
| 4263 | 4374 | const sym_id: Symbol.Id = .global(gsi); |
| 4264 | 4375 | |
| ... | ... | @@ -4293,8 +4404,8 @@ const Symbol = struct { |
| 4293 | 4404 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; |
| 4294 | 4405 | while (ri != .none) { |
| 4295 | 4406 | const reloc = ri.get(elf); |
| 4296 | | assert(reloc.target == sym_id); |
| 4297 | | try reloc.updateDynamicReloc(elf); |
| 4407 | assert(reloc.target_symbol == sym_id); |
| 4408 | try reloc.updateDynamicReloc(elf, ri); |
| 4298 | 4409 | ri = reloc.next; |
| 4299 | 4410 | } |
| 4300 | 4411 | |
| ... | ... | @@ -4429,20 +4540,8 @@ const Symbol = struct { |
| 4429 | 4540 | } |
| 4430 | 4541 | |
| 4431 | 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 | 4543 | const sym_index = sym_id.index(elf); |
| 4436 | 4544 | |
| 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 | 4545 | switch (elf.symPtr(sym_index)) { |
| 4447 | 4546 | inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)), |
| 4448 | 4547 | } |
| ... | ... | @@ -4467,10 +4566,13 @@ const Symbol = struct { |
| 4467 | 4566 | } |
| 4468 | 4567 | |
| 4469 | 4568 | fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { |
| 4569 | const trace = tracy.trace(@src()); |
| 4570 | defer trace.end(); |
| 4571 | |
| 4470 | 4572 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; |
| 4471 | 4573 | while (ri != .none) { |
| 4472 | 4574 | const reloc = ri.get(elf); |
| 4473 | | assert(reloc.target == sym_id); |
| 4575 | assert(reloc.target_symbol == sym_id); |
| 4474 | 4576 | reloc.apply(elf); |
| 4475 | 4577 | ri = reloc.next; |
| 4476 | 4578 | } |
| ... | ... | @@ -4563,7 +4665,7 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum { |
| 4563 | 4665 | |
| 4564 | 4666 | const shndx: Section.Index = switch (resolved_sym.unwrap()) { |
| 4565 | 4667 | .local => shndx: { |
| 4566 | | assert(symtab_shndx != .UNDEF); |
| 4668 | assert(sym == Symbol.Id.null or symtab_shndx != .UNDEF); |
| 4567 | 4669 | assert(visibility == .DEFAULT); |
| 4568 | 4670 | break :shndx symtab_shndx; |
| 4569 | 4671 | }, |
| ... | ... | @@ -4683,7 +4785,6 @@ fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.Symbol |
| 4683 | 4785 | .shndx = shndx, |
| 4684 | 4786 | }), |
| 4685 | 4787 | .first_symbol_reloc = .none, |
| 4686 | | .first_got_reloc = .none, |
| 4687 | 4788 | }; |
| 4688 | 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 | 4972 | } |
| 4872 | 4973 | |
| 4873 | 4974 | const StringTable = struct { |
| 4874 | | map: std.HashMapUnmanaged(u32, void, StringTable.Context, std.hash_map.default_max_load_percentage), |
| 4875 | | |
| 4876 | | const Context = struct { |
| 4877 | | slice: []const u8, |
| 4878 | | |
| 4879 | | pub fn eql(_: Context, lhs_key: u32, rhs_key: u32) bool { |
| 4880 | | return lhs_key == rhs_key; |
| 4881 | | } |
| 4882 | | |
| 4883 | | pub fn hash(ctx: Context, key: u32) u64 { |
| 4884 | | return std.hash_map.hashString(std.mem.sliceTo(ctx.slice[key..], 0)); |
| 4885 | | } |
| 4886 | | }; |
| 4975 | map: std.array_hash_map.Custom(u32, void, void, true), |
| 4887 | 4976 | |
| 4888 | 4977 | const Adapter = struct { |
| 4889 | 4978 | slice: []const u8, |
| 4890 | 4979 | |
| 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 | 4982 | return std.mem.startsWith(u8, adapter.slice[rhs_key..], lhs_key) and |
| 4893 | 4983 | adapter.slice[rhs_key + lhs_key.len] == 0; |
| 4894 | 4984 | } |
| 4895 | 4985 | |
| 4896 | | pub fn hash(_: Adapter, key: []const u8) u64 { |
| 4986 | pub fn hash(_: Adapter, key: []const u8) u32 { |
| 4897 | 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 | }; |
| 4901 | 4991 | |
| ... | ... | @@ -4909,8 +4999,7 @@ const StringTable = struct { |
| 4909 | 4999 | fn ensureAdditionalCapacity(st: *StringTable, elf: *Elf, shndx: Section.Index, string_len: usize) Error!void { |
| 4910 | 5000 | const gpa = elf.base.comp.gpa; |
| 4911 | 5001 | const ni = shndx.node(elf); |
| 4912 | | const slice_const = ni.sliceConst(&elf.mf); |
| 4913 | | try st.map.ensureUnusedCapacityContext(gpa, 1, .{ .slice = slice_const }); |
| 5002 | try st.map.ensureUnusedCapacity(gpa, 1); |
| 4914 | 5003 | const size: u64 = switch (elf.shdrPtr(shndx)) { |
| 4915 | 5004 | inline else => |shdr| elf.targetLoad(&shdr.size), |
| 4916 | 5005 | }; |
| ... | ... | @@ -5035,6 +5124,11 @@ fn create( |
| 5035 | 5124 | }, |
| 5036 | 5125 | .options = options, |
| 5037 | 5126 | .mf = try .init(file, comp.gpa, io), |
| 5127 | .ehdr_fields = .{ |
| 5128 | .class = class, |
| 5129 | .type = @"type", |
| 5130 | .machine = machine, |
| 5131 | }, |
| 5038 | 5132 | .ni = .{ |
| 5039 | 5133 | .elf = undefined, |
| 5040 | 5134 | .ehdr = undefined, |
| ... | ... | @@ -6787,7 +6881,6 @@ pub fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { |
| 6787 | 6881 | skip_symbol_relocs: MappedFile.Node.Index.Optional = .none, |
| 6788 | 6882 | first_node_reloc: ?*NodeReloc.Index = null, |
| 6789 | 6883 | skip_node_relocs: MappedFile.Node.Index.Optional = .none, |
| 6790 | | first_got_reloc: ?*GotReloc.Index = null, |
| 6791 | 6884 | } = switch (elf.getNode(ni)) { |
| 6792 | 6885 | .deleted, |
| 6793 | 6886 | .archive, |
| ... | ... | @@ -6814,18 +6907,15 @@ pub fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void { |
| 6814 | 6907 | => unreachable, // cannot contain relocs (.plt, .dynamic, and .eh_frame_hdr unsupported) |
| 6815 | 6908 | .input_section => |isi| .{ |
| 6816 | 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 | 6911 | .nav => |nmi| .{ |
| 6820 | 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 | 6914 | .uav => |umi| .{ |
| 6824 | 6915 | .first_symbol_reloc = &elf.uavs.values()[@backingInt(umi)].first_symbol_reloc, |
| 6825 | 6916 | }, |
| 6826 | 6917 | inline .lazy_code, .lazy_const_data => |lmi| .{ |
| 6827 | 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 | 6920 | .unit_debug_info_header => |ui| .{ |
| 6831 | 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 | 6983 | } |
| 6894 | 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 | } |
| 6910 | 6987 | |
| 6911 | 6988 | /// Given that `node` has moved, updates all relocations in `node` as needed. In relocatables, this |
| ... | ... | @@ -6918,7 +6995,6 @@ fn flushMovedNodeRelocs( |
| 6918 | 6995 | skip_symbol_relocs: MappedFile.Node.Index.Optional = .none, |
| 6919 | 6996 | first_node_reloc: NodeReloc.Index = .none, |
| 6920 | 6997 | skip_node_relocs: MappedFile.Node.Index.Optional = .none, |
| 6921 | | first_got_reloc: GotReloc.Index = .none, |
| 6922 | 6998 | }, |
| 6923 | 6999 | ) void { |
| 6924 | 7000 | if (opts.first_symbol_reloc != .none) { |
| ... | ... | @@ -6942,20 +7018,12 @@ fn flushMovedNodeRelocs( |
| 6942 | 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 | } |
| 6956 | 7022 | |
| 6957 | 7023 | fn 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 | } |
| 6960 | 7028 | |
| 6961 | 7029 | /// 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 | 7055 | } |
| 6988 | 7056 | }; |
| 6989 | 7057 | fn ehdrMachine(elf: *const Elf) EhdrMachine { |
| 6990 | | const ehdr_slice = elf.ni.ehdr.sliceConst(&elf.mf); |
| 6991 | | switch (elf.identClass()) { |
| 6992 | | .NONE, _ => unreachable, |
| 6993 | | inline else => |class| { |
| 6994 | | const ehdr: *const class.ElfN().Ehdr = @ptrCast(@alignCast(ehdr_slice)); |
| 6995 | | return @bitCast(elf.targetLoad(&ehdr.machine)); |
| 6996 | | }, |
| 6997 | | } |
| 7058 | // We used to get this value from `elf.ni.ehdr`, but it turns out this function is called often |
| 7059 | // enough to see measurable performance gains from memoizing its result on `Elf`. |
| 7060 | return elf.ehdr_fields.machine; |
| 6998 | 7061 | } |
| 6999 | 7062 | fn ehdrType(elf: *const Elf) EhdrType { |
| 7000 | | const ehdr_slice = elf.ni.ehdr.sliceConst(&elf.mf); |
| 7001 | | switch (elf.identClass()) { |
| 7002 | | .NONE, _ => unreachable, |
| 7003 | | inline else => |class| { |
| 7004 | | const ehdr: *const class.ElfN().Ehdr = @ptrCast(@alignCast(ehdr_slice)); |
| 7005 | | return @bitCast(elf.targetLoad(&ehdr.type)); |
| 7006 | | }, |
| 7007 | | } |
| 7063 | // We used to get this value from `elf.ni.ehdr`, but it turns out this function is called often |
| 7064 | // enough to see measurable performance gains from memoizing its result on `Elf`. |
| 7065 | return elf.ehdr_fields.type; |
| 7008 | 7066 | } |
| 7009 | 7067 | |
| 7010 | 7068 | fn baseLoadableSegmentVAddr(elf: *const Elf) u64 { |
| ... | ... | @@ -7227,11 +7285,17 @@ const SymPtr = union(std.elf.CLASS) { |
| 7227 | 7285 | @"32": *std.elf.Elf32.Sym, |
| 7228 | 7286 | @"64": *std.elf.Elf64.Sym, |
| 7229 | 7287 | }; |
| 7230 | | fn 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! |
| 7291 | inline fn symPtr(elf: *Elf, index: Symbol.Index) SymPtr { |
| 7231 | 7292 | const raw_slice = Section.Index.symtab.node(elf).slice(&elf.mf); |
| 7232 | | switch (elf.shdrPtr(.symtab)) { |
| 7233 | | inline else => |shdr, class| { |
| 7234 | | const size = elf.targetLoad(&shdr.size); |
| 7293 | // Avoid calling `shdrPtr` here: this function is extremely hot, so having to locate the shdr |
| 7294 | // actually noticably affects performance! |
| 7295 | switch (elf.identClass()) { |
| 7296 | .NONE, _ => unreachable, |
| 7297 | inline else => |class| { |
| 7298 | const size = elf.symtab.items.len * @sizeOf(class.ElfN().Sym); |
| 7235 | 7299 | const slice: []class.ElfN().Sym = @ptrCast(@alignCast(raw_slice[0..@intCast(size)])); |
| 7236 | 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 | 7560 | .shndx = shndx, |
| 7497 | 7561 | }), |
| 7498 | 7562 | .first_symbol_reloc = .none, |
| 7499 | | .first_got_reloc = .none, |
| 7500 | 7563 | }; |
| 7501 | 7564 | } |
| 7502 | 7565 | return nmi; |
| ... | ... | @@ -7651,6 +7714,10 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void |
| 7651 | 7714 | } |
| 7652 | 7715 | } |
| 7653 | 7716 | fn 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 | 7721 | const comp = elf.base.comp; |
| 7655 | 7722 | const gpa = comp.gpa; |
| 7656 | 7723 | const diags = &comp.link_diags; |
| ... | ... | @@ -7730,6 +7797,10 @@ fn loadObject( |
| 7730 | 7797 | fr: *Io.File.Reader, |
| 7731 | 7798 | fl: MappedFile.Node.FileLocation, |
| 7732 | 7799 | ) LoadParseInputError!void { |
| 7800 | const trace = tracy.trace(@src()); |
| 7801 | defer trace.end(); |
| 7802 | trace.addTextFmt("{f}{f}", .{ path.fmtEscapeString(), fmtMemberString(member) }); |
| 7803 | |
| 7733 | 7804 | const comp = elf.base.comp; |
| 7734 | 7805 | const gpa = comp.gpa; |
| 7735 | 7806 | const diags = &comp.link_diags; |
| ... | ... | @@ -7881,440 +7952,492 @@ fn loadObject( |
| 7881 | 7952 | defer gpa.free(shstrtab); |
| 7882 | 7953 | try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1); |
| 7883 | 7954 | try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1); |
| 7884 | | for (sections[1..]) |*section| { |
| 7885 | | if (section.shdr.name >= shstrtab.len) continue; |
| 7886 | | const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0); |
| 7887 | | if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue; |
| 7888 | | const sect_type = section.shdr.type; |
| 7889 | | const opts: struct { |
| 7890 | | shndx: Section.Index, |
| 7891 | | node_fixed: bool, |
| 7892 | | } = switch (sect_type) { |
| 7893 | | else => continue, |
| 7894 | | .PROGBITS, .NOBITS, .X86_64_UNWIND => opts: { |
| 7895 | | const shndx = elf.mapInputSection(.{ |
| 7896 | | .name = name, |
| 7897 | | .flags = section.shdr.flags.shf, |
| 7898 | | .entsize = section.shdr.entsize, |
| 7899 | | .type = section.shdr.type, |
| 7900 | | }) catch |err| switch (err) { |
| 7901 | | else => |e| return e, |
| 7902 | | error.StripSection => continue, |
| 7903 | | error.TlsSectionUnavailable => { |
| 7904 | | if (section.shdr.size == 0) continue; |
| 7905 | | |
| 7906 | | return diags.failParse( |
| 7907 | | path, |
| 7908 | | "thread-local storage section '{s}' is incompatible with '-fsingle-threaded'", |
| 7909 | | .{name}, |
| 7910 | | ); |
| 7911 | | }, |
| 7912 | | error.UnsupportedSectionFlags => if (!section.shdr.flags.shf.ALLOC) { |
| 7913 | | // It probably doesn't matter, just skip this section. |
| 7914 | | continue; |
| 7915 | | } else return diags.failParse( |
| 7916 | | path, |
| 7917 | | "unsupported flags for section '{s}'", |
| 7918 | | .{name}, |
| 7919 | | ), |
| 7920 | | error.SectionTypeConflict => if (!section.shdr.flags.shf.ALLOC) { |
| 7921 | | // It probably doesn't matter, just skip this section. |
| 7922 | | continue; |
| 7923 | | } else return diags.failParse( |
| 7924 | | path, |
| 7925 | | "type of section '{s}' conflicts with other inputs", |
| 7926 | | .{name}, |
| 7927 | | ), |
| 7928 | | error.SectionFlagsConflict => if (!section.shdr.flags.shf.ALLOC) { |
| 7929 | | // It probably doesn't matter, just skip this section. |
| 7955 | |
| 7956 | { |
| 7957 | const sections_trace = tracy.traceNamed(@src(), "scan input sections"); |
| 7958 | defer sections_trace.end(); |
| 7959 | for (sections[1..]) |*section| { |
| 7960 | const section_trace = tracy.traceNamed(@src(), "input section"); |
| 7961 | defer section_trace.end(); |
| 7962 | if (section.shdr.name >= shstrtab.len) continue; |
| 7963 | const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0); |
| 7964 | section_trace.addText(name); |
| 7965 | if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue; |
| 7966 | const sect_type = section.shdr.type; |
| 7967 | const opts: struct { |
| 7968 | shndx: Section.Index, |
| 7969 | node_fixed: bool, |
| 7970 | } = switch (sect_type) { |
| 7971 | else => continue, |
| 7972 | .PROGBITS, .NOBITS, .X86_64_UNWIND => opts: { |
| 7973 | const shndx = elf.mapInputSection(.{ |
| 7974 | .name = name, |
| 7975 | .flags = section.shdr.flags.shf, |
| 7976 | .entsize = section.shdr.entsize, |
| 7977 | .type = section.shdr.type, |
| 7978 | }) catch |err| switch (err) { |
| 7979 | else => |e| return e, |
| 7980 | error.StripSection => continue, |
| 7981 | error.TlsSectionUnavailable => { |
| 7982 | if (section.shdr.size == 0) continue; |
| 7983 | |
| 7984 | return diags.failParse( |
| 7985 | path, |
| 7986 | "thread-local storage section '{s}' is incompatible with '-fsingle-threaded'", |
| 7987 | .{name}, |
| 7988 | ); |
| 7989 | }, |
| 7990 | error.UnsupportedSectionFlags => if (!section.shdr.flags.shf.ALLOC) { |
| 7991 | // It probably doesn't matter, just skip this section. |
| 7992 | continue; |
| 7993 | } else return diags.failParse( |
| 7994 | path, |
| 7995 | "unsupported flags for section '{s}'", |
| 7996 | .{name}, |
| 7997 | ), |
| 7998 | error.SectionTypeConflict => if (!section.shdr.flags.shf.ALLOC) { |
| 7999 | // It probably doesn't matter, just skip this section. |
| 8000 | continue; |
| 8001 | } else return diags.failParse( |
| 8002 | path, |
| 8003 | "type of section '{s}' conflicts with other inputs", |
| 8004 | .{name}, |
| 8005 | ), |
| 8006 | error.SectionFlagsConflict => if (!section.shdr.flags.shf.ALLOC) { |
| 8007 | // It probably doesn't matter, just skip this section. |
| 8008 | continue; |
| 8009 | } else return diags.failParse( |
| 8010 | path, |
| 8011 | "flags of section '{s}' conflict with other inputs", |
| 8012 | .{name}, |
| 8013 | ), |
| 8014 | error.UnsupportedNobitsSection => return diags.failParse( |
| 8015 | path, |
| 8016 | "unsupported section type SHT_NOBITS for non-bss section '{s}'", |
| 8017 | .{name}, |
| 8018 | ), |
| 8019 | }; |
| 8020 | if (section.shdr.flags.shf.COMPRESSED) { |
| 8021 | // SHF_COMPRESSED is only allowed on non-alloc sections. |
| 8022 | if (section.shdr.flags.shf.ALLOC) return diags.failParse( |
| 8023 | path, |
| 8024 | "section '{s}' has conflicting flags SHF_ALLOC and SHF_COMPRESSED", |
| 8025 | .{name}, |
| 8026 | ); |
| 8027 | // TODO: handle compressed input sections. We'll need to set a flag to |
| 8028 | // indicate that `flushInputSection` needs to decompress the section. |
| 8029 | // But because this section isn't SHF_ALLOC, it's probably okay to just |
| 8030 | // skip it for now. |
| 7930 | 8031 | continue; |
| 7931 | | } else return diags.failParse( |
| 7932 | | path, |
| 7933 | | "flags of section '{s}' conflict with other inputs", |
| 7934 | | .{name}, |
| 7935 | | ), |
| 7936 | | error.UnsupportedNobitsSection => return diags.failParse( |
| 7937 | | path, |
| 7938 | | "unsupported section type SHT_NOBITS for non-bss section '{s}'", |
| 7939 | | .{name}, |
| 7940 | | ), |
| 8032 | } |
| 8033 | break :opts .{ |
| 8034 | .shndx = shndx, |
| 8035 | // For well-known sections, we know that it's fine to have e.g. random |
| 8036 | // padding, so there's no need to make the sections fixed. For custom |
| 8037 | // sections, however, we do want fixed nodes to avoid padding. |
| 8038 | .node_fixed = shndx != .text and |
| 8039 | shndx != .rodata and |
| 8040 | shndx != .data and |
| 8041 | shndx != .data_rel_ro and |
| 8042 | shndx != elf.shndx.tdata, |
| 8043 | }; |
| 8044 | }, |
| 8045 | inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{ |
| 8046 | .shndx = shndx: { |
| 8047 | // TODO: the input section name may include a "priority" value between 1 |
| 8048 | // and 65535 which should affect the order we assemble input sections in |
| 8049 | const init_fini_section_name: []const u8 = switch (@"type") { |
| 8050 | .INIT_ARRAY => "init_array", |
| 8051 | .FINI_ARRAY => "fini_array", |
| 8052 | .PREINIT_ARRAY => "preinit_array", |
| 8053 | else => comptime unreachable, |
| 8054 | }; |
| 8055 | const shndx: *Section.Index = &@field(elf.shndx, init_fini_section_name); |
| 8056 | const need_addralign: u8 = switch (class) { |
| 8057 | .NONE, _ => unreachable, |
| 8058 | .@"32" => 4, |
| 8059 | .@"64" => 8, |
| 8060 | }; |
| 8061 | if (section.shdr.addralign != need_addralign) { |
| 8062 | return diags.failParse(path, "bad addralign on {t} shdr", .{@"type"}); |
| 8063 | } |
| 8064 | if (shndx.* == .UNDEF) { |
| 8065 | try elf.createInitFiniArraySection(shndx, init_fini_section_name, @"type"); |
| 8066 | } |
| 8067 | switch (elf.shdrPtr(shndx.*)) { |
| 8068 | inline else => |shdr| { |
| 8069 | const old_size = elf.targetLoad(&shdr.size); |
| 8070 | const new_size = old_size + section.shdr.size; |
| 8071 | elf.targetStore(&shdr.size, @intCast(new_size)); |
| 8072 | elf.updateInitFiniArraySectionSize(shndx.*, init_fini_section_name); |
| 8073 | }, |
| 8074 | } |
| 8075 | break :shndx shndx.*; |
| 8076 | }, |
| 8077 | // This node must be fixed to prevent padding from being added between different |
| 8078 | // INIT_ARRAY/FINI_ARRAY/PREINIT_ARRAY input sections. |
| 8079 | .node_fixed = true, |
| 8080 | }, |
| 8081 | }; |
| 8082 | const need_align: Alignment = .fromByteUnits( |
| 8083 | std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))), |
| 8084 | ); |
| 8085 | |
| 8086 | try opts.shndx.ensureAligned(elf, need_align); |
| 8087 | |
| 8088 | if (sect_type == .NOBITS) { |
| 8089 | const offset: u64 = switch (elf.shdrPtr(opts.shndx)) { |
| 8090 | inline else => |shdr| offset: { |
| 8091 | // Bump our BSS output section size |
| 8092 | const old_size: u64 = elf.targetLoad(&shdr.size); |
| 8093 | const offset = need_align.forward(old_size); |
| 8094 | elf.targetStore(&shdr.size, @intCast(offset + section.shdr.size)); |
| 8095 | break :offset offset; |
| 8096 | }, |
| 7941 | 8097 | }; |
| 7942 | | if (section.shdr.flags.shf.COMPRESSED) { |
| 7943 | | // SHF_COMPRESSED is only allowed on non-alloc sections. |
| 7944 | | if (section.shdr.flags.shf.ALLOC) return diags.failParse( |
| 7945 | | path, |
| 7946 | | "section '{s}' has conflicting flags SHF_ALLOC and SHF_COMPRESSED", |
| 7947 | | .{name}, |
| 7948 | | ); |
| 7949 | | // TODO: handle compressed input sections. We'll need to set a flag to |
| 7950 | | // indicate that `flushInputSection` needs to decompress the section. |
| 7951 | | // But because this section isn't SHF_ALLOC, it's probably okay to just |
| 7952 | | // skip it for now. |
| 7953 | | continue; |
| 8098 | |
| 8099 | section.placement = .{ .nobits = .{ |
| 8100 | .shndx = opts.shndx, |
| 8101 | .offset = offset, |
| 8102 | } }; |
| 8103 | if (opts.shndx == elf.shndx.bss) { |
| 8104 | input_has_bss = true; |
| 8105 | } else if (opts.shndx == elf.shndx.tbss) { |
| 8106 | input_has_tbss = true; |
| 8107 | } else { |
| 8108 | assert(elf.ehdrType() == .REL); |
| 7954 | 8109 | } |
| 7955 | | break :opts .{ |
| 7956 | | .shndx = shndx, |
| 7957 | | // For well-known sections, we know that it's fine to have e.g. random |
| 7958 | | // padding, so there's no need to make the sections fixed. For custom |
| 7959 | | // sections, however, we do want fixed nodes to avoid padding. |
| 7960 | | .node_fixed = shndx != .text and |
| 7961 | | shndx != .rodata and |
| 7962 | | shndx != .data and |
| 7963 | | shndx != .data_rel_ro and |
| 7964 | | shndx != elf.shndx.tdata, |
| 7965 | | }; |
| 7966 | | }, |
| 7967 | | inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{ |
| 7968 | | .shndx = shndx: { |
| 7969 | | // TODO: the input section name may include a "priority" value between 1 |
| 7970 | | // and 65535 which should affect the order we assemble input sections in |
| 7971 | | const init_fini_section_name: []const u8 = switch (@"type") { |
| 7972 | | .INIT_ARRAY => "init_array", |
| 7973 | | .FINI_ARRAY => "fini_array", |
| 7974 | | .PREINIT_ARRAY => "preinit_array", |
| 7975 | | else => comptime unreachable, |
| 7976 | | }; |
| 7977 | | const shndx: *Section.Index = &@field(elf.shndx, init_fini_section_name); |
| 7978 | | const need_addralign: u8 = switch (class) { |
| 7979 | | .NONE, _ => unreachable, |
| 7980 | | .@"32" => 4, |
| 7981 | | .@"64" => 8, |
| 8110 | |
| 8111 | continue; |
| 8112 | } |
| 8113 | |
| 8114 | const add_node_opts: MappedFile.Node.AddOptions = .{ |
| 8115 | .size = need_align.forward(section.shdr.size), |
| 8116 | .alignment = need_align, |
| 8117 | .moved = true, // see assert at end of `flushInputSection` |
| 8118 | }; |
| 8119 | const ni = elf.addNodeAssumeCapacity( |
| 8120 | if (opts.node_fixed) ni: { |
| 8121 | const shndx_ni = opts.shndx.node(elf); |
| 8122 | const after_oni: MappedFile.Node.Index.Optional = after: { |
| 8123 | var node_it = shndx_ni.last(&elf.mf); |
| 8124 | while (node_it.unwrap()) |cur_ni| { |
| 8125 | switch (cur_ni.position(&elf.mf)) { |
| 8126 | .header => break :after .wrap(cur_ni), // last header |
| 8127 | .footer, .floating => node_it = cur_ni.prev(&elf.mf), |
| 8128 | } |
| 8129 | } else { |
| 8130 | break :after .none; |
| 8131 | } |
| 7982 | 8132 | }; |
| 7983 | | if (section.shdr.addralign != need_addralign) { |
| 7984 | | return diags.failParse(path, "bad addralign on {t} shdr", .{@"type"}); |
| 7985 | | } |
| 7986 | | if (shndx.* == .UNDEF) { |
| 7987 | | try elf.createInitFiniArraySection(shndx, init_fini_section_name, @"type"); |
| 7988 | | } |
| 7989 | | switch (elf.shdrPtr(shndx.*)) { |
| 7990 | | inline else => |shdr| { |
| 7991 | | const old_size = elf.targetLoad(&shdr.size); |
| 7992 | | const new_size = old_size + section.shdr.size; |
| 7993 | | elf.targetStore(&shdr.size, @intCast(new_size)); |
| 7994 | | elf.updateInitFiniArraySectionSize(shndx.*, init_fini_section_name); |
| 7995 | | }, |
| 7996 | | } |
| 7997 | | break :shndx shndx.*; |
| 8133 | break :ni try shndx_ni.addHeaderChildAfter(gpa, &elf.mf, after_oni, add_node_opts); |
| 8134 | } else try opts.shndx.node(elf).addFloatingChild(gpa, &elf.mf, add_node_opts), |
| 8135 | .{ .input_section = @fromBackingInt(@intCast(elf.input_sections.items.len)) }, |
| 8136 | ); |
| 8137 | section.placement = .{ .progbits = @fromBackingInt(@intCast(elf.input_sections.items.len)) }; |
| 8138 | elf.input_sections.addOneAssumeCapacity().* = .{ |
| 8139 | .input = input_index, |
| 8140 | .file_location = .{ |
| 8141 | .offset = fl.offset + section.shdr.offset, |
| 8142 | .size = if (section.shdr.type == .NOBITS) 0 else section.shdr.size, |
| 7998 | 8143 | }, |
| 7999 | | // This node must be fixed to prevent padding from being added between different |
| 8000 | | // INIT_ARRAY/FINI_ARRAY/PREINIT_ARRAY input sections. |
| 8001 | | .node_fixed = true, |
| 8002 | | }, |
| 8144 | // The section vaddr is initially 0, because the symbol addresses are |
| 8145 | // zero-based. This will eventually be updated by `flushMoved`. |
| 8146 | .vaddr = 0, |
| 8147 | .node = ni, |
| 8148 | .first_symbol_reloc = .none, |
| 8149 | }; |
| 8150 | elf.input_prog_node.increaseEstimatedTotalItems(1); |
| 8151 | } |
| 8152 | } |
| 8153 | |
| 8154 | // This array will exclude the "null" symbol (symtab index 0). |
| 8155 | var symmap: std.ArrayList(Symbol.Id) = .empty; |
| 8156 | defer symmap.deinit(gpa); |
| 8157 | |
| 8158 | const maybe_symtab_shndx: ?usize = for (sections[1..], 1..) |*symtab, symtab_shndx| { |
| 8159 | if (symtab.shdr.type == .SYMTAB) break symtab_shndx; |
| 8160 | } else null; |
| 8161 | |
| 8162 | if (maybe_symtab_shndx) |symtab_shndx| { |
| 8163 | const symtab = &sections[symtab_shndx]; |
| 8164 | |
| 8165 | const symbols_trace = tracy.traceNamed(@src(), "scan input symbols"); |
| 8166 | defer symbols_trace.end(); |
| 8167 | |
| 8168 | for (sections[symtab_shndx + 1 ..]) |*other_section| { |
| 8169 | if (other_section.shdr.type == .SYMTAB) { |
| 8170 | return diags.failParse(path, "multiple SHT_SYMTAB sections", .{}); |
| 8171 | } |
| 8172 | } |
| 8173 | |
| 8174 | if (symtab.shdr.entsize < @sizeOf(ElfN.Sym)) |
| 8175 | return diags.failParse(path, "unsupported symtab entsize", .{}); |
| 8176 | |
| 8177 | const strtab = strtab: { |
| 8178 | if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum) |
| 8179 | return diags.failParse(path, "missing symbol names", .{}); |
| 8180 | const shdr = &sections[symtab.shdr.link].shdr; |
| 8181 | if (shdr.type != .STRTAB) |
| 8182 | return diags.failParse(path, "invalid strtab type", .{}); |
| 8183 | const strtab = try gpa.alloc(u8, @intCast(shdr.size)); |
| 8184 | errdefer gpa.free(strtab); |
| 8185 | try fr.seekTo(fl.offset + shdr.offset); |
| 8186 | try r.readSliceAll(strtab); |
| 8187 | break :strtab strtab; |
| 8003 | 8188 | }; |
| 8004 | | const need_align: Alignment = .fromByteUnits( |
| 8005 | | std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))), |
| 8189 | defer gpa.free(strtab); |
| 8190 | |
| 8191 | const symnum = std.math.divExact( |
| 8192 | u32, |
| 8193 | @intCast(symtab.shdr.size), |
| 8194 | @intCast(symtab.shdr.entsize), |
| 8195 | ) catch return diags.failParse( |
| 8196 | path, |
| 8197 | "symtab section size (0x{x}) is not a multiple of entsize (0x{x})", |
| 8198 | .{ symtab.shdr.size, symtab.shdr.entsize }, |
| 8006 | 8199 | ); |
| 8007 | 8200 | |
| 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 | }; |
| 8009 | 8220 | |
| 8010 | | if (sect_type == .NOBITS) { |
| 8011 | | const offset: u64 = switch (elf.shdrPtr(opts.shndx)) { |
| 8012 | | inline else => |shdr| offset: { |
| 8013 | | // Bump our BSS output section size |
| 8014 | | const old_size: u64 = elf.targetLoad(&shdr.size); |
| 8015 | | const offset = need_align.forward(old_size); |
| 8016 | | elf.targetStore(&shdr.size, @intCast(offset + section.shdr.size)); |
| 8017 | | break :offset offset; |
| 8221 | if (input_sym.shndx == std.elf.SHN_UNDEF) switch (input_sym.info.bind) { |
| 8222 | else => |bind| return diags.failParse( |
| 8223 | path, |
| 8224 | "symbol '{s}' has unsupported binding (0x{x})", |
| 8225 | .{ name, bind }, |
| 8226 | ), |
| 8227 | .LOCAL => continue, |
| 8228 | .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| { |
| 8229 | si.* = .global(elf.addGlobalSymbol(.{ |
| 8230 | .node = .none, |
| 8231 | .name = name, |
| 8232 | .value = input_sym.value, |
| 8233 | .size = input_sym.size, |
| 8234 | .type = sym_type, |
| 8235 | .bind = switch (bind) { |
| 8236 | .WEAK, .GNU_UNIQUE => .weak, |
| 8237 | .GLOBAL => .strong, |
| 8238 | else => unreachable, |
| 8239 | }, |
| 8240 | .visibility = input_sym.other.visibility, |
| 8241 | .shndx = .UNDEF, |
| 8242 | }) catch |err| switch (err) { |
| 8243 | error.MultipleDefinitions => unreachable, // shndx is .UNDEF |
| 8244 | error.MultipleDefaultVersions => unreachable, // shndx is .UNDEF |
| 8245 | error.UndefinedDefaultVersion => return diags.failParse( |
| 8246 | path, |
| 8247 | "symbol '{s}' specifies default version without defining it", |
| 8248 | .{name}, |
| 8249 | ), |
| 8250 | else => |e| return e, |
| 8251 | }); |
| 8252 | continue; |
| 8018 | 8253 | }, |
| 8019 | 8254 | }; |
| 8020 | 8255 | |
| 8021 | | section.placement = .{ .nobits = .{ |
| 8022 | | .shndx = opts.shndx, |
| 8023 | | .offset = offset, |
| 8024 | | } }; |
| 8025 | | if (opts.shndx == elf.shndx.bss) { |
| 8026 | | input_has_bss = true; |
| 8027 | | } else if (opts.shndx == elf.shndx.tbss) { |
| 8028 | | input_has_tbss = true; |
| 8029 | | } else { |
| 8030 | | assert(elf.ehdrType() == .REL); |
| 8031 | | } |
| 8256 | const symbol_oni: MappedFile.Node.Index.Optional, const output_shndx: Section.Index, const symbol_value: u64 = switch (sections[input_sym.shndx].placement) { |
| 8257 | .none => continue, |
| 8258 | .progbits => |isi| .{ |
| 8259 | .wrap(isi.node(elf)), |
| 8260 | elf.getNodeShndx(isi.node(elf)), |
| 8261 | input_sym.value, |
| 8262 | }, |
| 8263 | .nobits => |nobits| sym: { |
| 8264 | const section_base: u64 = switch (elf.ehdrType()) { |
| 8265 | .REL => 0, |
| 8266 | .EXEC, .DYN => base: { |
| 8267 | if (elf.ni.tls != .none and |
| 8268 | nobits.shndx == elf.shndx.tbss) |
| 8269 | { |
| 8270 | break :base elf.tbssOffset(); |
| 8271 | } |
| 8272 | assert(nobits.shndx == elf.shndx.bss); |
| 8273 | break :base nobits.shndx.vaddr(elf); |
| 8274 | }, |
| 8275 | }; |
| 8276 | break :sym .{ |
| 8277 | .none, |
| 8278 | nobits.shndx, |
| 8279 | section_base + nobits.offset + input_sym.value, |
| 8280 | }; |
| 8281 | }, |
| 8282 | }; |
| 8032 | 8283 | |
| 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 | | }; |
| 8116 | 8333 | |
| 8117 | | if (input_sym.shndx == std.elf.SHN_UNDEF) switch (input_sym.info.bind) { |
| 8118 | | else => |bind| return diags.failParse( |
| 8334 | { |
| 8335 | const all_relocs_trace = tracy.traceNamed(@src(), "scan input relocations"); |
| 8336 | defer all_relocs_trace.end(); |
| 8337 | for (sections[1..]) |*rel_sec| switch (rel_sec.shdr.type) { |
| 8338 | else => {}, |
| 8339 | inline .REL, .RELA => |sht| { |
| 8340 | const relocs_trace = tracy.traceNamed(@src(), "input relocation section"); |
| 8341 | defer relocs_trace.end(); |
| 8342 | |
| 8343 | const link_is_symtab: bool = if (maybe_symtab_shndx) |symtab_shndx| s: { |
| 8344 | break :s rel_sec.shdr.link == symtab_shndx; |
| 8345 | } else false; |
| 8346 | if (!link_is_symtab) { |
| 8347 | return diags.failParse(path, "relocation section does not link to SHT_SYMTAB section", .{}); |
| 8348 | } |
| 8349 | if (rel_sec.shdr.info == std.elf.SHN_UNDEF or rel_sec.shdr.info >= ehdr.shnum) { |
| 8350 | return diags.failParse( |
| 8119 | 8351 | path, |
| 8120 | | "symbol '{s}' has unsupported binding (0x{x})", |
| 8121 | | .{ name, bind }, |
| 8122 | | ), |
| 8123 | | .LOCAL => continue, |
| 8124 | | .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| { |
| 8125 | | si.* = .global(elf.addGlobalSymbol(.{ |
| 8126 | | .node = .none, |
| 8127 | | .name = name, |
| 8128 | | .value = input_sym.value, |
| 8129 | | .size = input_sym.size, |
| 8130 | | .type = sym_type, |
| 8131 | | .bind = switch (bind) { |
| 8132 | | .WEAK, .GNU_UNIQUE => .weak, |
| 8133 | | .GLOBAL => .strong, |
| 8134 | | else => unreachable, |
| 8135 | | }, |
| 8136 | | .visibility = input_sym.other.visibility, |
| 8137 | | .shndx = .UNDEF, |
| 8138 | | }) catch |err| switch (err) { |
| 8139 | | error.MultipleDefinitions => unreachable, // shndx is .UNDEF |
| 8140 | | error.MultipleDefaultVersions => unreachable, // shndx is .UNDEF |
| 8141 | | error.UndefinedDefaultVersion => return diags.failParse( |
| 8142 | | path, |
| 8143 | | "symbol '{s}' specifies default version without defining it", |
| 8144 | | .{name}, |
| 8145 | | ), |
| 8146 | | else => |e| return e, |
| 8147 | | }); |
| 8148 | | continue; |
| 8149 | | }, |
| 8352 | "relocation section 'sh_info' refers to invalid section index '{d}'", |
| 8353 | .{rel_sec.shdr.info}, |
| 8354 | ); |
| 8355 | } |
| 8356 | const Rel = switch (sht) { |
| 8357 | else => comptime unreachable, |
| 8358 | .REL => ElfN.Rel, |
| 8359 | .RELA => ElfN.Rela, |
| 8150 | 8360 | }; |
| 8361 | if (rel_sec.shdr.entsize < @sizeOf(Rel)) |
| 8362 | return diags.failParse(path, "unsupported rel entsize", .{}); |
| 8151 | 8363 | |
| 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 | 8367 | .none => continue, |
| 8154 | | .progbits => |isi| .{ |
| 8155 | | .wrap(isi.node(elf)), |
| 8156 | | elf.getNodeShndx(isi.node(elf)), |
| 8157 | | input_sym.value, |
| 8158 | | }, |
| 8159 | | .nobits => |nobits| sym: { |
| 8160 | | const section_base: u64 = switch (elf.ehdrType()) { |
| 8161 | | .REL => 0, |
| 8162 | | .EXEC, .DYN => base: { |
| 8163 | | if (elf.ni.tls != .none and |
| 8164 | | nobits.shndx == elf.shndx.tbss) |
| 8165 | | { |
| 8166 | | break :base elf.tbssOffset(); |
| 8167 | | } |
| 8168 | | assert(nobits.shndx == elf.shndx.bss); |
| 8169 | | break :base nobits.shndx.vaddr(elf); |
| 8170 | | }, |
| 8171 | | }; |
| 8172 | | break :sym .{ |
| 8173 | | .none, |
| 8174 | | nobits.shndx, |
| 8175 | | section_base + nobits.offset + input_sym.value, |
| 8176 | | }; |
| 8177 | | }, |
| 8178 | | }; |
| 8179 | | |
| 8180 | | switch (input_sym.info.bind) { |
| 8181 | | else => |bind| return diags.failParse( |
| 8368 | .progbits => |isi| isi.node(elf), |
| 8369 | .nobits => return diags.failParse( |
| 8182 | 8370 | path, |
| 8183 | | "symbol '{s}' has unsupported binding (0x{x})", |
| 8184 | | .{ name, bind }, |
| 8371 | "cannot apply relocation to SHT_NOBITS section '{s}'", |
| 8372 | .{std.mem.sliceTo(shstrtab[loc_sec.shdr.name..], 0)}, |
| 8185 | 8373 | ), |
| 8186 | | .LOCAL => { |
| 8187 | | const lsi = try elf.addLocalSymbol(.{ |
| 8188 | | .node = symbol_oni, |
| 8189 | | .name = name, |
| 8190 | | .value = symbol_value, |
| 8191 | | .size = input_sym.size, |
| 8192 | | .type = sym_type, |
| 8193 | | .shndx = output_shndx, |
| 8194 | | }); |
| 8195 | | si.* = .local(lsi); |
| 8196 | | }, |
| 8197 | | .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| { |
| 8198 | | si.* = .global(elf.addGlobalSymbol(.{ |
| 8199 | | .node = symbol_oni, |
| 8200 | | .name = name, |
| 8201 | | .value = symbol_value, |
| 8202 | | .size = input_sym.size, |
| 8203 | | .type = sym_type, |
| 8204 | | .bind = switch (bind) { |
| 8205 | | .WEAK, .GNU_UNIQUE => .weak, |
| 8206 | | .GLOBAL => .strong, |
| 8207 | | else => unreachable, |
| 8208 | | }, |
| 8209 | | .visibility = input_sym.other.visibility, |
| 8210 | | .shndx = output_shndx, |
| 8211 | | }) catch |err| switch (err) { |
| 8212 | | error.MultipleDefinitions => return diags.failParse( |
| 8213 | | path, |
| 8214 | | "multiple definitions of '{s}'", |
| 8215 | | .{name}, |
| 8216 | | ), |
| 8217 | | error.MultipleDefaultVersions => return diags.failParse( |
| 8218 | | path, |
| 8219 | | "multiple default versions of '{s}'", |
| 8220 | | .{parseVersionedSymbolName(name).name}, |
| 8221 | | ), |
| 8222 | | error.UndefinedDefaultVersion => unreachable, // shndx is not `.UNDEF` (i.e. the symbol is defined) |
| 8223 | | else => |e| return e, |
| 8224 | | }); |
| 8225 | | }, |
| 8226 | | } |
| 8227 | | } |
| 8228 | | for (sections[1..]) |*rel_sec| switch (rel_sec.shdr.type) { |
| 8229 | | else => {}, |
| 8230 | | inline .REL, .RELA => |sht| { |
| 8231 | | if (rel_sec.shdr.link != symtab_shndx or rel_sec.shdr.info == std.elf.SHN_UNDEF or |
| 8232 | | rel_sec.shdr.info >= ehdr.shnum) continue; |
| 8233 | | const Rel = switch (sht) { |
| 8234 | | else => comptime unreachable, |
| 8235 | | .REL => ElfN.Rel, |
| 8236 | | .RELA => ElfN.Rela, |
| 8237 | | }; |
| 8238 | | if (rel_sec.shdr.entsize < @sizeOf(Rel)) |
| 8239 | | return diags.failParse(path, "unsupported rel entsize", .{}); |
| 8240 | | |
| 8241 | | const loc_sec = &sections[rel_sec.shdr.info]; |
| 8242 | | const loc_node = switch (loc_sec.placement) { |
| 8243 | | .none => continue, |
| 8244 | | .progbits => |isi| isi.node(elf), |
| 8245 | | .nobits => return diags.failParse( |
| 8246 | | path, |
| 8247 | | "cannot apply relocation to SHT_NOBITS section '{s}'", |
| 8248 | | .{std.mem.sliceTo(shstrtab[loc_sec.shdr.name..], 0)}, |
| 8249 | | ), |
| 8250 | | }; |
| 8374 | }; |
| 8251 | 8375 | |
| 8252 | | elf.resetNodeRelocs(loc_node); |
| 8376 | elf.resetNodeRelocs(loc_node); |
| 8253 | 8377 | |
| 8254 | | const relnum = std.math.divExact( |
| 8255 | | u32, |
| 8256 | | @intCast(rel_sec.shdr.size), |
| 8257 | | @intCast(rel_sec.shdr.entsize), |
| 8258 | | ) catch return diags.failParse( |
| 8378 | const relnum = std.math.divExact( |
| 8379 | u32, |
| 8380 | @intCast(rel_sec.shdr.size), |
| 8381 | @intCast(rel_sec.shdr.entsize), |
| 8382 | ) catch return diags.failParse( |
| 8383 | path, |
| 8384 | "relocation section size (0x{x}) is not a multiple of entsize (0x{x})", |
| 8385 | .{ rel_sec.shdr.size, rel_sec.shdr.entsize }, |
| 8386 | ); |
| 8387 | try elf.ensureUnusedRelocCapacity(loc_node, relnum); |
| 8388 | try fr.seekTo(fl.offset + rel_sec.shdr.offset); |
| 8389 | for (0..relnum) |_| { |
| 8390 | const rel = try r.peekStruct(Rel, target_endian); |
| 8391 | try r.discardAll64(rel_sec.shdr.entsize); |
| 8392 | if (rel.info.sym == 0) continue; |
| 8393 | if (rel.info.sym - 1 > symmap.items.len) return diags.failParse( |
| 8259 | 8394 | path, |
| 8260 | | "relocation section size (0x{x}) is not a multiple of entsize (0x{x})", |
| 8261 | | .{ rel_sec.shdr.size, rel_sec.shdr.entsize }, |
| 8395 | "relocation target symbol index {d} exceeds symtab size", |
| 8396 | .{rel.info.sym}, |
| 8262 | 8397 | ); |
| 8263 | | try elf.ensureUnusedRelocCapacity(loc_node, relnum); |
| 8264 | | try fr.seekTo(fl.offset + rel_sec.shdr.offset); |
| 8265 | | for (0..relnum) |_| { |
| 8266 | | const rel = try r.peekStruct(Rel, target_endian); |
| 8267 | | try r.discardAll64(rel_sec.shdr.entsize); |
| 8268 | | if (rel.info.sym == 0) continue; |
| 8269 | | if (rel.info.sym > symnum) return diags.failParse( |
| 8270 | | path, |
| 8271 | | "relocation target symbol index {d} exceeds symtab size", |
| 8272 | | .{rel.info.sym}, |
| 8273 | | ); |
| 8274 | | const target = symmap.items[rel.info.sym - 1]; |
| 8275 | | if (target == Symbol.Id.null) { |
| 8276 | | // If this is not an SHF_ALLOC section, then let's not report |
| 8277 | | // this for now, because it probably doesn't affect the final |
| 8278 | | // binary's functionality for this section to be a bit broken. |
| 8279 | | if (loc_sec.shdr.flags.shf.ALLOC) { |
| 8280 | | diags.addParseError( |
| 8281 | | path, |
| 8282 | | "unsupported symbol at index {d} required for relocation", |
| 8283 | | .{rel.info.sym}, |
| 8284 | | ); |
| 8285 | | } |
| 8286 | | continue; |
| 8287 | | } |
| 8288 | | const rt: MachineRelocType = .wrap(rel.info.type, elf); |
| 8289 | | elf.addRelocAssumeCapacity( |
| 8290 | | loc_node, |
| 8291 | | rel.offset - loc_sec.shdr.addr, |
| 8292 | | target, |
| 8293 | | rel.addend, |
| 8294 | | rt, |
| 8295 | | ) catch |err| switch (err) { |
| 8296 | | else => |e| return e, |
| 8297 | | error.UnknownRelocation => diags.addParseError( |
| 8298 | | path, |
| 8299 | | "unknown relocation type '{f}'", |
| 8300 | | .{rt.fmt(elf)}, |
| 8301 | | ), |
| 8302 | | error.NonStaticRelocation => diags.addParseError( |
| 8398 | const target = symmap.items[rel.info.sym - 1]; |
| 8399 | if (target == Symbol.Id.null) { |
| 8400 | // If this is not an SHF_ALLOC section, then let's not report |
| 8401 | // this for now, because it probably doesn't affect the final |
| 8402 | // binary's functionality for this section to be a bit broken. |
| 8403 | if (loc_sec.shdr.flags.shf.ALLOC) { |
| 8404 | diags.addParseError( |
| 8303 | 8405 | path, |
| 8304 | | "non-static relocation type '{f}'", |
| 8305 | | .{rt.fmt(elf)}, |
| 8306 | | ), |
| 8307 | | error.UnimplementedRelocation => diags.addParseError( |
| 8308 | | path, |
| 8309 | | "TODO(Elf2): unimplemented relocation type '{f}'", |
| 8310 | | .{rt.fmt(elf)}, |
| 8311 | | ), |
| 8312 | | }; |
| 8406 | "unsupported symbol at index {d} required for relocation", |
| 8407 | .{rel.info.sym}, |
| 8408 | ); |
| 8409 | } |
| 8410 | continue; |
| 8313 | 8411 | } |
| 8314 | | }, |
| 8315 | | }; |
| 8316 | | }, |
| 8317 | | }; |
| 8412 | const rt: MachineRelocType = .wrap(rel.info.type, elf); |
| 8413 | elf.addRelocAssumeCapacity( |
| 8414 | loc_node, |
| 8415 | rel.offset - loc_sec.shdr.addr, |
| 8416 | target, |
| 8417 | rel.addend, |
| 8418 | rt, |
| 8419 | ) catch |err| switch (err) { |
| 8420 | else => |e| return e, |
| 8421 | error.UnknownRelocation => diags.addParseError( |
| 8422 | path, |
| 8423 | "unknown relocation type '{f}'", |
| 8424 | .{rt.fmt(elf)}, |
| 8425 | ), |
| 8426 | error.NonStaticRelocation => diags.addParseError( |
| 8427 | path, |
| 8428 | "non-static relocation type '{f}'", |
| 8429 | .{rt.fmt(elf)}, |
| 8430 | ), |
| 8431 | error.UnimplementedRelocation => diags.addParseError( |
| 8432 | path, |
| 8433 | "TODO(Elf2): unimplemented relocation type '{f}'", |
| 8434 | .{rt.fmt(elf)}, |
| 8435 | ), |
| 8436 | }; |
| 8437 | } |
| 8438 | }, |
| 8439 | }; |
| 8440 | } |
| 8318 | 8441 | |
| 8319 | 8442 | if (input_has_bss) { |
| 8320 | 8443 | try elf.bss_inputs.append(gpa, input_index); |
| ... | ... | @@ -8374,6 +8497,10 @@ fn loadDso( |
| 8374 | 8497 | fallback_soname: link.Input.Dso.FallbackSoname, |
| 8375 | 8498 | fr: *Io.File.Reader, |
| 8376 | 8499 | ) (LoadParseInputError || error{BadMagic})!void { |
| 8500 | const trace = tracy.trace(@src()); |
| 8501 | defer trace.end(); |
| 8502 | trace.addTextFmt("{f}", .{path.fmtEscapeString()}); |
| 8503 | |
| 8377 | 8504 | const comp = elf.base.comp; |
| 8378 | 8505 | const gpa = comp.gpa; |
| 8379 | 8506 | const diags = &comp.link_diags; |
| ... | ... | @@ -8519,6 +8646,9 @@ fn loadDso( |
| 8519 | 8646 | const verdef: []DsoGlobals.String.Optional = verdef: { |
| 8520 | 8647 | const verdef_sh = shdr.verdef orelse break :verdef &.{}; |
| 8521 | 8648 | |
| 8649 | const versions_trace = tracy.traceNamed(@src(), "dso versions"); |
| 8650 | defer versions_trace.end(); |
| 8651 | |
| 8522 | 8652 | // This parsing is a bit of a mess because it appears that at this moment in history |
| 8523 | 8653 | // nobody at GNU was yet aware of the concept of an "array". |
| 8524 | 8654 | |
| ... | ... | @@ -8602,6 +8732,9 @@ fn loadDso( |
| 8602 | 8732 | const soname = try elf.string(.dynstr, soname_slice); |
| 8603 | 8733 | try elf.needed.put(gpa, soname, {}); |
| 8604 | 8734 | |
| 8735 | const symbols_trace = tracy.traceNamed(@src(), "dso symbols"); |
| 8736 | defer symbols_trace.end(); |
| 8737 | |
| 8605 | 8738 | // Scan the symbol table and populate `elf.dso_globals`. |
| 8606 | 8739 | const first_global = @min(shdr.dynsym.info, symnum); |
| 8607 | 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 | 9555 | const gpa = elf.base.comp.gpa; |
| 9423 | 9556 | try elf.symbol_relocs.ensureUnusedCapacity(gpa, len); |
| 9424 | 9557 | try elf.node_relocs.ensureUnusedCapacity(gpa, len); |
| 9425 | | try elf.got_relocs.ensureUnusedCapacity(gpa, len); |
| 9426 | 9558 | const class = elf.identClass(); |
| 9427 | 9559 | switch (elf.ehdrType()) { |
| 9428 | 9560 | .REL => { |
| ... | ... | @@ -9460,7 +9592,6 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) |
| 9460 | 9592 | try shndx.get(elf).rela.shndx.relaEnsureAdditionalCapacity(elf, len); |
| 9461 | 9593 | }, |
| 9462 | 9594 | .EXEC, .DYN => { |
| 9463 | | try elf.tls_size_symbol_relocs.ensureUnusedCapacity(gpa, len); |
| 9464 | 9595 | const new_got_entries = len * 2; // at worst, every reloc is a new TLSGD |
| 9465 | 9596 | try elf.got.ensureUnusedCapacity(gpa, new_got_entries); |
| 9466 | 9597 | const need_got_size = switch (class) { |
| ... | ... | @@ -9485,6 +9616,9 @@ fn addRelocAssumeCapacity( |
| 9485 | 9616 | addend: i64, |
| 9486 | 9617 | @"type": MachineRelocType, |
| 9487 | 9618 | ) (Error || error{ UnknownRelocation, NonStaticRelocation, UnimplementedRelocation })!void { |
| 9619 | const trace = tracy.trace(@src()); |
| 9620 | defer trace.end(); |
| 9621 | |
| 9488 | 9622 | switch (elf.ehdrType()) { |
| 9489 | 9623 | .REL => { |
| 9490 | 9624 | const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx; |
| ... | ... | @@ -9506,13 +9640,12 @@ fn addRelocAssumeCapacity( |
| 9506 | 9640 | elf.symbol_relocs.appendAssumeCapacity(.{ |
| 9507 | 9641 | .node = node.toOptional(), |
| 9508 | 9642 | .offset = offset, |
| 9509 | | .type = undefined, |
| 9510 | | .target = target, |
| 9643 | .bits = undefined, |
| 9644 | .target_symbol = target, |
| 9511 | 9645 | .addend = .{ .normal = addend }, |
| 9512 | 9646 | .next = next, |
| 9513 | 9647 | .prev = .none, |
| 9514 | 9648 | .rela_index = rela_index.toOptional(), |
| 9515 | | .result = .ok, |
| 9516 | 9649 | }); |
| 9517 | 9650 | ri.get(elf).apply(elf); |
| 9518 | 9651 | }, |
| ... | ... | @@ -9567,14 +9700,14 @@ fn addRelocAssumeCapacity( |
| 9567 | 9700 | .TLS_LE64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })), |
| 9568 | 9701 | .TLS_LE64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })), |
| 9569 | 9702 | |
| 9570 | | .GOT_PC_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })), |
| 9571 | | .GOT_PC_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala_hi20)), |
| 9572 | | .GOT64_PC_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala64_lo20)), |
| 9573 | | .GOT64_PC_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala64_hi12)), |
| 9574 | | .GOT_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })), |
| 9575 | | .GOT_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })), |
| 9576 | | .GOT64_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })), |
| 9577 | | .GOT64_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })), |
| 9703 | .GOT_PC_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotabs_symbol, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })), |
| 9704 | .GOT_PC_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_got_pcala_hi20)), |
| 9705 | .GOT64_PC_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_got_pcala64_lo20)), |
| 9706 | .GOT64_PC_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_got_pcala64_hi12)), |
| 9707 | .GOT_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotabs_symbol, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })), |
| 9708 | .GOT_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotabs_symbol, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })), |
| 9709 | .GOT64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotabs_symbol, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })), |
| 9710 | .GOT64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotabs_symbol, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })), |
| 9578 | 9711 | // zig fmt: on |
| 9579 | 9712 | }, |
| 9580 | 9713 | .PPC64 => switch (@"type".PPC64) { |
| ... | ... | @@ -9678,13 +9811,13 @@ fn addRelocAssumeCapacity( |
| 9678 | 9811 | .TLS_DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })), |
| 9679 | 9812 | .TLS_DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })), |
| 9680 | 9813 | |
| 9681 | | .GOT13 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[12:0]", .cast = .unsigned, .shift = .@"0" })), |
| 9682 | | .GOT22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })), |
| 9683 | | .GOTDATA_OP_LOX10 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.sparc_op_lox10)), |
| 9684 | | .GOTDATA_OP_HIX22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.sparc_op_hix22)), |
| 9685 | | .TLS_GD_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })), |
| 9686 | | .TLS_LDM_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })), |
| 9687 | | .TLS_IE_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })), |
| 9814 | .GOT13 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_symbol, .{ .dest = .@"32[12:0]", .cast = .unsigned, .shift = .@"0" })), |
| 9815 | .GOT22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_symbol, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })), |
| 9816 | .GOTDATA_OP_LOX10 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.sparc_got_op_lox10)), |
| 9817 | .GOTDATA_OP_HIX22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.sparc_got_op_hix22)), |
| 9818 | .TLS_GD_HI22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_tlsgd, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })), |
| 9819 | .TLS_LDM_HI22 => try elf.addSymbolRelocAssumeCapacity(node, offset, .null, addend, .simple(.gotoff_tlsgd, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })), |
| 9820 | .TLS_IE_HI22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_tpoff, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })), |
| 9688 | 9821 | // zig fmt: on |
| 9689 | 9822 | |
| 9690 | 9823 | .TLS_GD_CALL, .TLS_LDM_CALL => { |
| ... | ... | @@ -9750,19 +9883,19 @@ fn addRelocAssumeCapacity( |
| 9750 | 9883 | }, |
| 9751 | 9884 | .GOT10 => { |
| 9752 | 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 | 9888 | .TLS_GD_LO10 => { |
| 9756 | 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 | 9892 | .TLS_LDM_LO10 => { |
| 9760 | 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 | 9896 | .TLS_IE_LO10 => { |
| 9764 | 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 | 9901 | .X86_64 => rel_type: switch (@"type".X86_64) { |
| ... | ... | @@ -9790,12 +9923,7 @@ fn addRelocAssumeCapacity( |
| 9790 | 9923 | .GOTOFF64 => return error.UnimplementedRelocation, // offset of symbol from GOT base |
| 9791 | 9924 | .PLTOFF64 => return error.UnimplementedRelocation, // offset of PLT entry from GOT base (yes, I know, the name is stupid) |
| 9792 | 9925 | |
| 9793 | | // TODO: figure out how to do relaxations. Perhaps we want to remove a `GotReloc` |
| 9794 | | // and replace it with a `SymbolReloc` when a relaxation becomes possible, but we'd |
| 9795 | | // need to bear in mind whether incremental updates might make a relaxation |
| 9796 | | // impossible again or something like that. Relaxations seem kind of hostile to |
| 9797 | | // incremental compilation, so perhaps we just only support them in non-incremental |
| 9798 | | // compilations and just apply them in flush or something. |
| 9926 | // TODO: figure out how to do relaxations |
| 9799 | 9927 | |
| 9800 | 9928 | // Relaxable versions of other relocations. Since we don't yet implement relaxation, |
| 9801 | 9929 | // just use the handling for the non-relaxable versions. |
| ... | ... | @@ -9831,13 +9959,13 @@ fn addRelocAssumeCapacity( |
| 9831 | 9959 | .TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 9832 | 9960 | .TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })), |
| 9833 | 9961 | |
| 9834 | | .GOT32 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })), |
| 9835 | | .GOT64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })), |
| 9836 | | .GOTPCREL => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 9837 | | .GOTPCREL64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })), |
| 9838 | | .TLSGD => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 9839 | | .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 9840 | | .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 9962 | .GOT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_symbol, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })), |
| 9963 | .GOT64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotoff_symbol, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })), |
| 9964 | .GOTPCREL => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotrel_symbol, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 9965 | .GOTPCREL64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotrel_symbol, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })), |
| 9966 | .TLSGD => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotrel_tlsgd, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 9967 | .TLSLD => try elf.addSymbolRelocAssumeCapacity(node, offset, .null, addend, .simple(.gotrel_tlsgd, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 9968 | .GOTTPOFF => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.gotrel_tpoff, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })), |
| 9841 | 9969 | // zig fmt: on |
| 9842 | 9970 | |
| 9843 | 9971 | .GOTPC64 => { |
| ... | ... | @@ -9872,6 +10000,11 @@ fn addSymbolRelocAssumeCapacityInner( |
| 9872 | 10000 | ) Error!void { |
| 9873 | 10001 | assert(elf.ehdrType() != .REL); |
| 9874 | 10002 | |
| 10003 | const trace = tracy.trace(@src()); |
| 10004 | defer trace.end(); |
| 10005 | |
| 10006 | const gpa = elf.base.comp.gpa; |
| 10007 | |
| 9875 | 10008 | request_static_target: { |
| 9876 | 10009 | if (elf.shndx.dynamic == .UNDEF) break :request_static_target; |
| 9877 | 10010 | const gsi: Symbol.Global.Index = switch (target.unwrap()) { |
| ... | ... | @@ -9894,6 +10027,15 @@ fn addSymbolRelocAssumeCapacityInner( |
| 9894 | 10027 | .dtpoff, |
| 9895 | 10028 | .tpoff, |
| 9896 | 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 | 10039 | => false, |
| 9898 | 10040 | |
| 9899 | 10041 | .special => switch (@"type".action.special) { |
| ... | ... | @@ -9908,10 +10050,15 @@ fn addSymbolRelocAssumeCapacityInner( |
| 9908 | 10050 | .larch_b21, |
| 9909 | 10051 | .larch_b26, |
| 9910 | 10052 | .larch_call36, |
| 10053 | .larch_got_pcala_hi20, |
| 10054 | .larch_got_pcala64_lo20, |
| 10055 | .larch_got_pcala64_hi12, |
| 9911 | 10056 | |
| 9912 | 10057 | .sparc_le_hix22, |
| 9913 | 10058 | .sparc_wdisp10, |
| 9914 | 10059 | .sparc_wdisp16, |
| 10060 | .sparc_got_op_lox10, |
| 10061 | .sparc_got_op_hix22, |
| 9915 | 10062 | => false, |
| 9916 | 10063 | }, |
| 9917 | 10064 | }; |
| ... | ... | @@ -9923,6 +10070,42 @@ fn addSymbolRelocAssumeCapacityInner( |
| 9923 | 10070 | } |
| 9924 | 10071 | } |
| 9925 | 10072 | |
| 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 | 10109 | const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len)); |
| 9927 | 10110 | const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc; |
| 9928 | 10111 | const next = first_target_reloc.*; |
| ... | ... | @@ -9931,18 +10114,24 @@ fn addSymbolRelocAssumeCapacityInner( |
| 9931 | 10114 | elf.symbol_relocs.appendAssumeCapacity(.{ |
| 9932 | 10115 | .node = node.toOptional(), |
| 9933 | 10116 | .offset = offset, |
| 9934 | | .target = target, |
| 10117 | .target_symbol = target, |
| 9935 | 10118 | .addend = addend, |
| 9936 | | .type = @"type", |
| 10119 | .bits = .{ |
| 10120 | .type = @"type", |
| 10121 | .result = .ok, |
| 10122 | .relaxation = .none, |
| 10123 | }, |
| 9937 | 10124 | .next = next, |
| 9938 | 10125 | .prev = .none, |
| 9939 | 10126 | .rela_index = .none, |
| 9940 | | .result = .ok, |
| 9941 | 10127 | }); |
| 9942 | 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); |
| 9946 | 10135 | |
| 9947 | 10136 | // Actually apply the new relocation! |
| 9948 | 10137 | ri.get(elf).apply(elf); |
| ... | ... | @@ -10046,95 +10235,6 @@ fn addNodeRelocAssumeCapacity( |
| 10046 | 10235 | }, |
| 10047 | 10236 | } |
| 10048 | 10237 | } |
| 10049 | | fn 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 | | } |
| 10138 | 10238 | fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 10139 | 10239 | assert(elf.ehdrType() != .REL); |
| 10140 | 10240 | const entry_value: union(enum) { |
| ... | ... | @@ -10193,15 +10293,6 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void { |
| 10193 | 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 | 10296 | .tlsgd0 => |sym| switch (elf.base.comp.config.link_mode) { |
| 10206 | 10297 | .static => val: { |
| 10207 | 10298 | 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 | 10300 | }, |
| 10210 | 10301 | .dynamic => .{ .reloc = .{ |
| 10211 | 10302 | .type = .dtpMod(elf), |
| 10212 | | .dynsym_index = switch (elf.classifySymbolValue(sym)) { |
| 10213 | | .static, .static_relative => 0, |
| 10214 | | .dynamic => sym.unwrap().global.dynsymIndex(elf).?, |
| 10303 | .dynsym_index = switch (sym) { |
| 10304 | .null => 0, |
| 10305 | else => switch (elf.classifySymbolValue(sym)) { |
| 10306 | .static, .static_relative => 0, |
| 10307 | .dynamic => sym.unwrap().global.dynsymIndex(elf).?, |
| 10308 | }, |
| 10215 | 10309 | }, |
| 10216 | 10310 | .addend = 0, |
| 10217 | 10311 | } }, |
| 10218 | 10312 | }, |
| 10219 | | .tlsld0 => switch (elf.base.comp.config.link_mode) { |
| 10220 | | .static => val: { |
| 10221 | | assert(elf.base.comp.config.output_mode == .Exe); // static libraries don't have GOTs |
| 10222 | | break :val .{ .unsigned = 1 }; // TLS module ID for executable |
| 10313 | .tlsgd1 => |sym| switch (sym) { |
| 10314 | .null => .{ .unsigned = 0 }, |
| 10315 | else => switch (elf.classifySymbolValue(sym)) { |
| 10316 | .static => .{ .unsigned = sym.value(elf) }, |
| 10317 | .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative` |
| 10318 | .dynamic => .{ .reloc = .{ |
| 10319 | .type = .dtpOff(elf), |
| 10320 | .dynsym_index = sym.unwrap().global.dynsymIndex(elf).?, |
| 10321 | .addend = 0, |
| 10322 | } }, |
| 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 | }; |
| 10232 | 10326 | |
| 10233 | 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 | 11088 | var ri = symtab_index.ptr(elf).first_target_reloc; |
| 10995 | 11089 | while (ri != .none) { |
| 10996 | 11090 | const reloc = ri.get(elf); |
| 10997 | | assert(reloc.target == sym_id); |
| 11091 | assert(reloc.target_symbol == sym_id); |
| 10998 | 11092 | // In relocatables, every symbol relocation has an output relocation. |
| 10999 | 11093 | const rela_index = reloc.rela_index.unwrap().?; |
| 11000 | 11094 | reloc.relaSection(elf).relaUpdateSym(elf, rela_index, @backingInt(symtab_index)); |
| ... | ... | @@ -11024,7 +11118,7 @@ pub fn idle(elf: *Elf) link.Error!bool { |
| 11024 | 11118 | var ri = symtab_index.ptr(elf).first_target_reloc; |
| 11025 | 11119 | while (ri != .none) { |
| 11026 | 11120 | const reloc = ri.get(elf); |
| 11027 | | assert(reloc.target == sym_id); |
| 11121 | assert(reloc.target_symbol == sym_id); |
| 11028 | 11122 | // There may or may not be a runtime relocation for this symbol reloc. |
| 11029 | 11123 | if (reloc.rela_index.unwrap()) |rela_index| { |
| 11030 | 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 | 11709 | const rela_index = opt_rela_index.unwrap() orelse continue; |
| 11616 | 11710 | rela_dyn_shndx.relaAdjustOffset(elf, rela_index, old_addr, addr); |
| 11617 | 11711 | } |
| 11618 | | for (elf.got_relocs.items) |*reloc| { |
| 11619 | | reloc.apply(elf); |
| 11712 | for (elf.got_relocs.keys()) |ri| { |
| 11713 | ri.get(elf).apply(elf); |
| 11620 | 11714 | } |
| 11621 | 11715 | } else if (shndx == elf.shndx.plt) { |
| 11622 | 11716 | elf.flushMovedNodeRelocs(ni, .{ |
| ... | ... | @@ -11683,7 +11777,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11683 | 11777 | |
| 11684 | 11778 | elf.flushMovedNodeRelocs(ni, .{ |
| 11685 | 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 | 11782 | .copied_global => |global_name| { |
| ... | ... | @@ -11714,7 +11807,6 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void |
| 11714 | 11807 | .none, |
| 11715 | 11808 | .uav, .lazy_code, .lazy_const_data => .none, |
| 11716 | 11809 | }, |
| 11717 | | .first_got_reloc = mi.firstGotReloc(elf), |
| 11718 | 11810 | }); |
| 11719 | 11811 | }, |
| 11720 | 11812 | .debug_shared => |ss| { |
| ... | ... | @@ -12200,8 +12292,6 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo |
| 12200 | 12292 | switch (got_key) { |
| 12201 | 12293 | .reserved, |
| 12202 | 12294 | .symbol, |
| 12203 | | .tlsld0, |
| 12204 | | .tlsld1, |
| 12205 | 12295 | .tlsgd0, |
| 12206 | 12296 | .tlsgd1, |
| 12207 | 12297 | => { |