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
18using namespace llvm::MachO;
19using namespace llvm::support::endian;
20using namespace lld;
21using namespace lld::macho;
22
23namespace {
24
25struct 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
44static 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
52static 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
71uint64_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
117void 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.
165static 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
173static constexpr uint8_t stub[] = {
174    0xff, 0x25, 0, 0, 0, 0, // jmpq *__la_symbol_ptr(%rip)
175};
176
177void 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
184static 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
191static 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
196void 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
204void 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
212void 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
250uint64_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
274X86_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
283TargetInfo *macho::createX86_64TargetInfo() {
284  static X86_64 t;
285  return &t;
286}
287