aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/llvm/lib/Object/GOFFObjectFile.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2023-09-02 21:17:18 +0000
committerDimitry Andric <dim@FreeBSD.org>2023-12-08 17:34:50 +0000
commit06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e (patch)
tree62f873df87c7c675557a179e0c4c83fe9f3087bc /contrib/llvm-project/llvm/lib/Object/GOFFObjectFile.cpp
parentcf037972ea8863e2bab7461d77345367d2c1e054 (diff)
parent7fa27ce4a07f19b07799a767fc29416f3b625afb (diff)
Diffstat (limited to 'contrib/llvm-project/llvm/lib/Object/GOFFObjectFile.cpp')
-rw-r--r--contrib/llvm-project/llvm/lib/Object/GOFFObjectFile.cpp483
1 files changed, 483 insertions, 0 deletions
diff --git a/contrib/llvm-project/llvm/lib/Object/GOFFObjectFile.cpp b/contrib/llvm-project/llvm/lib/Object/GOFFObjectFile.cpp
new file mode 100644
index 000000000000..76a13559ebfe
--- /dev/null
+++ b/contrib/llvm-project/llvm/lib/Object/GOFFObjectFile.cpp
@@ -0,0 +1,483 @@
+//===- 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);
+}