xref: /llvm-project-15.0.7/lld/MachO/Arch/ARM.cpp (revision bcae3cdb)
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 &) 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 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   void handleDtraceReloc(const Symbol *sym, const Reloc &r,
45                          uint8_t *loc) const override;
46 };
47 
48 } // namespace
49 
50 const RelocAttrs &ARM::getRelocAttrs(uint8_t type) const {
51   static const std::array<RelocAttrs, 10> relocAttrsArray{{
52 #define B(x) RelocAttrBits::x
53       {"VANILLA", /* FIXME populate this */ B(_0)},
54       {"PAIR", /* FIXME populate this */ B(_0)},
55       {"SECTDIFF", /* FIXME populate this */ B(_0)},
56       {"LOCAL_SECTDIFF", /* FIXME populate this */ B(_0)},
57       {"PB_LA_PTR", /* FIXME populate this */ B(_0)},
58       {"BR24", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
59       {"BR22", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
60       {"32BIT_BRANCH", /* FIXME populate this */ B(_0)},
61       {"HALF", /* FIXME populate this */ B(_0)},
62       {"HALF_SECTDIFF", /* FIXME populate this */ B(_0)},
63 #undef B
64   }};
65   assert(type < relocAttrsArray.size() && "invalid relocation type");
66   if (type >= relocAttrsArray.size())
67     return invalidRelocAttrs;
68   return relocAttrsArray[type];
69 }
70 
71 int64_t ARM::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
72                                relocation_info rel) const {
73   // FIXME: implement this
74   return 0;
75 }
76 
77 template <int N> using BitfieldFlag = Bitfield::Element<bool, N, 1>;
78 
79 // ARM BL encoding:
80 //
81 // 30       28        24                                              0
82 // +---------+---------+----------------------------------------------+
83 // |  cond   | 1 0 1 1 |                  imm24                       |
84 // +---------+---------+----------------------------------------------+
85 //
86 // `cond` here varies depending on whether we have bleq, blne, etc.
87 // `imm24` encodes a 26-bit pcrel offset -- last 2 bits are zero as ARM
88 // functions are 4-byte-aligned.
89 //
90 // ARM BLX encoding:
91 //
92 // 30       28        24                                              0
93 // +---------+---------+----------------------------------------------+
94 // | 1 1 1 1 | 1 0 1 H |                  imm24                       |
95 // +---------+---------+----------------------------------------------+
96 //
97 // Since Thumb functions are 2-byte-aligned, we need one extra bit to encode
98 // the offset -- that is the H bit.
99 //
100 // BLX is always unconditional, so while we can convert directly from BLX to BL,
101 // we need to insert a shim if a BL's target is a Thumb function.
102 //
103 // Helper aliases for decoding BL / BLX:
104 using Cond = Bitfield::Element<uint32_t, 28, 4>;
105 using Imm24 = Bitfield::Element<int32_t, 0, 24>;
106 
107 void ARM::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
108                       uint64_t pc) const {
109   switch (r.type) {
110   case ARM_RELOC_BR24: {
111     uint32_t base = read32le(loc);
112     bool isBlx = Bitfield::get<Cond>(base) == 0xf;
113     const Symbol *sym = r.referent.get<Symbol *>();
114     int32_t offset = value - (pc + 8);
115 
116     if (auto *defined = dyn_cast<Defined>(sym)) {
117       if (!isBlx && defined->thumb) {
118         error("TODO: implement interworking shim");
119         return;
120       } else if (isBlx && !defined->thumb) {
121         Bitfield::set<Cond>(base, 0xe); // unconditional BL
122         Bitfield::set<BitfieldFlag<24>>(base, true);
123         isBlx = false;
124       }
125     } else {
126       error("TODO: Implement ARM_RELOC_BR24 for dylib symbols");
127       return;
128     }
129 
130     if (isBlx) {
131       assert((0x1 & value) == 0);
132       Bitfield::set<Imm24>(base, offset >> 2);
133       Bitfield::set<BitfieldFlag<24>>(base, (offset >> 1) & 1); // H bit
134     } else {
135       assert((0x3 & value) == 0);
136       Bitfield::set<Imm24>(base, offset >> 2);
137     }
138     write32le(loc, base);
139     break;
140   }
141   default:
142     fatal("unhandled relocation type");
143   }
144 }
145 
146 void ARM::writeStub(uint8_t *buf, const Symbol &sym) const {
147   fatal("TODO: implement this");
148 }
149 
150 void ARM::writeStubHelperHeader(uint8_t *buf) const {
151   fatal("TODO: implement this");
152 }
153 
154 void ARM::writeStubHelperEntry(uint8_t *buf, const Symbol &sym,
155                                uint64_t entryAddr) const {
156   fatal("TODO: implement this");
157 }
158 
159 void ARM::relaxGotLoad(uint8_t *loc, uint8_t type) const {
160   fatal("TODO: implement this");
161 }
162 
163 ARM::ARM(uint32_t cpuSubtype) : TargetInfo(ILP32()) {
164   cpuType = CPU_TYPE_ARM;
165   this->cpuSubtype = cpuSubtype;
166 
167   stubSize = 0 /* FIXME */;
168   stubHelperHeaderSize = 0 /* FIXME */;
169   stubHelperEntrySize = 0 /* FIXME */;
170 }
171 
172 TargetInfo *macho::createARMTargetInfo(uint32_t cpuSubtype) {
173   static ARM t(cpuSubtype);
174   return &t;
175 }
176 
177 void ARM::handleDtraceReloc(const Symbol *sym, const Reloc &r,
178                             uint8_t *loc) const {
179   if (config->outputType == MH_OBJECT)
180     return;
181 
182   switch (r.type) {
183   case ARM_RELOC_BR24:
184     if (sym->getName().startswith("___dtrace_probe")) {
185       // change call site to a NOP
186       write32le(loc, 0xE1A00000);
187     } else if (sym->getName().startswith("___dtrace_isenabled")) {
188       // change call site to 'eor r0, r0, r0'
189       write32le(loc, 0xE0200000);
190     } else {
191       error("Unrecognized dtrace symbol prefix: " + toString(*sym));
192     }
193     break;
194   case ARM_THUMB_RELOC_BR22:
195     if (sym->getName().startswith("___dtrace_probe")) {
196       // change 32-bit blx call site to two thumb NOPs
197       write32le(loc, 0x46C046C0);
198     } else if (sym->getName().startswith("___dtrace_isenabled")) {
199       // change 32-bit blx call site to 'nop', 'eor r0, r0'
200       write32le(loc, 0x46C04040);
201     } else {
202       error("Unrecognized dtrace symbol prefix: " + toString(*sym));
203     }
204     break;
205   default:
206     llvm_unreachable("Unsupported dtrace relocation type for ARM");
207   }
208 }
209