mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-08-23 02:29:18 +00:00
The values for the relocation type can (and do) overlap across various architectures. When performing an adjustment of the emitted relocation in the final object file, check that the file magic matches the target for which the relocation type is valid (e.g. a I386 relocation is only applied to an X86 object file, and an AMD64 relocation is only applied to an X86_64 object file). This was noticed while adding support for ARM WinCOFF object file emission. A test case for this is not really possible as the values for REL32 do not overlap on I386 and AMD64, which is why this was never noticed in practice. The ARM WinCOFF emission is not yet ready to merge into the tree. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@206138 91177308-0d34-0410-b5e6-96231b3b80d8
978 lines
33 KiB
C++
978 lines
33 KiB
C++
//===-- llvm/MC/WinCOFFObjectWriter.cpp -------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file contains an implementation of a Win32 COFF object file writer.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define DEBUG_TYPE "WinCOFFObjectWriter"
|
|
|
|
#include "llvm/MC/MCWinCOFFObjectWriter.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/MC/MCAsmLayout.h"
|
|
#include "llvm/MC/MCAssembler.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCExpr.h"
|
|
#include "llvm/MC/MCObjectWriter.h"
|
|
#include "llvm/MC/MCSection.h"
|
|
#include "llvm/MC/MCSectionCOFF.h"
|
|
#include "llvm/MC/MCSymbol.h"
|
|
#include "llvm/MC/MCValue.h"
|
|
#include "llvm/Support/COFF.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/TimeValue.h"
|
|
#include <cstdio>
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
typedef SmallString<COFF::NameSize> name;
|
|
|
|
enum AuxiliaryType {
|
|
ATFunctionDefinition,
|
|
ATbfAndefSymbol,
|
|
ATWeakExternal,
|
|
ATFile,
|
|
ATSectionDefinition
|
|
};
|
|
|
|
struct AuxSymbol {
|
|
AuxiliaryType AuxType;
|
|
COFF::Auxiliary Aux;
|
|
};
|
|
|
|
class COFFSymbol;
|
|
class COFFSection;
|
|
|
|
class COFFSymbol {
|
|
public:
|
|
COFF::symbol Data;
|
|
|
|
typedef SmallVector<AuxSymbol, 1> AuxiliarySymbols;
|
|
|
|
name Name;
|
|
int Index;
|
|
AuxiliarySymbols Aux;
|
|
COFFSymbol *Other;
|
|
COFFSection *Section;
|
|
int Relocations;
|
|
|
|
MCSymbolData const *MCData;
|
|
|
|
COFFSymbol(StringRef name);
|
|
size_t size() const;
|
|
void set_name_offset(uint32_t Offset);
|
|
|
|
bool should_keep() const;
|
|
};
|
|
|
|
// This class contains staging data for a COFF relocation entry.
|
|
struct COFFRelocation {
|
|
COFF::relocation Data;
|
|
COFFSymbol *Symb;
|
|
|
|
COFFRelocation() : Symb(nullptr) {}
|
|
static size_t size() { return COFF::RelocationSize; }
|
|
};
|
|
|
|
typedef std::vector<COFFRelocation> relocations;
|
|
|
|
class COFFSection {
|
|
public:
|
|
COFF::section Header;
|
|
|
|
std::string Name;
|
|
int Number;
|
|
MCSectionData const *MCData;
|
|
COFFSymbol *Symbol;
|
|
relocations Relocations;
|
|
|
|
COFFSection(StringRef name);
|
|
static size_t size();
|
|
};
|
|
|
|
// This class holds the COFF string table.
|
|
class StringTable {
|
|
typedef StringMap<size_t> map;
|
|
map Map;
|
|
|
|
void update_length();
|
|
public:
|
|
std::vector<char> Data;
|
|
|
|
StringTable();
|
|
size_t size() const;
|
|
size_t insert(StringRef String);
|
|
};
|
|
|
|
class WinCOFFObjectWriter : public MCObjectWriter {
|
|
public:
|
|
|
|
typedef std::vector<COFFSymbol*> symbols;
|
|
typedef std::vector<COFFSection*> sections;
|
|
|
|
typedef DenseMap<MCSymbol const *, COFFSymbol *> symbol_map;
|
|
typedef DenseMap<MCSection const *, COFFSection *> section_map;
|
|
|
|
std::unique_ptr<MCWinCOFFObjectTargetWriter> TargetObjectWriter;
|
|
|
|
// Root level file contents.
|
|
COFF::header Header;
|
|
sections Sections;
|
|
symbols Symbols;
|
|
StringTable Strings;
|
|
|
|
// Maps used during object file creation.
|
|
section_map SectionMap;
|
|
symbol_map SymbolMap;
|
|
|
|
WinCOFFObjectWriter(MCWinCOFFObjectTargetWriter *MOTW, raw_ostream &OS);
|
|
virtual ~WinCOFFObjectWriter();
|
|
|
|
COFFSymbol *createSymbol(StringRef Name);
|
|
COFFSymbol *GetOrCreateCOFFSymbol(const MCSymbol * Symbol);
|
|
COFFSection *createSection(StringRef Name);
|
|
|
|
template <typename object_t, typename list_t>
|
|
object_t *createCOFFEntity(StringRef Name, list_t &List);
|
|
|
|
void DefineSection(MCSectionData const &SectionData);
|
|
void DefineSymbol(MCSymbolData const &SymbolData, MCAssembler &Assembler,
|
|
const MCAsmLayout &Layout);
|
|
|
|
void MakeSymbolReal(COFFSymbol &S, size_t Index);
|
|
void MakeSectionReal(COFFSection &S, size_t Number);
|
|
|
|
bool ExportSymbol(MCSymbolData const &SymbolData, MCAssembler &Asm);
|
|
|
|
bool IsPhysicalSection(COFFSection *S);
|
|
|
|
// Entity writing methods.
|
|
|
|
void WriteFileHeader(const COFF::header &Header);
|
|
void WriteSymbol(const COFFSymbol *S);
|
|
void WriteAuxiliarySymbols(const COFFSymbol::AuxiliarySymbols &S);
|
|
void WriteSectionHeader(const COFF::section &S);
|
|
void WriteRelocation(const COFF::relocation &R);
|
|
|
|
// MCObjectWriter interface implementation.
|
|
|
|
void ExecutePostLayoutBinding(MCAssembler &Asm,
|
|
const MCAsmLayout &Layout) override;
|
|
|
|
void RecordRelocation(const MCAssembler &Asm, const MCAsmLayout &Layout,
|
|
const MCFragment *Fragment, const MCFixup &Fixup,
|
|
MCValue Target, bool &IsPCRel,
|
|
uint64_t &FixedValue) override;
|
|
|
|
void WriteObject(MCAssembler &Asm, const MCAsmLayout &Layout) override;
|
|
};
|
|
}
|
|
|
|
static inline void write_uint32_le(void *Data, uint32_t const &Value) {
|
|
uint8_t *Ptr = reinterpret_cast<uint8_t *>(Data);
|
|
Ptr[0] = (Value & 0x000000FF) >> 0;
|
|
Ptr[1] = (Value & 0x0000FF00) >> 8;
|
|
Ptr[2] = (Value & 0x00FF0000) >> 16;
|
|
Ptr[3] = (Value & 0xFF000000) >> 24;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Symbol class implementation
|
|
|
|
COFFSymbol::COFFSymbol(StringRef name)
|
|
: Name(name.begin(), name.end())
|
|
, Other(nullptr)
|
|
, Section(nullptr)
|
|
, Relocations(0)
|
|
, MCData(nullptr) {
|
|
memset(&Data, 0, sizeof(Data));
|
|
}
|
|
|
|
size_t COFFSymbol::size() const {
|
|
return COFF::SymbolSize + (Data.NumberOfAuxSymbols * COFF::SymbolSize);
|
|
}
|
|
|
|
// In the case that the name does not fit within 8 bytes, the offset
|
|
// into the string table is stored in the last 4 bytes instead, leaving
|
|
// the first 4 bytes as 0.
|
|
void COFFSymbol::set_name_offset(uint32_t Offset) {
|
|
write_uint32_le(Data.Name + 0, 0);
|
|
write_uint32_le(Data.Name + 4, Offset);
|
|
}
|
|
|
|
/// logic to decide if the symbol should be reported in the symbol table
|
|
bool COFFSymbol::should_keep() const {
|
|
// no section means its external, keep it
|
|
if (!Section)
|
|
return true;
|
|
|
|
// if it has relocations pointing at it, keep it
|
|
if (Relocations > 0) {
|
|
assert(Section->Number != -1 && "Sections with relocations must be real!");
|
|
return true;
|
|
}
|
|
|
|
// if the section its in is being droped, drop it
|
|
if (Section->Number == -1)
|
|
return false;
|
|
|
|
// if it is the section symbol, keep it
|
|
if (Section->Symbol == this)
|
|
return true;
|
|
|
|
// if its temporary, drop it
|
|
if (MCData && MCData->getSymbol().isTemporary())
|
|
return false;
|
|
|
|
// otherwise, keep it
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Section class implementation
|
|
|
|
COFFSection::COFFSection(StringRef name)
|
|
: Name(name)
|
|
, MCData(nullptr)
|
|
, Symbol(nullptr) {
|
|
memset(&Header, 0, sizeof(Header));
|
|
}
|
|
|
|
size_t COFFSection::size() {
|
|
return COFF::SectionSize;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// StringTable class implementation
|
|
|
|
/// Write the length of the string table into Data.
|
|
/// The length of the string table includes uint32 length header.
|
|
void StringTable::update_length() {
|
|
write_uint32_le(&Data.front(), Data.size());
|
|
}
|
|
|
|
StringTable::StringTable() {
|
|
// The string table data begins with the length of the entire string table
|
|
// including the length header. Allocate space for this header.
|
|
Data.resize(4);
|
|
update_length();
|
|
}
|
|
|
|
size_t StringTable::size() const {
|
|
return Data.size();
|
|
}
|
|
|
|
/// Add String to the table iff it is not already there.
|
|
/// @returns the index into the string table where the string is now located.
|
|
size_t StringTable::insert(StringRef String) {
|
|
map::iterator i = Map.find(String);
|
|
|
|
if (i != Map.end())
|
|
return i->second;
|
|
|
|
size_t Offset = Data.size();
|
|
|
|
// Insert string data into string table.
|
|
Data.insert(Data.end(), String.begin(), String.end());
|
|
Data.push_back('\0');
|
|
|
|
// Put a reference to it in the map.
|
|
Map[String] = Offset;
|
|
|
|
// Update the internal length field.
|
|
update_length();
|
|
|
|
return Offset;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// WinCOFFObjectWriter class implementation
|
|
|
|
WinCOFFObjectWriter::WinCOFFObjectWriter(MCWinCOFFObjectTargetWriter *MOTW,
|
|
raw_ostream &OS)
|
|
: MCObjectWriter(OS, true)
|
|
, TargetObjectWriter(MOTW) {
|
|
memset(&Header, 0, sizeof(Header));
|
|
|
|
Header.Machine = TargetObjectWriter->getMachine();
|
|
}
|
|
|
|
WinCOFFObjectWriter::~WinCOFFObjectWriter() {
|
|
for (symbols::iterator I = Symbols.begin(), E = Symbols.end(); I != E; ++I)
|
|
delete *I;
|
|
for (sections::iterator I = Sections.begin(), E = Sections.end(); I != E; ++I)
|
|
delete *I;
|
|
}
|
|
|
|
COFFSymbol *WinCOFFObjectWriter::createSymbol(StringRef Name) {
|
|
return createCOFFEntity<COFFSymbol>(Name, Symbols);
|
|
}
|
|
|
|
COFFSymbol *WinCOFFObjectWriter::GetOrCreateCOFFSymbol(const MCSymbol * Symbol){
|
|
symbol_map::iterator i = SymbolMap.find(Symbol);
|
|
if (i != SymbolMap.end())
|
|
return i->second;
|
|
COFFSymbol *RetSymbol
|
|
= createCOFFEntity<COFFSymbol>(Symbol->getName(), Symbols);
|
|
SymbolMap[Symbol] = RetSymbol;
|
|
return RetSymbol;
|
|
}
|
|
|
|
COFFSection *WinCOFFObjectWriter::createSection(StringRef Name) {
|
|
return createCOFFEntity<COFFSection>(Name, Sections);
|
|
}
|
|
|
|
/// A template used to lookup or create a symbol/section, and initialize it if
|
|
/// needed.
|
|
template <typename object_t, typename list_t>
|
|
object_t *WinCOFFObjectWriter::createCOFFEntity(StringRef Name,
|
|
list_t &List) {
|
|
object_t *Object = new object_t(Name);
|
|
|
|
List.push_back(Object);
|
|
|
|
return Object;
|
|
}
|
|
|
|
/// This function takes a section data object from the assembler
|
|
/// and creates the associated COFF section staging object.
|
|
void WinCOFFObjectWriter::DefineSection(MCSectionData const &SectionData) {
|
|
assert(SectionData.getSection().getVariant() == MCSection::SV_COFF
|
|
&& "Got non-COFF section in the COFF backend!");
|
|
// FIXME: Not sure how to verify this (at least in a debug build).
|
|
MCSectionCOFF const &Sec =
|
|
static_cast<MCSectionCOFF const &>(SectionData.getSection());
|
|
|
|
COFFSection *coff_section = createSection(Sec.getSectionName());
|
|
COFFSymbol *coff_symbol = createSymbol(Sec.getSectionName());
|
|
|
|
coff_section->Symbol = coff_symbol;
|
|
coff_symbol->Section = coff_section;
|
|
coff_symbol->Data.StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
|
|
|
|
// In this case the auxiliary symbol is a Section Definition.
|
|
coff_symbol->Aux.resize(1);
|
|
memset(&coff_symbol->Aux[0], 0, sizeof(coff_symbol->Aux[0]));
|
|
coff_symbol->Aux[0].AuxType = ATSectionDefinition;
|
|
coff_symbol->Aux[0].Aux.SectionDefinition.Selection = Sec.getSelection();
|
|
|
|
coff_section->Header.Characteristics = Sec.getCharacteristics();
|
|
|
|
uint32_t &Characteristics = coff_section->Header.Characteristics;
|
|
switch (SectionData.getAlignment()) {
|
|
case 1: Characteristics |= COFF::IMAGE_SCN_ALIGN_1BYTES; break;
|
|
case 2: Characteristics |= COFF::IMAGE_SCN_ALIGN_2BYTES; break;
|
|
case 4: Characteristics |= COFF::IMAGE_SCN_ALIGN_4BYTES; break;
|
|
case 8: Characteristics |= COFF::IMAGE_SCN_ALIGN_8BYTES; break;
|
|
case 16: Characteristics |= COFF::IMAGE_SCN_ALIGN_16BYTES; break;
|
|
case 32: Characteristics |= COFF::IMAGE_SCN_ALIGN_32BYTES; break;
|
|
case 64: Characteristics |= COFF::IMAGE_SCN_ALIGN_64BYTES; break;
|
|
case 128: Characteristics |= COFF::IMAGE_SCN_ALIGN_128BYTES; break;
|
|
case 256: Characteristics |= COFF::IMAGE_SCN_ALIGN_256BYTES; break;
|
|
case 512: Characteristics |= COFF::IMAGE_SCN_ALIGN_512BYTES; break;
|
|
case 1024: Characteristics |= COFF::IMAGE_SCN_ALIGN_1024BYTES; break;
|
|
case 2048: Characteristics |= COFF::IMAGE_SCN_ALIGN_2048BYTES; break;
|
|
case 4096: Characteristics |= COFF::IMAGE_SCN_ALIGN_4096BYTES; break;
|
|
case 8192: Characteristics |= COFF::IMAGE_SCN_ALIGN_8192BYTES; break;
|
|
default:
|
|
llvm_unreachable("unsupported section alignment");
|
|
}
|
|
|
|
// Bind internal COFF section to MC section.
|
|
coff_section->MCData = &SectionData;
|
|
SectionMap[&SectionData.getSection()] = coff_section;
|
|
}
|
|
|
|
/// This function takes a symbol data object from the assembler
|
|
/// and creates the associated COFF symbol staging object.
|
|
void WinCOFFObjectWriter::DefineSymbol(MCSymbolData const &SymbolData,
|
|
MCAssembler &Assembler,
|
|
const MCAsmLayout &Layout) {
|
|
MCSymbol const &Symbol = SymbolData.getSymbol();
|
|
COFFSymbol *coff_symbol = GetOrCreateCOFFSymbol(&Symbol);
|
|
SymbolMap[&Symbol] = coff_symbol;
|
|
|
|
if (SymbolData.getFlags() & COFF::SF_WeakExternal) {
|
|
coff_symbol->Data.StorageClass = COFF::IMAGE_SYM_CLASS_WEAK_EXTERNAL;
|
|
|
|
if (Symbol.isVariable()) {
|
|
const MCSymbolRefExpr *SymRef =
|
|
dyn_cast<MCSymbolRefExpr>(Symbol.getVariableValue());
|
|
|
|
if (!SymRef)
|
|
report_fatal_error("Weak externals may only alias symbols");
|
|
|
|
coff_symbol->Other = GetOrCreateCOFFSymbol(&SymRef->getSymbol());
|
|
} else {
|
|
std::string WeakName = std::string(".weak.")
|
|
+ Symbol.getName().str()
|
|
+ ".default";
|
|
COFFSymbol *WeakDefault = createSymbol(WeakName);
|
|
WeakDefault->Data.SectionNumber = COFF::IMAGE_SYM_ABSOLUTE;
|
|
WeakDefault->Data.StorageClass = COFF::IMAGE_SYM_CLASS_EXTERNAL;
|
|
WeakDefault->Data.Type = 0;
|
|
WeakDefault->Data.Value = 0;
|
|
coff_symbol->Other = WeakDefault;
|
|
}
|
|
|
|
// Setup the Weak External auxiliary symbol.
|
|
coff_symbol->Aux.resize(1);
|
|
memset(&coff_symbol->Aux[0], 0, sizeof(coff_symbol->Aux[0]));
|
|
coff_symbol->Aux[0].AuxType = ATWeakExternal;
|
|
coff_symbol->Aux[0].Aux.WeakExternal.TagIndex = 0;
|
|
coff_symbol->Aux[0].Aux.WeakExternal.Characteristics =
|
|
COFF::IMAGE_WEAK_EXTERN_SEARCH_LIBRARY;
|
|
|
|
coff_symbol->MCData = &SymbolData;
|
|
} else {
|
|
const MCSymbolData &ResSymData =
|
|
Assembler.getSymbolData(Symbol.AliasedSymbol());
|
|
|
|
if (Symbol.isVariable()) {
|
|
int64_t Addr;
|
|
if (Symbol.getVariableValue()->EvaluateAsAbsolute(Addr, Layout))
|
|
coff_symbol->Data.Value = Addr;
|
|
} else if (SymbolData.isExternal() && SymbolData.isCommon()) {
|
|
coff_symbol->Data.Value = SymbolData.getCommonSize();
|
|
}
|
|
|
|
coff_symbol->Data.Type = (ResSymData.getFlags() & 0x0000FFFF) >> 0;
|
|
coff_symbol->Data.StorageClass = (ResSymData.getFlags() & 0x00FF0000) >> 16;
|
|
|
|
// If no storage class was specified in the streamer, define it here.
|
|
if (coff_symbol->Data.StorageClass == 0) {
|
|
bool external = ResSymData.isExternal() || !ResSymData.Fragment;
|
|
|
|
coff_symbol->Data.StorageClass =
|
|
external ? COFF::IMAGE_SYM_CLASS_EXTERNAL : COFF::IMAGE_SYM_CLASS_STATIC;
|
|
}
|
|
|
|
if (Symbol.isAbsolute() || Symbol.AliasedSymbol().isVariable())
|
|
coff_symbol->Data.SectionNumber = COFF::IMAGE_SYM_ABSOLUTE;
|
|
else if (ResSymData.Fragment)
|
|
coff_symbol->Section =
|
|
SectionMap[&ResSymData.Fragment->getParent()->getSection()];
|
|
|
|
coff_symbol->MCData = &ResSymData;
|
|
}
|
|
}
|
|
|
|
// Maximum offsets for different string table entry encodings.
|
|
static const unsigned Max6DecimalOffset = 999999;
|
|
static const unsigned Max7DecimalOffset = 9999999;
|
|
static const uint64_t MaxBase64Offset = 0xFFFFFFFFFULL; // 64^6, including 0
|
|
|
|
// Encode a string table entry offset in base 64, padded to 6 chars, and
|
|
// prefixed with a double slash: '//AAAAAA', '//AAAAAB', ...
|
|
// Buffer must be at least 8 bytes large. No terminating null appended.
|
|
static void encodeBase64StringEntry(char* Buffer, uint64_t Value) {
|
|
assert(Value > Max7DecimalOffset && Value <= MaxBase64Offset &&
|
|
"Illegal section name encoding for value");
|
|
|
|
static const char Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
"abcdefghijklmnopqrstuvwxyz"
|
|
"0123456789+/";
|
|
|
|
Buffer[0] = '/';
|
|
Buffer[1] = '/';
|
|
|
|
char* Ptr = Buffer + 7;
|
|
for (unsigned i = 0; i < 6; ++i) {
|
|
unsigned Rem = Value % 64;
|
|
Value /= 64;
|
|
*(Ptr--) = Alphabet[Rem];
|
|
}
|
|
}
|
|
|
|
/// making a section real involves assigned it a number and putting
|
|
/// name into the string table if needed
|
|
void WinCOFFObjectWriter::MakeSectionReal(COFFSection &S, size_t Number) {
|
|
if (S.Name.size() > COFF::NameSize) {
|
|
uint64_t StringTableEntry = Strings.insert(S.Name.c_str());
|
|
|
|
if (StringTableEntry <= Max6DecimalOffset) {
|
|
std::sprintf(S.Header.Name, "/%d", unsigned(StringTableEntry));
|
|
} else if (StringTableEntry <= Max7DecimalOffset) {
|
|
// With seven digits, we have to skip the terminating null. Because
|
|
// sprintf always appends it, we use a larger temporary buffer.
|
|
char buffer[9] = { };
|
|
std::sprintf(buffer, "/%d", unsigned(StringTableEntry));
|
|
std::memcpy(S.Header.Name, buffer, 8);
|
|
} else if (StringTableEntry <= MaxBase64Offset) {
|
|
// Starting with 10,000,000, offsets are encoded as base64.
|
|
encodeBase64StringEntry(S.Header.Name, StringTableEntry);
|
|
} else {
|
|
report_fatal_error("COFF string table is greater than 64 GB.");
|
|
}
|
|
} else
|
|
std::memcpy(S.Header.Name, S.Name.c_str(), S.Name.size());
|
|
|
|
S.Number = Number;
|
|
S.Symbol->Data.SectionNumber = S.Number;
|
|
S.Symbol->Aux[0].Aux.SectionDefinition.Number = S.Number;
|
|
}
|
|
|
|
void WinCOFFObjectWriter::MakeSymbolReal(COFFSymbol &S, size_t Index) {
|
|
if (S.Name.size() > COFF::NameSize) {
|
|
size_t StringTableEntry = Strings.insert(S.Name.c_str());
|
|
|
|
S.set_name_offset(StringTableEntry);
|
|
} else
|
|
std::memcpy(S.Data.Name, S.Name.c_str(), S.Name.size());
|
|
S.Index = Index;
|
|
}
|
|
|
|
bool WinCOFFObjectWriter::ExportSymbol(MCSymbolData const &SymbolData,
|
|
MCAssembler &Asm) {
|
|
// This doesn't seem to be right. Strings referred to from the .data section
|
|
// need symbols so they can be linked to code in the .text section right?
|
|
|
|
// return Asm.isSymbolLinkerVisible (&SymbolData);
|
|
|
|
// For now, all non-variable symbols are exported,
|
|
// the linker will sort the rest out for us.
|
|
return SymbolData.isExternal() || !SymbolData.getSymbol().isVariable();
|
|
}
|
|
|
|
bool WinCOFFObjectWriter::IsPhysicalSection(COFFSection *S) {
|
|
return (S->Header.Characteristics
|
|
& COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) == 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// entity writing methods
|
|
|
|
void WinCOFFObjectWriter::WriteFileHeader(const COFF::header &Header) {
|
|
WriteLE16(Header.Machine);
|
|
WriteLE16(Header.NumberOfSections);
|
|
WriteLE32(Header.TimeDateStamp);
|
|
WriteLE32(Header.PointerToSymbolTable);
|
|
WriteLE32(Header.NumberOfSymbols);
|
|
WriteLE16(Header.SizeOfOptionalHeader);
|
|
WriteLE16(Header.Characteristics);
|
|
}
|
|
|
|
void WinCOFFObjectWriter::WriteSymbol(const COFFSymbol *S) {
|
|
WriteBytes(StringRef(S->Data.Name, COFF::NameSize));
|
|
WriteLE32(S->Data.Value);
|
|
WriteLE16(S->Data.SectionNumber);
|
|
WriteLE16(S->Data.Type);
|
|
Write8(S->Data.StorageClass);
|
|
Write8(S->Data.NumberOfAuxSymbols);
|
|
WriteAuxiliarySymbols(S->Aux);
|
|
}
|
|
|
|
void WinCOFFObjectWriter::WriteAuxiliarySymbols(
|
|
const COFFSymbol::AuxiliarySymbols &S) {
|
|
for(COFFSymbol::AuxiliarySymbols::const_iterator i = S.begin(), e = S.end();
|
|
i != e; ++i) {
|
|
switch(i->AuxType) {
|
|
case ATFunctionDefinition:
|
|
WriteLE32(i->Aux.FunctionDefinition.TagIndex);
|
|
WriteLE32(i->Aux.FunctionDefinition.TotalSize);
|
|
WriteLE32(i->Aux.FunctionDefinition.PointerToLinenumber);
|
|
WriteLE32(i->Aux.FunctionDefinition.PointerToNextFunction);
|
|
WriteZeros(sizeof(i->Aux.FunctionDefinition.unused));
|
|
break;
|
|
case ATbfAndefSymbol:
|
|
WriteZeros(sizeof(i->Aux.bfAndefSymbol.unused1));
|
|
WriteLE16(i->Aux.bfAndefSymbol.Linenumber);
|
|
WriteZeros(sizeof(i->Aux.bfAndefSymbol.unused2));
|
|
WriteLE32(i->Aux.bfAndefSymbol.PointerToNextFunction);
|
|
WriteZeros(sizeof(i->Aux.bfAndefSymbol.unused3));
|
|
break;
|
|
case ATWeakExternal:
|
|
WriteLE32(i->Aux.WeakExternal.TagIndex);
|
|
WriteLE32(i->Aux.WeakExternal.Characteristics);
|
|
WriteZeros(sizeof(i->Aux.WeakExternal.unused));
|
|
break;
|
|
case ATFile:
|
|
WriteBytes(StringRef(reinterpret_cast<const char *>(i->Aux.File.FileName),
|
|
sizeof(i->Aux.File.FileName)));
|
|
break;
|
|
case ATSectionDefinition:
|
|
WriteLE32(i->Aux.SectionDefinition.Length);
|
|
WriteLE16(i->Aux.SectionDefinition.NumberOfRelocations);
|
|
WriteLE16(i->Aux.SectionDefinition.NumberOfLinenumbers);
|
|
WriteLE32(i->Aux.SectionDefinition.CheckSum);
|
|
WriteLE16(i->Aux.SectionDefinition.Number);
|
|
Write8(i->Aux.SectionDefinition.Selection);
|
|
WriteZeros(sizeof(i->Aux.SectionDefinition.unused));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WinCOFFObjectWriter::WriteSectionHeader(const COFF::section &S) {
|
|
WriteBytes(StringRef(S.Name, COFF::NameSize));
|
|
|
|
WriteLE32(S.VirtualSize);
|
|
WriteLE32(S.VirtualAddress);
|
|
WriteLE32(S.SizeOfRawData);
|
|
WriteLE32(S.PointerToRawData);
|
|
WriteLE32(S.PointerToRelocations);
|
|
WriteLE32(S.PointerToLineNumbers);
|
|
WriteLE16(S.NumberOfRelocations);
|
|
WriteLE16(S.NumberOfLineNumbers);
|
|
WriteLE32(S.Characteristics);
|
|
}
|
|
|
|
void WinCOFFObjectWriter::WriteRelocation(const COFF::relocation &R) {
|
|
WriteLE32(R.VirtualAddress);
|
|
WriteLE32(R.SymbolTableIndex);
|
|
WriteLE16(R.Type);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// MCObjectWriter interface implementations
|
|
|
|
void WinCOFFObjectWriter::ExecutePostLayoutBinding(MCAssembler &Asm,
|
|
const MCAsmLayout &Layout) {
|
|
// "Define" each section & symbol. This creates section & symbol
|
|
// entries in the staging area.
|
|
|
|
for (MCAssembler::const_iterator i = Asm.begin(), e = Asm.end(); i != e; i++)
|
|
DefineSection(*i);
|
|
|
|
for (MCAssembler::const_symbol_iterator i = Asm.symbol_begin(),
|
|
e = Asm.symbol_end();
|
|
i != e; i++) {
|
|
if (ExportSymbol(*i, Asm)) {
|
|
DefineSymbol(*i, Asm, Layout);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WinCOFFObjectWriter::RecordRelocation(const MCAssembler &Asm,
|
|
const MCAsmLayout &Layout,
|
|
const MCFragment *Fragment,
|
|
const MCFixup &Fixup,
|
|
MCValue Target,
|
|
bool &IsPCRel,
|
|
uint64_t &FixedValue) {
|
|
assert(Target.getSymA() && "Relocation must reference a symbol!");
|
|
|
|
const MCSymbol &Symbol = Target.getSymA()->getSymbol();
|
|
const MCSymbol &A = Symbol.AliasedSymbol();
|
|
if (!Asm.hasSymbolData(A))
|
|
Asm.getContext().FatalError(
|
|
Fixup.getLoc(),
|
|
Twine("symbol '") + A.getName() + "' can not be undefined");
|
|
|
|
MCSymbolData &A_SD = Asm.getSymbolData(A);
|
|
|
|
MCSectionData const *SectionData = Fragment->getParent();
|
|
|
|
// Mark this symbol as requiring an entry in the symbol table.
|
|
assert(SectionMap.find(&SectionData->getSection()) != SectionMap.end() &&
|
|
"Section must already have been defined in ExecutePostLayoutBinding!");
|
|
assert(SymbolMap.find(&A_SD.getSymbol()) != SymbolMap.end() &&
|
|
"Symbol must already have been defined in ExecutePostLayoutBinding!");
|
|
|
|
COFFSection *coff_section = SectionMap[&SectionData->getSection()];
|
|
COFFSymbol *coff_symbol = SymbolMap[&A_SD.getSymbol()];
|
|
const MCSymbolRefExpr *SymB = Target.getSymB();
|
|
bool CrossSection = false;
|
|
|
|
if (SymB) {
|
|
const MCSymbol *B = &SymB->getSymbol();
|
|
MCSymbolData &B_SD = Asm.getSymbolData(*B);
|
|
if (!B_SD.getFragment())
|
|
Asm.getContext().FatalError(
|
|
Fixup.getLoc(),
|
|
Twine("symbol '") + B->getName() +
|
|
"' can not be undefined in a subtraction expression");
|
|
|
|
if (!A_SD.getFragment())
|
|
Asm.getContext().FatalError(
|
|
Fixup.getLoc(),
|
|
Twine("symbol '") + Symbol.getName() +
|
|
"' can not be undefined in a subtraction expression");
|
|
|
|
CrossSection = &Symbol.getSection() != &B->getSection();
|
|
|
|
// Offset of the symbol in the section
|
|
int64_t a = Layout.getSymbolOffset(&B_SD);
|
|
|
|
// Ofeset of the relocation in the section
|
|
int64_t b = Layout.getFragmentOffset(Fragment) + Fixup.getOffset();
|
|
|
|
FixedValue = b - a;
|
|
// In the case where we have SymbA and SymB, we just need to store the delta
|
|
// between the two symbols. Update FixedValue to account for the delta, and
|
|
// skip recording the relocation.
|
|
if (!CrossSection)
|
|
return;
|
|
} else {
|
|
FixedValue = Target.getConstant();
|
|
}
|
|
|
|
COFFRelocation Reloc;
|
|
|
|
Reloc.Data.SymbolTableIndex = 0;
|
|
Reloc.Data.VirtualAddress = Layout.getFragmentOffset(Fragment);
|
|
|
|
// Turn relocations for temporary symbols into section relocations.
|
|
if (coff_symbol->MCData->getSymbol().isTemporary() || CrossSection) {
|
|
Reloc.Symb = coff_symbol->Section->Symbol;
|
|
FixedValue += Layout.getFragmentOffset(coff_symbol->MCData->Fragment)
|
|
+ coff_symbol->MCData->getOffset();
|
|
} else
|
|
Reloc.Symb = coff_symbol;
|
|
|
|
++Reloc.Symb->Relocations;
|
|
|
|
Reloc.Data.VirtualAddress += Fixup.getOffset();
|
|
Reloc.Data.Type = TargetObjectWriter->getRelocType(Target, Fixup,
|
|
CrossSection);
|
|
|
|
// FIXME: Can anyone explain what this does other than adjust for the size
|
|
// of the offset?
|
|
if ((Header.Machine == COFF::IMAGE_FILE_MACHINE_AMD64 &&
|
|
Reloc.Data.Type == COFF::IMAGE_REL_AMD64_REL32) ||
|
|
(Header.Machine == COFF::IMAGE_FILE_MACHINE_I386 &&
|
|
Reloc.Data.Type == COFF::IMAGE_REL_I386_REL32))
|
|
FixedValue += 4;
|
|
|
|
coff_section->Relocations.push_back(Reloc);
|
|
}
|
|
|
|
void WinCOFFObjectWriter::WriteObject(MCAssembler &Asm,
|
|
const MCAsmLayout &Layout) {
|
|
// Assign symbol and section indexes and offsets.
|
|
Header.NumberOfSections = 0;
|
|
|
|
DenseMap<COFFSection *, uint16_t> SectionIndices;
|
|
for (sections::iterator i = Sections.begin(),
|
|
e = Sections.end(); i != e; i++) {
|
|
if (Layout.getSectionAddressSize((*i)->MCData) > 0) {
|
|
size_t Number = ++Header.NumberOfSections;
|
|
SectionIndices[*i] = Number;
|
|
MakeSectionReal(**i, Number);
|
|
} else {
|
|
(*i)->Number = -1;
|
|
}
|
|
}
|
|
|
|
Header.NumberOfSymbols = 0;
|
|
|
|
for (symbols::iterator i = Symbols.begin(), e = Symbols.end(); i != e; i++) {
|
|
COFFSymbol *coff_symbol = *i;
|
|
MCSymbolData const *SymbolData = coff_symbol->MCData;
|
|
|
|
// Update section number & offset for symbols that have them.
|
|
if (SymbolData && SymbolData->Fragment) {
|
|
assert(coff_symbol->Section != nullptr);
|
|
|
|
coff_symbol->Data.SectionNumber = coff_symbol->Section->Number;
|
|
coff_symbol->Data.Value = Layout.getFragmentOffset(SymbolData->Fragment)
|
|
+ SymbolData->Offset;
|
|
}
|
|
|
|
if (coff_symbol->should_keep()) {
|
|
MakeSymbolReal(*coff_symbol, Header.NumberOfSymbols++);
|
|
|
|
// Update auxiliary symbol info.
|
|
coff_symbol->Data.NumberOfAuxSymbols = coff_symbol->Aux.size();
|
|
Header.NumberOfSymbols += coff_symbol->Data.NumberOfAuxSymbols;
|
|
} else
|
|
coff_symbol->Index = -1;
|
|
}
|
|
|
|
// Fixup weak external references.
|
|
for (symbols::iterator i = Symbols.begin(), e = Symbols.end(); i != e; i++) {
|
|
COFFSymbol *coff_symbol = *i;
|
|
if (coff_symbol->Other) {
|
|
assert(coff_symbol->Index != -1);
|
|
assert(coff_symbol->Aux.size() == 1 &&
|
|
"Symbol must contain one aux symbol!");
|
|
assert(coff_symbol->Aux[0].AuxType == ATWeakExternal &&
|
|
"Symbol's aux symbol must be a Weak External!");
|
|
coff_symbol->Aux[0].Aux.WeakExternal.TagIndex = coff_symbol->Other->Index;
|
|
}
|
|
}
|
|
|
|
// Fixup associative COMDAT sections.
|
|
for (sections::iterator i = Sections.begin(),
|
|
e = Sections.end(); i != e; i++) {
|
|
if ((*i)->Symbol->Aux[0].Aux.SectionDefinition.Selection !=
|
|
COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE)
|
|
continue;
|
|
|
|
const MCSectionCOFF &MCSec = static_cast<const MCSectionCOFF &>(
|
|
(*i)->MCData->getSection());
|
|
|
|
COFFSection *Assoc = SectionMap.lookup(MCSec.getAssocSection());
|
|
if (!Assoc) {
|
|
report_fatal_error(Twine("Missing associated COMDAT section ") +
|
|
MCSec.getAssocSection()->getSectionName() +
|
|
" for section " + MCSec.getSectionName());
|
|
}
|
|
|
|
// Skip this section if the associated section is unused.
|
|
if (Assoc->Number == -1)
|
|
continue;
|
|
|
|
(*i)->Symbol->Aux[0].Aux.SectionDefinition.Number = SectionIndices[Assoc];
|
|
}
|
|
|
|
|
|
// Assign file offsets to COFF object file structures.
|
|
|
|
unsigned offset = 0;
|
|
|
|
offset += COFF::HeaderSize;
|
|
offset += COFF::SectionSize * Header.NumberOfSections;
|
|
|
|
for (MCAssembler::const_iterator i = Asm.begin(),
|
|
e = Asm.end();
|
|
i != e; i++) {
|
|
COFFSection *Sec = SectionMap[&i->getSection()];
|
|
|
|
if (Sec->Number == -1)
|
|
continue;
|
|
|
|
Sec->Header.SizeOfRawData = Layout.getSectionAddressSize(i);
|
|
|
|
if (IsPhysicalSection(Sec)) {
|
|
Sec->Header.PointerToRawData = offset;
|
|
|
|
offset += Sec->Header.SizeOfRawData;
|
|
}
|
|
|
|
if (Sec->Relocations.size() > 0) {
|
|
bool RelocationsOverflow = Sec->Relocations.size() >= 0xffff;
|
|
|
|
if (RelocationsOverflow) {
|
|
// Signal overflow by setting NumberOfSections to max value. Actual
|
|
// size is found in reloc #0. Microsoft tools understand this.
|
|
Sec->Header.NumberOfRelocations = 0xffff;
|
|
} else {
|
|
Sec->Header.NumberOfRelocations = Sec->Relocations.size();
|
|
}
|
|
Sec->Header.PointerToRelocations = offset;
|
|
|
|
if (RelocationsOverflow) {
|
|
// Reloc #0 will contain actual count, so make room for it.
|
|
offset += COFF::RelocationSize;
|
|
}
|
|
|
|
offset += COFF::RelocationSize * Sec->Relocations.size();
|
|
|
|
for (relocations::iterator cr = Sec->Relocations.begin(),
|
|
er = Sec->Relocations.end();
|
|
cr != er; ++cr) {
|
|
assert((*cr).Symb->Index != -1);
|
|
(*cr).Data.SymbolTableIndex = (*cr).Symb->Index;
|
|
}
|
|
}
|
|
|
|
assert(Sec->Symbol->Aux.size() == 1
|
|
&& "Section's symbol must have one aux!");
|
|
AuxSymbol &Aux = Sec->Symbol->Aux[0];
|
|
assert(Aux.AuxType == ATSectionDefinition &&
|
|
"Section's symbol's aux symbol must be a Section Definition!");
|
|
Aux.Aux.SectionDefinition.Length = Sec->Header.SizeOfRawData;
|
|
Aux.Aux.SectionDefinition.NumberOfRelocations =
|
|
Sec->Header.NumberOfRelocations;
|
|
Aux.Aux.SectionDefinition.NumberOfLinenumbers =
|
|
Sec->Header.NumberOfLineNumbers;
|
|
}
|
|
|
|
Header.PointerToSymbolTable = offset;
|
|
|
|
// We want a deterministic output. It looks like GNU as also writes 0 in here.
|
|
Header.TimeDateStamp = 0;
|
|
|
|
// Write it all to disk...
|
|
WriteFileHeader(Header);
|
|
|
|
{
|
|
sections::iterator i, ie;
|
|
MCAssembler::const_iterator j, je;
|
|
|
|
for (i = Sections.begin(), ie = Sections.end(); i != ie; i++)
|
|
if ((*i)->Number != -1) {
|
|
if ((*i)->Relocations.size() >= 0xffff) {
|
|
(*i)->Header.Characteristics |= COFF::IMAGE_SCN_LNK_NRELOC_OVFL;
|
|
}
|
|
WriteSectionHeader((*i)->Header);
|
|
}
|
|
|
|
for (i = Sections.begin(), ie = Sections.end(),
|
|
j = Asm.begin(), je = Asm.end();
|
|
(i != ie) && (j != je); ++i, ++j) {
|
|
|
|
if ((*i)->Number == -1)
|
|
continue;
|
|
|
|
if ((*i)->Header.PointerToRawData != 0) {
|
|
assert(OS.tell() == (*i)->Header.PointerToRawData &&
|
|
"Section::PointerToRawData is insane!");
|
|
|
|
Asm.writeSectionData(j, Layout);
|
|
}
|
|
|
|
if ((*i)->Relocations.size() > 0) {
|
|
assert(OS.tell() == (*i)->Header.PointerToRelocations &&
|
|
"Section::PointerToRelocations is insane!");
|
|
|
|
if ((*i)->Relocations.size() >= 0xffff) {
|
|
// In case of overflow, write actual relocation count as first
|
|
// relocation. Including the synthetic reloc itself (+ 1).
|
|
COFF::relocation r;
|
|
r.VirtualAddress = (*i)->Relocations.size() + 1;
|
|
r.SymbolTableIndex = 0;
|
|
r.Type = 0;
|
|
WriteRelocation(r);
|
|
}
|
|
|
|
for (relocations::const_iterator k = (*i)->Relocations.begin(),
|
|
ke = (*i)->Relocations.end();
|
|
k != ke; k++) {
|
|
WriteRelocation(k->Data);
|
|
}
|
|
} else
|
|
assert((*i)->Header.PointerToRelocations == 0 &&
|
|
"Section::PointerToRelocations is insane!");
|
|
}
|
|
}
|
|
|
|
assert(OS.tell() == Header.PointerToSymbolTable &&
|
|
"Header::PointerToSymbolTable is insane!");
|
|
|
|
for (symbols::iterator i = Symbols.begin(), e = Symbols.end(); i != e; i++)
|
|
if ((*i)->Index != -1)
|
|
WriteSymbol(*i);
|
|
|
|
OS.write((char const *)&Strings.Data.front(), Strings.Data.size());
|
|
}
|
|
|
|
MCWinCOFFObjectTargetWriter::MCWinCOFFObjectTargetWriter(unsigned Machine_) :
|
|
Machine(Machine_) {
|
|
}
|
|
|
|
// Pin the vtable to this file.
|
|
void MCWinCOFFObjectTargetWriter::anchor() {}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// WinCOFFObjectWriter factory function
|
|
|
|
namespace llvm {
|
|
MCObjectWriter *createWinCOFFObjectWriter(MCWinCOFFObjectTargetWriter *MOTW,
|
|
raw_ostream &OS) {
|
|
return new WinCOFFObjectWriter(MOTW, OS);
|
|
}
|
|
}
|