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