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