1 //===-- ARMMachObjectWriter.cpp - ARM Mach Object Writer ------------------===// 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 "MCTargetDesc/ARMFixupKinds.h" 10 #include "MCTargetDesc/ARMMCTargetDesc.h" 11 #include "llvm/ADT/StringExtras.h" 12 #include "llvm/ADT/Twine.h" 13 #include "llvm/BinaryFormat/MachO.h" 14 #include "llvm/MC/MCAssembler.h" 15 #include "llvm/MC/MCContext.h" 16 #include "llvm/MC/MCExpr.h" 17 #include "llvm/MC/MCFixup.h" 18 #include "llvm/MC/MCMachObjectWriter.h" 19 #include "llvm/MC/MCObjectStreamer.h" 20 #include "llvm/MC/MCSection.h" 21 #include "llvm/MC/MCSymbolMachO.h" 22 #include "llvm/MC/MCValue.h" 23 #include "llvm/Support/Casting.h" 24 #include "llvm/Support/ErrorHandling.h" 25 26 using namespace llvm; 27 28 namespace { 29 class ARMMachObjectWriter : public MCMachObjectTargetWriter { 30 void recordARMScatteredRelocation(MachObjectWriter *Writer, 31 const MCAssembler &Asm, 32 const MCFragment *Fragment, 33 const MCFixup &Fixup, MCValue Target, 34 unsigned Type, unsigned Log2Size, 35 uint64_t &FixedValue); 36 void recordARMScatteredHalfRelocation(MachObjectWriter *Writer, 37 const MCAssembler &Asm, 38 const MCFragment *Fragment, 39 const MCFixup &Fixup, MCValue Target, 40 uint64_t &FixedValue); 41 42 bool requiresExternRelocation(MachObjectWriter *Writer, 43 const MCAssembler &Asm, 44 const MCFragment &Fragment, unsigned RelocType, 45 const MCSymbol &S, uint64_t FixedValue); 46 47 public: 48 ARMMachObjectWriter(bool Is64Bit, uint32_t CPUType, uint32_t CPUSubtype) 49 : MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype) {} 50 51 void recordRelocation(MachObjectWriter *Writer, MCAssembler &Asm, 52 const MCFragment *Fragment, const MCFixup &Fixup, 53 MCValue Target, uint64_t &FixedValue) override; 54 }; 55 } 56 57 static bool getARMFixupKindMachOInfo(unsigned Kind, unsigned &RelocType, 58 unsigned &Log2Size) { 59 RelocType = unsigned(MachO::ARM_RELOC_VANILLA); 60 Log2Size = ~0U; 61 62 switch (Kind) { 63 default: 64 return false; 65 66 case FK_Data_1: 67 Log2Size = llvm::Log2_32(1); 68 return true; 69 case FK_Data_2: 70 Log2Size = llvm::Log2_32(2); 71 return true; 72 case FK_Data_4: 73 Log2Size = llvm::Log2_32(4); 74 return true; 75 case FK_Data_8: 76 Log2Size = llvm::Log2_32(8); 77 return false; 78 79 // These fixups are expected to always be resolvable at assembly time and 80 // have no relocations supported. 81 case ARM::fixup_arm_ldst_pcrel_12: 82 case ARM::fixup_arm_pcrel_10: 83 case ARM::fixup_arm_adr_pcrel_12: 84 case ARM::fixup_arm_thumb_br: 85 return false; 86 87 // Handle 24-bit branch kinds. 88 case ARM::fixup_arm_condbranch: 89 case ARM::fixup_arm_uncondbranch: 90 case ARM::fixup_arm_uncondbl: 91 case ARM::fixup_arm_condbl: 92 case ARM::fixup_arm_blx: 93 RelocType = unsigned(MachO::ARM_RELOC_BR24); 94 // Report as 'long', even though that is not quite accurate. 95 Log2Size = llvm::Log2_32(4); 96 return true; 97 98 case ARM::fixup_t2_uncondbranch: 99 case ARM::fixup_arm_thumb_bl: 100 case ARM::fixup_arm_thumb_blx: 101 RelocType = unsigned(MachO::ARM_THUMB_RELOC_BR22); 102 Log2Size = llvm::Log2_32(4); 103 return true; 104 105 // For movw/movt r_type relocations they always have a pair following them and 106 // the r_length bits are used differently. The encoding of the r_length is as 107 // follows: 108 // low bit of r_length: 109 // 0 - :lower16: for movw instructions 110 // 1 - :upper16: for movt instructions 111 // high bit of r_length: 112 // 0 - arm instructions 113 // 1 - thumb instructions 114 case ARM::fixup_arm_movt_hi16: 115 RelocType = unsigned(MachO::ARM_RELOC_HALF); 116 Log2Size = 1; 117 return true; 118 case ARM::fixup_t2_movt_hi16: 119 RelocType = unsigned(MachO::ARM_RELOC_HALF); 120 Log2Size = 3; 121 return true; 122 123 case ARM::fixup_arm_movw_lo16: 124 RelocType = unsigned(MachO::ARM_RELOC_HALF); 125 Log2Size = 0; 126 return true; 127 case ARM::fixup_t2_movw_lo16: 128 RelocType = unsigned(MachO::ARM_RELOC_HALF); 129 Log2Size = 2; 130 return true; 131 } 132 } 133 134 void ARMMachObjectWriter::recordARMScatteredHalfRelocation( 135 MachObjectWriter *Writer, const MCAssembler &Asm, 136 const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, 137 uint64_t &FixedValue) { 138 uint32_t FixupOffset = Asm.getFragmentOffset(*Fragment) + Fixup.getOffset(); 139 140 if (FixupOffset & 0xff000000) { 141 reportError(Fixup.getLoc(), "can not encode offset '0x" + 142 utohexstr(FixupOffset) + 143 "' in resulting scattered relocation."); 144 return; 145 } 146 147 unsigned IsPCRel = Fixup.isPCRel(); 148 unsigned Type = MachO::ARM_RELOC_HALF; 149 150 // See <reloc.h>. 151 const MCSymbol *A = Target.getAddSym(); 152 153 if (!A->getFragment()) { 154 reportError(Fixup.getLoc(), 155 "symbol '" + A->getName() + 156 "' can not be undefined in a subtraction expression"); 157 return; 158 } 159 160 uint32_t Value = Writer->getSymbolAddress(*A); 161 uint32_t Value2 = 0; 162 uint64_t SecAddr = Writer->getSectionAddress(A->getFragment()->getParent()); 163 FixedValue += SecAddr; 164 165 if (const MCSymbol *SB = Target.getSubSym()) { 166 if (!SB->getFragment()) { 167 reportError(Fixup.getLoc(), 168 "symbol '" + SB->getName() + 169 "' can not be undefined in a subtraction expression"); 170 return; 171 } 172 173 // Select the appropriate difference relocation type. 174 Type = MachO::ARM_RELOC_HALF_SECTDIFF; 175 Value2 = Writer->getSymbolAddress(*SB); 176 FixedValue -= Writer->getSectionAddress(SB->getFragment()->getParent()); 177 } 178 179 // Relocations are written out in reverse order, so the PAIR comes first. 180 // ARM_RELOC_HALF and ARM_RELOC_HALF_SECTDIFF abuse the r_length field: 181 // 182 // For these two r_type relocations they always have a pair following them and 183 // the r_length bits are used differently. The encoding of the r_length is as 184 // follows: 185 // low bit of r_length: 186 // 0 - :lower16: for movw instructions 187 // 1 - :upper16: for movt instructions 188 // high bit of r_length: 189 // 0 - arm instructions 190 // 1 - thumb instructions 191 // the other half of the relocated expression is in the following pair 192 // relocation entry in the low 16 bits of r_address field. 193 unsigned ThumbBit = 0; 194 unsigned MovtBit = 0; 195 switch (Fixup.getKind()) { 196 default: break; 197 case ARM::fixup_arm_movt_hi16: 198 MovtBit = 1; 199 // The thumb bit shouldn't be set in the 'other-half' bit of the 200 // relocation, but it will be set in FixedValue if the base symbol 201 // is a thumb function. Clear it out here. 202 if (Asm.isThumbFunc(A)) 203 FixedValue &= 0xfffffffe; 204 break; 205 case ARM::fixup_t2_movt_hi16: 206 if (Asm.isThumbFunc(A)) 207 FixedValue &= 0xfffffffe; 208 MovtBit = 1; 209 [[fallthrough]]; 210 case ARM::fixup_t2_movw_lo16: 211 ThumbBit = 1; 212 break; 213 } 214 215 if (Type == MachO::ARM_RELOC_HALF_SECTDIFF) { 216 uint32_t OtherHalf = MovtBit 217 ? (FixedValue & 0xffff) : ((FixedValue & 0xffff0000) >> 16); 218 219 MachO::any_relocation_info MRE; 220 MRE.r_word0 = ((OtherHalf << 0) | 221 (MachO::ARM_RELOC_PAIR << 24) | 222 (MovtBit << 28) | 223 (ThumbBit << 29) | 224 (IsPCRel << 30) | 225 MachO::R_SCATTERED); 226 MRE.r_word1 = Value2; 227 Writer->addRelocation(nullptr, Fragment->getParent(), MRE); 228 } 229 230 MachO::any_relocation_info MRE; 231 MRE.r_word0 = ((FixupOffset << 0) | 232 (Type << 24) | 233 (MovtBit << 28) | 234 (ThumbBit << 29) | 235 (IsPCRel << 30) | 236 MachO::R_SCATTERED); 237 MRE.r_word1 = Value; 238 Writer->addRelocation(nullptr, Fragment->getParent(), MRE); 239 } 240 241 void ARMMachObjectWriter::recordARMScatteredRelocation( 242 MachObjectWriter *Writer, const MCAssembler &Asm, 243 const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, 244 unsigned Type, unsigned Log2Size, uint64_t &FixedValue) { 245 uint32_t FixupOffset = Asm.getFragmentOffset(*Fragment) + Fixup.getOffset(); 246 247 if (FixupOffset & 0xff000000) { 248 reportError(Fixup.getLoc(), "can not encode offset '0x" + 249 utohexstr(FixupOffset) + 250 "' in resulting scattered relocation."); 251 return; 252 } 253 254 unsigned IsPCRel = Fixup.isPCRel(); 255 256 // See <reloc.h>. 257 const MCSymbol *A = Target.getAddSym(); 258 259 if (!A->getFragment()) { 260 reportError(Fixup.getLoc(), 261 "symbol '" + A->getName() + 262 "' can not be undefined in a subtraction expression"); 263 return; 264 } 265 266 uint32_t Value = Writer->getSymbolAddress(*A); 267 uint64_t SecAddr = Writer->getSectionAddress(A->getFragment()->getParent()); 268 FixedValue += SecAddr; 269 uint32_t Value2 = 0; 270 271 if (const MCSymbol *SB = Target.getSubSym()) { 272 assert(Type == MachO::ARM_RELOC_VANILLA && "invalid reloc for 2 symbols"); 273 274 if (!SB->getFragment()) { 275 reportError(Fixup.getLoc(), 276 "symbol '" + SB->getName() + 277 "' can not be undefined in a subtraction expression"); 278 return; 279 } 280 281 // Select the appropriate difference relocation type. 282 Type = MachO::ARM_RELOC_SECTDIFF; 283 Value2 = Writer->getSymbolAddress(*SB); 284 FixedValue -= Writer->getSectionAddress(SB->getFragment()->getParent()); 285 } 286 287 // Relocations are written out in reverse order, so the PAIR comes first. 288 if (Type == MachO::ARM_RELOC_SECTDIFF || 289 Type == MachO::ARM_RELOC_LOCAL_SECTDIFF) { 290 MachO::any_relocation_info MRE; 291 MRE.r_word0 = ((0 << 0) | 292 (MachO::ARM_RELOC_PAIR << 24) | 293 (Log2Size << 28) | 294 (IsPCRel << 30) | 295 MachO::R_SCATTERED); 296 MRE.r_word1 = Value2; 297 Writer->addRelocation(nullptr, Fragment->getParent(), MRE); 298 } 299 300 MachO::any_relocation_info MRE; 301 MRE.r_word0 = ((FixupOffset << 0) | 302 (Type << 24) | 303 (Log2Size << 28) | 304 (IsPCRel << 30) | 305 MachO::R_SCATTERED); 306 MRE.r_word1 = Value; 307 Writer->addRelocation(nullptr, Fragment->getParent(), MRE); 308 } 309 310 bool ARMMachObjectWriter::requiresExternRelocation(MachObjectWriter *Writer, 311 const MCAssembler &Asm, 312 const MCFragment &Fragment, 313 unsigned RelocType, 314 const MCSymbol &S, 315 uint64_t FixedValue) { 316 // Most cases can be identified purely from the symbol. 317 if (Writer->doesSymbolRequireExternRelocation(S)) 318 return true; 319 int64_t Value = (int64_t)FixedValue; // The displacement is signed. 320 int64_t Range; 321 switch (RelocType) { 322 default: 323 return false; 324 case MachO::ARM_RELOC_BR24: 325 // An ARM call might be to a Thumb function, in which case the offset may 326 // not be encodable in the instruction and we must use an external 327 // relocation that explicitly mentions the function. Not a problem if it's 328 // to a temporary "Lwhatever" symbol though, and in fact trying to use an 329 // external relocation there causes more issues. 330 if (!S.isTemporary()) 331 return true; 332 333 // PC pre-adjustment of 8 for these instructions. 334 Value -= 8; 335 // ARM BL/BLX has a 25-bit offset. 336 Range = 0x1ffffff; 337 break; 338 case MachO::ARM_THUMB_RELOC_BR22: 339 // PC pre-adjustment of 4 for these instructions. 340 Value -= 4; 341 // Thumb BL/BLX has a 24-bit offset. 342 Range = 0xffffff; 343 } 344 // BL/BLX also use external relocations when an internal relocation 345 // would result in the target being out of range. This gives the linker 346 // enough information to generate a branch island. 347 Value += Writer->getSectionAddress(&S.getSection()); 348 Value -= Writer->getSectionAddress(Fragment.getParent()); 349 // If the resultant value would be out of range for an internal relocation, 350 // use an external instead. 351 if (Value > Range || Value < -(Range + 1)) 352 return true; 353 return false; 354 } 355 356 void ARMMachObjectWriter::recordRelocation(MachObjectWriter *Writer, 357 MCAssembler &Asm, 358 const MCFragment *Fragment, 359 const MCFixup &Fixup, MCValue Target, 360 uint64_t &FixedValue) { 361 unsigned IsPCRel = Fixup.isPCRel(); 362 unsigned Log2Size; 363 unsigned RelocType = MachO::ARM_RELOC_VANILLA; 364 if (!getARMFixupKindMachOInfo(Fixup.getKind(), RelocType, Log2Size)) { 365 // If we failed to get fixup kind info, it's because there's no legal 366 // relocation type for the fixup kind. This happens when it's a fixup that's 367 // expected to always be resolvable at assembly time and not have any 368 // relocations needed. 369 reportError(Fixup.getLoc(), "unsupported relocation type"); 370 return; 371 } 372 373 // If this is a difference or a defined symbol plus an offset, then we need a 374 // scattered relocation entry. Differences always require scattered 375 // relocations. 376 if (Target.getSubSym()) { 377 if (RelocType == MachO::ARM_RELOC_HALF) 378 return recordARMScatteredHalfRelocation(Writer, Asm, Fragment, Fixup, 379 Target, FixedValue); 380 return recordARMScatteredRelocation(Writer, Asm, Fragment, Fixup, Target, 381 RelocType, Log2Size, FixedValue); 382 } 383 384 // Get the symbol data, if any. 385 const MCSymbol *A = Target.getAddSym(); 386 387 // FIXME: For other platforms, we need to use scattered relocations for 388 // internal relocations with offsets. If this is an internal relocation with 389 // an offset, it also needs a scattered relocation entry. 390 // 391 // Is this right for ARM? 392 uint32_t Offset = Target.getConstant(); 393 if (IsPCRel && RelocType == MachO::ARM_RELOC_VANILLA) 394 Offset += 1 << Log2Size; 395 if (Offset && A && !Writer->doesSymbolRequireExternRelocation(*A) && 396 RelocType != MachO::ARM_RELOC_HALF) 397 return recordARMScatteredRelocation(Writer, Asm, Fragment, Fixup, Target, 398 RelocType, Log2Size, FixedValue); 399 400 // See <reloc.h>. 401 uint32_t FixupOffset = Asm.getFragmentOffset(*Fragment) + Fixup.getOffset(); 402 unsigned Index = 0; 403 unsigned Type = 0; 404 const MCSymbol *RelSymbol = nullptr; 405 406 if (!A) { // constant 407 // FIXME! This is Target.isAbsolute() case as we check SymB above. We check 408 // !A to ensure that null pointer isn't dereferenced and suppress static 409 // analyzer warnings. 410 report_fatal_error("FIXME: relocations to absolute targets " 411 "not yet implemented"); 412 } else { 413 // Resolve constant variables. 414 if (A->isVariable()) { 415 MCValue Val; 416 bool Relocatable = 417 A->getVariableValue()->evaluateAsRelocatable(Val, &Asm); 418 int64_t Res = Val.getConstant(); 419 bool isAbs = Val.isAbsolute(); 420 if (Relocatable && Val.getAddSym() && Val.getSubSym()) { 421 Res += Writer->getSymbolAddress(*Val.getAddSym()) - 422 Writer->getSymbolAddress(*Val.getSubSym()); 423 isAbs = true; 424 } 425 if (isAbs) { 426 FixedValue = Res; 427 return; 428 } 429 } 430 431 // Check whether we need an external or internal relocation. 432 if (requiresExternRelocation(Writer, Asm, *Fragment, RelocType, *A, 433 FixedValue)) { 434 RelSymbol = A; 435 436 // For external relocations, make sure to offset the fixup value to 437 // compensate for the addend of the symbol address, if it was 438 // undefined. This occurs with weak definitions, for example. 439 if (!A->isUndefined()) 440 FixedValue -= Asm.getSymbolOffset(*A); 441 } else { 442 // The index is the section ordinal (1-based). 443 const MCSection &Sec = A->getSection(); 444 Index = Sec.getOrdinal() + 1; 445 FixedValue += Writer->getSectionAddress(&Sec); 446 } 447 if (IsPCRel) 448 FixedValue -= Writer->getSectionAddress(Fragment->getParent()); 449 450 // The type is determined by the fixup kind. 451 Type = RelocType; 452 } 453 454 // struct relocation_info (8 bytes) 455 MachO::any_relocation_info MRE; 456 MRE.r_word0 = FixupOffset; 457 MRE.r_word1 = 458 (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28); 459 460 // Even when it's not a scattered relocation, movw/movt always uses 461 // a PAIR relocation. 462 if (Type == MachO::ARM_RELOC_HALF) { 463 // The entire addend is needed to correctly apply a relocation. One half is 464 // extracted from the instruction itself, the other comes from this 465 // PAIR. I.e. it's correct that we insert the high bits of the addend in the 466 // MOVW case here. relocation entries. 467 uint32_t Value = 0; 468 switch (Fixup.getKind()) { 469 default: break; 470 case ARM::fixup_arm_movw_lo16: 471 case ARM::fixup_t2_movw_lo16: 472 Value = (FixedValue >> 16) & 0xffff; 473 break; 474 case ARM::fixup_arm_movt_hi16: 475 case ARM::fixup_t2_movt_hi16: 476 Value = FixedValue & 0xffff; 477 break; 478 } 479 MachO::any_relocation_info MREPair; 480 MREPair.r_word0 = Value; 481 MREPair.r_word1 = ((0xffffff << 0) | 482 (Log2Size << 25) | 483 (MachO::ARM_RELOC_PAIR << 28)); 484 485 Writer->addRelocation(nullptr, Fragment->getParent(), MREPair); 486 } 487 488 Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE); 489 } 490 491 std::unique_ptr<MCObjectTargetWriter> 492 llvm::createARMMachObjectWriter(bool Is64Bit, uint32_t CPUType, 493 uint32_t CPUSubtype) { 494 return std::make_unique<ARMMachObjectWriter>(Is64Bit, CPUType, CPUSubtype); 495 } 496 497 namespace { 498 class ARMTargetMachOStreamer : public ARMTargetStreamer { 499 public: 500 ARMTargetMachOStreamer(MCStreamer &S) : ARMTargetStreamer(S) {} 501 MCObjectStreamer &getStreamer() { 502 return static_cast<MCObjectStreamer &>(Streamer); 503 } 504 void emitThumbFunc(MCSymbol *Symbol) override { 505 // Remember that the function is a thumb function. Fixup and relocation 506 // values will need adjusted. 507 getStreamer().getAssembler().setIsThumbFunc(Symbol); 508 cast<MCSymbolMachO>(Symbol)->setThumbFunc(); 509 } 510 }; 511 } // namespace 512 513 MCTargetStreamer *llvm::createARMObjectTargetMachOStreamer(MCStreamer &S) { 514 return new ARMTargetMachOStreamer(S); 515 } 516