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