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