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::macho { |
20 | |
21 | struct ARM64Common : TargetInfo { |
22 | template <class LP> ARM64Common(LP lp) : TargetInfo(lp) {} |
23 | |
24 | int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset, |
25 | const llvm::MachO::relocation_info) const override; |
26 | void relocateOne(uint8_t *loc, const Reloc &, uint64_t va, |
27 | uint64_t pc) const override; |
28 | |
29 | void relaxGotLoad(uint8_t *loc, uint8_t type) const override; |
30 | uint64_t getPageSize() const override { return 16 * 1024; } |
31 | |
32 | void handleDtraceReloc(const Symbol *sym, const Reloc &r, |
33 | uint8_t *loc) const override; |
34 | }; |
35 | |
36 | inline uint64_t bitField(uint64_t value, int right, int width, int left) { |
37 | return ((value >> right) & ((1 << width) - 1)) << left; |
38 | } |
39 | |
40 | // 25 0 |
41 | // +-----------+---------------------------------------------------+ |
42 | // | | imm26 | |
43 | // +-----------+---------------------------------------------------+ |
44 | |
45 | inline void encodeBranch26(uint32_t *loc, const Reloc &r, uint32_t base, |
46 | uint64_t va) { |
47 | checkInt(loc, d: r, v: va, bits: 28); |
48 | // Since branch destinations are 4-byte aligned, the 2 least- |
49 | // significant bits are 0. They are right shifted off the end. |
50 | llvm::support::endian::write32le(P: loc, V: base | bitField(value: va, right: 2, width: 26, left: 0)); |
51 | } |
52 | |
53 | inline void encodeBranch26(uint32_t *loc, SymbolDiagnostic d, uint32_t base, |
54 | uint64_t va) { |
55 | checkInt(loc, d, v: va, bits: 28); |
56 | llvm::support::endian::write32le(P: loc, V: base | bitField(value: va, right: 2, width: 26, left: 0)); |
57 | } |
58 | |
59 | // 30 29 23 5 |
60 | // +-+---+---------+-------------------------------------+---------+ |
61 | // | |ilo| | immhi | | |
62 | // +-+---+---------+-------------------------------------+---------+ |
63 | |
64 | inline void encodePage21(uint32_t *loc, const Reloc &r, uint32_t base, |
65 | uint64_t va) { |
66 | checkInt(loc, d: r, v: va, bits: 35); |
67 | llvm::support::endian::write32le(P: loc, V: base | bitField(value: va, right: 12, width: 2, left: 29) | |
68 | bitField(value: va, right: 14, width: 19, left: 5)); |
69 | } |
70 | |
71 | inline void encodePage21(uint32_t *loc, SymbolDiagnostic d, uint32_t base, |
72 | uint64_t va) { |
73 | checkInt(loc, d, v: va, bits: 35); |
74 | llvm::support::endian::write32le(P: loc, V: base | bitField(value: va, right: 12, width: 2, left: 29) | |
75 | bitField(value: va, right: 14, width: 19, left: 5)); |
76 | } |
77 | |
78 | void reportUnalignedLdrStr(void *loc, const Reloc &, uint64_t va, int align); |
79 | void reportUnalignedLdrStr(void *loc, SymbolDiagnostic, uint64_t va, int align); |
80 | |
81 | // 21 10 |
82 | // +-------------------+-----------------------+-------------------+ |
83 | // | | imm12 | | |
84 | // +-------------------+-----------------------+-------------------+ |
85 | |
86 | template <typename Target> |
87 | inline void encodePageOff12(uint32_t *loc, Target t, uint32_t base, |
88 | uint64_t va) { |
89 | int scale = 0; |
90 | if ((base & 0x3b00'0000) == 0x3900'0000) { // load/store |
91 | scale = base >> 30; |
92 | if (scale == 0 && (base & 0x0480'0000) == 0x0480'0000) // 128-bit variant |
93 | scale = 4; |
94 | } |
95 | const int size = 1 << scale; |
96 | if ((va & (size - 1)) != 0) |
97 | reportUnalignedLdrStr(loc, t, va, size); |
98 | |
99 | // TODO(gkm): extract embedded addend and warn if != 0 |
100 | // uint64_t addend = ((base & 0x003FFC00) >> 10); |
101 | llvm::support::endian::write32le(P: loc, |
102 | V: base | bitField(value: va, right: scale, width: 12 - scale, left: 10)); |
103 | } |
104 | |
105 | inline uint64_t pageBits(uint64_t address) { |
106 | const uint64_t pageMask = ~0xfffull; |
107 | return address & pageMask; |
108 | } |
109 | |
110 | inline void writeStub(uint8_t *buf8, const uint32_t stubCode[3], |
111 | const macho::Symbol &sym, uint64_t pointerVA) { |
112 | auto *buf32 = reinterpret_cast<uint32_t *>(buf8); |
113 | constexpr size_t stubCodeSize = 3 * sizeof(uint32_t); |
114 | SymbolDiagnostic d = {.symbol: &sym, .reason: "stub" }; |
115 | uint64_t pcPageBits = |
116 | pageBits(address: in.stubs->addr + sym.stubsIndex * stubCodeSize); |
117 | encodePage21(loc: &buf32[0], d, base: stubCode[0], va: pageBits(address: pointerVA) - pcPageBits); |
118 | encodePageOff12(loc: &buf32[1], t: d, base: stubCode[1], va: pointerVA); |
119 | buf32[2] = stubCode[2]; |
120 | } |
121 | |
122 | template <class LP> |
123 | inline void (uint8_t *buf8, |
124 | const uint32_t [6]) { |
125 | auto *buf32 = reinterpret_cast<uint32_t *>(buf8); |
126 | auto pcPageBits = [](int i) { |
127 | return pageBits(address: in.stubHelper->addr + i * sizeof(uint32_t)); |
128 | }; |
129 | uint64_t loaderVA = in.imageLoaderCache->getVA(); |
130 | SymbolDiagnostic d = {.symbol: nullptr, .reason: "stub header helper" }; |
131 | encodePage21(&buf32[0], d, stubHelperHeaderCode[0], |
132 | pageBits(address: loaderVA) - pcPageBits(0)); |
133 | encodePageOff12(loc: &buf32[1], t: d, base: stubHelperHeaderCode[1], va: loaderVA); |
134 | buf32[2] = stubHelperHeaderCode[2]; |
135 | uint64_t binderVA = |
136 | in.got->addr + in.stubHelper->stubBinder->gotIndex * LP::wordSize; |
137 | encodePage21(&buf32[3], d, stubHelperHeaderCode[3], |
138 | pageBits(address: binderVA) - pcPageBits(3)); |
139 | encodePageOff12(loc: &buf32[4], t: d, base: stubHelperHeaderCode[4], va: binderVA); |
140 | buf32[5] = stubHelperHeaderCode[5]; |
141 | } |
142 | |
143 | inline void writeStubHelperEntry(uint8_t *buf8, |
144 | const uint32_t stubHelperEntryCode[3], |
145 | const Symbol &sym, uint64_t entryVA) { |
146 | auto *buf32 = reinterpret_cast<uint32_t *>(buf8); |
147 | auto pcVA = [entryVA](int i) { return entryVA + i * sizeof(uint32_t); }; |
148 | uint64_t = in.stubHelper->addr; |
149 | buf32[0] = stubHelperEntryCode[0]; |
150 | encodeBranch26(loc: &buf32[1], d: {.symbol: &sym, .reason: "stub helper" }, base: stubHelperEntryCode[1], |
151 | va: stubHelperHeaderVA - pcVA(1)); |
152 | buf32[2] = sym.lazyBindOffset; |
153 | } |
154 | |
155 | template <class LP> |
156 | inline void writeObjCMsgSendFastStub(uint8_t *buf, |
157 | const uint32_t objcStubsFastCode[8], |
158 | Symbol *sym, uint64_t stubsAddr, |
159 | uint64_t stubOffset, uint64_t selrefVA, |
160 | uint64_t gotAddr, uint64_t msgSendIndex) { |
161 | SymbolDiagnostic d = {.symbol: sym, .reason: sym->getName()}; |
162 | auto *buf32 = reinterpret_cast<uint32_t *>(buf); |
163 | |
164 | auto pcPageBits = [stubsAddr, stubOffset](int i) { |
165 | return pageBits(address: stubsAddr + stubOffset + i * sizeof(uint32_t)); |
166 | }; |
167 | |
168 | encodePage21(&buf32[0], d, objcStubsFastCode[0], |
169 | pageBits(address: selrefVA) - pcPageBits(0)); |
170 | encodePageOff12(loc: &buf32[1], t: d, base: objcStubsFastCode[1], va: selrefVA); |
171 | uint64_t gotOffset = msgSendIndex * LP::wordSize; |
172 | encodePage21(&buf32[2], d, objcStubsFastCode[2], |
173 | pageBits(address: gotAddr + gotOffset) - pcPageBits(2)); |
174 | encodePageOff12(loc: &buf32[3], t: d, base: objcStubsFastCode[3], va: gotAddr + gotOffset); |
175 | buf32[4] = objcStubsFastCode[4]; |
176 | buf32[5] = objcStubsFastCode[5]; |
177 | buf32[6] = objcStubsFastCode[6]; |
178 | buf32[7] = objcStubsFastCode[7]; |
179 | } |
180 | |
181 | template <class LP> |
182 | inline void |
183 | writeObjCMsgSendSmallStub(uint8_t *buf, const uint32_t objcStubsSmallCode[3], |
184 | Symbol *sym, uint64_t stubsAddr, uint64_t stubOffset, |
185 | uint64_t selrefVA, uint64_t msgSendAddr, |
186 | uint64_t msgSendIndex) { |
187 | SymbolDiagnostic d = {.symbol: sym, .reason: sym->getName()}; |
188 | auto *buf32 = reinterpret_cast<uint32_t *>(buf); |
189 | |
190 | auto pcPageBits = [stubsAddr, stubOffset](int i) { |
191 | return pageBits(address: stubsAddr + stubOffset + i * sizeof(uint32_t)); |
192 | }; |
193 | |
194 | encodePage21(&buf32[0], d, objcStubsSmallCode[0], |
195 | pageBits(address: selrefVA) - pcPageBits(0)); |
196 | encodePageOff12(loc: &buf32[1], t: d, base: objcStubsSmallCode[1], va: selrefVA); |
197 | uint64_t msgSendStubVA = msgSendAddr + msgSendIndex * target->stubSize; |
198 | uint64_t pcVA = stubsAddr + stubOffset + 2 * sizeof(uint32_t); |
199 | encodeBranch26(loc: &buf32[2], d: {.symbol: nullptr, .reason: "objc_msgSend stub" }, |
200 | base: objcStubsSmallCode[2], va: msgSendStubVA - pcVA); |
201 | } |
202 | |
203 | } // namespace lld::macho |
204 | |
205 | #endif |
206 | |