582 lines · cpp
1//===-- X86MachObjectWriter.cpp - X86 Mach-O 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/X86FixupKinds.h"10#include "MCTargetDesc/X86MCAsmInfo.h"11#include "MCTargetDesc/X86MCTargetDesc.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/MCMachObjectWriter.h"19#include "llvm/MC/MCSectionMachO.h"20#include "llvm/MC/MCSymbolMachO.h"21#include "llvm/MC/MCValue.h"22#include "llvm/Support/ErrorHandling.h"23#include "llvm/Support/Format.h"24 25using namespace llvm;26 27namespace {28class X86MachObjectWriter : public MCMachObjectTargetWriter {29 bool recordScatteredRelocation(MachObjectWriter *Writer,30 const MCAssembler &Asm,31 const MCFragment *Fragment,32 const MCFixup &Fixup,33 MCValue Target,34 unsigned Log2Size,35 uint64_t &FixedValue);36 void recordTLVPRelocation(MachObjectWriter *Writer,37 const MCAssembler &Asm,38 const MCFragment *Fragment,39 const MCFixup &Fixup,40 MCValue Target,41 uint64_t &FixedValue);42 43 void RecordX86Relocation(MachObjectWriter *Writer,44 const MCAssembler &Asm,45 const MCFragment *Fragment,46 const MCFixup &Fixup,47 MCValue Target,48 uint64_t &FixedValue);49 void RecordX86_64Relocation(MachObjectWriter *Writer, MCAssembler &Asm,50 const MCFragment *Fragment, const MCFixup &Fixup,51 MCValue Target, uint64_t &FixedValue);52 53public:54 X86MachObjectWriter(bool Is64Bit, uint32_t CPUType, uint32_t CPUSubtype)55 : MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype) {}56 57 void recordRelocation(MachObjectWriter *Writer, MCAssembler &Asm,58 const MCFragment *Fragment, const MCFixup &Fixup,59 MCValue Target, uint64_t &FixedValue) override {60 if (Writer->is64Bit())61 RecordX86_64Relocation(Writer, Asm, Fragment, Fixup, Target, FixedValue);62 else63 RecordX86Relocation(Writer, Asm, Fragment, Fixup, Target, FixedValue);64 }65};66} // namespace67 68static bool isFixupKindRIPRel(unsigned Kind) {69 return Kind == X86::reloc_riprel_4byte ||70 Kind == X86::reloc_riprel_4byte_movq_load ||71 Kind == X86::reloc_riprel_4byte_movq_load_rex2 ||72 Kind == X86::reloc_riprel_4byte_relax ||73 Kind == X86::reloc_riprel_4byte_relax_rex ||74 Kind == X86::reloc_riprel_4byte_relax_rex2 ||75 Kind == X86::reloc_riprel_4byte_relax_evex;76}77 78static unsigned getFixupKindLog2Size(unsigned Kind) {79 switch (Kind) {80 default:81 llvm_unreachable("invalid fixup kind!");82 case FK_Data_1: return 0;83 case FK_Data_2: return 1;84 // FIXME: Remove these!!!85 case X86::reloc_riprel_4byte:86 case X86::reloc_riprel_4byte_relax:87 case X86::reloc_riprel_4byte_relax_rex:88 case X86::reloc_riprel_4byte_relax_rex2:89 case X86::reloc_riprel_4byte_movq_load:90 case X86::reloc_riprel_4byte_movq_load_rex2:91 case X86::reloc_signed_4byte:92 case X86::reloc_signed_4byte_relax:93 case X86::reloc_branch_4byte_pcrel:94 case X86::reloc_riprel_4byte_relax_evex:95 case FK_Data_4: return 2;96 case FK_Data_8: return 3;97 }98}99 100void X86MachObjectWriter::RecordX86_64Relocation(101 MachObjectWriter *Writer, MCAssembler &Asm, const MCFragment *Fragment,102 const MCFixup &Fixup, MCValue Target, uint64_t &FixedValue) {103 unsigned IsPCRel = Fixup.isPCRel();104 unsigned IsRIPRel = isFixupKindRIPRel(Fixup.getKind());105 unsigned Log2Size = getFixupKindLog2Size(Fixup.getKind());106 107 // See <reloc.h>.108 uint32_t FixupOffset = Asm.getFragmentOffset(*Fragment) + Fixup.getOffset();109 uint32_t FixupAddress =110 Writer->getFragmentAddress(Asm, Fragment) + Fixup.getOffset();111 int64_t Value = 0;112 unsigned Index = 0;113 unsigned IsExtern = 0;114 unsigned Type = 0;115 const MCSymbol *RelSymbol = nullptr;116 117 Value = Target.getConstant();118 119 if (IsPCRel) {120 // Compensate for the relocation offset, Darwin x86_64 relocations only have121 // the addend and appear to have attempted to define it to be the actual122 // expression addend without the PCrel bias. However, instructions with data123 // following the relocation are not accommodated for (see comment below124 // regarding SIGNED{1,2,4}), so it isn't exactly that either.125 Value += 1LL << Log2Size;126 }127 128 if (Target.isAbsolute()) { // constant129 // SymbolNum of 0 indicates the absolute section.130 Type = MachO::X86_64_RELOC_UNSIGNED;131 132 // FIXME: I believe this is broken, I don't think the linker can understand133 // it. I think it would require a local relocation, but I'm not sure if that134 // would work either. The official way to get an absolute PCrel relocation135 // is to use an absolute symbol (which we don't support yet).136 if (IsPCRel) {137 IsExtern = 1;138 Type = MachO::X86_64_RELOC_BRANCH;139 }140 } else if (Target.getSubSym()) { // A - B + constant141 const MCSymbol *A = Target.getAddSym();142 if (A->isTemporary())143 A = &Writer->findAliasedSymbol(*A);144 const MCSymbol *A_Base = Writer->getAtom(*A);145 146 const MCSymbol *B = Target.getSubSym();147 if (B->isTemporary())148 B = &Writer->findAliasedSymbol(*B);149 const MCSymbol *B_Base = Writer->getAtom(*B);150 151 // Neither symbol can be modified.152 if (Target.getSpecifier()) {153 reportError(Fixup.getLoc(), "unsupported relocation of modified symbol");154 return;155 }156 157 // We don't support PCrel relocations of differences. Darwin 'as' doesn't158 // implement most of these correctly.159 if (IsPCRel) {160 reportError(Fixup.getLoc(),161 "unsupported pc-relative relocation of difference");162 return;163 }164 165 // The support for the situation where one or both of the symbols would166 // require a local relocation is handled just like if the symbols were167 // external. This is certainly used in the case of debug sections where the168 // section has only temporary symbols and thus the symbols don't have base169 // symbols. This is encoded using the section ordinal and non-extern170 // relocation entries.171 172 // Darwin 'as' doesn't emit correct relocations for this (it ends up with a173 // single SIGNED relocation); reject it for now. Except the case where both174 // symbols don't have a base, equal but both NULL.175 if (A_Base == B_Base && A_Base) {176 reportError(Fixup.getLoc(), "unsupported relocation with identical base");177 return;178 }179 180 // A subtraction expression where either symbol is undefined is a181 // non-relocatable expression.182 if (A->isUndefined() || B->isUndefined()) {183 StringRef Name = A->isUndefined() ? A->getName() : B->getName();184 reportError(185 Fixup.getLoc(),186 "unsupported relocation with subtraction expression, symbol '" +187 Name + "' can not be undefined in a subtraction expression");188 return;189 }190 191 Value += Writer->getSymbolAddress(*A) -192 (!A_Base ? 0 : Writer->getSymbolAddress(*A_Base));193 Value -= Writer->getSymbolAddress(*B) -194 (!B_Base ? 0 : Writer->getSymbolAddress(*B_Base));195 196 if (!A_Base)197 Index = A->getFragment()->getParent()->getOrdinal() + 1;198 Type = MachO::X86_64_RELOC_UNSIGNED;199 200 MachO::any_relocation_info MRE;201 MRE.r_word0 = FixupOffset;202 MRE.r_word1 =203 (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);204 Writer->addRelocation(A_Base, Fragment->getParent(), MRE);205 206 if (B_Base)207 RelSymbol = B_Base;208 else209 Index = B->getFragment()->getParent()->getOrdinal() + 1;210 Type = MachO::X86_64_RELOC_SUBTRACTOR;211 } else {212 const MCSymbol *Symbol = Target.getAddSym();213 if (Symbol->isTemporary() && Value) {214 const MCSection &Sec = Symbol->getSection();215 if (!MCAsmInfoDarwin::isSectionAtomizableBySymbols(Sec))216 Symbol->setUsedInReloc();217 }218 RelSymbol = Writer->getAtom(*Symbol);219 220 // Relocations inside debug sections always use local relocations when221 // possible. This seems to be done because the debugger doesn't fully222 // understand x86_64 relocation entries, and expects to find values that223 // have already been fixed up.224 if (Symbol->isInSection()) {225 const MCSectionMachO &Section =226 static_cast<const MCSectionMachO &>(*Fragment->getParent());227 if (Section.hasAttribute(MachO::S_ATTR_DEBUG))228 RelSymbol = nullptr;229 }230 231 // x86_64 almost always uses external relocations, except when there is no232 // symbol to use as a base address (a local symbol with no preceding233 // non-local symbol).234 if (RelSymbol) {235 // Add the local offset, if needed.236 if (RelSymbol != Symbol)237 Value += Asm.getSymbolOffset(*Symbol) - Asm.getSymbolOffset(*RelSymbol);238 } else if (Symbol->isInSection() && !Symbol->isVariable()) {239 // The index is the section ordinal (1-based).240 Index = Symbol->getFragment()->getParent()->getOrdinal() + 1;241 Value += Writer->getSymbolAddress(*Symbol);242 243 if (IsPCRel)244 Value -= FixupAddress + (1 << Log2Size);245 } else if (Symbol->isVariable()) {246 FixedValue = Writer->getSymbolAddress(*Symbol);247 return;248 } else {249 reportError(Fixup.getLoc(),250 "unsupported relocation of undefined symbol '" +251 Symbol->getName() + "'");252 return;253 }254 255 auto Specifier = Target.getSpecifier();256 if (IsPCRel) {257 if (IsRIPRel) {258 if (Specifier == X86::S_GOTPCREL) {259 // x86_64 distinguishes movq foo@GOTPCREL so that the linker can260 // rewrite the movq to an leaq at link time if the symbol ends up in261 // the same linkage unit.262 if (Fixup.getKind() == X86::reloc_riprel_4byte_movq_load)263 Type = MachO::X86_64_RELOC_GOT_LOAD;264 else265 Type = MachO::X86_64_RELOC_GOT;266 } else if (Specifier == X86::S_TLVP) {267 Type = MachO::X86_64_RELOC_TLV;268 } else if (Specifier) {269 reportError(Fixup.getLoc(),270 "unsupported symbol modifier in relocation");271 return;272 } else {273 Type = MachO::X86_64_RELOC_SIGNED;274 275 // The Darwin x86_64 relocation format has a problem where it cannot276 // encode an address (L<foo> + <constant>) which is outside the atom277 // containing L<foo>. Generally, this shouldn't occur but it does278 // happen when we have a RIPrel instruction with data following the279 // relocation entry (e.g., movb $012, L0(%rip)). Even with the PCrel280 // adjustment Darwin x86_64 uses, the offset is still negative and the281 // linker has no way to recognize this.282 //283 // To work around this, Darwin uses several special relocation types284 // to indicate the offsets. However, the specification or285 // implementation of these seems to also be incomplete; they should286 // adjust the addend as well based on the actual encoded instruction287 // (the additional bias), but instead appear to just look at the final288 // offset.289 switch (-(Target.getConstant() + (1LL << Log2Size))) {290 case 1: Type = MachO::X86_64_RELOC_SIGNED_1; break;291 case 2: Type = MachO::X86_64_RELOC_SIGNED_2; break;292 case 4: Type = MachO::X86_64_RELOC_SIGNED_4; break;293 }294 }295 } else {296 if (Specifier) {297 reportError(Fixup.getLoc(),298 "unsupported symbol modifier in branch relocation");299 return;300 }301 302 Type = MachO::X86_64_RELOC_BRANCH;303 }304 } else {305 if (Specifier == X86::S_GOT) {306 Type = MachO::X86_64_RELOC_GOT;307 } else if (Specifier == X86::S_GOTPCREL) {308 // GOTPCREL is allowed as a modifier on non-PCrel instructions, in which309 // case all we do is set the PCrel bit in the relocation entry; this is310 // used with exception handling, for example. The source is required to311 // include any necessary offset directly.312 Type = MachO::X86_64_RELOC_GOT;313 IsPCRel = 1;314 } else if (Specifier == X86::S_TLVP) {315 reportError(Fixup.getLoc(),316 "TLVP symbol modifier should have been rip-rel");317 return;318 } else if (Specifier) {319 reportError(Fixup.getLoc(),320 "unsupported symbol modifier in relocation");321 return;322 } else {323 Type = MachO::X86_64_RELOC_UNSIGNED;324 if (Fixup.getKind() == X86::reloc_signed_4byte) {325 reportError(326 Fixup.getLoc(),327 "32-bit absolute addressing is not supported in 64-bit mode");328 return;329 }330 }331 }332 }333 334 // x86_64 always writes custom values into the fixups.335 FixedValue = Value;336 337 // struct relocation_info (8 bytes)338 MachO::any_relocation_info MRE;339 MRE.r_word0 = FixupOffset;340 MRE.r_word1 = (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) |341 (IsExtern << 27) | (Type << 28);342 Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);343}344 345bool X86MachObjectWriter::recordScatteredRelocation(MachObjectWriter *Writer,346 const MCAssembler &Asm,347 const MCFragment *Fragment,348 const MCFixup &Fixup,349 MCValue Target,350 unsigned Log2Size,351 uint64_t &FixedValue) {352 uint64_t OriginalFixedValue = FixedValue;353 uint32_t FixupOffset = Asm.getFragmentOffset(*Fragment) + Fixup.getOffset();354 unsigned IsPCRel = Fixup.isPCRel();355 unsigned Type = MachO::GENERIC_RELOC_VANILLA;356 357 // See <reloc.h>.358 auto *A = static_cast<const MCSymbolMachO *>(Target.getAddSym());359 if (!A->getFragment()) {360 reportError(Fixup.getLoc(),361 "symbol '" + A->getName() +362 "' can not be undefined in a subtraction expression");363 return false;364 }365 366 uint32_t Value = Writer->getSymbolAddress(*A);367 uint64_t SecAddr = Writer->getSectionAddress(A->getFragment()->getParent());368 FixedValue += SecAddr;369 uint32_t Value2 = 0;370 371 if (const MCSymbol *SB = Target.getSubSym()) {372 if (!SB->getFragment()) {373 reportError(Fixup.getLoc(),374 "symbol '" + SB->getName() +375 "' can not be undefined in a subtraction expression");376 return false;377 }378 379 // Select the appropriate difference relocation type.380 //381 // Note that there is no longer any semantic difference between these two382 // relocation types from the linkers point of view, this is done solely for383 // pedantic compatibility with 'as'.384 Type = A->isExternal() ? (unsigned)MachO::GENERIC_RELOC_SECTDIFF385 : (unsigned)MachO::GENERIC_RELOC_LOCAL_SECTDIFF;386 Value2 = Writer->getSymbolAddress(*SB);387 FixedValue -= Writer->getSectionAddress(SB->getFragment()->getParent());388 }389 390 // Relocations are written out in reverse order, so the PAIR comes first.391 if (Type == MachO::GENERIC_RELOC_SECTDIFF ||392 Type == MachO::GENERIC_RELOC_LOCAL_SECTDIFF) {393 // If the offset is too large to fit in a scattered relocation,394 // we're hosed. It's an unfortunate limitation of the MachO format.395 if (FixupOffset > 0xffffff) {396 char Buffer[32];397 format("0x%x", FixupOffset).print(Buffer, sizeof(Buffer));398 reportError(Fixup.getLoc(), Twine("Section too large, can't encode "399 "r_address (") +400 Buffer +401 ") into 24 bits of scattered "402 "relocation entry.");403 return false;404 }405 406 MachO::any_relocation_info MRE;407 MRE.r_word0 = ((0 << 0) | // r_address408 (MachO::GENERIC_RELOC_PAIR << 24) | // r_type409 (Log2Size << 28) |410 (IsPCRel << 30) |411 MachO::R_SCATTERED);412 MRE.r_word1 = Value2;413 Writer->addRelocation(nullptr, Fragment->getParent(), MRE);414 } else {415 // If the offset is more than 24-bits, it won't fit in a scattered416 // relocation offset field, so we fall back to using a non-scattered417 // relocation. This is a bit risky, as if the offset reaches out of418 // the block and the linker is doing scattered loading on this419 // symbol, things can go badly.420 //421 // Required for 'as' compatibility.422 if (FixupOffset > 0xffffff) {423 FixedValue = OriginalFixedValue;424 return false;425 }426 }427 428 MachO::any_relocation_info MRE;429 MRE.r_word0 = ((FixupOffset << 0) |430 (Type << 24) |431 (Log2Size << 28) |432 (IsPCRel << 30) |433 MachO::R_SCATTERED);434 MRE.r_word1 = Value;435 Writer->addRelocation(nullptr, Fragment->getParent(), MRE);436 return true;437}438 439void X86MachObjectWriter::recordTLVPRelocation(MachObjectWriter *Writer,440 const MCAssembler &Asm,441 const MCFragment *Fragment,442 const MCFixup &Fixup,443 MCValue Target,444 uint64_t &FixedValue) {445 const MCSymbol *SymA = Target.getAddSym();446 assert(Target.getSpecifier() == X86::S_TLVP && !is64Bit() &&447 "Should only be called with a 32-bit TLVP relocation!");448 449 unsigned Log2Size = getFixupKindLog2Size(Fixup.getKind());450 uint32_t Value = Asm.getFragmentOffset(*Fragment) + Fixup.getOffset();451 unsigned IsPCRel = 0;452 453 // We're only going to have a second symbol in pic mode and it'll be a454 // subtraction from the picbase. For 32-bit pic the addend is the difference455 // between the picbase and the next address. For 32-bit static the addend is456 // zero.457 if (auto *SymB = Target.getSubSym()) {458 // If this is a subtraction then we're pcrel.459 uint32_t FixupAddress =460 Writer->getFragmentAddress(Asm, Fragment) + Fixup.getOffset();461 IsPCRel = 1;462 FixedValue =463 FixupAddress - Writer->getSymbolAddress(*SymB) + Target.getConstant();464 FixedValue += 1ULL << Log2Size;465 } else {466 FixedValue = 0;467 }468 469 // struct relocation_info (8 bytes)470 MachO::any_relocation_info MRE;471 MRE.r_word0 = Value;472 MRE.r_word1 =473 (IsPCRel << 24) | (Log2Size << 25) | (MachO::GENERIC_RELOC_TLV << 28);474 Writer->addRelocation(SymA, Fragment->getParent(), MRE);475}476 477void X86MachObjectWriter::RecordX86Relocation(MachObjectWriter *Writer,478 const MCAssembler &Asm,479 const MCFragment *Fragment,480 const MCFixup &Fixup,481 MCValue Target,482 uint64_t &FixedValue) {483 unsigned IsPCRel = Fixup.isPCRel();484 unsigned Log2Size = getFixupKindLog2Size(Fixup.getKind());485 const MCSymbol *A = Target.getAddSym();486 487 // If this is a 32-bit TLVP reloc it's handled a bit differently.488 if (A && Target.getSpecifier() == X86::S_TLVP) {489 recordTLVPRelocation(Writer, Asm, Fragment, Fixup, Target, FixedValue);490 return;491 }492 493 // If this is a difference or a defined symbol plus an offset, then we need a494 // scattered relocation entry. Differences always require scattered495 // relocations.496 if (Target.getSubSym()) {497 recordScatteredRelocation(Writer, Asm, Fragment, Fixup, Target, Log2Size,498 FixedValue);499 return;500 }501 502 // If this is an internal relocation with an offset, it also needs a scattered503 // relocation entry.504 uint32_t Offset = Target.getConstant();505 if (IsPCRel)506 Offset += 1 << Log2Size;507 508 // Try to record the scattered relocation if needed. Fall back to non509 // scattered if necessary (see comments in recordScatteredRelocation()510 // for details).511 if (Offset && A && !Writer->doesSymbolRequireExternRelocation(*A) &&512 recordScatteredRelocation(Writer, Asm, Fragment, Fixup, Target, Log2Size,513 FixedValue))514 return;515 516 // See <reloc.h>.517 uint32_t FixupOffset = Asm.getFragmentOffset(*Fragment) + Fixup.getOffset();518 unsigned Index = 0;519 unsigned Type = 0;520 const MCSymbol *RelSymbol = nullptr;521 522 if (Target.isAbsolute()) { // constant523 // SymbolNum of 0 indicates the absolute section.524 //525 // FIXME: Currently, these are never generated (see code below). I cannot526 // find a case where they are actually emitted.527 Type = MachO::GENERIC_RELOC_VANILLA;528 } else {529 assert(A && "Unknown symbol data");530 531 // Resolve constant variables.532 if (A->isVariable()) {533 MCValue Val;534 bool Relocatable =535 A->getVariableValue()->evaluateAsRelocatable(Val, &Asm);536 int64_t Res = Val.getConstant();537 bool isAbs = Val.isAbsolute();538 if (Relocatable && Val.getAddSym() && Val.getSubSym()) {539 Res += Writer->getSymbolAddress(*Val.getAddSym()) -540 Writer->getSymbolAddress(*Val.getSubSym());541 isAbs = true;542 }543 if (isAbs) {544 FixedValue = Res;545 return;546 }547 }548 549 // Check whether we need an external or internal relocation.550 if (Writer->doesSymbolRequireExternRelocation(*A)) {551 RelSymbol = A;552 // For external relocations, make sure to offset the fixup value to553 // compensate for the addend of the symbol address, if it was554 // undefined. This occurs with weak definitions, for example.555 if (!A->isUndefined())556 FixedValue -= Asm.getSymbolOffset(*A);557 } else {558 // The index is the section ordinal (1-based).559 const MCSection &Sec = A->getSection();560 Index = Sec.getOrdinal() + 1;561 FixedValue += Writer->getSectionAddress(&Sec);562 }563 if (IsPCRel)564 FixedValue -= Writer->getSectionAddress(Fragment->getParent());565 566 Type = MachO::GENERIC_RELOC_VANILLA;567 }568 569 // struct relocation_info (8 bytes)570 MachO::any_relocation_info MRE;571 MRE.r_word0 = FixupOffset;572 MRE.r_word1 =573 (Index << 0) | (IsPCRel << 24) | (Log2Size << 25) | (Type << 28);574 Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE);575}576 577std::unique_ptr<MCObjectTargetWriter>578llvm::createX86MachObjectWriter(bool Is64Bit, uint32_t CPUType,579 uint32_t CPUSubtype) {580 return std::make_unique<X86MachObjectWriter>(Is64Bit, CPUType, CPUSubtype);581}582