| //===-- SystemZELFObjectWriter.cpp - SystemZ 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/SystemZMCAsmInfo.h" |
| #include "MCTargetDesc/SystemZMCFixups.h" |
| #include "MCTargetDesc/SystemZMCTargetDesc.h" |
| #include "llvm/BinaryFormat/ELF.h" |
| #include "llvm/MC/MCContext.h" |
| #include "llvm/MC/MCELFObjectWriter.h" |
| #include "llvm/MC/MCExpr.h" |
| #include "llvm/MC/MCFixup.h" |
| #include "llvm/MC/MCObjectWriter.h" |
| #include "llvm/MC/MCValue.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include <cassert> |
| #include <cstdint> |
| #include <memory> |
| |
| using namespace llvm; |
| |
| namespace { |
| |
| class SystemZELFObjectWriter : public MCELFObjectTargetWriter { |
| public: |
| SystemZELFObjectWriter(uint8_t OSABI); |
| ~SystemZELFObjectWriter() override = default; |
| |
| protected: |
| // Override MCELFObjectTargetWriter. |
| unsigned getRelocType(const MCFixup &, const MCValue &, |
| bool IsPCRel) const override; |
| bool needsRelocateWithSymbol(const MCValue &, unsigned Type) const override; |
| unsigned getAbsoluteReloc(SMLoc Loc, unsigned Kind) const; |
| unsigned getPCRelReloc(SMLoc Loc, unsigned Kind) const; |
| }; |
| |
| } // end anonymous namespace |
| |
| SystemZELFObjectWriter::SystemZELFObjectWriter(uint8_t OSABI) |
| : MCELFObjectTargetWriter(/*Is64Bit_=*/true, OSABI, ELF::EM_S390, |
| /*HasRelocationAddend_=*/true) {} |
| |
| // Return the relocation type for an absolute value of MCFixupKind Kind. |
| unsigned SystemZELFObjectWriter::getAbsoluteReloc(SMLoc Loc, |
| unsigned Kind) const { |
| switch (Kind) { |
| case FK_Data_1: |
| case SystemZ::FK_390_U8Imm: |
| case SystemZ::FK_390_S8Imm: |
| return ELF::R_390_8; |
| case SystemZ::FK_390_U12Imm: |
| return ELF::R_390_12; |
| case FK_Data_2: |
| case SystemZ::FK_390_U16Imm: |
| case SystemZ::FK_390_S16Imm: |
| return ELF::R_390_16; |
| case SystemZ::FK_390_S20Imm: |
| return ELF::R_390_20; |
| case FK_Data_4: |
| case SystemZ::FK_390_U32Imm: |
| case SystemZ::FK_390_S32Imm: |
| return ELF::R_390_32; |
| case FK_Data_8: |
| return ELF::R_390_64; |
| } |
| reportError(Loc, "Unsupported absolute address"); |
| return 0; |
| } |
| |
| // Return the relocation type for a PC-relative value of MCFixupKind Kind. |
| unsigned SystemZELFObjectWriter::getPCRelReloc(SMLoc Loc, unsigned Kind) const { |
| switch (Kind) { |
| case FK_Data_2: |
| case SystemZ::FK_390_U16Imm: |
| case SystemZ::FK_390_S16Imm: |
| return ELF::R_390_PC16; |
| case FK_Data_4: |
| case SystemZ::FK_390_U32Imm: |
| case SystemZ::FK_390_S32Imm: |
| return ELF::R_390_PC32; |
| case FK_Data_8: |
| return ELF::R_390_PC64; |
| case SystemZ::FK_390_PC12DBL: |
| return ELF::R_390_PC12DBL; |
| case SystemZ::FK_390_PC16DBL: |
| return ELF::R_390_PC16DBL; |
| case SystemZ::FK_390_PC24DBL: |
| return ELF::R_390_PC24DBL; |
| case SystemZ::FK_390_PC32DBL: |
| return ELF::R_390_PC32DBL; |
| } |
| reportError(Loc, "Unsupported PC-relative address"); |
| return 0; |
| } |
| |
| unsigned SystemZELFObjectWriter::getRelocType(const MCFixup &Fixup, |
| const MCValue &Target, |
| bool IsPCRel) const { |
| SMLoc Loc = Fixup.getLoc(); |
| unsigned Kind = Fixup.getKind(); |
| auto Specifier = SystemZ::Specifier(Target.getSpecifier()); |
| switch (Specifier) { |
| case SystemZ::S_INDNTPOFF: |
| case SystemZ::S_NTPOFF: |
| case SystemZ::S_TLSGD: |
| case SystemZ::S_TLSLD: |
| case SystemZ::S_TLSLDM: |
| case SystemZ::S_DTPOFF: |
| if (auto *SA = const_cast<MCSymbol *>(Target.getAddSym())) |
| static_cast<MCSymbolELF *>(SA)->setType(ELF::STT_TLS); |
| break; |
| default: |
| break; |
| } |
| |
| switch (Specifier) { |
| case SystemZ::S_None: |
| if (IsPCRel) |
| return getPCRelReloc(Loc, Kind); |
| return getAbsoluteReloc(Loc, Kind); |
| |
| case SystemZ::S_NTPOFF: |
| assert(!IsPCRel && "NTPOFF shouldn't be PC-relative"); |
| switch (Kind) { |
| case FK_Data_4: |
| return ELF::R_390_TLS_LE32; |
| case FK_Data_8: |
| return ELF::R_390_TLS_LE64; |
| } |
| reportError(Loc, "Unsupported thread-local address (local-exec)"); |
| return 0; |
| |
| case SystemZ::S_INDNTPOFF: |
| if (IsPCRel && Kind == SystemZ::FK_390_PC32DBL) |
| return ELF::R_390_TLS_IEENT; |
| reportError(Loc, |
| "Only PC-relative INDNTPOFF accesses are supported for now"); |
| return 0; |
| |
| case SystemZ::S_DTPOFF: |
| assert(!IsPCRel && "DTPOFF shouldn't be PC-relative"); |
| switch (Kind) { |
| case FK_Data_4: |
| return ELF::R_390_TLS_LDO32; |
| case FK_Data_8: |
| return ELF::R_390_TLS_LDO64; |
| } |
| reportError(Loc, "Unsupported thread-local address (local-dynamic)"); |
| return 0; |
| |
| case SystemZ::S_TLSLDM: |
| assert(!IsPCRel && "TLSLDM shouldn't be PC-relative"); |
| switch (Kind) { |
| case FK_Data_4: |
| return ELF::R_390_TLS_LDM32; |
| case FK_Data_8: |
| return ELF::R_390_TLS_LDM64; |
| case SystemZ::FK_390_TLS_CALL: |
| return ELF::R_390_TLS_LDCALL; |
| } |
| reportError(Loc, "Unsupported thread-local address (local-dynamic)"); |
| return 0; |
| |
| case SystemZ::S_TLSGD: |
| assert(!IsPCRel && "TLSGD shouldn't be PC-relative"); |
| switch (Kind) { |
| case FK_Data_4: |
| return ELF::R_390_TLS_GD32; |
| case FK_Data_8: |
| return ELF::R_390_TLS_GD64; |
| case SystemZ::FK_390_TLS_CALL: |
| return ELF::R_390_TLS_GDCALL; |
| } |
| reportError(Loc, "Unsupported thread-local address (general-dynamic)"); |
| return 0; |
| |
| case SystemZ::S_GOT: |
| case SystemZ::S_GOTENT: |
| if (IsPCRel && Kind == SystemZ::FK_390_PC32DBL) |
| return ELF::R_390_GOTENT; |
| reportError(Loc, "Only PC-relative GOT accesses are supported for now"); |
| return 0; |
| |
| case SystemZ::S_PLT: |
| assert(IsPCRel && "@PLT shouldn't be PC-relative"); |
| switch (Kind) { |
| case SystemZ::FK_390_PC12DBL: |
| return ELF::R_390_PLT12DBL; |
| case SystemZ::FK_390_PC16DBL: |
| return ELF::R_390_PLT16DBL; |
| case SystemZ::FK_390_PC24DBL: |
| return ELF::R_390_PLT24DBL; |
| case SystemZ::FK_390_PC32DBL: |
| return ELF::R_390_PLT32DBL; |
| } |
| reportError(Loc, "Unsupported PC-relative PLT address"); |
| return 0; |
| |
| default: |
| llvm_unreachable("Modifier not supported"); |
| } |
| } |
| |
| bool SystemZELFObjectWriter::needsRelocateWithSymbol(const MCValue &V, |
| unsigned Type) const { |
| switch (V.getSpecifier()) { |
| case SystemZ::S_GOT: |
| case SystemZ::S_PLT: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| std::unique_ptr<MCObjectTargetWriter> |
| llvm::createSystemZELFObjectWriter(uint8_t OSABI) { |
| return std::make_unique<SystemZELFObjectWriter>(OSABI); |
| } |