| //===- 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/DataExtractor.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/Errc.h" | |
| #include "llvm/Support/FormatVariadic.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #ifndef DEBUG_TYPE | |
| #define DEBUG_TYPE "goff" | |
| #endif | |
| using namespace llvm::object; | |
| using namespace llvm; | |
| // Return the type of the record. | |
| static GOFF::RecordType getRecordType(const uint8_t *PhysicalRecord) { | |
| return GOFF::RecordType((PhysicalRecord[1] & 0xF0) >> 4); | |
| } | |
| // Return true if the record is a continuation record. | |
| static bool isContinuation(const uint8_t *PhysicalRecord) { | |
| return PhysicalRecord[1] & 0x02; | |
| } | |
| // Return true if the record has a continuation. | |
| static bool isContinued(const uint8_t *PhysicalRecord) { | |
| return PhysicalRecord[1] & 0x01; | |
| } | |
| // Helper function to get continuous data from a logical record | |
| // Includes PTV header + everything from first record + continuation payloads | |
| // Returns the number of physical records consumed (including the initial | |
| // record) | |
| Expected<unsigned> | |
| GOFFObjectFile::getContinuousData(SmallVectorImpl<uint8_t> &CompleteData, | |
| int DataIndex, uint16_t DataLength, | |
| const uint8_t *Record) const { | |
| CompleteData.reserve(DataLength + GOFF::RecordLength - DataIndex); | |
| // First record - include PTV header (bytes 0-2) | |
| CompleteData.append(Record, Record + GOFF::RecordPrefixLength); | |
| // Append everything from the first record before the start of the data. | |
| CompleteData.append(Record + GOFF::RecordPrefixLength, Record + DataIndex); | |
| // Append the data. | |
| const uint8_t *Ptr = Record + DataIndex; | |
| size_t SliceLength = std::min( | |
| DataLength, static_cast<uint16_t>(GOFF::RecordLength - DataIndex)); | |
| CompleteData.append(Ptr, Ptr + SliceLength); | |
| DataLength -= SliceLength; | |
| Ptr += SliceLength; | |
| unsigned BlocksConsumed = 1; // Count the initial record | |
| // Continuation records. | |
| while (DataLength > 0) { | |
| // Ptr now points to the start of the next physical record. | |
| // Check that this block is a Continuation. | |
| assert(isContinuation(Ptr) && "Continuation bit must be set"); | |
| // Check that the last Continuation is terminated correctly. | |
| if (DataLength <= GOFF::PayloadLength && isContinued(Ptr)) | |
| return createStringError(object_error::parse_failed, | |
| "continued bit should not be set"); | |
| SliceLength = | |
| std::min(DataLength, static_cast<uint16_t>(GOFF::PayloadLength)); | |
| Ptr += GOFF::RecordPrefixLength; // Skip the 3-byte prefix | |
| CompleteData.append(Ptr, Ptr + SliceLength); | |
| DataLength -= SliceLength; | |
| // Advance to the start of the next record | |
| Ptr += (GOFF::RecordLength - GOFF::RecordPrefixLength); | |
| BlocksConsumed++; | |
| } | |
| return BlocksConsumed; | |
| } | |
| // Walk over the object file and populate FlattenedData. | |
| Error GOFFObjectFile::createFlattenedData() { | |
| const uint8_t *It = base(); | |
| const uint8_t *End = base() + getData().size(); | |
| // First pass: validate continuation records. | |
| const uint8_t *ValidateIt = It; | |
| unsigned ValidateIndex = 0; | |
| bool PrevContinued = false; | |
| bool PrevWasContinuation = false; | |
| GOFF::RecordType PrevRecordType = GOFF::RT_HDR; | |
| while (ValidateIt < End) { | |
| bool IsCont = isContinuation(ValidateIt); | |
| bool IsContd = isContinued(ValidateIt); | |
| GOFF::RecordType CurrentType = ::getRecordType(ValidateIt); | |
| if (IsCont) { | |
| // Continuation record must be preceded by a continued record. | |
| if (!PrevContinued) { | |
| return createStringError(object_error::parse_failed, | |
| "record " + std::to_string(ValidateIndex) + | |
| " is a continuation record that is not " | |
| "preceded by a continued record"); | |
| } | |
| // Continuation record type must match previous record type. | |
| if (CurrentType != PrevRecordType) { | |
| return createStringError( | |
| object_error::parse_failed, | |
| "record " + std::to_string(ValidateIndex) + | |
| " is a continuation record that does not match " | |
| "the type of the previous record"); | |
| } | |
| // Update PrevContinued for continuation records. | |
| PrevContinued = IsContd; | |
| } else { | |
| // Check if previous non-continuation was marked as continued. | |
| if (PrevContinued && !PrevWasContinuation) { | |
| return createStringError(object_error::parse_failed, | |
| "record " + std::to_string(ValidateIndex) + | |
| " is not a continuation record but the " | |
| "preceding record is continued"); | |
| } | |
| PrevRecordType = CurrentType; | |
| PrevContinued = IsContd; | |
| } | |
| PrevWasContinuation = IsCont; | |
| ValidateIt += GOFF::RecordLength; | |
| ValidateIndex++; | |
| } | |
| // Second pass: process records now that we know they're valid. | |
| while (It < End) { | |
| // Skip continuation records - only process first physical record of each | |
| // logical record. | |
| if (isContinuation(It)) { | |
| It += GOFF::RecordLength; | |
| continue; | |
| } | |
| GOFF::RecordType RecordType = ::getRecordType(It); | |
| // Call get continuous data based on record type. | |
| int DataIndex = 0; | |
| uint16_t DataLength = 0; | |
| ArrayRef<uint8_t> Slice(It, GOFF::RecordLength); | |
| DataExtractor DE(Slice, false); | |
| switch (RecordType) { | |
| case GOFF::RT_ESD: { | |
| DataIndex = 72; | |
| uint64_t Offset = 70; | |
| DataLength = DE.getU16(&Offset); | |
| break; | |
| } | |
| case GOFF::RT_TXT: { | |
| DataIndex = 24; | |
| uint64_t Offset = 22; | |
| DataLength = DE.getU16(&Offset); | |
| break; | |
| } | |
| case GOFF::RT_RLD: { | |
| DataIndex = 6; | |
| uint64_t Offset = 4; | |
| DataLength = DE.getU16(&Offset); | |
| break; | |
| } | |
| case GOFF::RT_LEN: { | |
| DataIndex = 8; | |
| uint64_t Offset = 6; | |
| DataLength = DE.getU16(&Offset); | |
| break; | |
| } | |
| case GOFF::RT_END: { | |
| DataIndex = 26; | |
| uint64_t Offset = 24; | |
| DataLength = DE.getU16(&Offset); | |
| break; | |
| } | |
| case GOFF::RT_HDR: { | |
| DataIndex = 60; | |
| uint64_t Offset = 52; | |
| DataLength = DE.getU16(&Offset); | |
| break; | |
| } | |
| } | |
| // Get the flattened data for this logical record (including continuations). | |
| SmallVector<uint8_t> CompleteData; | |
| Expected<unsigned> BlocksConsumed = | |
| getContinuousData(CompleteData, DataIndex, DataLength, It); | |
| if (!BlocksConsumed) { | |
| // Log the error but don't fail construction - errors in continuation | |
| // data will be caught when the data is actually accessed. | |
| llvm::handleAllErrors( | |
| BlocksConsumed.takeError(), [](const llvm::ErrorInfoBase &EIB) { | |
| llvm::errs() << "ERROR: " << EIB.message() << "\n"; | |
| }); | |
| // Skip this record and continue. | |
| It += GOFF::RecordLength; | |
| continue; | |
| } | |
| FlattenedData.push_back({RecordType, std::move(CompleteData)}); | |
| // Move to next logical record using the number of blocks consumed. | |
| It += (*BlocksConsumed) * GOFF::RecordLength; | |
| } | |
| return Error::success(); | |
| } | |
| 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; | |
| } | |
| } | |
| if (Error E = createFlattenedData()) { | |
| Err = std::move(E); | |
| return; | |
| } | |
| SectionEntryImpl DummySection; | |
| SectionList.emplace_back(DummySection); // Dummy entry at index 0. | |
| // Dummy relocation entry at index 0. | |
| GOFFRelEntry DummyRelEntry; | |
| DummyRelEntry.PEsdId = 0; | |
| RelEntries.emplace_back(DummyRelEntry); | |
| for (const auto &[RecordType, Data] : FlattenedData) { | |
| const uint8_t *I = Data.data(); | |
| 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_TXT: | |
| // Save TXT records. | |
| TextPtrs.emplace_back(I); | |
| LLVM_DEBUG(dbgs() << " -- TXT\n"); | |
| break; | |
| case GOFF::RT_RLD: | |
| setRelocationData(I); | |
| LLVM_DEBUG(dbgs() << " -- RLD\n"); | |
| break; | |
| case GOFF::RT_LEN: | |
| LLVM_DEBUG(dbgs() << " -- LEN (GOFF record type) unhandled\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; | |
| } | |
| } | |
| } | |
| 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 (auto It = EsdNamesCache.find(Symb.d.a); It != EsdNamesCache.end()) { | |
| auto &StrPtr = It->second; | |
| return StringRef(StrPtr.second.get(), StrPtr.first); | |
| } | |
| // Get the ESD record pointer from EsdPtrs (points to FlattenedData) | |
| const uint8_t *EsdRecord = getSymbolEsdRecord(Symb); | |
| // Extract name from the flattened ESD record | |
| // Name length is at byte 70-71, name data starts at byte 72 | |
| uint16_t NameLength = ESDRecord::getNameLength(EsdRecord); | |
| SmallString<256> SymbolName; | |
| if (NameLength > 0) { | |
| // Name starts at byte 72 in the record (already flattened, no | |
| // continuations) | |
| const uint8_t *NameStart = EsdRecord + 72; | |
| SymbolName.append(NameStart, NameStart + NameLength); | |
| } | |
| 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); | |
| GOFF::ESDSymbolType Type; | |
| ESDRecord::getSymbolType(Record, Type); | |
| if (Type != GOFF::ESD_ST_SectionDefinition && | |
| Type != GOFF::ESD_ST_ElementDefinition && | |
| BindingScope != GOFF::ESD_BSC_Section && | |
| BindingScope != GOFF::ESD_BSC_Module) { | |
| 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)); | |
| } | |
| uint32_t GOFFObjectFile::getZOSSymbolArchiveAttributes(DataRefImpl Symb) const { | |
| const uint8_t *SymRecord = getSymbolEsdRecord(Symb); | |
| uint32_t Attrs = 0; | |
| // Bit 2 (0x4): 64-bit AMODE. If the child AMODE is unspecified, | |
| // query the parent ED. | |
| // TODO: The parent-walk path (child ESD_AMODE_None with a parent that has | |
| // ESD_AMODE_64) cannot currently be tested as GOFFObjectWriter always emits | |
| // ESD_AMODE_64 directly on LD/ER records and does not set AMODE on ED | |
| // records. Full coverage requires yaml2obj GOFF ESD record support. | |
| GOFF::ESDAmode Amode; | |
| ESDRecord::getAmode(SymRecord, Amode); | |
| if (Amode == GOFF::ESD_AMODE_None) { | |
| uint32_t ParentEsdId; | |
| ESDRecord::getParentEsdId(SymRecord, ParentEsdId); | |
| if (ParentEsdId) { | |
| const uint8_t *EdRecord = EsdPtrs[ParentEsdId]; | |
| ESDRecord::getAmode(EdRecord, Amode); | |
| } | |
| } | |
| if (Amode == GOFF::ESD_AMODE_64) | |
| Attrs |= 0x4; | |
| // Bit 1 (0x2): XPLink — LinkageType is ESD_LT_XPLink. | |
| GOFF::ESDLinkageType LinkageType; | |
| ESDRecord::getLinkageType(SymRecord, LinkageType); | |
| if (LinkageType == GOFF::ESD_LT_XPLink) | |
| Attrs |= 0x2; | |
| // Bit 0 (0x1): Writable Static Area. | |
| GOFF::ESDNameSpaceId NameSpace; | |
| ESDRecord::getNameSpaceId(SymRecord, NameSpace); | |
| if (NameSpace == GOFF::ESD_NS_Parts) | |
| Attrs |= 0x1; | |
| return Attrs; | |
| } | |
| uint64_t GOFFObjectFile::getSymbolSize(DataRefImpl Symb) const { | |
| const uint8_t *Record = getSymbolEsdRecord(Symb); | |
| uint32_t Length; | |
| ESDRecord::getLength(Record, Length); | |
| return Length; | |
| } | |
| 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; | |
| } | |
| uint32_t GOFFObjectFile::getSectionDefEsdId(DataRefImpl &Sec) const { | |
| const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec); | |
| uint32_t Length; | |
| ESDRecord::getLength(EsdRecord, Length); | |
| if (Length == 0) { | |
| const uint8_t *PrEsdRecord = getSectionPrEsdRecord(Sec); | |
| if (PrEsdRecord) | |
| EsdRecord = PrEsdRecord; | |
| } | |
| uint32_t DefEsdId; | |
| ESDRecord::getEsdId(EsdRecord, DefEsdId); | |
| LLVM_DEBUG(dbgs() << "Got def EsdId: " << DefEsdId << '\n'); | |
| return DefEsdId; | |
| } | |
| void GOFFObjectFile::moveSectionNext(DataRefImpl &Sec) const { | |
| Sec.d.a++; | |
| if ((Sec.d.a) >= SectionList.size()) | |
| Sec.d.a = 0; | |
| } | |
| Expected<StringRef> GOFFObjectFile::getSectionName(DataRefImpl Sec) const { | |
| DataRefImpl EdSym; | |
| SectionEntryImpl EsdIds = SectionList[Sec.d.a]; | |
| EdSym.d.a = EsdIds.d.a; | |
| Expected<StringRef> Name = getSymbolName(EdSym); | |
| if (Name) { | |
| StringRef Res = *Name; | |
| LLVM_DEBUG(dbgs() << "Got section: " << Res << '\n'); | |
| LLVM_DEBUG(dbgs() << "Final section name: " << Res << '\n'); | |
| Name = Res; | |
| } | |
| return Name; | |
| } | |
| uint64_t GOFFObjectFile::getSectionAddress(DataRefImpl Sec) const { | |
| uint32_t Offset; | |
| const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec); | |
| ESDRecord::getOffset(EsdRecord, Offset); | |
| return Offset; | |
| } | |
| uint64_t GOFFObjectFile::getSectionSize(DataRefImpl Sec) const { | |
| uint32_t Length; | |
| uint32_t DefEsdId = getSectionDefEsdId(Sec); | |
| const uint8_t *EsdRecord = EsdPtrs[DefEsdId]; | |
| ESDRecord::getLength(EsdRecord, Length); | |
| LLVM_DEBUG(dbgs() << "Got section size: " << Length << '\n'); | |
| return static_cast<uint64_t>(Length); | |
| } | |
| // Unravel TXT records and expand fill characters to produce | |
| // a contiguous sequence of bytes. | |
| Expected<ArrayRef<uint8_t>> | |
| GOFFObjectFile::getSectionContents(DataRefImpl Sec) const { | |
| if (auto It = SectionDataCache.find(Sec.d.a); It != SectionDataCache.end()) { | |
| auto &Buf = It->second; | |
| return ArrayRef<uint8_t>(Buf); | |
| } | |
| uint64_t SectionSize = getSectionSize(Sec); | |
| uint32_t DefEsdId = getSectionDefEsdId(Sec); | |
| const uint8_t *EdEsdRecord = getSectionEdEsdRecord(Sec); | |
| bool FillBytePresent; | |
| ESDRecord::getFillBytePresent(EdEsdRecord, FillBytePresent); | |
| uint8_t FillByte = '\0'; | |
| if (FillBytePresent) | |
| ESDRecord::getFillByteValue(EdEsdRecord, FillByte); | |
| // Initialize section with fill byte. | |
| SmallVector<uint8_t> Data(SectionSize, FillByte); | |
| // Replace section with content from text records. | |
| for (const uint8_t *TxtRecordPtr : TextPtrs) { | |
| uint32_t TxtEsdId; | |
| TXTRecord::getElementEsdId(TxtRecordPtr, TxtEsdId); | |
| LLVM_DEBUG(dbgs() << "Got txt EsdId: " << TxtEsdId << '\n'); | |
| if (TxtEsdId != DefEsdId) | |
| continue; | |
| uint32_t TxtDataOffset; | |
| TXTRecord::getOffset(TxtRecordPtr, TxtDataOffset); | |
| uint16_t TxtDataSize; | |
| TXTRecord::getDataLength(TxtRecordPtr, TxtDataSize); | |
| LLVM_DEBUG(dbgs() << "Record offset " << TxtDataOffset << ", data size " | |
| << TxtDataSize << "\n"); | |
| // Text data starts at byte 24 in the flattened record (already processed | |
| // continuations) | |
| const uint8_t *TxtData = TxtRecordPtr + 24; | |
| assert(TxtDataSize <= Data.size() - TxtDataOffset && | |
| "Text data exceeds section size"); | |
| std::copy(TxtData, TxtData + TxtDataSize, Data.begin() + TxtDataOffset); | |
| } | |
| auto &Cache = SectionDataCache[Sec.d.a]; | |
| Cache = std::move(Data); | |
| return ArrayRef<uint8_t>(Cache); | |
| } | |
| uint64_t GOFFObjectFile::getSectionAlignment(DataRefImpl Sec) const { | |
| const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec); | |
| GOFF::ESDAlignment Pow2Alignment; | |
| ESDRecord::getAlignment(EsdRecord, Pow2Alignment); | |
| return 1ULL << static_cast<uint64_t>(Pow2Alignment); | |
| } | |
| bool GOFFObjectFile::isSectionText(DataRefImpl Sec) const { | |
| const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec); | |
| GOFF::ESDExecutable Executable; | |
| ESDRecord::getExecutable(EsdRecord, Executable); | |
| return Executable == GOFF::ESD_EXE_CODE; | |
| } | |
| bool GOFFObjectFile::isSectionData(DataRefImpl Sec) const { | |
| const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec); | |
| GOFF::ESDExecutable Executable; | |
| ESDRecord::getExecutable(EsdRecord, Executable); | |
| return Executable == GOFF::ESD_EXE_DATA; | |
| } | |
| bool GOFFObjectFile::isSectionNoLoad(DataRefImpl Sec) const { | |
| const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec); | |
| GOFF::ESDLoadingBehavior LoadingBehavior; | |
| ESDRecord::getLoadingBehavior(EsdRecord, LoadingBehavior); | |
| return LoadingBehavior == GOFF::ESD_LB_NoLoad; | |
| } | |
| bool GOFFObjectFile::isSectionReadOnlyData(DataRefImpl Sec) const { | |
| if (!isSectionData(Sec)) | |
| return false; | |
| const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec); | |
| GOFF::ESDLoadingBehavior LoadingBehavior; | |
| ESDRecord::getLoadingBehavior(EsdRecord, LoadingBehavior); | |
| return LoadingBehavior == GOFF::ESD_LB_Initial; | |
| } | |
| bool GOFFObjectFile::isSectionZeroInit(DataRefImpl Sec) const { | |
| // GOFF uses fill characters and fill characters are applied | |
| // on getSectionContents() - so we say false to zero init. | |
| return false; | |
| } | |
| 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 (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)); | |
| } | |
| inline constexpr uint8_t SAME_R_ID = 0x80; | |
| inline constexpr uint8_t SAME_P_ID = 0x40; | |
| inline constexpr uint8_t SAME_OFFSET = 0x20; | |
| inline constexpr uint8_t EXT_ATTR_PRESENT = 0x04; | |
| inline constexpr uint8_t BYTE_OFFSET_8 = 0x02; | |
| // Populate the relocation entries. | |
| void GOFFObjectFile::setRelocationData(const uint8_t *RldRecord) { | |
| SmallVector<uint8_t, 8> RelocationData; | |
| int DataIndex = 6; | |
| uint16_t DataLength; | |
| RLDRecord::getDataLength(RldRecord, DataLength); | |
| // The record is already flattened if it's continued. | |
| const uint8_t *RldI = RldRecord + DataIndex; | |
| const uint8_t *RldE = RldI + DataLength; | |
| uint32_t CurREsdId = 0; | |
| uint32_t CurPEsdId = 0; | |
| uint64_t CurPOffset = 0; | |
| for (const uint8_t *Rld = RldI; Rld < RldE;) { | |
| GOFFRelEntry RelEntry; | |
| uint8_t Flags = Rld[0]; | |
| int32_t Length = 8; | |
| if (!(Flags & SAME_R_ID)) { | |
| CurREsdId = support::endian::read32be(&Rld[Length]); | |
| Length += 4; | |
| } | |
| if (!(Flags & SAME_P_ID)) { | |
| CurPEsdId = support::endian::read32be(&Rld[Length]); | |
| Length += 4; | |
| } | |
| if (!(Flags & SAME_OFFSET)) { | |
| if (Flags & BYTE_OFFSET_8) { | |
| CurPOffset = support::endian::read64be(&Rld[Length]); | |
| Length += 8; | |
| } else { | |
| CurPOffset = support::endian::read32be(&Rld[Length]); | |
| Length += 4; | |
| } | |
| } | |
| if (Flags & EXT_ATTR_PRESENT) | |
| Length += 8; | |
| RelEntry.PEsdId = CurPEsdId; | |
| RelEntry.REsdId = CurREsdId; | |
| RelEntry.POffset = CurPOffset; | |
| RelEntry.RelType = getRldType(Rld); | |
| RelEntries.emplace_back(RelEntry); | |
| Rld += Length; | |
| assert(Rld <= RldE && "RLD length?"); | |
| } | |
| } | |
| void GOFFObjectFile::moveRelocationNext(DataRefImpl &Rel) const { | |
| for (size_t I = Rel.d.b + 1, E = RelEntries.size(); I < E; ++I) { | |
| const GOFFRelEntry &RelEntry = RelEntries[I]; | |
| if (Rel.d.a == RelEntry.PEsdId) { | |
| Rel.d.b = I; | |
| return; | |
| } | |
| } | |
| Rel.d.b = 0; | |
| } | |
| uint64_t GOFFObjectFile::getRelocationOffset(DataRefImpl Rel) const { | |
| assert(Rel.d.b > 0 && Rel.d.b < RelEntries.size() && | |
| "Rel Index out of boundary"); | |
| const GOFFRelEntry &RelEntry = RelEntries[Rel.d.b]; | |
| return RelEntry.POffset; | |
| } | |
| symbol_iterator GOFFObjectFile::getRelocationSymbol(DataRefImpl Rel) const { | |
| assert(Rel.d.b > 0 && Rel.d.b < RelEntries.size() && | |
| "Rel Index out of boundary"); | |
| const GOFFRelEntry &RelEntry = RelEntries[Rel.d.b]; | |
| DataRefImpl RefSym; | |
| RefSym.d.a = RelEntry.REsdId; | |
| return basic_symbol_iterator(SymbolRef(RefSym, this)); | |
| } | |
| uint64_t GOFFObjectFile::getRelocationType(DataRefImpl Rel) const { | |
| assert(Rel.d.b > 0 && Rel.d.b < RelEntries.size() && | |
| "Rel Index out of boundary"); | |
| const GOFFRelEntry &RelEntry = RelEntries[Rel.d.b]; | |
| return RelEntry.RelType; | |
| } | |
| void GOFFObjectFile::getRelocationTypeName( | |
| DataRefImpl Rel, SmallVectorImpl<char> &Result) const { | |
| uint64_t RelType = getRelocationType(Rel); | |
| std::string HexStr = formatv("R_{0:x-8}", RelType).str(); | |
| Result.append(HexStr.begin(), HexStr.end()); | |
| } | |
| relocation_iterator GOFFObjectFile::section_rel_begin(DataRefImpl Sec) const { | |
| DataRefImpl Rel; | |
| Rel.d.a = getSectionDefEsdId(Sec); | |
| Rel.d.b = 0; | |
| moveRelocationNext(Rel); | |
| return relocation_iterator(RelocationRef(Rel, this)); | |
| } | |
| relocation_iterator GOFFObjectFile::section_rel_end(DataRefImpl Sec) const { | |
| DataRefImpl Rel; | |
| Rel.d.a = getSectionDefEsdId(Sec); | |
| Rel.d.b = 0; | |
| return relocation_iterator(RelocationRef(Rel, this)); | |
| } |