1 //===- GOFFObjectFile.cpp - GOFF object file implementation -----*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // Implementation of the GOFFObjectFile class.
11 //===----------------------------------------------------------------------===//
13 #include "llvm/Object/GOFFObjectFile.h"
14 #include "llvm/BinaryFormat/GOFF.h"
15 #include "llvm/Object/GOFF.h"
16 #include "llvm/Support/Debug.h"
17 #include "llvm/Support/Errc.h"
18 #include "llvm/Support/raw_ostream.h"
21 #define DEBUG_TYPE "goff"
24 using namespace llvm::object
;
27 Expected
<std::unique_ptr
<ObjectFile
>>
28 ObjectFile::createGOFFObjectFile(MemoryBufferRef Object
) {
29 Error Err
= Error::success();
30 std::unique_ptr
<GOFFObjectFile
> Ret(new GOFFObjectFile(Object
, Err
));
32 return std::move(Err
);
33 return std::move(Ret
);
36 GOFFObjectFile::GOFFObjectFile(MemoryBufferRef Object
, Error
&Err
)
37 : ObjectFile(Binary::ID_GOFF
, Object
) {
38 ErrorAsOutParameter
ErrAsOutParam(Err
);
39 // Object file isn't the right size, bail out early.
40 if ((Object
.getBufferSize() % GOFF::RecordLength
) != 0) {
41 Err
= createStringError(
42 object_error::unexpected_eof
,
43 "object file is not the right size. Must be a multiple "
44 "of 80 bytes, but is " +
45 std::to_string(Object
.getBufferSize()) + " bytes");
48 // Object file doesn't start/end with HDR/END records.
50 if (Object
.getBufferSize() != 0) {
51 if ((base()[1] & 0xF0) >> 4 != GOFF::RT_HDR
) {
52 Err
= createStringError(object_error::parse_failed
,
53 "object file must start with HDR record");
56 if ((base()[Object
.getBufferSize() - GOFF::RecordLength
+ 1] & 0xF0) >> 4 !=
58 Err
= createStringError(object_error::parse_failed
,
59 "object file must end with END record");
64 SectionEntryImpl DummySection
;
65 SectionList
.emplace_back(DummySection
); // Dummy entry at index 0.
67 uint8_t PrevRecordType
= 0;
68 uint8_t PrevContinuationBits
= 0;
69 const uint8_t *End
= reinterpret_cast<const uint8_t *>(Data
.getBufferEnd());
70 for (const uint8_t *I
= base(); I
< End
; I
+= GOFF::RecordLength
) {
71 uint8_t RecordType
= (I
[1] & 0xF0) >> 4;
72 bool IsContinuation
= I
[1] & 0x02;
73 bool PrevWasContinued
= PrevContinuationBits
& 0x01;
74 size_t RecordNum
= (I
- base()) / GOFF::RecordLength
;
76 // If the previous record was continued, the current record should be a
78 if (PrevWasContinued
&& !IsContinuation
) {
79 if (PrevRecordType
== RecordType
) {
80 Err
= createStringError(object_error::parse_failed
,
81 "record " + std::to_string(RecordNum
) +
82 " is not a continuation record but the "
83 "preceding record is continued");
87 // Don't parse continuations records, only parse initial record.
89 if (RecordType
!= PrevRecordType
) {
90 Err
= createStringError(object_error::parse_failed
,
91 "record " + std::to_string(RecordNum
) +
92 " is a continuation record that does not "
93 "match the type of the previous record");
96 if (!PrevWasContinued
) {
97 Err
= createStringError(object_error::parse_failed
,
98 "record " + std::to_string(RecordNum
) +
99 " is a continuation record that is not "
100 "preceded by a continued record");
103 PrevRecordType
= RecordType
;
104 PrevContinuationBits
= I
[1] & 0x03;
107 LLVM_DEBUG(for (size_t J
= 0; J
< GOFF::RecordLength
; ++J
) {
108 const uint8_t *P
= I
+ J
;
111 dbgs() << format("%02hhX", *P
);
114 switch (RecordType
) {
118 ESDRecord::getEsdId(I
, EsdId
);
122 // Determine and save the "sections" in GOFF.
123 // A section is saved as a tuple of the form
124 // case (1): (ED,child PR)
125 // - where the PR must have non-zero length.
127 // - where the ED is of non-zero length.
129 // - where the ED is zero length but
130 // contains a label (LD).
131 GOFF::ESDSymbolType SymbolType
;
132 ESDRecord::getSymbolType(I
, SymbolType
);
133 SectionEntryImpl Section
;
135 ESDRecord::getLength(I
, Length
);
136 if (SymbolType
== GOFF::ESD_ST_ElementDefinition
) {
140 SectionList
.emplace_back(Section
);
142 } else if (SymbolType
== GOFF::ESD_ST_PartReference
) {
146 ESDRecord::getParentEsdId(I
, SymEdId
);
147 Section
.d
.a
= SymEdId
;
149 SectionList
.emplace_back(Section
);
151 } else if (SymbolType
== GOFF::ESD_ST_LabelDefinition
) {
154 ESDRecord::getParentEsdId(I
, SymEdId
);
155 const uint8_t *SymEdRecord
= EsdPtrs
[SymEdId
];
157 ESDRecord::getLength(SymEdRecord
, EdLength
);
158 if (!EdLength
) { // [ EDID, PRID ]
159 // LD child of a zero length parent ED.
160 // Add the section ED which was previously ignored.
161 Section
.d
.a
= SymEdId
;
162 SectionList
.emplace_back(Section
);
165 LLVM_DEBUG(dbgs() << " -- ESD " << EsdId
<< "\n");
170 TextPtrs
.emplace_back(I
);
171 LLVM_DEBUG(dbgs() << " -- TXT\n");
174 LLVM_DEBUG(dbgs() << " -- END (GOFF record type) unhandled\n");
177 LLVM_DEBUG(dbgs() << " -- HDR (GOFF record type) unhandled\n");
180 llvm_unreachable("Unknown record type");
182 PrevRecordType
= RecordType
;
183 PrevContinuationBits
= I
[1] & 0x03;
187 const uint8_t *GOFFObjectFile::getSymbolEsdRecord(DataRefImpl Symb
) const {
188 const uint8_t *EsdRecord
= EsdPtrs
[Symb
.d
.a
];
192 Expected
<StringRef
> GOFFObjectFile::getSymbolName(DataRefImpl Symb
) const {
193 if (EsdNamesCache
.count(Symb
.d
.a
)) {
194 auto &StrPtr
= EsdNamesCache
[Symb
.d
.a
];
195 return StringRef(StrPtr
.second
.get(), StrPtr
.first
);
198 SmallString
<256> SymbolName
;
199 if (auto Err
= ESDRecord::getData(getSymbolEsdRecord(Symb
), SymbolName
))
200 return std::move(Err
);
202 SmallString
<256> SymbolNameConverted
;
203 ConverterEBCDIC::convertToUTF8(SymbolName
, SymbolNameConverted
);
205 size_t Size
= SymbolNameConverted
.size();
206 auto StrPtr
= std::make_pair(Size
, std::make_unique
<char[]>(Size
));
207 char *Buf
= StrPtr
.second
.get();
208 memcpy(Buf
, SymbolNameConverted
.data(), Size
);
209 EsdNamesCache
[Symb
.d
.a
] = std::move(StrPtr
);
210 return StringRef(Buf
, Size
);
213 Expected
<StringRef
> GOFFObjectFile::getSymbolName(SymbolRef Symbol
) const {
214 return getSymbolName(Symbol
.getRawDataRefImpl());
217 Expected
<uint64_t> GOFFObjectFile::getSymbolAddress(DataRefImpl Symb
) const {
219 const uint8_t *EsdRecord
= getSymbolEsdRecord(Symb
);
220 ESDRecord::getOffset(EsdRecord
, Offset
);
221 return static_cast<uint64_t>(Offset
);
224 uint64_t GOFFObjectFile::getSymbolValueImpl(DataRefImpl Symb
) const {
226 const uint8_t *EsdRecord
= getSymbolEsdRecord(Symb
);
227 ESDRecord::getOffset(EsdRecord
, Offset
);
228 return static_cast<uint64_t>(Offset
);
231 uint64_t GOFFObjectFile::getCommonSymbolSizeImpl(DataRefImpl Symb
) const {
235 bool GOFFObjectFile::isSymbolUnresolved(DataRefImpl Symb
) const {
236 const uint8_t *Record
= getSymbolEsdRecord(Symb
);
237 GOFF::ESDSymbolType SymbolType
;
238 ESDRecord::getSymbolType(Record
, SymbolType
);
240 if (SymbolType
== GOFF::ESD_ST_ExternalReference
)
242 if (SymbolType
== GOFF::ESD_ST_PartReference
) {
244 ESDRecord::getLength(Record
, Length
);
251 bool GOFFObjectFile::isSymbolIndirect(DataRefImpl Symb
) const {
252 const uint8_t *Record
= getSymbolEsdRecord(Symb
);
254 ESDRecord::getIndirectReference(Record
, Indirect
);
258 Expected
<uint32_t> GOFFObjectFile::getSymbolFlags(DataRefImpl Symb
) const {
260 if (isSymbolUnresolved(Symb
))
261 Flags
|= SymbolRef::SF_Undefined
;
263 const uint8_t *Record
= getSymbolEsdRecord(Symb
);
265 GOFF::ESDBindingStrength BindingStrength
;
266 ESDRecord::getBindingStrength(Record
, BindingStrength
);
267 if (BindingStrength
== GOFF::ESD_BST_Weak
)
268 Flags
|= SymbolRef::SF_Weak
;
270 GOFF::ESDBindingScope BindingScope
;
271 ESDRecord::getBindingScope(Record
, BindingScope
);
273 if (BindingScope
!= GOFF::ESD_BSC_Section
) {
274 Expected
<StringRef
> Name
= getSymbolName(Symb
);
275 if (Name
&& *Name
!= " ") { // Blank name is local.
276 Flags
|= SymbolRef::SF_Global
;
277 if (BindingScope
== GOFF::ESD_BSC_ImportExport
)
278 Flags
|= SymbolRef::SF_Exported
;
279 else if (!(Flags
& SymbolRef::SF_Undefined
))
280 Flags
|= SymbolRef::SF_Hidden
;
287 Expected
<SymbolRef::Type
>
288 GOFFObjectFile::getSymbolType(DataRefImpl Symb
) const {
289 const uint8_t *Record
= getSymbolEsdRecord(Symb
);
290 GOFF::ESDSymbolType SymbolType
;
291 ESDRecord::getSymbolType(Record
, SymbolType
);
292 GOFF::ESDExecutable Executable
;
293 ESDRecord::getExecutable(Record
, Executable
);
295 if (SymbolType
!= GOFF::ESD_ST_SectionDefinition
&&
296 SymbolType
!= GOFF::ESD_ST_ElementDefinition
&&
297 SymbolType
!= GOFF::ESD_ST_LabelDefinition
&&
298 SymbolType
!= GOFF::ESD_ST_PartReference
&&
299 SymbolType
!= GOFF::ESD_ST_ExternalReference
) {
301 ESDRecord::getEsdId(Record
, EsdId
);
302 return createStringError(llvm::errc::invalid_argument
,
303 "ESD record %" PRIu32
304 " has invalid symbol type 0x%02" PRIX8
,
307 switch (SymbolType
) {
308 case GOFF::ESD_ST_SectionDefinition
:
309 case GOFF::ESD_ST_ElementDefinition
:
310 return SymbolRef::ST_Other
;
311 case GOFF::ESD_ST_LabelDefinition
:
312 case GOFF::ESD_ST_PartReference
:
313 case GOFF::ESD_ST_ExternalReference
:
314 if (Executable
!= GOFF::ESD_EXE_CODE
&& Executable
!= GOFF::ESD_EXE_DATA
&&
315 Executable
!= GOFF::ESD_EXE_Unspecified
) {
317 ESDRecord::getEsdId(Record
, EsdId
);
318 return createStringError(llvm::errc::invalid_argument
,
319 "ESD record %" PRIu32
320 " has unknown Executable type 0x%02X",
323 switch (Executable
) {
324 case GOFF::ESD_EXE_CODE
:
325 return SymbolRef::ST_Function
;
326 case GOFF::ESD_EXE_DATA
:
327 return SymbolRef::ST_Data
;
328 case GOFF::ESD_EXE_Unspecified
:
329 return SymbolRef::ST_Unknown
;
331 llvm_unreachable("Unhandled ESDExecutable");
333 llvm_unreachable("Unhandled ESDSymbolType");
336 Expected
<section_iterator
>
337 GOFFObjectFile::getSymbolSection(DataRefImpl Symb
) const {
340 if (isSymbolUnresolved(Symb
))
341 return section_iterator(SectionRef(Sec
, this));
343 const uint8_t *SymEsdRecord
= EsdPtrs
[Symb
.d
.a
];
345 ESDRecord::getParentEsdId(SymEsdRecord
, SymEdId
);
346 const uint8_t *SymEdRecord
= EsdPtrs
[SymEdId
];
348 for (size_t I
= 0, E
= SectionList
.size(); I
< E
; ++I
) {
350 const uint8_t *SectionPrRecord
= getSectionPrEsdRecord(I
);
351 if (SectionPrRecord
) {
352 Found
= SymEsdRecord
== SectionPrRecord
;
354 const uint8_t *SectionEdRecord
= getSectionEdEsdRecord(I
);
355 Found
= SymEdRecord
== SectionEdRecord
;
360 return section_iterator(SectionRef(Sec
, this));
363 return createStringError(llvm::errc::invalid_argument
,
364 "symbol with ESD id " + std::to_string(Symb
.d
.a
) +
365 " refers to invalid section with ESD id " +
366 std::to_string(SymEdId
));
369 uint64_t GOFFObjectFile::getSymbolSize(DataRefImpl Symb
) const {
370 const uint8_t *Record
= getSymbolEsdRecord(Symb
);
372 ESDRecord::getLength(Record
, Length
);
376 const uint8_t *GOFFObjectFile::getSectionEdEsdRecord(DataRefImpl
&Sec
) const {
377 SectionEntryImpl EsdIds
= SectionList
[Sec
.d
.a
];
378 const uint8_t *EsdRecord
= EsdPtrs
[EsdIds
.d
.a
];
382 const uint8_t *GOFFObjectFile::getSectionPrEsdRecord(DataRefImpl
&Sec
) const {
383 SectionEntryImpl EsdIds
= SectionList
[Sec
.d
.a
];
384 const uint8_t *EsdRecord
= nullptr;
386 EsdRecord
= EsdPtrs
[EsdIds
.d
.b
];
391 GOFFObjectFile::getSectionEdEsdRecord(uint32_t SectionIndex
) const {
393 Sec
.d
.a
= SectionIndex
;
394 const uint8_t *EsdRecord
= getSectionEdEsdRecord(Sec
);
399 GOFFObjectFile::getSectionPrEsdRecord(uint32_t SectionIndex
) const {
401 Sec
.d
.a
= SectionIndex
;
402 const uint8_t *EsdRecord
= getSectionPrEsdRecord(Sec
);
406 uint32_t GOFFObjectFile::getSectionDefEsdId(DataRefImpl
&Sec
) const {
407 const uint8_t *EsdRecord
= getSectionEdEsdRecord(Sec
);
409 ESDRecord::getLength(EsdRecord
, Length
);
411 const uint8_t *PrEsdRecord
= getSectionPrEsdRecord(Sec
);
413 EsdRecord
= PrEsdRecord
;
417 ESDRecord::getEsdId(EsdRecord
, DefEsdId
);
418 LLVM_DEBUG(dbgs() << "Got def EsdId: " << DefEsdId
<< '\n');
422 void GOFFObjectFile::moveSectionNext(DataRefImpl
&Sec
) const {
424 if ((Sec
.d
.a
) >= SectionList
.size())
428 Expected
<StringRef
> GOFFObjectFile::getSectionName(DataRefImpl Sec
) const {
430 SectionEntryImpl EsdIds
= SectionList
[Sec
.d
.a
];
431 EdSym
.d
.a
= EsdIds
.d
.a
;
432 Expected
<StringRef
> Name
= getSymbolName(EdSym
);
434 StringRef Res
= *Name
;
435 LLVM_DEBUG(dbgs() << "Got section: " << Res
<< '\n');
436 LLVM_DEBUG(dbgs() << "Final section name: " << Res
<< '\n');
442 uint64_t GOFFObjectFile::getSectionAddress(DataRefImpl Sec
) const {
444 const uint8_t *EsdRecord
= getSectionEdEsdRecord(Sec
);
445 ESDRecord::getOffset(EsdRecord
, Offset
);
449 uint64_t GOFFObjectFile::getSectionSize(DataRefImpl Sec
) const {
451 uint32_t DefEsdId
= getSectionDefEsdId(Sec
);
452 const uint8_t *EsdRecord
= EsdPtrs
[DefEsdId
];
453 ESDRecord::getLength(EsdRecord
, Length
);
454 LLVM_DEBUG(dbgs() << "Got section size: " << Length
<< '\n');
455 return static_cast<uint64_t>(Length
);
458 // Unravel TXT records and expand fill characters to produce
459 // a contiguous sequence of bytes.
460 Expected
<ArrayRef
<uint8_t>>
461 GOFFObjectFile::getSectionContents(DataRefImpl Sec
) const {
462 if (SectionDataCache
.count(Sec
.d
.a
)) {
463 auto &Buf
= SectionDataCache
[Sec
.d
.a
];
464 return ArrayRef
<uint8_t>(Buf
);
466 uint64_t SectionSize
= getSectionSize(Sec
);
467 uint32_t DefEsdId
= getSectionDefEsdId(Sec
);
469 const uint8_t *EdEsdRecord
= getSectionEdEsdRecord(Sec
);
470 bool FillBytePresent
;
471 ESDRecord::getFillBytePresent(EdEsdRecord
, FillBytePresent
);
472 uint8_t FillByte
= '\0';
474 ESDRecord::getFillByteValue(EdEsdRecord
, FillByte
);
476 // Initialize section with fill byte.
477 SmallVector
<uint8_t> Data(SectionSize
, FillByte
);
479 // Replace section with content from text records.
480 for (const uint8_t *TxtRecordInt
: TextPtrs
) {
481 const uint8_t *TxtRecordPtr
= TxtRecordInt
;
483 TXTRecord::getElementEsdId(TxtRecordPtr
, TxtEsdId
);
484 LLVM_DEBUG(dbgs() << "Got txt EsdId: " << TxtEsdId
<< '\n');
486 if (TxtEsdId
!= DefEsdId
)
489 uint32_t TxtDataOffset
;
490 TXTRecord::getOffset(TxtRecordPtr
, TxtDataOffset
);
492 uint16_t TxtDataSize
;
493 TXTRecord::getDataLength(TxtRecordPtr
, TxtDataSize
);
495 LLVM_DEBUG(dbgs() << "Record offset " << TxtDataOffset
<< ", data size "
496 << TxtDataSize
<< "\n");
498 SmallString
<256> CompleteData
;
499 CompleteData
.reserve(TxtDataSize
);
500 if (Error Err
= TXTRecord::getData(TxtRecordPtr
, CompleteData
))
501 return std::move(Err
);
502 assert(CompleteData
.size() == TxtDataSize
&& "Wrong length of data");
503 std::copy(CompleteData
.data(), CompleteData
.data() + TxtDataSize
,
504 Data
.begin() + TxtDataOffset
);
506 SectionDataCache
[Sec
.d
.a
] = Data
;
507 return ArrayRef
<uint8_t>(SectionDataCache
[Sec
.d
.a
]);
510 uint64_t GOFFObjectFile::getSectionAlignment(DataRefImpl Sec
) const {
511 const uint8_t *EsdRecord
= getSectionEdEsdRecord(Sec
);
512 GOFF::ESDAlignment Pow2Alignment
;
513 ESDRecord::getAlignment(EsdRecord
, Pow2Alignment
);
514 return 1ULL << static_cast<uint64_t>(Pow2Alignment
);
517 bool GOFFObjectFile::isSectionText(DataRefImpl Sec
) const {
518 const uint8_t *EsdRecord
= getSectionEdEsdRecord(Sec
);
519 GOFF::ESDExecutable Executable
;
520 ESDRecord::getExecutable(EsdRecord
, Executable
);
521 return Executable
== GOFF::ESD_EXE_CODE
;
524 bool GOFFObjectFile::isSectionData(DataRefImpl Sec
) const {
525 const uint8_t *EsdRecord
= getSectionEdEsdRecord(Sec
);
526 GOFF::ESDExecutable Executable
;
527 ESDRecord::getExecutable(EsdRecord
, Executable
);
528 return Executable
== GOFF::ESD_EXE_DATA
;
531 bool GOFFObjectFile::isSectionNoLoad(DataRefImpl Sec
) const {
532 const uint8_t *EsdRecord
= getSectionEdEsdRecord(Sec
);
533 GOFF::ESDLoadingBehavior LoadingBehavior
;
534 ESDRecord::getLoadingBehavior(EsdRecord
, LoadingBehavior
);
535 return LoadingBehavior
== GOFF::ESD_LB_NoLoad
;
538 bool GOFFObjectFile::isSectionReadOnlyData(DataRefImpl Sec
) const {
539 if (!isSectionData(Sec
))
542 const uint8_t *EsdRecord
= getSectionEdEsdRecord(Sec
);
543 GOFF::ESDLoadingBehavior LoadingBehavior
;
544 ESDRecord::getLoadingBehavior(EsdRecord
, LoadingBehavior
);
545 return LoadingBehavior
== GOFF::ESD_LB_Initial
;
548 bool GOFFObjectFile::isSectionZeroInit(DataRefImpl Sec
) const {
549 // GOFF uses fill characters and fill characters are applied
550 // on getSectionContents() - so we say false to zero init.
554 section_iterator
GOFFObjectFile::section_begin() const {
556 moveSectionNext(Sec
);
557 return section_iterator(SectionRef(Sec
, this));
560 section_iterator
GOFFObjectFile::section_end() const {
562 return section_iterator(SectionRef(Sec
, this));
565 void GOFFObjectFile::moveSymbolNext(DataRefImpl
&Symb
) const {
566 for (uint32_t I
= Symb
.d
.a
+ 1, E
= EsdPtrs
.size(); I
< E
; ++I
) {
568 const uint8_t *EsdRecord
= EsdPtrs
[I
];
569 GOFF::ESDSymbolType SymbolType
;
570 ESDRecord::getSymbolType(EsdRecord
, SymbolType
);
571 // Skip EDs - i.e. section symbols.
572 bool IgnoreSpecialGOFFSymbols
= true;
573 bool SkipSymbol
= ((SymbolType
== GOFF::ESD_ST_ElementDefinition
) ||
574 (SymbolType
== GOFF::ESD_ST_SectionDefinition
)) &&
575 IgnoreSpecialGOFFSymbols
;
585 basic_symbol_iterator
GOFFObjectFile::symbol_begin() const {
587 moveSymbolNext(Symb
);
588 return basic_symbol_iterator(SymbolRef(Symb
, this));
591 basic_symbol_iterator
GOFFObjectFile::symbol_end() const {
593 return basic_symbol_iterator(SymbolRef(Symb
, this));
596 Error
Record::getContinuousData(const uint8_t *Record
, uint16_t DataLength
,
597 int DataIndex
, SmallString
<256> &CompleteData
) {
599 const uint8_t *Slice
= Record
+ DataIndex
;
601 std::min(DataLength
, (uint16_t)(GOFF::RecordLength
- DataIndex
));
602 CompleteData
.append(Slice
, Slice
+ SliceLength
);
603 DataLength
-= SliceLength
;
604 Slice
+= SliceLength
;
606 // Continuation records.
607 for (; DataLength
> 0;
608 DataLength
-= SliceLength
, Slice
+= GOFF::PayloadLength
) {
609 // Slice points to the start of the new record.
610 // Check that this block is a Continuation.
611 assert(Record::isContinuation(Slice
) && "Continuation bit must be set");
612 // Check that the last Continuation is terminated correctly.
613 if (DataLength
<= 77 && Record::isContinued(Slice
))
614 return createStringError(object_error::parse_failed
,
615 "continued bit should not be set");
617 SliceLength
= std::min(DataLength
, (uint16_t)GOFF::PayloadLength
);
618 Slice
+= GOFF::RecordPrefixLength
;
619 CompleteData
.append(Slice
, Slice
+ SliceLength
);
621 return Error::success();
624 Error
HDRRecord::getData(const uint8_t *Record
,
625 SmallString
<256> &CompleteData
) {
626 uint16_t Length
= getPropertyModuleLength(Record
);
627 return getContinuousData(Record
, Length
, 60, CompleteData
);
630 Error
ESDRecord::getData(const uint8_t *Record
,
631 SmallString
<256> &CompleteData
) {
632 uint16_t DataSize
= getNameLength(Record
);
633 return getContinuousData(Record
, DataSize
, 72, CompleteData
);
636 Error
TXTRecord::getData(const uint8_t *Record
,
637 SmallString
<256> &CompleteData
) {
639 getDataLength(Record
, Length
);
640 return getContinuousData(Record
, Length
, 24, CompleteData
);
643 Error
ENDRecord::getData(const uint8_t *Record
,
644 SmallString
<256> &CompleteData
) {
645 uint16_t Length
= getNameLength(Record
);
646 return getContinuousData(Record
, Length
, 26, CompleteData
);