diff options
| author | Yusra Syeda <99052248+ysyeda@users.noreply.github.com> | 2023-12-14 09:57:03 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-12-14 09:57:03 -0500 |
| commit | fd6e19cdc399fbda5a63a07ba4d5a0f6bc4b12f5 (patch) | |
| tree | 23f495761f4fe847465b73f516fb7e5fa70b720f /llvm/lib/ObjectYAML | |
| parent | 01061ed370448a758ccab89c2fcc31f795d4fe8a (diff) | |
| download | llvm-fd6e19cdc399fbda5a63a07ba4d5a0f6bc4b12f5.tar.gz llvm-fd6e19cdc399fbda5a63a07ba4d5a0f6bc4b12f5.tar.bz2 llvm-fd6e19cdc399fbda5a63a07ba4d5a0f6bc4b12f5.zip | |
[SystemZ][z/OS] yaml2obj for header and end records (#73859)
This PR implements part 1 of yaml2obj for the GOFF Object File Format.
It adds support for the header and end records.
---------
Co-authored-by: Yusra Syeda <yusra.syeda@ibm.com>
Diffstat (limited to 'llvm/lib/ObjectYAML')
| -rw-r--r-- | llvm/lib/ObjectYAML/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | llvm/lib/ObjectYAML/GOFFEmitter.cpp | 282 | ||||
| -rw-r--r-- | llvm/lib/ObjectYAML/GOFFYAML.cpp | 46 | ||||
| -rw-r--r-- | llvm/lib/ObjectYAML/ObjectYAML.cpp | 5 | ||||
| -rw-r--r-- | llvm/lib/ObjectYAML/yaml2obj.cpp | 2 |
5 files changed, 337 insertions, 0 deletions
diff --git a/llvm/lib/ObjectYAML/CMakeLists.txt b/llvm/lib/ObjectYAML/CMakeLists.txt index c081009653d4..b36974d47d9f 100644 --- a/llvm/lib/ObjectYAML/CMakeLists.txt +++ b/llvm/lib/ObjectYAML/CMakeLists.txt @@ -13,6 +13,8 @@ add_llvm_component_library(LLVMObjectYAML DXContainerYAML.cpp ELFEmitter.cpp ELFYAML.cpp + GOFFEmitter.cpp + GOFFYAML.cpp MachOEmitter.cpp MachOYAML.cpp ObjectYAML.cpp diff --git a/llvm/lib/ObjectYAML/GOFFEmitter.cpp b/llvm/lib/ObjectYAML/GOFFEmitter.cpp new file mode 100644 index 000000000000..345904407e1d --- /dev/null +++ b/llvm/lib/ObjectYAML/GOFFEmitter.cpp @@ -0,0 +1,282 @@ +//===- yaml2goff - Convert YAML to a GOFF 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 GOFF component of yaml2obj. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/IndexedMap.h" +#include "llvm/ObjectYAML/ObjectYAML.h" +#include "llvm/ObjectYAML/yaml2obj.h" +#include "llvm/Support/ConvertEBCDIC.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +namespace { + +// Common flag values on records. +enum { + // Flag: This record is continued. + Rec_Continued = 1, + + // Flag: This record is a continuation. + Rec_Continuation = 1 << (8 - 6 - 1), +}; + +template <typename ValueType> struct BinaryBeImpl { + ValueType Value; + BinaryBeImpl(ValueType V) : Value(V) {} +}; + +template <typename ValueType> +raw_ostream &operator<<(raw_ostream &OS, const BinaryBeImpl<ValueType> &BBE) { + char Buffer[sizeof(BBE.Value)]; + support::endian::write<ValueType, llvm::endianness::big, support::unaligned>( + Buffer, BBE.Value); + OS.write(Buffer, sizeof(BBE.Value)); + return OS; +} + +template <typename ValueType> BinaryBeImpl<ValueType> binaryBe(ValueType V) { + return BinaryBeImpl<ValueType>(V); +} + +struct ZerosImpl { + size_t NumBytes; +}; + +raw_ostream &operator<<(raw_ostream &OS, const ZerosImpl &Z) { + OS.write_zeros(Z.NumBytes); + return OS; +} + +ZerosImpl zeros(const size_t NumBytes) { return ZerosImpl{NumBytes}; } + +// The GOFFOstream is responsible to write the data into the fixed physical +// records of the format. A user of this class announces the start of a new +// logical record and the size of its payload. While writing the payload, the +// physical records are created for the data. Possible fill bytes at the end of +// a physical record are written automatically. +class GOFFOstream : public raw_ostream { +public: + explicit GOFFOstream(raw_ostream &OS) + : OS(OS), LogicalRecords(0), RemainingSize(0), NewLogicalRecord(false) { + SetBufferSize(GOFF::PayloadLength); + } + + ~GOFFOstream() { finalize(); } + + void makeNewRecord(GOFF::RecordType Type, size_t Size) { + fillRecord(); + CurrentType = Type; + RemainingSize = Size; + if (size_t Gap = (RemainingSize % GOFF::PayloadLength)) + RemainingSize += GOFF::PayloadLength - Gap; + NewLogicalRecord = true; + ++LogicalRecords; + } + + void finalize() { fillRecord(); } + + uint32_t logicalRecords() { return LogicalRecords; } + +private: + // The underlying raw_ostream. + raw_ostream &OS; + + // The number of logical records emitted so far. + uint32_t LogicalRecords; + + // The remaining size of this logical record, including fill bytes. + size_t RemainingSize; + + // The type of the current (logical) record. + GOFF::RecordType CurrentType; + + // Signals start of new record. + bool NewLogicalRecord; + + // Return the number of bytes left to write until next physical record. + // Please note that we maintain the total number of bytes left, not the + // written size. + size_t bytesToNextPhysicalRecord() { + size_t Bytes = RemainingSize % GOFF::PayloadLength; + return Bytes ? Bytes : GOFF::PayloadLength; + } + + // Write the record prefix of a physical record, using the current record + // type. + static void writeRecordPrefix(raw_ostream &OS, GOFF::RecordType Type, + size_t RemainingSize, + uint8_t Flags = Rec_Continuation) { + uint8_t TypeAndFlags = Flags | (Type << 4); + if (RemainingSize > GOFF::RecordLength) + TypeAndFlags |= Rec_Continued; + OS << binaryBe(static_cast<unsigned char>(GOFF::PTVPrefix)) + << binaryBe(static_cast<unsigned char>(TypeAndFlags)) + << binaryBe(static_cast<unsigned char>(0)); + } + + // Fill the last physical record of a logical record with zero bytes. + void fillRecord() { + assert((GetNumBytesInBuffer() <= RemainingSize) && + "More bytes in buffer than expected"); + size_t Remains = RemainingSize - GetNumBytesInBuffer(); + if (Remains) { + assert((Remains < GOFF::RecordLength) && + "Attempting to fill more than one physical record"); + raw_ostream::write_zeros(Remains); + } + flush(); + assert(RemainingSize == 0 && "Not fully flushed"); + assert(GetNumBytesInBuffer() == 0 && "Buffer not fully empty"); + } + + // See raw_ostream::write_impl. + void write_impl(const char *Ptr, size_t Size) override { + assert((RemainingSize >= Size) && "Attempt to write too much data"); + assert(RemainingSize && "Logical record overflow"); + if (!(RemainingSize % GOFF::PayloadLength)) { + writeRecordPrefix(OS, CurrentType, RemainingSize, + NewLogicalRecord ? 0 : Rec_Continuation); + NewLogicalRecord = false; + } + assert(!NewLogicalRecord && + "New logical record not on physical record boundary"); + + size_t Idx = 0; + while (Size > 0) { + size_t BytesToWrite = bytesToNextPhysicalRecord(); + if (BytesToWrite > Size) + BytesToWrite = Size; + OS.write(Ptr + Idx, BytesToWrite); + Idx += BytesToWrite; + Size -= BytesToWrite; + RemainingSize -= BytesToWrite; + if (Size) { + writeRecordPrefix(OS, CurrentType, RemainingSize); + } + } + } + + // Return the current position within the stream, not counting the bytes + // currently in the buffer. + uint64_t current_pos() const override { return OS.tell(); } +}; + +class GOFFState { + void writeHeader(GOFFYAML::FileHeader &FileHdr); + void writeEnd(); + + void reportError(const Twine &Msg) { + ErrHandler(Msg); + HasError = true; + } + + GOFFState(raw_ostream &OS, GOFFYAML::Object &Doc, + yaml::ErrorHandler ErrHandler) + : GW(OS), Doc(Doc), ErrHandler(ErrHandler), HasError(false) {} + + ~GOFFState() { GW.finalize(); } + + bool writeObject(); + +public: + static bool writeGOFF(raw_ostream &OS, GOFFYAML::Object &Doc, + yaml::ErrorHandler ErrHandler); + +private: + GOFFOstream GW; + GOFFYAML::Object &Doc; + yaml::ErrorHandler ErrHandler; + bool HasError; +}; + +void GOFFState::writeHeader(GOFFYAML::FileHeader &FileHdr) { + SmallString<16> CCSIDName; + if (std::error_code EC = + ConverterEBCDIC::convertToEBCDIC(FileHdr.CharacterSetName, CCSIDName)) + reportError("Conversion error on " + FileHdr.CharacterSetName); + if (CCSIDName.size() > 16) { + reportError("CharacterSetName too long"); + CCSIDName.resize(16); + } + SmallString<16> LangProd; + if (std::error_code EC = ConverterEBCDIC::convertToEBCDIC( + FileHdr.LanguageProductIdentifier, LangProd)) + reportError("Conversion error on " + FileHdr.LanguageProductIdentifier); + if (LangProd.size() > 16) { + reportError("LanguageProductIdentifier too long"); + LangProd.resize(16); + } + + GW.makeNewRecord(GOFF::RT_HDR, GOFF::PayloadLength); + GW << binaryBe(FileHdr.TargetEnvironment) // TargetEnvironment + << binaryBe(FileHdr.TargetOperatingSystem) // TargetOperatingSystem + << zeros(2) // Reserved + << binaryBe(FileHdr.CCSID) // CCSID + << CCSIDName // CharacterSetName + << zeros(16 - CCSIDName.size()) // Fill bytes + << LangProd // LanguageProductIdentifier + << zeros(16 - LangProd.size()) // Fill bytes + << binaryBe(FileHdr.ArchitectureLevel); // ArchitectureLevel + // The module propties are optional. Figure out if we need to write them. + uint16_t ModPropLen = 0; + if (FileHdr.TargetSoftwareEnvironment) + ModPropLen = 3; + else if (FileHdr.InternalCCSID) + ModPropLen = 2; + if (ModPropLen) { + GW << binaryBe(ModPropLen) << zeros(6); + if (ModPropLen >= 2) + GW << binaryBe(FileHdr.InternalCCSID ? *FileHdr.InternalCCSID : 0); + if (ModPropLen >= 3) + GW << binaryBe(FileHdr.TargetSoftwareEnvironment + ? *FileHdr.TargetSoftwareEnvironment + : 0); + } +} + +void GOFFState::writeEnd() { + GW.makeNewRecord(GOFF::RT_END, GOFF::PayloadLength); + GW << binaryBe(uint8_t(0)) // No entry point + << binaryBe(uint8_t(0)) // No AMODE + << zeros(3) // Reserved + << binaryBe(GW.logicalRecords()); + // No entry point yet. Automatically fill remaining space with zero bytes. + GW.finalize(); +} + +bool GOFFState::writeObject() { + writeHeader(Doc.Header); + if (HasError) + return false; + writeEnd(); + return true; +} + +bool GOFFState::writeGOFF(raw_ostream &OS, GOFFYAML::Object &Doc, + yaml::ErrorHandler ErrHandler) { + GOFFState State(OS, Doc, ErrHandler); + return State.writeObject(); +} +} // namespace + +namespace llvm { +namespace yaml { + +bool yaml2goff(llvm::GOFFYAML::Object &Doc, raw_ostream &Out, + ErrorHandler ErrHandler) { + return GOFFState::writeGOFF(Out, Doc, ErrHandler); +} + +} // namespace yaml +} // namespace llvm diff --git a/llvm/lib/ObjectYAML/GOFFYAML.cpp b/llvm/lib/ObjectYAML/GOFFYAML.cpp new file mode 100644 index 000000000000..ae857980a521 --- /dev/null +++ b/llvm/lib/ObjectYAML/GOFFYAML.cpp @@ -0,0 +1,46 @@ +//===-- GOFFYAML.cpp - GOFF YAMLIO implementation ---------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines classes for handling the YAML representation of GOFF. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ObjectYAML/GOFFYAML.h" +#include "llvm/BinaryFormat/GOFF.h" +#include <string.h> + +namespace llvm { +namespace GOFFYAML { + +Object::Object() {} + +} // namespace GOFFYAML + +namespace yaml { + +void MappingTraits<GOFFYAML::FileHeader>::mapping( + IO &IO, GOFFYAML::FileHeader &FileHdr) { + IO.mapOptional("TargetEnvironment", FileHdr.TargetEnvironment, 0); + IO.mapOptional("TargetOperatingSystem", FileHdr.TargetOperatingSystem, 0); + IO.mapOptional("CCSID", FileHdr.CCSID, 0); + IO.mapOptional("CharacterSetName", FileHdr.CharacterSetName, ""); + IO.mapOptional("LanguageProductIdentifier", FileHdr.LanguageProductIdentifier, + ""); + IO.mapOptional("ArchitectureLevel", FileHdr.ArchitectureLevel, 1); + IO.mapOptional("InternalCCSID", FileHdr.InternalCCSID); + IO.mapOptional("TargetSoftwareEnvironment", + FileHdr.TargetSoftwareEnvironment); +} + +void MappingTraits<GOFFYAML::Object>::mapping(IO &IO, GOFFYAML::Object &Obj) { + IO.mapTag("!GOFF", true); + IO.mapRequired("FileHeader", Obj.Header); +} + +} // namespace yaml +} // namespace llvm diff --git a/llvm/lib/ObjectYAML/ObjectYAML.cpp b/llvm/lib/ObjectYAML/ObjectYAML.cpp index d57e5583016b..1815eaff8e36 100644 --- a/llvm/lib/ObjectYAML/ObjectYAML.cpp +++ b/llvm/lib/ObjectYAML/ObjectYAML.cpp @@ -26,6 +26,8 @@ void MappingTraits<YamlObjectFile>::mapping(IO &IO, MappingTraits<ELFYAML::Object>::mapping(IO, *ObjectFile.Elf); if (ObjectFile.Coff) MappingTraits<COFFYAML::Object>::mapping(IO, *ObjectFile.Coff); + if (ObjectFile.Goff) + MappingTraits<GOFFYAML::Object>::mapping(IO, *ObjectFile.Goff); if (ObjectFile.MachO) MappingTraits<MachOYAML::Object>::mapping(IO, *ObjectFile.MachO); if (ObjectFile.FatMachO) @@ -46,6 +48,9 @@ void MappingTraits<YamlObjectFile>::mapping(IO &IO, } else if (IO.mapTag("!COFF")) { ObjectFile.Coff.reset(new COFFYAML::Object()); MappingTraits<COFFYAML::Object>::mapping(IO, *ObjectFile.Coff); + } else if (IO.mapTag("!GOFF")) { + ObjectFile.Goff.reset(new GOFFYAML::Object()); + MappingTraits<GOFFYAML::Object>::mapping(IO, *ObjectFile.Goff); } else if (IO.mapTag("!mach-o")) { ObjectFile.MachO.reset(new MachOYAML::Object()); MappingTraits<MachOYAML::Object>::mapping(IO, *ObjectFile.MachO); diff --git a/llvm/lib/ObjectYAML/yaml2obj.cpp b/llvm/lib/ObjectYAML/yaml2obj.cpp index 06050e246fbf..b9a9ad639709 100644 --- a/llvm/lib/ObjectYAML/yaml2obj.cpp +++ b/llvm/lib/ObjectYAML/yaml2obj.cpp @@ -38,6 +38,8 @@ bool convertYAML(yaml::Input &YIn, raw_ostream &Out, ErrorHandler ErrHandler, return yaml2elf(*Doc.Elf, Out, ErrHandler, MaxSize); if (Doc.Coff) return yaml2coff(*Doc.Coff, Out, ErrHandler); + if (Doc.Goff) + return yaml2goff(*Doc.Goff, Out, ErrHandler); if (Doc.MachO || Doc.FatMachO) return yaml2macho(Doc, Out, ErrHandler); if (Doc.Minidump) |
