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