1//===- ARM.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 "InputFiles.h"
10#include "Symbols.h"
11#include "SyntheticSections.h"
12#include "Target.h"
13
14#include "lld/Common/ErrorHandler.h"
15#include "llvm/ADT/Bitfields.h"
16#include "llvm/BinaryFormat/MachO.h"
17#include "llvm/Support/Endian.h"
18
19using namespace llvm;
20using namespace llvm::MachO;
21using namespace llvm::support::endian;
22using namespace lld;
23using namespace lld::macho;
24
25namespace {
26
27struct ARM : TargetInfo {
28 ARM(uint32_t cpuSubtype);
29
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;
34
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 DylibSymbol &,
38 uint64_t entryAddr) const override;
39
40 void relaxGotLoad(uint8_t *loc, uint8_t type) const override;
41 const RelocAttrs &getRelocAttrs(uint8_t type) const override;
42 uint64_t getPageSize() const override { return 4 * 1024; }
43};
44
45} // namespace
46
47const RelocAttrs &ARM::getRelocAttrs(uint8_t type) const {
48 static const 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)},
60#undef B
61 }};
62 assert(type < relocAttrsArray.size() && "invalid relocation type");
63 if (type >= relocAttrsArray.size())
64 return invalidRelocAttrs;
65 return relocAttrsArray[type];
66}
67
68int64_t ARM::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
69 relocation_info rel) const {
70 // FIXME: implement this
71 return 0;
72}
73
74template <int N> using BitfieldFlag = Bitfield::Element<bool, N, 1>;
75
76// ARM BL encoding:
77//
78// 30 28 24 0
79// +---------+---------+----------------------------------------------+
80// | cond | 1 0 1 1 | imm24 |
81// +---------+---------+----------------------------------------------+
82//
83// `cond` here varies depending on whether we have bleq, blne, etc.
84// `imm24` encodes a 26-bit pcrel offset -- last 2 bits are zero as ARM
85// functions are 4-byte-aligned.
86//
87// ARM BLX encoding:
88//
89// 30 28 24 0
90// +---------+---------+----------------------------------------------+
91// | 1 1 1 1 | 1 0 1 H | imm24 |
92// +---------+---------+----------------------------------------------+
93//
94// Since Thumb functions are 2-byte-aligned, we need one extra bit to encode
95// the offset -- that is the H bit.
96//
97// BLX is always unconditional, so while we can convert directly from BLX to BL,
98// we need to insert a shim if a BL's target is a Thumb function.
99//
100// Helper aliases for decoding BL / BLX:
101using Cond = Bitfield::Element<uint32_t, 28, 4>;
102using Imm24 = Bitfield::Element<int32_t, 0, 24>;
103
104void ARM::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
105 uint64_t pc) const {
106 switch (r.type) {
107 case ARM_RELOC_BR24: {
108 uint32_t base = read32le(loc);
109 bool isBlx = Bitfield::get<Cond>(base) == 0xf;
110 const Symbol *sym = r.referent.get<Symbol *>();
111 int32_t offset = value - (pc + 8);
112
113 if (auto *defined = dyn_cast<Defined>(sym)) {
114 if (!isBlx && defined->thumb) {
115 error("TODO: implement interworking shim");
116 return;
117 } else if (isBlx && !defined->thumb) {
118 Bitfield::set<Cond>(base, 0xe); // unconditional BL
119 Bitfield::set<BitfieldFlag<24>>(base, 1);
120 isBlx = false;
121 }
122 } else {
123 error("TODO: Implement ARM_RELOC_BR24 for dylib symbols");
124 return;
125 }
126
127 if (isBlx) {
128 assert((0x1 & value) == 0);
129 Bitfield::set<Imm24>(base, offset >> 2);
130 Bitfield::set<BitfieldFlag<24>>(base, (offset >> 1) & 1); // H bit
131 } else {
132 assert((0x3 & value) == 0);
133 Bitfield::set<Imm24>(base, offset >> 2);
134 }
135 write32le(loc, base);
136 break;
137 }
138 default:
139 fatal("unhandled relocation type");
140 }
141}
142
143void ARM::writeStub(uint8_t *buf, const Symbol &sym) const {
144 fatal("TODO: implement this");
145}
146
147void ARM::writeStubHelperHeader(uint8_t *buf) const {
148 fatal("TODO: implement this");
149}
150
151void ARM::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym,
152 uint64_t entryAddr) const {
153 fatal("TODO: implement this");
154}
155
156void ARM::relaxGotLoad(uint8_t *loc, uint8_t type) const {
157 fatal("TODO: implement this");
158}
159
160ARM::ARM(uint32_t cpuSubtype) : TargetInfo(ILP32()) {
161 cpuType = CPU_TYPE_ARM;
162 this->cpuSubtype = cpuSubtype;
163
164 stubSize = 0 /* FIXME */;
165 stubHelperHeaderSize = 0 /* FIXME */;
166 stubHelperEntrySize = 0 /* FIXME */;
167}
168
169TargetInfo *macho::createARMTargetInfo(uint32_t cpuSubtype) {
170 static ARM t(cpuSubtype);
171 return &t;
172}
173