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