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.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 MCAsmLayout &Layout, 151 const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, 152 uint64_t &FixedValue) { 153 unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind()); 154 155 // See <reloc.h>. 156 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment); 157 unsigned Log2Size = 0; 158 int64_t Value = 0; 159 unsigned Index = 0; 160 unsigned Type = 0; 161 unsigned Kind = Fixup.getKind(); 162 const MCSymbol *RelSymbol = nullptr; 163 164 FixupOffset += Fixup.getOffset(); 165 166 // AArch64 pcrel relocation addends do not include the section offset. 167 if (IsPCRel) 168 FixedValue += FixupOffset; 169 170 // ADRP fixups use relocations for the whole symbol value and only 171 // put the addend in the instruction itself. Clear out any value the 172 // generic code figured out from the sybmol definition. 173 if (Kind == AArch64::fixup_aarch64_pcrel_adrp_imm21) 174 FixedValue = 0; 175 176 // imm19 relocations are for conditional branches, which require 177 // assembler local symbols. If we got here, that's not what we have, 178 // so complain loudly. 179 if (Kind == AArch64::fixup_aarch64_pcrel_branch19) { 180 Asm.getContext().reportError(Fixup.getLoc(), 181 "conditional branch requires assembler-local" 182 " label. '" + 183 Target.getSymA()->getSymbol().getName() + 184 "' is external."); 185 return; 186 } 187 188 // 14-bit branch relocations should only target internal labels, and so 189 // should never get here. 190 if (Kind == AArch64::fixup_aarch64_pcrel_branch14) { 191 Asm.getContext().reportError(Fixup.getLoc(), 192 "Invalid relocation on conditional branch!"); 193 return; 194 } 195 196 if (!getAArch64FixupKindMachOInfo(Fixup, Type, Target.getSymA(), Log2Size, 197 Asm)) { 198 Asm.getContext().reportError(Fixup.getLoc(), "unknown AArch64 fixup kind!"); 199 return; 200 } 201 202 Value = Target.getConstant(); 203 204 if (Target.isAbsolute()) { // constant 205 // FIXME: Should this always be extern? 206 // SymbolNum of 0 indicates the absolute section. 207 Type = MachO::ARM64_RELOC_UNSIGNED; 208 209 if (IsPCRel) { 210 Asm.getContext().reportError(Fixup.getLoc(), 211 "PC relative absolute relocation!"); 212 return; 213 214 // FIXME: x86_64 sets the type to a branch reloc here. Should we do 215 // something similar? 216 } 217 } else if (Target.getSymB()) { // A - B + constant 218 const MCSymbol *A = &Target.getSymA()->getSymbol(); 219 const MCSymbol *A_Base = Asm.getAtom(*A); 220 221 const MCSymbol *B = &Target.getSymB()->getSymbol(); 222 const MCSymbol *B_Base = Asm.getAtom(*B); 223 224 // Check for "_foo@got - .", which comes through here as: 225 // Ltmp0: 226 // ... _foo@got - Ltmp0 227 if (Target.getSymA()->getKind() == MCSymbolRefExpr::VK_GOT && 228 Target.getSymB()->getKind() == MCSymbolRefExpr::VK_None && 229 Layout.getSymbolOffset(*B) == 230 Layout.getFragmentOffset(Fragment) + Fixup.getOffset()) { 231 // SymB is the PC, so use a PC-rel pointer-to-GOT relocation. 232 Type = MachO::ARM64_RELOC_POINTER_TO_GOT; 233 IsPCRel = 1; 234 MachO::any_relocation_info MRE; 235 MRE.r_word0 = FixupOffset; 236 MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28); 237 Writer->addRelocation(A_Base, Fragment->getParent(), MRE); 238 return; 239 } else if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None || 240 Target.getSymB()->getKind() != MCSymbolRefExpr::VK_None) { 241 // Otherwise, neither symbol can be modified. 242 Asm.getContext().reportError(Fixup.getLoc(), 243 "unsupported relocation of modified symbol"); 244 return; 245 } 246 247 // We don't support PCrel relocations of differences. 248 if (IsPCRel) { 249 Asm.getContext().reportError(Fixup.getLoc(), 250 "unsupported pc-relative relocation of " 251 "difference"); 252 return; 253 } 254 255 // AArch64 always uses external relocations. If there is no symbol to use as 256 // a base address (a local symbol with no preceding non-local symbol), 257 // error out. 258 // 259 // FIXME: We should probably just synthesize an external symbol and use 260 // that. 261 if (!A_Base) { 262 Asm.getContext().reportError( 263 Fixup.getLoc(), 264 "unsupported relocation of local symbol '" + A->getName() + 265 "'. Must have non-local symbol earlier in section."); 266 return; 267 } 268 if (!B_Base) { 269 Asm.getContext().reportError( 270 Fixup.getLoc(), 271 "unsupported relocation of local symbol '" + B->getName() + 272 "'. Must have non-local symbol earlier in section."); 273 return; 274 } 275 276 if (A_Base == B_Base && A_Base) { 277 Asm.getContext().reportError( 278 Fixup.getLoc(), "unsupported relocation with identical base"); 279 return; 280 } 281 282 Value += (!A->getFragment() ? 0 : Writer->getSymbolAddress(*A, Layout)) - 283 (!A_Base || !A_Base->getFragment() ? 0 : Writer->getSymbolAddress( 284 *A_Base, Layout)); 285 Value -= (!B->getFragment() ? 0 : Writer->getSymbolAddress(*B, Layout)) - 286 (!B_Base || !B_Base->getFragment() ? 0 : Writer->getSymbolAddress( 287 *B_Base, Layout)); 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 (!Asm.getContext().getAsmInfo()->isSectionAtomizableBySymbols(Sec)) 317 Symbol->setUsedInReloc(); 318 } 319 320 const MCSymbol *Base = Asm.getAtom(*Symbol); 321 // If the symbol is a variable it can either be in a section and 322 // we have a base or it is absolute and should have been expanded. 323 assert(!Symbol->isVariable() || Base); 324 325 // Relocations inside debug sections always use local relocations when 326 // possible. This seems to be done because the debugger doesn't fully 327 // understand relocation entries and expects to find values that 328 // have already been fixed up. 329 if (Symbol->isInSection()) { 330 if (Section.hasAttribute(MachO::S_ATTR_DEBUG)) 331 Base = nullptr; 332 } 333 334 // AArch64 uses external relocations as much as possible. For debug 335 // sections, and for pointer-sized relocations (.quad), we allow section 336 // relocations. It's code sections that run into trouble. 337 if (Base) { 338 RelSymbol = Base; 339 340 // Add the local offset, if needed. 341 if (Base != Symbol) 342 Value += 343 Layout.getSymbolOffset(*Symbol) - Layout.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, Layout); 357 358 if (IsPCRel) 359 Value -= Writer->getFragmentAddress(Fragment, Layout) + 360 Fixup.getOffset() + (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