1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-25 12:12:47 +01:00
llvm-mirror/include/llvm/MC/MCELFObjectWriter.h

159 lines
5.3 KiB
C
Raw Normal View History

//===- llvm/MC/MCELFObjectWriter.h - ELF Object Writer ----------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_MC_MCELFOBJECTWRITER_H
#define LLVM_MC_MCELFOBJECTWRITER_H
#include "llvm/ADT/Triple.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCSectionELF.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdint>
#include <vector>
namespace llvm {
class MCAssembler;
class MCContext;
class MCFixup;
class MCSymbol;
class MCSymbolELF;
class MCValue;
struct ELFRelocationEntry {
uint64_t Offset; // Where is the relocation.
const MCSymbolELF *Symbol; // The symbol to relocate with.
unsigned Type; // The type of the relocation.
uint64_t Addend; // The addend to use.
[mips] Correct the ordering of HI/LO pairs in the relocation table. Summary: There seems to have been a misunderstanding as to the meaning of 'offset' in the rules laid down by our ABI. The previous code believed that 'offset' meant the offset within the section that the relocation is applied to. However, it should have meant the offset from the symbol used in the relocation expression. This patch adds two fields to ELFRelocationEntry and uses them to correct the order of relocations for MIPS. These fields contain: * The original symbol before shouldRelocateWithSymbol() is considered. This ensures that R_MIPS_GOT16 is able to correctly distinguish between local and external symbols, allowing us to tell whether %got() requires a matching %lo() or not (local symbols require one, external symbols don't). It also prevents confusing cases where the fuzzy matching rules cause things like %hi(foo)/%lo(foo+3) and %hi(bar)/%lo(bar+1) to swap their %lo()'s. * The original offset before shouldRelocateWithSymbol() is considered. The existing Addend field is always zero when the object uses in place addends (because it's already moved it to the encoding) but MIPS needs to use the original offset to ensure that the linker correctly calculates the carry-in bit for %hi() and %got(). IAS ensures that unmatchable %hi()/%got() relocations are placed at the end of the table to ensure that the linker rejects the table (we're unable to report such errors directly). The alternatives to this risk accidental matching against inappropriate relocations which may silently compute incorrect values due to an incorrect carry bit between the %lo() and %hi()/%got(). Reviewers: sdardis Subscribers: dsanders, sdardis, rafael, llvm-commits Differential Revision: http://reviews.llvm.org/D19718 llvm-svn: 268733
2016-05-06 15:49:25 +02:00
const MCSymbolELF *OriginalSymbol; // The original value of Symbol if we changed it.
uint64_t OriginalAddend; // The original value of addend.
ELFRelocationEntry(uint64_t Offset, const MCSymbolELF *Symbol, unsigned Type,
[mips] Correct the ordering of HI/LO pairs in the relocation table. Summary: There seems to have been a misunderstanding as to the meaning of 'offset' in the rules laid down by our ABI. The previous code believed that 'offset' meant the offset within the section that the relocation is applied to. However, it should have meant the offset from the symbol used in the relocation expression. This patch adds two fields to ELFRelocationEntry and uses them to correct the order of relocations for MIPS. These fields contain: * The original symbol before shouldRelocateWithSymbol() is considered. This ensures that R_MIPS_GOT16 is able to correctly distinguish between local and external symbols, allowing us to tell whether %got() requires a matching %lo() or not (local symbols require one, external symbols don't). It also prevents confusing cases where the fuzzy matching rules cause things like %hi(foo)/%lo(foo+3) and %hi(bar)/%lo(bar+1) to swap their %lo()'s. * The original offset before shouldRelocateWithSymbol() is considered. The existing Addend field is always zero when the object uses in place addends (because it's already moved it to the encoding) but MIPS needs to use the original offset to ensure that the linker correctly calculates the carry-in bit for %hi() and %got(). IAS ensures that unmatchable %hi()/%got() relocations are placed at the end of the table to ensure that the linker rejects the table (we're unable to report such errors directly). The alternatives to this risk accidental matching against inappropriate relocations which may silently compute incorrect values due to an incorrect carry bit between the %lo() and %hi()/%got(). Reviewers: sdardis Subscribers: dsanders, sdardis, rafael, llvm-commits Differential Revision: http://reviews.llvm.org/D19718 llvm-svn: 268733
2016-05-06 15:49:25 +02:00
uint64_t Addend, const MCSymbolELF *OriginalSymbol,
uint64_t OriginalAddend)
: Offset(Offset), Symbol(Symbol), Type(Type), Addend(Addend),
OriginalSymbol(OriginalSymbol), OriginalAddend(OriginalAddend) {}
void print(raw_ostream &Out) const {
Out << "Off=" << Offset << ", Sym=" << Symbol << ", Type=" << Type
<< ", Addend=" << Addend << ", OriginalSymbol=" << OriginalSymbol
<< ", OriginalAddend=" << OriginalAddend;
}
LLVM_DUMP_METHOD void dump() const { print(errs()); }
};
class MCELFObjectTargetWriter : public MCObjectTargetWriter {
const uint8_t OSABI;
const uint8_t ABIVersion;
const uint16_t EMachine;
const unsigned HasRelocationAddend : 1;
const unsigned Is64Bit : 1;
protected:
MCELFObjectTargetWriter(bool Is64Bit_, uint8_t OSABI_, uint16_t EMachine_,
bool HasRelocationAddend_, uint8_t ABIVersion_ = 0);
public:
virtual ~MCELFObjectTargetWriter() = default;
Triple::ObjectFormatType getFormat() const override { return Triple::ELF; }
static bool classof(const MCObjectTargetWriter *W) {
return W->getFormat() == Triple::ELF;
}
static uint8_t getOSABI(Triple::OSType OSType) {
switch (OSType) {
case Triple::CloudABI:
return ELF::ELFOSABI_CLOUDABI;
case Triple::HermitCore:
return ELF::ELFOSABI_STANDALONE;
case Triple::PS4:
case Triple::FreeBSD:
return ELF::ELFOSABI_FREEBSD;
default:
return ELF::ELFOSABI_NONE;
}
}
virtual unsigned getRelocType(MCContext &Ctx, const MCValue &Target,
const MCFixup &Fixup, bool IsPCRel) const = 0;
virtual bool needsRelocateWithSymbol(const MCSymbol &Sym,
unsigned Type) const;
virtual void sortRelocs(const MCAssembler &Asm,
std::vector<ELFRelocationEntry> &Relocs);
virtual void addTargetSectionFlags(MCContext &Ctx, MCSectionELF &Sec);
/// \name Accessors
/// @{
uint8_t getOSABI() const { return OSABI; }
uint8_t getABIVersion() const { return ABIVersion; }
uint16_t getEMachine() const { return EMachine; }
bool hasRelocationAddend() const { return HasRelocationAddend; }
2011-12-21 15:48:04 +01:00
bool is64Bit() const { return 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 00:28:30 +02:00
// Instead of changing everyone's API we pack the N64 Type fields
// into the existing 32 bit data unsigned.
#define R_TYPE_SHIFT 0
#define R_TYPE_MASK 0xffffff00
#define R_TYPE2_SHIFT 8
#define R_TYPE2_MASK 0xffff00ff
#define R_TYPE3_SHIFT 16
#define R_TYPE3_MASK 0xff00ffff
#define R_SSYM_SHIFT 24
#define R_SSYM_MASK 0x00ffffff
// N64 relocation type accessors
uint8_t getRType(uint32_t Type) const {
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 00:28:30 +02:00
return (unsigned)((Type >> R_TYPE_SHIFT) & 0xff);
}
uint8_t getRType2(uint32_t Type) const {
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 00:28:30 +02:00
return (unsigned)((Type >> R_TYPE2_SHIFT) & 0xff);
}
uint8_t getRType3(uint32_t Type) const {
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 00:28:30 +02:00
return (unsigned)((Type >> R_TYPE3_SHIFT) & 0xff);
}
uint8_t getRSsym(uint32_t Type) const {
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 00:28:30 +02:00
return (unsigned)((Type >> R_SSYM_SHIFT) & 0xff);
}
// N64 relocation type setting
static unsigned setRTypes(unsigned Value1, unsigned Value2, unsigned Value3) {
return ((Value1 & 0xff) << R_TYPE_SHIFT) |
((Value2 & 0xff) << R_TYPE2_SHIFT) |
((Value3 & 0xff) << R_TYPE3_SHIFT);
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 00:28:30 +02:00
}
unsigned setRSsym(unsigned Value, unsigned Type) const {
return (Type & R_SSYM_MASK) | ((Value & 0xff) << R_SSYM_SHIFT);
}
};
/// Construct a new ELF writer instance.
///
/// \param MOTW - The target specific ELF writer subclass.
/// \param OS - The stream to write to.
/// \returns The constructed object writer.
std::unique_ptr<MCObjectWriter>
createELFObjectWriter(std::unique_ptr<MCELFObjectTargetWriter> MOTW,
raw_pwrite_stream &OS, bool IsLittleEndian);
std::unique_ptr<MCObjectWriter>
createELFDwoObjectWriter(std::unique_ptr<MCELFObjectTargetWriter> MOTW,
raw_pwrite_stream &OS, raw_pwrite_stream &DwoOS,
bool IsLittleEndian);
} // end namespace llvm
#endif // LLVM_MC_MCELFOBJECTWRITER_H