xref: /freebsd-src/contrib/llvm-project/lld/MachO/Arch/X86_64.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
15ffd83dbSDimitry Andric //===- X86_64.cpp ---------------------------------------------------------===//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric 
95ffd83dbSDimitry Andric #include "InputFiles.h"
105ffd83dbSDimitry Andric #include "Symbols.h"
115ffd83dbSDimitry Andric #include "SyntheticSections.h"
125ffd83dbSDimitry Andric #include "Target.h"
135ffd83dbSDimitry Andric 
145ffd83dbSDimitry Andric #include "lld/Common/ErrorHandler.h"
1581ad6265SDimitry Andric #include "mach-o/compact_unwind_encoding.h"
165ffd83dbSDimitry Andric #include "llvm/BinaryFormat/MachO.h"
175ffd83dbSDimitry Andric #include "llvm/Support/Endian.h"
185ffd83dbSDimitry Andric 
195ffd83dbSDimitry Andric using namespace llvm::MachO;
205ffd83dbSDimitry Andric using namespace llvm::support::endian;
215ffd83dbSDimitry Andric using namespace lld;
225ffd83dbSDimitry Andric using namespace lld::macho;
235ffd83dbSDimitry Andric 
245ffd83dbSDimitry Andric namespace {
255ffd83dbSDimitry Andric 
265ffd83dbSDimitry Andric struct X86_64 : TargetInfo {
275ffd83dbSDimitry Andric   X86_64();
285ffd83dbSDimitry Andric 
29fe6060f1SDimitry Andric   int64_t getEmbeddedAddend(MemoryBufferRef, uint64_t offset,
30fe6060f1SDimitry Andric                             const relocation_info) const override;
31fe6060f1SDimitry Andric   void relocateOne(uint8_t *loc, const Reloc &, uint64_t va,
32fe6060f1SDimitry Andric                    uint64_t relocVA) const override;
335ffd83dbSDimitry Andric 
34bdd1243dSDimitry Andric   void writeStub(uint8_t *buf, const Symbol &,
35bdd1243dSDimitry Andric                  uint64_t pointerVA) const override;
365ffd83dbSDimitry Andric   void writeStubHelperHeader(uint8_t *buf) const override;
3781ad6265SDimitry Andric   void writeStubHelperEntry(uint8_t *buf, const Symbol &,
385ffd83dbSDimitry Andric                             uint64_t entryAddr) const override;
395ffd83dbSDimitry Andric 
40bdd1243dSDimitry Andric   void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
41*0fca6ea1SDimitry Andric                             uint64_t &stubOffset, uint64_t selrefVA,
427a6dacacSDimitry Andric                             Symbol *objcMsgSend) const override;
43bdd1243dSDimitry Andric 
44fe6060f1SDimitry Andric   void relaxGotLoad(uint8_t *loc, uint8_t type) const override;
45fe6060f1SDimitry Andric   uint64_t getPageSize() const override { return 4 * 1024; }
46753f127fSDimitry Andric 
47753f127fSDimitry Andric   void handleDtraceReloc(const Symbol *sym, const Reloc &r,
48753f127fSDimitry Andric                          uint8_t *loc) const override;
495ffd83dbSDimitry Andric };
505ffd83dbSDimitry Andric } // namespace
515ffd83dbSDimitry Andric 
52fcaf7f86SDimitry Andric static constexpr std::array<RelocAttrs, 10> relocAttrsArray{{
53fe6060f1SDimitry Andric #define B(x) RelocAttrBits::x
54fe6060f1SDimitry Andric     {"UNSIGNED",
55fe6060f1SDimitry Andric      B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)},
56fe6060f1SDimitry Andric     {"SIGNED", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
57fe6060f1SDimitry Andric     {"BRANCH", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
58fe6060f1SDimitry Andric     {"GOT_LOAD", B(PCREL) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)},
59fe6060f1SDimitry Andric     {"GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)},
60fe6060f1SDimitry Andric     {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)},
61fe6060f1SDimitry Andric     {"SIGNED_1", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
62fe6060f1SDimitry Andric     {"SIGNED_2", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
63fe6060f1SDimitry Andric     {"SIGNED_4", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
64fe6060f1SDimitry Andric     {"TLV", B(PCREL) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)},
65fe6060f1SDimitry Andric #undef B
66fe6060f1SDimitry Andric }};
675ffd83dbSDimitry Andric 
68fe6060f1SDimitry Andric static int pcrelOffset(uint8_t type) {
69fe6060f1SDimitry Andric   switch (type) {
705ffd83dbSDimitry Andric   case X86_64_RELOC_SIGNED_1:
71fe6060f1SDimitry Andric     return 1;
725ffd83dbSDimitry Andric   case X86_64_RELOC_SIGNED_2:
73fe6060f1SDimitry Andric     return 2;
745ffd83dbSDimitry Andric   case X86_64_RELOC_SIGNED_4:
75fe6060f1SDimitry Andric     return 4;
765ffd83dbSDimitry Andric   default:
775ffd83dbSDimitry Andric     return 0;
785ffd83dbSDimitry Andric   }
79fe6060f1SDimitry Andric }
80fe6060f1SDimitry Andric 
81fe6060f1SDimitry Andric int64_t X86_64::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
82fe6060f1SDimitry Andric                                   relocation_info rel) const {
83fe6060f1SDimitry Andric   auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
84fe6060f1SDimitry Andric   const uint8_t *loc = buf + offset + rel.r_address;
855ffd83dbSDimitry Andric 
865ffd83dbSDimitry Andric   switch (rel.r_length) {
875ffd83dbSDimitry Andric   case 2:
88fe6060f1SDimitry Andric     return static_cast<int32_t>(read32le(loc)) + pcrelOffset(rel.r_type);
895ffd83dbSDimitry Andric   case 3:
90fe6060f1SDimitry Andric     return read64le(loc) + pcrelOffset(rel.r_type);
915ffd83dbSDimitry Andric   default:
925ffd83dbSDimitry Andric     llvm_unreachable("invalid r_length");
935ffd83dbSDimitry Andric   }
945ffd83dbSDimitry Andric }
955ffd83dbSDimitry Andric 
96fe6060f1SDimitry Andric void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
97fe6060f1SDimitry Andric                          uint64_t relocVA) const {
98fe6060f1SDimitry Andric   if (r.pcrel) {
99fe6060f1SDimitry Andric     uint64_t pc = relocVA + 4 + pcrelOffset(r.type);
100fe6060f1SDimitry Andric     value -= pc;
1015ffd83dbSDimitry Andric   }
1025ffd83dbSDimitry Andric 
1035ffd83dbSDimitry Andric   switch (r.length) {
1045ffd83dbSDimitry Andric   case 2:
105fe6060f1SDimitry Andric     if (r.type == X86_64_RELOC_UNSIGNED)
10681ad6265SDimitry Andric       checkUInt(loc, r, value, 32);
107fe6060f1SDimitry Andric     else
10881ad6265SDimitry Andric       checkInt(loc, r, value, 32);
109fe6060f1SDimitry Andric     write32le(loc, value);
1105ffd83dbSDimitry Andric     break;
1115ffd83dbSDimitry Andric   case 3:
112fe6060f1SDimitry Andric     write64le(loc, value);
1135ffd83dbSDimitry Andric     break;
1145ffd83dbSDimitry Andric   default:
1155ffd83dbSDimitry Andric     llvm_unreachable("invalid r_length");
1165ffd83dbSDimitry Andric   }
1175ffd83dbSDimitry Andric }
1185ffd83dbSDimitry Andric 
1195ffd83dbSDimitry Andric // The following methods emit a number of assembly sequences with RIP-relative
1205ffd83dbSDimitry Andric // addressing. Note that RIP-relative addressing on X86-64 has the RIP pointing
1215ffd83dbSDimitry Andric // to the next instruction, not the current instruction, so we always have to
1225ffd83dbSDimitry Andric // account for the current instruction's size when calculating offsets.
1235ffd83dbSDimitry Andric // writeRipRelative helps with that.
1245ffd83dbSDimitry Andric //
1255ffd83dbSDimitry Andric // bufAddr:  The virtual address corresponding to buf[0].
1265ffd83dbSDimitry Andric // bufOff:   The offset within buf of the next instruction.
1275ffd83dbSDimitry Andric // destAddr: The destination address that the current instruction references.
128fe6060f1SDimitry Andric static void writeRipRelative(SymbolDiagnostic d, uint8_t *buf, uint64_t bufAddr,
129fe6060f1SDimitry Andric                              uint64_t bufOff, uint64_t destAddr) {
1305ffd83dbSDimitry Andric   uint64_t rip = bufAddr + bufOff;
13181ad6265SDimitry Andric   checkInt(buf, d, destAddr - rip, 32);
1325ffd83dbSDimitry Andric   // For the instructions we care about, the RIP-relative address is always
1335ffd83dbSDimitry Andric   // stored in the last 4 bytes of the instruction.
1345ffd83dbSDimitry Andric   write32le(buf + bufOff - 4, destAddr - rip);
1355ffd83dbSDimitry Andric }
1365ffd83dbSDimitry Andric 
1375ffd83dbSDimitry Andric static constexpr uint8_t stub[] = {
1385ffd83dbSDimitry Andric     0xff, 0x25, 0, 0, 0, 0, // jmpq *__la_symbol_ptr(%rip)
1395ffd83dbSDimitry Andric };
1405ffd83dbSDimitry Andric 
141bdd1243dSDimitry Andric void X86_64::writeStub(uint8_t *buf, const Symbol &sym,
142bdd1243dSDimitry Andric                        uint64_t pointerVA) const {
1435ffd83dbSDimitry Andric   memcpy(buf, stub, 2); // just copy the two nonzero bytes
1445ffd83dbSDimitry Andric   uint64_t stubAddr = in.stubs->addr + sym.stubsIndex * sizeof(stub);
145bdd1243dSDimitry Andric   writeRipRelative({&sym, "stub"}, buf, stubAddr, sizeof(stub), pointerVA);
1465ffd83dbSDimitry Andric }
1475ffd83dbSDimitry Andric 
1485ffd83dbSDimitry Andric static constexpr uint8_t stubHelperHeader[] = {
1495ffd83dbSDimitry Andric     0x4c, 0x8d, 0x1d, 0, 0, 0, 0, // 0x0: leaq ImageLoaderCache(%rip), %r11
1505ffd83dbSDimitry Andric     0x41, 0x53,                   // 0x7: pushq %r11
1515ffd83dbSDimitry Andric     0xff, 0x25, 0,    0, 0, 0,    // 0x9: jmpq *dyld_stub_binder@GOT(%rip)
1525ffd83dbSDimitry Andric     0x90,                         // 0xf: nop
1535ffd83dbSDimitry Andric };
1545ffd83dbSDimitry Andric 
155fe6060f1SDimitry Andric void X86_64::writeStubHelperHeader(uint8_t *buf) const {
156fe6060f1SDimitry Andric   memcpy(buf, stubHelperHeader, sizeof(stubHelperHeader));
157fe6060f1SDimitry Andric   SymbolDiagnostic d = {nullptr, "stub helper header"};
158fe6060f1SDimitry Andric   writeRipRelative(d, buf, in.stubHelper->addr, 7,
159fe6060f1SDimitry Andric                    in.imageLoaderCache->getVA());
160fe6060f1SDimitry Andric   writeRipRelative(d, buf, in.stubHelper->addr, 0xf,
161fe6060f1SDimitry Andric                    in.got->addr +
162fe6060f1SDimitry Andric                        in.stubHelper->stubBinder->gotIndex * LP64::wordSize);
163fe6060f1SDimitry Andric }
164fe6060f1SDimitry Andric 
1655ffd83dbSDimitry Andric static constexpr uint8_t stubHelperEntry[] = {
1665ffd83dbSDimitry Andric     0x68, 0, 0, 0, 0, // 0x0: pushq <bind offset>
1675ffd83dbSDimitry Andric     0xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper>
1685ffd83dbSDimitry Andric };
1695ffd83dbSDimitry Andric 
17081ad6265SDimitry Andric void X86_64::writeStubHelperEntry(uint8_t *buf, const Symbol &sym,
1715ffd83dbSDimitry Andric                                   uint64_t entryAddr) const {
1725ffd83dbSDimitry Andric   memcpy(buf, stubHelperEntry, sizeof(stubHelperEntry));
1735ffd83dbSDimitry Andric   write32le(buf + 1, sym.lazyBindOffset);
174fe6060f1SDimitry Andric   writeRipRelative({&sym, "stub helper"}, buf, entryAddr,
175fe6060f1SDimitry Andric                    sizeof(stubHelperEntry), in.stubHelper->addr);
1765ffd83dbSDimitry Andric }
1775ffd83dbSDimitry Andric 
178bdd1243dSDimitry Andric static constexpr uint8_t objcStubsFastCode[] = {
179bdd1243dSDimitry Andric     0x48, 0x8b, 0x35, 0, 0, 0, 0, // 0x0: movq selrefs@selector(%rip), %rsi
180bdd1243dSDimitry Andric     0xff, 0x25, 0,    0, 0, 0,    // 0x7: jmpq *_objc_msgSend@GOT(%rip)
181bdd1243dSDimitry Andric };
182bdd1243dSDimitry Andric 
183bdd1243dSDimitry Andric void X86_64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
184*0fca6ea1SDimitry Andric                                   uint64_t &stubOffset, uint64_t selrefVA,
1857a6dacacSDimitry Andric                                   Symbol *objcMsgSend) const {
1867a6dacacSDimitry Andric   uint64_t objcMsgSendAddr = in.got->addr;
1877a6dacacSDimitry Andric   uint64_t objcMsgSendIndex = objcMsgSend->gotIndex;
1887a6dacacSDimitry Andric 
189bdd1243dSDimitry Andric   memcpy(buf, objcStubsFastCode, sizeof(objcStubsFastCode));
190bdd1243dSDimitry Andric   SymbolDiagnostic d = {sym, sym->getName()};
191bdd1243dSDimitry Andric   uint64_t stubAddr = stubsAddr + stubOffset;
192*0fca6ea1SDimitry Andric   writeRipRelative(d, buf, stubAddr, 7, selrefVA);
193bdd1243dSDimitry Andric   writeRipRelative(d, buf, stubAddr, 0xd,
1947a6dacacSDimitry Andric                    objcMsgSendAddr + objcMsgSendIndex * LP64::wordSize);
1957a6dacacSDimitry Andric   stubOffset += target->objcStubsFastSize;
196bdd1243dSDimitry Andric }
197bdd1243dSDimitry Andric 
198fe6060f1SDimitry Andric void X86_64::relaxGotLoad(uint8_t *loc, uint8_t type) const {
199fe6060f1SDimitry Andric   // Convert MOVQ to LEAQ
200fe6060f1SDimitry Andric   if (loc[-2] != 0x8b)
201fe6060f1SDimitry Andric     error(getRelocAttrs(type).name + " reloc requires MOVQ instruction");
202fe6060f1SDimitry Andric   loc[-2] = 0x8d;
2035ffd83dbSDimitry Andric }
2045ffd83dbSDimitry Andric 
205fe6060f1SDimitry Andric X86_64::X86_64() : TargetInfo(LP64()) {
2065ffd83dbSDimitry Andric   cpuType = CPU_TYPE_X86_64;
2075ffd83dbSDimitry Andric   cpuSubtype = CPU_SUBTYPE_X86_64_ALL;
2085ffd83dbSDimitry Andric 
20981ad6265SDimitry Andric   modeDwarfEncoding = UNWIND_X86_MODE_DWARF;
21081ad6265SDimitry Andric   subtractorRelocType = X86_64_RELOC_SUBTRACTOR;
21181ad6265SDimitry Andric   unsignedRelocType = X86_64_RELOC_UNSIGNED;
21281ad6265SDimitry Andric 
2135ffd83dbSDimitry Andric   stubSize = sizeof(stub);
2145ffd83dbSDimitry Andric   stubHelperHeaderSize = sizeof(stubHelperHeader);
2155ffd83dbSDimitry Andric   stubHelperEntrySize = sizeof(stubHelperEntry);
216fcaf7f86SDimitry Andric 
217bdd1243dSDimitry Andric   objcStubsFastSize = sizeof(objcStubsFastCode);
2187a6dacacSDimitry Andric   objcStubsFastAlignment = 1;
219bdd1243dSDimitry Andric 
220fcaf7f86SDimitry Andric   relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()};
2215ffd83dbSDimitry Andric }
2225ffd83dbSDimitry Andric 
2235ffd83dbSDimitry Andric TargetInfo *macho::createX86_64TargetInfo() {
2245ffd83dbSDimitry Andric   static X86_64 t;
2255ffd83dbSDimitry Andric   return &t;
2265ffd83dbSDimitry Andric }
227753f127fSDimitry Andric 
228753f127fSDimitry Andric void X86_64::handleDtraceReloc(const Symbol *sym, const Reloc &r,
229753f127fSDimitry Andric                                uint8_t *loc) const {
230753f127fSDimitry Andric   assert(r.type == X86_64_RELOC_BRANCH);
231753f127fSDimitry Andric 
232753f127fSDimitry Andric   if (config->outputType == MH_OBJECT)
233753f127fSDimitry Andric     return;
234753f127fSDimitry Andric 
23506c3fb27SDimitry Andric   if (sym->getName().starts_with("___dtrace_probe")) {
236753f127fSDimitry Andric     // change call site to a NOP
237753f127fSDimitry Andric     loc[-1] = 0x90;
238753f127fSDimitry Andric     write32le(loc, 0x00401F0F);
23906c3fb27SDimitry Andric   } else if (sym->getName().starts_with("___dtrace_isenabled")) {
240753f127fSDimitry Andric     // change call site to a clear eax
241753f127fSDimitry Andric     loc[-1] = 0x33;
242753f127fSDimitry Andric     write32le(loc, 0x909090C0);
243753f127fSDimitry Andric   } else {
244753f127fSDimitry Andric     error("Unrecognized dtrace symbol prefix: " + toString(*sym));
245753f127fSDimitry Andric   }
246753f127fSDimitry Andric }
247