1//===-- AArch64MachObjectWriter.cpp - ARM Mach Object Writer --------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10#include "MCTargetDesc/AArch64FixupKinds.h" 11#include "MCTargetDesc/AArch64MCTargetDesc.h" 12#include "llvm/MC/MCAssembler.h" 13#include "llvm/MC/MCAsmLayout.h" 14#include "llvm/MC/MCContext.h" 15#include "llvm/MC/MCExpr.h" 16#include "llvm/MC/MCFixup.h" 17#include "llvm/MC/MCMachObjectWriter.h" 18#include "llvm/MC/MCSectionMachO.h" 19#include "llvm/MC/MCValue.h" 20#include "llvm/ADT/Twine.h" 21#include "llvm/Support/ErrorHandling.h" 22#include "llvm/Support/MachO.h" 23using namespace llvm; 24 25namespace { 26class AArch64MachObjectWriter : public MCMachObjectTargetWriter { 27 bool getAArch64FixupKindMachOInfo(const MCFixup &Fixup, unsigned &RelocType, 28 const MCSymbolRefExpr *Sym, 29 unsigned &Log2Size, const MCAssembler &Asm); 30 31public: 32 AArch64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype) 33 : MCMachObjectTargetWriter(true /* is64Bit */, CPUType, CPUSubtype, 34 /*UseAggressiveSymbolFolding=*/true) {} 35 36 void RecordRelocation(MachObjectWriter *Writer, const MCAssembler &Asm, 37 const MCAsmLayout &Layout, const MCFragment *Fragment, 38 const MCFixup &Fixup, MCValue Target, 39 uint64_t &FixedValue) override; 40}; 41} 42 43bool AArch64MachObjectWriter::getAArch64FixupKindMachOInfo( 44 const MCFixup &Fixup, unsigned &RelocType, const MCSymbolRefExpr *Sym, 45 unsigned &Log2Size, const MCAssembler &Asm) { 46 RelocType = unsigned(MachO::ARM64_RELOC_UNSIGNED); 47 Log2Size = ~0U; 48 49 switch ((unsigned)Fixup.getKind()) { 50 default: 51 return false; 52 53 case FK_Data_1: 54 Log2Size = llvm::Log2_32(1); 55 return true; 56 case FK_Data_2: 57 Log2Size = llvm::Log2_32(2); 58 return true; 59 case FK_Data_4: 60 Log2Size = llvm::Log2_32(4); 61 if (Sym->getKind() == MCSymbolRefExpr::VK_GOT) 62 RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT); 63 return true; 64 case FK_Data_8: 65 Log2Size = llvm::Log2_32(8); 66 if (Sym->getKind() == MCSymbolRefExpr::VK_GOT) 67 RelocType = unsigned(MachO::ARM64_RELOC_POINTER_TO_GOT); 68 return true; 69 case AArch64::fixup_aarch64_add_imm12: 70 case AArch64::fixup_aarch64_ldst_imm12_scale1: 71 case AArch64::fixup_aarch64_ldst_imm12_scale2: 72 case AArch64::fixup_aarch64_ldst_imm12_scale4: 73 case AArch64::fixup_aarch64_ldst_imm12_scale8: 74 case AArch64::fixup_aarch64_ldst_imm12_scale16: 75 Log2Size = llvm::Log2_32(4); 76 switch (Sym->getKind()) { 77 default: 78 llvm_unreachable("Unexpected symbol reference variant kind!"); 79 case MCSymbolRefExpr::VK_PAGEOFF: 80 RelocType = unsigned(MachO::ARM64_RELOC_PAGEOFF12); 81 return true; 82 case MCSymbolRefExpr::VK_GOTPAGEOFF: 83 RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGEOFF12); 84 return true; 85 case MCSymbolRefExpr::VK_TLVPPAGEOFF: 86 RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGEOFF12); 87 return true; 88 } 89 case AArch64::fixup_aarch64_pcrel_adrp_imm21: 90 Log2Size = llvm::Log2_32(4); 91 // This encompasses the relocation for the whole 21-bit value. 92 switch (Sym->getKind()) { 93 default: 94 Asm.getContext().FatalError(Fixup.getLoc(), 95 "ADR/ADRP relocations must be GOT relative"); 96 case MCSymbolRefExpr::VK_PAGE: 97 RelocType = unsigned(MachO::ARM64_RELOC_PAGE21); 98 return true; 99 case MCSymbolRefExpr::VK_GOTPAGE: 100 RelocType = unsigned(MachO::ARM64_RELOC_GOT_LOAD_PAGE21); 101 return true; 102 case MCSymbolRefExpr::VK_TLVPPAGE: 103 RelocType = unsigned(MachO::ARM64_RELOC_TLVP_LOAD_PAGE21); 104 return true; 105 } 106 return true; 107 case AArch64::fixup_aarch64_pcrel_branch26: 108 case AArch64::fixup_aarch64_pcrel_call26: 109 Log2Size = llvm::Log2_32(4); 110 RelocType = unsigned(MachO::ARM64_RELOC_BRANCH26); 111 return true; 112 } 113} 114 115void AArch64MachObjectWriter::RecordRelocation( 116 MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout, 117 const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, 118 uint64_t &FixedValue) { 119 unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind()); 120 121 // See <reloc.h>. 122 uint32_t FixupOffset = Layout.getFragmentOffset(Fragment); 123 unsigned Log2Size = 0; 124 int64_t Value = 0; 125 unsigned Index = 0; 126 unsigned IsExtern = 0; 127 unsigned Type = 0; 128 unsigned Kind = Fixup.getKind(); 129 130 FixupOffset += Fixup.getOffset(); 131 132 // AArch64 pcrel relocation addends do not include the section offset. 133 if (IsPCRel) 134 FixedValue += FixupOffset; 135 136 // ADRP fixups use relocations for the whole symbol value and only 137 // put the addend in the instruction itself. Clear out any value the 138 // generic code figured out from the sybmol definition. 139 if (Kind == AArch64::fixup_aarch64_pcrel_adrp_imm21) 140 FixedValue = 0; 141 142 // imm19 relocations are for conditional branches, which require 143 // assembler local symbols. If we got here, that's not what we have, 144 // so complain loudly. 145 if (Kind == AArch64::fixup_aarch64_pcrel_branch19) { 146 Asm.getContext().FatalError(Fixup.getLoc(), 147 "conditional branch requires assembler-local" 148 " label. '" + 149 Target.getSymA()->getSymbol().getName() + 150 "' is external."); 151 return; 152 } 153 154 // 14-bit branch relocations should only target internal labels, and so 155 // should never get here. 156 if (Kind == AArch64::fixup_aarch64_pcrel_branch14) { 157 Asm.getContext().FatalError(Fixup.getLoc(), 158 "Invalid relocation on conditional branch!"); 159 return; 160 } 161 162 if (!getAArch64FixupKindMachOInfo(Fixup, Type, Target.getSymA(), Log2Size, 163 Asm)) { 164 Asm.getContext().FatalError(Fixup.getLoc(), "unknown AArch64 fixup kind!"); 165 return; 166 } 167 168 Value = Target.getConstant(); 169 170 if (Target.isAbsolute()) { // constant 171 // FIXME: Should this always be extern? 172 // SymbolNum of 0 indicates the absolute section. 173 Type = MachO::ARM64_RELOC_UNSIGNED; 174 Index = 0; 175 176 if (IsPCRel) { 177 IsExtern = 1; 178 Asm.getContext().FatalError(Fixup.getLoc(), 179 "PC relative absolute relocation!"); 180 181 // FIXME: x86_64 sets the type to a branch reloc here. Should we do 182 // something similar? 183 } 184 } else if (Target.getSymB()) { // A - B + constant 185 const MCSymbol *A = &Target.getSymA()->getSymbol(); 186 const MCSymbolData &A_SD = Asm.getSymbolData(*A); 187 const MCSymbolData *A_Base = Asm.getAtom(&A_SD); 188 189 const MCSymbol *B = &Target.getSymB()->getSymbol(); 190 const MCSymbolData &B_SD = Asm.getSymbolData(*B); 191 const MCSymbolData *B_Base = Asm.getAtom(&B_SD); 192 193 // Check for "_foo@got - .", which comes through here as: 194 // Ltmp0: 195 // ... _foo@got - Ltmp0 196 if (Target.getSymA()->getKind() == MCSymbolRefExpr::VK_GOT && 197 Target.getSymB()->getKind() == MCSymbolRefExpr::VK_None && 198 Layout.getSymbolOffset(&B_SD) == 199 Layout.getFragmentOffset(Fragment) + Fixup.getOffset()) { 200 // SymB is the PC, so use a PC-rel pointer-to-GOT relocation. 201 Index = A_Base->getIndex(); 202 IsExtern = 1; 203 Type = MachO::ARM64_RELOC_POINTER_TO_GOT; 204 IsPCRel = 1; 205 MachO::any_relocation_info MRE; 206 MRE.r_word0 = FixupOffset; 207 MRE.r_word1 = ((Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | 208 (IsExtern << 27) | (Type << 28)); 209 Writer->addRelocation(Fragment->getParent(), MRE); 210 return; 211 } else if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None || 212 Target.getSymB()->getKind() != MCSymbolRefExpr::VK_None) 213 // Otherwise, neither symbol can be modified. 214 Asm.getContext().FatalError(Fixup.getLoc(), 215 "unsupported relocation of modified symbol"); 216 217 // We don't support PCrel relocations of differences. 218 if (IsPCRel) 219 Asm.getContext().FatalError(Fixup.getLoc(), 220 "unsupported pc-relative relocation of " 221 "difference"); 222 223 // AArch64 always uses external relocations. If there is no symbol to use as 224 // a base address (a local symbol with no preceding non-local symbol), 225 // error out. 226 // 227 // FIXME: We should probably just synthesize an external symbol and use 228 // that. 229 if (!A_Base) 230 Asm.getContext().FatalError( 231 Fixup.getLoc(), 232 "unsupported relocation of local symbol '" + A->getName() + 233 "'. Must have non-local symbol earlier in section."); 234 if (!B_Base) 235 Asm.getContext().FatalError( 236 Fixup.getLoc(), 237 "unsupported relocation of local symbol '" + B->getName() + 238 "'. Must have non-local symbol earlier in section."); 239 240 if (A_Base == B_Base && A_Base) 241 Asm.getContext().FatalError(Fixup.getLoc(), 242 "unsupported relocation with identical base"); 243 244 Value += (!A_SD.getFragment() ? 0 245 : Writer->getSymbolAddress(&A_SD, Layout)) - 246 (!A_Base || !A_Base->getFragment() 247 ? 0 248 : Writer->getSymbolAddress(A_Base, Layout)); 249 Value -= (!B_SD.getFragment() ? 0 250 : Writer->getSymbolAddress(&B_SD, Layout)) - 251 (!B_Base || !B_Base->getFragment() 252 ? 0 253 : Writer->getSymbolAddress(B_Base, Layout)); 254 255 Index = A_Base->getIndex(); 256 IsExtern = 1; 257 Type = MachO::ARM64_RELOC_UNSIGNED; 258 259 MachO::any_relocation_info MRE; 260 MRE.r_word0 = FixupOffset; 261 MRE.r_word1 = ((Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | 262 (IsExtern << 27) | (Type << 28)); 263 Writer->addRelocation(Fragment->getParent(), MRE); 264 265 Index = B_Base->getIndex(); 266 IsExtern = 1; 267 Type = MachO::ARM64_RELOC_SUBTRACTOR; 268 } else { // A + constant 269 const MCSymbol *Symbol = &Target.getSymA()->getSymbol(); 270 const MCSymbolData &SD = Asm.getSymbolData(*Symbol); 271 const MCSymbolData *Base = Asm.getAtom(&SD); 272 const MCSectionMachO &Section = static_cast<const MCSectionMachO &>( 273 Fragment->getParent()->getSection()); 274 275 // If the symbol is a variable and we weren't able to get a Base for it 276 // (i.e., it's not in the symbol table associated with a section) resolve 277 // the relocation based its expansion instead. 278 if (Symbol->isVariable() && !Base) { 279 // If the evaluation is an absolute value, just use that directly 280 // to keep things easy. 281 int64_t Res; 282 if (SD.getSymbol().getVariableValue()->EvaluateAsAbsolute( 283 Res, Layout, Writer->getSectionAddressMap())) { 284 FixedValue = Res; 285 return; 286 } 287 288 // FIXME: Will the Target we already have ever have any data in it 289 // we need to preserve and merge with the new Target? How about 290 // the FixedValue? 291 if (!Symbol->getVariableValue()->EvaluateAsRelocatable(Target, &Layout)) 292 Asm.getContext().FatalError(Fixup.getLoc(), 293 "unable to resolve variable '" + 294 Symbol->getName() + "'"); 295 return RecordRelocation(Writer, Asm, Layout, Fragment, Fixup, Target, 296 FixedValue); 297 } 298 299 // Relocations inside debug sections always use local relocations when 300 // possible. This seems to be done because the debugger doesn't fully 301 // understand relocation entries and expects to find values that 302 // have already been fixed up. 303 if (Symbol->isInSection()) { 304 if (Section.hasAttribute(MachO::S_ATTR_DEBUG)) 305 Base = nullptr; 306 } 307 308 // AArch64 uses external relocations as much as possible. For debug 309 // sections, and for pointer-sized relocations (.quad), we allow section 310 // relocations. It's code sections that run into trouble. 311 if (Base) { 312 Index = Base->getIndex(); 313 IsExtern = 1; 314 315 // Add the local offset, if needed. 316 if (Base != &SD) 317 Value += Layout.getSymbolOffset(&SD) - Layout.getSymbolOffset(Base); 318 } else if (Symbol->isInSection()) { 319 // Pointer-sized relocations can use a local relocation. Otherwise, 320 // we have to be in a debug info section. 321 if (!Section.hasAttribute(MachO::S_ATTR_DEBUG) && Log2Size != 3) 322 Asm.getContext().FatalError( 323 Fixup.getLoc(), 324 "unsupported relocation of local symbol '" + Symbol->getName() + 325 "'. Must have non-local symbol earlier in section."); 326 // Adjust the relocation to be section-relative. 327 // The index is the section ordinal (1-based). 328 const MCSectionData &SymSD = 329 Asm.getSectionData(SD.getSymbol().getSection()); 330 Index = SymSD.getOrdinal() + 1; 331 IsExtern = 0; 332 Value += Writer->getSymbolAddress(&SD, Layout); 333 334 if (IsPCRel) 335 Value -= Writer->getFragmentAddress(Fragment, Layout) + 336 Fixup.getOffset() + (1ULL << Log2Size); 337 } else { 338 // Resolve constant variables. 339 if (SD.getSymbol().isVariable()) { 340 int64_t Res; 341 if (SD.getSymbol().getVariableValue()->EvaluateAsAbsolute( 342 Res, Layout, Writer->getSectionAddressMap())) { 343 FixedValue = Res; 344 return; 345 } 346 } 347 Asm.getContext().FatalError(Fixup.getLoc(), 348 "unsupported relocation of variable '" + 349 Symbol->getName() + "'"); 350 } 351 } 352 353 // If the relocation kind is Branch26, Page21, or Pageoff12, any addend 354 // is represented via an Addend relocation, not encoded directly into 355 // the instruction. 356 if ((Type == MachO::ARM64_RELOC_BRANCH26 || 357 Type == MachO::ARM64_RELOC_PAGE21 || 358 Type == MachO::ARM64_RELOC_PAGEOFF12) && 359 Value) { 360 assert((Value & 0xff000000) == 0 && "Added relocation out of range!"); 361 362 MachO::any_relocation_info MRE; 363 MRE.r_word0 = FixupOffset; 364 MRE.r_word1 = ((Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | 365 (IsExtern << 27) | (Type << 28)); 366 Writer->addRelocation(Fragment->getParent(), MRE); 367 368 // Now set up the Addend relocation. 369 Type = MachO::ARM64_RELOC_ADDEND; 370 Index = Value; 371 IsPCRel = 0; 372 Log2Size = 2; 373 IsExtern = 0; 374 375 // Put zero into the instruction itself. The addend is in the relocation. 376 Value = 0; 377 } 378 379 // If there's any addend left to handle, encode it in the instruction. 380 FixedValue = Value; 381 382 // struct relocation_info (8 bytes) 383 MachO::any_relocation_info MRE; 384 MRE.r_word0 = FixupOffset; 385 MRE.r_word1 = ((Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | 386 (IsExtern << 27) | (Type << 28)); 387 Writer->addRelocation(Fragment->getParent(), MRE); 388} 389 390MCObjectWriter *llvm::createAArch64MachObjectWriter(raw_ostream &OS, 391 uint32_t CPUType, 392 uint32_t CPUSubtype) { 393 return createMachObjectWriter( 394 new AArch64MachObjectWriter(CPUType, CPUSubtype), OS, 395 /*IsLittleEndian=*/true); 396} 397