1234285Sdim//===-- PPCELFObjectWriter.cpp - PPC ELF Writer ---------------------------===// 2234285Sdim// 3234285Sdim// The LLVM Compiler Infrastructure 4234285Sdim// 5234285Sdim// This file is distributed under the University of Illinois Open Source 6234285Sdim// License. See LICENSE.TXT for details. 7234285Sdim// 8234285Sdim//===----------------------------------------------------------------------===// 9234285Sdim 10249423Sdim#include "MCTargetDesc/PPCMCTargetDesc.h" 11234285Sdim#include "MCTargetDesc/PPCFixupKinds.h" 12249423Sdim#include "llvm/ADT/STLExtras.h" 13234285Sdim#include "llvm/MC/MCELFObjectWriter.h" 14243830Sdim#include "llvm/MC/MCExpr.h" 15243830Sdim#include "llvm/MC/MCValue.h" 16249423Sdim#include "llvm/Support/ErrorHandling.h" 17234285Sdim 18234285Sdimusing namespace llvm; 19234285Sdim 20234285Sdimnamespace { 21234285Sdim class PPCELFObjectWriter : public MCELFObjectTargetWriter { 22234285Sdim public: 23234285Sdim PPCELFObjectWriter(bool Is64Bit, uint8_t OSABI); 24234285Sdim 25234285Sdim virtual ~PPCELFObjectWriter(); 26234285Sdim protected: 27243830Sdim virtual unsigned getRelocTypeInner(const MCValue &Target, 28243830Sdim const MCFixup &Fixup, 29243830Sdim bool IsPCRel) const; 30234285Sdim virtual unsigned GetRelocType(const MCValue &Target, const MCFixup &Fixup, 31234285Sdim bool IsPCRel, bool IsRelocWithSymbol, 32234285Sdim int64_t Addend) const; 33243830Sdim virtual const MCSymbol *undefinedExplicitRelSym(const MCValue &Target, 34243830Sdim const MCFixup &Fixup, 35243830Sdim bool IsPCRel) const; 36234285Sdim virtual void adjustFixupOffset(const MCFixup &Fixup, uint64_t &RelocOffset); 37249423Sdim 38249423Sdim virtual void sortRelocs(const MCAssembler &Asm, 39249423Sdim std::vector<ELFRelocationEntry> &Relocs); 40234285Sdim }; 41249423Sdim 42249423Sdim class PPCELFRelocationEntry : public ELFRelocationEntry { 43249423Sdim public: 44249423Sdim PPCELFRelocationEntry(const ELFRelocationEntry &RE); 45249423Sdim bool operator<(const PPCELFRelocationEntry &RE) const { 46249423Sdim return (RE.r_offset < r_offset || 47249423Sdim (RE.r_offset == r_offset && RE.Type > Type)); 48249423Sdim } 49249423Sdim }; 50234285Sdim} 51234285Sdim 52249423SdimPPCELFRelocationEntry::PPCELFRelocationEntry(const ELFRelocationEntry &RE) 53249423Sdim : ELFRelocationEntry(RE.r_offset, RE.Index, RE.Type, RE.Symbol, 54249423Sdim RE.r_addend, *RE.Fixup) {} 55249423Sdim 56234285SdimPPCELFObjectWriter::PPCELFObjectWriter(bool Is64Bit, uint8_t OSABI) 57234285Sdim : MCELFObjectTargetWriter(Is64Bit, OSABI, 58234285Sdim Is64Bit ? ELF::EM_PPC64 : ELF::EM_PPC, 59234285Sdim /*HasRelocationAddend*/ true) {} 60234285Sdim 61234285SdimPPCELFObjectWriter::~PPCELFObjectWriter() { 62234285Sdim} 63234285Sdim 64243830Sdimunsigned PPCELFObjectWriter::getRelocTypeInner(const MCValue &Target, 65243830Sdim const MCFixup &Fixup, 66243830Sdim bool IsPCRel) const 67243830Sdim{ 68243830Sdim MCSymbolRefExpr::VariantKind Modifier = Target.isAbsolute() ? 69243830Sdim MCSymbolRefExpr::VK_None : Target.getSymA()->getKind(); 70243830Sdim 71234285Sdim // determine the type of the relocation 72234285Sdim unsigned Type; 73234285Sdim if (IsPCRel) { 74234285Sdim switch ((unsigned)Fixup.getKind()) { 75234285Sdim default: 76234285Sdim llvm_unreachable("Unimplemented"); 77234285Sdim case PPC::fixup_ppc_br24: 78234285Sdim Type = ELF::R_PPC_REL24; 79234285Sdim break; 80251662Sdim case PPC::fixup_ppc_brcond14: 81251662Sdim Type = ELF::R_PPC_REL14; 82251662Sdim break; 83249423Sdim case FK_Data_4: 84234285Sdim case FK_PCRel_4: 85234285Sdim Type = ELF::R_PPC_REL32; 86234285Sdim break; 87249423Sdim case FK_Data_8: 88249423Sdim case FK_PCRel_8: 89249423Sdim Type = ELF::R_PPC64_REL64; 90249423Sdim break; 91234285Sdim } 92234285Sdim } else { 93234285Sdim switch ((unsigned)Fixup.getKind()) { 94234285Sdim default: llvm_unreachable("invalid fixup kind!"); 95234285Sdim case PPC::fixup_ppc_br24: 96234285Sdim Type = ELF::R_PPC_ADDR24; 97234285Sdim break; 98234285Sdim case PPC::fixup_ppc_brcond14: 99243830Sdim Type = ELF::R_PPC_ADDR14; // XXX: or BRNTAKEN?_ 100234285Sdim break; 101234285Sdim case PPC::fixup_ppc_ha16: 102243830Sdim switch (Modifier) { 103243830Sdim default: llvm_unreachable("Unsupported Modifier"); 104243830Sdim case MCSymbolRefExpr::VK_PPC_TPREL16_HA: 105243830Sdim Type = ELF::R_PPC_TPREL16_HA; 106243830Sdim break; 107249423Sdim case MCSymbolRefExpr::VK_PPC_DTPREL16_HA: 108249423Sdim Type = ELF::R_PPC64_DTPREL16_HA; 109249423Sdim break; 110251662Sdim case MCSymbolRefExpr::VK_PPC_GAS_HA16: 111251662Sdim case MCSymbolRefExpr::VK_PPC_DARWIN_HA16: 112243830Sdim Type = ELF::R_PPC_ADDR16_HA; 113243830Sdim break; 114249423Sdim case MCSymbolRefExpr::VK_PPC_TOC16_HA: 115249423Sdim Type = ELF::R_PPC64_TOC16_HA; 116249423Sdim break; 117249423Sdim case MCSymbolRefExpr::VK_PPC_GOT_TPREL16_HA: 118249423Sdim Type = ELF::R_PPC64_GOT_TPREL16_HA; 119249423Sdim break; 120249423Sdim case MCSymbolRefExpr::VK_PPC_GOT_TLSGD16_HA: 121249423Sdim Type = ELF::R_PPC64_GOT_TLSGD16_HA; 122249423Sdim break; 123249423Sdim case MCSymbolRefExpr::VK_PPC_GOT_TLSLD16_HA: 124249423Sdim Type = ELF::R_PPC64_GOT_TLSLD16_HA; 125249423Sdim break; 126243830Sdim } 127234285Sdim break; 128234285Sdim case PPC::fixup_ppc_lo16: 129243830Sdim switch (Modifier) { 130243830Sdim default: llvm_unreachable("Unsupported Modifier"); 131243830Sdim case MCSymbolRefExpr::VK_PPC_TPREL16_LO: 132243830Sdim Type = ELF::R_PPC_TPREL16_LO; 133243830Sdim break; 134249423Sdim case MCSymbolRefExpr::VK_PPC_DTPREL16_LO: 135249423Sdim Type = ELF::R_PPC64_DTPREL16_LO; 136249423Sdim break; 137243830Sdim case MCSymbolRefExpr::VK_None: 138251662Sdim Type = ELF::R_PPC_ADDR16; 139251662Sdim break; 140251662Sdim case MCSymbolRefExpr::VK_PPC_GAS_LO16: 141251662Sdim case MCSymbolRefExpr::VK_PPC_DARWIN_LO16: 142243830Sdim Type = ELF::R_PPC_ADDR16_LO; 143243830Sdim break; 144249423Sdim case MCSymbolRefExpr::VK_PPC_TOC_ENTRY: 145249423Sdim Type = ELF::R_PPC64_TOC16; 146249423Sdim break; 147249423Sdim case MCSymbolRefExpr::VK_PPC_TOC16_LO: 148249423Sdim Type = ELF::R_PPC64_TOC16_LO; 149249423Sdim break; 150249423Sdim case MCSymbolRefExpr::VK_PPC_GOT_TLSGD16_LO: 151249423Sdim Type = ELF::R_PPC64_GOT_TLSGD16_LO; 152249423Sdim break; 153249423Sdim case MCSymbolRefExpr::VK_PPC_GOT_TLSLD16_LO: 154249423Sdim Type = ELF::R_PPC64_GOT_TLSLD16_LO; 155249423Sdim break; 156243830Sdim } 157234285Sdim break; 158249423Sdim case PPC::fixup_ppc_lo16_ds: 159249423Sdim switch (Modifier) { 160249423Sdim default: llvm_unreachable("Unsupported Modifier"); 161249423Sdim case MCSymbolRefExpr::VK_None: 162249423Sdim Type = ELF::R_PPC64_ADDR16_DS; 163249423Sdim break; 164251662Sdim case MCSymbolRefExpr::VK_PPC_GAS_LO16: 165251662Sdim case MCSymbolRefExpr::VK_PPC_DARWIN_LO16: 166251662Sdim Type = ELF::R_PPC64_ADDR16_LO_DS; 167251662Sdim break; 168249423Sdim case MCSymbolRefExpr::VK_PPC_TOC_ENTRY: 169249423Sdim Type = ELF::R_PPC64_TOC16_DS; 170249423Sdim break; 171249423Sdim case MCSymbolRefExpr::VK_PPC_TOC16_LO: 172249423Sdim Type = ELF::R_PPC64_TOC16_LO_DS; 173249423Sdim break; 174249423Sdim case MCSymbolRefExpr::VK_PPC_GOT_TPREL16_LO: 175249423Sdim Type = ELF::R_PPC64_GOT_TPREL16_LO_DS; 176249423Sdim break; 177249423Sdim } 178234285Sdim break; 179249423Sdim case PPC::fixup_ppc_tlsreg: 180249423Sdim Type = ELF::R_PPC64_TLS; 181243830Sdim break; 182249423Sdim case PPC::fixup_ppc_nofixup: 183249423Sdim switch (Modifier) { 184249423Sdim default: llvm_unreachable("Unsupported Modifier"); 185249423Sdim case MCSymbolRefExpr::VK_PPC_TLSGD: 186249423Sdim Type = ELF::R_PPC64_TLSGD; 187249423Sdim break; 188249423Sdim case MCSymbolRefExpr::VK_PPC_TLSLD: 189249423Sdim Type = ELF::R_PPC64_TLSLD; 190249423Sdim break; 191249423Sdim } 192243830Sdim break; 193243830Sdim case FK_Data_8: 194243830Sdim switch (Modifier) { 195243830Sdim default: llvm_unreachable("Unsupported Modifier"); 196243830Sdim case MCSymbolRefExpr::VK_PPC_TOC: 197243830Sdim Type = ELF::R_PPC64_TOC; 198243830Sdim break; 199243830Sdim case MCSymbolRefExpr::VK_None: 200243830Sdim Type = ELF::R_PPC64_ADDR64; 201243830Sdim break; 202243830Sdim } 203243830Sdim break; 204234285Sdim case FK_Data_4: 205234285Sdim Type = ELF::R_PPC_ADDR32; 206234285Sdim break; 207234285Sdim case FK_Data_2: 208234285Sdim Type = ELF::R_PPC_ADDR16; 209234285Sdim break; 210234285Sdim } 211234285Sdim } 212234285Sdim return Type; 213234285Sdim} 214234285Sdim 215243830Sdimunsigned PPCELFObjectWriter::GetRelocType(const MCValue &Target, 216243830Sdim const MCFixup &Fixup, 217243830Sdim bool IsPCRel, 218243830Sdim bool IsRelocWithSymbol, 219243830Sdim int64_t Addend) const { 220243830Sdim return getRelocTypeInner(Target, Fixup, IsPCRel); 221243830Sdim} 222243830Sdim 223243830Sdimconst MCSymbol *PPCELFObjectWriter::undefinedExplicitRelSym(const MCValue &Target, 224243830Sdim const MCFixup &Fixup, 225243830Sdim bool IsPCRel) const { 226243830Sdim assert(Target.getSymA() && "SymA cannot be 0"); 227243830Sdim const MCSymbol &Symbol = Target.getSymA()->getSymbol().AliasedSymbol(); 228243830Sdim 229243830Sdim unsigned RelocType = getRelocTypeInner(Target, Fixup, IsPCRel); 230243830Sdim 231243830Sdim // The .odp creation emits a relocation against the symbol ".TOC." which 232243830Sdim // create a R_PPC64_TOC relocation. However the relocation symbol name 233243830Sdim // in final object creation should be NULL, since the symbol does not 234243830Sdim // really exist, it is just the reference to TOC base for the current 235243830Sdim // object file. 236243830Sdim bool EmitThisSym = RelocType != ELF::R_PPC64_TOC; 237243830Sdim 238243830Sdim if (EmitThisSym && !Symbol.isTemporary()) 239243830Sdim return &Symbol; 240243830Sdim return NULL; 241243830Sdim} 242243830Sdim 243234285Sdimvoid PPCELFObjectWriter:: 244234285SdimadjustFixupOffset(const MCFixup &Fixup, uint64_t &RelocOffset) { 245234285Sdim switch ((unsigned)Fixup.getKind()) { 246234285Sdim case PPC::fixup_ppc_ha16: 247234285Sdim case PPC::fixup_ppc_lo16: 248249423Sdim case PPC::fixup_ppc_lo16_ds: 249234285Sdim RelocOffset += 2; 250234285Sdim break; 251234285Sdim default: 252234285Sdim break; 253234285Sdim } 254234285Sdim} 255234285Sdim 256249423Sdim// The standard sorter only sorts on the r_offset field, but PowerPC can 257249423Sdim// have multiple relocations at the same offset. Sort secondarily on the 258249423Sdim// relocation type to avoid nondeterminism. 259249423Sdimvoid PPCELFObjectWriter::sortRelocs(const MCAssembler &Asm, 260249423Sdim std::vector<ELFRelocationEntry> &Relocs) { 261249423Sdim 262249423Sdim // Copy to a temporary vector of relocation entries having a different 263249423Sdim // sort function. 264249423Sdim std::vector<PPCELFRelocationEntry> TmpRelocs; 265249423Sdim 266249423Sdim for (std::vector<ELFRelocationEntry>::iterator R = Relocs.begin(); 267249423Sdim R != Relocs.end(); ++R) { 268249423Sdim TmpRelocs.push_back(PPCELFRelocationEntry(*R)); 269249423Sdim } 270249423Sdim 271249423Sdim // Sort in place by ascending r_offset and descending r_type. 272249423Sdim array_pod_sort(TmpRelocs.begin(), TmpRelocs.end()); 273249423Sdim 274249423Sdim // Copy back to the original vector. 275249423Sdim unsigned I = 0; 276249423Sdim for (std::vector<PPCELFRelocationEntry>::iterator R = TmpRelocs.begin(); 277249423Sdim R != TmpRelocs.end(); ++R, ++I) { 278249423Sdim Relocs[I] = ELFRelocationEntry(R->r_offset, R->Index, R->Type, 279249423Sdim R->Symbol, R->r_addend, *R->Fixup); 280249423Sdim } 281249423Sdim} 282249423Sdim 283249423Sdim 284234285SdimMCObjectWriter *llvm::createPPCELFObjectWriter(raw_ostream &OS, 285234285Sdim bool Is64Bit, 286234285Sdim uint8_t OSABI) { 287234285Sdim MCELFObjectTargetWriter *MOTW = new PPCELFObjectWriter(Is64Bit, OSABI); 288234285Sdim return createELFObjectWriter(MOTW, OS, /*IsLittleEndian=*/false); 289234285Sdim} 290