1//===- ARM64Common.cpp ----------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "Arch/ARM64Common.h"
10
11#include "lld/Common/ErrorHandler.h"
12#include "llvm/Support/Endian.h"
13
14using namespace llvm::MachO;
15using namespace llvm::support::endian;
16using namespace lld;
17using namespace lld::macho;
18
19int64_t ARM64Common::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
20 const relocation_info rel) const {
21 if (rel.r_type != ARM64_RELOC_UNSIGNED &&
22 rel.r_type != ARM64_RELOC_SUBTRACTOR) {
23 // All other reloc types should use the ADDEND relocation to store their
24 // addends.
25 // TODO(gkm): extract embedded addend just so we can assert that it is 0
26 return 0;
27 }
28
29 const auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
30 const uint8_t *loc = buf + offset + rel.r_address;
31 switch (rel.r_length) {
32 case 2:
33 return static_cast<int32_t>(read32le(P: loc));
34 case 3:
35 return read64le(P: loc);
36 default:
37 llvm_unreachable("invalid r_length");
38 }
39}
40
41static void writeValue(uint8_t *loc, const Reloc &r, uint64_t value) {
42 switch (r.length) {
43 case 2:
44 checkInt(loc, d: r, v: value, bits: 32);
45 write32le(P: loc, V: value);
46 break;
47 case 3:
48 write64le(P: loc, V: value);
49 break;
50 default:
51 llvm_unreachable("invalid r_length");
52 }
53}
54
55// For instruction relocations (load, store, add), the base
56// instruction is pre-populated in the text section. A pre-populated
57// instruction has opcode & register-operand bits set, with immediate
58// operands zeroed. We read it from text, OR-in the immediate
59// operands, then write-back the completed instruction.
60void ARM64Common::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
61 uint64_t pc) const {
62 auto loc32 = reinterpret_cast<uint32_t *>(loc);
63 uint32_t base = ((r.length == 2) ? read32le(P: loc) : 0);
64 switch (r.type) {
65 case ARM64_RELOC_BRANCH26:
66 encodeBranch26(loc: loc32, r, base, va: value - pc);
67 break;
68 case ARM64_RELOC_SUBTRACTOR:
69 case ARM64_RELOC_UNSIGNED:
70 writeValue(loc, r, value);
71 break;
72 case ARM64_RELOC_POINTER_TO_GOT:
73 if (r.pcrel)
74 value -= pc;
75 writeValue(loc, r, value);
76 break;
77 case ARM64_RELOC_PAGE21:
78 case ARM64_RELOC_GOT_LOAD_PAGE21:
79 case ARM64_RELOC_TLVP_LOAD_PAGE21:
80 assert(r.pcrel);
81 encodePage21(loc: loc32, r, base, va: pageBits(address: value) - pageBits(address: pc));
82 break;
83 case ARM64_RELOC_PAGEOFF12:
84 case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
85 case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
86 assert(!r.pcrel);
87 encodePageOff12(loc: loc32, t: r, base, va: value);
88 break;
89 default:
90 llvm_unreachable("unexpected relocation type");
91 }
92}
93
94void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const {
95 // The instruction format comments below are quoted from
96 // ArmĀ® Architecture Reference Manual
97 // Armv8, for Armv8-A architecture profile
98 // ARM DDI 0487G.a (ID011921)
99 uint32_t instruction = read32le(P: loc);
100 // C6.2.132 LDR (immediate)
101 // This matches both the 64- and 32-bit variants:
102 // LDR <(X|W)t>, [<Xn|SP>{, #<pimm>}]
103 if ((instruction & 0xbfc00000) != 0xb9400000)
104 error(msg: getRelocAttrs(type).name + " reloc requires LDR instruction");
105 assert(((instruction >> 10) & 0xfff) == 0 &&
106 "non-zero embedded LDR immediate");
107 // C6.2.4 ADD (immediate)
108 // ADD <Xd|SP>, <Xn|SP>, #<imm>{, <shift>}
109 instruction = ((instruction & 0x001fffff) | 0x91000000);
110 write32le(P: loc, V: instruction);
111}
112
113void ARM64Common::handleDtraceReloc(const Symbol *sym, const Reloc &r,
114 uint8_t *loc) const {
115 assert(r.type == ARM64_RELOC_BRANCH26);
116
117 if (config->outputType == MH_OBJECT)
118 return;
119
120 if (sym->getName().starts_with(Prefix: "___dtrace_probe")) {
121 // change call site to a NOP
122 write32le(P: loc, V: 0xD503201F);
123 } else if (sym->getName().starts_with(Prefix: "___dtrace_isenabled")) {
124 // change call site to 'MOVZ X0,0'
125 write32le(P: loc, V: 0xD2800000);
126 } else {
127 error(msg: "Unrecognized dtrace symbol prefix: " + toString(*sym));
128 }
129}
130
131static void reportUnalignedLdrStr(Twine loc, uint64_t va, int align,
132 const Symbol *sym) {
133 std::string symbolHint;
134 if (sym)
135 symbolHint = " (" + toString(*sym) + ")";
136 error(msg: loc + ": " + Twine(8 * align) + "-bit LDR/STR to 0x" +
137 llvm::utohexstr(X: va) + symbolHint + " is not " + Twine(align) +
138 "-byte aligned");
139}
140
141void macho::reportUnalignedLdrStr(void *loc, const lld::macho::Reloc &r,
142 uint64_t va, int align) {
143 uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart;
144 const InputSection *isec = offsetToInputSection(&off);
145 std::string locStr = isec ? isec->getLocation(off) : "(invalid location)";
146 ::reportUnalignedLdrStr(loc: locStr, va, align, sym: r.referent.dyn_cast<Symbol *>());
147}
148
149void macho::reportUnalignedLdrStr(void *loc, lld::macho::SymbolDiagnostic d,
150 uint64_t va, int align) {
151 ::reportUnalignedLdrStr(loc: d.reason, va, align, sym: d.symbol);
152}
153