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