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