xref: /freebsd/contrib/llvm-project/lld/MachO/Arch/X86_64.cpp (revision 9e5787d2284e187abb5b654d924394a65772e004)
1 //===- X86_64.cpp ---------------------------------------------------------===//
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 #include "InputFiles.h"
10 #include "Symbols.h"
11 #include "SyntheticSections.h"
12 #include "Target.h"
13 
14 #include "lld/Common/ErrorHandler.h"
15 #include "llvm/BinaryFormat/MachO.h"
16 #include "llvm/Support/Endian.h"
17 
18 using namespace llvm::MachO;
19 using namespace llvm::support::endian;
20 using namespace lld;
21 using namespace lld::macho;
22 
23 namespace {
24 
25 struct X86_64 : TargetInfo {
26   X86_64();
27 
28   uint64_t getImplicitAddend(MemoryBufferRef, const section_64 &,
29                              const relocation_info &) const override;
30   void relocateOne(uint8_t *loc, const Reloc &, uint64_t val) const override;
31 
32   void writeStub(uint8_t *buf, const DylibSymbol &) const override;
33   void writeStubHelperHeader(uint8_t *buf) const override;
34   void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &,
35                             uint64_t entryAddr) const override;
36 
37   void prepareSymbolRelocation(lld::macho::Symbol &, const InputSection *,
38                                const Reloc &) override;
39   uint64_t getSymbolVA(const lld::macho::Symbol &, uint8_t type) const override;
40 };
41 
42 } // namespace
43 
44 static std::string getErrorLocation(MemoryBufferRef mb, const section_64 &sec,
45                                     const relocation_info &rel) {
46   return ("invalid relocation at offset " + std::to_string(rel.r_address) +
47           " of " + sec.segname + "," + sec.sectname + " in " +
48           mb.getBufferIdentifier())
49       .str();
50 }
51 
52 static void validateLength(MemoryBufferRef mb, const section_64 &sec,
53                            const relocation_info &rel,
54                            const std::vector<uint8_t> &validLengths) {
55   if (std::find(validLengths.begin(), validLengths.end(), rel.r_length) !=
56       validLengths.end())
57     return;
58 
59   std::string msg = getErrorLocation(mb, sec, rel) + ": relocations of type " +
60                     std::to_string(rel.r_type) + " must have r_length of ";
61   bool first = true;
62   for (uint8_t length : validLengths) {
63     if (!first)
64       msg += " or ";
65     first = false;
66     msg += std::to_string(length);
67   }
68   fatal(msg);
69 }
70 
71 uint64_t X86_64::getImplicitAddend(MemoryBufferRef mb, const section_64 &sec,
72                                    const relocation_info &rel) const {
73   auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
74   const uint8_t *loc = buf + sec.offset + rel.r_address;
75   switch (rel.r_type) {
76   case X86_64_RELOC_BRANCH:
77     // XXX: ld64 also supports r_length = 0 here but I'm not sure when such a
78     // relocation will actually be generated.
79     validateLength(mb, sec, rel, {2});
80     break;
81   case X86_64_RELOC_SIGNED:
82   case X86_64_RELOC_SIGNED_1:
83   case X86_64_RELOC_SIGNED_2:
84   case X86_64_RELOC_SIGNED_4:
85   case X86_64_RELOC_GOT_LOAD:
86   case X86_64_RELOC_GOT:
87     if (!rel.r_pcrel)
88       fatal(getErrorLocation(mb, sec, rel) + ": relocations of type " +
89             std::to_string(rel.r_type) + " must be pcrel");
90     validateLength(mb, sec, rel, {2});
91     break;
92   case X86_64_RELOC_UNSIGNED:
93     if (rel.r_pcrel)
94       fatal(getErrorLocation(mb, sec, rel) + ": relocations of type " +
95             std::to_string(rel.r_type) + " must not be pcrel");
96     validateLength(mb, sec, rel, {2, 3});
97     break;
98   default:
99     error("TODO: Unhandled relocation type " + std::to_string(rel.r_type));
100     return 0;
101   }
102 
103   switch (rel.r_length) {
104   case 0:
105     return *loc;
106   case 1:
107     return read16le(loc);
108   case 2:
109     return read32le(loc);
110   case 3:
111     return read64le(loc);
112   default:
113     llvm_unreachable("invalid r_length");
114   }
115 }
116 
117 void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t val) const {
118   switch (r.type) {
119   case X86_64_RELOC_BRANCH:
120   case X86_64_RELOC_SIGNED:
121   case X86_64_RELOC_SIGNED_1:
122   case X86_64_RELOC_SIGNED_2:
123   case X86_64_RELOC_SIGNED_4:
124   case X86_64_RELOC_GOT_LOAD:
125   case X86_64_RELOC_GOT:
126     // These types are only used for pc-relative relocations, so offset by 4
127     // since the RIP has advanced by 4 at this point. This is only valid when
128     // r_length = 2, which is enforced by validateLength().
129     val -= 4;
130     break;
131   case X86_64_RELOC_UNSIGNED:
132     break;
133   default:
134     llvm_unreachable(
135         "getImplicitAddend should have flagged all unhandled relocation types");
136   }
137 
138   switch (r.length) {
139   case 0:
140     *loc = val;
141     break;
142   case 1:
143     write16le(loc, val);
144     break;
145   case 2:
146     write32le(loc, val);
147     break;
148   case 3:
149     write64le(loc, val);
150     break;
151   default:
152     llvm_unreachable("invalid r_length");
153   }
154 }
155 
156 // The following methods emit a number of assembly sequences with RIP-relative
157 // addressing. Note that RIP-relative addressing on X86-64 has the RIP pointing
158 // to the next instruction, not the current instruction, so we always have to
159 // account for the current instruction's size when calculating offsets.
160 // writeRipRelative helps with that.
161 //
162 // bufAddr:  The virtual address corresponding to buf[0].
163 // bufOff:   The offset within buf of the next instruction.
164 // destAddr: The destination address that the current instruction references.
165 static void writeRipRelative(uint8_t *buf, uint64_t bufAddr, uint64_t bufOff,
166                              uint64_t destAddr) {
167   uint64_t rip = bufAddr + bufOff;
168   // For the instructions we care about, the RIP-relative address is always
169   // stored in the last 4 bytes of the instruction.
170   write32le(buf + bufOff - 4, destAddr - rip);
171 }
172 
173 static constexpr uint8_t stub[] = {
174     0xff, 0x25, 0, 0, 0, 0, // jmpq *__la_symbol_ptr(%rip)
175 };
176 
177 void X86_64::writeStub(uint8_t *buf, const DylibSymbol &sym) const {
178   memcpy(buf, stub, 2); // just copy the two nonzero bytes
179   uint64_t stubAddr = in.stubs->addr + sym.stubsIndex * sizeof(stub);
180   writeRipRelative(buf, stubAddr, sizeof(stub),
181                    in.lazyPointers->addr + sym.stubsIndex * WordSize);
182 }
183 
184 static constexpr uint8_t stubHelperHeader[] = {
185     0x4c, 0x8d, 0x1d, 0, 0, 0, 0, // 0x0: leaq ImageLoaderCache(%rip), %r11
186     0x41, 0x53,                   // 0x7: pushq %r11
187     0xff, 0x25, 0,    0, 0, 0,    // 0x9: jmpq *dyld_stub_binder@GOT(%rip)
188     0x90,                         // 0xf: nop
189 };
190 
191 static constexpr uint8_t stubHelperEntry[] = {
192     0x68, 0, 0, 0, 0, // 0x0: pushq <bind offset>
193     0xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper>
194 };
195 
196 void X86_64::writeStubHelperHeader(uint8_t *buf) const {
197   memcpy(buf, stubHelperHeader, sizeof(stubHelperHeader));
198   writeRipRelative(buf, in.stubHelper->addr, 7, in.imageLoaderCache->getVA());
199   writeRipRelative(buf, in.stubHelper->addr, 0xf,
200                    in.got->addr +
201                        in.stubHelper->stubBinder->gotIndex * WordSize);
202 }
203 
204 void X86_64::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym,
205                                   uint64_t entryAddr) const {
206   memcpy(buf, stubHelperEntry, sizeof(stubHelperEntry));
207   write32le(buf + 1, sym.lazyBindOffset);
208   writeRipRelative(buf, entryAddr, sizeof(stubHelperEntry),
209                    in.stubHelper->addr);
210 }
211 
212 void X86_64::prepareSymbolRelocation(lld::macho::Symbol &sym,
213                                      const InputSection *isec, const Reloc &r) {
214   switch (r.type) {
215   case X86_64_RELOC_GOT_LOAD:
216     // TODO: implement mov -> lea relaxation for non-dynamic symbols
217   case X86_64_RELOC_GOT:
218     in.got->addEntry(sym);
219     break;
220   case X86_64_RELOC_BRANCH: {
221     if (auto *dysym = dyn_cast<DylibSymbol>(&sym))
222       in.stubs->addEntry(*dysym);
223     break;
224   }
225   case X86_64_RELOC_UNSIGNED: {
226     if (auto *dysym = dyn_cast<DylibSymbol>(&sym)) {
227       if (r.length != 3) {
228         error("X86_64_RELOC_UNSIGNED referencing the dynamic symbol " +
229               dysym->getName() + " must have r_length = 3");
230         return;
231       }
232       in.binding->addEntry(dysym, isec, r.offset, r.addend);
233     }
234     break;
235   }
236   case X86_64_RELOC_SIGNED:
237   case X86_64_RELOC_SIGNED_1:
238   case X86_64_RELOC_SIGNED_2:
239   case X86_64_RELOC_SIGNED_4:
240     break;
241   case X86_64_RELOC_SUBTRACTOR:
242   case X86_64_RELOC_TLV:
243     fatal("TODO: handle relocation type " + std::to_string(r.type));
244     break;
245   default:
246     llvm_unreachable("unexpected relocation type");
247   }
248 }
249 
250 uint64_t X86_64::getSymbolVA(const lld::macho::Symbol &sym,
251                              uint8_t type) const {
252   switch (type) {
253   case X86_64_RELOC_GOT_LOAD:
254   case X86_64_RELOC_GOT:
255     return in.got->addr + sym.gotIndex * WordSize;
256   case X86_64_RELOC_BRANCH:
257     if (auto *dysym = dyn_cast<DylibSymbol>(&sym))
258       return in.stubs->addr + dysym->stubsIndex * sizeof(stub);
259     return sym.getVA();
260   case X86_64_RELOC_UNSIGNED:
261   case X86_64_RELOC_SIGNED:
262   case X86_64_RELOC_SIGNED_1:
263   case X86_64_RELOC_SIGNED_2:
264   case X86_64_RELOC_SIGNED_4:
265     return sym.getVA();
266   case X86_64_RELOC_SUBTRACTOR:
267   case X86_64_RELOC_TLV:
268     fatal("TODO: handle relocation type " + std::to_string(type));
269   default:
270     llvm_unreachable("Unexpected relocation type");
271   }
272 }
273 
274 X86_64::X86_64() {
275   cpuType = CPU_TYPE_X86_64;
276   cpuSubtype = CPU_SUBTYPE_X86_64_ALL;
277 
278   stubSize = sizeof(stub);
279   stubHelperHeaderSize = sizeof(stubHelperHeader);
280   stubHelperEntrySize = sizeof(stubHelperEntry);
281 }
282 
283 TargetInfo *macho::createX86_64TargetInfo() {
284   static X86_64 t;
285   return &t;
286 }
287