//===-- PPCELFObjectWriter.cpp - PPC ELF Writer ---------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "MCTargetDesc/PPCFixupKinds.h" #include "MCTargetDesc/PPCMCAsmInfo.h" #include "MCTargetDesc/PPCMCTargetDesc.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCSymbolELF.h" #include "llvm/MC/MCValue.h" #include "llvm/Support/ErrorHandling.h" using namespace llvm; namespace { class PPCELFObjectWriter : public MCELFObjectTargetWriter { public: PPCELFObjectWriter(bool Is64Bit, uint8_t OSABI); protected: unsigned getRelocType(const MCFixup &Fixup, const MCValue &Target, bool IsPCRel) const override; bool needsRelocateWithSymbol(const MCValue &, unsigned Type) const override; }; } PPCELFObjectWriter::PPCELFObjectWriter(bool Is64Bit, uint8_t OSABI) : MCELFObjectTargetWriter(Is64Bit, OSABI, Is64Bit ? ELF::EM_PPC64 : ELF::EM_PPC, /*HasRelocationAddend*/ true) {} unsigned PPCELFObjectWriter::getRelocType(const MCFixup &Fixup, const MCValue &Target, bool IsPCRel) const { SMLoc Loc = Fixup.getValue()->getLoc(); auto Spec = static_cast(Target.getSpecifier()); switch (Spec) { case PPC::S_DTPMOD: case PPC::S_DTPREL: case PPC::S_DTPREL_HA: case PPC::S_DTPREL_HI: case PPC::S_DTPREL_HIGH: case PPC::S_DTPREL_HIGHA: case PPC::S_DTPREL_HIGHER: case PPC::S_DTPREL_HIGHERA: case PPC::S_DTPREL_HIGHEST: case PPC::S_DTPREL_HIGHESTA: case PPC::S_DTPREL_LO: case PPC::S_GOT_DTPREL: case PPC::S_GOT_DTPREL_HA: case PPC::S_GOT_DTPREL_HI: case PPC::S_GOT_DTPREL_LO: case PPC::S_GOT_TLSGD: case PPC::S_GOT_TLSGD_HA: case PPC::S_GOT_TLSGD_HI: case PPC::S_GOT_TLSGD_LO: case PPC::S_GOT_TLSGD_PCREL: case PPC::S_GOT_TLSLD: case PPC::S_GOT_TLSLD_HA: case PPC::S_GOT_TLSLD_HI: case PPC::S_GOT_TLSLD_LO: case PPC::S_GOT_TPREL: case PPC::S_GOT_TPREL_HA: case PPC::S_GOT_TPREL_HI: case PPC::S_GOT_TPREL_LO: case PPC::S_GOT_TPREL_PCREL: case PPC::S_TLS: case PPC::S_TLSGD: case PPC::S_TLSLD: case PPC::S_TLS_PCREL: case PPC::S_TPREL: case PPC::S_TPREL_HA: case PPC::S_TPREL_HI: case PPC::S_TPREL_HIGH: case PPC::S_TPREL_HIGHA: case PPC::S_TPREL_HIGHER: case PPC::S_TPREL_HIGHERA: case PPC::S_TPREL_HIGHEST: case PPC::S_TPREL_HIGHESTA: case PPC::S_TPREL_LO: if (auto *SA = Target.getAddSym()) cast(SA)->setType(ELF::STT_TLS); break; default: break; } // determine the type of the relocation unsigned Type = 0; if (IsPCRel) { switch (Fixup.getKind()) { default: llvm_unreachable("Unimplemented"); case PPC::fixup_ppc_br24: case PPC::fixup_ppc_br24abs: case PPC::fixup_ppc_br24_notoc: switch (Spec) { default: reportError(Loc, "unsupported relocation type"); break; case PPC::S_None: Type = ELF::R_PPC_REL24; break; case PPC::S_PLT: Type = ELF::R_PPC_PLTREL24; break; case PPC::S_LOCAL: Type = ELF::R_PPC_LOCAL24PC; break; case PPC::S_NOTOC: Type = ELF::R_PPC64_REL24_NOTOC; break; } break; case PPC::fixup_ppc_brcond14: case PPC::fixup_ppc_brcond14abs: Type = ELF::R_PPC_REL14; break; case PPC::fixup_ppc_half16: switch (Spec) { default: reportError(Loc, "unsupported relocation type"); return ELF::R_PPC_NONE; case PPC::S_None: return ELF::R_PPC_REL16; case PPC::S_LO: return ELF::R_PPC_REL16_LO; case PPC::S_HI: return ELF::R_PPC_REL16_HI; case PPC::S_HA: return ELF::R_PPC_REL16_HA; } break; case PPC::fixup_ppc_half16ds: case PPC::fixup_ppc_half16dq: reportError(Loc, "unsupported relocation type"); break; case PPC::fixup_ppc_pcrel34: switch (Spec) { default: reportError(Loc, "unsupported relocation type"); break; case PPC::S_PCREL: Type = ELF::R_PPC64_PCREL34; break; case PPC::S_GOT_PCREL: Type = ELF::R_PPC64_GOT_PCREL34; break; case PPC::S_GOT_TLSGD_PCREL: Type = ELF::R_PPC64_GOT_TLSGD_PCREL34; break; case PPC::S_GOT_TLSLD_PCREL: Type = ELF::R_PPC64_GOT_TLSLD_PCREL34; break; case PPC::S_GOT_TPREL_PCREL: Type = ELF::R_PPC64_GOT_TPREL_PCREL34; break; } break; case FK_Data_4: Type = ELF::R_PPC_REL32; break; case FK_Data_8: Type = ELF::R_PPC64_REL64; break; } } else { switch (Fixup.getKind()) { default: llvm_unreachable("invalid fixup kind!"); case PPC::fixup_ppc_br24abs: Type = ELF::R_PPC_ADDR24; break; case PPC::fixup_ppc_brcond14abs: Type = ELF::R_PPC_ADDR14; // XXX: or BRNTAKEN?_ break; case PPC::fixup_ppc_half16: switch (Spec) { default: reportError(Loc, "unsupported relocation type"); break; case PPC::S_LO: return ELF::R_PPC_ADDR16_LO; case PPC::S_HI: return ELF::R_PPC_ADDR16_HI; case PPC::S_HA: return ELF::R_PPC_ADDR16_HA; case PPC::S_HIGH: return ELF::R_PPC64_ADDR16_HIGH; case PPC::S_HIGHA: return ELF::R_PPC64_ADDR16_HIGHA; case PPC::S_HIGHER: return ELF::R_PPC64_ADDR16_HIGHER; case PPC::S_HIGHERA: return ELF::R_PPC64_ADDR16_HIGHERA; case PPC::S_HIGHEST: return ELF::R_PPC64_ADDR16_HIGHEST; case PPC::S_HIGHESTA: return ELF::R_PPC64_ADDR16_HIGHESTA; case PPC::S_None: Type = ELF::R_PPC_ADDR16; break; case PPC::S_GOT: Type = ELF::R_PPC_GOT16; break; case PPC::S_GOT_LO: Type = ELF::R_PPC_GOT16_LO; break; case PPC::S_GOT_HI: Type = ELF::R_PPC_GOT16_HI; break; case PPC::S_GOT_HA: Type = ELF::R_PPC_GOT16_HA; break; case PPC::S_TOC: Type = ELF::R_PPC64_TOC16; break; case PPC::S_TOC_LO: Type = ELF::R_PPC64_TOC16_LO; break; case PPC::S_TOC_HI: Type = ELF::R_PPC64_TOC16_HI; break; case PPC::S_TOC_HA: Type = ELF::R_PPC64_TOC16_HA; break; case PPC::S_TPREL: Type = ELF::R_PPC_TPREL16; break; case PPC::S_TPREL_LO: Type = ELF::R_PPC_TPREL16_LO; break; case PPC::S_TPREL_HI: Type = ELF::R_PPC_TPREL16_HI; break; case PPC::S_TPREL_HA: Type = ELF::R_PPC_TPREL16_HA; break; case PPC::S_TPREL_HIGH: Type = ELF::R_PPC64_TPREL16_HIGH; break; case PPC::S_TPREL_HIGHA: Type = ELF::R_PPC64_TPREL16_HIGHA; break; case PPC::S_TPREL_HIGHER: Type = ELF::R_PPC64_TPREL16_HIGHER; break; case PPC::S_TPREL_HIGHERA: Type = ELF::R_PPC64_TPREL16_HIGHERA; break; case PPC::S_TPREL_HIGHEST: Type = ELF::R_PPC64_TPREL16_HIGHEST; break; case PPC::S_TPREL_HIGHESTA: Type = ELF::R_PPC64_TPREL16_HIGHESTA; break; case PPC::S_DTPREL: Type = ELF::R_PPC64_DTPREL16; break; case PPC::S_DTPREL_LO: Type = ELF::R_PPC64_DTPREL16_LO; break; case PPC::S_DTPREL_HI: Type = ELF::R_PPC64_DTPREL16_HI; break; case PPC::S_DTPREL_HA: Type = ELF::R_PPC64_DTPREL16_HA; break; case PPC::S_DTPREL_HIGH: Type = ELF::R_PPC64_DTPREL16_HIGH; break; case PPC::S_DTPREL_HIGHA: Type = ELF::R_PPC64_DTPREL16_HIGHA; break; case PPC::S_DTPREL_HIGHER: Type = ELF::R_PPC64_DTPREL16_HIGHER; break; case PPC::S_DTPREL_HIGHERA: Type = ELF::R_PPC64_DTPREL16_HIGHERA; break; case PPC::S_DTPREL_HIGHEST: Type = ELF::R_PPC64_DTPREL16_HIGHEST; break; case PPC::S_DTPREL_HIGHESTA: Type = ELF::R_PPC64_DTPREL16_HIGHESTA; break; case PPC::S_GOT_TLSGD: if (is64Bit()) Type = ELF::R_PPC64_GOT_TLSGD16; else Type = ELF::R_PPC_GOT_TLSGD16; break; case PPC::S_GOT_TLSGD_LO: Type = ELF::R_PPC64_GOT_TLSGD16_LO; break; case PPC::S_GOT_TLSGD_HI: Type = ELF::R_PPC64_GOT_TLSGD16_HI; break; case PPC::S_GOT_TLSGD_HA: Type = ELF::R_PPC64_GOT_TLSGD16_HA; break; case PPC::S_GOT_TLSLD: if (is64Bit()) Type = ELF::R_PPC64_GOT_TLSLD16; else Type = ELF::R_PPC_GOT_TLSLD16; break; case PPC::S_GOT_TLSLD_LO: Type = ELF::R_PPC64_GOT_TLSLD16_LO; break; case PPC::S_GOT_TLSLD_HI: Type = ELF::R_PPC64_GOT_TLSLD16_HI; break; case PPC::S_GOT_TLSLD_HA: Type = ELF::R_PPC64_GOT_TLSLD16_HA; break; case PPC::S_GOT_TPREL: /* We don't have R_PPC64_GOT_TPREL16, but since GOT offsets are always 4-aligned, we can use R_PPC64_GOT_TPREL16_DS. */ Type = ELF::R_PPC64_GOT_TPREL16_DS; break; case PPC::S_GOT_TPREL_LO: /* We don't have R_PPC64_GOT_TPREL16_LO, but since GOT offsets are always 4-aligned, we can use R_PPC64_GOT_TPREL16_LO_DS. */ Type = ELF::R_PPC64_GOT_TPREL16_LO_DS; break; case PPC::S_GOT_TPREL_HI: Type = ELF::R_PPC64_GOT_TPREL16_HI; break; case PPC::S_GOT_DTPREL: /* We don't have R_PPC64_GOT_DTPREL16, but since GOT offsets are always 4-aligned, we can use R_PPC64_GOT_DTPREL16_DS. */ Type = ELF::R_PPC64_GOT_DTPREL16_DS; break; case PPC::S_GOT_DTPREL_LO: /* We don't have R_PPC64_GOT_DTPREL16_LO, but since GOT offsets are always 4-aligned, we can use R_PPC64_GOT_DTPREL16_LO_DS. */ Type = ELF::R_PPC64_GOT_DTPREL16_LO_DS; break; case PPC::S_GOT_TPREL_HA: Type = ELF::R_PPC64_GOT_TPREL16_HA; break; case PPC::S_GOT_DTPREL_HI: Type = ELF::R_PPC64_GOT_DTPREL16_HI; break; case PPC::S_GOT_DTPREL_HA: Type = ELF::R_PPC64_GOT_DTPREL16_HA; break; } break; case PPC::fixup_ppc_half16ds: case PPC::fixup_ppc_half16dq: switch (Spec) { default: reportError(Loc, "unsupported relocation type"); break; case PPC::S_LO: return ELF::R_PPC64_ADDR16_LO_DS; case PPC::S_None: Type = ELF::R_PPC64_ADDR16_DS; break; case PPC::S_GOT: Type = ELF::R_PPC64_GOT16_DS; break; case PPC::S_GOT_LO: Type = ELF::R_PPC64_GOT16_LO_DS; break; case PPC::S_TOC: Type = ELF::R_PPC64_TOC16_DS; break; case PPC::S_TOC_LO: Type = ELF::R_PPC64_TOC16_LO_DS; break; case PPC::S_TPREL: Type = ELF::R_PPC64_TPREL16_DS; break; case PPC::S_TPREL_LO: Type = ELF::R_PPC64_TPREL16_LO_DS; break; case PPC::S_DTPREL: Type = ELF::R_PPC64_DTPREL16_DS; break; case PPC::S_DTPREL_LO: Type = ELF::R_PPC64_DTPREL16_LO_DS; break; case PPC::S_GOT_TPREL: Type = ELF::R_PPC64_GOT_TPREL16_DS; break; case PPC::S_GOT_TPREL_LO: Type = ELF::R_PPC64_GOT_TPREL16_LO_DS; break; case PPC::S_GOT_DTPREL: Type = ELF::R_PPC64_GOT_DTPREL16_DS; break; case PPC::S_GOT_DTPREL_LO: Type = ELF::R_PPC64_GOT_DTPREL16_LO_DS; break; } break; case PPC::fixup_ppc_nofixup: switch (Spec) { default: reportError(Loc, "unsupported relocation type"); break; case PPC::S_TLSGD: if (is64Bit()) Type = ELF::R_PPC64_TLSGD; else Type = ELF::R_PPC_TLSGD; break; case PPC::S_TLSLD: if (is64Bit()) Type = ELF::R_PPC64_TLSLD; else Type = ELF::R_PPC_TLSLD; break; case PPC::S_TLS: if (is64Bit()) Type = ELF::R_PPC64_TLS; else Type = ELF::R_PPC_TLS; break; case PPC::S_TLS_PCREL: Type = ELF::R_PPC64_TLS; break; } break; case PPC::fixup_ppc_imm34: switch (Spec) { default: reportError(Loc, "unsupported relocation type"); break; case PPC::S_DTPREL: Type = ELF::R_PPC64_DTPREL34; break; case PPC::S_TPREL: Type = ELF::R_PPC64_TPREL34; break; } break; case FK_Data_8: switch (Spec) { default: reportError(Loc, "unsupported relocation type"); break; case PPC::S_TOCBASE: Type = ELF::R_PPC64_TOC; break; case PPC::S_None: Type = ELF::R_PPC64_ADDR64; break; case PPC::S_DTPMOD: Type = ELF::R_PPC64_DTPMOD64; break; case PPC::S_TPREL: Type = ELF::R_PPC64_TPREL64; break; case PPC::S_DTPREL: Type = ELF::R_PPC64_DTPREL64; break; } break; case FK_Data_4: switch (Spec) { case PPC::S_DTPREL: Type = ELF::R_PPC_DTPREL32; break; default: Type = ELF::R_PPC_ADDR32; } break; case FK_Data_2: Type = ELF::R_PPC_ADDR16; break; } } return Type; } bool PPCELFObjectWriter::needsRelocateWithSymbol(const MCValue &V, unsigned Type) const { switch (Type) { default: return false; case ELF::R_PPC_REL24: case ELF::R_PPC64_REL24_NOTOC: { // If the target symbol has a local entry point, we must keep the // target symbol to preserve that information for the linker. // The "other" values are stored in the last 6 bits of the second byte. // The traditional defines for STO values assume the full byte and thus // the shift to pack it. unsigned Other = cast(V.getAddSym())->getOther() << 2; return (Other & ELF::STO_PPC64_LOCAL_MASK) != 0; } case ELF::R_PPC64_GOT16: case ELF::R_PPC64_GOT16_DS: case ELF::R_PPC64_GOT16_LO: case ELF::R_PPC64_GOT16_LO_DS: case ELF::R_PPC64_GOT16_HI: case ELF::R_PPC64_GOT16_HA: return true; } } std::unique_ptr llvm::createPPCELFObjectWriter(bool Is64Bit, uint8_t OSABI) { return std::make_unique(Is64Bit, OSABI); }