xref: /openbsd-src/gnu/llvm/lld/MachO/Arch/ARM.cpp (revision dfe94b169149f14cc1aee2cf6dad58a8d9a1860c)
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 
19 using namespace llvm;
20 using namespace llvm::MachO;
21 using namespace llvm::support::endian;
22 using namespace lld;
23 using namespace lld::macho;
24 
25 namespace {
26 
27 struct 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 &, uint64_t) const override;
36   void writeStubHelperHeader(uint8_t *buf) const override;
37   void writeStubHelperEntry(uint8_t *buf, const Symbol &,
38                             uint64_t entryAddr) const override;
39 
40   void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
41                             uint64_t stubOffset, uint64_t selrefsVA,
42                             uint64_t selectorIndex, uint64_t gotAddr,
43                             uint64_t msgSendIndex) const override;
44 
45   void relaxGotLoad(uint8_t *loc, uint8_t type) const override;
getPageSize__anon504094860111::ARM46   uint64_t getPageSize() const override { return 4 * 1024; }
47 
48   void handleDtraceReloc(const Symbol *sym, const Reloc &r,
49                          uint8_t *loc) const override;
50 };
51 } // namespace
52 
53 static constexpr std::array<RelocAttrs, 10> relocAttrsArray{{
54 #define B(x) RelocAttrBits::x
55     {"VANILLA", /* FIXME populate this */ B(_0)},
56     {"PAIR", /* FIXME populate this */ B(_0)},
57     {"SECTDIFF", /* FIXME populate this */ B(_0)},
58     {"LOCAL_SECTDIFF", /* FIXME populate this */ B(_0)},
59     {"PB_LA_PTR", /* FIXME populate this */ B(_0)},
60     {"BR24", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
61     {"BR22", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
62     {"32BIT_BRANCH", /* FIXME populate this */ B(_0)},
63     {"HALF", /* FIXME populate this */ B(_0)},
64     {"HALF_SECTDIFF", /* FIXME populate this */ B(_0)},
65 #undef B
66 }};
67 
getEmbeddedAddend(MemoryBufferRef mb,uint64_t offset,relocation_info rel) const68 int64_t ARM::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
69                                relocation_info rel) const {
70   // FIXME: implement this
71   return 0;
72 }
73 
74 template <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:
101 using Cond = Bitfield::Element<uint32_t, 28, 4>;
102 using Imm24 = Bitfield::Element<int32_t, 0, 24>;
103 
relocateOne(uint8_t * loc,const Reloc & r,uint64_t value,uint64_t pc) const104 void 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, true);
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 
writeStub(uint8_t * buf,const Symbol & sym,uint64_t) const143 void ARM::writeStub(uint8_t *buf, const Symbol &sym, uint64_t) const {
144   fatal("TODO: implement this");
145 }
146 
writeStubHelperHeader(uint8_t * buf) const147 void ARM::writeStubHelperHeader(uint8_t *buf) const {
148   fatal("TODO: implement this");
149 }
150 
writeStubHelperEntry(uint8_t * buf,const Symbol & sym,uint64_t entryAddr) const151 void ARM::writeStubHelperEntry(uint8_t *buf, const Symbol &sym,
152                                uint64_t entryAddr) const {
153   fatal("TODO: implement this");
154 }
155 
writeObjCMsgSendStub(uint8_t * buf,Symbol * sym,uint64_t stubsAddr,uint64_t stubOffset,uint64_t selrefsVA,uint64_t selectorIndex,uint64_t gotAddr,uint64_t msgSendIndex) const156 void ARM::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
157                                uint64_t stubOffset, uint64_t selrefsVA,
158                                uint64_t selectorIndex, uint64_t gotAddr,
159                                uint64_t msgSendIndex) const {
160   fatal("TODO: implement this");
161 }
162 
relaxGotLoad(uint8_t * loc,uint8_t type) const163 void ARM::relaxGotLoad(uint8_t *loc, uint8_t type) const {
164   fatal("TODO: implement this");
165 }
166 
ARM(uint32_t cpuSubtype)167 ARM::ARM(uint32_t cpuSubtype) : TargetInfo(ILP32()) {
168   cpuType = CPU_TYPE_ARM;
169   this->cpuSubtype = cpuSubtype;
170 
171   stubSize = 0 /* FIXME */;
172   stubHelperHeaderSize = 0 /* FIXME */;
173   stubHelperEntrySize = 0 /* FIXME */;
174 
175   relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()};
176 }
177 
createARMTargetInfo(uint32_t cpuSubtype)178 TargetInfo *macho::createARMTargetInfo(uint32_t cpuSubtype) {
179   static ARM t(cpuSubtype);
180   return &t;
181 }
182 
handleDtraceReloc(const Symbol * sym,const Reloc & r,uint8_t * loc) const183 void ARM::handleDtraceReloc(const Symbol *sym, const Reloc &r,
184                             uint8_t *loc) const {
185   if (config->outputType == MH_OBJECT)
186     return;
187 
188   switch (r.type) {
189   case ARM_RELOC_BR24:
190     if (sym->getName().startswith("___dtrace_probe")) {
191       // change call site to a NOP
192       write32le(loc, 0xE1A00000);
193     } else if (sym->getName().startswith("___dtrace_isenabled")) {
194       // change call site to 'eor r0, r0, r0'
195       write32le(loc, 0xE0200000);
196     } else {
197       error("Unrecognized dtrace symbol prefix: " + toString(*sym));
198     }
199     break;
200   case ARM_THUMB_RELOC_BR22:
201     if (sym->getName().startswith("___dtrace_probe")) {
202       // change 32-bit blx call site to two thumb NOPs
203       write32le(loc, 0x46C046C0);
204     } else if (sym->getName().startswith("___dtrace_isenabled")) {
205       // change 32-bit blx call site to 'nop', 'eor r0, r0'
206       write32le(loc, 0x46C04040);
207     } else {
208       error("Unrecognized dtrace symbol prefix: " + toString(*sym));
209     }
210     break;
211   default:
212     llvm_unreachable("Unsupported dtrace relocation type for ARM");
213   }
214 }
215