diff options
Diffstat (limited to 'llvm/lib/ObjectYAML/XCOFFEmitter.cpp')
-rw-r--r-- | llvm/lib/ObjectYAML/XCOFFEmitter.cpp | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/llvm/lib/ObjectYAML/XCOFFEmitter.cpp b/llvm/lib/ObjectYAML/XCOFFEmitter.cpp new file mode 100644 index 000000000000..14fea5437a32 --- /dev/null +++ b/llvm/lib/ObjectYAML/XCOFFEmitter.cpp @@ -0,0 +1,339 @@ +//===- 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 |