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/BinaryFormat/MachO.h" 14 #include "llvm/MC/MCAsmInfo.h" 15 #include "llvm/MC/MCAsmLayout.h" 16 #include "llvm/MC/MCAssembler.h" 17 #include "llvm/MC/MCContext.h" 18 #include "llvm/MC/MCExpr.h" 19 #include "llvm/MC/MCFixup.h" 20 #include "llvm/MC/MCFragment.h" 21 #include "llvm/MC/MCMachObjectWriter.h" 22 #include "llvm/MC/MCSection.h" 23 #include "llvm/MC/MCSectionMachO.h" 24 #include "llvm/MC/MCSymbol.h" 25 #include "llvm/MC/MCValue.h" 26 #include "llvm/Support/Casting.h" 27 #include "llvm/Support/MathExtras.h" 28 #include <cassert> 29 #include <cstdint> 30 31 using namespace llvm; 32 33 namespace { 34 35 class AArch64MachObjectWriter : public MCMachObjectTargetWriter { 36 bool getAArch64FixupKindMachOInfo(const MCFixup &Fixup, unsigned &RelocType, 37 const MCSymbolRefExpr *Sym, 38 unsigned &Log2Size, const MCAssembler &Asm); 39 40 public: 41 AArch64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype) 42 : MCMachObjectTargetWriter(true /* is64Bit */, CPUType, CPUSubtype) {} 43 44 void recordRelocation(MachObjectWriter *Writer, MCAssembler &Asm, 45 const MCAsmLayout &Layout, const MCFragment *Fragment, 46 const MCFixup &Fixup, MCValue Target, 47 uint64_t &FixedValue) override; 48 }; 49 50 } // end anonymous namespace 51 52 bool AArch64MachObjectWriter::getAArch64FixupKindMachOInfo( 53 const MCFixup &Fixup, unsigned &RelocType, const MCSymbolRefExpr *Sym, 54 unsigned &Log2Size, const MCAssembler &Asm) { 55 RelocType = unsigned(MachO::ARM64_RELOC_UNSIGNED); 56 Log2Size = ~0U; 57 58 switch ((unsigned)Fixup.getKind()) { 59 default: 60 return false; 61 62 case FK_Data_1: 63 Log2Size = Log2_32(1); 64 return true; 65 case FK_Data_2: 66 Log2Size = Log2_32(2); 67 return true; 68 case FK_Data_4: 69 Log2Size = Log2_32(4); 70 if (Sym->getKind() == MCSymbolRefExpr::VK_GOT) 71 RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT); 72 return true; 73 case FK_Data_8: 74 Log2Size = Log2_32(8); 75 if (Sym->getKind() == MCSymbolRefExpr::VK_GOT) 76 RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT); 77 return true; 78 case AArch64::fixup_aarch64_add_imm12: 79 case AArch64::fixup_aarch64_ldst_imm12_scale1: 80 case AArch64::fixup_aarch64_ldst_imm12_scale2: 81 case AArch64::fixup_aarch64_ldst_imm12_scale4: 82 case AArch64::fixup_aarch64_ldst_imm12_scale8: 83 case AArch64::fixup_aarch64_ldst_imm12_scale16: 84 Log2Size = Log2_32(4); 85 switch (Sym->getKind()) { 86 default: 87 return false; 88 case MCSymbolRefExpr::VK_PAGEOFF: 89 RelocType = unsigned(MachO::ARM64_RELOC_PAGEOFF12); 90 return true; 91 case MCSymbolRefExpr::VK_GOTPAGEOFF: 92 RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12); 93 return true; 94 case MCSymbolRefExpr::VK_TLVPPAGEOFF: 95 RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12); 96 return true; 97 } 98 case AArch64::fixup_aarch64_pcrel_adrp_imm21: 99 Log2Size = Log2_32(4); 100 // This encompasses the relocation for the whole 21-bit value. 101 switch (Sym->getKind()) { 102 default: 103 Asm.getContext().reportError(Fixup.getLoc(), 104 "ADR/ADRP relocations must be GOT relative"); 105 return false; 106 case MCSymbolRefExpr::VK_PAGE: 107 RelocType = unsigned(MachO::ARM64_RELOC_PAGE21); 108 return true; 109 case MCSymbolRefExpr::VK_GOTPAGE: 110 RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGE21); 111 return true; 112 case MCSymbolRefExpr::VK_TLVPPAGE: 113 RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGE21); 114 return true; 115 } 116 return true; 117 case AArch64::fixup_aarch64_pcrel_branch26: 118 case AArch64::fixup_aarch64_pcrel_call26: 119 Log2Size = Log2_32(4); 120 RelocType = unsigned(MachO::ARM64_RELOC_BRANCH26); 121 return true; 122 } 123 } 124 125 static bool canUseLocalRelocation(const MCSectionMachO &Section, 126 const MCSymbol &Symbol, unsigned Log2Size) { 127 // Debug info sections can use local relocations. 128 if (Section.hasAttribute(MachO::S_ATTR_DEBUG)) 129 return true; 130 131 // Otherwise, only pointer sized relocations are supported. 132 if (Log2Size != 3) 133 return false; 134 135 // But only if they don't point to a few forbidden sections. 136 if (!Symbol.isInSection()) 137 return true; 138 const MCSectionMachO &RefSec = cast<MCSectionMachO>(Symbol.getSection()); 139 if (RefSec.getType() == MachO::S_CSTRING_LITERALS) 140 return false; 141 142 if (RefSec.getSegmentName() == "__DATA" && 143 RefSec.getSectionName() == "__objc_classrefs") 144 return false; 145 146 // FIXME: ld64 currently handles internal pointer-sized relocations 147 // incorrectly (applying the addend twice). We should be able to return true 148 // unconditionally by this point when that's fixed. 149 return false; 150 } 151 152 void AArch64MachObjectWriter::recordRelocation( 153 MachObjectWriter *Writer, MCAssembler &Asm, const MCAsmLayout &Layout, 154 const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, 155 uint64_t &FixedValue) { 156 unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind()); 157 158 // See <reloc.h>. 159 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment); 160 unsigned Log2Size = 0; 161 int64_t Value = 0; 162 unsigned Index = 0; 163 unsigned Type = 0; 164 unsigned Kind = Fixup.getKind(); 165 const MCSymbol *RelSymbol = nullptr; 166 167 FixupOffset += Fixup.getOffset(); 168 169 // AArch64 pcrel relocation addends do not include the section offset. 170 if (IsPCRel) 171 FixedValue += FixupOffset; 172 173 // ADRP fixups use relocations for the whole symbol value and only 174 // put the addend in the instruction itself. Clear out any value the 175 // generic code figured out from the sybmol definition. 176 if (Kind == AArch64::fixup_aarch64_pcrel_adrp_imm21) 177 FixedValue = 0; 178 179 // imm19 relocations are for conditional branches, which require 180 // assembler local symbols. If we got here, that's not what we have, 181 // so complain loudly. 182 if (Kind == AArch64::fixup_aarch64_pcrel_branch19) { 183 Asm.getContext().reportError(Fixup.getLoc(), 184 "conditional branch requires assembler-local" 185 " label. '" + 186 Target.getSymA()->getSymbol().getName() + 187 "' is external."); 188 return; 189 } 190 191 // 14-bit branch relocations should only target internal labels, and so 192 // should never get here. 193 if (Kind == AArch64::fixup_aarch64_pcrel_branch14) { 194 Asm.getContext().reportError(Fixup.getLoc(), 195 "Invalid relocation on conditional branch!"); 196 return; 197 } 198 199 if (!getAArch64FixupKindMachOInfo(Fixup, Type, Target.getSymA(), Log2Size, 200 Asm)) { 201 Asm.getContext().reportError(Fixup.getLoc(), "unknown AArch64 fixup kind!"); 202 return; 203 } 204 205 Value = Target.getConstant(); 206 207 if (Target.isAbsolute()) { // constant 208 // FIXME: Should this always be extern? 209 // SymbolNum of 0 indicates the absolute section. 210 Type = MachO::ARM64_RELOC_UNSIGNED; 211 212 if (IsPCRel) { 213 Asm.getContext().reportError(Fixup.getLoc(), 214 "PC relative absolute relocation!"); 215 return; 216 217 // FIXME: x86_64 sets the type to a branch reloc here. Should we do 218 // something similar? 219 } 220 } else if (Target.getSymB()) { // A - B + constant 221 const MCSymbol *A = &Target.getSymA()->getSymbol(); 222 const MCSymbol *A_Base = Asm.getAtom(*A); 223 224 const MCSymbol *B = &Target.getSymB()->getSymbol(); 225 const MCSymbol *B_Base = Asm.getAtom(*B); 226 227 // Check for "_foo@got - .", which comes through here as: 228 // Ltmp0: 229 // ... _foo@got - Ltmp0 230 if (Target.getSymA()->getKind() == MCSymbolRefExpr::VK_GOT && 231 Target.getSymB()->getKind() == MCSymbolRefExpr::VK_None && 232 Layout.getSymbolOffset(*B) == 233 Layout.getFragmentOffset(Fragment) + Fixup.getOffset()) { 234 // SymB is the PC, so use a PC-rel pointer-to-GOT relocation. 235 Type = MachO::ARM64_RELOC_POINTER_TO_GOT; 236 IsPCRel = 1; 237 MachO::any_relocation_info MRE; 238 MRE.r_word0 = FixupOffset; 239 MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28); 240 Writer->addRelocation(A_Base, Fragment->getParent(), MRE); 241 return; 242 } else if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None || 243 Target.getSymB()->getKind() != MCSymbolRefExpr::VK_None) { 244 // Otherwise, neither symbol can be modified. 245 Asm.getContext().reportError(Fixup.getLoc(), 246 "unsupported relocation of modified symbol"); 247 return; 248 } 249 250 // We don't support PCrel relocations of differences. 251 if (IsPCRel) { 252 Asm.getContext().reportError(Fixup.getLoc(), 253 "unsupported pc-relative relocation of " 254 "difference"); 255 return; 256 } 257 258 // AArch64 always uses external relocations. If there is no symbol to use as 259 // a base address (a local symbol with no preceding non-local symbol), 260 // error out. 261 // 262 // FIXME: We should probably just synthesize an external symbol and use 263 // that. 264 if (!A_Base) { 265 Asm.getContext().reportError( 266 Fixup.getLoc(), 267 "unsupported relocation of local symbol '" + A->getName() + 268 "'. Must have non-local symbol earlier in section."); 269 return; 270 } 271 if (!B_Base) { 272 Asm.getContext().reportError( 273 Fixup.getLoc(), 274 "unsupported relocation of local symbol '" + B->getName() + 275 "'. Must have non-local symbol earlier in section."); 276 return; 277 } 278 279 if (A_Base == B_Base && A_Base) { 280 Asm.getContext().reportError( 281 Fixup.getLoc(), "unsupported relocation with identical base"); 282 return; 283 } 284 285 Value += (!A->getFragment() ? 0 : Writer->getSymbolAddress(*A, Layout)) - 286 (!A_Base || !A_Base->getFragment() ? 0 : Writer->getSymbolAddress( 287 *A_Base, Layout)); 288 Value -= (!B->getFragment() ? 0 : Writer->getSymbolAddress(*B, Layout)) - 289 (!B_Base || !B_Base->getFragment() ? 0 : Writer->getSymbolAddress( 290 *B_Base, Layout)); 291 292 Type = MachO::ARM64_RELOC_UNSIGNED; 293 294 MachO::any_relocation_info MRE; 295 MRE.r_word0 = FixupOffset; 296 MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28); 297 Writer->addRelocation(A_Base, Fragment->getParent(), MRE); 298 299 RelSymbol = B_Base; 300 Type = MachO::ARM64_RELOC_SUBTRACTOR; 301 } else { // A + constant 302 const MCSymbol *Symbol = &Target.getSymA()->getSymbol(); 303 const MCSectionMachO &Section = 304 static_cast<const MCSectionMachO &>(*Fragment->getParent()); 305 306 bool CanUseLocalRelocation = 307 canUseLocalRelocation(Section, *Symbol, Log2Size); 308 if (Symbol->isTemporary() && (Value || !CanUseLocalRelocation)) { 309 // Make sure that the symbol is actually in a section here. If it isn't, 310 // emit an error and exit. 311 if (!Symbol->isInSection()) { 312 Asm.getContext().reportError( 313 Fixup.getLoc(), 314 "unsupported relocation of local symbol '" + Symbol->getName() + 315 "'. Must have non-local symbol earlier in section."); 316 return; 317 } 318 const MCSection &Sec = Symbol->getSection(); 319 if (!Asm.getContext().getAsmInfo()->isSectionAtomizableBySymbols(Sec)) 320 Symbol->setUsedInReloc(); 321 } 322 323 const MCSymbol *Base = Asm.getAtom(*Symbol); 324 // If the symbol is a variable it can either be in a section and 325 // we have a base or it is absolute and should have been expanded. 326 assert(!Symbol->isVariable() || Base); 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 != Symbol) 345 Value += 346 Layout.getSymbolOffset(*Symbol) - Layout.getSymbolOffset(*Base); 347 } else if (Symbol->isInSection()) { 348 if (!CanUseLocalRelocation) { 349 Asm.getContext().reportError( 350 Fixup.getLoc(), 351 "unsupported relocation of local symbol '" + Symbol->getName() + 352 "'. Must have non-local symbol earlier in section."); 353 return; 354 } 355 // Adjust the relocation to be section-relative. 356 // The index is the section ordinal (1-based). 357 const MCSection &Sec = Symbol->getSection(); 358 Index = Sec.getOrdinal() + 1; 359 Value += Writer->getSymbolAddress(*Symbol, Layout); 360 361 if (IsPCRel) 362 Value -= Writer->getFragmentAddress(Fragment, Layout) + 363 Fixup.getOffset() + (1ULL << Log2Size); 364 } else { 365 llvm_unreachable( 366 "This constant variable should have been expanded during evaluation"); 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 std::unique_ptr<MCObjectTargetWriter> 408 llvm::createAArch64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype) { 409 return llvm::make_unique<AArch64MachObjectWriter>(CPUType, CPUSubtype); 410 } 411