1 | //===- ARM64Common.h --------------------------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef LLD_MACHO_ARCH_ARM64COMMON_H |
10 | #define LLD_MACHO_ARCH_ARM64COMMON_H |
11 | |
12 | #include "InputFiles.h" |
13 | #include "Symbols.h" |
14 | #include "SyntheticSections.h" |
15 | #include "Target.h" |
16 | |
17 | #include "llvm/BinaryFormat/MachO.h" |
18 | |
19 | namespace lld { |
20 | namespace macho { |
21 | |
22 | struct ARM64Common : TargetInfo { |
23 | template <class LP> ARM64Common(LP lp) : TargetInfo(lp) {} |
24 | |
25 | int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset, |
26 | const llvm::MachO::relocation_info) const override; |
27 | void relocateOne(uint8_t *loc, const Reloc &, uint64_t va, |
28 | uint64_t pc) const override; |
29 | |
30 | void relaxGotLoad(uint8_t *loc, uint8_t type) const override; |
31 | uint64_t getPageSize() const override { return 16 * 1024; } |
32 | }; |
33 | |
34 | inline uint64_t bitField(uint64_t value, int right, int width, int left) { |
35 | return ((value >> right) & ((1 << width) - 1)) << left; |
36 | } |
37 | |
38 | // 25 0 |
39 | // +-----------+---------------------------------------------------+ |
40 | // | | imm26 | |
41 | // +-----------+---------------------------------------------------+ |
42 | |
43 | inline uint64_t encodeBranch26(const Reloc &r, uint64_t base, uint64_t va) { |
44 | checkInt(r, va, 28); |
45 | // Since branch destinations are 4-byte aligned, the 2 least- |
46 | // significant bits are 0. They are right shifted off the end. |
47 | return (base | bitField(va, 2, 26, 0)); |
48 | } |
49 | |
50 | inline uint64_t encodeBranch26(SymbolDiagnostic d, uint64_t base, uint64_t va) { |
51 | checkInt(d, va, 28); |
52 | return (base | bitField(va, 2, 26, 0)); |
53 | } |
54 | |
55 | // 30 29 23 5 |
56 | // +-+---+---------+-------------------------------------+---------+ |
57 | // | |ilo| | immhi | | |
58 | // +-+---+---------+-------------------------------------+---------+ |
59 | |
60 | inline uint64_t encodePage21(const Reloc &r, uint64_t base, uint64_t va) { |
61 | checkInt(r, va, 35); |
62 | return (base | bitField(va, 12, 2, 29) | bitField(va, 14, 19, 5)); |
63 | } |
64 | |
65 | inline uint64_t encodePage21(SymbolDiagnostic d, uint64_t base, uint64_t va) { |
66 | checkInt(d, va, 35); |
67 | return (base | bitField(va, 12, 2, 29) | bitField(va, 14, 19, 5)); |
68 | } |
69 | |
70 | // 21 10 |
71 | // +-------------------+-----------------------+-------------------+ |
72 | // | | imm12 | | |
73 | // +-------------------+-----------------------+-------------------+ |
74 | |
75 | inline uint64_t encodePageOff12(uint32_t base, uint64_t va) { |
76 | int scale = 0; |
77 | if ((base & 0x3b00'0000) == 0x3900'0000) { // load/store |
78 | scale = base >> 30; |
79 | if (scale == 0 && (base & 0x0480'0000) == 0x0480'0000) // 128-bit variant |
80 | scale = 4; |
81 | } |
82 | |
83 | // TODO(gkm): extract embedded addend and warn if != 0 |
84 | // uint64_t addend = ((base & 0x003FFC00) >> 10); |
85 | return (base | bitField(va, scale, 12 - scale, 10)); |
86 | } |
87 | |
88 | inline uint64_t pageBits(uint64_t address) { |
89 | const uint64_t pageMask = ~0xfffull; |
90 | return address & pageMask; |
91 | } |
92 | |
93 | template <class LP> |
94 | inline void writeStub(uint8_t *buf8, const uint32_t stubCode[3], |
95 | const macho::Symbol &sym) { |
96 | auto *buf32 = reinterpret_cast<uint32_t *>(buf8); |
97 | constexpr size_t stubCodeSize = 3 * sizeof(uint32_t); |
98 | uint64_t pcPageBits = |
99 | pageBits(in.stubs->addr + sym.stubsIndex * stubCodeSize); |
100 | uint64_t lazyPointerVA = |
101 | in.lazyPointers->addr + sym.stubsIndex * LP::wordSize; |
102 | buf32[0] = encodePage21({&sym, "stub" }, stubCode[0], |
103 | pageBits(lazyPointerVA) - pcPageBits); |
104 | buf32[1] = encodePageOff12(stubCode[1], lazyPointerVA); |
105 | buf32[2] = stubCode[2]; |
106 | } |
107 | |
108 | template <class LP> |
109 | inline void (uint8_t *buf8, |
110 | const uint32_t [6]) { |
111 | auto *buf32 = reinterpret_cast<uint32_t *>(buf8); |
112 | auto pcPageBits = [](int i) { |
113 | return pageBits(in.stubHelper->addr + i * sizeof(uint32_t)); |
114 | }; |
115 | uint64_t loaderVA = in.imageLoaderCache->getVA(); |
116 | SymbolDiagnostic d = {nullptr, "stub header helper" }; |
117 | buf32[0] = encodePage21(d, stubHelperHeaderCode[0], |
118 | pageBits(loaderVA) - pcPageBits(0)); |
119 | buf32[1] = encodePageOff12(stubHelperHeaderCode[1], loaderVA); |
120 | buf32[2] = stubHelperHeaderCode[2]; |
121 | uint64_t binderVA = |
122 | in.got->addr + in.stubHelper->stubBinder->gotIndex * LP::wordSize; |
123 | buf32[3] = encodePage21(d, stubHelperHeaderCode[3], |
124 | pageBits(binderVA) - pcPageBits(3)); |
125 | buf32[4] = encodePageOff12(stubHelperHeaderCode[4], binderVA); |
126 | buf32[5] = stubHelperHeaderCode[5]; |
127 | } |
128 | |
129 | inline void writeStubHelperEntry(uint8_t *buf8, |
130 | const uint32_t stubHelperEntryCode[3], |
131 | const DylibSymbol &sym, uint64_t entryVA) { |
132 | auto *buf32 = reinterpret_cast<uint32_t *>(buf8); |
133 | auto pcVA = [entryVA](int i) { return entryVA + i * sizeof(uint32_t); }; |
134 | uint64_t = in.stubHelper->addr; |
135 | buf32[0] = stubHelperEntryCode[0]; |
136 | buf32[1] = encodeBranch26({&sym, "stub helper" }, stubHelperEntryCode[1], |
137 | stubHelperHeaderVA - pcVA(1)); |
138 | buf32[2] = sym.lazyBindOffset; |
139 | } |
140 | |
141 | } // namespace macho |
142 | } // namespace lld |
143 | |
144 | #endif |
145 | |