1 //===- ARM64Common.cpp ----------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "Arch/ARM64Common.h"
11 #include "lld/Common/ErrorHandler.h"
12 #include "llvm/Support/Endian.h"
14 using namespace llvm::MachO
;
15 using namespace llvm::support::endian
;
17 using namespace lld::macho
;
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
25 // TODO(gkm): extract embedded addend just so we can assert that it is 0
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
) {
33 return static_cast<int32_t>(read32le(loc
));
37 llvm_unreachable("invalid r_length");
41 static void writeValue(uint8_t *loc
, const Reloc
&r
, uint64_t value
) {
44 checkInt(loc
, r
, value
, 32);
45 write32le(loc
, value
);
48 write64le(loc
, value
);
51 llvm_unreachable("invalid r_length");
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
,
62 auto loc32
= reinterpret_cast<uint32_t *>(loc
);
63 uint32_t base
= ((r
.length
== 2) ? read32le(loc
) : 0);
65 case ARM64_RELOC_BRANCH26
:
66 encodeBranch26(loc32
, r
, base
, value
- pc
);
68 case ARM64_RELOC_SUBTRACTOR
:
69 case ARM64_RELOC_UNSIGNED
:
70 writeValue(loc
, r
, value
);
72 case ARM64_RELOC_POINTER_TO_GOT
:
75 writeValue(loc
, r
, value
);
77 case ARM64_RELOC_PAGE21
:
78 case ARM64_RELOC_GOT_LOAD_PAGE21
:
79 case ARM64_RELOC_TLVP_LOAD_PAGE21
:
81 encodePage21(loc32
, r
, base
, pageBits(value
) - pageBits(pc
));
83 case ARM64_RELOC_PAGEOFF12
:
84 case ARM64_RELOC_GOT_LOAD_PAGEOFF12
:
85 case ARM64_RELOC_TLVP_LOAD_PAGEOFF12
:
87 encodePageOff12(loc32
, r
, base
, value
);
90 llvm_unreachable("unexpected relocation type");
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(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(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(loc
, instruction
);
113 void ARM64Common::handleDtraceReloc(const Symbol
*sym
, const Reloc
&r
,
114 uint8_t *loc
) const {
115 assert(r
.type
== ARM64_RELOC_BRANCH26
);
117 if (config
->outputType
== MH_OBJECT
)
120 if (sym
->getName().starts_with("___dtrace_probe")) {
121 // change call site to a NOP
122 write32le(loc
, 0xD503201F);
123 } else if (sym
->getName().starts_with("___dtrace_isenabled")) {
124 // change call site to 'MOVZ X0,0'
125 write32le(loc
, 0xD2800000);
127 error("Unrecognized dtrace symbol prefix: " + toString(*sym
));
131 static void reportUnalignedLdrStr(Twine loc
, uint64_t va
, int align
,
133 std::string symbolHint
;
135 symbolHint
= " (" + toString(*sym
) + ")";
136 error(loc
+ ": " + Twine(8 * align
) + "-bit LDR/STR to 0x" +
137 llvm::utohexstr(va
) + symbolHint
+ " is not " + Twine(align
) +
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(locStr
, va
, align
, r
.referent
.dyn_cast
<Symbol
*>());
149 void macho::reportUnalignedLdrStr(void *loc
, lld::macho::SymbolDiagnostic d
,
150 uint64_t va
, int align
) {
151 ::reportUnalignedLdrStr(d
.reason
, va
, align
, d
.symbol
);