xref: /freebsd-src/contrib/llvm-project/lld/MachO/Arch/ARM64Common.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
1fe6060f1SDimitry Andric //===- ARM64Common.cpp ----------------------------------------------------===//
2fe6060f1SDimitry Andric //
3fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6fe6060f1SDimitry Andric //
7fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
8fe6060f1SDimitry Andric 
9fe6060f1SDimitry Andric #include "Arch/ARM64Common.h"
10fe6060f1SDimitry Andric 
11fe6060f1SDimitry Andric #include "lld/Common/ErrorHandler.h"
12fe6060f1SDimitry Andric #include "llvm/Support/Endian.h"
13fe6060f1SDimitry Andric 
14fe6060f1SDimitry Andric using namespace llvm::MachO;
15fe6060f1SDimitry Andric using namespace llvm::support::endian;
16fe6060f1SDimitry Andric using namespace lld;
17fe6060f1SDimitry Andric using namespace lld::macho;
18fe6060f1SDimitry Andric 
getEmbeddedAddend(MemoryBufferRef mb,uint64_t offset,const relocation_info rel) const19fe6060f1SDimitry Andric int64_t ARM64Common::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
20fe6060f1SDimitry Andric                                        const relocation_info rel) const {
21fe6060f1SDimitry Andric   if (rel.r_type != ARM64_RELOC_UNSIGNED &&
22fe6060f1SDimitry Andric       rel.r_type != ARM64_RELOC_SUBTRACTOR) {
23fe6060f1SDimitry Andric     // All other reloc types should use the ADDEND relocation to store their
24fe6060f1SDimitry Andric     // addends.
25fe6060f1SDimitry Andric     // TODO(gkm): extract embedded addend just so we can assert that it is 0
26fe6060f1SDimitry Andric     return 0;
27fe6060f1SDimitry Andric   }
28fe6060f1SDimitry Andric 
29fe6060f1SDimitry Andric   const auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
30fe6060f1SDimitry Andric   const uint8_t *loc = buf + offset + rel.r_address;
31fe6060f1SDimitry Andric   switch (rel.r_length) {
32fe6060f1SDimitry Andric   case 2:
33fe6060f1SDimitry Andric     return static_cast<int32_t>(read32le(loc));
34fe6060f1SDimitry Andric   case 3:
35fe6060f1SDimitry Andric     return read64le(loc);
36fe6060f1SDimitry Andric   default:
37fe6060f1SDimitry Andric     llvm_unreachable("invalid r_length");
38fe6060f1SDimitry Andric   }
39fe6060f1SDimitry Andric }
40fe6060f1SDimitry Andric 
writeValue(uint8_t * loc,const Reloc & r,uint64_t value)4181ad6265SDimitry Andric static void writeValue(uint8_t *loc, const Reloc &r, uint64_t value) {
42fe6060f1SDimitry Andric   switch (r.length) {
43fe6060f1SDimitry Andric   case 2:
4481ad6265SDimitry Andric     checkInt(loc, r, value, 32);
45fe6060f1SDimitry Andric     write32le(loc, value);
46fe6060f1SDimitry Andric     break;
47fe6060f1SDimitry Andric   case 3:
48fe6060f1SDimitry Andric     write64le(loc, value);
49fe6060f1SDimitry Andric     break;
50fe6060f1SDimitry Andric   default:
51fe6060f1SDimitry Andric     llvm_unreachable("invalid r_length");
52fe6060f1SDimitry Andric   }
53fe6060f1SDimitry Andric }
54fe6060f1SDimitry Andric 
5581ad6265SDimitry Andric // For instruction relocations (load, store, add), the base
5681ad6265SDimitry Andric // instruction is pre-populated in the text section. A pre-populated
5781ad6265SDimitry Andric // instruction has opcode & register-operand bits set, with immediate
5881ad6265SDimitry Andric // operands zeroed. We read it from text, OR-in the immediate
5981ad6265SDimitry Andric // operands, then write-back the completed instruction.
relocateOne(uint8_t * loc,const Reloc & r,uint64_t value,uint64_t pc) const6081ad6265SDimitry Andric void ARM64Common::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
6181ad6265SDimitry Andric                               uint64_t pc) const {
6281ad6265SDimitry Andric   auto loc32 = reinterpret_cast<uint32_t *>(loc);
6381ad6265SDimitry Andric   uint32_t base = ((r.length == 2) ? read32le(loc) : 0);
6481ad6265SDimitry Andric   switch (r.type) {
6581ad6265SDimitry Andric   case ARM64_RELOC_BRANCH26:
6681ad6265SDimitry Andric     encodeBranch26(loc32, r, base, value - pc);
6781ad6265SDimitry Andric     break;
6881ad6265SDimitry Andric   case ARM64_RELOC_SUBTRACTOR:
6981ad6265SDimitry Andric   case ARM64_RELOC_UNSIGNED:
7081ad6265SDimitry Andric     writeValue(loc, r, value);
7181ad6265SDimitry Andric     break;
7281ad6265SDimitry Andric   case ARM64_RELOC_POINTER_TO_GOT:
7381ad6265SDimitry Andric     if (r.pcrel)
7481ad6265SDimitry Andric       value -= pc;
7581ad6265SDimitry Andric     writeValue(loc, r, value);
7681ad6265SDimitry Andric     break;
7781ad6265SDimitry Andric   case ARM64_RELOC_PAGE21:
7881ad6265SDimitry Andric   case ARM64_RELOC_GOT_LOAD_PAGE21:
7981ad6265SDimitry Andric   case ARM64_RELOC_TLVP_LOAD_PAGE21:
8081ad6265SDimitry Andric     assert(r.pcrel);
8181ad6265SDimitry Andric     encodePage21(loc32, r, base, pageBits(value) - pageBits(pc));
8281ad6265SDimitry Andric     break;
8381ad6265SDimitry Andric   case ARM64_RELOC_PAGEOFF12:
8481ad6265SDimitry Andric   case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
8581ad6265SDimitry Andric   case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
8681ad6265SDimitry Andric     assert(!r.pcrel);
87bdd1243dSDimitry Andric     encodePageOff12(loc32, r, base, value);
8881ad6265SDimitry Andric     break;
8981ad6265SDimitry Andric   default:
9081ad6265SDimitry Andric     llvm_unreachable("unexpected relocation type");
9181ad6265SDimitry Andric   }
9281ad6265SDimitry Andric }
9381ad6265SDimitry Andric 
relaxGotLoad(uint8_t * loc,uint8_t type) const94fe6060f1SDimitry Andric void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const {
95fe6060f1SDimitry Andric   // The instruction format comments below are quoted from
96fe6060f1SDimitry Andric   // Arm® Architecture Reference Manual
97fe6060f1SDimitry Andric   // Armv8, for Armv8-A architecture profile
98fe6060f1SDimitry Andric   // ARM DDI 0487G.a (ID011921)
99fe6060f1SDimitry Andric   uint32_t instruction = read32le(loc);
100fe6060f1SDimitry Andric   // C6.2.132 LDR (immediate)
101fe6060f1SDimitry Andric   // This matches both the 64- and 32-bit variants:
102fe6060f1SDimitry Andric   // LDR <(X|W)t>, [<Xn|SP>{, #<pimm>}]
103fe6060f1SDimitry Andric   if ((instruction & 0xbfc00000) != 0xb9400000)
104fe6060f1SDimitry Andric     error(getRelocAttrs(type).name + " reloc requires LDR instruction");
105fe6060f1SDimitry Andric   assert(((instruction >> 10) & 0xfff) == 0 &&
106fe6060f1SDimitry Andric          "non-zero embedded LDR immediate");
107fe6060f1SDimitry Andric   // C6.2.4 ADD (immediate)
108fe6060f1SDimitry Andric   // ADD <Xd|SP>, <Xn|SP>, #<imm>{, <shift>}
109fe6060f1SDimitry Andric   instruction = ((instruction & 0x001fffff) | 0x91000000);
110fe6060f1SDimitry Andric   write32le(loc, instruction);
111fe6060f1SDimitry Andric }
112753f127fSDimitry Andric 
handleDtraceReloc(const Symbol * sym,const Reloc & r,uint8_t * loc) const113753f127fSDimitry Andric void ARM64Common::handleDtraceReloc(const Symbol *sym, const Reloc &r,
114753f127fSDimitry Andric                                     uint8_t *loc) const {
115753f127fSDimitry Andric   assert(r.type == ARM64_RELOC_BRANCH26);
116753f127fSDimitry Andric 
117753f127fSDimitry Andric   if (config->outputType == MH_OBJECT)
118753f127fSDimitry Andric     return;
119753f127fSDimitry Andric 
120*06c3fb27SDimitry Andric   if (sym->getName().starts_with("___dtrace_probe")) {
121753f127fSDimitry Andric     // change call site to a NOP
122753f127fSDimitry Andric     write32le(loc, 0xD503201F);
123*06c3fb27SDimitry Andric   } else if (sym->getName().starts_with("___dtrace_isenabled")) {
124753f127fSDimitry Andric     // change call site to 'MOVZ X0,0'
125753f127fSDimitry Andric     write32le(loc, 0xD2800000);
126753f127fSDimitry Andric   } else {
127753f127fSDimitry Andric     error("Unrecognized dtrace symbol prefix: " + toString(*sym));
128753f127fSDimitry Andric   }
129753f127fSDimitry Andric }
130bdd1243dSDimitry Andric 
reportUnalignedLdrStr(Twine loc,uint64_t va,int align,const Symbol * sym)131bdd1243dSDimitry Andric static void reportUnalignedLdrStr(Twine loc, uint64_t va, int align,
132bdd1243dSDimitry Andric                                   const Symbol *sym) {
133bdd1243dSDimitry Andric   std::string symbolHint;
134bdd1243dSDimitry Andric   if (sym)
135bdd1243dSDimitry Andric     symbolHint = " (" + toString(*sym) + ")";
136bdd1243dSDimitry Andric   error(loc + ": " + Twine(8 * align) + "-bit LDR/STR to 0x" +
137bdd1243dSDimitry Andric         llvm::utohexstr(va) + symbolHint + " is not " + Twine(align) +
138bdd1243dSDimitry Andric         "-byte aligned");
139bdd1243dSDimitry Andric }
140bdd1243dSDimitry Andric 
reportUnalignedLdrStr(void * loc,const lld::macho::Reloc & r,uint64_t va,int align)141bdd1243dSDimitry Andric void macho::reportUnalignedLdrStr(void *loc, const lld::macho::Reloc &r,
142bdd1243dSDimitry Andric                                   uint64_t va, int align) {
143bdd1243dSDimitry Andric   uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart;
144bdd1243dSDimitry Andric   const InputSection *isec = offsetToInputSection(&off);
145bdd1243dSDimitry Andric   std::string locStr = isec ? isec->getLocation(off) : "(invalid location)";
146bdd1243dSDimitry Andric   ::reportUnalignedLdrStr(locStr, va, align, r.referent.dyn_cast<Symbol *>());
147bdd1243dSDimitry Andric }
148bdd1243dSDimitry Andric 
reportUnalignedLdrStr(void * loc,lld::macho::SymbolDiagnostic d,uint64_t va,int align)149bdd1243dSDimitry Andric void macho::reportUnalignedLdrStr(void *loc, lld::macho::SymbolDiagnostic d,
150bdd1243dSDimitry Andric                                   uint64_t va, int align) {
151bdd1243dSDimitry Andric   ::reportUnalignedLdrStr(d.reason, va, align, d.symbol);
152bdd1243dSDimitry Andric }
153