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