blob: 14fea5437a32843a50e9238c43dc814cb76b2732 [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/raw_ostream.h"
#include "llvm/Support/LEB128.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),
Strings(StringTableBuilder::XCOFF) {
Is64Bit = Obj.Header.Magic == (llvm::yaml::Hex16)XCOFF::XCOFF64;
}
bool writeXCOFF();
private:
bool nameShouldBeInStringTable(StringRef SymbolName);
bool initFileHeader(uint64_t CurrentOffset);
bool initSectionHeader(uint64_t &CurrentOffset);
bool initRelocations(uint64_t &CurrentOffset);
bool assignAddressesAndIndices();
void writeFileHeader();
void writeSectionHeader();
bool writeSectionData();
bool writeRelocations();
bool writeSymbols();
XCOFFYAML::Object &Obj;
bool Is64Bit = false;
support::endian::Writer W;
yaml::ErrorHandler ErrHandler;
StringTableBuilder Strings;
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;
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) {
return SymbolName.size() > XCOFF::NameSize;
}
bool XCOFFWriter::initRelocations(uint64_t &CurrentOffset) {
for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) {
if (!InitSections[I].Relocations.empty()) {
InitSections[I].NumberOfRelocations = InitSections[I].Relocations.size();
InitSections[I].FileOffsetToRelocations = CurrentOffset;
CurrentOffset += InitSections[I].NumberOfRelocations *
XCOFF::RelocationSerializationSize32;
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::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 (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
// Add the number of auxiliary symbols to the total number.
InitFileHdr.NumberOfSymTableEntries += YamlSym.NumberOfAuxEntries;
if (nameShouldBeInStringTable(YamlSym.SymbolName))
Strings.add(YamlSym.SymbolName);
}
// Finalize the string table.
Strings.finalize();
// 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;
}
bool XCOFFWriter::assignAddressesAndIndices() {
Strings.clear();
uint64_t CurrentOffset =
XCOFF::FileHeaderSize32 /* TODO: + auxiliaryHeaderSize() */ +
InitSections.size() * XCOFF::SectionHeaderSize32;
// Calculate section header info.
if (!initSectionHeader(CurrentOffset))
return false;
// Calculate file header info.
return initFileHeader(CurrentOffset);
}
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);
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>(Obj.Header.AuxHeaderSize);
W.write<uint16_t>(Obj.Header.Flags);
}
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.
uint32_t SectionAddress =
YamlSec.Address ? YamlSec.Address : DerivedSec.Address;
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;
}
if (PaddingSize > 0)
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;
}
if (PaddingSize > 0)
W.OS.write_zeros(PaddingSize);
for (const XCOFFYAML::Relocation &YamlRel : YamlSec.Relocations) {
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;
}
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;
}
if (PaddingSize > 0)
W.OS.write_zeros(PaddingSize);
for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) {
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>(Strings.getOffset(YamlSym.SymbolName));
} else {
writeName(YamlSym.SymbolName, W);
}
W.write<uint32_t>(YamlSym.Value);
W.write<int16_t>(
YamlSym.SectionName.size() ? SectionIndexMap[YamlSym.SectionName] : 0);
W.write<uint16_t>(YamlSym.Type);
W.write<uint8_t>(YamlSym.StorageClass);
W.write<uint8_t>(YamlSym.NumberOfAuxEntries);
// Now output the auxiliary entry.
for (uint8_t I = 0, E = YamlSym.NumberOfAuxEntries; I < E; ++I) {
// TODO: Auxiliary entry is not supported yet.
// The auxiliary entries for a symbol follow its symbol table entry. The
// length of each auxiliary entry is the same as a symbol table entry (18
// bytes). The format and quantity of auxiliary entries depend on the
// storage class (n_sclass) and type (n_type) of the symbol table entry.
W.OS.write_zeros(18);
}
}
return true;
}
bool XCOFFWriter::writeXCOFF() {
if (Is64Bit) {
ErrHandler("only XCOFF32 is currently supported");
return false;
}
if (!assignAddressesAndIndices())
return false;
StartOffset = W.OS.tell();
writeFileHeader();
if (!Obj.Sections.empty()) {
writeSectionHeader();
if (!writeSectionData())
return false;
if (!writeRelocations())
return false;
}
if (!Obj.Symbols.empty() && !writeSymbols())
return false;
// Write the string table.
if (Strings.getSize() > 4)
Strings.write(W.OS);
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