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(loc)); |
34 | case 3: |
35 | return read64le(loc); |
36 | default: |
37 | llvm_unreachable("invalid r_length" ); |
38 | } |
39 | } |
40 | |
41 | // For instruction relocations (load, store, add), the base |
42 | // instruction is pre-populated in the text section. A pre-populated |
43 | // instruction has opcode & register-operand bits set, with immediate |
44 | // operands zeroed. We read it from text, OR-in the immediate |
45 | // operands, then write-back the completed instruction. |
46 | |
47 | void ARM64Common::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value, |
48 | uint64_t pc) const { |
49 | uint32_t base = ((r.length == 2) ? read32le(loc) : 0); |
50 | switch (r.type) { |
51 | case ARM64_RELOC_BRANCH26: |
52 | value = encodeBranch26(r, base, value - pc); |
53 | break; |
54 | case ARM64_RELOC_SUBTRACTOR: |
55 | case ARM64_RELOC_UNSIGNED: |
56 | if (r.length == 2) |
57 | checkInt(r, value, 32); |
58 | break; |
59 | case ARM64_RELOC_POINTER_TO_GOT: |
60 | if (r.pcrel) |
61 | value -= pc; |
62 | checkInt(r, value, 32); |
63 | break; |
64 | case ARM64_RELOC_PAGE21: |
65 | case ARM64_RELOC_GOT_LOAD_PAGE21: |
66 | case ARM64_RELOC_TLVP_LOAD_PAGE21: { |
67 | assert(r.pcrel); |
68 | value = encodePage21(r, base, pageBits(value) - pageBits(pc)); |
69 | break; |
70 | } |
71 | case ARM64_RELOC_PAGEOFF12: |
72 | case ARM64_RELOC_GOT_LOAD_PAGEOFF12: |
73 | case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: |
74 | assert(!r.pcrel); |
75 | value = encodePageOff12(base, value); |
76 | break; |
77 | default: |
78 | llvm_unreachable("unexpected relocation type" ); |
79 | } |
80 | |
81 | switch (r.length) { |
82 | case 2: |
83 | write32le(loc, value); |
84 | break; |
85 | case 3: |
86 | write64le(loc, value); |
87 | break; |
88 | default: |
89 | llvm_unreachable("invalid r_length" ); |
90 | } |
91 | } |
92 | |
93 | void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const { |
94 | // The instruction format comments below are quoted from |
95 | // ArmĀ® Architecture Reference Manual |
96 | // Armv8, for Armv8-A architecture profile |
97 | // ARM DDI 0487G.a (ID011921) |
98 | uint32_t instruction = read32le(loc); |
99 | // C6.2.132 LDR (immediate) |
100 | // This matches both the 64- and 32-bit variants: |
101 | // LDR <(X|W)t>, [<Xn|SP>{, #<pimm>}] |
102 | if ((instruction & 0xbfc00000) != 0xb9400000) |
103 | error(getRelocAttrs(type).name + " reloc requires LDR instruction" ); |
104 | assert(((instruction >> 10) & 0xfff) == 0 && |
105 | "non-zero embedded LDR immediate" ); |
106 | // C6.2.4 ADD (immediate) |
107 | // ADD <Xd|SP>, <Xn|SP>, #<imm>{, <shift>} |
108 | instruction = ((instruction & 0x001fffff) | 0x91000000); |
109 | write32le(loc, instruction); |
110 | } |
111 | |