llvm-project/llvm/lib/Target/Mips/MCTargetDesc/MipsELFObjectWriter.cpp

328 lines
11 KiB
C++
Raw Normal View History

//===-- MipsELFObjectWriter.cpp - Mips 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/MipsBaseInfo.h"
#include "MCTargetDesc/MipsFixupKinds.h"
#include "MCTargetDesc/MipsMCTargetDesc.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCELFObjectWriter.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCSection.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/ErrorHandling.h"
#include <list>
using namespace llvm;
namespace {
struct RelEntry {
RelEntry(const ELFRelocationEntry &R, const MCSymbol *S, int64_t O) :
Reloc(R), Sym(S), Offset(O) {}
ELFRelocationEntry Reloc;
const MCSymbol *Sym;
int64_t Offset;
};
typedef std::list<RelEntry> RelLs;
typedef RelLs::iterator RelLsIter;
class MipsELFObjectWriter : public MCELFObjectTargetWriter {
public:
MipsELFObjectWriter(bool _is64Bit, uint8_t OSABI,
bool _isN64, bool IsLittleEndian);
virtual ~MipsELFObjectWriter();
unsigned GetRelocType(const MCValue &Target, const MCFixup &Fixup,
bool IsPCRel) const override;
virtual const MCSymbol *ExplicitRelSym(const MCAssembler &Asm,
const MCValue &Target,
const MCFragment &F,
const MCFixup &Fixup,
bool IsPCRel) const;
virtual void sortRelocs(const MCAssembler &Asm,
std::vector<ELFRelocationEntry> &Relocs);
};
}
The ELF relocation record format is different for N64 which many Mips 64 ABIs use than for O64 which many if not all other target ABIs use. Most architectures have the following 64 bit relocation record format: typedef struct { Elf64_Addr r_offset; /* Address of reference */ Elf64_Xword r_info; /* Symbol index and type of relocation */ } Elf64_Rel; typedef struct { Elf64_Addr r_offset; Elf64_Xword r_info; Elf64_Sxword r_addend; } Elf64_Rela; Whereas N64 has the following format: typedef struct { Elf64_Addr r_offset;/* Address of reference */ Elf64_Word r_sym; /* Symbol index */ Elf64_Byte r_ssym; /* Special symbol */ Elf64_Byte r_type3; /* Relocation type */ Elf64_Byte r_type2; /* Relocation type */ Elf64_Byte r_type; /* Relocation type */ } Elf64_Rel; typedef struct { Elf64_Addr r_offset;/* Address of reference */ Elf64_Word r_sym; /* Symbol index */ Elf64_Byte r_ssym; /* Special symbol */ Elf64_Byte r_type3; /* Relocation type */ Elf64_Byte r_type2; /* Relocation type */ Elf64_Byte r_type; /* Relocation type */ Elf64_Sxword r_addend; } Elf64_Rela; The structure is the same size, but the r_info data element is now 5 separate elements. Besides the content aspects, endian byte reordering will be different for the area with each element being endianized separately. I treat this as generic and continue to pass r_type as an integer masking and unmasking the byte sized N64 values for N64 mode. I've implemented this and it causes no affect on other current targets. This passes make check. Jack llvm-svn: 159299
2012-06-28 06:28:30 +08:00
MipsELFObjectWriter::MipsELFObjectWriter(bool _is64Bit, uint8_t OSABI,
bool _isN64, bool IsLittleEndian)
: MCELFObjectTargetWriter(_is64Bit, OSABI, ELF::EM_MIPS,
/*HasRelocationAddend*/ (_isN64) ? true : false,
The ELF relocation record format is different for N64 which many Mips 64 ABIs use than for O64 which many if not all other target ABIs use. Most architectures have the following 64 bit relocation record format: typedef struct { Elf64_Addr r_offset; /* Address of reference */ Elf64_Xword r_info; /* Symbol index and type of relocation */ } Elf64_Rel; typedef struct { Elf64_Addr r_offset; Elf64_Xword r_info; Elf64_Sxword r_addend; } Elf64_Rela; Whereas N64 has the following format: typedef struct { Elf64_Addr r_offset;/* Address of reference */ Elf64_Word r_sym; /* Symbol index */ Elf64_Byte r_ssym; /* Special symbol */ Elf64_Byte r_type3; /* Relocation type */ Elf64_Byte r_type2; /* Relocation type */ Elf64_Byte r_type; /* Relocation type */ } Elf64_Rel; typedef struct { Elf64_Addr r_offset;/* Address of reference */ Elf64_Word r_sym; /* Symbol index */ Elf64_Byte r_ssym; /* Special symbol */ Elf64_Byte r_type3; /* Relocation type */ Elf64_Byte r_type2; /* Relocation type */ Elf64_Byte r_type; /* Relocation type */ Elf64_Sxword r_addend; } Elf64_Rela; The structure is the same size, but the r_info data element is now 5 separate elements. Besides the content aspects, endian byte reordering will be different for the area with each element being endianized separately. I treat this as generic and continue to pass r_type as an integer masking and unmasking the byte sized N64 values for N64 mode. I've implemented this and it causes no affect on other current targets. This passes make check. Jack llvm-svn: 159299
2012-06-28 06:28:30 +08:00
/*IsN64*/ _isN64) {}
MipsELFObjectWriter::~MipsELFObjectWriter() {}
const MCSymbol *MipsELFObjectWriter::ExplicitRelSym(const MCAssembler &Asm,
const MCValue &Target,
const MCFragment &F,
const MCFixup &Fixup,
bool IsPCRel) const {
assert(Target.getSymA() && "SymA cannot be 0.");
const MCSymbol &Sym = Target.getSymA()->getSymbol().AliasedSymbol();
if (Sym.getSection().getKind().isMergeableCString() ||
Sym.getSection().getKind().isMergeableConst())
return &Sym;
return NULL;
}
unsigned MipsELFObjectWriter::GetRelocType(const MCValue &Target,
const MCFixup &Fixup,
bool IsPCRel) const {
// determine the type of the relocation
unsigned Type = (unsigned)ELF::R_MIPS_NONE;
unsigned Kind = (unsigned)Fixup.getKind();
switch (Kind) {
default:
llvm_unreachable("invalid fixup kind!");
case FK_Data_4:
Type = ELF::R_MIPS_32;
break;
case FK_Data_8:
Type = ELF::R_MIPS_64;
break;
case FK_GPRel_4:
if (isN64()) {
Type = setRType((unsigned)ELF::R_MIPS_GPREL32, Type);
Type = setRType2((unsigned)ELF::R_MIPS_64, Type);
Type = setRType3((unsigned)ELF::R_MIPS_NONE, Type);
}
else
Type = ELF::R_MIPS_GPREL32;
break;
case Mips::fixup_Mips_GPREL16:
Type = ELF::R_MIPS_GPREL16;
break;
case Mips::fixup_Mips_26:
Type = ELF::R_MIPS_26;
break;
case Mips::fixup_Mips_CALL16:
Type = ELF::R_MIPS_CALL16;
break;
case Mips::fixup_Mips_GOT_Global:
case Mips::fixup_Mips_GOT_Local:
Type = ELF::R_MIPS_GOT16;
break;
case Mips::fixup_Mips_HI16:
Type = ELF::R_MIPS_HI16;
break;
case Mips::fixup_Mips_LO16:
Type = ELF::R_MIPS_LO16;
break;
case Mips::fixup_Mips_TLSGD:
Type = ELF::R_MIPS_TLS_GD;
break;
case Mips::fixup_Mips_GOTTPREL:
Type = ELF::R_MIPS_TLS_GOTTPREL;
break;
case Mips::fixup_Mips_TPREL_HI:
Type = ELF::R_MIPS_TLS_TPREL_HI16;
break;
case Mips::fixup_Mips_TPREL_LO:
Type = ELF::R_MIPS_TLS_TPREL_LO16;
break;
case Mips::fixup_Mips_TLSLDM:
Type = ELF::R_MIPS_TLS_LDM;
break;
case Mips::fixup_Mips_DTPREL_HI:
Type = ELF::R_MIPS_TLS_DTPREL_HI16;
break;
case Mips::fixup_Mips_DTPREL_LO:
Type = ELF::R_MIPS_TLS_DTPREL_LO16;
break;
case Mips::fixup_Mips_Branch_PCRel:
case Mips::fixup_Mips_PC16:
Type = ELF::R_MIPS_PC16;
break;
case Mips::fixup_Mips_GOT_PAGE:
Type = ELF::R_MIPS_GOT_PAGE;
break;
case Mips::fixup_Mips_GOT_OFST:
Type = ELF::R_MIPS_GOT_OFST;
break;
case Mips::fixup_Mips_GOT_DISP:
Type = ELF::R_MIPS_GOT_DISP;
break;
case Mips::fixup_Mips_GPOFF_HI:
Type = setRType((unsigned)ELF::R_MIPS_GPREL16, Type);
Type = setRType2((unsigned)ELF::R_MIPS_SUB, Type);
Type = setRType3((unsigned)ELF::R_MIPS_HI16, Type);
break;
case Mips::fixup_Mips_GPOFF_LO:
Type = setRType((unsigned)ELF::R_MIPS_GPREL16, Type);
Type = setRType2((unsigned)ELF::R_MIPS_SUB, Type);
Type = setRType3((unsigned)ELF::R_MIPS_LO16, Type);
break;
case Mips::fixup_Mips_HIGHER:
Type = ELF::R_MIPS_HIGHER;
break;
case Mips::fixup_Mips_HIGHEST:
Type = ELF::R_MIPS_HIGHEST;
break;
case Mips::fixup_Mips_GOT_HI16:
Type = ELF::R_MIPS_GOT_HI16;
break;
case Mips::fixup_Mips_GOT_LO16:
Type = ELF::R_MIPS_GOT_LO16;
break;
case Mips::fixup_Mips_CALL_HI16:
Type = ELF::R_MIPS_CALL_HI16;
break;
case Mips::fixup_Mips_CALL_LO16:
Type = ELF::R_MIPS_CALL_LO16;
break;
case Mips::fixup_MICROMIPS_26_S1:
Type = ELF::R_MICROMIPS_26_S1;
break;
case Mips::fixup_MICROMIPS_HI16:
Type = ELF::R_MICROMIPS_HI16;
break;
case Mips::fixup_MICROMIPS_LO16:
Type = ELF::R_MICROMIPS_LO16;
break;
case Mips::fixup_MICROMIPS_GOT16:
Type = ELF::R_MICROMIPS_GOT16;
break;
case Mips::fixup_MICROMIPS_PC16_S1:
Type = ELF::R_MICROMIPS_PC16_S1;
break;
case Mips::fixup_MICROMIPS_CALL16:
Type = ELF::R_MICROMIPS_CALL16;
break;
case Mips::fixup_MICROMIPS_GOT_DISP:
Type = ELF::R_MICROMIPS_GOT_DISP;
break;
case Mips::fixup_MICROMIPS_GOT_PAGE:
Type = ELF::R_MICROMIPS_GOT_PAGE;
break;
case Mips::fixup_MICROMIPS_GOT_OFST:
Type = ELF::R_MICROMIPS_GOT_OFST;
break;
case Mips::fixup_MICROMIPS_TLS_GD:
Type = ELF::R_MICROMIPS_TLS_GD;
break;
case Mips::fixup_MICROMIPS_TLS_LDM:
Type = ELF::R_MICROMIPS_TLS_LDM;
break;
case Mips::fixup_MICROMIPS_TLS_DTPREL_HI16:
Type = ELF::R_MICROMIPS_TLS_DTPREL_HI16;
break;
case Mips::fixup_MICROMIPS_TLS_DTPREL_LO16:
Type = ELF::R_MICROMIPS_TLS_DTPREL_LO16;
break;
case Mips::fixup_MICROMIPS_TLS_TPREL_HI16:
Type = ELF::R_MICROMIPS_TLS_TPREL_HI16;
break;
case Mips::fixup_MICROMIPS_TLS_TPREL_LO16:
Type = ELF::R_MICROMIPS_TLS_TPREL_LO16;
break;
}
return Type;
}
// Return true if R is either a GOT16 against a local symbol or HI16.
static bool NeedsMatchingLo(const MCAssembler &Asm, const RelEntry &R) {
if (!R.Sym)
return false;
MCSymbolData &SD = Asm.getSymbolData(R.Sym->AliasedSymbol());
return ((R.Reloc.Type == ELF::R_MIPS_GOT16) && !SD.isExternal()) ||
(R.Reloc.Type == ELF::R_MIPS_HI16);
}
static bool HasMatchingLo(const MCAssembler &Asm, RelLsIter I, RelLsIter Last) {
if (I == Last)
return false;
RelLsIter Hi = I++;
return (I->Reloc.Type == ELF::R_MIPS_LO16) && (Hi->Sym == I->Sym) &&
(Hi->Offset == I->Offset);
}
static bool HasSameSymbol(const RelEntry &R0, const RelEntry &R1) {
return R0.Sym == R1.Sym;
}
static int CompareOffset(const RelEntry &R0, const RelEntry &R1) {
return (R0.Offset > R1.Offset) ? 1 : ((R0.Offset == R1.Offset) ? 0 : -1);
}
void MipsELFObjectWriter::sortRelocs(const MCAssembler &Asm,
std::vector<ELFRelocationEntry> &Relocs) {
The ELF relocation record format is different for N64 which many Mips 64 ABIs use than for O64 which many if not all other target ABIs use. Most architectures have the following 64 bit relocation record format: typedef struct { Elf64_Addr r_offset; /* Address of reference */ Elf64_Xword r_info; /* Symbol index and type of relocation */ } Elf64_Rel; typedef struct { Elf64_Addr r_offset; Elf64_Xword r_info; Elf64_Sxword r_addend; } Elf64_Rela; Whereas N64 has the following format: typedef struct { Elf64_Addr r_offset;/* Address of reference */ Elf64_Word r_sym; /* Symbol index */ Elf64_Byte r_ssym; /* Special symbol */ Elf64_Byte r_type3; /* Relocation type */ Elf64_Byte r_type2; /* Relocation type */ Elf64_Byte r_type; /* Relocation type */ } Elf64_Rel; typedef struct { Elf64_Addr r_offset;/* Address of reference */ Elf64_Word r_sym; /* Symbol index */ Elf64_Byte r_ssym; /* Special symbol */ Elf64_Byte r_type3; /* Relocation type */ Elf64_Byte r_type2; /* Relocation type */ Elf64_Byte r_type; /* Relocation type */ Elf64_Sxword r_addend; } Elf64_Rela; The structure is the same size, but the r_info data element is now 5 separate elements. Besides the content aspects, endian byte reordering will be different for the area with each element being endianized separately. I treat this as generic and continue to pass r_type as an integer masking and unmasking the byte sized N64 values for N64 mode. I've implemented this and it causes no affect on other current targets. This passes make check. Jack llvm-svn: 159299
2012-06-28 06:28:30 +08:00
// Call the default function first. Relocations are sorted in descending
// order of r_offset.
MCELFObjectTargetWriter::sortRelocs(Asm, Relocs);
RelLs RelocLs;
std::vector<RelLsIter> Unmatched;
// Fill RelocLs. Traverse Relocs backwards so that relocations in RelocLs
// are in ascending order of r_offset.
for (std::vector<ELFRelocationEntry>::reverse_iterator R = Relocs.rbegin();
R != Relocs.rend(); ++R) {
std::pair<const MCSymbolRefExpr*, int64_t> P =
MipsGetSymAndOffset(*R->Fixup);
RelocLs.push_back(RelEntry(*R, P.first ? &P.first->getSymbol() : 0,
P.second));
}
// Get list of unmatched HI16 and GOT16.
for (RelLsIter R = RelocLs.begin(); R != RelocLs.end(); ++R)
if (NeedsMatchingLo(Asm, *R) && !HasMatchingLo(Asm, R, --RelocLs.end()))
Unmatched.push_back(R);
// Insert unmatched HI16 and GOT16 immediately before their matching LO16.
for (std::vector<RelLsIter>::iterator U = Unmatched.begin();
U != Unmatched.end(); ++U) {
RelLsIter LoPos = RelocLs.end(), HiPos = *U;
bool MatchedLo = false;
for (RelLsIter R = RelocLs.begin(); R != RelocLs.end(); ++R) {
if ((R->Reloc.Type == ELF::R_MIPS_LO16) && HasSameSymbol(*HiPos, *R) &&
(CompareOffset(*R, *HiPos) >= 0) &&
((LoPos == RelocLs.end()) || ((CompareOffset(*R, *LoPos) < 0)) ||
(!MatchedLo && !CompareOffset(*R, *LoPos))))
LoPos = R;
MatchedLo = NeedsMatchingLo(Asm, *R) &&
HasMatchingLo(Asm, R, --RelocLs.end());
}
// If a matching LoPos was found, move HiPos and insert it before LoPos.
// Make the offsets of HiPos and LoPos match.
if (LoPos != RelocLs.end()) {
HiPos->Offset = LoPos->Offset;
RelocLs.insert(LoPos, *HiPos);
RelocLs.erase(HiPos);
}
}
// Put the sorted list back in reverse order.
assert(Relocs.size() == RelocLs.size());
unsigned I = RelocLs.size();
for (RelLsIter R = RelocLs.begin(); R != RelocLs.end(); ++R)
Relocs[--I] = R->Reloc;
}
MCObjectWriter *llvm::createMipsELFObjectWriter(raw_ostream &OS,
uint8_t OSABI,
bool IsLittleEndian,
bool Is64Bit) {
The ELF relocation record format is different for N64 which many Mips 64 ABIs use than for O64 which many if not all other target ABIs use. Most architectures have the following 64 bit relocation record format: typedef struct { Elf64_Addr r_offset; /* Address of reference */ Elf64_Xword r_info; /* Symbol index and type of relocation */ } Elf64_Rel; typedef struct { Elf64_Addr r_offset; Elf64_Xword r_info; Elf64_Sxword r_addend; } Elf64_Rela; Whereas N64 has the following format: typedef struct { Elf64_Addr r_offset;/* Address of reference */ Elf64_Word r_sym; /* Symbol index */ Elf64_Byte r_ssym; /* Special symbol */ Elf64_Byte r_type3; /* Relocation type */ Elf64_Byte r_type2; /* Relocation type */ Elf64_Byte r_type; /* Relocation type */ } Elf64_Rel; typedef struct { Elf64_Addr r_offset;/* Address of reference */ Elf64_Word r_sym; /* Symbol index */ Elf64_Byte r_ssym; /* Special symbol */ Elf64_Byte r_type3; /* Relocation type */ Elf64_Byte r_type2; /* Relocation type */ Elf64_Byte r_type; /* Relocation type */ Elf64_Sxword r_addend; } Elf64_Rela; The structure is the same size, but the r_info data element is now 5 separate elements. Besides the content aspects, endian byte reordering will be different for the area with each element being endianized separately. I treat this as generic and continue to pass r_type as an integer masking and unmasking the byte sized N64 values for N64 mode. I've implemented this and it causes no affect on other current targets. This passes make check. Jack llvm-svn: 159299
2012-06-28 06:28:30 +08:00
MCELFObjectTargetWriter *MOTW = new MipsELFObjectWriter(Is64Bit, OSABI,
(Is64Bit) ? true : false,
IsLittleEndian);
return createELFObjectWriter(MOTW, OS, IsLittleEndian);
}