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