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 | |
14 | using namespace llvm::MachO; |
15 | using namespace llvm::support::endian; |
16 | using namespace lld; |
17 | using namespace lld::macho; |
18 | |
19 | int64_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 | |
41 | static 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. |
60 | void 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 | |
94 | void 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 | |
113 | void 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 | |
131 | static 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 | |
141 | void 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 | |
149 | void 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 | |