1 //===--- RuntimeDyldCOFFThumb.h --- COFF/Thumb specific code ---*- C++ --*-===// 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 // COFF thumb support for MC-JIT runtime dynamic linker. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFTHUMB_H 14 #define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFTHUMB_H 15 16 #include "../RuntimeDyldCOFF.h" 17 #include "llvm/BinaryFormat/COFF.h" 18 #include "llvm/Object/COFF.h" 19 20 #define DEBUG_TYPE "dyld" 21 22 namespace llvm { 23 24 static bool isThumbFunc(object::symbol_iterator Symbol, 25 const object::ObjectFile &Obj, 26 object::section_iterator Section) { 27 Expected<object::SymbolRef::Type> SymTypeOrErr = Symbol->getType(); 28 if (!SymTypeOrErr) { 29 std::string Buf; 30 raw_string_ostream OS(Buf); 31 logAllUnhandledErrors(SymTypeOrErr.takeError(), OS); 32 report_fatal_error(Twine(OS.str())); 33 } 34 35 if (*SymTypeOrErr != object::SymbolRef::ST_Function) 36 return false; 37 38 // We check the IMAGE_SCN_MEM_16BIT flag in the section of the symbol to tell 39 // if it's thumb or not 40 return cast<object::COFFObjectFile>(Obj) 41 .getCOFFSection(*Section) 42 ->Characteristics & 43 COFF::IMAGE_SCN_MEM_16BIT; 44 } 45 46 class RuntimeDyldCOFFThumb : public RuntimeDyldCOFF { 47 public: 48 RuntimeDyldCOFFThumb(RuntimeDyld::MemoryManager &MM, 49 JITSymbolResolver &Resolver) 50 : RuntimeDyldCOFF(MM, Resolver, 4, COFF::IMAGE_REL_ARM_ADDR32) {} 51 52 unsigned getMaxStubSize() const override { 53 return 16; // 8-byte load instructions, 4-byte jump, 4-byte padding 54 } 55 56 Align getStubAlignment() override { return Align(1); } 57 58 Expected<object::relocation_iterator> 59 processRelocationRef(unsigned SectionID, 60 object::relocation_iterator RelI, 61 const object::ObjectFile &Obj, 62 ObjSectionToIDMap &ObjSectionToID, 63 StubMap &Stubs) override { 64 auto Symbol = RelI->getSymbol(); 65 if (Symbol == Obj.symbol_end()) 66 report_fatal_error("Unknown symbol in relocation"); 67 68 Expected<StringRef> TargetNameOrErr = Symbol->getName(); 69 if (!TargetNameOrErr) 70 return TargetNameOrErr.takeError(); 71 StringRef TargetName = *TargetNameOrErr; 72 73 auto SectionOrErr = Symbol->getSection(); 74 if (!SectionOrErr) 75 return SectionOrErr.takeError(); 76 auto Section = *SectionOrErr; 77 78 uint64_t RelType = RelI->getType(); 79 uint64_t Offset = RelI->getOffset(); 80 81 // Determine the Addend used to adjust the relocation value. 82 uint64_t Addend = 0; 83 SectionEntry &AddendSection = Sections[SectionID]; 84 uintptr_t ObjTarget = AddendSection.getObjAddress() + Offset; 85 uint8_t *Displacement = (uint8_t *)ObjTarget; 86 87 switch (RelType) { 88 case COFF::IMAGE_REL_ARM_ADDR32: 89 case COFF::IMAGE_REL_ARM_ADDR32NB: 90 case COFF::IMAGE_REL_ARM_SECREL: 91 Addend = readBytesUnaligned(Displacement, 4); 92 break; 93 default: 94 break; 95 } 96 97 #if !defined(NDEBUG) 98 SmallString<32> RelTypeName; 99 RelI->getTypeName(RelTypeName); 100 #endif 101 LLVM_DEBUG(dbgs() << "\t\tIn Section " << SectionID << " Offset " << Offset 102 << " RelType: " << RelTypeName << " TargetName: " 103 << TargetName << " Addend " << Addend << "\n"); 104 105 bool IsExtern = Section == Obj.section_end(); 106 unsigned TargetSectionID = -1; 107 uint64_t TargetOffset = -1; 108 109 if (TargetName.startswith(getImportSymbolPrefix())) { 110 TargetSectionID = SectionID; 111 TargetOffset = getDLLImportOffset(SectionID, Stubs, TargetName, true); 112 TargetName = StringRef(); 113 IsExtern = false; 114 } else if (!IsExtern) { 115 if (auto TargetSectionIDOrErr = 116 findOrEmitSection(Obj, *Section, Section->isText(), ObjSectionToID)) 117 TargetSectionID = *TargetSectionIDOrErr; 118 else 119 return TargetSectionIDOrErr.takeError(); 120 if (RelType != COFF::IMAGE_REL_ARM_SECTION) 121 TargetOffset = getSymbolOffset(*Symbol); 122 } 123 124 if (IsExtern) { 125 RelocationEntry RE(SectionID, Offset, RelType, 0, -1, 0, 0, 0, false, 0); 126 addRelocationForSymbol(RE, TargetName); 127 } else { 128 129 // We need to find out if the relocation is relative to a thumb function 130 // so that we include the ISA selection bit when resolve the relocation 131 bool IsTargetThumbFunc = isThumbFunc(Symbol, Obj, Section); 132 133 switch (RelType) { 134 default: llvm_unreachable("unsupported relocation type"); 135 case COFF::IMAGE_REL_ARM_ABSOLUTE: 136 // This relocation is ignored. 137 break; 138 case COFF::IMAGE_REL_ARM_ADDR32: { 139 RelocationEntry RE = 140 RelocationEntry(SectionID, Offset, RelType, Addend, TargetSectionID, 141 TargetOffset, 0, 0, false, 0, IsTargetThumbFunc); 142 addRelocationForSection(RE, TargetSectionID); 143 break; 144 } 145 case COFF::IMAGE_REL_ARM_ADDR32NB: { 146 RelocationEntry RE = 147 RelocationEntry(SectionID, Offset, RelType, Addend, TargetSectionID, 148 TargetOffset, 0, 0, false, 0); 149 addRelocationForSection(RE, TargetSectionID); 150 break; 151 } 152 case COFF::IMAGE_REL_ARM_SECTION: { 153 RelocationEntry RE = 154 RelocationEntry(TargetSectionID, Offset, RelType, 0); 155 addRelocationForSection(RE, TargetSectionID); 156 break; 157 } 158 case COFF::IMAGE_REL_ARM_SECREL: { 159 RelocationEntry RE = 160 RelocationEntry(SectionID, Offset, RelType, TargetOffset + Addend); 161 addRelocationForSection(RE, TargetSectionID); 162 break; 163 } 164 case COFF::IMAGE_REL_ARM_MOV32T: { 165 RelocationEntry RE = 166 RelocationEntry(SectionID, Offset, RelType, Addend, TargetSectionID, 167 TargetOffset, 0, 0, false, 0, IsTargetThumbFunc); 168 addRelocationForSection(RE, TargetSectionID); 169 break; 170 } 171 case COFF::IMAGE_REL_ARM_BRANCH20T: 172 case COFF::IMAGE_REL_ARM_BRANCH24T: 173 case COFF::IMAGE_REL_ARM_BLX23T: { 174 RelocationEntry RE = RelocationEntry(SectionID, Offset, RelType, 175 TargetOffset + Addend, true, 0); 176 addRelocationForSection(RE, TargetSectionID); 177 break; 178 } 179 } 180 } 181 182 return ++RelI; 183 } 184 185 void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override { 186 const auto Section = Sections[RE.SectionID]; 187 uint8_t *Target = Section.getAddressWithOffset(RE.Offset); 188 int ISASelectionBit = RE.IsTargetThumbFunc ? 1 : 0; 189 190 switch (RE.RelType) { 191 default: llvm_unreachable("unsupported relocation type"); 192 case COFF::IMAGE_REL_ARM_ABSOLUTE: 193 // This relocation is ignored. 194 break; 195 case COFF::IMAGE_REL_ARM_ADDR32: { 196 // The target's 32-bit VA. 197 uint64_t Result = 198 RE.Sections.SectionA == static_cast<uint32_t>(-1) 199 ? Value 200 : Sections[RE.Sections.SectionA].getLoadAddressWithOffset(RE.Addend); 201 Result |= ISASelectionBit; 202 assert(Result <= UINT32_MAX && "relocation overflow"); 203 LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset 204 << " RelType: IMAGE_REL_ARM_ADDR32" 205 << " TargetSection: " << RE.Sections.SectionA 206 << " Value: " << format("0x%08" PRIx32, Result) 207 << '\n'); 208 writeBytesUnaligned(Result, Target, 4); 209 break; 210 } 211 case COFF::IMAGE_REL_ARM_ADDR32NB: { 212 // The target's 32-bit RVA. 213 // NOTE: use Section[0].getLoadAddress() as an approximation of ImageBase 214 uint64_t Result = Sections[RE.Sections.SectionA].getLoadAddress() - 215 Sections[0].getLoadAddress() + RE.Addend; 216 assert(Result <= UINT32_MAX && "relocation overflow"); 217 LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset 218 << " RelType: IMAGE_REL_ARM_ADDR32NB" 219 << " TargetSection: " << RE.Sections.SectionA 220 << " Value: " << format("0x%08" PRIx32, Result) 221 << '\n'); 222 Result |= ISASelectionBit; 223 writeBytesUnaligned(Result, Target, 4); 224 break; 225 } 226 case COFF::IMAGE_REL_ARM_SECTION: 227 // 16-bit section index of the section that contains the target. 228 assert(static_cast<uint32_t>(RE.SectionID) <= UINT16_MAX && 229 "relocation overflow"); 230 LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset 231 << " RelType: IMAGE_REL_ARM_SECTION Value: " 232 << RE.SectionID << '\n'); 233 writeBytesUnaligned(RE.SectionID, Target, 2); 234 break; 235 case COFF::IMAGE_REL_ARM_SECREL: 236 // 32-bit offset of the target from the beginning of its section. 237 assert(static_cast<uint64_t>(RE.Addend) <= UINT32_MAX && 238 "relocation overflow"); 239 LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset 240 << " RelType: IMAGE_REL_ARM_SECREL Value: " << RE.Addend 241 << '\n'); 242 writeBytesUnaligned(RE.Addend, Target, 2); 243 break; 244 case COFF::IMAGE_REL_ARM_MOV32T: { 245 // 32-bit VA of the target applied to a contiguous MOVW+MOVT pair. 246 uint64_t Result = 247 Sections[RE.Sections.SectionA].getLoadAddressWithOffset(RE.Addend); 248 assert(Result <= UINT32_MAX && "relocation overflow"); 249 LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset 250 << " RelType: IMAGE_REL_ARM_MOV32T" 251 << " TargetSection: " << RE.Sections.SectionA 252 << " Value: " << format("0x%08" PRIx32, Result) 253 << '\n'); 254 255 // MOVW(T3): |11110|i|10|0|1|0|0|imm4|0|imm3|Rd|imm8| 256 // imm32 = zext imm4:i:imm3:imm8 257 // MOVT(T1): |11110|i|10|1|1|0|0|imm4|0|imm3|Rd|imm8| 258 // imm16 = imm4:i:imm3:imm8 259 260 auto EncodeImmediate = [](uint8_t *Bytes, uint16_t Immediate) { 261 Bytes[0] |= ((Immediate & 0xf000) >> 12); 262 Bytes[1] |= ((Immediate & 0x0800) >> 11); 263 Bytes[2] |= ((Immediate & 0x00ff) >> 0); 264 Bytes[3] |= (((Immediate & 0x0700) >> 8) << 4); 265 }; 266 267 EncodeImmediate(&Target[0], 268 (static_cast<uint32_t>(Result) >> 00) | ISASelectionBit); 269 EncodeImmediate(&Target[4], static_cast<uint32_t>(Result) >> 16); 270 break; 271 } 272 case COFF::IMAGE_REL_ARM_BRANCH20T: { 273 // The most significant 20-bits of the signed 21-bit relative displacement 274 uint64_t Value = 275 RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4; 276 assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX && 277 "relocation overflow"); 278 assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN && 279 "relocation underflow"); 280 LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset 281 << " RelType: IMAGE_REL_ARM_BRANCH20T" 282 << " Value: " << static_cast<int32_t>(Value) << '\n'); 283 static_cast<void>(Value); 284 llvm_unreachable("unimplemented relocation"); 285 break; 286 } 287 case COFF::IMAGE_REL_ARM_BRANCH24T: { 288 // The most significant 24-bits of the signed 25-bit relative displacement 289 uint64_t Value = 290 RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4; 291 assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX && 292 "relocation overflow"); 293 assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN && 294 "relocation underflow"); 295 LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset 296 << " RelType: IMAGE_REL_ARM_BRANCH24T" 297 << " Value: " << static_cast<int32_t>(Value) << '\n'); 298 static_cast<void>(Value); 299 llvm_unreachable("unimplemented relocation"); 300 break; 301 } 302 case COFF::IMAGE_REL_ARM_BLX23T: { 303 // The most significant 24-bits of the signed 25-bit relative displacement 304 uint64_t Value = 305 RE.Addend - (Sections[RE.SectionID].getLoadAddress() + RE.Offset) - 4; 306 assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX && 307 "relocation overflow"); 308 assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN && 309 "relocation underflow"); 310 LLVM_DEBUG(dbgs() << "\t\tOffset: " << RE.Offset 311 << " RelType: IMAGE_REL_ARM_BLX23T" 312 << " Value: " << static_cast<int32_t>(Value) << '\n'); 313 static_cast<void>(Value); 314 llvm_unreachable("unimplemented relocation"); 315 break; 316 } 317 } 318 } 319 320 void registerEHFrames() override {} 321 }; 322 323 } 324 325 #endif 326