Skip to content

[obj2yaml] Add ability to dump GOFF header records #90871

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 71 additions & 4 deletions llvm/include/llvm/Object/GOFFObjectFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/ConvertEBCDIC.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/SubtargetFeature.h"
#include "llvm/TargetParser/Triple.h"
Expand All @@ -28,6 +29,66 @@ namespace llvm {

namespace object {

// The GOFF file format consists of 2 layers. The data is organized in logical
// records, which are divided into physical records. A RecordRef object
// represents a logical record, with the data still divided in physical record.
class RecordRef {
const ObjectFile *OwningObject = nullptr;
const uint8_t *Data = nullptr;
uint64_t Size = 0U;
Error *Err = nullptr;

const uint8_t *base() const {
return reinterpret_cast<const uint8_t *>(OwningObject->getData().data());
}

// Create a string error, and clear the position.
void createError(std::error_code EC, const Twine &S) {
if (Err)
*Err = createStringError(EC, S);
Data = nullptr;
Size = 0;
}

// Determine the size of the logical record. Also makes sure that the physical
// records are correctly linked.
void determineSize();

public:
RecordRef() = default;
RecordRef(const ObjectFile *Owner) : OwningObject(Owner) {}
RecordRef(const ObjectFile *Owner, Error *Err)
: OwningObject(Owner), Data(base()), Err(Err) {
determineSize();
};
RecordRef(const RecordRef &Other) {
OwningObject = Other.OwningObject;
Data = Other.Data;
Size = Other.Size;
Err = Other.Err;
};

bool operator==(const RecordRef &Other) const;
void moveNext();

/// Returns the type of the record.
llvm::GOFF::RecordType getRecordType() const;

/// Returns the size of the logical record. This is a multiply of the size of
/// a physical record.
uint64_t getSize() const;

/// Data of the logical record, still divided in physical records.
const ArrayRef<uint8_t> getContents() const;
};

inline bool RecordRef::operator==(const RecordRef &Other) const {
return OwningObject == Other.OwningObject && Data == Other.Data &&
Size == Other.Size;
}

using record_iterator = content_iterator<RecordRef>;

class GOFFObjectFile : public ObjectFile {
friend class GOFFSymbolRef;

Expand All @@ -44,6 +105,12 @@ class GOFFObjectFile : public ObjectFile {
mutable DenseMap<uint32_t, SmallVector<uint8_t>> SectionDataCache;

public:
record_iterator record_begin(Error *Err) const;
record_iterator record_end() const;
iterator_range<record_iterator> records(Error *Err) const {
return make_range(record_begin(Err), record_end());
}

Expected<StringRef> getSymbolName(SymbolRef Symbol) const;

GOFFObjectFile(MemoryBufferRef Object, Error &Err);
Expand All @@ -57,17 +124,17 @@ class GOFFObjectFile : public ObjectFile {

Triple::ArchType getArch() const override { return Triple::systemz; }

Expected<SubtargetFeatures> getFeatures() const override { return SubtargetFeatures(); }
Expected<SubtargetFeatures> getFeatures() const override {
return SubtargetFeatures();
}

bool isRelocatableObject() const override { return true; }

void moveSymbolNext(DataRefImpl &Symb) const override;
basic_symbol_iterator symbol_begin() const override;
basic_symbol_iterator symbol_end() const override;

bool is64Bit() const override {
return true;
}
bool is64Bit() const override { return true; }

bool isSectionNoLoad(DataRefImpl Sec) const;
bool isSectionReadOnlyData(DataRefImpl Sec) const;
Expand Down
4 changes: 2 additions & 2 deletions llvm/include/llvm/ObjectYAML/GOFFYAML.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ struct FileHeader {
uint32_t TargetEnvironment = 0;
uint32_t TargetOperatingSystem = 0;
uint16_t CCSID = 0;
StringRef CharacterSetName;
StringRef LanguageProductIdentifier;
std::string CharacterSetName;
std::string LanguageProductIdentifier;
uint32_t ArchitectureLevel = 0;
std::optional<uint16_t> InternalCCSID;
std::optional<uint8_t> TargetSoftwareEnvironment;
Expand Down
150 changes: 102 additions & 48 deletions llvm/lib/Object/GOFFObjectFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/raw_ostream.h"
#include <cstddef>

#ifndef DEBUG_TYPE
#define DEBUG_TYPE "goff"
Expand All @@ -24,6 +25,94 @@
using namespace llvm::object;
using namespace llvm;

namespace {
// Return the type of the record.
GOFF::RecordType getRecordType(const uint8_t *PhysicalRecord) {
return GOFF::RecordType((PhysicalRecord[1] & 0xF0) >> 4);
}

// Return true if the record is a continuation record.
bool isContinuation(const uint8_t *PhysicalRecord) {
return PhysicalRecord[1] & 0x02;
}

// Return true if the record has a continuation.
bool isContinued(const uint8_t *PhysicalRecord) {
return PhysicalRecord[1] & 0x01;
}
} // namespace

void RecordRef::determineSize() {
GOFF::RecordType CurrRecordType = ::getRecordType(Data);
bool PrevWasContinued = isContinued(Data);
const uint8_t *It = Data + GOFF::RecordLength;
const uint8_t *End = reinterpret_cast<const uint8_t *>(
base() + OwningObject->getData().size());
for (; It < End;
PrevWasContinued = isContinued(It), It += GOFF::RecordLength) {
GOFF::RecordType RecordType = ::getRecordType(It);
bool IsContinuation = isContinuation(It);
size_t RecordNum = (It - base()) / GOFF::RecordLength;
// If the previous record was continued, the current record should be a
// continuation.
if (PrevWasContinued && !IsContinuation) {
createError(object_error::parse_failed,
"record " + std::to_string(RecordNum) +
" is not a continuation record but the "
"preceding record is continued");
return;
}
// If the current record is a continuation, then the previous record should
// be continued, and have the same record type.
if (IsContinuation) {
if (RecordType != CurrRecordType) {
createError(object_error::parse_failed,
"record " + std::to_string(RecordNum) +
" is a continuation record that does not "
"match the type of the previous record");
return;
}
if (!PrevWasContinued) {
createError(object_error::parse_failed,
"record " + std::to_string(RecordNum) +
" is a continuation record that is not "
"preceded by a continued record");
return;
}
}

// Break out of loop when we reached a new record.
if (!IsContinuation)
break;
}
Size = It - Data;
}

void RecordRef::moveNext() {
if (Data == nullptr)
return;
const uint8_t *Base = base();
std::ptrdiff_t Offset = Data - Base;
uint64_t NewOffset = Offset + Size;
if (NewOffset > OwningObject->getData().size()) {
Data = nullptr;
Size = 0;
} else {
Data = &Base[NewOffset];
determineSize();
}
}

GOFF::RecordType RecordRef::getRecordType() const {
return ::getRecordType(Data);
}

uint64_t RecordRef::getSize() const { return Size; }

const ArrayRef<uint8_t> RecordRef::getContents() const {
return ArrayRef<uint8_t>(Data, Size);
}

Expected<std::unique_ptr<ObjectFile>>
ObjectFile::createGOFFObjectFile(MemoryBufferRef Object) {
Error Err = Error::success();
Expand Down Expand Up @@ -64,52 +153,11 @@ GOFFObjectFile::GOFFObjectFile(MemoryBufferRef Object, Error &Err)
SectionEntryImpl DummySection;
SectionList.emplace_back(DummySection); // Dummy entry at index 0.

uint8_t PrevRecordType = 0;
uint8_t PrevContinuationBits = 0;
const uint8_t *End = reinterpret_cast<const uint8_t *>(Data.getBufferEnd());
for (const uint8_t *I = base(); I < End; I += GOFF::RecordLength) {
uint8_t RecordType = (I[1] & 0xF0) >> 4;
bool IsContinuation = I[1] & 0x02;
bool PrevWasContinued = PrevContinuationBits & 0x01;
size_t RecordNum = (I - base()) / GOFF::RecordLength;

// If the previous record was continued, the current record should be a
// continuation.
if (PrevWasContinued && !IsContinuation) {
if (PrevRecordType == RecordType) {
Err = createStringError(object_error::parse_failed,
"record " + std::to_string(RecordNum) +
" is not a continuation record but the "
"preceding record is continued");
return;
}
}
// Don't parse continuations records, only parse initial record.
if (IsContinuation) {
if (RecordType != PrevRecordType) {
Err = createStringError(object_error::parse_failed,
"record " + std::to_string(RecordNum) +
" is a continuation record that does not "
"match the type of the previous record");
return;
}
if (!PrevWasContinued) {
Err = createStringError(object_error::parse_failed,
"record " + std::to_string(RecordNum) +
" is a continuation record that is not "
"preceded by a continued record");
return;
}
PrevRecordType = RecordType;
PrevContinuationBits = I[1] & 0x03;
continue;
}
LLVM_DEBUG(for (size_t J = 0; J < GOFF::RecordLength; ++J) {
const uint8_t *P = I + J;
if (J % 8 == 0)
dbgs() << " ";
dbgs() << format("%02hhX", *P);
});
for (auto &Rec : records(&Err)) {
if (Err)
return;
uint8_t RecordType = Rec.getRecordType();
const uint8_t *I = Rec.getContents().data();

switch (RecordType) {
case GOFF::RT_ESD: {
Expand Down Expand Up @@ -179,11 +227,17 @@ GOFFObjectFile::GOFFObjectFile(MemoryBufferRef Object, Error &Err)
default:
llvm_unreachable("Unknown record type");
}
PrevRecordType = RecordType;
PrevContinuationBits = I[1] & 0x03;
}
}

record_iterator GOFFObjectFile::record_begin(Error *Err) const {
return record_iterator(RecordRef(this, Err));
}

record_iterator GOFFObjectFile::record_end() const {
return record_iterator(RecordRef(this));
}

const uint8_t *GOFFObjectFile::getSymbolEsdRecord(DataRefImpl Symb) const {
const uint8_t *EsdRecord = EsdPtrs[Symb.d.a];
return EsdRecord;
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Object/ObjectFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ ObjectFile::createObjectFile(MemoryBufferRef Object, file_magic Type,
case file_magic::windows_resource:
case file_magic::pdb:
case file_magic::minidump:
case file_magic::goff_object:
case file_magic::cuda_fatbinary:
case file_magic::offload_binary:
case file_magic::dxcontainer_object:
Expand All @@ -178,6 +177,8 @@ ObjectFile::createObjectFile(MemoryBufferRef Object, file_magic Type,
case file_magic::elf_shared_object:
case file_magic::elf_core:
return createELFObjectFile(Object, InitContent);
case file_magic::goff_object:
return createGOFFObjectFile(Object);
case file_magic::macho_object:
case file_magic::macho_executable:
case file_magic::macho_fixed_virtual_memory_shared_lib:
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/ObjectYAML/GOFFEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,8 @@ void GOFFState::writeHeader(GOFFYAML::FileHeader &FileHdr) {
}

GW.makeNewRecord(GOFF::RT_HDR, GOFF::PayloadLength);
GW << binaryBe(FileHdr.TargetEnvironment) // TargetEnvironment
GW << zeros(1) // Reserved
<< binaryBe(FileHdr.TargetEnvironment) // TargetEnvironment
<< binaryBe(FileHdr.TargetOperatingSystem) // TargetOperatingSystem
<< zeros(2) // Reserved
<< binaryBe(FileHdr.CCSID) // CCSID
Expand Down
13 changes: 13 additions & 0 deletions llvm/test/ObjectYAML/GOFF/header-default.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# RUN: yaml2obj %s | obj2yaml | FileCheck %s

# CHECK: FileHeader: {}

--- !GOFF
FileHeader:
TargetEnvironment: 0
TargetOperatingSystem: 0
CCSID: 0
CharacterSetName: "\0"
LanguageProductIdentifier: "\0"
ArchitectureLevel: 1
...
21 changes: 21 additions & 0 deletions llvm/test/ObjectYAML/GOFF/header-filled.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# RUN: yaml2obj %s | obj2yaml | FileCheck %s

# CHECK: --- !GOFF
# CHECK-NEXT: FileHeader:
# CHECK-NEXT: TargetEnvironment: 1
# CHECK-NEXT: TargetOperatingSystem: 2
# CHECK-NEXT: CCSID: 1047
# CHECK-NEXT: CharacterSetName: EBCDIC-1047
# CHECK-NEXT: LanguageProductIdentifier: Foo
# CHECK-NEXT: ArchitectureLevel: 2
# CHECK: ...

--- !GOFF
FileHeader:
TargetEnvironment: 1
TargetOperatingSystem: 2
CCSID: 1047
CharacterSetName: EBCDIC-1047
LanguageProductIdentifier: Foo
ArchitectureLevel: 2
...
1 change: 1 addition & 0 deletions llvm/tools/obj2yaml/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ add_llvm_utility(obj2yaml
dwarf2yaml.cpp
dxcontainer2yaml.cpp
elf2yaml.cpp
goff2yaml.cpp
macho2yaml.cpp
minidump2yaml.cpp
offload2yaml.cpp
Expand Down
Loading
Loading