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;
109 for (size_t J
= 0; J
< GOFF::RecordLength
; ++J
) {
110 const uint8_t *P
= I
+ J
;
114 dbgs() << format("%02hhX", *P
);
117 switch (RecordType
) {
121 ESDRecord::getEsdId(I
, EsdId
);
125 // Determine and save the "sections" in GOFF.
126 // A section is saved as a tuple of the form
127 // case (1): (ED,child PR)
128 // - where the PR must have non-zero length.
130 // - where the ED is of non-zero length.
132 // - where the ED is zero length but
133 // contains a label (LD).
134 GOFF::ESDSymbolType SymbolType
;
135 ESDRecord::getSymbolType(I
, SymbolType
);
136 SectionEntryImpl Section
;
138 ESDRecord::getLength(I
, Length
);
139 if (SymbolType
== GOFF::ESD_ST_ElementDefinition
) {
143 SectionList
.emplace_back(Section
);
145 } else if (SymbolType
== GOFF::ESD_ST_PartReference
) {
149 ESDRecord::getParentEsdId(I
, SymEdId
);
150 Section
.d
.a
= SymEdId
;
152 SectionList
.emplace_back(Section
);
154 } else if (SymbolType
== GOFF::ESD_ST_LabelDefinition
) {
157 ESDRecord::getParentEsdId(I
, SymEdId
);
158 const uint8_t *SymEdRecord
= EsdPtrs
[SymEdId
];
160 ESDRecord::getLength(SymEdRecord
, EdLength
);
161 if (!EdLength
) { // [ EDID, PRID ]
162 // LD child of a zero length parent ED.
163 // Add the section ED which was previously ignored.
164 Section
.d
.a
= SymEdId
;
165 SectionList
.emplace_back(Section
);
168 LLVM_DEBUG(dbgs() << " -- ESD " << EsdId
<< "\n");
172 LLVM_DEBUG(dbgs() << " -- END (GOFF record type) unhandled\n");
175 LLVM_DEBUG(dbgs() << " -- HDR (GOFF record type) unhandled\n");
178 llvm_unreachable("Unknown record type");
180 PrevRecordType
= RecordType
;
181 PrevContinuationBits
= I
[1] & 0x03;
185 const uint8_t *GOFFObjectFile::getSymbolEsdRecord(DataRefImpl Symb
) const {
186 const uint8_t *EsdRecord
= EsdPtrs
[Symb
.d
.a
];
190 Expected
<StringRef
> GOFFObjectFile::getSymbolName(DataRefImpl Symb
) const {
191 if (EsdNamesCache
.count(Symb
.d
.a
)) {
192 auto &StrPtr
= EsdNamesCache
[Symb
.d
.a
];
193 return StringRef(StrPtr
.second
.get(), StrPtr
.first
);
196 SmallString
<256> SymbolName
;
197 if (auto Err
= ESDRecord::getData(getSymbolEsdRecord(Symb
), SymbolName
))
198 return std::move(Err
);
200 SmallString
<256> SymbolNameConverted
;
201 ConverterEBCDIC::convertToUTF8(SymbolName
, SymbolNameConverted
);
203 size_t Size
= SymbolNameConverted
.size();
204 auto StrPtr
= std::make_pair(Size
, std::make_unique
<char[]>(Size
));
205 char *Buf
= StrPtr
.second
.get();
206 memcpy(Buf
, SymbolNameConverted
.data(), Size
);
207 EsdNamesCache
[Symb
.d
.a
] = std::move(StrPtr
);
208 return StringRef(Buf
, Size
);
211 Expected
<StringRef
> GOFFObjectFile::getSymbolName(SymbolRef Symbol
) const {
212 return getSymbolName(Symbol
.getRawDataRefImpl());
215 Expected
<uint64_t> GOFFObjectFile::getSymbolAddress(DataRefImpl Symb
) const {
217 const uint8_t *EsdRecord
= getSymbolEsdRecord(Symb
);
218 ESDRecord::getOffset(EsdRecord
, Offset
);
219 return static_cast<uint64_t>(Offset
);
222 uint64_t GOFFObjectFile::getSymbolValueImpl(DataRefImpl Symb
) const {
224 const uint8_t *EsdRecord
= getSymbolEsdRecord(Symb
);
225 ESDRecord::getOffset(EsdRecord
, Offset
);
226 return static_cast<uint64_t>(Offset
);
229 uint64_t GOFFObjectFile::getCommonSymbolSizeImpl(DataRefImpl Symb
) const {
233 bool GOFFObjectFile::isSymbolUnresolved(DataRefImpl Symb
) const {
234 const uint8_t *Record
= getSymbolEsdRecord(Symb
);
235 GOFF::ESDSymbolType SymbolType
;
236 ESDRecord::getSymbolType(Record
, SymbolType
);
238 if (SymbolType
== GOFF::ESD_ST_ExternalReference
)
240 if (SymbolType
== GOFF::ESD_ST_PartReference
) {
242 ESDRecord::getLength(Record
, Length
);
249 bool GOFFObjectFile::isSymbolIndirect(DataRefImpl Symb
) const {
250 const uint8_t *Record
= getSymbolEsdRecord(Symb
);
252 ESDRecord::getIndirectReference(Record
, Indirect
);
256 Expected
<uint32_t> GOFFObjectFile::getSymbolFlags(DataRefImpl Symb
) const {
258 if (isSymbolUnresolved(Symb
))
259 Flags
|= SymbolRef::SF_Undefined
;
261 const uint8_t *Record
= getSymbolEsdRecord(Symb
);
263 GOFF::ESDBindingStrength BindingStrength
;
264 ESDRecord::getBindingStrength(Record
, BindingStrength
);
265 if (BindingStrength
== GOFF::ESD_BST_Weak
)
266 Flags
|= SymbolRef::SF_Weak
;
268 GOFF::ESDBindingScope BindingScope
;
269 ESDRecord::getBindingScope(Record
, BindingScope
);
271 if (BindingScope
!= GOFF::ESD_BSC_Section
) {
272 Expected
<StringRef
> Name
= getSymbolName(Symb
);
273 if (Name
&& *Name
!= " ") { // Blank name is local.
274 Flags
|= SymbolRef::SF_Global
;
275 if (BindingScope
== GOFF::ESD_BSC_ImportExport
)
276 Flags
|= SymbolRef::SF_Exported
;
277 else if (!(Flags
& SymbolRef::SF_Undefined
))
278 Flags
|= SymbolRef::SF_Hidden
;
285 Expected
<SymbolRef::Type
>
286 GOFFObjectFile::getSymbolType(DataRefImpl Symb
) const {
287 const uint8_t *Record
= getSymbolEsdRecord(Symb
);
288 GOFF::ESDSymbolType SymbolType
;
289 ESDRecord::getSymbolType(Record
, SymbolType
);
290 GOFF::ESDExecutable Executable
;
291 ESDRecord::getExecutable(Record
, Executable
);
293 if (SymbolType
!= GOFF::ESD_ST_SectionDefinition
&&
294 SymbolType
!= GOFF::ESD_ST_ElementDefinition
&&
295 SymbolType
!= GOFF::ESD_ST_LabelDefinition
&&
296 SymbolType
!= GOFF::ESD_ST_PartReference
&&
297 SymbolType
!= GOFF::ESD_ST_ExternalReference
) {
299 ESDRecord::getEsdId(Record
, EsdId
);
300 return createStringError(llvm::errc::invalid_argument
,
301 "ESD record %" PRIu32
302 " has invalid symbol type 0x%02" PRIX8
,
305 switch (SymbolType
) {
306 case GOFF::ESD_ST_SectionDefinition
:
307 case GOFF::ESD_ST_ElementDefinition
:
308 return SymbolRef::ST_Other
;
309 case GOFF::ESD_ST_LabelDefinition
:
310 case GOFF::ESD_ST_PartReference
:
311 case GOFF::ESD_ST_ExternalReference
:
312 if (Executable
!= GOFF::ESD_EXE_CODE
&& Executable
!= GOFF::ESD_EXE_DATA
&&
313 Executable
!= GOFF::ESD_EXE_Unspecified
) {
315 ESDRecord::getEsdId(Record
, EsdId
);
316 return createStringError(llvm::errc::invalid_argument
,
317 "ESD record %" PRIu32
318 " has unknown Executable type 0x%02X",
321 switch (Executable
) {
322 case GOFF::ESD_EXE_CODE
:
323 return SymbolRef::ST_Function
;
324 case GOFF::ESD_EXE_DATA
:
325 return SymbolRef::ST_Data
;
326 case GOFF::ESD_EXE_Unspecified
:
327 return SymbolRef::ST_Unknown
;
329 llvm_unreachable("Unhandled ESDExecutable");
331 llvm_unreachable("Unhandled ESDSymbolType");
334 Expected
<section_iterator
>
335 GOFFObjectFile::getSymbolSection(DataRefImpl Symb
) const {
338 if (isSymbolUnresolved(Symb
))
339 return section_iterator(SectionRef(Sec
, this));
341 const uint8_t *SymEsdRecord
= EsdPtrs
[Symb
.d
.a
];
343 ESDRecord::getParentEsdId(SymEsdRecord
, SymEdId
);
344 const uint8_t *SymEdRecord
= EsdPtrs
[SymEdId
];
346 for (size_t I
= 0, E
= SectionList
.size(); I
< E
; ++I
) {
348 const uint8_t *SectionPrRecord
= getSectionPrEsdRecord(I
);
349 if (SectionPrRecord
) {
350 Found
= SymEsdRecord
== SectionPrRecord
;
352 const uint8_t *SectionEdRecord
= getSectionEdEsdRecord(I
);
353 Found
= SymEdRecord
== SectionEdRecord
;
358 return section_iterator(SectionRef(Sec
, this));
361 return createStringError(llvm::errc::invalid_argument
,
362 "symbol with ESD id " + std::to_string(Symb
.d
.a
) +
363 " refers to invalid section with ESD id " +
364 std::to_string(SymEdId
));
367 const uint8_t *GOFFObjectFile::getSectionEdEsdRecord(DataRefImpl
&Sec
) const {
368 SectionEntryImpl EsdIds
= SectionList
[Sec
.d
.a
];
369 const uint8_t *EsdRecord
= EsdPtrs
[EsdIds
.d
.a
];
373 const uint8_t *GOFFObjectFile::getSectionPrEsdRecord(DataRefImpl
&Sec
) const {
374 SectionEntryImpl EsdIds
= SectionList
[Sec
.d
.a
];
375 const uint8_t *EsdRecord
= nullptr;
377 EsdRecord
= EsdPtrs
[EsdIds
.d
.b
];
382 GOFFObjectFile::getSectionEdEsdRecord(uint32_t SectionIndex
) const {
384 Sec
.d
.a
= SectionIndex
;
385 const uint8_t *EsdRecord
= getSectionEdEsdRecord(Sec
);
390 GOFFObjectFile::getSectionPrEsdRecord(uint32_t SectionIndex
) const {
392 Sec
.d
.a
= SectionIndex
;
393 const uint8_t *EsdRecord
= getSectionPrEsdRecord(Sec
);
397 section_iterator
GOFFObjectFile::section_begin() const {
399 moveSectionNext(Sec
);
400 return section_iterator(SectionRef(Sec
, this));
403 section_iterator
GOFFObjectFile::section_end() const {
405 return section_iterator(SectionRef(Sec
, this));
408 void GOFFObjectFile::moveSymbolNext(DataRefImpl
&Symb
) const {
409 for (uint32_t I
= Symb
.d
.a
+ 1, E
= EsdPtrs
.size(); I
< E
; ++I
) {
411 const uint8_t *EsdRecord
= EsdPtrs
[I
];
412 GOFF::ESDSymbolType SymbolType
;
413 ESDRecord::getSymbolType(EsdRecord
, SymbolType
);
414 // Skip EDs - i.e. section symbols.
415 bool IgnoreSpecialGOFFSymbols
= true;
416 bool SkipSymbol
= ((SymbolType
== GOFF::ESD_ST_ElementDefinition
) ||
417 (SymbolType
== GOFF::ESD_ST_SectionDefinition
)) &&
418 IgnoreSpecialGOFFSymbols
;
428 basic_symbol_iterator
GOFFObjectFile::symbol_begin() const {
430 moveSymbolNext(Symb
);
431 return basic_symbol_iterator(SymbolRef(Symb
, this));
434 basic_symbol_iterator
GOFFObjectFile::symbol_end() const {
436 return basic_symbol_iterator(SymbolRef(Symb
, this));
439 Error
Record::getContinuousData(const uint8_t *Record
, uint16_t DataLength
,
440 int DataIndex
, SmallString
<256> &CompleteData
) {
442 const uint8_t *Slice
= Record
+ DataIndex
;
444 std::min(DataLength
, (uint16_t)(GOFF::RecordLength
- DataIndex
));
445 CompleteData
.append(Slice
, Slice
+ SliceLength
);
446 DataLength
-= SliceLength
;
447 Slice
+= SliceLength
;
449 // Continuation records.
450 for (; DataLength
> 0;
451 DataLength
-= SliceLength
, Slice
+= GOFF::PayloadLength
) {
452 // Slice points to the start of the new record.
453 // Check that this block is a Continuation.
454 assert(Record::isContinuation(Slice
) && "Continuation bit must be set");
455 // Check that the last Continuation is terminated correctly.
456 if (DataLength
<= 77 && Record::isContinued(Slice
))
457 return createStringError(object_error::parse_failed
,
458 "continued bit should not be set");
460 SliceLength
= std::min(DataLength
, (uint16_t)GOFF::PayloadLength
);
461 Slice
+= GOFF::RecordPrefixLength
;
462 CompleteData
.append(Slice
, Slice
+ SliceLength
);
464 return Error::success();
467 Error
HDRRecord::getData(const uint8_t *Record
,
468 SmallString
<256> &CompleteData
) {
469 uint16_t Length
= getPropertyModuleLength(Record
);
470 return getContinuousData(Record
, Length
, 60, CompleteData
);
473 Error
ESDRecord::getData(const uint8_t *Record
,
474 SmallString
<256> &CompleteData
) {
475 uint16_t DataSize
= getNameLength(Record
);
476 return getContinuousData(Record
, DataSize
, 72, CompleteData
);
479 Error
ENDRecord::getData(const uint8_t *Record
,
480 SmallString
<256> &CompleteData
) {
481 uint16_t Length
= getNameLength(Record
);
482 return getContinuousData(Record
, Length
, 26, CompleteData
);