Path: blob/main/contrib/llvm-project/llvm/lib/Target/X86/MCTargetDesc/X86MachObjectWriter.cpp
35294 views
//===-- X86MachObjectWriter.cpp - X86 Mach-O Writer -----------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "MCTargetDesc/X86FixupKinds.h"9#include "MCTargetDesc/X86MCTargetDesc.h"10#include "llvm/ADT/Twine.h"11#include "llvm/BinaryFormat/MachO.h"12#include "llvm/MC/MCAsmInfo.h"13#include "llvm/MC/MCAsmInfoDarwin.h"14#include "llvm/MC/MCAssembler.h"15#include "llvm/MC/MCContext.h"16#include "llvm/MC/MCMachObjectWriter.h"17#include "llvm/MC/MCSectionMachO.h"18#include "llvm/MC/MCValue.h"19#include "llvm/Support/ErrorHandling.h"20#include "llvm/Support/Format.h"2122using namespace llvm;2324namespace {25class X86MachObjectWriter : public MCMachObjectTargetWriter {26bool recordScatteredRelocation(MachObjectWriter *Writer,27const MCAssembler &Asm,28const MCFragment *Fragment,29const MCFixup &Fixup,30MCValue Target,31unsigned Log2Size,32uint64_t &FixedValue);33void recordTLVPRelocation(MachObjectWriter *Writer,34const MCAssembler &Asm,35const MCFragment *Fragment,36const MCFixup &Fixup,37MCValue Target,38uint64_t &FixedValue);3940void RecordX86Relocation(MachObjectWriter *Writer,41const MCAssembler &Asm,42const MCFragment *Fragment,43const MCFixup &Fixup,44MCValue Target,45uint64_t &FixedValue);46void RecordX86_64Relocation(MachObjectWriter *Writer, MCAssembler &Asm,47const MCFragment *Fragment, const MCFixup &Fixup,48MCValue Target, uint64_t &FixedValue);4950public:51X86MachObjectWriter(bool Is64Bit, uint32_t CPUType, uint32_t CPUSubtype)52: MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype) {}5354void recordRelocation(MachObjectWriter *Writer, MCAssembler &Asm,55const MCFragment *Fragment, const MCFixup &Fixup,56MCValue Target, uint64_t &FixedValue) override {57if (Writer->is64Bit())58RecordX86_64Relocation(Writer, Asm, Fragment, Fixup, Target, FixedValue);59else60RecordX86Relocation(Writer, Asm, Fragment, Fixup, Target, FixedValue);61}62};63} // namespace6465static bool isFixupKindRIPRel(unsigned Kind) {66return Kind == X86::reloc_riprel_4byte ||67Kind == X86::reloc_riprel_4byte_movq_load ||68Kind == X86::reloc_riprel_4byte_relax ||69Kind == X86::reloc_riprel_4byte_relax_rex;70}7172static unsigned getFixupKindLog2Size(unsigned Kind) {73switch (Kind) {74default:75llvm_unreachable("invalid fixup kind!");76case FK_PCRel_1:77case FK_Data_1: return 0;78case FK_PCRel_2:79case FK_Data_2: return 1;80case FK_PCRel_4:81// FIXME: Remove these!!!82case X86::reloc_riprel_4byte:83case X86::reloc_riprel_4byte_relax:84case X86::reloc_riprel_4byte_relax_rex:85case X86::reloc_riprel_4byte_movq_load:86case X86::reloc_signed_4byte:87case X86::reloc_signed_4byte_relax:88case X86::reloc_branch_4byte_pcrel:89case FK_Data_4: return 2;90case FK_Data_8: return 3;91}92}9394void X86MachObjectWriter::RecordX86_64Relocation(95MachObjectWriter *Writer, MCAssembler &Asm, const MCFragment *Fragment,96const MCFixup &Fixup, MCValue Target, uint64_t &FixedValue) {97unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());98unsigned IsRIPRel = isFixupKindRIPRel(Fixup.getKind());99unsigned Log2Size = getFixupKindLog2Size(Fixup.getKind());100101// See <reloc.h>.102uint32_t FixupOffset = Asm.getFragmentOffset(*Fragment) + Fixup.getOffset();103uint32_t FixupAddress =104Writer->getFragmentAddress(Asm, Fragment) + Fixup.getOffset();105int64_t Value = 0;106unsigned Index = 0;107unsigned IsExtern = 0;108unsigned Type = 0;109const MCSymbol *RelSymbol = nullptr;110111Value = Target.getConstant();112113if (IsPCRel) {114// Compensate for the relocation offset, Darwin x86_64 relocations only have115// the addend and appear to have attempted to define it to be the actual116// expression addend without the PCrel bias. However, instructions with data117// following the relocation are not accommodated for (see comment below118// regarding SIGNED{1,2,4}), so it isn't exactly that either.119Value += 1LL << Log2Size;120}121122if (Target.isAbsolute()) { // constant123// SymbolNum of 0 indicates the absolute section.124Type = MachO::X86_64_RELOC_UNSIGNED;125126// FIXME: I believe this is broken, I don't think the linker can understand127// it. I think it would require a local relocation, but I'm not sure if that128// would work either. The official way to get an absolute PCrel relocation129// is to use an absolute symbol (which we don't support yet).130if (IsPCRel) {131IsExtern = 1;132Type = MachO::X86_64_RELOC_BRANCH;133}134} else if (Target.getSymB()) { // A - B + constant135const MCSymbol *A = &Target.getSymA()->getSymbol();136if (A->isTemporary())137A = &Writer->findAliasedSymbol(*A);138const MCSymbol *A_Base = Writer->getAtom(*A);139140const MCSymbol *B = &Target.getSymB()->getSymbol();141if (B->isTemporary())142B = &Writer->findAliasedSymbol(*B);143const MCSymbol *B_Base = Writer->getAtom(*B);144145// Neither symbol can be modified.146if (Target.getSymA()->getKind() != MCSymbolRefExpr::VK_None) {147Asm.getContext().reportError(Fixup.getLoc(),148"unsupported relocation of modified symbol");149return;150}151152// We don't support PCrel relocations of differences. Darwin 'as' doesn't153// implement most of these correctly.154if (IsPCRel) {155Asm.getContext().reportError(156Fixup.getLoc(), "unsupported pc-relative relocation of difference");157return;158}159160// The support for the situation where one or both of the symbols would161// require a local relocation is handled just like if the symbols were162// external. This is certainly used in the case of debug sections where the163// section has only temporary symbols and thus the symbols don't have base164// symbols. This is encoded using the section ordinal and non-extern165// relocation entries.166167// Darwin 'as' doesn't emit correct relocations for this (it ends up with a168// single SIGNED relocation); reject it for now. Except the case where both169// symbols don't have a base, equal but both NULL.170if (A_Base == B_Base && A_Base) {171Asm.getContext().reportError(172Fixup.getLoc(), "unsupported relocation with identical base");173return;174}175176// A subtraction expression where either symbol is undefined is a177// non-relocatable expression.178if (A->isUndefined() || B->isUndefined()) {179StringRef Name = A->isUndefined() ? A->getName() : B->getName();180Asm.getContext().reportError(Fixup.getLoc(),181"unsupported relocation with subtraction expression, symbol '" +182Name + "' can not be undefined in a subtraction expression");183return;184}185186Value += Writer->getSymbolAddress(*A, Asm) -187(!A_Base ? 0 : Writer->getSymbolAddress(*A_Base, Asm));188Value -= Writer->getSymbolAddress(*B, Asm) -189(!B_Base ? 0 : Writer->getSymbolAddress(*B_Base, Asm));190191if (!A_Base)192Index = A->getFragment()->getParent()->getOrdinal() + 1;193Type = MachO::X86_64_RELOC_UNSIGNED;194195MachO::any_relocation_info MRE;196MRE.r_word0 = FixupOffset;197MRE.r_word1 =198(Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);199Writer->addRelocation(A_Base, Fragment->getParent(), MRE);200201if (B_Base)202RelSymbol = B_Base;203else204Index = B->getFragment()->getParent()->getOrdinal() + 1;205Type = MachO::X86_64_RELOC_SUBTRACTOR;206} else {207const MCSymbol *Symbol = &Target.getSymA()->getSymbol();208if (Symbol->isTemporary() && Value) {209const MCSection &Sec = Symbol->getSection();210if (!MCAsmInfoDarwin::isSectionAtomizableBySymbols(Sec))211Symbol->setUsedInReloc();212}213RelSymbol = Writer->getAtom(*Symbol);214215// Relocations inside debug sections always use local relocations when216// possible. This seems to be done because the debugger doesn't fully217// understand x86_64 relocation entries, and expects to find values that218// have already been fixed up.219if (Symbol->isInSection()) {220const MCSectionMachO &Section =221static_cast<const MCSectionMachO &>(*Fragment->getParent());222if (Section.hasAttribute(MachO::S_ATTR_DEBUG))223RelSymbol = nullptr;224}225226// x86_64 almost always uses external relocations, except when there is no227// symbol to use as a base address (a local symbol with no preceding228// non-local symbol).229if (RelSymbol) {230// Add the local offset, if needed.231if (RelSymbol != Symbol)232Value += Asm.getSymbolOffset(*Symbol) - Asm.getSymbolOffset(*RelSymbol);233} else if (Symbol->isInSection() && !Symbol->isVariable()) {234// The index is the section ordinal (1-based).235Index = Symbol->getFragment()->getParent()->getOrdinal() + 1;236Value += Writer->getSymbolAddress(*Symbol, Asm);237238if (IsPCRel)239Value -= FixupAddress + (1 << Log2Size);240} else if (Symbol->isVariable()) {241const MCExpr *Value = Symbol->getVariableValue();242int64_t Res;243bool isAbs =244Value->evaluateAsAbsolute(Res, Asm, Writer->getSectionAddressMap());245if (isAbs) {246FixedValue = Res;247return;248} else {249Asm.getContext().reportError(Fixup.getLoc(),250"unsupported relocation of variable '" +251Symbol->getName() + "'");252return;253}254} else {255Asm.getContext().reportError(256Fixup.getLoc(), "unsupported relocation of undefined symbol '" +257Symbol->getName() + "'");258return;259}260261MCSymbolRefExpr::VariantKind Modifier = Target.getSymA()->getKind();262if (IsPCRel) {263if (IsRIPRel) {264if (Modifier == MCSymbolRefExpr::VK_GOTPCREL) {265// x86_64 distinguishes movq foo@GOTPCREL so that the linker can266// rewrite the movq to an leaq at link time if the symbol ends up in267// the same linkage unit.268if (Fixup.getTargetKind() == X86::reloc_riprel_4byte_movq_load)269Type = MachO::X86_64_RELOC_GOT_LOAD;270else271Type = MachO::X86_64_RELOC_GOT;272} else if (Modifier == MCSymbolRefExpr::VK_TLVP) {273Type = MachO::X86_64_RELOC_TLV;274} else if (Modifier != MCSymbolRefExpr::VK_None) {275Asm.getContext().reportError(276Fixup.getLoc(), "unsupported symbol modifier in relocation");277return;278} else {279Type = MachO::X86_64_RELOC_SIGNED;280281// The Darwin x86_64 relocation format has a problem where it cannot282// encode an address (L<foo> + <constant>) which is outside the atom283// containing L<foo>. Generally, this shouldn't occur but it does284// happen when we have a RIPrel instruction with data following the285// relocation entry (e.g., movb $012, L0(%rip)). Even with the PCrel286// adjustment Darwin x86_64 uses, the offset is still negative and the287// linker has no way to recognize this.288//289// To work around this, Darwin uses several special relocation types290// to indicate the offsets. However, the specification or291// implementation of these seems to also be incomplete; they should292// adjust the addend as well based on the actual encoded instruction293// (the additional bias), but instead appear to just look at the final294// offset.295switch (-(Target.getConstant() + (1LL << Log2Size))) {296case 1: Type = MachO::X86_64_RELOC_SIGNED_1; break;297case 2: Type = MachO::X86_64_RELOC_SIGNED_2; break;298case 4: Type = MachO::X86_64_RELOC_SIGNED_4; break;299}300}301} else {302if (Modifier != MCSymbolRefExpr::VK_None) {303Asm.getContext().reportError(304Fixup.getLoc(),305"unsupported symbol modifier in branch relocation");306return;307}308309Type = MachO::X86_64_RELOC_BRANCH;310}311} else {312if (Modifier == MCSymbolRefExpr::VK_GOT) {313Type = MachO::X86_64_RELOC_GOT;314} else if (Modifier == MCSymbolRefExpr::VK_GOTPCREL) {315// GOTPCREL is allowed as a modifier on non-PCrel instructions, in which316// case all we do is set the PCrel bit in the relocation entry; this is317// used with exception handling, for example. The source is required to318// include any necessary offset directly.319Type = MachO::X86_64_RELOC_GOT;320IsPCRel = 1;321} else if (Modifier == MCSymbolRefExpr::VK_TLVP) {322Asm.getContext().reportError(323Fixup.getLoc(), "TLVP symbol modifier should have been rip-rel");324return;325} else if (Modifier != MCSymbolRefExpr::VK_None) {326Asm.getContext().reportError(327Fixup.getLoc(), "unsupported symbol modifier in relocation");328return;329} else {330Type = MachO::X86_64_RELOC_UNSIGNED;331if (Fixup.getTargetKind() == X86::reloc_signed_4byte) {332Asm.getContext().reportError(333Fixup.getLoc(),334"32-bit absolute addressing is not supported in 64-bit mode");335return;336}337}338}339}340341// x86_64 always writes custom values into the fixups.342FixedValue = Value;343344// struct relocation_info (8 bytes)345MachO::any_relocation_info MRE;346MRE.r_word0 = FixupOffset;347MRE.r_word1 = (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) |348(IsExtern << 27) | (Type << 28);349Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);350}351352bool X86MachObjectWriter::recordScatteredRelocation(MachObjectWriter *Writer,353const MCAssembler &Asm,354const MCFragment *Fragment,355const MCFixup &Fixup,356MCValue Target,357unsigned Log2Size,358uint64_t &FixedValue) {359uint64_t OriginalFixedValue = FixedValue;360uint32_t FixupOffset = Asm.getFragmentOffset(*Fragment) + Fixup.getOffset();361unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());362unsigned Type = MachO::GENERIC_RELOC_VANILLA;363364// See <reloc.h>.365const MCSymbol *A = &Target.getSymA()->getSymbol();366367if (!A->getFragment()) {368Asm.getContext().reportError(369Fixup.getLoc(),370"symbol '" + A->getName() +371"' can not be undefined in a subtraction expression");372return false;373}374375uint32_t Value = Writer->getSymbolAddress(*A, Asm);376uint64_t SecAddr = Writer->getSectionAddress(A->getFragment()->getParent());377FixedValue += SecAddr;378uint32_t Value2 = 0;379380if (const MCSymbolRefExpr *B = Target.getSymB()) {381const MCSymbol *SB = &B->getSymbol();382383if (!SB->getFragment()) {384Asm.getContext().reportError(385Fixup.getLoc(),386"symbol '" + SB->getName() +387"' can not be undefined in a subtraction expression");388return false;389}390391// Select the appropriate difference relocation type.392//393// Note that there is no longer any semantic difference between these two394// relocation types from the linkers point of view, this is done solely for395// pedantic compatibility with 'as'.396Type = A->isExternal() ? (unsigned)MachO::GENERIC_RELOC_SECTDIFF397: (unsigned)MachO::GENERIC_RELOC_LOCAL_SECTDIFF;398Value2 = Writer->getSymbolAddress(*SB, Asm);399FixedValue -= Writer->getSectionAddress(SB->getFragment()->getParent());400}401402// Relocations are written out in reverse order, so the PAIR comes first.403if (Type == MachO::GENERIC_RELOC_SECTDIFF ||404Type == MachO::GENERIC_RELOC_LOCAL_SECTDIFF) {405// If the offset is too large to fit in a scattered relocation,406// we're hosed. It's an unfortunate limitation of the MachO format.407if (FixupOffset > 0xffffff) {408char Buffer[32];409format("0x%x", FixupOffset).print(Buffer, sizeof(Buffer));410Asm.getContext().reportError(Fixup.getLoc(),411Twine("Section too large, can't encode "412"r_address (") + Buffer +413") into 24 bits of scattered "414"relocation entry.");415return false;416}417418MachO::any_relocation_info MRE;419MRE.r_word0 = ((0 << 0) | // r_address420(MachO::GENERIC_RELOC_PAIR << 24) | // r_type421(Log2Size << 28) |422(IsPCRel << 30) |423MachO::R_SCATTERED);424MRE.r_word1 = Value2;425Writer->addRelocation(nullptr, Fragment->getParent(), MRE);426} else {427// If the offset is more than 24-bits, it won't fit in a scattered428// relocation offset field, so we fall back to using a non-scattered429// relocation. This is a bit risky, as if the offset reaches out of430// the block and the linker is doing scattered loading on this431// symbol, things can go badly.432//433// Required for 'as' compatibility.434if (FixupOffset > 0xffffff) {435FixedValue = OriginalFixedValue;436return false;437}438}439440MachO::any_relocation_info MRE;441MRE.r_word0 = ((FixupOffset << 0) |442(Type << 24) |443(Log2Size << 28) |444(IsPCRel << 30) |445MachO::R_SCATTERED);446MRE.r_word1 = Value;447Writer->addRelocation(nullptr, Fragment->getParent(), MRE);448return true;449}450451void X86MachObjectWriter::recordTLVPRelocation(MachObjectWriter *Writer,452const MCAssembler &Asm,453const MCFragment *Fragment,454const MCFixup &Fixup,455MCValue Target,456uint64_t &FixedValue) {457const MCSymbolRefExpr *SymA = Target.getSymA();458assert(SymA->getKind() == MCSymbolRefExpr::VK_TLVP && !is64Bit() &&459"Should only be called with a 32-bit TLVP relocation!");460461unsigned Log2Size = getFixupKindLog2Size(Fixup.getKind());462uint32_t Value = Asm.getFragmentOffset(*Fragment) + Fixup.getOffset();463unsigned IsPCRel = 0;464465// We're only going to have a second symbol in pic mode and it'll be a466// subtraction from the picbase. For 32-bit pic the addend is the difference467// between the picbase and the next address. For 32-bit static the addend is468// zero.469if (auto *SymB = Target.getSymB()) {470// If this is a subtraction then we're pcrel.471uint32_t FixupAddress =472Writer->getFragmentAddress(Asm, Fragment) + Fixup.getOffset();473IsPCRel = 1;474FixedValue = FixupAddress -475Writer->getSymbolAddress(SymB->getSymbol(), Asm) +476Target.getConstant();477FixedValue += 1ULL << Log2Size;478} else {479FixedValue = 0;480}481482// struct relocation_info (8 bytes)483MachO::any_relocation_info MRE;484MRE.r_word0 = Value;485MRE.r_word1 =486(IsPCRel << 24) | (Log2Size << 25) | (MachO::GENERIC_RELOC_TLV << 28);487Writer->addRelocation(&SymA->getSymbol(), Fragment->getParent(), MRE);488}489490void X86MachObjectWriter::RecordX86Relocation(MachObjectWriter *Writer,491const MCAssembler &Asm,492const MCFragment *Fragment,493const MCFixup &Fixup,494MCValue Target,495uint64_t &FixedValue) {496unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, Fixup.getKind());497unsigned Log2Size = getFixupKindLog2Size(Fixup.getKind());498499// If this is a 32-bit TLVP reloc it's handled a bit differently.500if (Target.getSymA() &&501Target.getSymA()->getKind() == MCSymbolRefExpr::VK_TLVP) {502recordTLVPRelocation(Writer, Asm, Fragment, Fixup, Target, FixedValue);503return;504}505506// If this is a difference or a defined symbol plus an offset, then we need a507// scattered relocation entry. Differences always require scattered508// relocations.509if (Target.getSymB()) {510recordScatteredRelocation(Writer, Asm, Fragment, Fixup, Target, Log2Size,511FixedValue);512return;513}514515// Get the symbol data, if any.516const MCSymbol *A = nullptr;517if (Target.getSymA())518A = &Target.getSymA()->getSymbol();519520// If this is an internal relocation with an offset, it also needs a scattered521// relocation entry.522uint32_t Offset = Target.getConstant();523if (IsPCRel)524Offset += 1 << Log2Size;525526// Try to record the scattered relocation if needed. Fall back to non527// scattered if necessary (see comments in recordScatteredRelocation()528// for details).529if (Offset && A && !Writer->doesSymbolRequireExternRelocation(*A) &&530recordScatteredRelocation(Writer, Asm, Fragment, Fixup, Target, Log2Size,531FixedValue))532return;533534// See <reloc.h>.535uint32_t FixupOffset = Asm.getFragmentOffset(*Fragment) + Fixup.getOffset();536unsigned Index = 0;537unsigned Type = 0;538const MCSymbol *RelSymbol = nullptr;539540if (Target.isAbsolute()) { // constant541// SymbolNum of 0 indicates the absolute section.542//543// FIXME: Currently, these are never generated (see code below). I cannot544// find a case where they are actually emitted.545Type = MachO::GENERIC_RELOC_VANILLA;546} else {547assert(A && "Unknown symbol data");548549// Resolve constant variables.550if (A->isVariable()) {551int64_t Res;552if (A->getVariableValue()->evaluateAsAbsolute(553Res, Asm, Writer->getSectionAddressMap())) {554FixedValue = Res;555return;556}557}558559// Check whether we need an external or internal relocation.560if (Writer->doesSymbolRequireExternRelocation(*A)) {561RelSymbol = A;562// For external relocations, make sure to offset the fixup value to563// compensate for the addend of the symbol address, if it was564// undefined. This occurs with weak definitions, for example.565if (!A->isUndefined())566FixedValue -= Asm.getSymbolOffset(*A);567} else {568// The index is the section ordinal (1-based).569const MCSection &Sec = A->getSection();570Index = Sec.getOrdinal() + 1;571FixedValue += Writer->getSectionAddress(&Sec);572}573if (IsPCRel)574FixedValue -= Writer->getSectionAddress(Fragment->getParent());575576Type = MachO::GENERIC_RELOC_VANILLA;577}578579// struct relocation_info (8 bytes)580MachO::any_relocation_info MRE;581MRE.r_word0 = FixupOffset;582MRE.r_word1 =583(Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);584Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);585}586587std::unique_ptr<MCObjectTargetWriter>588llvm::createX86MachObjectWriter(bool Is64Bit, uint32_t CPUType,589uint32_t CPUSubtype) {590return std::make_unique<X86MachObjectWriter>(Is64Bit, CPUType, CPUSubtype);591}592593594