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