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; 46 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 68 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 104 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 143 void ARM::writeStub(uint8_t *buf, const Symbol &sym, uint64_t) const { 144 fatal("TODO: implement this"); 145 } 146 147 void ARM::writeStubHelperHeader(uint8_t *buf) const { 148 fatal("TODO: implement this"); 149 } 150 151 void ARM::writeStubHelperEntry(uint8_t *buf, const Symbol &sym, 152 uint64_t entryAddr) const { 153 fatal("TODO: implement this"); 154 } 155 156 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 163 void ARM::relaxGotLoad(uint8_t *loc, uint8_t type) const { 164 fatal("TODO: implement this"); 165 } 166 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 178 TargetInfo *macho::createARMTargetInfo(uint32_t cpuSubtype) { 179 static ARM t(cpuSubtype); 180 return &t; 181 } 182 183 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