xref: /openbsd-src/gnu/llvm/lld/MachO/Arch/ARM64Common.cpp (revision dfe94b169149f14cc1aee2cf6dad58a8d9a1860c)
11cf9926bSpatrick //===- ARM64Common.cpp ----------------------------------------------------===//
21cf9926bSpatrick //
31cf9926bSpatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
41cf9926bSpatrick // See https://llvm.org/LICENSE.txt for license information.
51cf9926bSpatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
61cf9926bSpatrick //
71cf9926bSpatrick //===----------------------------------------------------------------------===//
81cf9926bSpatrick 
91cf9926bSpatrick #include "Arch/ARM64Common.h"
101cf9926bSpatrick 
111cf9926bSpatrick #include "lld/Common/ErrorHandler.h"
121cf9926bSpatrick #include "llvm/Support/Endian.h"
131cf9926bSpatrick 
141cf9926bSpatrick using namespace llvm::MachO;
151cf9926bSpatrick using namespace llvm::support::endian;
161cf9926bSpatrick using namespace lld;
171cf9926bSpatrick using namespace lld::macho;
181cf9926bSpatrick 
getEmbeddedAddend(MemoryBufferRef mb,uint64_t offset,const relocation_info rel) const191cf9926bSpatrick int64_t ARM64Common::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
201cf9926bSpatrick                                        const relocation_info rel) const {
211cf9926bSpatrick   if (rel.r_type != ARM64_RELOC_UNSIGNED &&
221cf9926bSpatrick       rel.r_type != ARM64_RELOC_SUBTRACTOR) {
231cf9926bSpatrick     // All other reloc types should use the ADDEND relocation to store their
241cf9926bSpatrick     // addends.
251cf9926bSpatrick     // TODO(gkm): extract embedded addend just so we can assert that it is 0
261cf9926bSpatrick     return 0;
271cf9926bSpatrick   }
281cf9926bSpatrick 
291cf9926bSpatrick   const auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
301cf9926bSpatrick   const uint8_t *loc = buf + offset + rel.r_address;
311cf9926bSpatrick   switch (rel.r_length) {
321cf9926bSpatrick   case 2:
331cf9926bSpatrick     return static_cast<int32_t>(read32le(loc));
341cf9926bSpatrick   case 3:
351cf9926bSpatrick     return read64le(loc);
361cf9926bSpatrick   default:
371cf9926bSpatrick     llvm_unreachable("invalid r_length");
381cf9926bSpatrick   }
391cf9926bSpatrick }
401cf9926bSpatrick 
writeValue(uint8_t * loc,const Reloc & r,uint64_t value)41*dfe94b16Srobert static void writeValue(uint8_t *loc, const Reloc &r, uint64_t value) {
421cf9926bSpatrick   switch (r.length) {
431cf9926bSpatrick   case 2:
44*dfe94b16Srobert     checkInt(loc, r, value, 32);
451cf9926bSpatrick     write32le(loc, value);
461cf9926bSpatrick     break;
471cf9926bSpatrick   case 3:
481cf9926bSpatrick     write64le(loc, value);
491cf9926bSpatrick     break;
501cf9926bSpatrick   default:
511cf9926bSpatrick     llvm_unreachable("invalid r_length");
521cf9926bSpatrick   }
531cf9926bSpatrick }
541cf9926bSpatrick 
55*dfe94b16Srobert // For instruction relocations (load, store, add), the base
56*dfe94b16Srobert // instruction is pre-populated in the text section. A pre-populated
57*dfe94b16Srobert // instruction has opcode & register-operand bits set, with immediate
58*dfe94b16Srobert // operands zeroed. We read it from text, OR-in the immediate
59*dfe94b16Srobert // operands, then write-back the completed instruction.
relocateOne(uint8_t * loc,const Reloc & r,uint64_t value,uint64_t pc) const60*dfe94b16Srobert void ARM64Common::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
61*dfe94b16Srobert                               uint64_t pc) const {
62*dfe94b16Srobert   auto loc32 = reinterpret_cast<uint32_t *>(loc);
63*dfe94b16Srobert   uint32_t base = ((r.length == 2) ? read32le(loc) : 0);
64*dfe94b16Srobert   switch (r.type) {
65*dfe94b16Srobert   case ARM64_RELOC_BRANCH26:
66*dfe94b16Srobert     encodeBranch26(loc32, r, base, value - pc);
67*dfe94b16Srobert     break;
68*dfe94b16Srobert   case ARM64_RELOC_SUBTRACTOR:
69*dfe94b16Srobert   case ARM64_RELOC_UNSIGNED:
70*dfe94b16Srobert     writeValue(loc, r, value);
71*dfe94b16Srobert     break;
72*dfe94b16Srobert   case ARM64_RELOC_POINTER_TO_GOT:
73*dfe94b16Srobert     if (r.pcrel)
74*dfe94b16Srobert       value -= pc;
75*dfe94b16Srobert     writeValue(loc, r, value);
76*dfe94b16Srobert     break;
77*dfe94b16Srobert   case ARM64_RELOC_PAGE21:
78*dfe94b16Srobert   case ARM64_RELOC_GOT_LOAD_PAGE21:
79*dfe94b16Srobert   case ARM64_RELOC_TLVP_LOAD_PAGE21:
80*dfe94b16Srobert     assert(r.pcrel);
81*dfe94b16Srobert     encodePage21(loc32, r, base, pageBits(value) - pageBits(pc));
82*dfe94b16Srobert     break;
83*dfe94b16Srobert   case ARM64_RELOC_PAGEOFF12:
84*dfe94b16Srobert   case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
85*dfe94b16Srobert   case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
86*dfe94b16Srobert     assert(!r.pcrel);
87*dfe94b16Srobert     encodePageOff12(loc32, r, base, value);
88*dfe94b16Srobert     break;
89*dfe94b16Srobert   default:
90*dfe94b16Srobert     llvm_unreachable("unexpected relocation type");
91*dfe94b16Srobert   }
92*dfe94b16Srobert }
93*dfe94b16Srobert 
relaxGotLoad(uint8_t * loc,uint8_t type) const941cf9926bSpatrick void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const {
951cf9926bSpatrick   // The instruction format comments below are quoted from
961cf9926bSpatrick   // Arm® Architecture Reference Manual
971cf9926bSpatrick   // Armv8, for Armv8-A architecture profile
981cf9926bSpatrick   // ARM DDI 0487G.a (ID011921)
991cf9926bSpatrick   uint32_t instruction = read32le(loc);
1001cf9926bSpatrick   // C6.2.132 LDR (immediate)
1011cf9926bSpatrick   // This matches both the 64- and 32-bit variants:
1021cf9926bSpatrick   // LDR <(X|W)t>, [<Xn|SP>{, #<pimm>}]
1031cf9926bSpatrick   if ((instruction & 0xbfc00000) != 0xb9400000)
1041cf9926bSpatrick     error(getRelocAttrs(type).name + " reloc requires LDR instruction");
1051cf9926bSpatrick   assert(((instruction >> 10) & 0xfff) == 0 &&
1061cf9926bSpatrick          "non-zero embedded LDR immediate");
1071cf9926bSpatrick   // C6.2.4 ADD (immediate)
1081cf9926bSpatrick   // ADD <Xd|SP>, <Xn|SP>, #<imm>{, <shift>}
1091cf9926bSpatrick   instruction = ((instruction & 0x001fffff) | 0x91000000);
1101cf9926bSpatrick   write32le(loc, instruction);
1111cf9926bSpatrick }
112*dfe94b16Srobert 
handleDtraceReloc(const Symbol * sym,const Reloc & r,uint8_t * loc) const113*dfe94b16Srobert void ARM64Common::handleDtraceReloc(const Symbol *sym, const Reloc &r,
114*dfe94b16Srobert                                     uint8_t *loc) const {
115*dfe94b16Srobert   assert(r.type == ARM64_RELOC_BRANCH26);
116*dfe94b16Srobert 
117*dfe94b16Srobert   if (config->outputType == MH_OBJECT)
118*dfe94b16Srobert     return;
119*dfe94b16Srobert 
120*dfe94b16Srobert   if (sym->getName().startswith("___dtrace_probe")) {
121*dfe94b16Srobert     // change call site to a NOP
122*dfe94b16Srobert     write32le(loc, 0xD503201F);
123*dfe94b16Srobert   } else if (sym->getName().startswith("___dtrace_isenabled")) {
124*dfe94b16Srobert     // change call site to 'MOVZ X0,0'
125*dfe94b16Srobert     write32le(loc, 0xD2800000);
126*dfe94b16Srobert   } else {
127*dfe94b16Srobert     error("Unrecognized dtrace symbol prefix: " + toString(*sym));
128*dfe94b16Srobert   }
129*dfe94b16Srobert }
130*dfe94b16Srobert 
reportUnalignedLdrStr(Twine loc,uint64_t va,int align,const Symbol * sym)131*dfe94b16Srobert static void reportUnalignedLdrStr(Twine loc, uint64_t va, int align,
132*dfe94b16Srobert                                   const Symbol *sym) {
133*dfe94b16Srobert   std::string symbolHint;
134*dfe94b16Srobert   if (sym)
135*dfe94b16Srobert     symbolHint = " (" + toString(*sym) + ")";
136*dfe94b16Srobert   error(loc + ": " + Twine(8 * align) + "-bit LDR/STR to 0x" +
137*dfe94b16Srobert         llvm::utohexstr(va) + symbolHint + " is not " + Twine(align) +
138*dfe94b16Srobert         "-byte aligned");
139*dfe94b16Srobert }
140*dfe94b16Srobert 
reportUnalignedLdrStr(void * loc,const lld::macho::Reloc & r,uint64_t va,int align)141*dfe94b16Srobert void macho::reportUnalignedLdrStr(void *loc, const lld::macho::Reloc &r,
142*dfe94b16Srobert                                   uint64_t va, int align) {
143*dfe94b16Srobert   uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart;
144*dfe94b16Srobert   const InputSection *isec = offsetToInputSection(&off);
145*dfe94b16Srobert   std::string locStr = isec ? isec->getLocation(off) : "(invalid location)";
146*dfe94b16Srobert   ::reportUnalignedLdrStr(locStr, va, align, r.referent.dyn_cast<Symbol *>());
147*dfe94b16Srobert }
148*dfe94b16Srobert 
reportUnalignedLdrStr(void * loc,lld::macho::SymbolDiagnostic d,uint64_t va,int align)149*dfe94b16Srobert void macho::reportUnalignedLdrStr(void *loc, lld::macho::SymbolDiagnostic d,
150*dfe94b16Srobert                                   uint64_t va, int align) {
151*dfe94b16Srobert   ::reportUnalignedLdrStr(d.reason, va, align, d.symbol);
152*dfe94b16Srobert }
153