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().reportFatalError(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() == "__objc_classrefs") 135 return false; 136 137 // FIXME: ld64 currently handles internal pointer-sized relocations 138 // incorrectly (applying the addend twice). We should be able to return true 139 // unconditionally by this point when that's fixed. 140 return false; 141 } 142 143 void AArch64MachObjectWriter::RecordRelocation( 144 MachObjectWriter *Writer, MCAssembler &Asm, const MCAsmLayout &Layout, 145 const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, 146 uint64_t &FixedValue) { 147 unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind()); 148 149 // See <reloc.h>. 150 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment); 151 unsigned Log2Size = 0; 152 int64_t Value = 0; 153 unsigned Index = 0; 154 unsigned Type = 0; 155 unsigned Kind = Fixup.getKind(); 156 const MCSymbol *RelSymbol = nullptr; 157 158 FixupOffset += Fixup.getOffset(); 159 160 // AArch64 pcrel relocation addends do not include the section offset. 161 if (IsPCRel) 162 FixedValue += FixupOffset; 163 164 // ADRP fixups use relocations for the whole symbol value and only 165 // put the addend in the instruction itself. Clear out any value the 166 // generic code figured out from the sybmol definition. 167 if (Kind == AArch64::fixup_aarch64_pcrel_adrp_imm21) 168 FixedValue = 0; 169 170 // imm19 relocations are for conditional branches, which require 171 // assembler local symbols. If we got here, that's not what we have, 172 // so complain loudly. 173 if (Kind == AArch64::fixup_aarch64_pcrel_branch19) { 174 Asm.getContext().reportFatalError(Fixup.getLoc(), 175 "conditional branch requires assembler-local" 176 " label. '" + 177 Target.getSymA()->getSymbol().getName() + 178 "' is external."); 179 return; 180 } 181 182 // 14-bit branch relocations should only target internal labels, and so 183 // should never get here. 184 if (Kind == AArch64::fixup_aarch64_pcrel_branch14) { 185 Asm.getContext().reportFatalError(Fixup.getLoc(), 186 "Invalid relocation on conditional branch!"); 187 return; 188 } 189 190 if (!getAArch64FixupKindMachOInfo(Fixup, Type, Target.getSymA(), Log2Size, 191 Asm)) { 192 Asm.getContext().reportFatalError(Fixup.getLoc(), "unknown AArch64 fixup kind!"); 193 return; 194 } 195 196 Value = Target.getConstant(); 197 198 if (Target.isAbsolute()) { // constant 199 // FIXME: Should this always be extern? 200 // SymbolNum of 0 indicates the absolute section. 201 Type = MachO::ARM64_RELOC_UNSIGNED; 202 203 if (IsPCRel) { 204 Asm.getContext().reportFatalError(Fixup.getLoc(), 205 "PC relative absolute relocation!"); 206 207 // FIXME: x86_64 sets the type to a branch reloc here. Should we do 208 // something similar? 209 } 210 } else if (Target.getSymB()) { // A - B + constant 211 const MCSymbol *A = &Target.getSymA()->getSymbol(); 212 const MCSymbolData &A_SD = Asm.getSymbolData(*A); 213 const MCSymbol *A_Base = Asm.getAtom(&A_SD); 214 215 const MCSymbol *B = &Target.getSymB()->getSymbol(); 216 const MCSymbolData &B_SD = Asm.getSymbolData(*B); 217 const MCSymbol *B_Base = Asm.getAtom(&B_SD); 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_SD) == 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().reportFatalError(Fixup.getLoc(), 238 "unsupported relocation of modified symbol"); 239 240 // We don't support PCrel relocations of differences. 241 if (IsPCRel) 242 Asm.getContext().reportFatalError(Fixup.getLoc(), 243 "unsupported pc-relative relocation of " 244 "difference"); 245 246 // AArch64 always uses external relocations. If there is no symbol to use as 247 // a base address (a local symbol with no preceding non-local symbol), 248 // error out. 249 // 250 // FIXME: We should probably just synthesize an external symbol and use 251 // that. 252 if (!A_Base) 253 Asm.getContext().reportFatalError( 254 Fixup.getLoc(), 255 "unsupported relocation of local symbol '" + A->getName() + 256 "'. Must have non-local symbol earlier in section."); 257 if (!B_Base) 258 Asm.getContext().reportFatalError( 259 Fixup.getLoc(), 260 "unsupported relocation of local symbol '" + B->getName() + 261 "'. Must have non-local symbol earlier in section."); 262 263 if (A_Base == B_Base && A_Base) 264 Asm.getContext().reportFatalError(Fixup.getLoc(), 265 "unsupported relocation with identical base"); 266 267 Value += 268 (!A_SD.getFragment() ? 0 : Writer->getSymbolAddress(&A_SD, Layout)) - 269 (!A_Base || !A_Base->getData().getFragment() 270 ? 0 271 : Writer->getSymbolAddress(&A_Base->getData(), Layout)); 272 Value -= 273 (!B_SD.getFragment() ? 0 : Writer->getSymbolAddress(&B_SD, Layout)) - 274 (!B_Base || !B_Base->getData().getFragment() 275 ? 0 276 : Writer->getSymbolAddress(&B_Base->getData(), Layout)); 277 278 Type = MachO::ARM64_RELOC_UNSIGNED; 279 280 MachO::any_relocation_info MRE; 281 MRE.r_word0 = FixupOffset; 282 MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28); 283 Writer->addRelocation(A_Base, Fragment->getParent(), MRE); 284 285 RelSymbol = B_Base; 286 Type = MachO::ARM64_RELOC_SUBTRACTOR; 287 } else { // A + constant 288 const MCSymbol *Symbol = &Target.getSymA()->getSymbol(); 289 const MCSectionMachO &Section = static_cast<const MCSectionMachO &>( 290 Fragment->getParent()->getSection()); 291 292 bool CanUseLocalRelocation = 293 canUseLocalRelocation(Section, *Symbol, Log2Size); 294 if (Symbol->isTemporary() && (Value || !CanUseLocalRelocation)) { 295 const MCSection &Sec = Symbol->getSection(); 296 if (!Asm.getContext().getAsmInfo()->isSectionAtomizableBySymbols(Sec)) 297 Asm.addLocalUsedInReloc(*Symbol); 298 } 299 300 const MCSymbolData &SD = Asm.getSymbolData(*Symbol); 301 const MCSymbol *Base = Asm.getAtom(&SD); 302 303 // If the symbol is a variable and we weren't able to get a Base for it 304 // (i.e., it's not in the symbol table associated with a section) resolve 305 // the relocation based its expansion instead. 306 if (Symbol->isVariable() && !Base) { 307 // If the evaluation is an absolute value, just use that directly 308 // to keep things easy. 309 int64_t Res; 310 if (SD.getSymbol().getVariableValue()->EvaluateAsAbsolute( 311 Res, Layout, Writer->getSectionAddressMap())) { 312 FixedValue = Res; 313 return; 314 } 315 316 // FIXME: Will the Target we already have ever have any data in it 317 // we need to preserve and merge with the new Target? How about 318 // the FixedValue? 319 if (!Symbol->getVariableValue()->EvaluateAsRelocatable(Target, &Layout, 320 &Fixup)) 321 Asm.getContext().reportFatalError(Fixup.getLoc(), 322 "unable to resolve variable '" + 323 Symbol->getName() + "'"); 324 return RecordRelocation(Writer, Asm, Layout, Fragment, Fixup, Target, 325 FixedValue); 326 } 327 328 // Relocations inside debug sections always use local relocations when 329 // possible. This seems to be done because the debugger doesn't fully 330 // understand relocation entries and expects to find values that 331 // have already been fixed up. 332 if (Symbol->isInSection()) { 333 if (Section.hasAttribute(MachO::S_ATTR_DEBUG)) 334 Base = nullptr; 335 } 336 337 // AArch64 uses external relocations as much as possible. For debug 338 // sections, and for pointer-sized relocations (.quad), we allow section 339 // relocations. It's code sections that run into trouble. 340 if (Base) { 341 RelSymbol = Base; 342 343 // Add the local offset, if needed. 344 if (&Base->getData() != &SD) 345 Value += Layout.getSymbolOffset(&SD) - 346 Layout.getSymbolOffset(&Base->getData()); 347 } else if (Symbol->isInSection()) { 348 if (!CanUseLocalRelocation) 349 Asm.getContext().reportFatalError( 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().reportFatalError(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_pwrite_stream &OS, 417 uint32_t CPUType, 418 uint32_t CPUSubtype) { 419 return createMachObjectWriter( 420 new AArch64MachObjectWriter(CPUType, CPUSubtype), OS, 421 /*IsLittleEndian=*/true); 422 } 423