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