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