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 "mach-o/compact_unwind_encoding.h"
16 #include "llvm/BinaryFormat/MachO.h"
17 #include "llvm/Support/Endian.h"
18 
19 using namespace llvm::MachO;
20 using namespace llvm::support::endian;
21 using namespace lld;
22 using namespace lld::macho;
23 
24 namespace {
25 
26 struct X86_64 : TargetInfo {
27   X86_64();
28 
29   int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset,
30                             const relocation_info) const override;
31   void relocateOne(uint8_t *loc, const Reloc &, uint64_t va,
32                    uint64_t relocVA) const override;
33 
34   void writeStub(uint8_t *buf, const Symbol &) const override;
35   void writeStubHelperHeader(uint8_t *buf) const override;
36   void writeStubHelperEntry(uint8_t *buf, const Symbol &,
37                             uint64_t entryAddr) const override;
38 
39   void relaxGotLoad(uint8_t *loc, uint8_t type) const override;
40   const RelocAttrs &getRelocAttrs(uint8_t type) const override;
41   uint64_t getPageSize() const override { return 4 * 1024; }
42 
43   void handleDtraceReloc(const Symbol *sym, const Reloc &r,
44                          uint8_t *loc) const override;
45 };
46 
47 } // namespace
48 
49 const RelocAttrs &X86_64::getRelocAttrs(uint8_t type) const {
50   static const std::array<RelocAttrs, 10> relocAttrsArray{{
51 #define B(x) RelocAttrBits::x
52       {"UNSIGNED",
53        B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)},
54       {"SIGNED", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
55       {"BRANCH", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
56       {"GOT_LOAD", B(PCREL) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)},
57       {"GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)},
58       {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)},
59       {"SIGNED_1", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
60       {"SIGNED_2", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
61       {"SIGNED_4", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
62       {"TLV", B(PCREL) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)},
63 #undef B
64   }};
65   assert(type < relocAttrsArray.size() && "invalid relocation type");
66   if (type >= relocAttrsArray.size())
67     return invalidRelocAttrs;
68   return relocAttrsArray[type];
69 }
70 
71 static int pcrelOffset(uint8_t type) {
72   switch (type) {
73   case X86_64_RELOC_SIGNED_1:
74     return 1;
75   case X86_64_RELOC_SIGNED_2:
76     return 2;
77   case X86_64_RELOC_SIGNED_4:
78     return 4;
79   default:
80     return 0;
81   }
82 }
83 
84 int64_t X86_64::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
85                                   relocation_info rel) const {
86   auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
87   const uint8_t *loc = buf + offset + rel.r_address;
88 
89   switch (rel.r_length) {
90   case 2:
91     return static_cast<int32_t>(read32le(loc)) + pcrelOffset(rel.r_type);
92   case 3:
93     return read64le(loc) + pcrelOffset(rel.r_type);
94   default:
95     llvm_unreachable("invalid r_length");
96   }
97 }
98 
99 void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
100                          uint64_t relocVA) const {
101   if (r.pcrel) {
102     uint64_t pc = relocVA + 4 + pcrelOffset(r.type);
103     value -= pc;
104   }
105 
106   switch (r.length) {
107   case 2:
108     if (r.type == X86_64_RELOC_UNSIGNED)
109       checkUInt(loc, r, value, 32);
110     else
111       checkInt(loc, r, value, 32);
112     write32le(loc, value);
113     break;
114   case 3:
115     write64le(loc, value);
116     break;
117   default:
118     llvm_unreachable("invalid r_length");
119   }
120 }
121 
122 // The following methods emit a number of assembly sequences with RIP-relative
123 // addressing. Note that RIP-relative addressing on X86-64 has the RIP pointing
124 // to the next instruction, not the current instruction, so we always have to
125 // account for the current instruction's size when calculating offsets.
126 // writeRipRelative helps with that.
127 //
128 // bufAddr:  The virtual address corresponding to buf[0].
129 // bufOff:   The offset within buf of the next instruction.
130 // destAddr: The destination address that the current instruction references.
131 static void writeRipRelative(SymbolDiagnostic d, uint8_t *buf, uint64_t bufAddr,
132                              uint64_t bufOff, uint64_t destAddr) {
133   uint64_t rip = bufAddr + bufOff;
134   checkInt(buf, d, destAddr - rip, 32);
135   // For the instructions we care about, the RIP-relative address is always
136   // stored in the last 4 bytes of the instruction.
137   write32le(buf + bufOff - 4, destAddr - rip);
138 }
139 
140 static constexpr uint8_t stub[] = {
141     0xff, 0x25, 0, 0, 0, 0, // jmpq *__la_symbol_ptr(%rip)
142 };
143 
144 void X86_64::writeStub(uint8_t *buf, const Symbol &sym) const {
145   memcpy(buf, stub, 2); // just copy the two nonzero bytes
146   uint64_t stubAddr = in.stubs->addr + sym.stubsIndex * sizeof(stub);
147   writeRipRelative({&sym, "stub"}, buf, stubAddr, sizeof(stub),
148                    in.lazyPointers->addr + sym.stubsIndex * LP64::wordSize);
149 }
150 
151 static constexpr uint8_t stubHelperHeader[] = {
152     0x4c, 0x8d, 0x1d, 0, 0, 0, 0, // 0x0: leaq ImageLoaderCache(%rip), %r11
153     0x41, 0x53,                   // 0x7: pushq %r11
154     0xff, 0x25, 0,    0, 0, 0,    // 0x9: jmpq *dyld_stub_binder@GOT(%rip)
155     0x90,                         // 0xf: nop
156 };
157 
158 void X86_64::writeStubHelperHeader(uint8_t *buf) const {
159   memcpy(buf, stubHelperHeader, sizeof(stubHelperHeader));
160   SymbolDiagnostic d = {nullptr, "stub helper header"};
161   writeRipRelative(d, buf, in.stubHelper->addr, 7,
162                    in.imageLoaderCache->getVA());
163   writeRipRelative(d, buf, in.stubHelper->addr, 0xf,
164                    in.got->addr +
165                        in.stubHelper->stubBinder->gotIndex * LP64::wordSize);
166 }
167 
168 static constexpr uint8_t stubHelperEntry[] = {
169     0x68, 0, 0, 0, 0, // 0x0: pushq <bind offset>
170     0xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper>
171 };
172 
173 void X86_64::writeStubHelperEntry(uint8_t *buf, const Symbol &sym,
174                                   uint64_t entryAddr) const {
175   memcpy(buf, stubHelperEntry, sizeof(stubHelperEntry));
176   write32le(buf + 1, sym.lazyBindOffset);
177   writeRipRelative({&sym, "stub helper"}, buf, entryAddr,
178                    sizeof(stubHelperEntry), in.stubHelper->addr);
179 }
180 
181 void X86_64::relaxGotLoad(uint8_t *loc, uint8_t type) const {
182   // Convert MOVQ to LEAQ
183   if (loc[-2] != 0x8b)
184     error(getRelocAttrs(type).name + " reloc requires MOVQ instruction");
185   loc[-2] = 0x8d;
186 }
187 
188 X86_64::X86_64() : TargetInfo(LP64()) {
189   cpuType = CPU_TYPE_X86_64;
190   cpuSubtype = CPU_SUBTYPE_X86_64_ALL;
191 
192   modeDwarfEncoding = UNWIND_X86_MODE_DWARF;
193   subtractorRelocType = X86_64_RELOC_SUBTRACTOR;
194   unsignedRelocType = X86_64_RELOC_UNSIGNED;
195 
196   stubSize = sizeof(stub);
197   stubHelperHeaderSize = sizeof(stubHelperHeader);
198   stubHelperEntrySize = sizeof(stubHelperEntry);
199 }
200 
201 TargetInfo *macho::createX86_64TargetInfo() {
202   static X86_64 t;
203   return &t;
204 }
205 
206 void X86_64::handleDtraceReloc(const Symbol *sym, const Reloc &r,
207                                uint8_t *loc) const {
208   assert(r.type == X86_64_RELOC_BRANCH);
209 
210   if (config->outputType == MH_OBJECT)
211     return;
212 
213   if (sym->getName().startswith("___dtrace_probe")) {
214     // change call site to a NOP
215     loc[-1] = 0x90;
216     write32le(loc, 0x00401F0F);
217   } else if (sym->getName().startswith("___dtrace_isenabled")) {
218     // change call site to a clear eax
219     loc[-1] = 0x33;
220     write32le(loc, 0x909090C0);
221   } else {
222     error("Unrecognized dtrace symbol prefix: " + toString(*sym));
223   }
224 }
225