1 //===-- AArch64MachObjectWriter.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/AArch64FixupKinds.h" 11 #include "MCTargetDesc/AArch64MCTargetDesc.h" 12 #include "llvm/ADT/Twine.h" 13 #include "llvm/MC/MCAsmInfo.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/MCMachObjectWriter.h" 20 #include "llvm/MC/MCSectionMachO.h" 21 #include "llvm/MC/MCValue.h" 22 #include "llvm/Support/ErrorHandling.h" 23 #include "llvm/Support/MachO.h" 24 using namespace llvm; 25 26 namespace { 27 class AArch64MachObjectWriter : public MCMachObjectTargetWriter { 28 bool getAArch64FixupKindMachOInfo(const MCFixup &Fixup, unsigned &RelocType, 29 const MCSymbolRefExpr *Sym, 30 unsigned &Log2Size, const MCAssembler &Asm); 31 32 public: 33 AArch64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype) 34 : MCMachObjectTargetWriter(true /* is64Bit */, CPUType, CPUSubtype, 35 /*UseAggressiveSymbolFolding=*/true) {} 36 37 void RecordRelocation(MachObjectWriter *Writer, MCAssembler &Asm, 38 const MCAsmLayout &Layout, const MCFragment *Fragment, 39 const MCFixup &Fixup, MCValue Target, 40 uint64_t &FixedValue) override; 41 }; 42 } 43 44 bool AArch64MachObjectWriter::getAArch64FixupKindMachOInfo( 45 const MCFixup &Fixup, unsigned &RelocType, const MCSymbolRefExpr *Sym, 46 unsigned &Log2Size, const MCAssembler &Asm) { 47 RelocType = unsigned(MachO::ARM64_RELOC_UNSIGNED); 48 Log2Size = ~0U; 49 50 switch ((unsigned)Fixup.getKind()) { 51 default: 52 return false; 53 54 case FK_Data_1: 55 Log2Size = llvm::Log2_32(1); 56 return true; 57 case FK_Data_2: 58 Log2Size = llvm::Log2_32(2); 59 return true; 60 case FK_Data_4: 61 Log2Size = llvm::Log2_32(4); 62 if (Sym->getKind() == MCSymbolRefExpr::VK_GOT) 63 RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT); 64 return true; 65 case FK_Data_8: 66 Log2Size = llvm::Log2_32(8); 67 if (Sym->getKind() == MCSymbolRefExpr::VK_GOT) 68 RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT); 69 return true; 70 case AArch64::fixup_aarch64_add_imm12: 71 case AArch64::fixup_aarch64_ldst_imm12_scale1: 72 case AArch64::fixup_aarch64_ldst_imm12_scale2: 73 case AArch64::fixup_aarch64_ldst_imm12_scale4: 74 case AArch64::fixup_aarch64_ldst_imm12_scale8: 75 case AArch64::fixup_aarch64_ldst_imm12_scale16: 76 Log2Size = llvm::Log2_32(4); 77 switch (Sym->getKind()) { 78 default: 79 llvm_unreachable("Unexpected symbol reference variant kind!"); 80 case MCSymbolRefExpr::VK_PAGEOFF: 81 RelocType = unsigned(MachO::ARM64_RELOC_PAGEOFF12); 82 return true; 83 case MCSymbolRefExpr::VK_GOTPAGEOFF: 84 RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12); 85 return true; 86 case MCSymbolRefExpr::VK_TLVPPAGEOFF: 87 RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12); 88 return true; 89 } 90 case AArch64::fixup_aarch64_pcrel_adrp_imm21: 91 Log2Size = llvm::Log2_32(4); 92 // This encompasses the relocation for the whole 21-bit value. 93 switch (Sym->getKind()) { 94 default: 95 Asm.getContext().FatalError(Fixup.getLoc(), 96 "ADR/ADRP relocations must be GOT relative"); 97 case MCSymbolRefExpr::VK_PAGE: 98 RelocType = unsigned(MachO::ARM64_RELOC_PAGE21); 99 return true; 100 case MCSymbolRefExpr::VK_GOTPAGE: 101 RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGE21); 102 return true; 103 case MCSymbolRefExpr::VK_TLVPPAGE: 104 RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGE21); 105 return true; 106 } 107 return true; 108 case AArch64::fixup_aarch64_pcrel_branch26: 109 case AArch64::fixup_aarch64_pcrel_call26: 110 Log2Size = llvm::Log2_32(4); 111 RelocType = unsigned(MachO::ARM64_RELOC_BRANCH26); 112 return true; 113 } 114 } 115 116 static bool canUseLocalRelocation(const MCSectionMachO &Section, 117 const MCSymbol &Symbol, unsigned Log2Size) { 118 // Debug info sections can use local relocations. 119 if (Section.hasAttribute(MachO::S_ATTR_DEBUG)) 120 return true; 121 122 // Otherwise, only pointer sized relocations are supported. 123 if (Log2Size != 3) 124 return false; 125 126 // But only if they don't point to a few forbidden sections. 127 if (!Symbol.isInSection()) 128 return true; 129 const MCSectionMachO &RefSec = cast<MCSectionMachO>(Symbol.getSection()); 130 if (RefSec.getType() == MachO::S_CSTRING_LITERALS) 131 return false; 132 133 if (RefSec.getSegmentName() == "__DATA" && 134 RefSec.getSectionName() == "__cfstring") 135 return false; 136 137 if (RefSec.getSegmentName() == "__DATA" && 138 RefSec.getSectionName() == "__objc_classrefs") 139 return false; 140 141 return true; 142 } 143 144 void AArch64MachObjectWriter::RecordRelocation( 145 MachObjectWriter *Writer, MCAssembler &Asm, const MCAsmLayout &Layout, 146 const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, 147 uint64_t &FixedValue) { 148 unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind()); 149 150 // See <reloc.h>. 151 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment); 152 unsigned Log2Size = 0; 153 int64_t Value = 0; 154 unsigned Index = 0; 155 unsigned Type = 0; 156 unsigned Kind = Fixup.getKind(); 157 const MCSymbolData *RelSymbol = nullptr; 158 159 FixupOffset += Fixup.getOffset(); 160 161 // AArch64 pcrel relocation addends do not include the section offset. 162 if (IsPCRel) 163 FixedValue += FixupOffset; 164 165 // ADRP fixups use relocations for the whole symbol value and only 166 // put the addend in the instruction itself. Clear out any value the 167 // generic code figured out from the sybmol definition. 168 if (Kind == AArch64::fixup_aarch64_pcrel_adrp_imm21) 169 FixedValue = 0; 170 171 // imm19 relocations are for conditional branches, which require 172 // assembler local symbols. If we got here, that's not what we have, 173 // so complain loudly. 174 if (Kind == AArch64::fixup_aarch64_pcrel_branch19) { 175 Asm.getContext().FatalError(Fixup.getLoc(), 176 "conditional branch requires assembler-local" 177 " label. '" + 178 Target.getSymA()->getSymbol().getName() + 179 "' is external."); 180 return; 181 } 182 183 // 14-bit branch relocations should only target internal labels, and so 184 // should never get here. 185 if (Kind == AArch64::fixup_aarch64_pcrel_branch14) { 186 Asm.getContext().FatalError(Fixup.getLoc(), 187 "Invalid relocation on conditional branch!"); 188 return; 189 } 190 191 if (!getAArch64FixupKindMachOInfo(Fixup, Type, Target.getSymA(), Log2Size, 192 Asm)) { 193 Asm.getContext().FatalError(Fixup.getLoc(), "unknown AArch64 fixup kind!"); 194 return; 195 } 196 197 Value = Target.getConstant(); 198 199 if (Target.isAbsolute()) { // constant 200 // FIXME: Should this always be extern? 201 // SymbolNum of 0 indicates the absolute section. 202 Type = MachO::ARM64_RELOC_UNSIGNED; 203 204 if (IsPCRel) { 205 Asm.getContext().FatalError(Fixup.getLoc(), 206 "PC relative absolute relocation!"); 207 208 // FIXME: x86_64 sets the type to a branch reloc here. Should we do 209 // something similar? 210 } 211 } else if (Target.getSymB()) { // A - B + constant 212 const MCSymbol *A = &Target.getSymA()->getSymbol(); 213 const MCSymbolData &A_SD = Asm.getSymbolData(*A); 214 const MCSymbolData *A_Base = Asm.getAtom(&A_SD); 215 216 const MCSymbol *B = &Target.getSymB()->getSymbol(); 217 const MCSymbolData &B_SD = Asm.getSymbolData(*B); 218 const MCSymbolData *B_Base = Asm.getAtom(&B_SD); 219 220 // Check for "_foo@got - .", which comes through here as: 221 // Ltmp0: 222 // ... _foo@got - Ltmp0 223 if (Target.getSymA()->getKind() == MCSymbolRefExpr::VK_GOT && 224 Target.getSymB()->getKind() == MCSymbolRefExpr::VK_None && 225 Layout.getSymbolOffset(&B_SD) == 226 Layout.getFragmentOffset(Fragment) + Fixup.getOffset()) { 227 // SymB is the PC, so use a PC-rel pointer-to-GOT relocation. 228 Type = MachO::ARM64_RELOC_POINTER_TO_GOT; 229 IsPCRel = 1; 230 MachO::any_relocation_info MRE; 231 MRE.r_word0 = FixupOffset; 232 MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28); 233 Writer->addRelocation(A_Base, Fragment->getParent(), MRE); 234 return; 235 } else if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None || 236 Target.getSymB()->getKind() != MCSymbolRefExpr::VK_None) 237 // Otherwise, neither symbol can be modified. 238 Asm.getContext().FatalError(Fixup.getLoc(), 239 "unsupported relocation of modified symbol"); 240 241 // We don't support PCrel relocations of differences. 242 if (IsPCRel) 243 Asm.getContext().FatalError(Fixup.getLoc(), 244 "unsupported pc-relative relocation of " 245 "difference"); 246 247 // AArch64 always uses external relocations. If there is no symbol to use as 248 // a base address (a local symbol with no preceding non-local symbol), 249 // error out. 250 // 251 // FIXME: We should probably just synthesize an external symbol and use 252 // that. 253 if (!A_Base) 254 Asm.getContext().FatalError( 255 Fixup.getLoc(), 256 "unsupported relocation of local symbol '" + A->getName() + 257 "'. Must have non-local symbol earlier in section."); 258 if (!B_Base) 259 Asm.getContext().FatalError( 260 Fixup.getLoc(), 261 "unsupported relocation of local symbol '" + B->getName() + 262 "'. Must have non-local symbol earlier in section."); 263 264 if (A_Base == B_Base && A_Base) 265 Asm.getContext().FatalError(Fixup.getLoc(), 266 "unsupported relocation with identical base"); 267 268 Value += (!A_SD.getFragment() ? 0 269 : Writer->getSymbolAddress(&A_SD, Layout)) - 270 (!A_Base || !A_Base->getFragment() 271 ? 0 272 : Writer->getSymbolAddress(A_Base, Layout)); 273 Value -= (!B_SD.getFragment() ? 0 274 : Writer->getSymbolAddress(&B_SD, Layout)) - 275 (!B_Base || !B_Base->getFragment() 276 ? 0 277 : Writer->getSymbolAddress(B_Base, Layout)); 278 279 Type = MachO::ARM64_RELOC_UNSIGNED; 280 281 MachO::any_relocation_info MRE; 282 MRE.r_word0 = FixupOffset; 283 MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28); 284 Writer->addRelocation(A_Base, Fragment->getParent(), MRE); 285 286 RelSymbol = B_Base; 287 Type = MachO::ARM64_RELOC_SUBTRACTOR; 288 } else { // A + constant 289 const MCSymbol *Symbol = &Target.getSymA()->getSymbol(); 290 const MCSectionMachO &Section = static_cast<const MCSectionMachO &>( 291 Fragment->getParent()->getSection()); 292 293 bool CanUseLocalRelocation = 294 canUseLocalRelocation(Section, *Symbol, Log2Size); 295 if (Symbol->isTemporary() && (Value || !CanUseLocalRelocation)) { 296 const MCSection &Sec = Symbol->getSection(); 297 if (!Asm.getContext().getAsmInfo()->isSectionAtomizableBySymbols(Sec)) 298 Asm.addLocalUsedInReloc(*Symbol); 299 } 300 301 const MCSymbolData &SD = Asm.getSymbolData(*Symbol); 302 const MCSymbolData *Base = Asm.getAtom(&SD); 303 304 // If the symbol is a variable and we weren't able to get a Base for it 305 // (i.e., it's not in the symbol table associated with a section) resolve 306 // the relocation based its expansion instead. 307 if (Symbol->isVariable() && !Base) { 308 // If the evaluation is an absolute value, just use that directly 309 // to keep things easy. 310 int64_t Res; 311 if (SD.getSymbol().getVariableValue()->EvaluateAsAbsolute( 312 Res, Layout, Writer->getSectionAddressMap())) { 313 FixedValue = Res; 314 return; 315 } 316 317 // FIXME: Will the Target we already have ever have any data in it 318 // we need to preserve and merge with the new Target? How about 319 // the FixedValue? 320 if (!Symbol->getVariableValue()->EvaluateAsRelocatable(Target, &Layout, 321 &Fixup)) 322 Asm.getContext().FatalError(Fixup.getLoc(), 323 "unable to resolve variable '" + 324 Symbol->getName() + "'"); 325 return RecordRelocation(Writer, Asm, Layout, Fragment, Fixup, Target, 326 FixedValue); 327 } 328 329 // Relocations inside debug sections always use local relocations when 330 // possible. This seems to be done because the debugger doesn't fully 331 // understand relocation entries and expects to find values that 332 // have already been fixed up. 333 if (Symbol->isInSection()) { 334 if (Section.hasAttribute(MachO::S_ATTR_DEBUG)) 335 Base = nullptr; 336 } 337 338 // AArch64 uses external relocations as much as possible. For debug 339 // sections, and for pointer-sized relocations (.quad), we allow section 340 // relocations. It's code sections that run into trouble. 341 if (Base) { 342 RelSymbol = Base; 343 344 // Add the local offset, if needed. 345 if (Base != &SD) 346 Value += Layout.getSymbolOffset(&SD) - Layout.getSymbolOffset(Base); 347 } else if (Symbol->isInSection()) { 348 if (!CanUseLocalRelocation) 349 Asm.getContext().FatalError( 350 Fixup.getLoc(), 351 "unsupported relocation of local symbol '" + Symbol->getName() + 352 "'. Must have non-local symbol earlier in section."); 353 // Adjust the relocation to be section-relative. 354 // The index is the section ordinal (1-based). 355 const MCSectionData &SymSD = 356 Asm.getSectionData(SD.getSymbol().getSection()); 357 Index = SymSD.getOrdinal() + 1; 358 Value += Writer->getSymbolAddress(&SD, Layout); 359 360 if (IsPCRel) 361 Value -= Writer->getFragmentAddress(Fragment, Layout) + 362 Fixup.getOffset() + (1ULL << Log2Size); 363 } else { 364 // Resolve constant variables. 365 if (SD.getSymbol().isVariable()) { 366 int64_t Res; 367 if (SD.getSymbol().getVariableValue()->EvaluateAsAbsolute( 368 Res, Layout, Writer->getSectionAddressMap())) { 369 FixedValue = Res; 370 return; 371 } 372 } 373 Asm.getContext().FatalError(Fixup.getLoc(), 374 "unsupported relocation of variable '" + 375 Symbol->getName() + "'"); 376 } 377 } 378 379 // If the relocation kind is Branch26, Page21, or Pageoff12, any addend 380 // is represented via an Addend relocation, not encoded directly into 381 // the instruction. 382 if ((Type == MachO::ARM64_RELOC_BRANCH26 || 383 Type == MachO::ARM64_RELOC_PAGE21 || 384 Type == MachO::ARM64_RELOC_PAGEOFF12) && 385 Value) { 386 assert((Value & 0xff000000) == 0 && "Added relocation out of range!"); 387 388 MachO::any_relocation_info MRE; 389 MRE.r_word0 = FixupOffset; 390 MRE.r_word1 = 391 (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28); 392 Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE); 393 394 // Now set up the Addend relocation. 395 Type = MachO::ARM64_RELOC_ADDEND; 396 Index = Value; 397 RelSymbol = nullptr; 398 IsPCRel = 0; 399 Log2Size = 2; 400 401 // Put zero into the instruction itself. The addend is in the relocation. 402 Value = 0; 403 } 404 405 // If there's any addend left to handle, encode it in the instruction. 406 FixedValue = Value; 407 408 // struct relocation_info (8 bytes) 409 MachO::any_relocation_info MRE; 410 MRE.r_word0 = FixupOffset; 411 MRE.r_word1 = 412 (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28); 413 Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE); 414 } 415 416 MCObjectWriter *llvm::createAArch64MachObjectWriter(raw_ostream &OS, 417 uint32_t CPUType, 418 uint32_t CPUSubtype) { 419 return createMachObjectWriter( 420 new AArch64MachObjectWriter(CPUType, CPUSubtype), OS, 421 /*IsLittleEndian=*/true); 422 } 423