blob: 76a13559ebfe352cd0ad7160e3e23fe913eaedcb [file] [log] [blame]
//===- GOFFObjectFile.cpp - GOFF object file 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
//
//===----------------------------------------------------------------------===//
//
// Implementation of the GOFFObjectFile class.
//
//===----------------------------------------------------------------------===//
#include "llvm/Object/GOFFObjectFile.h"
#include "llvm/BinaryFormat/GOFF.h"
#include "llvm/Object/GOFF.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/raw_ostream.h"
#ifndef DEBUG_TYPE
#define DEBUG_TYPE "goff"
#endif
using namespace llvm::object;
using namespace llvm;
Expected<std::unique_ptr<ObjectFile>>
ObjectFile::createGOFFObjectFile(MemoryBufferRef Object) {
Error Err = Error::success();
std::unique_ptr<GOFFObjectFile> Ret(new GOFFObjectFile(Object, Err));
if (Err)
return std::move(Err);
return std::move(Ret);
}
GOFFObjectFile::GOFFObjectFile(MemoryBufferRef Object, Error &Err)
: ObjectFile(Binary::ID_GOFF, Object) {
ErrorAsOutParameter ErrAsOutParam(&Err);
// Object file isn't the right size, bail out early.
if ((Object.getBufferSize() % GOFF::RecordLength) != 0) {
Err = createStringError(
object_error::unexpected_eof,
"object file is not the right size. Must be a multiple "
"of 80 bytes, but is " +
std::to_string(Object.getBufferSize()) + " bytes");
return;
}
// Object file doesn't start/end with HDR/END records.
// Bail out early.
if (Object.getBufferSize() != 0) {
if ((base()[1] & 0xF0) >> 4 != GOFF::RT_HDR) {
Err = createStringError(object_error::parse_failed,
"object file must start with HDR record");
return;
}
if ((base()[Object.getBufferSize() - GOFF::RecordLength + 1] & 0xF0) >> 4 !=
GOFF::RT_END) {
Err = createStringError(object_error::parse_failed,
"object file must end with END record");
return;
}
}
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;
}
#ifndef NDEBUG
for (size_t J = 0; J < GOFF::RecordLength; ++J) {
const uint8_t *P = I + J;
if (J % 8 == 0)
dbgs() << " ";
dbgs() << format("%02hhX", *P);
}
#endif
switch (RecordType) {
case GOFF::RT_ESD: {
// Save ESD record.
uint32_t EsdId;
ESDRecord::getEsdId(I, EsdId);
EsdPtrs.grow(EsdId);
EsdPtrs[EsdId] = I;
// Determine and save the "sections" in GOFF.
// A section is saved as a tuple of the form
// case (1): (ED,child PR)
// - where the PR must have non-zero length.
// case (2a) (ED,0)
// - where the ED is of non-zero length.
// case (2b) (ED,0)
// - where the ED is zero length but
// contains a label (LD).
GOFF::ESDSymbolType SymbolType;
ESDRecord::getSymbolType(I, SymbolType);
SectionEntryImpl Section;
uint32_t Length;
ESDRecord::getLength(I, Length);
if (SymbolType == GOFF::ESD_ST_ElementDefinition) {
// case (2a)
if (Length != 0) {
Section.d.a = EsdId;
SectionList.emplace_back(Section);
}
} else if (SymbolType == GOFF::ESD_ST_PartReference) {
// case (1)
if (Length != 0) {
uint32_t SymEdId;
ESDRecord::getParentEsdId(I, SymEdId);
Section.d.a = SymEdId;
Section.d.b = EsdId;
SectionList.emplace_back(Section);
}
} else if (SymbolType == GOFF::ESD_ST_LabelDefinition) {
// case (2b)
uint32_t SymEdId;
ESDRecord::getParentEsdId(I, SymEdId);
const uint8_t *SymEdRecord = EsdPtrs[SymEdId];
uint32_t EdLength;
ESDRecord::getLength(SymEdRecord, EdLength);
if (!EdLength) { // [ EDID, PRID ]
// LD child of a zero length parent ED.
// Add the section ED which was previously ignored.
Section.d.a = SymEdId;
SectionList.emplace_back(Section);
}
}
LLVM_DEBUG(dbgs() << " -- ESD " << EsdId << "\n");
break;
}
case GOFF::RT_END:
LLVM_DEBUG(dbgs() << " -- END (GOFF record type) unhandled\n");
break;
case GOFF::RT_HDR:
LLVM_DEBUG(dbgs() << " -- HDR (GOFF record type) unhandled\n");
break;
default:
llvm_unreachable("Unknown record type");
}
PrevRecordType = RecordType;
PrevContinuationBits = I[1] & 0x03;
}
}
const uint8_t *GOFFObjectFile::getSymbolEsdRecord(DataRefImpl Symb) const {
const uint8_t *EsdRecord = EsdPtrs[Symb.d.a];
return EsdRecord;
}
Expected<StringRef> GOFFObjectFile::getSymbolName(DataRefImpl Symb) const {
if (EsdNamesCache.count(Symb.d.a)) {
auto &StrPtr = EsdNamesCache[Symb.d.a];
return StringRef(StrPtr.second.get(), StrPtr.first);
}
SmallString<256> SymbolName;
if (auto Err = ESDRecord::getData(getSymbolEsdRecord(Symb), SymbolName))
return std::move(Err);
SmallString<256> SymbolNameConverted;
ConverterEBCDIC::convertToUTF8(SymbolName, SymbolNameConverted);
size_t Size = SymbolNameConverted.size();
auto StrPtr = std::make_pair(Size, std::make_unique<char[]>(Size));
char *Buf = StrPtr.second.get();
memcpy(Buf, SymbolNameConverted.data(), Size);
EsdNamesCache[Symb.d.a] = std::move(StrPtr);
return StringRef(Buf, Size);
}
Expected<StringRef> GOFFObjectFile::getSymbolName(SymbolRef Symbol) const {
return getSymbolName(Symbol.getRawDataRefImpl());
}
Expected<uint64_t> GOFFObjectFile::getSymbolAddress(DataRefImpl Symb) const {
uint32_t Offset;
const uint8_t *EsdRecord = getSymbolEsdRecord(Symb);
ESDRecord::getOffset(EsdRecord, Offset);
return static_cast<uint64_t>(Offset);
}
uint64_t GOFFObjectFile::getSymbolValueImpl(DataRefImpl Symb) const {
uint32_t Offset;
const uint8_t *EsdRecord = getSymbolEsdRecord(Symb);
ESDRecord::getOffset(EsdRecord, Offset);
return static_cast<uint64_t>(Offset);
}
uint64_t GOFFObjectFile::getCommonSymbolSizeImpl(DataRefImpl Symb) const {
return 0;
}
bool GOFFObjectFile::isSymbolUnresolved(DataRefImpl Symb) const {
const uint8_t *Record = getSymbolEsdRecord(Symb);
GOFF::ESDSymbolType SymbolType;
ESDRecord::getSymbolType(Record, SymbolType);
if (SymbolType == GOFF::ESD_ST_ExternalReference)
return true;
if (SymbolType == GOFF::ESD_ST_PartReference) {
uint32_t Length;
ESDRecord::getLength(Record, Length);
if (Length == 0)
return true;
}
return false;
}
bool GOFFObjectFile::isSymbolIndirect(DataRefImpl Symb) const {
const uint8_t *Record = getSymbolEsdRecord(Symb);
bool Indirect;
ESDRecord::getIndirectReference(Record, Indirect);
return Indirect;
}
Expected<uint32_t> GOFFObjectFile::getSymbolFlags(DataRefImpl Symb) const {
uint32_t Flags = 0;
if (isSymbolUnresolved(Symb))
Flags |= SymbolRef::SF_Undefined;
const uint8_t *Record = getSymbolEsdRecord(Symb);
GOFF::ESDBindingStrength BindingStrength;
ESDRecord::getBindingStrength(Record, BindingStrength);
if (BindingStrength == GOFF::ESD_BST_Weak)
Flags |= SymbolRef::SF_Weak;
GOFF::ESDBindingScope BindingScope;
ESDRecord::getBindingScope(Record, BindingScope);
if (BindingScope != GOFF::ESD_BSC_Section) {
Expected<StringRef> Name = getSymbolName(Symb);
if (Name && *Name != " ") { // Blank name is local.
Flags |= SymbolRef::SF_Global;
if (BindingScope == GOFF::ESD_BSC_ImportExport)
Flags |= SymbolRef::SF_Exported;
else if (!(Flags & SymbolRef::SF_Undefined))
Flags |= SymbolRef::SF_Hidden;
}
}
return Flags;
}
Expected<SymbolRef::Type>
GOFFObjectFile::getSymbolType(DataRefImpl Symb) const {
const uint8_t *Record = getSymbolEsdRecord(Symb);
GOFF::ESDSymbolType SymbolType;
ESDRecord::getSymbolType(Record, SymbolType);
GOFF::ESDExecutable Executable;
ESDRecord::getExecutable(Record, Executable);
if (SymbolType != GOFF::ESD_ST_SectionDefinition &&
SymbolType != GOFF::ESD_ST_ElementDefinition &&
SymbolType != GOFF::ESD_ST_LabelDefinition &&
SymbolType != GOFF::ESD_ST_PartReference &&
SymbolType != GOFF::ESD_ST_ExternalReference) {
uint32_t EsdId;
ESDRecord::getEsdId(Record, EsdId);
return createStringError(llvm::errc::invalid_argument,
"ESD record %" PRIu32
" has invalid symbol type 0x%02" PRIX8,
EsdId, SymbolType);
}
switch (SymbolType) {
case GOFF::ESD_ST_SectionDefinition:
case GOFF::ESD_ST_ElementDefinition:
return SymbolRef::ST_Other;
case GOFF::ESD_ST_LabelDefinition:
case GOFF::ESD_ST_PartReference:
case GOFF::ESD_ST_ExternalReference:
if (Executable != GOFF::ESD_EXE_CODE && Executable != GOFF::ESD_EXE_DATA &&
Executable != GOFF::ESD_EXE_Unspecified) {
uint32_t EsdId;
ESDRecord::getEsdId(Record, EsdId);
return createStringError(llvm::errc::invalid_argument,
"ESD record %" PRIu32
" has unknown Executable type 0x%02X",
EsdId, Executable);
}
switch (Executable) {
case GOFF::ESD_EXE_CODE:
return SymbolRef::ST_Function;
case GOFF::ESD_EXE_DATA:
return SymbolRef::ST_Data;
case GOFF::ESD_EXE_Unspecified:
return SymbolRef::ST_Unknown;
}
llvm_unreachable("Unhandled ESDExecutable");
}
llvm_unreachable("Unhandled ESDSymbolType");
}
Expected<section_iterator>
GOFFObjectFile::getSymbolSection(DataRefImpl Symb) const {
DataRefImpl Sec;
if (isSymbolUnresolved(Symb))
return section_iterator(SectionRef(Sec, this));
const uint8_t *SymEsdRecord = EsdPtrs[Symb.d.a];
uint32_t SymEdId;
ESDRecord::getParentEsdId(SymEsdRecord, SymEdId);
const uint8_t *SymEdRecord = EsdPtrs[SymEdId];
for (size_t I = 0, E = SectionList.size(); I < E; ++I) {
bool Found;
const uint8_t *SectionPrRecord = getSectionPrEsdRecord(I);
if (SectionPrRecord) {
Found = SymEsdRecord == SectionPrRecord;
} else {
const uint8_t *SectionEdRecord = getSectionEdEsdRecord(I);
Found = SymEdRecord == SectionEdRecord;
}
if (Found) {
Sec.d.a = I;
return section_iterator(SectionRef(Sec, this));
}
}
return createStringError(llvm::errc::invalid_argument,
"symbol with ESD id " + std::to_string(Symb.d.a) +
" refers to invalid section with ESD id " +
std::to_string(SymEdId));
}
const uint8_t *GOFFObjectFile::getSectionEdEsdRecord(DataRefImpl &Sec) const {
SectionEntryImpl EsdIds = SectionList[Sec.d.a];
const uint8_t *EsdRecord = EsdPtrs[EsdIds.d.a];
return EsdRecord;
}
const uint8_t *GOFFObjectFile::getSectionPrEsdRecord(DataRefImpl &Sec) const {
SectionEntryImpl EsdIds = SectionList[Sec.d.a];
const uint8_t *EsdRecord = nullptr;
if (EsdIds.d.b)
EsdRecord = EsdPtrs[EsdIds.d.b];
return EsdRecord;
}
const uint8_t *
GOFFObjectFile::getSectionEdEsdRecord(uint32_t SectionIndex) const {
DataRefImpl Sec;
Sec.d.a = SectionIndex;
const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec);
return EsdRecord;
}
const uint8_t *
GOFFObjectFile::getSectionPrEsdRecord(uint32_t SectionIndex) const {
DataRefImpl Sec;
Sec.d.a = SectionIndex;
const uint8_t *EsdRecord = getSectionPrEsdRecord(Sec);
return EsdRecord;
}
section_iterator GOFFObjectFile::section_begin() const {
DataRefImpl Sec;
moveSectionNext(Sec);
return section_iterator(SectionRef(Sec, this));
}
section_iterator GOFFObjectFile::section_end() const {
DataRefImpl Sec;
return section_iterator(SectionRef(Sec, this));
}
void GOFFObjectFile::moveSymbolNext(DataRefImpl &Symb) const {
for (uint32_t I = Symb.d.a + 1, E = EsdPtrs.size(); I < E; ++I) {
if (EsdPtrs[I]) {
const uint8_t *EsdRecord = EsdPtrs[I];
GOFF::ESDSymbolType SymbolType;
ESDRecord::getSymbolType(EsdRecord, SymbolType);
// Skip EDs - i.e. section symbols.
bool IgnoreSpecialGOFFSymbols = true;
bool SkipSymbol = ((SymbolType == GOFF::ESD_ST_ElementDefinition) ||
(SymbolType == GOFF::ESD_ST_SectionDefinition)) &&
IgnoreSpecialGOFFSymbols;
if (!SkipSymbol) {
Symb.d.a = I;
return;
}
}
}
Symb.d.a = 0;
}
basic_symbol_iterator GOFFObjectFile::symbol_begin() const {
DataRefImpl Symb;
moveSymbolNext(Symb);
return basic_symbol_iterator(SymbolRef(Symb, this));
}
basic_symbol_iterator GOFFObjectFile::symbol_end() const {
DataRefImpl Symb;
return basic_symbol_iterator(SymbolRef(Symb, this));
}
Error Record::getContinuousData(const uint8_t *Record, uint16_t DataLength,
int DataIndex, SmallString<256> &CompleteData) {
// First record.
const uint8_t *Slice = Record + DataIndex;
size_t SliceLength =
std::min(DataLength, (uint16_t)(GOFF::RecordLength - DataIndex));
CompleteData.append(Slice, Slice + SliceLength);
DataLength -= SliceLength;
Slice += SliceLength;
// Continuation records.
for (; DataLength > 0;
DataLength -= SliceLength, Slice += GOFF::PayloadLength) {
// Slice points to the start of the new record.
// Check that this block is a Continuation.
assert(Record::isContinuation(Slice) && "Continuation bit must be set");
// Check that the last Continuation is terminated correctly.
if (DataLength <= 77 && Record::isContinued(Slice))
return createStringError(object_error::parse_failed,
"continued bit should not be set");
SliceLength = std::min(DataLength, (uint16_t)GOFF::PayloadLength);
Slice += GOFF::RecordPrefixLength;
CompleteData.append(Slice, Slice + SliceLength);
}
return Error::success();
}
Error HDRRecord::getData(const uint8_t *Record,
SmallString<256> &CompleteData) {
uint16_t Length = getPropertyModuleLength(Record);
return getContinuousData(Record, Length, 60, CompleteData);
}
Error ESDRecord::getData(const uint8_t *Record,
SmallString<256> &CompleteData) {
uint16_t DataSize = getNameLength(Record);
return getContinuousData(Record, DataSize, 72, CompleteData);
}
Error ENDRecord::getData(const uint8_t *Record,
SmallString<256> &CompleteData) {
uint16_t Length = getNameLength(Record);
return getContinuousData(Record, Length, 26, CompleteData);
}