xref: /llvm-project/llvm/lib/Object/GOFFObjectFile.cpp (revision 6634c3e9377abf88c08bb065fb55aa15cda4c248)
1 //===- GOFFObjectFile.cpp - GOFF object file implementation -----*- C++ -*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Implementation of the GOFFObjectFile class.
10 //
11 //===----------------------------------------------------------------------===//
12 
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"
19 
20 #ifndef DEBUG_TYPE
21 #define DEBUG_TYPE "goff"
22 #endif
23 
24 using namespace llvm::object;
25 using namespace llvm;
26 
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));
31   if (Err)
32     return std::move(Err);
33   return std::move(Ret);
34 }
35 
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");
46     return;
47   }
48   // Object file doesn't start/end with HDR/END records.
49   // Bail out early.
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");
54       return;
55     }
56     if ((base()[Object.getBufferSize() - GOFF::RecordLength + 1] & 0xF0) >> 4 !=
57         GOFF::RT_END) {
58       Err = createStringError(object_error::parse_failed,
59                               "object file must end with END record");
60       return;
61     }
62   }
63 
64   SectionEntryImpl DummySection;
65   SectionList.emplace_back(DummySection); // Dummy entry at index 0.
66 
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;
75 
76     // If the previous record was continued, the current record should be a
77     // continuation.
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");
84         return;
85       }
86     }
87     // Don't parse continuations records, only parse initial record.
88     if (IsContinuation) {
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");
94         return;
95       }
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");
101         return;
102       }
103       PrevRecordType = RecordType;
104       PrevContinuationBits = I[1] & 0x03;
105       continue;
106     }
107     LLVM_DEBUG(for (size_t J = 0; J < GOFF::RecordLength; ++J) {
108       const uint8_t *P = I + J;
109       if (J % 8 == 0)
110         dbgs() << "  ";
111       dbgs() << format("%02hhX", *P);
112     });
113 
114     switch (RecordType) {
115     case GOFF::RT_ESD: {
116       // Save ESD record.
117       uint32_t EsdId;
118       ESDRecord::getEsdId(I, EsdId);
119       EsdPtrs.grow(EsdId);
120       EsdPtrs[EsdId] = I;
121 
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.
126       // case (2a) (ED,0)
127       //   - where the ED is of non-zero length.
128       // case (2b) (ED,0)
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;
134       uint32_t Length;
135       ESDRecord::getLength(I, Length);
136       if (SymbolType == GOFF::ESD_ST_ElementDefinition) {
137         // case (2a)
138         if (Length != 0) {
139           Section.d.a = EsdId;
140           SectionList.emplace_back(Section);
141         }
142       } else if (SymbolType == GOFF::ESD_ST_PartReference) {
143         // case (1)
144         if (Length != 0) {
145           uint32_t SymEdId;
146           ESDRecord::getParentEsdId(I, SymEdId);
147           Section.d.a = SymEdId;
148           Section.d.b = EsdId;
149           SectionList.emplace_back(Section);
150         }
151       } else if (SymbolType == GOFF::ESD_ST_LabelDefinition) {
152         // case (2b)
153         uint32_t SymEdId;
154         ESDRecord::getParentEsdId(I, SymEdId);
155         const uint8_t *SymEdRecord = EsdPtrs[SymEdId];
156         uint32_t EdLength;
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);
163         }
164       }
165       LLVM_DEBUG(dbgs() << "  --  ESD " << EsdId << "\n");
166       break;
167     }
168     case GOFF::RT_END:
169       LLVM_DEBUG(dbgs() << "  --  END (GOFF record type) unhandled\n");
170       break;
171     case GOFF::RT_HDR:
172       LLVM_DEBUG(dbgs() << "  --  HDR (GOFF record type) unhandled\n");
173       break;
174     default:
175       llvm_unreachable("Unknown record type");
176     }
177     PrevRecordType = RecordType;
178     PrevContinuationBits = I[1] & 0x03;
179   }
180 }
181 
182 const uint8_t *GOFFObjectFile::getSymbolEsdRecord(DataRefImpl Symb) const {
183   const uint8_t *EsdRecord = EsdPtrs[Symb.d.a];
184   return EsdRecord;
185 }
186 
187 Expected<StringRef> GOFFObjectFile::getSymbolName(DataRefImpl Symb) const {
188   if (EsdNamesCache.count(Symb.d.a)) {
189     auto &StrPtr = EsdNamesCache[Symb.d.a];
190     return StringRef(StrPtr.second.get(), StrPtr.first);
191   }
192 
193   SmallString<256> SymbolName;
194   if (auto Err = ESDRecord::getData(getSymbolEsdRecord(Symb), SymbolName))
195     return std::move(Err);
196 
197   SmallString<256> SymbolNameConverted;
198   ConverterEBCDIC::convertToUTF8(SymbolName, SymbolNameConverted);
199 
200   size_t Size = SymbolNameConverted.size();
201   auto StrPtr = std::make_pair(Size, std::make_unique<char[]>(Size));
202   char *Buf = StrPtr.second.get();
203   memcpy(Buf, SymbolNameConverted.data(), Size);
204   EsdNamesCache[Symb.d.a] = std::move(StrPtr);
205   return StringRef(Buf, Size);
206 }
207 
208 Expected<StringRef> GOFFObjectFile::getSymbolName(SymbolRef Symbol) const {
209   return getSymbolName(Symbol.getRawDataRefImpl());
210 }
211 
212 Expected<uint64_t> GOFFObjectFile::getSymbolAddress(DataRefImpl Symb) const {
213   uint32_t Offset;
214   const uint8_t *EsdRecord = getSymbolEsdRecord(Symb);
215   ESDRecord::getOffset(EsdRecord, Offset);
216   return static_cast<uint64_t>(Offset);
217 }
218 
219 uint64_t GOFFObjectFile::getSymbolValueImpl(DataRefImpl Symb) const {
220   uint32_t Offset;
221   const uint8_t *EsdRecord = getSymbolEsdRecord(Symb);
222   ESDRecord::getOffset(EsdRecord, Offset);
223   return static_cast<uint64_t>(Offset);
224 }
225 
226 uint64_t GOFFObjectFile::getCommonSymbolSizeImpl(DataRefImpl Symb) const {
227   return 0;
228 }
229 
230 bool GOFFObjectFile::isSymbolUnresolved(DataRefImpl Symb) const {
231   const uint8_t *Record = getSymbolEsdRecord(Symb);
232   GOFF::ESDSymbolType SymbolType;
233   ESDRecord::getSymbolType(Record, SymbolType);
234 
235   if (SymbolType == GOFF::ESD_ST_ExternalReference)
236     return true;
237   if (SymbolType == GOFF::ESD_ST_PartReference) {
238     uint32_t Length;
239     ESDRecord::getLength(Record, Length);
240     if (Length == 0)
241       return true;
242   }
243   return false;
244 }
245 
246 bool GOFFObjectFile::isSymbolIndirect(DataRefImpl Symb) const {
247   const uint8_t *Record = getSymbolEsdRecord(Symb);
248   bool Indirect;
249   ESDRecord::getIndirectReference(Record, Indirect);
250   return Indirect;
251 }
252 
253 Expected<uint32_t> GOFFObjectFile::getSymbolFlags(DataRefImpl Symb) const {
254   uint32_t Flags = 0;
255   if (isSymbolUnresolved(Symb))
256     Flags |= SymbolRef::SF_Undefined;
257 
258   const uint8_t *Record = getSymbolEsdRecord(Symb);
259 
260   GOFF::ESDBindingStrength BindingStrength;
261   ESDRecord::getBindingStrength(Record, BindingStrength);
262   if (BindingStrength == GOFF::ESD_BST_Weak)
263     Flags |= SymbolRef::SF_Weak;
264 
265   GOFF::ESDBindingScope BindingScope;
266   ESDRecord::getBindingScope(Record, BindingScope);
267 
268   if (BindingScope != GOFF::ESD_BSC_Section) {
269     Expected<StringRef> Name = getSymbolName(Symb);
270     if (Name && *Name != " ") { // Blank name is local.
271       Flags |= SymbolRef::SF_Global;
272       if (BindingScope == GOFF::ESD_BSC_ImportExport)
273         Flags |= SymbolRef::SF_Exported;
274       else if (!(Flags & SymbolRef::SF_Undefined))
275         Flags |= SymbolRef::SF_Hidden;
276     }
277   }
278 
279   return Flags;
280 }
281 
282 Expected<SymbolRef::Type>
283 GOFFObjectFile::getSymbolType(DataRefImpl Symb) const {
284   const uint8_t *Record = getSymbolEsdRecord(Symb);
285   GOFF::ESDSymbolType SymbolType;
286   ESDRecord::getSymbolType(Record, SymbolType);
287   GOFF::ESDExecutable Executable;
288   ESDRecord::getExecutable(Record, Executable);
289 
290   if (SymbolType != GOFF::ESD_ST_SectionDefinition &&
291       SymbolType != GOFF::ESD_ST_ElementDefinition &&
292       SymbolType != GOFF::ESD_ST_LabelDefinition &&
293       SymbolType != GOFF::ESD_ST_PartReference &&
294       SymbolType != GOFF::ESD_ST_ExternalReference) {
295     uint32_t EsdId;
296     ESDRecord::getEsdId(Record, EsdId);
297     return createStringError(llvm::errc::invalid_argument,
298                              "ESD record %" PRIu32
299                              " has invalid symbol type 0x%02" PRIX8,
300                              EsdId, SymbolType);
301   }
302   switch (SymbolType) {
303   case GOFF::ESD_ST_SectionDefinition:
304   case GOFF::ESD_ST_ElementDefinition:
305     return SymbolRef::ST_Other;
306   case GOFF::ESD_ST_LabelDefinition:
307   case GOFF::ESD_ST_PartReference:
308   case GOFF::ESD_ST_ExternalReference:
309     if (Executable != GOFF::ESD_EXE_CODE && Executable != GOFF::ESD_EXE_DATA &&
310         Executable != GOFF::ESD_EXE_Unspecified) {
311       uint32_t EsdId;
312       ESDRecord::getEsdId(Record, EsdId);
313       return createStringError(llvm::errc::invalid_argument,
314                                "ESD record %" PRIu32
315                                " has unknown Executable type 0x%02X",
316                                EsdId, Executable);
317     }
318     switch (Executable) {
319     case GOFF::ESD_EXE_CODE:
320       return SymbolRef::ST_Function;
321     case GOFF::ESD_EXE_DATA:
322       return SymbolRef::ST_Data;
323     case GOFF::ESD_EXE_Unspecified:
324       return SymbolRef::ST_Unknown;
325     }
326     llvm_unreachable("Unhandled ESDExecutable");
327   }
328   llvm_unreachable("Unhandled ESDSymbolType");
329 }
330 
331 Expected<section_iterator>
332 GOFFObjectFile::getSymbolSection(DataRefImpl Symb) const {
333   DataRefImpl Sec;
334 
335   if (isSymbolUnresolved(Symb))
336     return section_iterator(SectionRef(Sec, this));
337 
338   const uint8_t *SymEsdRecord = EsdPtrs[Symb.d.a];
339   uint32_t SymEdId;
340   ESDRecord::getParentEsdId(SymEsdRecord, SymEdId);
341   const uint8_t *SymEdRecord = EsdPtrs[SymEdId];
342 
343   for (size_t I = 0, E = SectionList.size(); I < E; ++I) {
344     bool Found;
345     const uint8_t *SectionPrRecord = getSectionPrEsdRecord(I);
346     if (SectionPrRecord) {
347       Found = SymEsdRecord == SectionPrRecord;
348     } else {
349       const uint8_t *SectionEdRecord = getSectionEdEsdRecord(I);
350       Found = SymEdRecord == SectionEdRecord;
351     }
352 
353     if (Found) {
354       Sec.d.a = I;
355       return section_iterator(SectionRef(Sec, this));
356     }
357   }
358   return createStringError(llvm::errc::invalid_argument,
359                            "symbol with ESD id " + std::to_string(Symb.d.a) +
360                                " refers to invalid section with ESD id " +
361                                std::to_string(SymEdId));
362 }
363 
364 const uint8_t *GOFFObjectFile::getSectionEdEsdRecord(DataRefImpl &Sec) const {
365   SectionEntryImpl EsdIds = SectionList[Sec.d.a];
366   const uint8_t *EsdRecord = EsdPtrs[EsdIds.d.a];
367   return EsdRecord;
368 }
369 
370 const uint8_t *GOFFObjectFile::getSectionPrEsdRecord(DataRefImpl &Sec) const {
371   SectionEntryImpl EsdIds = SectionList[Sec.d.a];
372   const uint8_t *EsdRecord = nullptr;
373   if (EsdIds.d.b)
374     EsdRecord = EsdPtrs[EsdIds.d.b];
375   return EsdRecord;
376 }
377 
378 const uint8_t *
379 GOFFObjectFile::getSectionEdEsdRecord(uint32_t SectionIndex) const {
380   DataRefImpl Sec;
381   Sec.d.a = SectionIndex;
382   const uint8_t *EsdRecord = getSectionEdEsdRecord(Sec);
383   return EsdRecord;
384 }
385 
386 const uint8_t *
387 GOFFObjectFile::getSectionPrEsdRecord(uint32_t SectionIndex) const {
388   DataRefImpl Sec;
389   Sec.d.a = SectionIndex;
390   const uint8_t *EsdRecord = getSectionPrEsdRecord(Sec);
391   return EsdRecord;
392 }
393 
394 section_iterator GOFFObjectFile::section_begin() const {
395   DataRefImpl Sec;
396   moveSectionNext(Sec);
397   return section_iterator(SectionRef(Sec, this));
398 }
399 
400 section_iterator GOFFObjectFile::section_end() const {
401   DataRefImpl Sec;
402   return section_iterator(SectionRef(Sec, this));
403 }
404 
405 void GOFFObjectFile::moveSymbolNext(DataRefImpl &Symb) const {
406   for (uint32_t I = Symb.d.a + 1, E = EsdPtrs.size(); I < E; ++I) {
407     if (EsdPtrs[I]) {
408       const uint8_t *EsdRecord = EsdPtrs[I];
409       GOFF::ESDSymbolType SymbolType;
410       ESDRecord::getSymbolType(EsdRecord, SymbolType);
411       // Skip EDs - i.e. section symbols.
412       bool IgnoreSpecialGOFFSymbols = true;
413       bool SkipSymbol = ((SymbolType == GOFF::ESD_ST_ElementDefinition) ||
414                          (SymbolType == GOFF::ESD_ST_SectionDefinition)) &&
415                         IgnoreSpecialGOFFSymbols;
416       if (!SkipSymbol) {
417         Symb.d.a = I;
418         return;
419       }
420     }
421   }
422   Symb.d.a = 0;
423 }
424 
425 basic_symbol_iterator GOFFObjectFile::symbol_begin() const {
426   DataRefImpl Symb;
427   moveSymbolNext(Symb);
428   return basic_symbol_iterator(SymbolRef(Symb, this));
429 }
430 
431 basic_symbol_iterator GOFFObjectFile::symbol_end() const {
432   DataRefImpl Symb;
433   return basic_symbol_iterator(SymbolRef(Symb, this));
434 }
435 
436 Error Record::getContinuousData(const uint8_t *Record, uint16_t DataLength,
437                                 int DataIndex, SmallString<256> &CompleteData) {
438   // First record.
439   const uint8_t *Slice = Record + DataIndex;
440   size_t SliceLength =
441       std::min(DataLength, (uint16_t)(GOFF::RecordLength - DataIndex));
442   CompleteData.append(Slice, Slice + SliceLength);
443   DataLength -= SliceLength;
444   Slice += SliceLength;
445 
446   // Continuation records.
447   for (; DataLength > 0;
448        DataLength -= SliceLength, Slice += GOFF::PayloadLength) {
449     // Slice points to the start of the new record.
450     // Check that this block is a Continuation.
451     assert(Record::isContinuation(Slice) && "Continuation bit must be set");
452     // Check that the last Continuation is terminated correctly.
453     if (DataLength <= 77 && Record::isContinued(Slice))
454       return createStringError(object_error::parse_failed,
455                                "continued bit should not be set");
456 
457     SliceLength = std::min(DataLength, (uint16_t)GOFF::PayloadLength);
458     Slice += GOFF::RecordPrefixLength;
459     CompleteData.append(Slice, Slice + SliceLength);
460   }
461   return Error::success();
462 }
463 
464 Error HDRRecord::getData(const uint8_t *Record,
465                          SmallString<256> &CompleteData) {
466   uint16_t Length = getPropertyModuleLength(Record);
467   return getContinuousData(Record, Length, 60, CompleteData);
468 }
469 
470 Error ESDRecord::getData(const uint8_t *Record,
471                          SmallString<256> &CompleteData) {
472   uint16_t DataSize = getNameLength(Record);
473   return getContinuousData(Record, DataSize, 72, CompleteData);
474 }
475 
476 Error ENDRecord::getData(const uint8_t *Record,
477                          SmallString<256> &CompleteData) {
478   uint16_t Length = getNameLength(Record);
479   return getContinuousData(Record, Length, 26, CompleteData);
480 }
481