Path: blob/main/contrib/llvm-project/llvm/lib/Object/GOFFObjectFile.cpp
35232 views
//===- GOFFObjectFile.cpp - GOFF object file implementation -----*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Implementation of the GOFFObjectFile class.9//10//===----------------------------------------------------------------------===//1112#include "llvm/Object/GOFFObjectFile.h"13#include "llvm/BinaryFormat/GOFF.h"14#include "llvm/Object/GOFF.h"15#include "llvm/Support/Debug.h"16#include "llvm/Support/Errc.h"17#include "llvm/Support/raw_ostream.h"1819#ifndef DEBUG_TYPE20#define DEBUG_TYPE "goff"21#endif2223using namespace llvm::object;24using namespace llvm;2526Expected<std::unique_ptr<ObjectFile>>27ObjectFile::createGOFFObjectFile(MemoryBufferRef Object) {28Error Err = Error::success();29std::unique_ptr<GOFFObjectFile> Ret(new GOFFObjectFile(Object, Err));30if (Err)31return std::move(Err);32return std::move(Ret);33}3435GOFFObjectFile::GOFFObjectFile(MemoryBufferRef Object, Error &Err)36: ObjectFile(Binary::ID_GOFF, Object) {37ErrorAsOutParameter ErrAsOutParam(&Err);38// Object file isn't the right size, bail out early.39if ((Object.getBufferSize() % GOFF::RecordLength) != 0) {40Err = createStringError(41object_error::unexpected_eof,42"object file is not the right size. Must be a multiple "43"of 80 bytes, but is " +44std::to_string(Object.getBufferSize()) + " bytes");45return;46}47// Object file doesn't start/end with HDR/END records.48// Bail out early.49if (Object.getBufferSize() != 0) {50if ((base()[1] & 0xF0) >> 4 != GOFF::RT_HDR) {51Err = createStringError(object_error::parse_failed,52"object file must start with HDR record");53return;54}55if ((base()[Object.getBufferSize() - GOFF::RecordLength + 1] & 0xF0) >> 4 !=56GOFF::RT_END) {57Err = createStringError(object_error::parse_failed,58"object file must end with END record");59return;60}61}6263SectionEntryImpl DummySection;64SectionList.emplace_back(DummySection); // Dummy entry at index 0.6566uint8_t PrevRecordType = 0;67uint8_t PrevContinuationBits = 0;68const uint8_t *End = reinterpret_cast<const uint8_t *>(Data.getBufferEnd());69for (const uint8_t *I = base(); I < End; I += GOFF::RecordLength) {70uint8_t RecordType = (I[1] & 0xF0) >> 4;71bool IsContinuation = I[1] & 0x02;72bool PrevWasContinued = PrevContinuationBits & 0x01;73size_t RecordNum = (I - base()) / GOFF::RecordLength;7475// If the previous record was continued, the current record should be a76// continuation.77if (PrevWasContinued && !IsContinuation) {78if (PrevRecordType == RecordType) {79Err = createStringError(object_error::parse_failed,80"record " + std::to_string(RecordNum) +81" is not a continuation record but the "82"preceding record is continued");83return;84}85}86// Don't parse continuations records, only parse initial record.87if (IsContinuation) {88if (RecordType != PrevRecordType) {89Err = createStringError(object_error::parse_failed,90"record " + std::to_string(RecordNum) +91" is a continuation record that does not "92"match the type of the previous record");93return;94}95if (!PrevWasContinued) {96Err = createStringError(object_error::parse_failed,97"record " + std::to_string(RecordNum) +98" is a continuation record that is not "99"preceded by a continued record");100return;101}102PrevRecordType = RecordType;103PrevContinuationBits = I[1] & 0x03;104continue;105}106LLVM_DEBUG(for (size_t J = 0; J < GOFF::RecordLength; ++J) {107const uint8_t *P = I + J;108if (J % 8 == 0)109dbgs() << " ";110dbgs() << format("%02hhX", *P);111});112113switch (RecordType) {114case GOFF::RT_ESD: {115// Save ESD record.116uint32_t EsdId;117ESDRecord::getEsdId(I, EsdId);118EsdPtrs.grow(EsdId);119EsdPtrs[EsdId] = I;120121// Determine and save the "sections" in GOFF.122// A section is saved as a tuple of the form123// case (1): (ED,child PR)124// - where the PR must have non-zero length.125// case (2a) (ED,0)126// - where the ED is of non-zero length.127// case (2b) (ED,0)128// - where the ED is zero length but129// contains a label (LD).130GOFF::ESDSymbolType SymbolType;131ESDRecord::getSymbolType(I, SymbolType);132SectionEntryImpl Section;133uint32_t Length;134ESDRecord::getLength(I, Length);135if (SymbolType == GOFF::ESD_ST_ElementDefinition) {136// case (2a)137if (Length != 0) {138Section.d.a = EsdId;139SectionList.emplace_back(Section);140}141} else if (SymbolType == GOFF::ESD_ST_PartReference) {142// case (1)143if (Length != 0) {144uint32_t SymEdId;145ESDRecord::getParentEsdId(I, SymEdId);146Section.d.a = SymEdId;147Section.d.b = EsdId;148SectionList.emplace_back(Section);149}150} else if (SymbolType == GOFF::ESD_ST_LabelDefinition) {151// case (2b)152uint32_t SymEdId;153ESDRecord::getParentEsdId(I, SymEdId);154const uint8_t *SymEdRecord = EsdPtrs[SymEdId];155uint32_t EdLength;156ESDRecord::getLength(SymEdRecord, EdLength);157if (!EdLength) { // [ EDID, PRID ]158// LD child of a zero length parent ED.159// Add the section ED which was previously ignored.160Section.d.a = SymEdId;161SectionList.emplace_back(Section);162}163}164LLVM_DEBUG(dbgs() << " -- ESD " << EsdId << "\n");165break;166}167case GOFF::RT_TXT:168// Save TXT records.169TextPtrs.emplace_back(I);170LLVM_DEBUG(dbgs() << " -- TXT\n");171break;172case GOFF::RT_END:173LLVM_DEBUG(dbgs() << " -- END (GOFF record type) unhandled\n");174break;175case GOFF::RT_HDR:176LLVM_DEBUG(dbgs() << " -- HDR (GOFF record type) unhandled\n");177break;178default:179llvm_unreachable("Unknown record type");180}181PrevRecordType = RecordType;182PrevContinuationBits = I[1] & 0x03;183}184}185186const uint8_t *GOFFObjectFile::getSymbolEsdRecord(DataRefImpl Symb) const {187const uint8_t *EsdRecord = EsdPtrs[Symb.d.a];188return EsdRecord;189}190191Expected<StringRef> GOFFObjectFile::getSymbolName(DataRefImpl Symb) const {192if (EsdNamesCache.count(Symb.d.a)) {193auto &StrPtr = EsdNamesCache[Symb.d.a];194return StringRef(StrPtr.second.get(), StrPtr.first);195}196197SmallString<256> SymbolName;198if (auto Err = ESDRecord::getData(getSymbolEsdRecord(Symb), SymbolName))199return std::move(Err);200201SmallString<256> SymbolNameConverted;202ConverterEBCDIC::convertToUTF8(SymbolName, SymbolNameConverted);203204size_t Size = SymbolNameConverted.size();205auto StrPtr = std::make_pair(Size, std::make_unique<char[]>(Size));206char *Buf = StrPtr.second.get();207memcpy(Buf, SymbolNameConverted.data(), Size);208EsdNamesCache[Symb.d.a] = std::move(StrPtr);209return StringRef(Buf, Size);210}211212Expected<StringRef> GOFFObjectFile::getSymbolName(SymbolRef Symbol) const {213return getSymbolName(Symbol.getRawDataRefImpl());214}215216Expected<uint64_t> GOFFObjectFile::getSymbolAddress(DataRefImpl Symb) const {217uint32_t Offset;218const uint8_t *EsdRecord = getSymbolEsdRecord(Symb);219ESDRecord::getOffset(EsdRecord, Offset);220return static_cast<uint64_t>(Offset);221}222223uint64_t GOFFObjectFile::getSymbolValueImpl(DataRefImpl Symb) const {224uint32_t Offset;225const uint8_t *EsdRecord = getSymbolEsdRecord(Symb);226ESDRecord::getOffset(EsdRecord, Offset);227return static_cast<uint64_t>(Offset);228}229230uint64_t GOFFObjectFile::getCommonSymbolSizeImpl(DataRefImpl Symb) const {231return 0;232}233234bool GOFFObjectFile::isSymbolUnresolved(DataRefImpl Symb) const {235const uint8_t *Record = getSymbolEsdRecord(Symb);236GOFF::ESDSymbolType SymbolType;237ESDRecord::getSymbolType(Record, SymbolType);238239if (SymbolType == GOFF::ESD_ST_ExternalReference)240return true;241if (SymbolType == GOFF::ESD_ST_PartReference) {242uint32_t Length;243ESDRecord::getLength(Record, Length);244if (Length == 0)245return true;246}247return false;248}249250bool GOFFObjectFile::isSymbolIndirect(DataRefImpl Symb) const {251const uint8_t *Record = getSymbolEsdRecord(Symb);252bool Indirect;253ESDRecord::getIndirectReference(Record, Indirect);254return Indirect;255}256257Expected<uint32_t> GOFFObjectFile::getSymbolFlags(DataRefImpl Symb) const {258uint32_t Flags = 0;259if (isSymbolUnresolved(Symb))260Flags |= SymbolRef::SF_Undefined;261262const uint8_t *Record = getSymbolEsdRecord(Symb);263264GOFF::ESDBindingStrength BindingStrength;265ESDRecord::getBindingStrength(Record, BindingStrength);266if (BindingStrength == GOFF::ESD_BST_Weak)267Flags |= SymbolRef::SF_Weak;268269GOFF::ESDBindingScope BindingScope;270ESDRecord::getBindingScope(Record, BindingScope);271272if (BindingScope != GOFF::ESD_BSC_Section) {273Expected<StringRef> Name = getSymbolName(Symb);274if (Name && *Name != " ") { // Blank name is local.275Flags |= SymbolRef::SF_Global;276if (BindingScope == GOFF::ESD_BSC_ImportExport)277Flags |= SymbolRef::SF_Exported;278else if (!(Flags & SymbolRef::SF_Undefined))279Flags |= SymbolRef::SF_Hidden;280}281}282283return Flags;284}285286Expected<SymbolRef::Type>287GOFFObjectFile::getSymbolType(DataRefImpl Symb) const {288const uint8_t *Record = getSymbolEsdRecord(Symb);289GOFF::ESDSymbolType SymbolType;290ESDRecord::getSymbolType(Record, SymbolType);291GOFF::ESDExecutable Executable;292ESDRecord::getExecutable(Record, Executable);293294if (SymbolType != GOFF::ESD_ST_SectionDefinition &&295SymbolType != GOFF::ESD_ST_ElementDefinition &&296SymbolType != GOFF::ESD_ST_LabelDefinition &&297SymbolType != GOFF::ESD_ST_PartReference &&298SymbolType != GOFF::ESD_ST_ExternalReference) {299uint32_t EsdId;300ESDRecord::getEsdId(Record, EsdId);301return createStringError(llvm::errc::invalid_argument,302"ESD record %" PRIu32303" has invalid symbol type 0x%02" PRIX8,304EsdId, SymbolType);305}306switch (SymbolType) {307case GOFF::ESD_ST_SectionDefinition:308case GOFF::ESD_ST_ElementDefinition:309return SymbolRef::ST_Other;310case GOFF::ESD_ST_LabelDefinition:311case GOFF::ESD_ST_PartReference:312case GOFF::ESD_ST_ExternalReference:313if (Executable != GOFF::ESD_EXE_CODE && Executable != GOFF::ESD_EXE_DATA &&314Executable != GOFF::ESD_EXE_Unspecified) {315uint32_t EsdId;316ESDRecord::getEsdId(Record, EsdId);317return createStringError(llvm::errc::invalid_argument,318"ESD record %" PRIu32319" has unknown Executable type 0x%02X",320EsdId, Executable);321}322switch (Executable) {323case GOFF::ESD_EXE_CODE:324return SymbolRef::ST_Function;325case GOFF::ESD_EXE_DATA:326return SymbolRef::ST_Data;327case GOFF::ESD_EXE_Unspecified:328return SymbolRef::ST_Unknown;329}330llvm_unreachable("Unhandled ESDExecutable");331}332llvm_unreachable("Unhandled ESDSymbolType");333}334335Expected<section_iterator>336GOFFObjectFile::getSymbolSection(DataRefImpl Symb) const {337DataRefImpl Sec;338339if (isSymbolUnresolved(Symb))340return section_iterator(SectionRef(Sec, this));341342const uint8_t *SymEsdRecord = EsdPtrs[Symb.d.a];343uint32_t SymEdId;344ESDRecord::getParentEsdId(SymEsdRecord, SymEdId);345const uint8_t *SymEdRecord = EsdPtrs[SymEdId];346347for (size_t I = 0, E = SectionList.size(); I < E; ++I) {348bool Found;349const uint8_t *SectionPrRecord = getSectionPrEsdRecord(I);350if (SectionPrRecord) {351Found = SymEsdRecord == SectionPrRecord;352} else {353const uint8_t *SectionEdRecord = getSectionEdEsdRecord(I);354Found = SymEdRecord == SectionEdRecord;355}356357if (Found) {358Sec.d.a = I;359return section_iterator(SectionRef(Sec, this));360}361}362return createStringError(llvm::errc::invalid_argument,363"symbol with ESD id " + std::to_string(Symb.d.a) +364" refers to invalid section with ESD id " +365std::to_string(SymEdId));366}367368uint64_t GOFFObjectFile::getSymbolSize(DataRefImpl Symb) const {369const uint8_t *Record = getSymbolEsdRecord(Symb);370uint32_t Length;371ESDRecord::getLength(Record, Length);372return Length;373}374375const uint8_t *GOFFObjectFile::getSectionEdEsdRecord(DataRefImpl &Sec) const {376SectionEntryImpl EsdIds = SectionList[Sec.d.a];377const uint8_t *EsdRecord = EsdPtrs[EsdIds.d.a];378return EsdRecord;379}380381const uint8_t *GOFFObjectFile::getSectionPrEsdRecord(DataRefImpl &Sec) const {382SectionEntryImpl EsdIds = SectionList[Sec.d.a];383const uint8_t *EsdRecord = nullptr;384if (EsdIds.d.b)385EsdRecord = EsdPtrs[EsdIds.d.b];386return EsdRecord;387}388389const uint8_t *390GOFFObjectFile::getSectionEdEsdRecord(uint32_t SectionIndex) const {391DataRefImpl Sec;392Sec.d.a = SectionIndex;393const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec);394return EsdRecord;395}396397const uint8_t *398GOFFObjectFile::getSectionPrEsdRecord(uint32_t SectionIndex) const {399DataRefImpl Sec;400Sec.d.a = SectionIndex;401const uint8_t *EsdRecord = getSectionPrEsdRecord(Sec);402return EsdRecord;403}404405uint32_t GOFFObjectFile::getSectionDefEsdId(DataRefImpl &Sec) const {406const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec);407uint32_t Length;408ESDRecord::getLength(EsdRecord, Length);409if (Length == 0) {410const uint8_t *PrEsdRecord = getSectionPrEsdRecord(Sec);411if (PrEsdRecord)412EsdRecord = PrEsdRecord;413}414415uint32_t DefEsdId;416ESDRecord::getEsdId(EsdRecord, DefEsdId);417LLVM_DEBUG(dbgs() << "Got def EsdId: " << DefEsdId << '\n');418return DefEsdId;419}420421void GOFFObjectFile::moveSectionNext(DataRefImpl &Sec) const {422Sec.d.a++;423if ((Sec.d.a) >= SectionList.size())424Sec.d.a = 0;425}426427Expected<StringRef> GOFFObjectFile::getSectionName(DataRefImpl Sec) const {428DataRefImpl EdSym;429SectionEntryImpl EsdIds = SectionList[Sec.d.a];430EdSym.d.a = EsdIds.d.a;431Expected<StringRef> Name = getSymbolName(EdSym);432if (Name) {433StringRef Res = *Name;434LLVM_DEBUG(dbgs() << "Got section: " << Res << '\n');435LLVM_DEBUG(dbgs() << "Final section name: " << Res << '\n');436Name = Res;437}438return Name;439}440441uint64_t GOFFObjectFile::getSectionAddress(DataRefImpl Sec) const {442uint32_t Offset;443const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec);444ESDRecord::getOffset(EsdRecord, Offset);445return Offset;446}447448uint64_t GOFFObjectFile::getSectionSize(DataRefImpl Sec) const {449uint32_t Length;450uint32_t DefEsdId = getSectionDefEsdId(Sec);451const uint8_t *EsdRecord = EsdPtrs[DefEsdId];452ESDRecord::getLength(EsdRecord, Length);453LLVM_DEBUG(dbgs() << "Got section size: " << Length << '\n');454return static_cast<uint64_t>(Length);455}456457// Unravel TXT records and expand fill characters to produce458// a contiguous sequence of bytes.459Expected<ArrayRef<uint8_t>>460GOFFObjectFile::getSectionContents(DataRefImpl Sec) const {461if (SectionDataCache.count(Sec.d.a)) {462auto &Buf = SectionDataCache[Sec.d.a];463return ArrayRef<uint8_t>(Buf);464}465uint64_t SectionSize = getSectionSize(Sec);466uint32_t DefEsdId = getSectionDefEsdId(Sec);467468const uint8_t *EdEsdRecord = getSectionEdEsdRecord(Sec);469bool FillBytePresent;470ESDRecord::getFillBytePresent(EdEsdRecord, FillBytePresent);471uint8_t FillByte = '\0';472if (FillBytePresent)473ESDRecord::getFillByteValue(EdEsdRecord, FillByte);474475// Initialize section with fill byte.476SmallVector<uint8_t> Data(SectionSize, FillByte);477478// Replace section with content from text records.479for (const uint8_t *TxtRecordInt : TextPtrs) {480const uint8_t *TxtRecordPtr = TxtRecordInt;481uint32_t TxtEsdId;482TXTRecord::getElementEsdId(TxtRecordPtr, TxtEsdId);483LLVM_DEBUG(dbgs() << "Got txt EsdId: " << TxtEsdId << '\n');484485if (TxtEsdId != DefEsdId)486continue;487488uint32_t TxtDataOffset;489TXTRecord::getOffset(TxtRecordPtr, TxtDataOffset);490491uint16_t TxtDataSize;492TXTRecord::getDataLength(TxtRecordPtr, TxtDataSize);493494LLVM_DEBUG(dbgs() << "Record offset " << TxtDataOffset << ", data size "495<< TxtDataSize << "\n");496497SmallString<256> CompleteData;498CompleteData.reserve(TxtDataSize);499if (Error Err = TXTRecord::getData(TxtRecordPtr, CompleteData))500return std::move(Err);501assert(CompleteData.size() == TxtDataSize && "Wrong length of data");502std::copy(CompleteData.data(), CompleteData.data() + TxtDataSize,503Data.begin() + TxtDataOffset);504}505SectionDataCache[Sec.d.a] = Data;506return ArrayRef<uint8_t>(SectionDataCache[Sec.d.a]);507}508509uint64_t GOFFObjectFile::getSectionAlignment(DataRefImpl Sec) const {510const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec);511GOFF::ESDAlignment Pow2Alignment;512ESDRecord::getAlignment(EsdRecord, Pow2Alignment);513return 1ULL << static_cast<uint64_t>(Pow2Alignment);514}515516bool GOFFObjectFile::isSectionText(DataRefImpl Sec) const {517const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec);518GOFF::ESDExecutable Executable;519ESDRecord::getExecutable(EsdRecord, Executable);520return Executable == GOFF::ESD_EXE_CODE;521}522523bool GOFFObjectFile::isSectionData(DataRefImpl Sec) const {524const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec);525GOFF::ESDExecutable Executable;526ESDRecord::getExecutable(EsdRecord, Executable);527return Executable == GOFF::ESD_EXE_DATA;528}529530bool GOFFObjectFile::isSectionNoLoad(DataRefImpl Sec) const {531const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec);532GOFF::ESDLoadingBehavior LoadingBehavior;533ESDRecord::getLoadingBehavior(EsdRecord, LoadingBehavior);534return LoadingBehavior == GOFF::ESD_LB_NoLoad;535}536537bool GOFFObjectFile::isSectionReadOnlyData(DataRefImpl Sec) const {538if (!isSectionData(Sec))539return false;540541const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec);542GOFF::ESDLoadingBehavior LoadingBehavior;543ESDRecord::getLoadingBehavior(EsdRecord, LoadingBehavior);544return LoadingBehavior == GOFF::ESD_LB_Initial;545}546547bool GOFFObjectFile::isSectionZeroInit(DataRefImpl Sec) const {548// GOFF uses fill characters and fill characters are applied549// on getSectionContents() - so we say false to zero init.550return false;551}552553section_iterator GOFFObjectFile::section_begin() const {554DataRefImpl Sec;555moveSectionNext(Sec);556return section_iterator(SectionRef(Sec, this));557}558559section_iterator GOFFObjectFile::section_end() const {560DataRefImpl Sec;561return section_iterator(SectionRef(Sec, this));562}563564void GOFFObjectFile::moveSymbolNext(DataRefImpl &Symb) const {565for (uint32_t I = Symb.d.a + 1, E = EsdPtrs.size(); I < E; ++I) {566if (EsdPtrs[I]) {567const uint8_t *EsdRecord = EsdPtrs[I];568GOFF::ESDSymbolType SymbolType;569ESDRecord::getSymbolType(EsdRecord, SymbolType);570// Skip EDs - i.e. section symbols.571bool IgnoreSpecialGOFFSymbols = true;572bool SkipSymbol = ((SymbolType == GOFF::ESD_ST_ElementDefinition) ||573(SymbolType == GOFF::ESD_ST_SectionDefinition)) &&574IgnoreSpecialGOFFSymbols;575if (!SkipSymbol) {576Symb.d.a = I;577return;578}579}580}581Symb.d.a = 0;582}583584basic_symbol_iterator GOFFObjectFile::symbol_begin() const {585DataRefImpl Symb;586moveSymbolNext(Symb);587return basic_symbol_iterator(SymbolRef(Symb, this));588}589590basic_symbol_iterator GOFFObjectFile::symbol_end() const {591DataRefImpl Symb;592return basic_symbol_iterator(SymbolRef(Symb, this));593}594595Error Record::getContinuousData(const uint8_t *Record, uint16_t DataLength,596int DataIndex, SmallString<256> &CompleteData) {597// First record.598const uint8_t *Slice = Record + DataIndex;599size_t SliceLength =600std::min(DataLength, (uint16_t)(GOFF::RecordLength - DataIndex));601CompleteData.append(Slice, Slice + SliceLength);602DataLength -= SliceLength;603Slice += SliceLength;604605// Continuation records.606for (; DataLength > 0;607DataLength -= SliceLength, Slice += GOFF::PayloadLength) {608// Slice points to the start of the new record.609// Check that this block is a Continuation.610assert(Record::isContinuation(Slice) && "Continuation bit must be set");611// Check that the last Continuation is terminated correctly.612if (DataLength <= 77 && Record::isContinued(Slice))613return createStringError(object_error::parse_failed,614"continued bit should not be set");615616SliceLength = std::min(DataLength, (uint16_t)GOFF::PayloadLength);617Slice += GOFF::RecordPrefixLength;618CompleteData.append(Slice, Slice + SliceLength);619}620return Error::success();621}622623Error HDRRecord::getData(const uint8_t *Record,624SmallString<256> &CompleteData) {625uint16_t Length = getPropertyModuleLength(Record);626return getContinuousData(Record, Length, 60, CompleteData);627}628629Error ESDRecord::getData(const uint8_t *Record,630SmallString<256> &CompleteData) {631uint16_t DataSize = getNameLength(Record);632return getContinuousData(Record, DataSize, 72, CompleteData);633}634635Error TXTRecord::getData(const uint8_t *Record,636SmallString<256> &CompleteData) {637uint16_t Length;638getDataLength(Record, Length);639return getContinuousData(Record, Length, 24, CompleteData);640}641642Error ENDRecord::getData(const uint8_t *Record,643SmallString<256> &CompleteData) {644uint16_t Length = getNameLength(Record);645return getContinuousData(Record, Length, 26, CompleteData);646}647648649