xref: /llvm-project/lld/MachO/Arch/ARM64Common.cpp (revision 8d85c96e0e3a058ae82930cf6767d9a1c8cc7c1d)
13bc88eb3SJez Ng //===- ARM64Common.cpp ----------------------------------------------------===//
23bc88eb3SJez Ng //
33bc88eb3SJez Ng // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43bc88eb3SJez Ng // See https://llvm.org/LICENSE.txt for license information.
53bc88eb3SJez Ng // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63bc88eb3SJez Ng //
73bc88eb3SJez Ng //===----------------------------------------------------------------------===//
83bc88eb3SJez Ng 
93bc88eb3SJez Ng #include "Arch/ARM64Common.h"
103bc88eb3SJez Ng 
113bc88eb3SJez Ng #include "lld/Common/ErrorHandler.h"
123bc88eb3SJez Ng #include "llvm/Support/Endian.h"
133bc88eb3SJez Ng 
143bc88eb3SJez Ng using namespace llvm::MachO;
153bc88eb3SJez Ng using namespace llvm::support::endian;
163bc88eb3SJez Ng using namespace lld;
173bc88eb3SJez Ng using namespace lld::macho;
183bc88eb3SJez Ng 
getEmbeddedAddend(MemoryBufferRef mb,uint64_t offset,const relocation_info rel) const193bc88eb3SJez Ng int64_t ARM64Common::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
203bc88eb3SJez Ng                                        const relocation_info rel) const {
213bc88eb3SJez Ng   if (rel.r_type != ARM64_RELOC_UNSIGNED &&
223bc88eb3SJez Ng       rel.r_type != ARM64_RELOC_SUBTRACTOR) {
233bc88eb3SJez Ng     // All other reloc types should use the ADDEND relocation to store their
243bc88eb3SJez Ng     // addends.
253bc88eb3SJez Ng     // TODO(gkm): extract embedded addend just so we can assert that it is 0
263bc88eb3SJez Ng     return 0;
273bc88eb3SJez Ng   }
283bc88eb3SJez Ng 
293bc88eb3SJez Ng   const auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
303bc88eb3SJez Ng   const uint8_t *loc = buf + offset + rel.r_address;
313bc88eb3SJez Ng   switch (rel.r_length) {
323bc88eb3SJez Ng   case 2:
333bc88eb3SJez Ng     return static_cast<int32_t>(read32le(loc));
343bc88eb3SJez Ng   case 3:
353bc88eb3SJez Ng     return read64le(loc);
363bc88eb3SJez Ng   default:
373bc88eb3SJez Ng     llvm_unreachable("invalid r_length");
383bc88eb3SJez Ng   }
393bc88eb3SJez Ng }
403bc88eb3SJez Ng 
writeValue(uint8_t * loc,const Reloc & r,uint64_t value)41a552fb2aSJez Ng static void writeValue(uint8_t *loc, const Reloc &r, uint64_t value) {
423bc88eb3SJez Ng   switch (r.length) {
433bc88eb3SJez Ng   case 2:
44a552fb2aSJez Ng     checkInt(loc, r, value, 32);
453bc88eb3SJez Ng     write32le(loc, value);
463bc88eb3SJez Ng     break;
473bc88eb3SJez Ng   case 3:
483bc88eb3SJez Ng     write64le(loc, value);
493bc88eb3SJez Ng     break;
503bc88eb3SJez Ng   default:
513bc88eb3SJez Ng     llvm_unreachable("invalid r_length");
523bc88eb3SJez Ng   }
533bc88eb3SJez Ng }
543bc88eb3SJez Ng 
55a552fb2aSJez Ng // For instruction relocations (load, store, add), the base
56a552fb2aSJez Ng // instruction is pre-populated in the text section. A pre-populated
57a552fb2aSJez Ng // instruction has opcode & register-operand bits set, with immediate
58a552fb2aSJez Ng // operands zeroed. We read it from text, OR-in the immediate
59a552fb2aSJez Ng // operands, then write-back the completed instruction.
relocateOne(uint8_t * loc,const Reloc & r,uint64_t value,uint64_t pc) const60a552fb2aSJez Ng void ARM64Common::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
61a552fb2aSJez Ng                               uint64_t pc) const {
62a552fb2aSJez Ng   auto loc32 = reinterpret_cast<uint32_t *>(loc);
63a552fb2aSJez Ng   uint32_t base = ((r.length == 2) ? read32le(loc) : 0);
64a552fb2aSJez Ng   switch (r.type) {
65a552fb2aSJez Ng   case ARM64_RELOC_BRANCH26:
66a552fb2aSJez Ng     encodeBranch26(loc32, r, base, value - pc);
67a552fb2aSJez Ng     break;
68a552fb2aSJez Ng   case ARM64_RELOC_SUBTRACTOR:
69a552fb2aSJez Ng   case ARM64_RELOC_UNSIGNED:
70a552fb2aSJez Ng     writeValue(loc, r, value);
71a552fb2aSJez Ng     break;
72a552fb2aSJez Ng   case ARM64_RELOC_POINTER_TO_GOT:
73a552fb2aSJez Ng     if (r.pcrel)
74a552fb2aSJez Ng       value -= pc;
75a552fb2aSJez Ng     writeValue(loc, r, value);
76a552fb2aSJez Ng     break;
77a552fb2aSJez Ng   case ARM64_RELOC_PAGE21:
78a552fb2aSJez Ng   case ARM64_RELOC_GOT_LOAD_PAGE21:
79a552fb2aSJez Ng   case ARM64_RELOC_TLVP_LOAD_PAGE21:
80a552fb2aSJez Ng     assert(r.pcrel);
81a552fb2aSJez Ng     encodePage21(loc32, r, base, pageBits(value) - pageBits(pc));
82a552fb2aSJez Ng     break;
83a552fb2aSJez Ng   case ARM64_RELOC_PAGEOFF12:
84a552fb2aSJez Ng   case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
85a552fb2aSJez Ng   case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
86a552fb2aSJez Ng     assert(!r.pcrel);
871b65d209SDaniel Bertalan     encodePageOff12(loc32, r, base, value);
88a552fb2aSJez Ng     break;
89a552fb2aSJez Ng   default:
90a552fb2aSJez Ng     llvm_unreachable("unexpected relocation type");
91a552fb2aSJez Ng   }
92a552fb2aSJez Ng }
93a552fb2aSJez Ng 
relaxGotLoad(uint8_t * loc,uint8_t type) const943bc88eb3SJez Ng void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const {
953bc88eb3SJez Ng   // The instruction format comments below are quoted from
963bc88eb3SJez Ng   // Arm® Architecture Reference Manual
973bc88eb3SJez Ng   // Armv8, for Armv8-A architecture profile
983bc88eb3SJez Ng   // ARM DDI 0487G.a (ID011921)
993bc88eb3SJez Ng   uint32_t instruction = read32le(loc);
1003bc88eb3SJez Ng   // C6.2.132 LDR (immediate)
1011acda12dSJez Ng   // This matches both the 64- and 32-bit variants:
1021acda12dSJez Ng   // LDR <(X|W)t>, [<Xn|SP>{, #<pimm>}]
1031acda12dSJez Ng   if ((instruction & 0xbfc00000) != 0xb9400000)
1043bc88eb3SJez Ng     error(getRelocAttrs(type).name + " reloc requires LDR instruction");
1053bc88eb3SJez Ng   assert(((instruction >> 10) & 0xfff) == 0 &&
1063bc88eb3SJez Ng          "non-zero embedded LDR immediate");
1073bc88eb3SJez Ng   // C6.2.4 ADD (immediate)
1083bc88eb3SJez Ng   // ADD <Xd|SP>, <Xn|SP>, #<imm>{, <shift>}
1093bc88eb3SJez Ng   instruction = ((instruction & 0x001fffff) | 0x91000000);
1103bc88eb3SJez Ng   write32le(loc, instruction);
1113bc88eb3SJez Ng }
1126c641d0dSKaining Zhong 
handleDtraceReloc(const Symbol * sym,const Reloc & r,uint8_t * loc) const1136c641d0dSKaining Zhong void ARM64Common::handleDtraceReloc(const Symbol *sym, const Reloc &r,
1146c641d0dSKaining Zhong                                     uint8_t *loc) const {
1156c641d0dSKaining Zhong   assert(r.type == ARM64_RELOC_BRANCH26);
1166c641d0dSKaining Zhong 
1176c641d0dSKaining Zhong   if (config->outputType == MH_OBJECT)
1186c641d0dSKaining Zhong     return;
1196c641d0dSKaining Zhong 
120*8d85c96eSFangrui Song   if (sym->getName().starts_with("___dtrace_probe")) {
1216c641d0dSKaining Zhong     // change call site to a NOP
1226c641d0dSKaining Zhong     write32le(loc, 0xD503201F);
123*8d85c96eSFangrui Song   } else if (sym->getName().starts_with("___dtrace_isenabled")) {
1246c641d0dSKaining Zhong     // change call site to 'MOVZ X0,0'
1256c641d0dSKaining Zhong     write32le(loc, 0xD2800000);
1266c641d0dSKaining Zhong   } else {
1276c641d0dSKaining Zhong     error("Unrecognized dtrace symbol prefix: " + toString(*sym));
1286c641d0dSKaining Zhong   }
1296c641d0dSKaining Zhong }
1301b65d209SDaniel Bertalan 
reportUnalignedLdrStr(Twine loc,uint64_t va,int align,const Symbol * sym)1311b65d209SDaniel Bertalan static void reportUnalignedLdrStr(Twine loc, uint64_t va, int align,
1321b65d209SDaniel Bertalan                                   const Symbol *sym) {
1331b65d209SDaniel Bertalan   std::string symbolHint;
1341b65d209SDaniel Bertalan   if (sym)
1351b65d209SDaniel Bertalan     symbolHint = " (" + toString(*sym) + ")";
1361b65d209SDaniel Bertalan   error(loc + ": " + Twine(8 * align) + "-bit LDR/STR to 0x" +
1371b65d209SDaniel Bertalan         llvm::utohexstr(va) + symbolHint + " is not " + Twine(align) +
1381b65d209SDaniel Bertalan         "-byte aligned");
1391b65d209SDaniel Bertalan }
1401b65d209SDaniel Bertalan 
reportUnalignedLdrStr(void * loc,const lld::macho::Reloc & r,uint64_t va,int align)1411b65d209SDaniel Bertalan void macho::reportUnalignedLdrStr(void *loc, const lld::macho::Reloc &r,
1421b65d209SDaniel Bertalan                                   uint64_t va, int align) {
1431b65d209SDaniel Bertalan   uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart;
1441b65d209SDaniel Bertalan   const InputSection *isec = offsetToInputSection(&off);
1451b65d209SDaniel Bertalan   std::string locStr = isec ? isec->getLocation(off) : "(invalid location)";
1461b65d209SDaniel Bertalan   ::reportUnalignedLdrStr(locStr, va, align, r.referent.dyn_cast<Symbol *>());
1471b65d209SDaniel Bertalan }
1481b65d209SDaniel Bertalan 
reportUnalignedLdrStr(void * loc,lld::macho::SymbolDiagnostic d,uint64_t va,int align)1491b65d209SDaniel Bertalan void macho::reportUnalignedLdrStr(void *loc, lld::macho::SymbolDiagnostic d,
1501b65d209SDaniel Bertalan                                   uint64_t va, int align) {
1511b65d209SDaniel Bertalan   ::reportUnalignedLdrStr(d.reason, va, align, d.symbol);
1521b65d209SDaniel Bertalan }
153