| //===------ goff2yaml.cpp - obj2yaml conversion tool ----------------------===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "obj2yaml.h" | |
| #include "llvm/Object/GOFF.h" | |
| #include "llvm/Object/GOFFObjectFile.h" | |
| #include "llvm/ObjectYAML/ObjectYAML.h" | |
| #include "llvm/Support/ConvertEBCDIC.h" | |
| using namespace llvm; | |
| static std::string getFixedLengthEBCDICString(DataExtractor &Data, | |
| DataExtractor::Cursor &C, | |
| uint64_t Length, | |
| StringRef TrimChars = {"\0", 1}) { | |
| StringRef FixedLenStr = Data.getBytes(C, Length); | |
| SmallString<16> Str; | |
| ConverterEBCDIC::convertToUTF8(FixedLenStr, Str); | |
| return Str.str().trim(TrimChars).str(); | |
| } | |
| class GOFFDumper { | |
| const object::GOFFObjectFile &Obj; | |
| GOFFYAML::Object YAMLObj; | |
| Error dumpHeader(ArrayRef<uint8_t> Data); | |
| Error dumpExternalSymbol(ArrayRef<uint8_t> Data); | |
| Error dumpText(ArrayRef<uint8_t> Data); | |
| Error dumpRelocationDirectory(ArrayRef<uint8_t> Data); | |
| Error dumpDeferredLength(ArrayRef<uint8_t> Data); | |
| Error dumpEnd(ArrayRef<uint8_t> Data); | |
| public: | |
| GOFFDumper(const object::GOFFObjectFile &Obj); | |
| Error dump(); | |
| GOFFYAML::Object &getYAMLObj(); | |
| }; | |
| GOFFDumper::GOFFDumper(const object::GOFFObjectFile &Obj) : Obj(Obj) {} | |
| Error GOFFDumper::dumpHeader(ArrayRef<uint8_t> Data) { | |
| DataExtractor DE(Data, false); | |
| DataExtractor::Cursor C(0); | |
| // Flattened data contains: PTV header (bytes 0-2) + bytes 3-60 (prefix) + | |
| // data from byte 60 onwards HDR data starts at byte 4 in the original record | |
| C.seek(4); // Skip PTV header and record type | |
| YAMLObj.Header.TargetEnvironment = DE.getU32(C); | |
| YAMLObj.Header.TargetOperatingSystem = DE.getU32(C); | |
| DE.skip(C, 2); | |
| YAMLObj.Header.CCSID = DE.getU16(C); | |
| YAMLObj.Header.CharacterSetName = getFixedLengthEBCDICString(DE, C, 16); | |
| YAMLObj.Header.LanguageProductIdentifier = | |
| getFixedLengthEBCDICString(DE, C, 16); | |
| YAMLObj.Header.ArchitectureLevel = DE.getU32(C); | |
| uint16_t PropertiesLength = DE.getU16(C); | |
| DE.skip(C, 6); | |
| if (PropertiesLength) { | |
| YAMLObj.Header.InternalCCSID = DE.getU16(C); | |
| PropertiesLength -= 2; | |
| } | |
| if (PropertiesLength) { | |
| YAMLObj.Header.TargetSoftwareEnvironment = DE.getU8(C); | |
| PropertiesLength -= 1; | |
| } | |
| if (!C) | |
| return C.takeError(); | |
| return Error::success(); | |
| } | |
| Error GOFFDumper::dumpExternalSymbol(ArrayRef<uint8_t> Data) { | |
| // TODO: Implement dumping ESD records | |
| return Error::success(); | |
| } | |
| Error GOFFDumper::dumpText(ArrayRef<uint8_t> Data) { | |
| // TODO: Implement dumping TXT records | |
| return Error::success(); | |
| } | |
| Error GOFFDumper::dumpRelocationDirectory(ArrayRef<uint8_t> Data) { | |
| // TODO: Implement dumping RLD records | |
| return Error::success(); | |
| } | |
| Error GOFFDumper::dumpDeferredLength(ArrayRef<uint8_t> Records) { | |
| // TODO: Implement if/when GOFF LEN records are emitted by current producers | |
| // or covered by handcrafted-object tests. | |
| return Error::success(); | |
| } | |
| Error GOFFDumper::dumpEnd(ArrayRef<uint8_t> Records) { | |
| // TODO: implement dumping END records | |
| return Error::success(); | |
| } | |
| Error GOFFDumper::dump() { | |
| Error Err = Error::success(); | |
| // Use the pre-flattened data structure instead of iterating through records | |
| const auto &FlattenedData = Obj.getFlattenedData(); | |
| for (const auto &[RecordType, Data] : FlattenedData) { | |
| switch (RecordType) { | |
| case GOFF::RT_HDR: | |
| if (auto Err = dumpHeader(Data)) | |
| return Err; | |
| break; | |
| case GOFF::RT_ESD: | |
| if (auto Err = dumpExternalSymbol(Data)) | |
| return Err; | |
| break; | |
| case GOFF::RT_TXT: | |
| if (auto Err = dumpText(Data)) | |
| return Err; | |
| break; | |
| case GOFF::RT_RLD: | |
| if (auto Err = dumpRelocationDirectory(Data)) | |
| return Err; | |
| break; | |
| case GOFF::RT_LEN: | |
| if (auto Err = dumpDeferredLength(Data)) | |
| return Err; | |
| break; | |
| case GOFF::RT_END: | |
| if (auto Err = dumpEnd(Data)) | |
| return Err; | |
| break; | |
| } | |
| } | |
| return Err; | |
| } | |
| GOFFYAML::Object &GOFFDumper::getYAMLObj() { return YAMLObj; } | |
| Error goff2yaml(raw_ostream &Out, const llvm::object::GOFFObjectFile &Obj) { | |
| GOFFDumper Dumper(Obj); | |
| if (auto Err = Dumper.dump()) | |
| return Err; | |
| yaml::Output Yout(Out); | |
| Yout << Dumper.getYAMLObj(); | |
| return Error::success(); | |
| } |