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