diff --git a/llvm/include/llvm/ObjectYAML/ObjectYAML.h b/llvm/include/llvm/ObjectYAML/ObjectYAML.h --- a/llvm/include/llvm/ObjectYAML/ObjectYAML.h +++ b/llvm/include/llvm/ObjectYAML/ObjectYAML.h @@ -15,6 +15,7 @@ #include "llvm/ObjectYAML/MachOYAML.h" #include "llvm/ObjectYAML/MinidumpYAML.h" #include "llvm/ObjectYAML/WasmYAML.h" +#include "llvm/ObjectYAML/XCOFFYAML.h" #include "llvm/Support/YAMLTraits.h" #include @@ -31,6 +32,7 @@ std::unique_ptr FatMachO; std::unique_ptr Minidump; std::unique_ptr Wasm; + std::unique_ptr Xcoff; }; template <> struct MappingTraits { diff --git a/llvm/include/llvm/ObjectYAML/XCOFFYAML.h b/llvm/include/llvm/ObjectYAML/XCOFFYAML.h --- a/llvm/include/llvm/ObjectYAML/XCOFFYAML.h +++ b/llvm/include/llvm/ObjectYAML/XCOFFYAML.h @@ -29,6 +29,27 @@ llvm::yaml::Hex16 Flags; }; +struct Relocation { + uint32_t VirtualAddress; + uint32_t SymbolIndex; + uint8_t Size; + uint8_t Type; +}; + +struct Section { + StringRef SectionName; + uint32_t Address; + uint32_t Size; + uint32_t FileOffsetToData; + uint32_t FileOffsetToRelocations; + uint32_t FileOffsetToLineNumbers; // Line number pointer. Not supported yet. + uint16_t NumberOfRelocations; + uint16_t NumberOfLineNumbers; // Line number counts. Not supported yet. + int32_t Flags; + yaml::BinaryRef SectionData; + std::vector Relocations; +}; + struct Symbol { StringRef SymbolName; llvm::yaml::Hex32 Value; // Symbol value; storage class-dependent. @@ -40,12 +61,17 @@ struct Object { FileHeader Header; + std::vector
Sections; std::vector Symbols; Object(); }; } // namespace XCOFFYAML } // namespace llvm + LLVM_YAML_IS_SEQUENCE_VECTOR(XCOFFYAML::Symbol) +LLVM_YAML_IS_SEQUENCE_VECTOR(XCOFFYAML::Relocation) +LLVM_YAML_IS_SEQUENCE_VECTOR(XCOFFYAML::Section) + namespace llvm { namespace yaml { @@ -61,6 +87,14 @@ static void mapping(IO &IO, XCOFFYAML::Object &Obj); }; +template <> struct MappingTraits { + static void mapping(IO &IO, XCOFFYAML::Relocation &R); +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, XCOFFYAML::Section &S); +}; + template <> struct MappingTraits { static void mapping(IO &IO, XCOFFYAML::Symbol &S); }; diff --git a/llvm/include/llvm/ObjectYAML/yaml2obj.h b/llvm/include/llvm/ObjectYAML/yaml2obj.h --- a/llvm/include/llvm/ObjectYAML/yaml2obj.h +++ b/llvm/include/llvm/ObjectYAML/yaml2obj.h @@ -40,6 +40,10 @@ struct Object; } +namespace XCOFFYAML { +struct Object; +} + namespace ArchYAML { struct Archive; } @@ -58,6 +62,7 @@ bool yaml2minidump(MinidumpYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH); bool yaml2wasm(WasmYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH); +bool yaml2xcoff(XCOFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH); bool convertYAML(Input &YIn, raw_ostream &Out, ErrorHandler ErrHandler, unsigned DocNum = 1, uint64_t MaxSize = UINT64_MAX); diff --git a/llvm/lib/ObjectYAML/CMakeLists.txt b/llvm/lib/ObjectYAML/CMakeLists.txt --- a/llvm/lib/ObjectYAML/CMakeLists.txt +++ b/llvm/lib/ObjectYAML/CMakeLists.txt @@ -18,6 +18,7 @@ MinidumpYAML.cpp WasmEmitter.cpp WasmYAML.cpp + XCOFFEmitter.cpp XCOFFYAML.cpp YAML.cpp yaml2obj.cpp diff --git a/llvm/lib/ObjectYAML/ObjectYAML.cpp b/llvm/lib/ObjectYAML/ObjectYAML.cpp --- a/llvm/lib/ObjectYAML/ObjectYAML.cpp +++ b/llvm/lib/ObjectYAML/ObjectYAML.cpp @@ -59,6 +59,9 @@ } else if (IO.mapTag("!WASM")) { ObjectFile.Wasm.reset(new WasmYAML::Object()); MappingTraits::mapping(IO, *ObjectFile.Wasm); + } else if (IO.mapTag("!XCOFF")) { + ObjectFile.Xcoff.reset(new XCOFFYAML::Object()); + MappingTraits::mapping(IO, *ObjectFile.Xcoff); } else if (const Node *N = In.getCurrentNode()) { if (N->getRawTag().empty()) IO.setError("YAML Object File missing document type tag!"); diff --git a/llvm/lib/ObjectYAML/XCOFFEmitter.cpp b/llvm/lib/ObjectYAML/XCOFFEmitter.cpp --- a/llvm/lib/ObjectYAML/XCOFFEmitter.cpp +++ b/llvm/lib/ObjectYAML/XCOFFEmitter.cpp @@ -0,0 +1,277 @@ +//===- 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/StringSwitch.h" +#include "llvm/BinaryFormat/XCOFF.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" + +// Only 32-bit supported. + +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) {} + bool writeXCOFF(); + +private: + bool assignAddressesAndIndices(); + void writeFileHeader(); + void writeSectionHeader(); + bool writeSectionData(); + bool writeRelocations(); + bool writeSymbols(); + + XCOFFYAML::Object &Obj; + support::endian::Writer W; + yaml::ErrorHandler ErrHandler; + uint64_t StartOffset; + DenseMap SectionIndexMap; +}; + +bool XCOFFWriter::assignAddressesAndIndices() { + int32_t SectionIndex = 0; + uint32_t SectionCount = 0; + for (auto &YamlSec : Obj.Sections) { + if (YamlSec.Address % DefaultSectionAlign != 0) { + ErrHandler("The address of section is not aligned."); + return false; + } + // Assign the section index for each section. + SectionIndexMap[YamlSec.SectionName.data()] = + StringSwitch(YamlSec.SectionName) + .Case("N_DEBUG", XCOFF::N_DEBUG) + .Case("N_ABS", XCOFF::N_ABS) + .Case("N_UNDEF", XCOFF::N_UNDEF) + .Default(++SectionIndex); + + if (SectionIndex > MaxSectionIndex) { + ErrHandler("Section index overflow!"); + return false; + } + ++SectionCount; + } + if (SectionCount != Obj.Header.NumberOfSections) { + ErrHandler("Number Of sections is not correct!"); + return false; + } + // Calculate FileOffsetToData for section. + uint64_t CurrentOffset = + sizeof(XCOFF::FileHeader32) /* TODO + auxiliaryHeaderSize()*/ + + SectionCount * sizeof(XCOFF::SectionHeader32); + for (auto &YamlSec : Obj.Sections) { + YamlSec.FileOffsetToData = CurrentOffset; + CurrentOffset += YamlSec.Size; + if (CurrentOffset > MaxRawDataSize) { + ErrHandler("Section data overflow!"); + return false; + } + } + // Calculate FileOffsetToRelocations for section. + for (auto &YamlSec : Obj.Sections) { + YamlSec.FileOffsetToRelocations = CurrentOffset; + CurrentOffset += + YamlSec.NumberOfRelocations * XCOFF::RelocationSerializationSize32; + if (CurrentOffset > MaxRawDataSize) { + ErrHandler("Relocation data overflow!"); + return false; + } + } + // TODO: Calculate FileOffsetToLineNumbers when line number supported. + // Calculate SymbolTableOffset for file header. + if (Obj.Header.NumberOfSymTableEntries) { + Obj.Header.SymbolTableOffset = CurrentOffset; + CurrentOffset += + Obj.Header.NumberOfSymTableEntries * XCOFF::SymbolTableEntrySize; + if (CurrentOffset > MaxRawDataSize) { + ErrHandler("Symbol data overflow!"); + return false; + } + } + return true; +} + +void XCOFFWriter::writeFileHeader() { + // Target machine. + W.write(0x01DF); + // Number of sections. + W.write(Obj.Header.NumberOfSections); + // Time and date of file creation. + W.write(Obj.Header.TimeStamp); + // Byte offset to symbol table start. + W.write(Obj.Header.SymbolTableOffset); + // Number of entries in symbol table. + W.write(Obj.Header.NumberOfSymTableEntries); + // Number of bytes in optional header. + W.write(Obj.Header.AuxHeaderSize); + // Flags. + W.write(Obj.Header.Flags); +} + +void XCOFFWriter::writeSectionHeader() { + for (auto &YamlSec : Obj.Sections) { + // Section name. + W.OS.write(YamlSec.SectionName.data(), sizeof(XCOFF::NameSize)); + // Physical Address. + W.write(YamlSec.Address); + // Virtual address (same as physical address). + W.write(YamlSec.Address); + // Section size. + W.write(YamlSec.Size); + // Offset in file to raw data for section. + W.write(YamlSec.FileOffsetToData); + // Offset in file to relocation entries for section. + W.write(YamlSec.FileOffsetToRelocations); + // Offset in file to line number entries for section. + W.write(YamlSec.FileOffsetToLineNumbers); + // Number of relocation entries. + W.write(YamlSec.NumberOfRelocations); + // Number of line number entries. + W.write(YamlSec.NumberOfLineNumbers); + // Flags to define the section type. + W.write(YamlSec.Flags); + } +} + +bool XCOFFWriter::writeSectionData() { + for (auto &YamlSec : Obj.Sections) { + // Fill the padding size with zeros. + int32_t PaddingSize = + YamlSec.FileOffsetToData - (W.OS.tell() - StartOffset); + if (PaddingSize < 0) { + ErrHandler("Wrong data was wrote somewhere!"); + return false; + } + W.OS.write_zeros(PaddingSize); + + // TODO: Write section data. + if (YamlSec.SectionData.binary_size()) + YamlSec.SectionData.writeAsBinary(W.OS); + } + return true; +} + +bool XCOFFWriter::writeRelocations() { + for (auto &YamlSec : Obj.Sections) { + // Fill the padding size with zeros. + int32_t PaddingSize = + YamlSec.FileOffsetToRelocations - (W.OS.tell() - StartOffset); + if (PaddingSize < 0) { + ErrHandler("Wrong data was wrote somewhere!"); + return false; + } + W.OS.write_zeros(PaddingSize); + for (auto &YamlRel : YamlSec.Relocations) { + W.write(YamlRel.VirtualAddress); + W.write(YamlRel.SymbolIndex); + W.write(YamlRel.Size); + W.write(YamlRel.Type); + } + } + return true; +} + +bool XCOFFWriter::writeSymbols() { + int32_t PaddingSize = + (uint64_t)Obj.Header.SymbolTableOffset - (W.OS.tell() - StartOffset); + if (PaddingSize < 0) { + ErrHandler("Wrong data was wrote somewhere!"); + return false; + } + W.OS.write_zeros(PaddingSize); + int32_t SymbolCount = 0; + for (auto &YamlSym : Obj.Symbols) { + // Symbol name. + W.OS.write(YamlSym.SymbolName.data(), sizeof(XCOFF::NameSize)); + // Symbol value; storage class-dependent. + W.write(YamlSym.Value); + // Section number of symbol. + W.write(SectionIndexMap[YamlSym.SectionName.data()]); + // Basic and derived type specification. + W.write(YamlSym.Type); + // Storage class of symbol. + W.write(YamlSym.StorageClass); + // Number of auxiliary entries. + W.write(1); + // Now output the auxiliary entry. + // 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.write(0); + W.write(0); + W.write(0); + // Symbol type. + W.write(0); + // Storage mapping class. + W.write(0); + // Reserved (x_stab). + W.write(0); + // Reserved (x_snstab). + W.write(0); + SymbolCount += 2; + } + if (SymbolCount != Obj.Header.NumberOfSymTableEntries) { + ErrHandler("Number of symbol entries is not correct!"); + return false; + } + return true; +} + +bool XCOFFWriter::writeXCOFF() { + // TODO: Add support for 64-bit. + if ((uint16_t)Obj.Header.Magic != 0x01DF) { + ErrHandler("Only 32-bit is supported!"); + return false; + } + bool Res = true; + Res &= assignAddressesAndIndices(); + StartOffset = W.OS.tell(); + // Write Filer Header. + writeFileHeader(); + // TODO: Add support for the auxiliary header. + // Write Section Header. + writeSectionHeader(); + // Write Section Data if any. + Res &= writeSectionData(); + // Write relocations if any. + Res &= writeRelocations(); + // Write symbol table. + Res &= writeSymbols(); + // TODO: Add support for the string table. + return Res; +} + +} // 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 diff --git a/llvm/lib/ObjectYAML/XCOFFYAML.cpp b/llvm/lib/ObjectYAML/XCOFFYAML.cpp --- a/llvm/lib/ObjectYAML/XCOFFYAML.cpp +++ b/llvm/lib/ObjectYAML/XCOFFYAML.cpp @@ -90,6 +90,27 @@ IO.mapRequired("Flags", FileHdr.Flags); } +void MappingTraits::mapping(IO &IO, XCOFFYAML::Relocation &R) { + IO.mapRequired("Address", R.VirtualAddress); + IO.mapRequired("Symbol", R.SymbolIndex); + IO.mapRequired("Size", R.Size); + IO.mapRequired("Type", R.Type); +} + +void MappingTraits::mapping(IO &IO, XCOFFYAML::Section &Sec) { + IO.mapRequired("Name", Sec.SectionName); + IO.mapRequired("Address", Sec.Address); + IO.mapRequired("Size", Sec.Size); + IO.mapOptional("FileOffsetToData", Sec.FileOffsetToData); + IO.mapOptional("FileOffsetToRelocations", Sec.FileOffsetToRelocations); + IO.mapOptional("FileOffsetToLineNumbers", Sec.FileOffsetToLineNumbers); + IO.mapOptional("NumberOfRelocations", Sec.NumberOfRelocations); + IO.mapRequired("NumberOfLineNumbers", Sec.NumberOfLineNumbers); + IO.mapRequired("Flags", Sec.Flags); + IO.mapOptional("SectionData", Sec.SectionData); + IO.mapOptional("Relocations", Sec.Relocations); +} + void MappingTraits::mapping(IO &IO, XCOFFYAML::Symbol &S) { IO.mapRequired("Name", S.SymbolName); IO.mapRequired("Value", S.Value); @@ -102,7 +123,8 @@ void MappingTraits::mapping(IO &IO, XCOFFYAML::Object &Obj) { IO.mapTag("!XCOFF", true); IO.mapRequired("FileHeader", Obj.Header); - IO.mapRequired("Symbols", Obj.Symbols); + IO.mapOptional("Sections", Obj.Sections); + IO.mapOptional("Symbols", Obj.Symbols); } } // namespace yaml diff --git a/llvm/lib/ObjectYAML/yaml2obj.cpp b/llvm/lib/ObjectYAML/yaml2obj.cpp --- a/llvm/lib/ObjectYAML/yaml2obj.cpp +++ b/llvm/lib/ObjectYAML/yaml2obj.cpp @@ -44,6 +44,8 @@ return yaml2minidump(*Doc.Minidump, Out, ErrHandler); if (Doc.Wasm) return yaml2wasm(*Doc.Wasm, Out, ErrHandler); + if (Doc.Xcoff) + return yaml2xcoff(*Doc.Xcoff, Out, ErrHandler); ErrHandler("unknown document type"); return false; diff --git a/llvm/test/tools/yaml2obj/xcoff-file.yaml b/llvm/test/tools/yaml2obj/xcoff-file.yaml --- a/llvm/test/tools/yaml2obj/xcoff-file.yaml +++ b/llvm/test/tools/yaml2obj/xcoff-file.yaml @@ -0,0 +1,59 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-readobj --headers %t | FileCheck %s --check-prefix=CHECK-HEADER +# RUN: llvm-readobj --section-headers %t | FileCheck %s --check-prefix=CHECK-SEC + +# CHECK-HEADER: FileHeader { +# CHECK-HEADER-NEXT: Magic: 0x1DF +# CHECK-HEADER-NEXT: NumberOfSections: 1 +# CHECK-HEADER-NEXT: TimeStamp: None (0x0) +# CHECK-HEADER-NEXT: SymbolTableOffset: 0x4A +# CHECK-HEADER-NEXT: SymbolTableEntries: 2 +# CHECK-HEADER-NEXT: OptionalHeaderSize: 0x0 +# CHECK-HEADER-NEXT: Flags: 0x0 +# CHECK-HEADER-NEXT: } + +# CHECK-SEC: Sections [ +# CHECK-SEC-NEXT: Section { +# CHECK-SEC-NEXT: Index: 1 +# CHECK-SEC-NEXT: Name: .data +# CHECK-SEC: PhysicalAddress: 0x50 +# CHECK-SEC-NEXT: VirtualAddress: 0x50 +# CHECK-SEC-NEXT: Size: 0x4 +# CHECK-SEC-NEXT: RawDataOffset: 0x3C +# CHECK-SEC-NEXT: RelocationPointer: 0x40 +# CHECK-SEC-NEXT: LineNumberPointer: 0x0 +# CHECK-SEC-NEXT: NumberOfRelocations: 1 +# CHECK-SEC-NEXT: NumberOfLineNumbers: 0 +# CHECK-SEC-NEXT: Type: 0x0 +# CHECK-SEC-NEXT: } +# CHECK-SEC-NEXT: ] + +--- !XCOFF +FileHeader: + MagicNumber: 0x1DF + NumberOfSections: 1 + CreationTime: 0 + OffsetToSymbolTable: 0x4A + EntriesInSymbolTable: 2 + AuxiliaryHeaderSize: 0 + Flags: 0x0 +Sections: + - Name: .data + Address: 80 + Size: 4 + NumberOfRelocations: 1 + NumberOfLineNumbers: 0 + Flags: 0x0 + SectionData: "1001" + Relocations: + - Address: 0x3A + Symbol: 1 + Size: 0x04 + Type: 0x02 +Symbols: + - Name: main + Value: 0xB4 + Section: .data + Type: 0x0 + StorageClass: C_EXT + NumberOfAuxEntries: 0 diff --git a/llvm/utils/gn/secondary/llvm/lib/ObjectYAML/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/ObjectYAML/BUILD.gn --- a/llvm/utils/gn/secondary/llvm/lib/ObjectYAML/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/lib/ObjectYAML/BUILD.gn @@ -26,6 +26,7 @@ "ObjectYAML.cpp", "WasmEmitter.cpp", "WasmYAML.cpp", + "XCOFFEmitter.cpp" "XCOFFYAML.cpp", "YAML.cpp", "yaml2obj.cpp",