#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/COFF.h"
#include "llvm/MC/MCAsmLayout.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCFixup.h"
#include "llvm/MC/MCFragment.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCSection.h"
#include "llvm/MC/MCSectionCOFF.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/MCSymbolCOFF.h"
#include "llvm/MC/MCValue.h"
#include "llvm/MC/MCWinCOFFObjectWriter.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Support/CRC.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <ctime>
#include <memory>
#include <string>
#include <vector>
using namespace llvm;
using llvm::support::endian::write32le;
#define DEBUG_TYPE "WinCOFFObjectWriter"
namespace {
constexpr int OffsetLabelIntervalBits = 20;
using name = SmallString<COFF::NameSize>;
enum AuxiliaryType {
ATWeakExternal,
ATFile,
ATSectionDefinition
};
struct AuxSymbol {
AuxiliaryType AuxType;
COFF::Auxiliary Aux;
};
class COFFSection;
class COFFSymbol {
public:
COFF::symbol Data = {};
using AuxiliarySymbols = SmallVector<AuxSymbol, 1>;
name Name;
int Index;
AuxiliarySymbols Aux;
COFFSymbol *Other = nullptr;
COFFSection *Section = nullptr;
int Relocations = 0;
const MCSymbol *MC = nullptr;
COFFSymbol(StringRef Name) : Name(Name) {}
void set_name_offset(uint32_t Offset);
int64_t getIndex() const { return Index; }
void setIndex(int Value) {
Index = Value;
if (MC)
MC->setIndex(static_cast<uint32_t>(Value));
}
};
struct COFFRelocation {
COFF::relocation Data;
COFFSymbol *Symb = nullptr;
COFFRelocation() = default;
static size_t size() { return COFF::RelocationSize; }
};
using relocations = std::vector<COFFRelocation>;
class COFFSection {
public:
COFF::section Header = {};
std::string Name;
int Number;
MCSectionCOFF const *MCSection = nullptr;
COFFSymbol *Symbol = nullptr;
relocations Relocations;
COFFSection(StringRef Name) : Name(std::string(Name)) {}
SmallVector<COFFSymbol *, 1> OffsetSymbols;
};
class WinCOFFObjectWriter : public MCObjectWriter {
public:
support::endian::Writer W;
using symbols = std::vector<std::unique_ptr<COFFSymbol>>;
using sections = std::vector<std::unique_ptr<COFFSection>>;
using symbol_map = DenseMap<MCSymbol const *, COFFSymbol *>;
using section_map = DenseMap<MCSection const *, COFFSection *>;
using symbol_list = DenseSet<COFFSymbol *>;
std::unique_ptr<MCWinCOFFObjectTargetWriter> TargetObjectWriter;
COFF::header Header = {};
sections Sections;
symbols Symbols;
StringTableBuilder Strings{StringTableBuilder::WinCOFF};
section_map SectionMap;
symbol_map SymbolMap;
symbol_list WeakDefaults;
bool UseBigObj;
bool UseOffsetLabels = false;
MCSectionCOFF *AddrsigSection;
MCSectionCOFF *CGProfileSection = nullptr;
WinCOFFObjectWriter(std::unique_ptr<MCWinCOFFObjectTargetWriter> MOTW,
raw_pwrite_stream &OS);
void reset() override {
memset(&Header, 0, sizeof(Header));
Header.Machine = TargetObjectWriter->getMachine();
Sections.clear();
Symbols.clear();
Strings.clear();
SectionMap.clear();
SymbolMap.clear();
WeakDefaults.clear();
MCObjectWriter::reset();
}
COFFSymbol *createSymbol(StringRef Name);
COFFSymbol *GetOrCreateCOFFSymbol(const MCSymbol *Symbol);
COFFSection *createSection(StringRef Name);
void defineSection(MCSectionCOFF const &Sec, const MCAsmLayout &Layout);
COFFSymbol *getLinkedSymbol(const MCSymbol &Symbol);
void DefineSymbol(const MCSymbol &Symbol, MCAssembler &Assembler,
const MCAsmLayout &Layout);
void SetSymbolName(COFFSymbol &S);
void SetSectionName(COFFSection &S);
bool IsPhysicalSection(COFFSection *S);
void WriteFileHeader(const COFF::header &Header);
void WriteSymbol(const COFFSymbol &S);
void WriteAuxiliarySymbols(const COFFSymbol::AuxiliarySymbols &S);
void writeSectionHeaders();
void WriteRelocation(const COFF::relocation &R);
uint32_t writeSectionContents(MCAssembler &Asm, const MCAsmLayout &Layout,
const MCSection &MCSec);
void writeSection(MCAssembler &Asm, const MCAsmLayout &Layout,
const COFFSection &Sec, const MCSection &MCSec);
void executePostLayoutBinding(MCAssembler &Asm,
const MCAsmLayout &Layout) override;
bool isSymbolRefDifferenceFullyResolvedImpl(const MCAssembler &Asm,
const MCSymbol &SymA,
const MCFragment &FB, bool InSet,
bool IsPCRel) const override;
void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout,
const MCFragment *Fragment, const MCFixup &Fixup,
MCValue Target, uint64_t &FixedValue) override;
void createFileSymbols(MCAssembler &Asm);
void setWeakDefaultNames();
void assignSectionNumbers();
void assignFileOffsets(MCAssembler &Asm, const MCAsmLayout &Layout);
uint64_t writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override;
};
}
void COFFSymbol::set_name_offset(uint32_t Offset) {
write32le(Data.Name + 0, 0);
write32le(Data.Name + 4, Offset);
}
WinCOFFObjectWriter::WinCOFFObjectWriter(
std::unique_ptr<MCWinCOFFObjectTargetWriter> MOTW, raw_pwrite_stream &OS)
: W(OS, support::little), TargetObjectWriter(std::move(MOTW)) {
Header.Machine = TargetObjectWriter->getMachine();
UseOffsetLabels = Header.Machine == COFF::IMAGE_FILE_MACHINE_ARM64;
}
COFFSymbol *WinCOFFObjectWriter::createSymbol(StringRef Name) {
Symbols.push_back(std::make_unique<COFFSymbol>(Name));
return Symbols.back().get();
}
COFFSymbol *WinCOFFObjectWriter::GetOrCreateCOFFSymbol(const MCSymbol *Symbol) {
COFFSymbol *&Ret = SymbolMap[Symbol];
if (!Ret)
Ret = createSymbol(Symbol->getName());
return Ret;
}
COFFSection *WinCOFFObjectWriter::createSection(StringRef Name) {
Sections.emplace_back(std::make_unique<COFFSection>(Name));
return Sections.back().get();
}
static uint32_t getAlignment(const MCSectionCOFF &Sec) {
switch (Sec.getAlignment()) {
case 1:
return COFF::IMAGE_SCN_ALIGN_1BYTES;
case 2:
return COFF::IMAGE_SCN_ALIGN_2BYTES;
case 4:
return COFF::IMAGE_SCN_ALIGN_4BYTES;
case 8:
return COFF::IMAGE_SCN_ALIGN_8BYTES;
case 16:
return COFF::IMAGE_SCN_ALIGN_16BYTES;
case 32:
return COFF::IMAGE_SCN_ALIGN_32BYTES;
case 64:
return COFF::IMAGE_SCN_ALIGN_64BYTES;
case 128:
return COFF::IMAGE_SCN_ALIGN_128BYTES;
case 256:
return COFF::IMAGE_SCN_ALIGN_256BYTES;
case 512:
return COFF::IMAGE_SCN_ALIGN_512BYTES;
case 1024:
return COFF::IMAGE_SCN_ALIGN_1024BYTES;
case 2048:
return COFF::IMAGE_SCN_ALIGN_2048BYTES;
case 4096:
return COFF::IMAGE_SCN_ALIGN_4096BYTES;
case 8192:
return COFF::IMAGE_SCN_ALIGN_8192BYTES;
}
llvm_unreachable("unsupported section alignment");
}
void WinCOFFObjectWriter::defineSection(const MCSectionCOFF &MCSec,
const MCAsmLayout &Layout) {
COFFSection *Section = createSection(MCSec.getName());
COFFSymbol *Symbol = createSymbol(MCSec.getName());
Section->Symbol = Symbol;
Symbol->Section = Section;
Symbol->Data.StorageClass = COFF::IMAGE_SYM_CLASS_STATIC;
if (MCSec.getSelection() != COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) {
if (const MCSymbol *S = MCSec.getCOMDATSymbol()) {
COFFSymbol *COMDATSymbol = GetOrCreateCOFFSymbol(S);
if (COMDATSymbol->Section)
report_fatal_error("two sections have the same comdat");
COMDATSymbol->Section = Section;
}
}
Symbol->Aux.resize(1);
Symbol->Aux[0] = {};
Symbol->Aux[0].AuxType = ATSectionDefinition;
Symbol->Aux[0].Aux.SectionDefinition.Selection = MCSec.getSelection();
Section->Header.Characteristics = MCSec.getCharacteristics();
Section->Header.Characteristics |= getAlignment(MCSec);
Section->MCSection = &MCSec;
SectionMap[&MCSec] = Section;
if (UseOffsetLabels && !MCSec.getFragmentList().empty()) {
const uint32_t Interval = 1 << OffsetLabelIntervalBits;
uint32_t N = 1;
for (uint32_t Off = Interval, E = Layout.getSectionAddressSize(&MCSec);
Off < E; Off += Interval) {
auto Name = ("$L" + MCSec.getName() + "_" + Twine(N++)).str();
COFFSymbol *Label = createSymbol(Name);
Label->Section = Section;
Label->Data.StorageClass = COFF::IMAGE_SYM_CLASS_LABEL;
Label->Data.Value = Off;
Section->OffsetSymbols.push_back(Label);
}
}
}
static uint64_t getSymbolValue(const MCSymbol &Symbol,
const MCAsmLayout &Layout) {
if (Symbol.isCommon() && Symbol.isExternal())
return Symbol.getCommonSize();
uint64_t Res;
if (!Layout.getSymbolOffset(Symbol, Res))
return 0;
return Res;
}
COFFSymbol *WinCOFFObjectWriter::getLinkedSymbol(const MCSymbol &Symbol) {
if (!Symbol.isVariable())
return nullptr;
const MCSymbolRefExpr *SymRef =
dyn_cast<MCSymbolRefExpr>(Symbol.getVariableValue());
if (!SymRef)
return nullptr;
const MCSymbol &Aliasee = SymRef->getSymbol();
if (Aliasee.isUndefined() || Aliasee.isExternal())
return GetOrCreateCOFFSymbol(&Aliasee);
else
return nullptr;
}
void WinCOFFObjectWriter::DefineSymbol(const MCSymbol &MCSym,
MCAssembler &Assembler,
const MCAsmLayout &Layout) {
COFFSymbol *Sym = GetOrCreateCOFFSymbol(&MCSym);
const MCSymbol *Base = Layout.getBaseSymbol(MCSym);
COFFSection *Sec = nullptr;
if (Base && Base->getFragment()) {
Sec = SectionMap[Base->getFragment()->getParent()];
if (Sym->Section && Sym->Section != Sec)
report_fatal_error("conflicting sections for symbol");
}
COFFSymbol *Local = nullptr;
if (cast<MCSymbolCOFF>(MCSym).isWeakExternal()) {
Sym->Data.StorageClass = COFF::IMAGE_SYM_CLASS_WEAK_EXTERNAL;
Sym->Section = nullptr;
COFFSymbol *WeakDefault = getLinkedSymbol(MCSym);
if (!WeakDefault) {
std::string WeakName = (".weak." + MCSym.getName() + ".default").str();
WeakDefault = createSymbol(WeakName);
if (!Sec)
WeakDefault->Data.SectionNumber = COFF::IMAGE_SYM_ABSOLUTE;
else
WeakDefault->Section = Sec;
WeakDefaults.insert(WeakDefault);
Local = WeakDefault;
}
Sym->Other = WeakDefault;
Sym->Aux.resize(1);
memset(&Sym->Aux[0], 0, sizeof(Sym->Aux[0]));
Sym->Aux[0].AuxType = ATWeakExternal;
Sym->Aux[0].Aux.WeakExternal.TagIndex = 0;
Sym->Aux[0].Aux.WeakExternal.Characteristics =
COFF::IMAGE_WEAK_EXTERN_SEARCH_ALIAS;
} else {
if (!Base)
Sym->Data.SectionNumber = COFF::IMAGE_SYM_ABSOLUTE;
else
Sym->Section = Sec;
Local = Sym;
}
if (Local) {
Local->Data.Value = getSymbolValue(MCSym, Layout);
const MCSymbolCOFF &SymbolCOFF = cast<MCSymbolCOFF>(MCSym);
Local->Data.Type = SymbolCOFF.getType();
Local->Data.StorageClass = SymbolCOFF.getClass();
if (Local->Data.StorageClass == COFF::IMAGE_SYM_CLASS_NULL) {
bool IsExternal = MCSym.isExternal() ||
(!MCSym.getFragment() && !MCSym.isVariable());
Local->Data.StorageClass = IsExternal ? COFF::IMAGE_SYM_CLASS_EXTERNAL
: COFF::IMAGE_SYM_CLASS_STATIC;
}
}
Sym->MC = &MCSym;
}
void WinCOFFObjectWriter::SetSectionName(COFFSection &S) {
if (S.Name.size() <= COFF::NameSize) {
std::memcpy(S.Header.Name, S.Name.c_str(), S.Name.size());
return;
}
uint64_t StringTableEntry = Strings.getOffset(S.Name);
if (!COFF::encodeSectionName(S.Header.Name, StringTableEntry))
report_fatal_error("COFF string table is greater than 64 GB.");
}
void WinCOFFObjectWriter::SetSymbolName(COFFSymbol &S) {
if (S.Name.size() > COFF::NameSize)
S.set_name_offset(Strings.getOffset(S.Name));
else
std::memcpy(S.Data.Name, S.Name.c_str(), S.Name.size());
}
bool WinCOFFObjectWriter::IsPhysicalSection(COFFSection *S) {
return (S->Header.Characteristics & COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA) ==
0;
}
void WinCOFFObjectWriter::WriteFileHeader(const COFF::header &Header) {
if (UseBigObj) {
W.write<uint16_t>(COFF::IMAGE_FILE_MACHINE_UNKNOWN);
W.write<uint16_t>(0xFFFF);
W.write<uint16_t>(COFF::BigObjHeader::MinBigObjectVersion);
W.write<uint16_t>(Header.Machine);
W.write<uint32_t>(Header.TimeDateStamp);
W.OS.write(COFF::BigObjMagic, sizeof(COFF::BigObjMagic));
W.write<uint32_t>(0);
W.write<uint32_t>(0);
W.write<uint32_t>(0);
W.write<uint32_t>(0);
W.write<uint32_t>(Header.NumberOfSections);
W.write<uint32_t>(Header.PointerToSymbolTable);
W.write<uint32_t>(Header.NumberOfSymbols);
} else {
W.write<uint16_t>(Header.Machine);
W.write<uint16_t>(static_cast<int16_t>(Header.NumberOfSections));
W.write<uint32_t>(Header.TimeDateStamp);
W.write<uint32_t>(Header.PointerToSymbolTable);
W.write<uint32_t>(Header.NumberOfSymbols);
W.write<uint16_t>(Header.SizeOfOptionalHeader);
W.write<uint16_t>(Header.Characteristics);
}
}
void WinCOFFObjectWriter::WriteSymbol(const COFFSymbol &S) {
W.OS.write(S.Data.Name, COFF::NameSize);
W.write<uint32_t>(S.Data.Value);
if (UseBigObj)
W.write<uint32_t>(S.Data.SectionNumber);
else
W.write<uint16_t>(static_cast<int16_t>(S.Data.SectionNumber));
W.write<uint16_t>(S.Data.Type);
W.OS << char(S.Data.StorageClass);
W.OS << char(S.Data.NumberOfAuxSymbols);
WriteAuxiliarySymbols(S.Aux);
}
void WinCOFFObjectWriter::WriteAuxiliarySymbols(
const COFFSymbol::AuxiliarySymbols &S) {
for (const AuxSymbol &i : S) {
switch (i.AuxType) {
case ATWeakExternal:
W.write<uint32_t>(i.Aux.WeakExternal.TagIndex);
W.write<uint32_t>(i.Aux.WeakExternal.Characteristics);
W.OS.write_zeros(sizeof(i.Aux.WeakExternal.unused));
if (UseBigObj)
W.OS.write_zeros(COFF::Symbol32Size - COFF::Symbol16Size);
break;
case ATFile:
W.OS.write(reinterpret_cast<const char *>(&i.Aux),
UseBigObj ? COFF::Symbol32Size : COFF::Symbol16Size);
break;
case ATSectionDefinition:
W.write<uint32_t>(i.Aux.SectionDefinition.Length);
W.write<uint16_t>(i.Aux.SectionDefinition.NumberOfRelocations);
W.write<uint16_t>(i.Aux.SectionDefinition.NumberOfLinenumbers);
W.write<uint32_t>(i.Aux.SectionDefinition.CheckSum);
W.write<uint16_t>(static_cast<int16_t>(i.Aux.SectionDefinition.Number));
W.OS << char(i.Aux.SectionDefinition.Selection);
W.OS.write_zeros(sizeof(i.Aux.SectionDefinition.unused));
W.write<uint16_t>(static_cast<int16_t>(i.Aux.SectionDefinition.Number >> 16));
if (UseBigObj)
W.OS.write_zeros(COFF::Symbol32Size - COFF::Symbol16Size);
break;
}
}
}
void WinCOFFObjectWriter::writeSectionHeaders() {
std::vector<COFFSection *> Arr;
for (auto &Section : Sections)
Arr.push_back(Section.get());
llvm::sort(Arr, [](const COFFSection *A, const COFFSection *B) {
return A->Number < B->Number;
});
for (auto &Section : Arr) {
if (Section->Number == -1)
continue;
COFF::section &S = Section->Header;
if (Section->Relocations.size() >= 0xffff)
S.Characteristics |= COFF::IMAGE_SCN_LNK_NRELOC_OVFL;
W.OS.write(S.Name, COFF::NameSize);
W.write<uint32_t>(S.VirtualSize);
W.write<uint32_t>(S.VirtualAddress);
W.write<uint32_t>(S.SizeOfRawData);
W.write<uint32_t>(S.PointerToRawData);
W.write<uint32_t>(S.PointerToRelocations);
W.write<uint32_t>(S.PointerToLineNumbers);
W.write<uint16_t>(S.NumberOfRelocations);
W.write<uint16_t>(S.NumberOfLineNumbers);
W.write<uint32_t>(S.Characteristics);
}
}
void WinCOFFObjectWriter::WriteRelocation(const COFF::relocation &R) {
W.write<uint32_t>(R.VirtualAddress);
W.write<uint32_t>(R.SymbolTableIndex);
W.write<uint16_t>(R.Type);
}
uint32_t WinCOFFObjectWriter::writeSectionContents(MCAssembler &Asm,
const MCAsmLayout &Layout,
const MCSection &MCSec) {
SmallVector<char, 128> Buf;
raw_svector_ostream VecOS(Buf);
Asm.writeSectionData(VecOS, &MCSec, Layout);
W.OS << Buf;
JamCRC JC(0);
JC.update(makeArrayRef(reinterpret_cast<uint8_t*>(Buf.data()), Buf.size()));
return JC.getCRC();
}
void WinCOFFObjectWriter::writeSection(MCAssembler &Asm,
const MCAsmLayout &Layout,
const COFFSection &Sec,
const MCSection &MCSec) {
if (Sec.Number == -1)
return;
if (Sec.Header.PointerToRawData != 0) {
assert(W.OS.tell() == Sec.Header.PointerToRawData &&
"Section::PointerToRawData is insane!");
uint32_t CRC = writeSectionContents(Asm, Layout, MCSec);
COFFSection *Sec = SectionMap[&MCSec];
COFFSymbol::AuxiliarySymbols &AuxSyms = Sec->Symbol->Aux;
assert(AuxSyms.size() == 1 && AuxSyms[0].AuxType == ATSectionDefinition);
AuxSymbol &SecDef = AuxSyms[0];
SecDef.Aux.SectionDefinition.CheckSum = CRC;
}
if (Sec.Relocations.empty()) {
assert(Sec.Header.PointerToRelocations == 0 &&
"Section::PointerToRelocations is insane!");
return;
}
assert(W.OS.tell() == Sec.Header.PointerToRelocations &&
"Section::PointerToRelocations is insane!");
if (Sec.Relocations.size() >= 0xffff) {
COFF::relocation R;
R.VirtualAddress = Sec.Relocations.size() + 1;
R.SymbolTableIndex = 0;
R.Type = 0;
WriteRelocation(R);
}
for (const auto &Relocation : Sec.Relocations)
WriteRelocation(Relocation.Data);
}
void WinCOFFObjectWriter::executePostLayoutBinding(MCAssembler &Asm,
const MCAsmLayout &Layout) {
if (EmitAddrsigSection) {
AddrsigSection = Asm.getContext().getCOFFSection(
".llvm_addrsig", COFF::IMAGE_SCN_LNK_REMOVE,
SectionKind::getMetadata());
Asm.registerSection(*AddrsigSection);
}
if (!Asm.CGProfile.empty()) {
CGProfileSection = Asm.getContext().getCOFFSection(
".llvm.call-graph-profile", COFF::IMAGE_SCN_LNK_REMOVE,
SectionKind::getMetadata());
Asm.registerSection(*CGProfileSection);
}
for (const auto &Section : Asm)
defineSection(static_cast<const MCSectionCOFF &>(Section), Layout);
for (const MCSymbol &Symbol : Asm.symbols())
if (!Symbol.isTemporary())
DefineSymbol(Symbol, Asm, Layout);
}
bool WinCOFFObjectWriter::isSymbolRefDifferenceFullyResolvedImpl(
const MCAssembler &Asm, const MCSymbol &SymA, const MCFragment &FB,
bool InSet, bool IsPCRel) const {
uint16_t Type = cast<MCSymbolCOFF>(SymA).getType();
if ((Type >> COFF::SCT_COMPLEX_TYPE_SHIFT) == COFF::IMAGE_SYM_DTYPE_FUNCTION)
return false;
return MCObjectWriter::isSymbolRefDifferenceFullyResolvedImpl(Asm, SymA, FB,
InSet, IsPCRel);
}
void WinCOFFObjectWriter::recordRelocation(MCAssembler &Asm,
const MCAsmLayout &Layout,
const MCFragment *Fragment,
const MCFixup &Fixup, MCValue Target,
uint64_t &FixedValue) {
assert(Target.getSymA() && "Relocation must reference a symbol!");
const MCSymbol &A = Target.getSymA()->getSymbol();
if (!A.isRegistered()) {
Asm.getContext().reportError(Fixup.getLoc(),
Twine("symbol '") + A.getName() +
"' can not be undefined");
return;
}
if (A.isTemporary() && A.isUndefined()) {
Asm.getContext().reportError(Fixup.getLoc(),
Twine("assembler label '") + A.getName() +
"' can not be undefined");
return;
}
MCSection *MCSec = Fragment->getParent();
assert(SectionMap.find(MCSec) != SectionMap.end() &&
"Section must already have been defined in executePostLayoutBinding!");
COFFSection *Sec = SectionMap[MCSec];
const MCSymbolRefExpr *SymB = Target.getSymB();
if (SymB) {
const MCSymbol *B = &SymB->getSymbol();
if (!B->getFragment()) {
Asm.getContext().reportError(
Fixup.getLoc(),
Twine("symbol '") + B->getName() +
"' can not be undefined in a subtraction expression");
return;
}
int64_t OffsetOfB = Layout.getSymbolOffset(*B);
int64_t OffsetOfRelocation =
Layout.getFragmentOffset(Fragment) + Fixup.getOffset();
FixedValue = (OffsetOfRelocation - OffsetOfB) + Target.getConstant();
} else {
FixedValue = Target.getConstant();
}
COFFRelocation Reloc;
Reloc.Data.SymbolTableIndex = 0;
Reloc.Data.VirtualAddress = Layout.getFragmentOffset(Fragment);
if (A.isTemporary()) {
MCSection *TargetSection = &A.getSection();
assert(
SectionMap.find(TargetSection) != SectionMap.end() &&
"Section must already have been defined in executePostLayoutBinding!");
COFFSection *Section = SectionMap[TargetSection];
Reloc.Symb = Section->Symbol;
FixedValue += Layout.getSymbolOffset(A);
if (UseOffsetLabels && !Section->OffsetSymbols.empty()) {
uint64_t LabelIndex = FixedValue >> OffsetLabelIntervalBits;
if (LabelIndex > 0) {
if (LabelIndex <= Section->OffsetSymbols.size())
Reloc.Symb = Section->OffsetSymbols[LabelIndex - 1];
else
Reloc.Symb = Section->OffsetSymbols.back();
FixedValue -= Reloc.Symb->Data.Value;
}
}
} else {
assert(
SymbolMap.find(&A) != SymbolMap.end() &&
"Symbol must already have been defined in executePostLayoutBinding!");
Reloc.Symb = SymbolMap[&A];
}
++Reloc.Symb->Relocations;
Reloc.Data.VirtualAddress += Fixup.getOffset();
Reloc.Data.Type = TargetObjectWriter->getRelocType(
Asm.getContext(), Target, Fixup, SymB, Asm.getBackend());
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) ||
(Header.Machine == COFF::IMAGE_FILE_MACHINE_ARMNT &&
Reloc.Data.Type == COFF::IMAGE_REL_ARM_REL32) ||
(Header.Machine == COFF::IMAGE_FILE_MACHINE_ARM64 &&
Reloc.Data.Type == COFF::IMAGE_REL_ARM64_REL32))
FixedValue += 4;
if (Header.Machine == COFF::IMAGE_FILE_MACHINE_ARMNT) {
switch (Reloc.Data.Type) {
case COFF::IMAGE_REL_ARM_ABSOLUTE:
case COFF::IMAGE_REL_ARM_ADDR32:
case COFF::IMAGE_REL_ARM_ADDR32NB:
case COFF::IMAGE_REL_ARM_TOKEN:
case COFF::IMAGE_REL_ARM_SECTION:
case COFF::IMAGE_REL_ARM_SECREL:
break;
case COFF::IMAGE_REL_ARM_BRANCH11:
case COFF::IMAGE_REL_ARM_BLX11:
case COFF::IMAGE_REL_ARM_BRANCH24:
case COFF::IMAGE_REL_ARM_BLX24:
case COFF::IMAGE_REL_ARM_MOV32A:
llvm_unreachable("unsupported relocation");
break;
case COFF::IMAGE_REL_ARM_MOV32T:
break;
case COFF::IMAGE_REL_ARM_BRANCH20T:
case COFF::IMAGE_REL_ARM_BRANCH24T:
case COFF::IMAGE_REL_ARM_BLX23T:
FixedValue = FixedValue + 4;
break;
}
}
if (Fixup.getKind() == FK_SecRel_2)
FixedValue = 0;
if (TargetObjectWriter->recordRelocation(Fixup))
Sec->Relocations.push_back(Reloc);
}
static std::time_t getTime() {
std::time_t Now = time(nullptr);
if (Now < 0 || !isUInt<32>(Now))
return UINT32_MAX;
return Now;
}
void WinCOFFObjectWriter::createFileSymbols(MCAssembler &Asm) {
for (const std::pair<std::string, size_t> &It : Asm.getFileNames()) {
const std::string &Name = It.first;
unsigned SymbolSize = UseBigObj ? COFF::Symbol32Size : COFF::Symbol16Size;
unsigned Count = (Name.size() + SymbolSize - 1) / SymbolSize;
COFFSymbol *File = createSymbol(".file");
File->Data.SectionNumber = COFF::IMAGE_SYM_DEBUG;
File->Data.StorageClass = COFF::IMAGE_SYM_CLASS_FILE;
File->Aux.resize(Count);
unsigned Offset = 0;
unsigned Length = Name.size();
for (auto &Aux : File->Aux) {
Aux.AuxType = ATFile;
if (Length > SymbolSize) {
memcpy(&Aux.Aux, Name.c_str() + Offset, SymbolSize);
Length = Length - SymbolSize;
} else {
memcpy(&Aux.Aux, Name.c_str() + Offset, Length);
memset((char *)&Aux.Aux + Length, 0, SymbolSize - Length);
break;
}
Offset += SymbolSize;
}
}
}
void WinCOFFObjectWriter::setWeakDefaultNames() {
if (WeakDefaults.empty())
return;
COFFSymbol *Unique = nullptr;
for (bool AllowComdat : {false, true}) {
for (auto &Sym : Symbols) {
if (WeakDefaults.count(Sym.get()))
continue;
if (Sym->Data.StorageClass != COFF::IMAGE_SYM_CLASS_EXTERNAL)
continue;
if (!Sym->Section && Sym->Data.SectionNumber != COFF::IMAGE_SYM_ABSOLUTE)
continue;
if (!AllowComdat && Sym->Section &&
Sym->Section->Header.Characteristics & COFF::IMAGE_SCN_LNK_COMDAT)
continue;
Unique = Sym.get();
break;
}
if (Unique)
break;
}
if (!Unique)
return;
for (auto *Sym : WeakDefaults) {
Sym->Name.append(".");
Sym->Name.append(Unique->Name);
}
}
static bool isAssociative(const COFFSection &Section) {
return Section.Symbol->Aux[0].Aux.SectionDefinition.Selection ==
COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE;
}
void WinCOFFObjectWriter::assignSectionNumbers() {
size_t I = 1;
auto Assign = [&](COFFSection &Section) {
Section.Number = I;
Section.Symbol->Data.SectionNumber = I;
Section.Symbol->Aux[0].Aux.SectionDefinition.Number = I;
++I;
};
for (const std::unique_ptr<COFFSection> &Section : Sections)
if (!isAssociative(*Section))
Assign(*Section);
for (const std::unique_ptr<COFFSection> &Section : Sections)
if (isAssociative(*Section))
Assign(*Section);
}
void WinCOFFObjectWriter::assignFileOffsets(MCAssembler &Asm,
const MCAsmLayout &Layout) {
unsigned Offset = W.OS.tell();
Offset += UseBigObj ? COFF::Header32Size : COFF::Header16Size;
Offset += COFF::SectionSize * Header.NumberOfSections;
for (const auto &Section : Asm) {
COFFSection *Sec = SectionMap[&Section];
if (!Sec || Sec->Number == -1)
continue;
Sec->Header.SizeOfRawData = Layout.getSectionAddressSize(&Section);
if (IsPhysicalSection(Sec)) {
Sec->Header.PointerToRawData = Offset;
Offset += Sec->Header.SizeOfRawData;
}
if (!Sec->Relocations.empty()) {
bool RelocationsOverflow = Sec->Relocations.size() >= 0xffff;
if (RelocationsOverflow) {
Sec->Header.NumberOfRelocations = 0xffff;
} else {
Sec->Header.NumberOfRelocations = Sec->Relocations.size();
}
Sec->Header.PointerToRelocations = Offset;
if (RelocationsOverflow) {
Offset += COFF::RelocationSize;
}
Offset += COFF::RelocationSize * Sec->Relocations.size();
for (auto &Relocation : Sec->Relocations) {
assert(Relocation.Symb->getIndex() != -1);
Relocation.Data.SymbolTableIndex = Relocation.Symb->getIndex();
}
}
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;
}
uint64_t WinCOFFObjectWriter::writeObject(MCAssembler &Asm,
const MCAsmLayout &Layout) {
uint64_t StartOffset = W.OS.tell();
if (Sections.size() > INT32_MAX)
report_fatal_error(
"PE COFF object files can't have more than 2147483647 sections");
UseBigObj = Sections.size() > COFF::MaxNumberOfSections16;
Header.NumberOfSections = Sections.size();
Header.NumberOfSymbols = 0;
setWeakDefaultNames();
assignSectionNumbers();
createFileSymbols(Asm);
for (auto &Symbol : Symbols) {
if (Symbol->Section)
Symbol->Data.SectionNumber = Symbol->Section->Number;
Symbol->setIndex(Header.NumberOfSymbols++);
Symbol->Data.NumberOfAuxSymbols = Symbol->Aux.size();
Header.NumberOfSymbols += Symbol->Data.NumberOfAuxSymbols;
}
for (const auto &S : Sections)
if (S->Name.size() > COFF::NameSize)
Strings.add(S->Name);
for (const auto &S : Symbols)
if (S->Name.size() > COFF::NameSize)
Strings.add(S->Name);
Strings.finalize();
for (const auto &S : Sections)
SetSectionName(*S);
for (auto &S : Symbols)
SetSymbolName(*S);
for (auto &Symbol : Symbols) {
if (Symbol->Other) {
assert(Symbol->getIndex() != -1);
assert(Symbol->Aux.size() == 1 && "Symbol must contain one aux symbol!");
assert(Symbol->Aux[0].AuxType == ATWeakExternal &&
"Symbol's aux symbol must be a Weak External!");
Symbol->Aux[0].Aux.WeakExternal.TagIndex = Symbol->Other->getIndex();
}
}
for (auto &Section : Sections) {
if (Section->Symbol->Aux[0].Aux.SectionDefinition.Selection !=
COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE)
continue;
const MCSectionCOFF &MCSec = *Section->MCSection;
const MCSymbol *AssocMCSym = MCSec.getCOMDATSymbol();
assert(AssocMCSym);
if (!AssocMCSym->isInSection()) {
Asm.getContext().reportError(
SMLoc(), Twine("cannot make section ") + MCSec.getName() +
Twine(" associative with sectionless symbol ") +
AssocMCSym->getName());
continue;
}
const auto *AssocMCSec = cast<MCSectionCOFF>(&AssocMCSym->getSection());
assert(SectionMap.count(AssocMCSec));
COFFSection *AssocSec = SectionMap[AssocMCSec];
if (AssocSec->Number == -1)
continue;
Section->Symbol->Aux[0].Aux.SectionDefinition.Number = AssocSec->Number;
}
if (EmitAddrsigSection) {
auto Frag = new MCDataFragment(AddrsigSection);
Frag->setLayoutOrder(0);
raw_svector_ostream OS(Frag->getContents());
for (const MCSymbol *S : AddrsigSyms) {
if (!S->isTemporary()) {
encodeULEB128(S->getIndex(), OS);
continue;
}
MCSection *TargetSection = &S->getSection();
assert(SectionMap.find(TargetSection) != SectionMap.end() &&
"Section must already have been defined in "
"executePostLayoutBinding!");
encodeULEB128(SectionMap[TargetSection]->Symbol->getIndex(), OS);
}
}
if (CGProfileSection) {
auto *Frag = new MCDataFragment(CGProfileSection);
Frag->setLayoutOrder(0);
raw_svector_ostream OS(Frag->getContents());
for (const MCAssembler::CGProfileEntry &CGPE : Asm.CGProfile) {
uint32_t FromIndex = CGPE.From->getSymbol().getIndex();
uint32_t ToIndex = CGPE.To->getSymbol().getIndex();
support::endian::write(OS, FromIndex, W.Endian);
support::endian::write(OS, ToIndex, W.Endian);
support::endian::write(OS, CGPE.Count, W.Endian);
}
}
assignFileOffsets(Asm, Layout);
if (Asm.isIncrementalLinkerCompatible()) {
Header.TimeDateStamp = getTime();
} else {
Header.TimeDateStamp = 0;
}
WriteFileHeader(Header);
writeSectionHeaders();
sections::iterator I = Sections.begin();
sections::iterator IE = Sections.end();
MCAssembler::iterator J = Asm.begin();
MCAssembler::iterator JE = Asm.end();
for (; I != IE && J != JE; ++I, ++J)
writeSection(Asm, Layout, **I, *J);
assert(W.OS.tell() == Header.PointerToSymbolTable &&
"Header::PointerToSymbolTable is insane!");
for (auto &Symbol : Symbols)
if (Symbol->getIndex() != -1)
WriteSymbol(*Symbol);
Strings.write(W.OS);
return W.OS.tell() - StartOffset;
}
MCWinCOFFObjectTargetWriter::MCWinCOFFObjectTargetWriter(unsigned Machine_)
: Machine(Machine_) {}
void MCWinCOFFObjectTargetWriter::anchor() {}
std::unique_ptr<MCObjectWriter> llvm::createWinCOFFObjectWriter(
std::unique_ptr<MCWinCOFFObjectTargetWriter> MOTW, raw_pwrite_stream &OS) {
return std::make_unique<WinCOFFObjectWriter>(std::move(MOTW), OS);
}