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