438 lines · cpp
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-exception6//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 30using namespace llvm;31 32namespace {33 34class AArch64MachObjectWriter : public MCMachObjectTargetWriter {35 bool getAArch64FixupKindMachOInfo(const MCFixup &Fixup, unsigned &RelocType,36 AArch64::Specifier Spec, unsigned &Log2Size,37 const MCAssembler &Asm);38 39public: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 namespace49 50bool 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 122static 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 =136 static_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 148void AArch64MachObjectWriter::recordRelocation(149 MachObjectWriter *Writer, MCAssembler &Asm, const MCFragment *Fragment,150 const MCFixup &Fixup, MCValue Target, uint64_t &FixedValue) {151 unsigned IsPCRel = Fixup.isPCRel();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 only169 // put the addend in the instruction itself. Clear out any value the170 // 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 require175 // 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 reportError(Fixup.getLoc(), "conditional branch requires assembler-local"179 " label. '" +180 Target.getAddSym()->getName() +181 "' is external.");182 return;183 }184 185 // 14-bit branch relocations should only target internal labels, and so186 // should never get here.187 if (Kind == AArch64::fixup_aarch64_pcrel_branch14) {188 reportError(Fixup.getLoc(), "Invalid relocation on conditional branch!");189 return;190 }191 192 if (!getAArch64FixupKindMachOInfo(Fixup, Type, Target.getSpecifier(),193 Log2Size, Asm)) {194 reportError(Fixup.getLoc(), "unknown AArch64 fixup kind!");195 return;196 }197 198 Value = Target.getConstant();199 200 if (Target.isAbsolute()) { // constant201 // FIXME: Should this always be extern?202 // SymbolNum of 0 indicates the absolute section.203 Type = MachO::ARM64_RELOC_UNSIGNED;204 205 if (IsPCRel) {206 reportError(Fixup.getLoc(), "PC relative absolute relocation!");207 return;208 209 // FIXME: x86_64 sets the type to a branch reloc here. Should we do210 // something similar?211 }212 } else if (auto *B = Target.getSubSym()) { // A - B + constant213 const MCSymbol *A = Target.getAddSym();214 const MCSymbol *A_Base = Writer->getAtom(*A);215 const MCSymbol *B_Base = Writer->getAtom(*B);216 217 // Check for "_foo@got - .", which comes through here as:218 // Ltmp0:219 // ... _foo@got - Ltmp0220 if (Target.getSpecifier() == AArch64::S_MACHO_GOT &&221 Asm.getSymbolOffset(*B) ==222 Asm.getFragmentOffset(*Fragment) + Fixup.getOffset()) {223 // SymB is the PC, so use a PC-rel pointer-to-GOT relocation.224 Type = MachO::ARM64_RELOC_POINTER_TO_GOT;225 IsPCRel = 1;226 MachO::any_relocation_info MRE;227 MRE.r_word0 = FixupOffset;228 MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);229 Writer->addRelocation(A_Base, Fragment->getParent(), MRE);230 return;231 } else if (Target.getSpecifier() != AArch64::S_None) {232 // Otherwise, neither symbol can be modified.233 reportError(Fixup.getLoc(), "unsupported relocation of modified symbol");234 return;235 }236 237 // We don't support PCrel relocations of differences.238 if (IsPCRel) {239 reportError(Fixup.getLoc(), "unsupported pc-relative relocation of "240 "difference");241 return;242 }243 244 // AArch64 always uses external relocations. If there is no symbol to use as245 // a base address (a local symbol with no preceding non-local symbol),246 // error out.247 //248 // FIXME: We should probably just synthesize an external symbol and use249 // that.250 if (!A_Base) {251 reportError(Fixup.getLoc(),252 "unsupported relocation of local symbol '" + A->getName() +253 "'. Must have non-local symbol earlier in section.");254 return;255 }256 if (!B_Base) {257 reportError(Fixup.getLoc(),258 "unsupported relocation of local symbol '" + B->getName() +259 "'. Must have non-local symbol earlier in section.");260 return;261 }262 263 if (A_Base == B_Base && A_Base) {264 reportError(Fixup.getLoc(), "unsupported relocation with identical base");265 return;266 }267 268 Value +=269 (!A->getFragment() ? 0 : Writer->getSymbolAddress(*A)) -270 (!A_Base || !A_Base->getFragment() ? 0271 : Writer->getSymbolAddress(*A_Base));272 Value -=273 (!B->getFragment() ? 0 : Writer->getSymbolAddress(*B)) -274 (!B_Base || !B_Base->getFragment() ? 0275 : Writer->getSymbolAddress(*B_Base));276 277 Type = MachO::ARM64_RELOC_UNSIGNED;278 279 MachO::any_relocation_info MRE;280 MRE.r_word0 = FixupOffset;281 MRE.r_word1 = (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);282 Writer->addRelocation(A_Base, Fragment->getParent(), MRE);283 284 RelSymbol = B_Base;285 Type = MachO::ARM64_RELOC_SUBTRACTOR;286 } else { // A + constant287 const MCSymbol *Symbol = Target.getAddSym();288 const MCSectionMachO &Section =289 static_cast<const MCSectionMachO &>(*Fragment->getParent());290 291 bool CanUseLocalRelocation =292 canUseLocalRelocation(Section, *Symbol, Log2Size);293 if (Symbol->isTemporary() && (Value || !CanUseLocalRelocation)) {294 // Make sure that the symbol is actually in a section here. If it isn't,295 // emit an error and exit.296 if (!Symbol->isInSection()) {297 reportError(Fixup.getLoc(),298 "unsupported relocation of local symbol '" +299 Symbol->getName() +300 "'. Must have non-local symbol earlier in section.");301 return;302 }303 const MCSection &Sec = Symbol->getSection();304 if (!MCAsmInfoDarwin::isSectionAtomizableBySymbols(Sec))305 Symbol->setUsedInReloc();306 }307 308 const MCSymbol *Base = Writer->getAtom(*Symbol);309 310 // If the symbol is a variable it can either be in a section and311 // we have a base or it is absolute and should have been expanded.312 assert(!Symbol->isVariable() || Base);313 314 // Relocations inside debug sections always use local relocations when315 // possible. This seems to be done because the debugger doesn't fully316 // understand relocation entries and expects to find values that317 // have already been fixed up.318 if (Symbol->isInSection()) {319 if (Section.hasAttribute(MachO::S_ATTR_DEBUG))320 Base = nullptr;321 }322 323 // AArch64 uses external relocations as much as possible. For debug324 // sections, and for pointer-sized relocations (.quad), we allow section325 // relocations. It's code sections that run into trouble.326 if (Base) {327 RelSymbol = Base;328 329 // Add the local offset, if needed.330 if (Base != Symbol)331 Value += Asm.getSymbolOffset(*Symbol) - Asm.getSymbolOffset(*Base);332 } else if (Symbol->isInSection()) {333 if (!CanUseLocalRelocation) {334 reportError(Fixup.getLoc(),335 "unsupported relocation of local symbol '" +336 Symbol->getName() +337 "'. Must have non-local symbol earlier in section.");338 return;339 }340 // Adjust the relocation to be section-relative.341 // The index is the section ordinal (1-based).342 const MCSection &Sec = Symbol->getSection();343 Index = Sec.getOrdinal() + 1;344 Value += Writer->getSymbolAddress(*Symbol);345 346 if (IsPCRel)347 Value -= Writer->getFragmentAddress(Asm, Fragment) + Fixup.getOffset() +348 (1ULL << Log2Size);349 } else {350 llvm_unreachable(351 "This constant variable should have been expanded during evaluation");352 }353 }354 355 // If the relocation kind is Branch26, Page21, or Pageoff12, any addend356 // is represented via an Addend relocation, not encoded directly into357 // the instruction.358 if ((Type == MachO::ARM64_RELOC_BRANCH26 ||359 Type == MachO::ARM64_RELOC_PAGE21 ||360 Type == MachO::ARM64_RELOC_PAGEOFF12) &&361 Value) {362 if (!isInt<24>(Value)) {363 reportError(Fixup.getLoc(), "addend too big for relocation");364 return;365 }366 367 MachO::any_relocation_info MRE;368 MRE.r_word0 = FixupOffset;369 MRE.r_word1 =370 (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);371 Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);372 373 // Now set up the Addend relocation.374 Type = MachO::ARM64_RELOC_ADDEND;375 Index = Value;376 RelSymbol = nullptr;377 IsPCRel = 0;378 Log2Size = 2;379 380 // Put zero into the instruction itself. The addend is in the relocation.381 Value = 0;382 }383 384 if (Target.getSpecifier() == AArch64::S_AUTH ||385 Target.getSpecifier() == AArch64::S_AUTHADDR) {386 auto *Expr = cast<AArch64AuthMCExpr>(Fixup.getValue());387 388 assert(Type == MachO::ARM64_RELOC_UNSIGNED);389 390 if (IsPCRel) {391 reportError(Fixup.getLoc(), "invalid PC relative auth relocation");392 return;393 }394 395 if (Log2Size != 3) {396 reportError(Fixup.getLoc(),397 "invalid auth relocation size, must be 8 bytes");398 return;399 }400 401 if (Target.getSubSym()) {402 reportError(Fixup.getLoc(),403 "invalid auth relocation, can't reference two symbols");404 return;405 }406 407 uint16_t Discriminator = Expr->getDiscriminator();408 AArch64PACKey::ID Key = Expr->getKey();409 410 if (!isInt<32>(Value)) {411 reportError(Fixup.getLoc(), "addend too big for relocation");412 return;413 }414 415 Type = MachO::ARM64_RELOC_AUTHENTICATED_POINTER;416 Value = (uint32_t(Value)) | (uint64_t(Discriminator) << 32) |417 (uint64_t(Expr->hasAddressDiversity()) << 48) |418 (uint64_t(Key) << 49) | (1ULL << 63);419 }420 421 // If there's any addend left to handle, encode it in the instruction.422 FixedValue = Value;423 424 // struct relocation_info (8 bytes)425 MachO::any_relocation_info MRE;426 MRE.r_word0 = FixupOffset;427 MRE.r_word1 =428 (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);429 Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);430}431 432std::unique_ptr<MCObjectTargetWriter>433llvm::createAArch64MachObjectWriter(uint32_t CPUType, uint32_t CPUSubtype,434 bool IsILP32) {435 return std::make_unique<AArch64MachObjectWriter>(CPUType, CPUSubtype,436 IsILP32);437}438