The SystemZ linkers provide an optimization to transform a general- or local-dynamic TLS sequence into an initial-exec sequence if possible. Do do that, the compiler generates a function call to __tls_get_offset, which is a brasl instruction annotated with *two* relocations: - a R_390_PLT32DBL to install __tls_get_offset as branch target - a R_390_TLS_GDCALL / R_390_TLS_LDCALL to inform the linker that the TLS optimization should be performed if possible If the optimization is performed, the brasl is replaced by an ld load instruction. However, *both* relocs are processed independently by the linker. Therefore it is crucial that the R_390_PLT32DBL is processed *first* (installing the branch target for the brasl) and the R_390_TLS_GDCALL is processed *second* (replacing the whole brasl with an ld). If the relocs are swapped, the linker will first replace the brasl with an ld, and *then* install the __tls_get_offset branch target offset. Since ld has a different layout than brasl, this may even result in a completely different (or invalid) instruction; in any case, the resulting code is corrupted. Unfortunately, the way the MC common code sorts relocations causes these two to *always* end up the wrong way around, resulting in wrong code generation by the linker and crashes. This patch overrides the sortRelocs routine to detect this particular pair of relocs and enforce the required order. llvm-svn: 255787
179 lines
6.2 KiB
C++
179 lines
6.2 KiB
C++
//===-- SystemZMCObjectWriter.cpp - SystemZ ELF writer --------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "MCTargetDesc/SystemZMCTargetDesc.h"
|
|
#include "MCTargetDesc/SystemZMCFixups.h"
|
|
#include "llvm/MC/MCELFObjectWriter.h"
|
|
#include "llvm/MC/MCExpr.h"
|
|
#include "llvm/MC/MCValue.h"
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
class SystemZObjectWriter : public MCELFObjectTargetWriter {
|
|
public:
|
|
SystemZObjectWriter(uint8_t OSABI);
|
|
|
|
~SystemZObjectWriter() override;
|
|
|
|
protected:
|
|
// Override MCELFObjectTargetWriter.
|
|
unsigned GetRelocType(const MCValue &Target, const MCFixup &Fixup,
|
|
bool IsPCRel) const override;
|
|
void sortRelocs(const MCAssembler &Asm,
|
|
std::vector<ELFRelocationEntry> &Relocs) override;
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
SystemZObjectWriter::SystemZObjectWriter(uint8_t OSABI)
|
|
: MCELFObjectTargetWriter(/*Is64Bit=*/true, OSABI, ELF::EM_S390,
|
|
/*HasRelocationAddend=*/ true) {}
|
|
|
|
SystemZObjectWriter::~SystemZObjectWriter() {
|
|
}
|
|
|
|
// Return the relocation type for an absolute value of MCFixupKind Kind.
|
|
static unsigned getAbsoluteReloc(unsigned Kind) {
|
|
switch (Kind) {
|
|
case FK_Data_1: return ELF::R_390_8;
|
|
case FK_Data_2: return ELF::R_390_16;
|
|
case FK_Data_4: return ELF::R_390_32;
|
|
case FK_Data_8: return ELF::R_390_64;
|
|
}
|
|
llvm_unreachable("Unsupported absolute address");
|
|
}
|
|
|
|
// Return the relocation type for a PC-relative value of MCFixupKind Kind.
|
|
static unsigned getPCRelReloc(unsigned Kind) {
|
|
switch (Kind) {
|
|
case FK_Data_2: return ELF::R_390_PC16;
|
|
case FK_Data_4: return ELF::R_390_PC32;
|
|
case FK_Data_8: return ELF::R_390_PC64;
|
|
case SystemZ::FK_390_PC16DBL: return ELF::R_390_PC16DBL;
|
|
case SystemZ::FK_390_PC32DBL: return ELF::R_390_PC32DBL;
|
|
}
|
|
llvm_unreachable("Unsupported PC-relative address");
|
|
}
|
|
|
|
// Return the R_390_TLS_LE* relocation type for MCFixupKind Kind.
|
|
static unsigned getTLSLEReloc(unsigned Kind) {
|
|
switch (Kind) {
|
|
case FK_Data_4: return ELF::R_390_TLS_LE32;
|
|
case FK_Data_8: return ELF::R_390_TLS_LE64;
|
|
}
|
|
llvm_unreachable("Unsupported absolute address");
|
|
}
|
|
|
|
// Return the R_390_TLS_LDO* relocation type for MCFixupKind Kind.
|
|
static unsigned getTLSLDOReloc(unsigned Kind) {
|
|
switch (Kind) {
|
|
case FK_Data_4: return ELF::R_390_TLS_LDO32;
|
|
case FK_Data_8: return ELF::R_390_TLS_LDO64;
|
|
}
|
|
llvm_unreachable("Unsupported absolute address");
|
|
}
|
|
|
|
// Return the R_390_TLS_LDM* relocation type for MCFixupKind Kind.
|
|
static unsigned getTLSLDMReloc(unsigned Kind) {
|
|
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;
|
|
}
|
|
llvm_unreachable("Unsupported absolute address");
|
|
}
|
|
|
|
// Return the R_390_TLS_GD* relocation type for MCFixupKind Kind.
|
|
static unsigned getTLSGDReloc(unsigned Kind) {
|
|
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;
|
|
}
|
|
llvm_unreachable("Unsupported absolute address");
|
|
}
|
|
|
|
// Return the PLT relocation counterpart of MCFixupKind Kind.
|
|
static unsigned getPLTReloc(unsigned Kind) {
|
|
switch (Kind) {
|
|
case SystemZ::FK_390_PC16DBL: return ELF::R_390_PLT16DBL;
|
|
case SystemZ::FK_390_PC32DBL: return ELF::R_390_PLT32DBL;
|
|
}
|
|
llvm_unreachable("Unsupported absolute address");
|
|
}
|
|
|
|
unsigned SystemZObjectWriter::GetRelocType(const MCValue &Target,
|
|
const MCFixup &Fixup,
|
|
bool IsPCRel) const {
|
|
MCSymbolRefExpr::VariantKind Modifier = Target.getAccessVariant();
|
|
unsigned Kind = Fixup.getKind();
|
|
switch (Modifier) {
|
|
case MCSymbolRefExpr::VK_None:
|
|
if (IsPCRel)
|
|
return getPCRelReloc(Kind);
|
|
return getAbsoluteReloc(Kind);
|
|
|
|
case MCSymbolRefExpr::VK_NTPOFF:
|
|
assert(!IsPCRel && "NTPOFF shouldn't be PC-relative");
|
|
return getTLSLEReloc(Kind);
|
|
|
|
case MCSymbolRefExpr::VK_INDNTPOFF:
|
|
if (IsPCRel && Kind == SystemZ::FK_390_PC32DBL)
|
|
return ELF::R_390_TLS_IEENT;
|
|
llvm_unreachable("Only PC-relative INDNTPOFF accesses are supported for now");
|
|
|
|
case MCSymbolRefExpr::VK_DTPOFF:
|
|
assert(!IsPCRel && "DTPOFF shouldn't be PC-relative");
|
|
return getTLSLDOReloc(Kind);
|
|
|
|
case MCSymbolRefExpr::VK_TLSLDM:
|
|
assert(!IsPCRel && "TLSLDM shouldn't be PC-relative");
|
|
return getTLSLDMReloc(Kind);
|
|
|
|
case MCSymbolRefExpr::VK_TLSGD:
|
|
assert(!IsPCRel && "TLSGD shouldn't be PC-relative");
|
|
return getTLSGDReloc(Kind);
|
|
|
|
case MCSymbolRefExpr::VK_GOT:
|
|
if (IsPCRel && Kind == SystemZ::FK_390_PC32DBL)
|
|
return ELF::R_390_GOTENT;
|
|
llvm_unreachable("Only PC-relative GOT accesses are supported for now");
|
|
|
|
case MCSymbolRefExpr::VK_PLT:
|
|
assert(IsPCRel && "@PLT shouldt be PC-relative");
|
|
return getPLTReloc(Kind);
|
|
|
|
default:
|
|
llvm_unreachable("Modifier not supported");
|
|
}
|
|
}
|
|
|
|
void SystemZObjectWriter::sortRelocs(const MCAssembler &Asm,
|
|
std::vector<ELFRelocationEntry> &Relocs) {
|
|
// The default function sorts entries by Offset in descending order.
|
|
MCELFObjectTargetWriter::sortRelocs(Asm, Relocs);
|
|
|
|
// This is OK for SystemZ, except for R_390_TLS_GDCALL/LDCALL relocs.
|
|
// There is typically another reloc, a R_390_PLT32DBL, on the same
|
|
// instruction. This other reloc must come *before* the GDCALL reloc,
|
|
// or else the TLS linker optimization may generate incorrect code.
|
|
for (unsigned i = 0, e = Relocs.size(); i + 1 < e; ++i) {
|
|
if ((Relocs[i + 1].Type == ELF::R_390_TLS_GDCALL ||
|
|
Relocs[i + 1].Type == ELF::R_390_TLS_LDCALL) &&
|
|
Relocs[i].Offset == Relocs[i + 1].Offset + 2)
|
|
std::swap(Relocs[i], Relocs[i + 1]);
|
|
}
|
|
}
|
|
|
|
MCObjectWriter *llvm::createSystemZObjectWriter(raw_pwrite_stream &OS,
|
|
uint8_t OSABI) {
|
|
MCELFObjectTargetWriter *MOTW = new SystemZObjectWriter(OSABI);
|
|
return createELFObjectWriter(MOTW, OS, /*IsLittleEndian=*/false);
|
|
}
|