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(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
47void 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
93void 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