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