1 //===- ARM.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 "InputFiles.h"
11 #include "SyntheticSections.h"
14 #include "lld/Common/ErrorHandler.h"
15 #include "llvm/ADT/Bitfields.h"
16 #include "llvm/BinaryFormat/MachO.h"
17 #include "llvm/Support/Endian.h"
20 using namespace llvm::MachO
;
21 using namespace llvm::support::endian
;
23 using namespace lld::macho
;
27 struct ARM
: TargetInfo
{
28 ARM(uint32_t cpuSubtype
);
30 int64_t getEmbeddedAddend(MemoryBufferRef
, uint64_t offset
,
31 const relocation_info
) const override
;
32 void relocateOne(uint8_t *loc
, const Reloc
&, uint64_t va
,
33 uint64_t pc
) const override
;
35 void writeStub(uint8_t *buf
, const Symbol
&) const override
;
36 void writeStubHelperHeader(uint8_t *buf
) const override
;
37 void writeStubHelperEntry(uint8_t *buf
, const Symbol
&,
38 uint64_t entryAddr
) const override
;
40 void relaxGotLoad(uint8_t *loc
, uint8_t type
) const override
;
41 uint64_t getPageSize() const override
{ return 4 * 1024; }
43 void handleDtraceReloc(const Symbol
*sym
, const Reloc
&r
,
44 uint8_t *loc
) const override
;
48 static constexpr std::array
<RelocAttrs
, 10> relocAttrsArray
{{
49 #define B(x) RelocAttrBits::x
50 {"VANILLA", /* FIXME populate this */ B(_0
)},
51 {"PAIR", /* FIXME populate this */ B(_0
)},
52 {"SECTDIFF", /* FIXME populate this */ B(_0
)},
53 {"LOCAL_SECTDIFF", /* FIXME populate this */ B(_0
)},
54 {"PB_LA_PTR", /* FIXME populate this */ B(_0
)},
55 {"BR24", B(PCREL
) | B(LOCAL
) | B(EXTERN
) | B(BRANCH
) | B(BYTE4
)},
56 {"BR22", B(PCREL
) | B(LOCAL
) | B(EXTERN
) | B(BRANCH
) | B(BYTE4
)},
57 {"32BIT_BRANCH", /* FIXME populate this */ B(_0
)},
58 {"HALF", /* FIXME populate this */ B(_0
)},
59 {"HALF_SECTDIFF", /* FIXME populate this */ B(_0
)},
63 int64_t ARM::getEmbeddedAddend(MemoryBufferRef mb
, uint64_t offset
,
64 relocation_info rel
) const {
65 // FIXME: implement this
69 template <int N
> using BitfieldFlag
= Bitfield::Element
<bool, N
, 1>;
74 // +---------+---------+----------------------------------------------+
75 // | cond | 1 0 1 1 | imm24 |
76 // +---------+---------+----------------------------------------------+
78 // `cond` here varies depending on whether we have bleq, blne, etc.
79 // `imm24` encodes a 26-bit pcrel offset -- last 2 bits are zero as ARM
80 // functions are 4-byte-aligned.
85 // +---------+---------+----------------------------------------------+
86 // | 1 1 1 1 | 1 0 1 H | imm24 |
87 // +---------+---------+----------------------------------------------+
89 // Since Thumb functions are 2-byte-aligned, we need one extra bit to encode
90 // the offset -- that is the H bit.
92 // BLX is always unconditional, so while we can convert directly from BLX to BL,
93 // we need to insert a shim if a BL's target is a Thumb function.
95 // Helper aliases for decoding BL / BLX:
96 using Cond
= Bitfield::Element
<uint32_t, 28, 4>;
97 using Imm24
= Bitfield::Element
<int32_t, 0, 24>;
99 void ARM::relocateOne(uint8_t *loc
, const Reloc
&r
, uint64_t value
,
102 case ARM_RELOC_BR24
: {
103 uint32_t base
= read32le(loc
);
104 bool isBlx
= Bitfield::get
<Cond
>(base
) == 0xf;
105 const Symbol
*sym
= r
.referent
.get
<Symbol
*>();
106 int32_t offset
= value
- (pc
+ 8);
108 if (auto *defined
= dyn_cast
<Defined
>(sym
)) {
109 if (!isBlx
&& defined
->thumb
) {
110 error("TODO: implement interworking shim");
112 } else if (isBlx
&& !defined
->thumb
) {
113 Bitfield::set
<Cond
>(base
, 0xe); // unconditional BL
114 Bitfield::set
<BitfieldFlag
<24>>(base
, true);
118 error("TODO: Implement ARM_RELOC_BR24 for dylib symbols");
123 assert((0x1 & value
) == 0);
124 Bitfield::set
<Imm24
>(base
, offset
>> 2);
125 Bitfield::set
<BitfieldFlag
<24>>(base
, (offset
>> 1) & 1); // H bit
127 assert((0x3 & value
) == 0);
128 Bitfield::set
<Imm24
>(base
, offset
>> 2);
130 write32le(loc
, base
);
134 fatal("unhandled relocation type");
138 void ARM::writeStub(uint8_t *buf
, const Symbol
&sym
) const {
139 fatal("TODO: implement this");
142 void ARM::writeStubHelperHeader(uint8_t *buf
) const {
143 fatal("TODO: implement this");
146 void ARM::writeStubHelperEntry(uint8_t *buf
, const Symbol
&sym
,
147 uint64_t entryAddr
) const {
148 fatal("TODO: implement this");
151 void ARM::relaxGotLoad(uint8_t *loc
, uint8_t type
) const {
152 fatal("TODO: implement this");
155 ARM::ARM(uint32_t cpuSubtype
) : TargetInfo(ILP32()) {
156 cpuType
= CPU_TYPE_ARM
;
157 this->cpuSubtype
= cpuSubtype
;
159 stubSize
= 0 /* FIXME */;
160 stubHelperHeaderSize
= 0 /* FIXME */;
161 stubHelperEntrySize
= 0 /* FIXME */;
163 relocAttrs
= {relocAttrsArray
.data(), relocAttrsArray
.size()};
166 TargetInfo
*macho::createARMTargetInfo(uint32_t cpuSubtype
) {
167 static ARM
t(cpuSubtype
);
171 void ARM::handleDtraceReloc(const Symbol
*sym
, const Reloc
&r
,
172 uint8_t *loc
) const {
173 if (config
->outputType
== MH_OBJECT
)
178 if (sym
->getName().startswith("___dtrace_probe")) {
179 // change call site to a NOP
180 write32le(loc
, 0xE1A00000);
181 } else if (sym
->getName().startswith("___dtrace_isenabled")) {
182 // change call site to 'eor r0, r0, r0'
183 write32le(loc
, 0xE0200000);
185 error("Unrecognized dtrace symbol prefix: " + toString(*sym
));
188 case ARM_THUMB_RELOC_BR22
:
189 if (sym
->getName().startswith("___dtrace_probe")) {
190 // change 32-bit blx call site to two thumb NOPs
191 write32le(loc
, 0x46C046C0);
192 } else if (sym
->getName().startswith("___dtrace_isenabled")) {
193 // change 32-bit blx call site to 'nop', 'eor r0, r0'
194 write32le(loc
, 0x46C04040);
196 error("Unrecognized dtrace symbol prefix: " + toString(*sym
));
200 llvm_unreachable("Unsupported dtrace relocation type for ARM");