1251607Sdim//===-- SystemZMCObjectWriter.cpp - SystemZ ELF writer --------------------===//
2251607Sdim//
3251607Sdim//                     The LLVM Compiler Infrastructure
4251607Sdim//
5251607Sdim// This file is distributed under the University of Illinois Open Source
6251607Sdim// License. See LICENSE.TXT for details.
7251607Sdim//
8251607Sdim//===----------------------------------------------------------------------===//
9251607Sdim
10251607Sdim#include "MCTargetDesc/SystemZMCTargetDesc.h"
11251607Sdim#include "MCTargetDesc/SystemZMCFixups.h"
12251607Sdim#include "llvm/MC/MCELFObjectWriter.h"
13251607Sdim#include "llvm/MC/MCExpr.h"
14251607Sdim#include "llvm/MC/MCValue.h"
15251607Sdim
16251607Sdimusing namespace llvm;
17251607Sdim
18251607Sdimnamespace {
19251607Sdimclass SystemZObjectWriter : public MCELFObjectTargetWriter {
20251607Sdimpublic:
21251607Sdim  SystemZObjectWriter(uint8_t OSABI);
22251607Sdim
23288943Sdim  ~SystemZObjectWriter() override;
24251607Sdim
25251607Sdimprotected:
26251607Sdim  // Override MCELFObjectTargetWriter.
27276479Sdim  unsigned GetRelocType(const MCValue &Target, const MCFixup &Fixup,
28276479Sdim                        bool IsPCRel) const override;
29251607Sdim};
30276479Sdim} // end anonymous namespace
31251607Sdim
32251607SdimSystemZObjectWriter::SystemZObjectWriter(uint8_t OSABI)
33251607Sdim  : MCELFObjectTargetWriter(/*Is64Bit=*/true, OSABI, ELF::EM_S390,
34251607Sdim                            /*HasRelocationAddend=*/ true) {}
35251607Sdim
36251607SdimSystemZObjectWriter::~SystemZObjectWriter() {
37251607Sdim}
38251607Sdim
39251607Sdim// Return the relocation type for an absolute value of MCFixupKind Kind.
40251607Sdimstatic unsigned getAbsoluteReloc(unsigned Kind) {
41251607Sdim  switch (Kind) {
42251607Sdim  case FK_Data_1: return ELF::R_390_8;
43251607Sdim  case FK_Data_2: return ELF::R_390_16;
44251607Sdim  case FK_Data_4: return ELF::R_390_32;
45251607Sdim  case FK_Data_8: return ELF::R_390_64;
46251607Sdim  }
47251607Sdim  llvm_unreachable("Unsupported absolute address");
48251607Sdim}
49251607Sdim
50251607Sdim// Return the relocation type for a PC-relative value of MCFixupKind Kind.
51251607Sdimstatic unsigned getPCRelReloc(unsigned Kind) {
52251607Sdim  switch (Kind) {
53251607Sdim  case FK_Data_2:                return ELF::R_390_PC16;
54251607Sdim  case FK_Data_4:                return ELF::R_390_PC32;
55251607Sdim  case FK_Data_8:                return ELF::R_390_PC64;
56251607Sdim  case SystemZ::FK_390_PC16DBL:  return ELF::R_390_PC16DBL;
57251607Sdim  case SystemZ::FK_390_PC32DBL:  return ELF::R_390_PC32DBL;
58251607Sdim  }
59251607Sdim  llvm_unreachable("Unsupported PC-relative address");
60251607Sdim}
61251607Sdim
62251607Sdim// Return the R_390_TLS_LE* relocation type for MCFixupKind Kind.
63251607Sdimstatic unsigned getTLSLEReloc(unsigned Kind) {
64251607Sdim  switch (Kind) {
65251607Sdim  case FK_Data_4: return ELF::R_390_TLS_LE32;
66251607Sdim  case FK_Data_8: return ELF::R_390_TLS_LE64;
67251607Sdim  }
68251607Sdim  llvm_unreachable("Unsupported absolute address");
69251607Sdim}
70251607Sdim
71288943Sdim// Return the R_390_TLS_LDO* relocation type for MCFixupKind Kind.
72288943Sdimstatic unsigned getTLSLDOReloc(unsigned Kind) {
73288943Sdim  switch (Kind) {
74288943Sdim  case FK_Data_4: return ELF::R_390_TLS_LDO32;
75288943Sdim  case FK_Data_8: return ELF::R_390_TLS_LDO64;
76288943Sdim  }
77288943Sdim  llvm_unreachable("Unsupported absolute address");
78288943Sdim}
79288943Sdim
80288943Sdim// Return the R_390_TLS_LDM* relocation type for MCFixupKind Kind.
81288943Sdimstatic unsigned getTLSLDMReloc(unsigned Kind) {
82288943Sdim  switch (Kind) {
83288943Sdim  case FK_Data_4: return ELF::R_390_TLS_LDM32;
84288943Sdim  case FK_Data_8: return ELF::R_390_TLS_LDM64;
85288943Sdim  case SystemZ::FK_390_TLS_CALL: return ELF::R_390_TLS_LDCALL;
86288943Sdim  }
87288943Sdim  llvm_unreachable("Unsupported absolute address");
88288943Sdim}
89288943Sdim
90288943Sdim// Return the R_390_TLS_GD* relocation type for MCFixupKind Kind.
91288943Sdimstatic unsigned getTLSGDReloc(unsigned Kind) {
92288943Sdim  switch (Kind) {
93288943Sdim  case FK_Data_4: return ELF::R_390_TLS_GD32;
94288943Sdim  case FK_Data_8: return ELF::R_390_TLS_GD64;
95288943Sdim  case SystemZ::FK_390_TLS_CALL: return ELF::R_390_TLS_GDCALL;
96288943Sdim  }
97288943Sdim  llvm_unreachable("Unsupported absolute address");
98288943Sdim}
99288943Sdim
100251607Sdim// Return the PLT relocation counterpart of MCFixupKind Kind.
101251607Sdimstatic unsigned getPLTReloc(unsigned Kind) {
102251607Sdim  switch (Kind) {
103251607Sdim  case SystemZ::FK_390_PC16DBL: return ELF::R_390_PLT16DBL;
104251607Sdim  case SystemZ::FK_390_PC32DBL: return ELF::R_390_PLT32DBL;
105251607Sdim  }
106251607Sdim  llvm_unreachable("Unsupported absolute address");
107251607Sdim}
108251607Sdim
109251607Sdimunsigned SystemZObjectWriter::GetRelocType(const MCValue &Target,
110251607Sdim                                           const MCFixup &Fixup,
111276479Sdim                                           bool IsPCRel) const {
112276479Sdim  MCSymbolRefExpr::VariantKind Modifier = Target.getAccessVariant();
113251607Sdim  unsigned Kind = Fixup.getKind();
114251607Sdim  switch (Modifier) {
115251607Sdim  case MCSymbolRefExpr::VK_None:
116251607Sdim    if (IsPCRel)
117251607Sdim      return getPCRelReloc(Kind);
118251607Sdim    return getAbsoluteReloc(Kind);
119251607Sdim
120251607Sdim  case MCSymbolRefExpr::VK_NTPOFF:
121251607Sdim    assert(!IsPCRel && "NTPOFF shouldn't be PC-relative");
122251607Sdim    return getTLSLEReloc(Kind);
123251607Sdim
124288943Sdim  case MCSymbolRefExpr::VK_INDNTPOFF:
125288943Sdim    if (IsPCRel && Kind == SystemZ::FK_390_PC32DBL)
126288943Sdim      return ELF::R_390_TLS_IEENT;
127288943Sdim    llvm_unreachable("Only PC-relative INDNTPOFF accesses are supported for now");
128288943Sdim
129288943Sdim  case MCSymbolRefExpr::VK_DTPOFF:
130288943Sdim    assert(!IsPCRel && "DTPOFF shouldn't be PC-relative");
131288943Sdim    return getTLSLDOReloc(Kind);
132288943Sdim
133288943Sdim  case MCSymbolRefExpr::VK_TLSLDM:
134288943Sdim    assert(!IsPCRel && "TLSLDM shouldn't be PC-relative");
135288943Sdim    return getTLSLDMReloc(Kind);
136288943Sdim
137288943Sdim  case MCSymbolRefExpr::VK_TLSGD:
138288943Sdim    assert(!IsPCRel && "TLSGD shouldn't be PC-relative");
139288943Sdim    return getTLSGDReloc(Kind);
140288943Sdim
141251607Sdim  case MCSymbolRefExpr::VK_GOT:
142251607Sdim    if (IsPCRel && Kind == SystemZ::FK_390_PC32DBL)
143251607Sdim      return ELF::R_390_GOTENT;
144251607Sdim    llvm_unreachable("Only PC-relative GOT accesses are supported for now");
145251607Sdim
146251607Sdim  case MCSymbolRefExpr::VK_PLT:
147251607Sdim    assert(IsPCRel && "@PLT shouldt be PC-relative");
148251607Sdim    return getPLTReloc(Kind);
149251607Sdim
150251607Sdim  default:
151251607Sdim    llvm_unreachable("Modifier not supported");
152251607Sdim  }
153251607Sdim}
154251607Sdim
155288943SdimMCObjectWriter *llvm::createSystemZObjectWriter(raw_pwrite_stream &OS,
156251607Sdim                                                uint8_t OSABI) {
157251607Sdim  MCELFObjectTargetWriter *MOTW = new SystemZObjectWriter(OSABI);
158251607Sdim  return createELFObjectWriter(MOTW, OS, /*IsLittleEndian=*/false);
159251607Sdim}
160