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/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/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 MCAsmLayout &Layout, const MCFragment *Fragment, 45 const MCFixup &Fixup, MCValue Target, 46 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.getSectionName() == "__objc_classrefs") 143 return false; 144 145 // FIXME: ld64 currently handles internal pointer-sized relocations 146 // incorrectly (applying the addend twice). We should be able to return true 147 // unconditionally by this point when that's fixed. 148 return false; 149 } 150 151 void AArch64MachObjectWriter::recordRelocation( 152 MachObjectWriter *Writer, MCAssembler &Asm, const MCAsmLayout &Layout, 153 const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, 154 uint64_t &FixedValue) { 155 unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind()); 156 157 // See <reloc.h>. 158 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment); 159 unsigned Log2Size = 0; 160 int64_t Value = 0; 161 unsigned Index = 0; 162 unsigned Type = 0; 163 unsigned Kind = Fixup.getKind(); 164 const MCSymbol *RelSymbol = nullptr; 165 166 FixupOffset += Fixup.getOffset(); 167 168 // AArch64 pcrel relocation addends do not include the section offset. 169 if (IsPCRel) 170 FixedValue += FixupOffset; 171 172 // ADRP fixups use relocations for the whole symbol value and only 173 // put the addend in the instruction itself. Clear out any value the 174 // generic code figured out from the sybmol definition. 175 if (Kind == AArch64::fixup_aarch64_pcrel_adrp_imm21) 176 FixedValue = 0; 177 178 // imm19 relocations are for conditional branches, which require 179 // assembler local symbols. If we got here, that's not what we have, 180 // so complain loudly. 181 if (Kind == AArch64::fixup_aarch64_pcrel_branch19) { 182 Asm.getContext().reportError(Fixup.getLoc(), 183 "conditional branch requires assembler-local" 184 " label. '" + 185 Target.getSymA()->getSymbol().getName() + 186 "' is external."); 187 return; 188 } 189 190 // 14-bit branch relocations should only target internal labels, and so 191 // should never get here. 192 if (Kind == AArch64::fixup_aarch64_pcrel_branch14) { 193 Asm.getContext().reportError(Fixup.getLoc(), 194 "Invalid relocation on conditional branch!"); 195 return; 196 } 197 198 if (!getAArch64FixupKindMachOInfo(Fixup, Type, Target.getSymA(), Log2Size, 199 Asm)) { 200 Asm.getContext().reportError(Fixup.getLoc(), "unknown AArch64 fixup kind!"); 201 return; 202 } 203 204 Value = Target.getConstant(); 205 206 if (Target.isAbsolute()) { // constant 207 // FIXME: Should this always be extern? 208 // SymbolNum of 0 indicates the absolute section. 209 Type = MachO::ARM64_RELOC_UNSIGNED; 210 211 if (IsPCRel) { 212 Asm.getContext().reportError(Fixup.getLoc(), 213 "PC relative absolute relocation!"); 214 return; 215 216 // FIXME: x86_64 sets the type to a branch reloc here. Should we do 217 // something similar? 218 } 219 } else if (Target.getSymB()) { // A - B + constant 220 const MCSymbol *A = &Target.getSymA()->getSymbol(); 221 const MCSymbol *A_Base = Asm.getAtom(*A); 222 223 const MCSymbol *B = &Target.getSymB()->getSymbol(); 224 const MCSymbol *B_Base = Asm.getAtom(*B); 225 226 // Check for "_foo@got - .", which comes through here as: 227 // Ltmp0: 228 // ... _foo@got - Ltmp0 229 if (Target.getSymA()->getKind() == MCSymbolRefExpr::VK_GOT && 230 Target.getSymB()->getKind() == MCSymbolRefExpr::VK_None && 231 Layout.getSymbolOffset(*B) == 232 Layout.getFragmentOffset(Fragment) + Fixup.getOffset()) { 233 // SymB is the PC, so use a PC-rel pointer-to-GOT relocation. 234 Type = MachO::ARM64_RELOC_POINTER_TO_GOT; 235 IsPCRel = 1; 236 MachO::any_relocation_info MRE; 237 MRE.r_word0 = FixupOffset; 238 MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28); 239 Writer->addRelocation(A_Base, Fragment->getParent(), MRE); 240 return; 241 } else if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None || 242 Target.getSymB()->getKind() != MCSymbolRefExpr::VK_None) { 243 // Otherwise, neither symbol can be modified. 244 Asm.getContext().reportError(Fixup.getLoc(), 245 "unsupported relocation of modified symbol"); 246 return; 247 } 248 249 // We don't support PCrel relocations of differences. 250 if (IsPCRel) { 251 Asm.getContext().reportError(Fixup.getLoc(), 252 "unsupported pc-relative relocation of " 253 "difference"); 254 return; 255 } 256 257 // AArch64 always uses external relocations. If there is no symbol to use as 258 // a base address (a local symbol with no preceding non-local symbol), 259 // error out. 260 // 261 // FIXME: We should probably just synthesize an external symbol and use 262 // that. 263 if (!A_Base) { 264 Asm.getContext().reportError( 265 Fixup.getLoc(), 266 "unsupported relocation of local symbol '" + A->getName() + 267 "'. Must have non-local symbol earlier in section."); 268 return; 269 } 270 if (!B_Base) { 271 Asm.getContext().reportError( 272 Fixup.getLoc(), 273 "unsupported relocation of local symbol '" + B->getName() + 274 "'. Must have non-local symbol earlier in section."); 275 return; 276 } 277 278 if (A_Base == B_Base && A_Base) { 279 Asm.getContext().reportError( 280 Fixup.getLoc(), "unsupported relocation with identical base"); 281 return; 282 } 283 284 Value += (!A->getFragment() ? 0 : Writer->getSymbolAddress(*A, Layout)) - 285 (!A_Base || !A_Base->getFragment() ? 0 : Writer->getSymbolAddress( 286 *A_Base, Layout)); 287 Value -= (!B->getFragment() ? 0 : Writer->getSymbolAddress(*B, Layout)) - 288 (!B_Base || !B_Base->getFragment() ? 0 : Writer->getSymbolAddress( 289 *B_Base, Layout)); 290 291 Type = MachO::ARM64_RELOC_UNSIGNED; 292 293 MachO::any_relocation_info MRE; 294 MRE.r_word0 = FixupOffset; 295 MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28); 296 Writer->addRelocation(A_Base, Fragment->getParent(), MRE); 297 298 RelSymbol = B_Base; 299 Type = MachO::ARM64_RELOC_SUBTRACTOR; 300 } else { // A + constant 301 const MCSymbol *Symbol = &Target.getSymA()->getSymbol(); 302 const MCSectionMachO &Section = 303 static_cast<const MCSectionMachO &>(*Fragment->getParent()); 304 305 bool CanUseLocalRelocation = 306 canUseLocalRelocation(Section, *Symbol, Log2Size); 307 if (Symbol->isTemporary() && (Value || !CanUseLocalRelocation)) { 308 // Make sure that the symbol is actually in a section here. If it isn't, 309 // emit an error and exit. 310 if (!Symbol->isInSection()) { 311 Asm.getContext().reportError( 312 Fixup.getLoc(), 313 "unsupported relocation of local symbol '" + Symbol->getName() + 314 "'. Must have non-local symbol earlier in section."); 315 return; 316 } 317 const MCSection &Sec = Symbol->getSection(); 318 if (!Asm.getContext().getAsmInfo()->isSectionAtomizableBySymbols(Sec)) 319 Symbol->setUsedInReloc(); 320 } 321 322 const MCSymbol *Base = Asm.getAtom(*Symbol); 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 += 345 Layout.getSymbolOffset(*Symbol) - Layout.getSymbolOffset(*Base); 346 } else if (Symbol->isInSection()) { 347 if (!CanUseLocalRelocation) { 348 Asm.getContext().reportError( 349 Fixup.getLoc(), 350 "unsupported relocation of local symbol '" + Symbol->getName() + 351 "'. Must have non-local symbol earlier in section."); 352 return; 353 } 354 // Adjust the relocation to be section-relative. 355 // The index is the section ordinal (1-based). 356 const MCSection &Sec = Symbol->getSection(); 357 Index = Sec.getOrdinal() + 1; 358 Value += Writer->getSymbolAddress(*Symbol, Layout); 359 360 if (IsPCRel) 361 Value -= Writer->getFragmentAddress(Fragment, Layout) + 362 Fixup.getOffset() + (1ULL << Log2Size); 363 } else { 364 llvm_unreachable( 365 "This constant variable should have been expanded during evaluation"); 366 } 367 } 368 369 // If the relocation kind is Branch26, Page21, or Pageoff12, any addend 370 // is represented via an Addend relocation, not encoded directly into 371 // the instruction. 372 if ((Type == MachO::ARM64_RELOC_BRANCH26 || 373 Type == MachO::ARM64_RELOC_PAGE21 || 374 Type == MachO::ARM64_RELOC_PAGEOFF12) && 375 Value) { 376 assert((Value & 0xff000000) == 0 && "Added relocation out of range!"); 377 378 MachO::any_relocation_info MRE; 379 MRE.r_word0 = FixupOffset; 380 MRE.r_word1 = 381 (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28); 382 Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE); 383 384 // Now set up the Addend relocation. 385 Type = MachO::ARM64_RELOC_ADDEND; 386 Index = Value; 387 RelSymbol = nullptr; 388 IsPCRel = 0; 389 Log2Size = 2; 390 391 // Put zero into the instruction itself. The addend is in the relocation. 392 Value = 0; 393 } 394 395 // If there's any addend left to handle, encode it in the instruction. 396 FixedValue = Value; 397 398 // struct relocation_info (8 bytes) 399 MachO::any_relocation_info MRE; 400 MRE.r_word0 = FixupOffset; 401 MRE.r_word1 = 402 (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28); 403 Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE); 404 } 405 406 std::unique_ptr<MCObjectTargetWriter> 407 llvm::createAArch64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype, 408 bool IsILP32) { 409 return std::make_unique<AArch64MachObjectWriter>(CPUType, CPUSubtype, 410 IsILP32); 411 } 412