blob: 1ceac6c05893cd137d5ddd1ea85dfaf01280f5c6 [file] [log] [blame]
//===- yaml2xcoff - Convert YAML to a xcoff object file -------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// The xcoff component of yaml2obj.
///
//===----------------------------------------------------------------------===//
#include "llvm/ADT/DenseMap.h"
#include "llvm/BinaryFormat/XCOFF.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Object/XCOFFObjectFile.h"
#include "llvm/ObjectYAML/ObjectYAML.h"
#include "llvm/ObjectYAML/yaml2obj.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
namespace {
constexpr unsigned DefaultSectionAlign = 4;
constexpr int16_t MaxSectionIndex = INT16_MAX;
constexpr uint32_t MaxRawDataSize = UINT32_MAX;
class XCOFFWriter {
public:
XCOFFWriter(XCOFFYAML::Object &Obj, raw_ostream &OS, yaml::ErrorHandler EH)
: Obj(Obj), W(OS, support::big), ErrHandler(EH),
StrTblBuilder(StringTableBuilder::XCOFF) {
Is64Bit = Obj.Header.Magic == (llvm::yaml::Hex16)XCOFF::XCOFF64;
}
bool writeXCOFF();
private:
bool nameShouldBeInStringTable(StringRef SymbolName);
bool initFileHeader(uint64_t CurrentOffset);
void initAuxFileHeader();
bool initSectionHeader(uint64_t &CurrentOffset);
bool initRelocations(uint64_t &CurrentOffset);
bool initStringTable();
bool assignAddressesAndIndices();
void writeFileHeader();
void writeAuxFileHeader();
void writeSectionHeader();
bool writeSectionData();
bool writeRelocations();
bool writeSymbols();
void writeStringTable();
void writeAuxSymbol(const XCOFFYAML::CsectAuxEnt &AuxSym);
void writeAuxSymbol(const XCOFFYAML::FileAuxEnt &AuxSym);
void writeAuxSymbol(const XCOFFYAML::FunctionAuxEnt &AuxSym);
void writeAuxSymbol(const XCOFFYAML::ExcpetionAuxEnt &AuxSym);
void writeAuxSymbol(const XCOFFYAML::BlockAuxEnt &AuxSym);
void writeAuxSymbol(const XCOFFYAML::SectAuxEntForDWARF &AuxSym);
void writeAuxSymbol(const XCOFFYAML::SectAuxEntForStat &AuxSym);
void writeAuxSymbol(const std::unique_ptr<XCOFFYAML::AuxSymbolEnt> &AuxSym);
XCOFFYAML::Object &Obj;
bool Is64Bit = false;
support::endian::Writer W;
yaml::ErrorHandler ErrHandler;
StringTableBuilder StrTblBuilder;
uint64_t StartOffset;
// Map the section name to its corrresponding section index.
DenseMap<StringRef, int16_t> SectionIndexMap = {
{StringRef("N_DEBUG"), XCOFF::N_DEBUG},
{StringRef("N_ABS"), XCOFF::N_ABS},
{StringRef("N_UNDEF"), XCOFF::N_UNDEF}};
XCOFFYAML::FileHeader InitFileHdr = Obj.Header;
XCOFFYAML::AuxiliaryHeader InitAuxFileHdr;
std::vector<XCOFFYAML::Section> InitSections = Obj.Sections;
};
static void writeName(StringRef StrName, support::endian::Writer W) {
char Name[XCOFF::NameSize];
memset(Name, 0, XCOFF::NameSize);
char SrcName[] = "";
memcpy(Name, StrName.size() ? StrName.data() : SrcName, StrName.size());
ArrayRef<char> NameRef(Name, XCOFF::NameSize);
W.write(NameRef);
}
bool XCOFFWriter::nameShouldBeInStringTable(StringRef SymbolName) {
// For XCOFF64: The symbol name is always in the string table.
return (SymbolName.size() > XCOFF::NameSize) || Is64Bit;
}
bool XCOFFWriter::initRelocations(uint64_t &CurrentOffset) {
for (XCOFFYAML::Section &InitSection : InitSections) {
if (!InitSection.Relocations.empty()) {
InitSection.NumberOfRelocations = InitSection.Relocations.size();
InitSection.FileOffsetToRelocations = CurrentOffset;
uint64_t RelSize = Is64Bit ? XCOFF::RelocationSerializationSize64
: XCOFF::RelocationSerializationSize32;
CurrentOffset += InitSection.NumberOfRelocations * RelSize;
if (CurrentOffset > MaxRawDataSize) {
ErrHandler("maximum object size of" + Twine(MaxRawDataSize) +
"exceeded when writing relocation data");
return false;
}
}
}
return true;
}
bool XCOFFWriter::initSectionHeader(uint64_t &CurrentOffset) {
uint64_t CurrentSecAddr = 0;
for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) {
if (CurrentOffset > MaxRawDataSize) {
ErrHandler("maximum object size of" + Twine(MaxRawDataSize) +
"exceeded when writing section data");
return false;
}
// Assign indices for sections.
if (InitSections[I].SectionName.size() &&
!SectionIndexMap[InitSections[I].SectionName]) {
// The section index starts from 1.
SectionIndexMap[InitSections[I].SectionName] = I + 1;
if ((I + 1) > MaxSectionIndex) {
ErrHandler("exceeded the maximum permitted section index of " +
Twine(MaxSectionIndex));
return false;
}
}
// Calculate the physical/virtual address. This field should contain 0 for
// all sections except the text, data and bss sections.
if (InitSections[I].Flags != XCOFF::STYP_TEXT &&
InitSections[I].Flags != XCOFF::STYP_DATA &&
InitSections[I].Flags != XCOFF::STYP_BSS)
InitSections[I].Address = 0;
else
InitSections[I].Address = CurrentSecAddr;
// Calculate the FileOffsetToData and data size for sections.
if (InitSections[I].SectionData.binary_size()) {
InitSections[I].FileOffsetToData = CurrentOffset;
CurrentOffset += InitSections[I].SectionData.binary_size();
// Ensure the offset is aligned to DefaultSectionAlign.
CurrentOffset = alignTo(CurrentOffset, DefaultSectionAlign);
InitSections[I].Size = CurrentOffset - InitSections[I].FileOffsetToData;
CurrentSecAddr += InitSections[I].Size;
}
}
return initRelocations(CurrentOffset);
}
bool XCOFFWriter::initStringTable() {
if (Obj.StrTbl.RawContent) {
size_t RawSize = Obj.StrTbl.RawContent->binary_size();
if (Obj.StrTbl.Strings || Obj.StrTbl.Length) {
ErrHandler(
"can't specify Strings or Length when RawContent is specified");
return false;
}
if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize < RawSize) {
ErrHandler("specified ContentSize (" + Twine(*Obj.StrTbl.ContentSize) +
") is less than the RawContent data size (" + Twine(RawSize) +
")");
return false;
}
return true;
}
if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize <= 3) {
ErrHandler("ContentSize shouldn't be less than 4 without RawContent");
return false;
}
// Build the string table.
StrTblBuilder.clear();
if (Obj.StrTbl.Strings) {
// All specified strings should be added to the string table.
for (StringRef StringEnt : *Obj.StrTbl.Strings)
StrTblBuilder.add(StringEnt);
size_t StrTblIdx = 0;
size_t NumOfStrings = Obj.StrTbl.Strings->size();
for (XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
if (nameShouldBeInStringTable(YamlSym.SymbolName)) {
if (StrTblIdx < NumOfStrings) {
// Overwrite the symbol name with the specified string.
YamlSym.SymbolName = (*Obj.StrTbl.Strings)[StrTblIdx];
++StrTblIdx;
} else
// Names that are not overwritten are still stored in the string
// table.
StrTblBuilder.add(YamlSym.SymbolName);
}
}
} else {
for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
if (nameShouldBeInStringTable(YamlSym.SymbolName))
StrTblBuilder.add(YamlSym.SymbolName);
}
}
// Check if the file name in the File Auxiliary Entry should be added to the
// string table.
for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
for (const std::unique_ptr<XCOFFYAML::AuxSymbolEnt> &AuxSym :
YamlSym.AuxEntries) {
if (auto AS = dyn_cast<XCOFFYAML::FileAuxEnt>(AuxSym.get()))
if (nameShouldBeInStringTable(AS->FileNameOrString.value_or("")))
StrTblBuilder.add(AS->FileNameOrString.value_or(""));
}
}
StrTblBuilder.finalize();
size_t StrTblSize = StrTblBuilder.getSize();
if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize < StrTblSize) {
ErrHandler("specified ContentSize (" + Twine(*Obj.StrTbl.ContentSize) +
") is less than the size of the data that would otherwise be "
"written (" +
Twine(StrTblSize) + ")");
return false;
}
return true;
}
bool XCOFFWriter::initFileHeader(uint64_t CurrentOffset) {
// The default format of the object file is XCOFF32.
InitFileHdr.Magic = XCOFF::XCOFF32;
InitFileHdr.NumberOfSections = Obj.Sections.size();
InitFileHdr.NumberOfSymTableEntries = Obj.Symbols.size();
for (XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
uint32_t AuxCount = YamlSym.AuxEntries.size();
if (YamlSym.NumberOfAuxEntries && *YamlSym.NumberOfAuxEntries < AuxCount) {
ErrHandler("specified NumberOfAuxEntries " +
Twine(static_cast<uint32_t>(*YamlSym.NumberOfAuxEntries)) +
" is less than the actual number "
"of auxiliary entries " +
Twine(AuxCount));
return false;
}
YamlSym.NumberOfAuxEntries = YamlSym.NumberOfAuxEntries.value_or(AuxCount);
// Add the number of auxiliary symbols to the total number.
InitFileHdr.NumberOfSymTableEntries += *YamlSym.NumberOfAuxEntries;
}
// Calculate SymbolTableOffset for the file header.
if (InitFileHdr.NumberOfSymTableEntries) {
InitFileHdr.SymbolTableOffset = CurrentOffset;
CurrentOffset +=
InitFileHdr.NumberOfSymTableEntries * XCOFF::SymbolTableEntrySize;
if (CurrentOffset > MaxRawDataSize) {
ErrHandler("maximum object size of" + Twine(MaxRawDataSize) +
"exceeded when writing symbols");
return false;
}
}
// TODO: Calculate FileOffsetToLineNumbers when line number supported.
return true;
}
void XCOFFWriter::initAuxFileHeader() {
InitAuxFileHdr = *Obj.AuxHeader;
// In general, an object file might contain multiple sections of a given type,
// but in a loadable module, there must be exactly one .text, .data, .bss, and
// .loader section. A loadable object might also have one .tdata section and
// one .tbss section.
// Set these section-related values if not set explicitly. We assume that the
// input YAML matches the format of the loadable object, but if multiple input
// sections still have the same type, the first section with that type
// prevails.
for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) {
switch (InitSections[I].Flags) {
case XCOFF::STYP_TEXT:
if (!InitAuxFileHdr.TextSize)
InitAuxFileHdr.TextSize = InitSections[I].Size;
if (!InitAuxFileHdr.TextStartAddr)
InitAuxFileHdr.TextStartAddr = InitSections[I].Address;
if (!InitAuxFileHdr.SecNumOfText)
InitAuxFileHdr.SecNumOfText = I + 1;
break;
case XCOFF::STYP_DATA:
if (!InitAuxFileHdr.InitDataSize)
InitAuxFileHdr.InitDataSize = InitSections[I].Size;
if (!InitAuxFileHdr.DataStartAddr)
InitAuxFileHdr.DataStartAddr = InitSections[I].Address;
if (!InitAuxFileHdr.SecNumOfData)
InitAuxFileHdr.SecNumOfData = I + 1;
break;
case XCOFF::STYP_BSS:
if (!InitAuxFileHdr.BssDataSize)
InitAuxFileHdr.BssDataSize = InitSections[I].Size;
if (!InitAuxFileHdr.SecNumOfBSS)
InitAuxFileHdr.SecNumOfBSS = I + 1;
break;
case XCOFF::STYP_TDATA:
if (!InitAuxFileHdr.SecNumOfTData)
InitAuxFileHdr.SecNumOfTData = I + 1;
break;
case XCOFF::STYP_TBSS:
if (!InitAuxFileHdr.SecNumOfTBSS)
InitAuxFileHdr.SecNumOfTBSS = I + 1;
break;
case XCOFF::STYP_LOADER:
if (!InitAuxFileHdr.SecNumOfLoader)
InitAuxFileHdr.SecNumOfLoader = I + 1;
break;
default:
break;
}
}
}
bool XCOFFWriter::assignAddressesAndIndices() {
uint64_t FileHdrSize =
Is64Bit ? XCOFF::FileHeaderSize64 : XCOFF::FileHeaderSize32;
uint64_t AuxFileHdrSize = 0;
if (Obj.AuxHeader)
AuxFileHdrSize = Obj.Header.AuxHeaderSize
? Obj.Header.AuxHeaderSize
: (Is64Bit ? XCOFF::AuxFileHeaderSize64
: XCOFF::AuxFileHeaderSize32);
uint64_t SecHdrSize =
Is64Bit ? XCOFF::SectionHeaderSize64 : XCOFF::SectionHeaderSize32;
uint64_t CurrentOffset =
FileHdrSize + AuxFileHdrSize + InitSections.size() * SecHdrSize;
// Calculate section header info.
if (!initSectionHeader(CurrentOffset))
return false;
InitFileHdr.AuxHeaderSize = AuxFileHdrSize;
// Calculate file header info.
if (!initFileHeader(CurrentOffset))
return false;
// Initialize the auxiliary file header.
if (Obj.AuxHeader)
initAuxFileHeader();
// Initialize the string table.
return initStringTable();
}
void XCOFFWriter::writeFileHeader() {
W.write<uint16_t>(Obj.Header.Magic ? Obj.Header.Magic : InitFileHdr.Magic);
W.write<uint16_t>(Obj.Header.NumberOfSections ? Obj.Header.NumberOfSections
: InitFileHdr.NumberOfSections);
W.write<int32_t>(Obj.Header.TimeStamp);
if (Is64Bit) {
W.write<uint64_t>(Obj.Header.SymbolTableOffset
? Obj.Header.SymbolTableOffset
: InitFileHdr.SymbolTableOffset);
W.write<uint16_t>(InitFileHdr.AuxHeaderSize);
W.write<uint16_t>(Obj.Header.Flags);
W.write<int32_t>(Obj.Header.NumberOfSymTableEntries
? Obj.Header.NumberOfSymTableEntries
: InitFileHdr.NumberOfSymTableEntries);
} else {
W.write<uint32_t>(Obj.Header.SymbolTableOffset
? Obj.Header.SymbolTableOffset
: InitFileHdr.SymbolTableOffset);
W.write<int32_t>(Obj.Header.NumberOfSymTableEntries
? Obj.Header.NumberOfSymTableEntries
: InitFileHdr.NumberOfSymTableEntries);
W.write<uint16_t>(InitFileHdr.AuxHeaderSize);
W.write<uint16_t>(Obj.Header.Flags);
}
}
void XCOFFWriter::writeAuxFileHeader() {
W.write<uint16_t>(InitAuxFileHdr.Magic.value_or(yaml::Hex16(1)));
W.write<uint16_t>(InitAuxFileHdr.Version.value_or(yaml::Hex16(1)));
if (Is64Bit) {
W.OS.write_zeros(4); // Reserved for debugger.
W.write<uint64_t>(InitAuxFileHdr.TextStartAddr.value_or(yaml::Hex64(0)));
W.write<uint64_t>(InitAuxFileHdr.DataStartAddr.value_or(yaml::Hex64(0)));
W.write<uint64_t>(InitAuxFileHdr.TOCAnchorAddr.value_or(yaml::Hex64(0)));
} else {
W.write<uint32_t>(InitAuxFileHdr.TextSize.value_or(yaml::Hex64(0)));
W.write<uint32_t>(InitAuxFileHdr.InitDataSize.value_or(yaml::Hex64(0)));
W.write<uint32_t>(InitAuxFileHdr.BssDataSize.value_or(yaml::Hex64(0)));
W.write<uint32_t>(InitAuxFileHdr.EntryPointAddr.value_or(yaml::Hex64(0)));
W.write<uint32_t>(InitAuxFileHdr.TextStartAddr.value_or(yaml::Hex64(0)));
W.write<uint32_t>(InitAuxFileHdr.DataStartAddr.value_or(yaml::Hex64(0)));
W.write<uint32_t>(InitAuxFileHdr.TOCAnchorAddr.value_or(yaml::Hex64(0)));
}
W.write<uint16_t>(InitAuxFileHdr.SecNumOfEntryPoint.value_or(0));
W.write<uint16_t>(InitAuxFileHdr.SecNumOfText.value_or(0));
W.write<uint16_t>(InitAuxFileHdr.SecNumOfData.value_or(0));
W.write<uint16_t>(InitAuxFileHdr.SecNumOfTOC.value_or(0));
W.write<uint16_t>(InitAuxFileHdr.SecNumOfLoader.value_or(0));
W.write<uint16_t>(InitAuxFileHdr.SecNumOfBSS.value_or(0));
W.write<uint16_t>(InitAuxFileHdr.MaxAlignOfText.value_or(yaml::Hex16(0)));
W.write<uint16_t>(InitAuxFileHdr.MaxAlignOfData.value_or(yaml::Hex16(0)));
W.write<uint16_t>(InitAuxFileHdr.ModuleType.value_or(yaml::Hex16(0)));
W.write<uint8_t>(InitAuxFileHdr.CpuFlag.value_or(yaml::Hex8(0)));
W.write<uint8_t>(0); // Reserved for CPU type.
if (Is64Bit) {
W.write<uint8_t>(InitAuxFileHdr.TextPageSize.value_or(yaml::Hex8(0)));
W.write<uint8_t>(InitAuxFileHdr.DataPageSize.value_or(yaml::Hex8(0)));
W.write<uint8_t>(InitAuxFileHdr.StackPageSize.value_or(yaml::Hex8(0)));
W.write<uint8_t>(
InitAuxFileHdr.FlagAndTDataAlignment.value_or(yaml::Hex8(0x80)));
W.write<uint64_t>(InitAuxFileHdr.TextSize.value_or(yaml::Hex64(0)));
W.write<uint64_t>(InitAuxFileHdr.InitDataSize.value_or(yaml::Hex64(0)));
W.write<uint64_t>(InitAuxFileHdr.BssDataSize.value_or(yaml::Hex64(0)));
W.write<uint64_t>(InitAuxFileHdr.EntryPointAddr.value_or(yaml::Hex64(0)));
W.write<uint64_t>(InitAuxFileHdr.MaxStackSize.value_or(yaml::Hex64(0)));
W.write<uint64_t>(InitAuxFileHdr.MaxDataSize.value_or(yaml::Hex64(0)));
} else {
W.write<uint32_t>(InitAuxFileHdr.MaxStackSize.value_or(yaml::Hex64(0)));
W.write<uint32_t>(InitAuxFileHdr.MaxDataSize.value_or(yaml::Hex64(0)));
W.OS.write_zeros(4); // Reserved for debugger.
W.write<uint8_t>(InitAuxFileHdr.TextPageSize.value_or(yaml::Hex8(0)));
W.write<uint8_t>(InitAuxFileHdr.DataPageSize.value_or(yaml::Hex8(0)));
W.write<uint8_t>(InitAuxFileHdr.StackPageSize.value_or(yaml::Hex8(0)));
W.write<uint8_t>(
InitAuxFileHdr.FlagAndTDataAlignment.value_or(yaml::Hex8(0)));
}
W.write<uint16_t>(InitAuxFileHdr.SecNumOfTData.value_or(0));
W.write<uint16_t>(InitAuxFileHdr.SecNumOfTBSS.value_or(0));
if (Is64Bit) {
W.write<uint16_t>(
InitAuxFileHdr.Flag.value_or(yaml::Hex16(XCOFF::SHR_SYMTAB)));
if (InitFileHdr.AuxHeaderSize > XCOFF::AuxFileHeaderSize64)
W.OS.write_zeros(InitFileHdr.AuxHeaderSize - XCOFF::AuxFileHeaderSize64);
} else if (InitFileHdr.AuxHeaderSize > XCOFF::AuxFileHeaderSize32) {
W.OS.write_zeros(InitFileHdr.AuxHeaderSize - XCOFF::AuxFileHeaderSize32);
}
}
void XCOFFWriter::writeSectionHeader() {
for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
XCOFFYAML::Section YamlSec = Obj.Sections[I];
XCOFFYAML::Section DerivedSec = InitSections[I];
writeName(YamlSec.SectionName, W);
// Virtual address is the same as physical address.
uint64_t SectionAddress =
YamlSec.Address ? YamlSec.Address : DerivedSec.Address;
if (Is64Bit) {
W.write<uint64_t>(SectionAddress); // Physical address
W.write<uint64_t>(SectionAddress); // Virtual address
W.write<uint64_t>(YamlSec.Size ? YamlSec.Size : DerivedSec.Size);
W.write<uint64_t>(YamlSec.FileOffsetToData ? YamlSec.FileOffsetToData
: DerivedSec.FileOffsetToData);
W.write<uint64_t>(YamlSec.FileOffsetToRelocations
? YamlSec.FileOffsetToRelocations
: DerivedSec.FileOffsetToRelocations);
W.write<uint64_t>(YamlSec.FileOffsetToLineNumbers);
W.write<uint32_t>(YamlSec.NumberOfRelocations
? YamlSec.NumberOfRelocations
: DerivedSec.NumberOfRelocations);
W.write<uint32_t>(YamlSec.NumberOfLineNumbers);
W.write<int32_t>(YamlSec.Flags);
W.OS.write_zeros(4);
} else {
W.write<uint32_t>(SectionAddress); // Physical address
W.write<uint32_t>(SectionAddress); // Virtual address
W.write<uint32_t>(YamlSec.Size ? YamlSec.Size : DerivedSec.Size);
W.write<uint32_t>(YamlSec.FileOffsetToData ? YamlSec.FileOffsetToData
: DerivedSec.FileOffsetToData);
W.write<uint32_t>(YamlSec.FileOffsetToRelocations
? YamlSec.FileOffsetToRelocations
: DerivedSec.FileOffsetToRelocations);
W.write<uint32_t>(YamlSec.FileOffsetToLineNumbers);
W.write<uint16_t>(YamlSec.NumberOfRelocations
? YamlSec.NumberOfRelocations
: DerivedSec.NumberOfRelocations);
W.write<uint16_t>(YamlSec.NumberOfLineNumbers);
W.write<int32_t>(YamlSec.Flags);
}
}
}
bool XCOFFWriter::writeSectionData() {
for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
XCOFFYAML::Section YamlSec = Obj.Sections[I];
if (YamlSec.SectionData.binary_size()) {
// Fill the padding size with zeros.
int64_t PaddingSize =
InitSections[I].FileOffsetToData - (W.OS.tell() - StartOffset);
if (PaddingSize < 0) {
ErrHandler("redundant data was written before section data");
return false;
}
W.OS.write_zeros(PaddingSize);
YamlSec.SectionData.writeAsBinary(W.OS);
}
}
return true;
}
bool XCOFFWriter::writeRelocations() {
for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) {
XCOFFYAML::Section YamlSec = Obj.Sections[I];
if (!YamlSec.Relocations.empty()) {
int64_t PaddingSize =
InitSections[I].FileOffsetToRelocations - (W.OS.tell() - StartOffset);
if (PaddingSize < 0) {
ErrHandler("redundant data was written before relocations");
return false;
}
W.OS.write_zeros(PaddingSize);
for (const XCOFFYAML::Relocation &YamlRel : YamlSec.Relocations) {
if (Is64Bit)
W.write<uint64_t>(YamlRel.VirtualAddress);
else
W.write<uint32_t>(YamlRel.VirtualAddress);
W.write<uint32_t>(YamlRel.SymbolIndex);
W.write<uint8_t>(YamlRel.Info);
W.write<uint8_t>(YamlRel.Type);
}
}
}
return true;
}
void XCOFFWriter::writeAuxSymbol(const XCOFFYAML::CsectAuxEnt &AuxSym) {
if (Is64Bit) {
W.write<uint32_t>(AuxSym.SectionOrLengthLo.value_or(0));
W.write<uint32_t>(AuxSym.ParameterHashIndex.value_or(0));
W.write<uint16_t>(AuxSym.TypeChkSectNum.value_or(0));
W.write<uint8_t>(AuxSym.SymbolAlignmentAndType.value_or(0));
W.write<uint8_t>(AuxSym.StorageMappingClass.value_or(XCOFF::XMC_PR));
W.write<uint32_t>(AuxSym.SectionOrLengthHi.value_or(0));
W.write<uint8_t>(0);
W.write<uint8_t>(XCOFF::AUX_CSECT);
} else {
W.write<uint32_t>(AuxSym.SectionOrLength.value_or(0));
W.write<uint32_t>(AuxSym.ParameterHashIndex.value_or(0));
W.write<uint16_t>(AuxSym.TypeChkSectNum.value_or(0));
W.write<uint8_t>(AuxSym.SymbolAlignmentAndType.value_or(0));
W.write<uint8_t>(AuxSym.StorageMappingClass.value_or(XCOFF::XMC_PR));
W.write<uint32_t>(AuxSym.StabInfoIndex.value_or(0));
W.write<uint16_t>(AuxSym.StabSectNum.value_or(0));
}
}
void XCOFFWriter::writeAuxSymbol(const XCOFFYAML::ExcpetionAuxEnt &AuxSym) {
assert(Is64Bit && "can't write the exception auxiliary symbol for XCOFF32");
W.write<uint64_t>(AuxSym.OffsetToExceptionTbl.value_or(0));
W.write<uint32_t>(AuxSym.SizeOfFunction.value_or(0));
W.write<uint32_t>(AuxSym.SymIdxOfNextBeyond.value_or(0));
W.write<uint8_t>(0);
W.write<uint8_t>(XCOFF::AUX_EXCEPT);
}
void XCOFFWriter::writeAuxSymbol(const XCOFFYAML::FunctionAuxEnt &AuxSym) {
if (Is64Bit) {
W.write<uint64_t>(AuxSym.PtrToLineNum.value_or(0));
W.write<uint32_t>(AuxSym.SizeOfFunction.value_or(0));
W.write<uint32_t>(AuxSym.SymIdxOfNextBeyond.value_or(0));
W.write<uint8_t>(0);
W.write<uint8_t>(XCOFF::AUX_FCN);
} else {
W.write<uint32_t>(AuxSym.OffsetToExceptionTbl.value_or(0));
W.write<uint32_t>(AuxSym.SizeOfFunction.value_or(0));
W.write<uint32_t>(AuxSym.PtrToLineNum.value_or(0));
W.write<uint32_t>(AuxSym.SymIdxOfNextBeyond.value_or(0));
W.OS.write_zeros(2);
}
}
void XCOFFWriter::writeAuxSymbol(const XCOFFYAML::FileAuxEnt &AuxSym) {
StringRef FileName = AuxSym.FileNameOrString.value_or("");
if (nameShouldBeInStringTable(FileName)) {
W.write<int32_t>(0);
W.write<uint32_t>(StrTblBuilder.getOffset(FileName));
} else {
writeName(FileName, W);
}
W.OS.write_zeros(XCOFF::FileNamePadSize);
W.write<uint8_t>(AuxSym.FileStringType.value_or(XCOFF::XFT_FN));
if (Is64Bit) {
W.OS.write_zeros(2);
W.write<uint8_t>(XCOFF::AUX_FILE);
} else {
W.OS.write_zeros(3);
}
}
void XCOFFWriter::writeAuxSymbol(const XCOFFYAML::BlockAuxEnt &AuxSym) {
if (Is64Bit) {
W.write<uint32_t>(AuxSym.LineNum.value_or(0));
W.OS.write_zeros(13);
W.write<uint8_t>(XCOFF::AUX_SYM);
} else {
W.OS.write_zeros(2);
W.write<uint16_t>(AuxSym.LineNumHi.value_or(0));
W.write<uint16_t>(AuxSym.LineNumLo.value_or(0));
W.OS.write_zeros(12);
}
}
void XCOFFWriter::writeAuxSymbol(const XCOFFYAML::SectAuxEntForDWARF &AuxSym) {
if (Is64Bit) {
W.write<uint64_t>(AuxSym.LengthOfSectionPortion.value_or(0));
W.write<uint64_t>(AuxSym.NumberOfRelocEnt.value_or(0));
W.write<uint8_t>(0);
W.write<uint8_t>(XCOFF::AUX_SECT);
} else {
W.write<uint32_t>(AuxSym.LengthOfSectionPortion.value_or(0));
W.OS.write_zeros(4);
W.write<uint32_t>(AuxSym.NumberOfRelocEnt.value_or(0));
W.OS.write_zeros(6);
}
}
void XCOFFWriter::writeAuxSymbol(const XCOFFYAML::SectAuxEntForStat &AuxSym) {
assert(!Is64Bit && "can't write the stat auxiliary symbol for XCOFF64");
W.write<uint32_t>(AuxSym.SectionLength.value_or(0));
W.write<uint16_t>(AuxSym.NumberOfRelocEnt.value_or(0));
W.write<uint16_t>(AuxSym.NumberOfLineNum.value_or(0));
W.OS.write_zeros(10);
}
void XCOFFWriter::writeAuxSymbol(
const std::unique_ptr<XCOFFYAML::AuxSymbolEnt> &AuxSym) {
if (auto AS = dyn_cast<XCOFFYAML::CsectAuxEnt>(AuxSym.get()))
writeAuxSymbol(*AS);
else if (auto AS = dyn_cast<XCOFFYAML::FunctionAuxEnt>(AuxSym.get()))
writeAuxSymbol(*AS);
else if (auto AS = dyn_cast<XCOFFYAML::ExcpetionAuxEnt>(AuxSym.get()))
writeAuxSymbol(*AS);
else if (auto AS = dyn_cast<XCOFFYAML::FileAuxEnt>(AuxSym.get()))
writeAuxSymbol(*AS);
else if (auto AS = dyn_cast<XCOFFYAML::BlockAuxEnt>(AuxSym.get()))
writeAuxSymbol(*AS);
else if (auto AS = dyn_cast<XCOFFYAML::SectAuxEntForDWARF>(AuxSym.get()))
writeAuxSymbol(*AS);
else if (auto AS = dyn_cast<XCOFFYAML::SectAuxEntForStat>(AuxSym.get()))
writeAuxSymbol(*AS);
else
llvm_unreachable("unknown auxiliary symbol type");
}
bool XCOFFWriter::writeSymbols() {
int64_t PaddingSize =
(uint64_t)InitFileHdr.SymbolTableOffset - (W.OS.tell() - StartOffset);
if (PaddingSize < 0) {
ErrHandler("redundant data was written before symbols");
return false;
}
W.OS.write_zeros(PaddingSize);
for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
if (Is64Bit) {
W.write<uint64_t>(YamlSym.Value);
W.write<uint32_t>(StrTblBuilder.getOffset(YamlSym.SymbolName));
} else {
if (nameShouldBeInStringTable(YamlSym.SymbolName)) {
// For XCOFF32: A value of 0 indicates that the symbol name is in the
// string table.
W.write<int32_t>(0);
W.write<uint32_t>(StrTblBuilder.getOffset(YamlSym.SymbolName));
} else {
writeName(YamlSym.SymbolName, W);
}
W.write<uint32_t>(YamlSym.Value);
}
if (YamlSym.SectionName) {
if (!SectionIndexMap.count(*YamlSym.SectionName)) {
ErrHandler("the SectionName " + *YamlSym.SectionName +
" specified in the symbol does not exist");
return false;
}
if (YamlSym.SectionIndex &&
SectionIndexMap[*YamlSym.SectionName] != *YamlSym.SectionIndex) {
ErrHandler("the SectionName " + *YamlSym.SectionName +
" and the SectionIndex (" + Twine(*YamlSym.SectionIndex) +
") refer to different sections");
return false;
}
W.write<int16_t>(SectionIndexMap[*YamlSym.SectionName]);
} else {
W.write<int16_t>(YamlSym.SectionIndex ? *YamlSym.SectionIndex : 0);
}
W.write<uint16_t>(YamlSym.Type);
W.write<uint8_t>(YamlSym.StorageClass);
uint8_t NumOfAuxSym = YamlSym.NumberOfAuxEntries.value_or(0);
W.write<uint8_t>(NumOfAuxSym);
if (!NumOfAuxSym && !YamlSym.AuxEntries.size())
continue;
// Now write auxiliary entries.
if (!YamlSym.AuxEntries.size()) {
W.OS.write_zeros(XCOFF::SymbolTableEntrySize * NumOfAuxSym);
} else {
for (const std::unique_ptr<XCOFFYAML::AuxSymbolEnt> &AuxSym :
YamlSym.AuxEntries) {
writeAuxSymbol(AuxSym);
}
// Pad with zeros.
if (NumOfAuxSym > YamlSym.AuxEntries.size())
W.OS.write_zeros(XCOFF::SymbolTableEntrySize *
(NumOfAuxSym - YamlSym.AuxEntries.size()));
}
}
return true;
}
void XCOFFWriter::writeStringTable() {
if (Obj.StrTbl.RawContent) {
Obj.StrTbl.RawContent->writeAsBinary(W.OS);
if (Obj.StrTbl.ContentSize) {
assert(*Obj.StrTbl.ContentSize >= Obj.StrTbl.RawContent->binary_size() &&
"Specified ContentSize is less than the RawContent size.");
W.OS.write_zeros(*Obj.StrTbl.ContentSize -
Obj.StrTbl.RawContent->binary_size());
}
return;
}
size_t StrTblBuilderSize = StrTblBuilder.getSize();
// If neither Length nor ContentSize is specified, write the StrTblBuilder
// directly, which contains the auto-generated Length value.
if (!Obj.StrTbl.Length && !Obj.StrTbl.ContentSize) {
if (StrTblBuilderSize <= 4)
return;
StrTblBuilder.write(W.OS);
return;
}
// Serialize the string table's content to a temporary buffer.
std::unique_ptr<WritableMemoryBuffer> Buf =
WritableMemoryBuffer::getNewMemBuffer(StrTblBuilderSize);
uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart());
StrTblBuilder.write(Ptr);
// Replace the first 4 bytes, which contain the auto-generated Length value,
// with the specified value.
memset(Ptr, 0, 4);
support::endian::write32be(Ptr, Obj.StrTbl.Length ? *Obj.StrTbl.Length
: *Obj.StrTbl.ContentSize);
// Copy the buffer content to the actual output stream.
W.OS.write(Buf->getBufferStart(), Buf->getBufferSize());
// Add zeros as padding after strings.
if (Obj.StrTbl.ContentSize) {
assert(*Obj.StrTbl.ContentSize >= StrTblBuilderSize &&
"Specified ContentSize is less than the StringTableBuilder size.");
W.OS.write_zeros(*Obj.StrTbl.ContentSize - StrTblBuilderSize);
}
}
bool XCOFFWriter::writeXCOFF() {
if (!assignAddressesAndIndices())
return false;
StartOffset = W.OS.tell();
writeFileHeader();
if (Obj.AuxHeader)
writeAuxFileHeader();
if (!Obj.Sections.empty()) {
writeSectionHeader();
if (!writeSectionData())
return false;
if (!writeRelocations())
return false;
}
if (!Obj.Symbols.empty() && !writeSymbols())
return false;
writeStringTable();
return true;
}
} // end anonymous namespace
namespace llvm {
namespace yaml {
bool yaml2xcoff(XCOFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH) {
XCOFFWriter Writer(Doc, Out, EH);
return Writer.writeXCOFF();
}
} // namespace yaml
} // namespace llvm