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