//===-- PPCMachObjectWriter.cpp - PPC Mach-O Writer -----------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "MCTargetDesc/PPCMCTargetDesc.h" #include "MCTargetDesc/PPCFixupKinds.h" #include "llvm/ADT/Twine.h" #include "llvm/MC/MCAsmLayout.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCMachObjectWriter.h" #include "llvm/MC/MCSectionMachO.h" #include "llvm/MC/MCValue.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" #include "llvm/Support/MachO.h" using namespace llvm; namespace { class PPCMachObjectWriter : public MCMachObjectTargetWriter { bool recordScatteredRelocation(MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout, const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, unsigned Log2Size, uint64_t &FixedValue); void RecordPPCRelocation(MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout, const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, uint64_t &FixedValue); public: PPCMachObjectWriter(bool Is64Bit, uint32_t CPUType, uint32_t CPUSubtype) : MCMachObjectTargetWriter(Is64Bit, CPUType, CPUSubtype) {} void recordRelocation(MachObjectWriter *Writer, MCAssembler &Asm, const MCAsmLayout &Layout, const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, uint64_t &FixedValue) override { if (Writer->is64Bit()) { report_fatal_error("Relocation emission for MachO/PPC64 unimplemented."); } else RecordPPCRelocation(Writer, Asm, Layout, Fragment, Fixup, Target, FixedValue); } }; } /// computes the log2 of the size of the relocation, /// used for relocation_info::r_length. static unsigned getFixupKindLog2Size(unsigned Kind) { switch (Kind) { default: report_fatal_error("log2size(FixupKind): Unhandled fixup kind!"); case FK_PCRel_1: case FK_Data_1: return 0; case FK_PCRel_2: case FK_Data_2: return 1; case FK_PCRel_4: case PPC::fixup_ppc_brcond14: case PPC::fixup_ppc_half16: case PPC::fixup_ppc_br24: case FK_Data_4: return 2; case FK_PCRel_8: case FK_Data_8: return 3; } return 0; } /// Translates generic PPC fixup kind to Mach-O/PPC relocation type enum. /// Outline based on PPCELFObjectWriter::getRelocType(). static unsigned getRelocType(const MCValue &Target, const MCFixupKind FixupKind, // from // Fixup.getKind() const bool IsPCRel) { const MCSymbolRefExpr::VariantKind Modifier = Target.isAbsolute() ? MCSymbolRefExpr::VK_None : Target.getSymA()->getKind(); // determine the type of the relocation unsigned Type = MachO::GENERIC_RELOC_VANILLA; if (IsPCRel) { // relative to PC switch ((unsigned)FixupKind) { default: report_fatal_error("Unimplemented fixup kind (relative)"); case PPC::fixup_ppc_br24: Type = MachO::PPC_RELOC_BR24; // R_PPC_REL24 break; case PPC::fixup_ppc_brcond14: Type = MachO::PPC_RELOC_BR14; break; case PPC::fixup_ppc_half16: switch (Modifier) { default: llvm_unreachable("Unsupported modifier for half16 fixup"); case MCSymbolRefExpr::VK_PPC_HA: Type = MachO::PPC_RELOC_HA16; break; case MCSymbolRefExpr::VK_PPC_LO: Type = MachO::PPC_RELOC_LO16; break; case MCSymbolRefExpr::VK_PPC_HI: Type = MachO::PPC_RELOC_HI16; break; } break; } } else { switch ((unsigned)FixupKind) { default: report_fatal_error("Unimplemented fixup kind (absolute)!"); case PPC::fixup_ppc_half16: switch (Modifier) { default: llvm_unreachable("Unsupported modifier for half16 fixup"); case MCSymbolRefExpr::VK_PPC_HA: Type = MachO::PPC_RELOC_HA16_SECTDIFF; break; case MCSymbolRefExpr::VK_PPC_LO: Type = MachO::PPC_RELOC_LO16_SECTDIFF; break; case MCSymbolRefExpr::VK_PPC_HI: Type = MachO::PPC_RELOC_HI16_SECTDIFF; break; } break; case FK_Data_4: break; case FK_Data_2: break; } } return Type; } static void makeRelocationInfo(MachO::any_relocation_info &MRE, const uint32_t FixupOffset, const uint32_t Index, const unsigned IsPCRel, const unsigned Log2Size, const unsigned IsExtern, const unsigned Type) { MRE.r_word0 = FixupOffset; // The bitfield offsets that work (as determined by trial-and-error) // are different than what is documented in the mach-o manuals. // This appears to be an endianness issue; reversing the order of the // documented bitfields in <llvm/Support/MachO.h> fixes this (but // breaks x86/ARM assembly). MRE.r_word1 = ((Index << 8) | // was << 0 (IsPCRel << 7) | // was << 24 (Log2Size << 5) | // was << 25 (IsExtern << 4) | // was << 27 (Type << 0)); // was << 28 } static void makeScatteredRelocationInfo(MachO::any_relocation_info &MRE, const uint32_t Addr, const unsigned Type, const unsigned Log2Size, const unsigned IsPCRel, const uint32_t Value2) { // For notes on bitfield positions and endianness, see: // https://developer.apple.com/library/mac/documentation/developertools/conceptual/MachORuntime/Reference/reference.html#//apple_ref/doc/uid/20001298-scattered_relocation_entry MRE.r_word0 = ((Addr << 0) | (Type << 24) | (Log2Size << 28) | (IsPCRel << 30) | MachO::R_SCATTERED); MRE.r_word1 = Value2; } /// Compute fixup offset (address). static uint32_t getFixupOffset(const MCAsmLayout &Layout, const MCFragment *Fragment, const MCFixup &Fixup) { uint32_t FixupOffset = Layout.getFragmentOffset(Fragment) + Fixup.getOffset(); // On Mach-O, ppc_fixup_half16 relocations must refer to the // start of the instruction, not the second halfword, as ELF does if (unsigned(Fixup.getKind()) == PPC::fixup_ppc_half16) FixupOffset &= ~uint32_t(3); return FixupOffset; } /// \return false if falling back to using non-scattered relocation, /// otherwise true for normal scattered relocation. /// based on X86MachObjectWriter::recordScatteredRelocation /// and ARMMachObjectWriter::recordScatteredRelocation bool PPCMachObjectWriter::recordScatteredRelocation( MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout, const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, unsigned Log2Size, uint64_t &FixedValue) { // caller already computes these, can we just pass and reuse? const uint32_t FixupOffset = getFixupOffset(Layout, Fragment, Fixup); const MCFixupKind FK = Fixup.getKind(); const unsigned IsPCRel = Writer->isFixupKindPCRel(Asm, FK); const unsigned Type = getRelocType(Target, FK, IsPCRel); // Is this a local or SECTDIFF relocation entry? // SECTDIFF relocation entries have symbol subtractions, // and require two entries, the first for the add-symbol value, // the second for the subtract-symbol value. // See <reloc.h>. const MCSymbol *A = &Target.getSymA()->getSymbol(); if (!A->getFragment()) report_fatal_error("symbol '" + A->getName() + "' can not be undefined in a subtraction expression"); uint32_t Value = Writer->getSymbolAddress(*A, Layout); uint64_t SecAddr = Writer->getSectionAddress(A->getFragment()->getParent()); FixedValue += SecAddr; uint32_t Value2 = 0; if (const MCSymbolRefExpr *B = Target.getSymB()) { const MCSymbol *SB = &B->getSymbol(); if (!SB->getFragment()) report_fatal_error("symbol '" + B->getSymbol().getName() + "' can not be undefined in a subtraction expression"); // FIXME: is Type correct? see include/llvm/Support/MachO.h Value2 = Writer->getSymbolAddress(B->getSymbol(), Layout); FixedValue -= Writer->getSectionAddress(SB->getFragment()->getParent()); } // FIXME: does FixedValue get used?? // Relocations are written out in reverse order, so the PAIR comes first. if (Type == MachO::PPC_RELOC_SECTDIFF || Type == MachO::PPC_RELOC_HI16_SECTDIFF || Type == MachO::PPC_RELOC_LO16_SECTDIFF || Type == MachO::PPC_RELOC_HA16_SECTDIFF || Type == MachO::PPC_RELOC_LO14_SECTDIFF || Type == MachO::PPC_RELOC_LOCAL_SECTDIFF) { // X86 had this piece, but ARM does not // If the offset is too large to fit in a scattered relocation, // we're hosed. It's an unfortunate limitation of the MachO format. if (FixupOffset > 0xffffff) { char Buffer[32]; format("0x%x", FixupOffset).print(Buffer, sizeof(Buffer)); Asm.getContext().reportError(Fixup.getLoc(), Twine("Section too large, can't encode " "r_address (") + Buffer + ") into 24 bits of scattered " "relocation entry."); return false; } // Is this supposed to follow MCTarget/PPCAsmBackend.cpp:adjustFixupValue()? // see PPCMCExpr::evaluateAsRelocatableImpl() uint32_t other_half = 0; switch (Type) { case MachO::PPC_RELOC_LO16_SECTDIFF: other_half = (FixedValue >> 16) & 0xffff; // applyFixupOffset longer extracts the high part because it now assumes // this was already done. // It looks like this is not true for the FixedValue needed with Mach-O // relocs. // So we need to adjust FixedValue again here. FixedValue &= 0xffff; break; case MachO::PPC_RELOC_HA16_SECTDIFF: other_half = FixedValue & 0xffff; FixedValue = ((FixedValue >> 16) + ((FixedValue & 0x8000) ? 1 : 0)) & 0xffff; break; case MachO::PPC_RELOC_HI16_SECTDIFF: other_half = FixedValue & 0xffff; FixedValue = (FixedValue >> 16) & 0xffff; break; default: llvm_unreachable("Invalid PPC scattered relocation type."); break; } MachO::any_relocation_info MRE; makeScatteredRelocationInfo(MRE, other_half, MachO::GENERIC_RELOC_PAIR, Log2Size, IsPCRel, Value2); Writer->addRelocation(nullptr, Fragment->getParent(), MRE); } else { // If the offset is more than 24-bits, it won't fit in a scattered // relocation offset field, so we fall back to using a non-scattered // relocation. This is a bit risky, as if the offset reaches out of // the block and the linker is doing scattered loading on this // symbol, things can go badly. // // Required for 'as' compatibility. if (FixupOffset > 0xffffff) return false; } MachO::any_relocation_info MRE; makeScatteredRelocationInfo(MRE, FixupOffset, Type, Log2Size, IsPCRel, Value); Writer->addRelocation(nullptr, Fragment->getParent(), MRE); return true; } // see PPCELFObjectWriter for a general outline of cases void PPCMachObjectWriter::RecordPPCRelocation( MachObjectWriter *Writer, const MCAssembler &Asm, const MCAsmLayout &Layout, const MCFragment *Fragment, const MCFixup &Fixup, MCValue Target, uint64_t &FixedValue) { const MCFixupKind FK = Fixup.getKind(); // unsigned const unsigned Log2Size = getFixupKindLog2Size(FK); const bool IsPCRel = Writer->isFixupKindPCRel(Asm, FK); const unsigned RelocType = getRelocType(Target, FK, IsPCRel); // If this is a difference or a defined symbol plus an offset, then we need a // scattered relocation entry. Differences always require scattered // relocations. if (Target.getSymB() && // Q: are branch targets ever scattered? RelocType != MachO::PPC_RELOC_BR24 && RelocType != MachO::PPC_RELOC_BR14) { recordScatteredRelocation(Writer, Asm, Layout, Fragment, Fixup, Target, Log2Size, FixedValue); return; } // this doesn't seem right for RIT_PPC_BR24 // Get the symbol data, if any. const MCSymbol *A = nullptr; if (Target.getSymA()) A = &Target.getSymA()->getSymbol(); // See <reloc.h>. const uint32_t FixupOffset = getFixupOffset(Layout, Fragment, Fixup); unsigned Index = 0; unsigned Type = RelocType; const MCSymbol *RelSymbol = nullptr; if (Target.isAbsolute()) { // constant // SymbolNum of 0 indicates the absolute section. // // FIXME: Currently, these are never generated (see code below). I cannot // find a case where they are actually emitted. report_fatal_error("FIXME: relocations to absolute targets " "not yet implemented"); // the above line stolen from ARM, not sure } else { // Resolve constant variables. if (A->isVariable()) { int64_t Res; if (A->getVariableValue()->evaluateAsAbsolute( Res, Layout, Writer->getSectionAddressMap())) { FixedValue = Res; return; } } // Check whether we need an external or internal relocation. if (Writer->doesSymbolRequireExternRelocation(*A)) { RelSymbol = A; // For external relocations, make sure to offset the fixup value to // compensate for the addend of the symbol address, if it was // undefined. This occurs with weak definitions, for example. if (!A->isUndefined()) FixedValue -= Layout.getSymbolOffset(*A); } else { // The index is the section ordinal (1-based). const MCSection &Sec = A->getSection(); Index = Sec.getOrdinal() + 1; FixedValue += Writer->getSectionAddress(&Sec); } if (IsPCRel) FixedValue -= Writer->getSectionAddress(Fragment->getParent()); } // struct relocation_info (8 bytes) MachO::any_relocation_info MRE; makeRelocationInfo(MRE, FixupOffset, Index, IsPCRel, Log2Size, false, Type); Writer->addRelocation(RelSymbol, Fragment->getParent(), MRE); } MCObjectWriter *llvm::createPPCMachObjectWriter(raw_pwrite_stream &OS, bool Is64Bit, uint32_t CPUType, uint32_t CPUSubtype) { return createMachObjectWriter( new PPCMachObjectWriter(Is64Bit, CPUType, CPUSubtype), OS, /*IsLittleEndian=*/false); }