1 //===--- XCOFFObjectFile.cpp - XCOFF object file implementation -----------===//
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 // This file defines the XCOFFObjectFile class.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm/Object/XCOFFObjectFile.h"
14 #include "llvm/ADT/ArrayRef.h"
15 #include "llvm/Support/BinaryStreamReader.h"
16 #include "llvm/Support/Endian.h"
17 #include "llvm/Support/ErrorHandling.h"
18 #include "llvm/Support/MathExtras.h"
19 #include <cstddef>
20 #include <cstring>
21 
22 namespace llvm {
23 namespace object {
24 
25 enum { XCOFF32FileHeaderSize = 20 };
26 static_assert(sizeof(XCOFFFileHeader) == XCOFF32FileHeaderSize,
27               "Wrong size for XCOFF file header.");
28 
29 // Sets EC and returns false if there is less than 'Size' bytes left in the
30 // buffer at 'Offset'.
31 static bool checkSize(MemoryBufferRef M, std::error_code &EC, uint64_t Offset,
32                       uint64_t Size) {
33   if (M.getBufferSize() < Offset + Size) {
34     EC = object_error::unexpected_eof;
35     return false;
36   }
37   return true;
38 }
39 
40 // Sets Obj unless any bytes in [addr, addr + size) fall outsize of m.
41 // Returns unexpected_eof on error.
42 template <typename T>
43 static std::error_code getObject(const T *&Obj, MemoryBufferRef M,
44                                  const void *Ptr,
45                                  const uint64_t Size = sizeof(T)) {
46   uintptr_t Addr = uintptr_t(Ptr);
47   if (std::error_code EC = Binary::checkOffset(M, Addr, Size))
48     return EC;
49   Obj = reinterpret_cast<const T *>(Addr);
50   return std::error_code();
51 }
52 
53 template <typename T> static const T *viewAs(uintptr_t in) {
54   return reinterpret_cast<const T *>(in);
55 }
56 
57 static StringRef generateStringRef(const char *Name, uint64_t Size) {
58   auto NulCharPtr = static_cast<const char *>(memchr(Name, '\0', Size));
59   return NulCharPtr ? StringRef(Name, NulCharPtr - Name)
60                     : StringRef(Name, Size);
61 }
62 
63 const XCOFFSectionHeader *XCOFFObjectFile::toSection(DataRefImpl Ref) const {
64   auto Sec = viewAs<XCOFFSectionHeader>(Ref.p);
65 #ifndef NDEBUG
66   if (Sec < SectionHdrTablePtr ||
67       Sec >= (SectionHdrTablePtr + getNumberOfSections()))
68     report_fatal_error("Section header outside of section header table.");
69 
70   uintptr_t Offset = uintptr_t(Sec) - uintptr_t(SectionHdrTablePtr);
71   if (Offset % getSectionHeaderSize() != 0)
72     report_fatal_error(
73         "Section header pointer does not point to a valid section header.");
74 #endif
75   return Sec;
76 }
77 
78 const XCOFFSymbolEntry *XCOFFObjectFile::toSymbolEntry(DataRefImpl Ref) const {
79   assert(Ref.p != 0 && "Symbol table pointer can not be nullptr!");
80   auto SymEntPtr = viewAs<XCOFFSymbolEntry>(Ref.p);
81   return SymEntPtr;
82 }
83 
84 // The next 2 functions are not exactly necessary yet, but they are useful to
85 // abstract over the size difference between XCOFF32 and XCOFF64 structure
86 // definitions.
87 size_t XCOFFObjectFile::getFileHeaderSize() const {
88   return sizeof(XCOFFFileHeader);
89 }
90 
91 size_t XCOFFObjectFile::getSectionHeaderSize() const {
92   return sizeof(XCOFFSectionHeader);
93 }
94 
95 uint16_t XCOFFObjectFile::getMagic() const { return FileHdrPtr->Magic; }
96 
97 void XCOFFObjectFile::moveSymbolNext(DataRefImpl &Symb) const {
98   const XCOFFSymbolEntry *SymEntPtr = toSymbolEntry(Symb);
99 
100   SymEntPtr += SymEntPtr->NumberOfAuxEntries + 1;
101   Symb.p = reinterpret_cast<uintptr_t>(SymEntPtr);
102 }
103 
104 Expected<StringRef> XCOFFObjectFile::getSymbolName(DataRefImpl Symb) const {
105   const XCOFFSymbolEntry *SymEntPtr = toSymbolEntry(Symb);
106 
107   if (SymEntPtr->NameInStrTbl.Magic != XCOFFSymbolEntry::NAME_IN_STR_TBL_MAGIC)
108     return generateStringRef(SymEntPtr->SymbolName, XCOFF::SymbolNameSize);
109 
110   // A storage class value with the high-order bit on indicates that the name is
111   // a symbolic debugger stabstring.
112   if (SymEntPtr->StorageClass & 0x80)
113     return StringRef("Unimplemented Debug Name");
114 
115   uint32_t Offset = SymEntPtr->NameInStrTbl.Offset;
116   // The byte offset is relative to the start of the string table
117   // or .debug section. A byte offset value of 0 is a null or zero-length symbol
118   // name. A byte offset in the range 1 to 3 (inclusive) points into the length
119   // field; as a soft-error recovery mechanism, we treat such cases as having an
120   // offset of 0.
121   if (Offset < 4)
122     return StringRef(nullptr, 0);
123 
124   if (StringTable.Data != nullptr && StringTable.Size > Offset)
125     return (StringTable.Data + Offset);
126 
127   return make_error<GenericBinaryError>("Symbol Name parse failed",
128                                         object_error::parse_failed);
129 }
130 
131 Expected<uint64_t> XCOFFObjectFile::getSymbolAddress(DataRefImpl Symb) const {
132   uint64_t Result = 0;
133   llvm_unreachable("Not yet implemented!");
134   return Result;
135 }
136 
137 uint64_t XCOFFObjectFile::getSymbolValueImpl(DataRefImpl Symb) const {
138   return toSymbolEntry(Symb)->Value;
139 }
140 
141 uint64_t XCOFFObjectFile::getCommonSymbolSizeImpl(DataRefImpl Symb) const {
142   uint64_t Result = 0;
143   llvm_unreachable("Not yet implemented!");
144   return Result;
145 }
146 
147 Expected<SymbolRef::Type>
148 XCOFFObjectFile::getSymbolType(DataRefImpl Symb) const {
149   llvm_unreachable("Not yet implemented!");
150   return SymbolRef::ST_Other;
151 }
152 
153 Expected<section_iterator>
154 XCOFFObjectFile::getSymbolSection(DataRefImpl Symb) const {
155   const XCOFFSymbolEntry *SymEntPtr = toSymbolEntry(Symb);
156   int16_t SectNum = SymEntPtr->SectionNumber;
157 
158   if (isReservedSectionNumber(SectNum))
159     return section_end();
160 
161   const XCOFFSectionHeader *Sec;
162   if (std::error_code EC = getSectionByNum(SectNum, Sec))
163     return errorCodeToError(EC);
164 
165   DataRefImpl SecDRI;
166   SecDRI.p = reinterpret_cast<uintptr_t>(Sec);
167 
168   return section_iterator(SectionRef(SecDRI, this));
169 }
170 
171 void XCOFFObjectFile::moveSectionNext(DataRefImpl &Sec) const {
172   const char *Ptr = reinterpret_cast<const char *>(Sec.p);
173   Sec.p = reinterpret_cast<uintptr_t>(Ptr + getSectionHeaderSize());
174 }
175 
176 Expected<StringRef> XCOFFObjectFile::getSectionName(DataRefImpl Sec) const {
177   const char *Name = toSection(Sec)->Name;
178   auto NulCharPtr =
179       static_cast<const char *>(memchr(Name, '\0', XCOFF::SectionNameSize));
180   return NulCharPtr ? StringRef(Name, NulCharPtr - Name)
181                     : StringRef(Name, XCOFF::SectionNameSize);
182 }
183 
184 uint64_t XCOFFObjectFile::getSectionAddress(DataRefImpl Sec) const {
185   return toSection(Sec)->VirtualAddress;
186 }
187 
188 uint64_t XCOFFObjectFile::getSectionIndex(DataRefImpl Sec) const {
189   // Section numbers in XCOFF are numbered beginning at 1. A section number of
190   // zero is used to indicate that a symbol is being imported or is undefined.
191   return toSection(Sec) - SectionHdrTablePtr + 1;
192 }
193 
194 uint64_t XCOFFObjectFile::getSectionSize(DataRefImpl Sec) const {
195   return toSection(Sec)->SectionSize;
196 }
197 
198 Expected<ArrayRef<uint8_t>>
199 XCOFFObjectFile::getSectionContents(DataRefImpl Sec) const {
200   llvm_unreachable("Not yet implemented!");
201 }
202 
203 uint64_t XCOFFObjectFile::getSectionAlignment(DataRefImpl Sec) const {
204   uint64_t Result = 0;
205   llvm_unreachable("Not yet implemented!");
206   return Result;
207 }
208 
209 bool XCOFFObjectFile::isSectionCompressed(DataRefImpl Sec) const {
210   bool Result = false;
211   llvm_unreachable("Not yet implemented!");
212   return Result;
213 }
214 
215 bool XCOFFObjectFile::isSectionText(DataRefImpl Sec) const {
216   return toSection(Sec)->Flags & XCOFF::STYP_TEXT;
217 }
218 
219 bool XCOFFObjectFile::isSectionData(DataRefImpl Sec) const {
220   unsigned Flags = toSection(Sec)->Flags;
221   return Flags & (XCOFF::STYP_DATA | XCOFF::STYP_TDATA);
222 }
223 
224 bool XCOFFObjectFile::isSectionBSS(DataRefImpl Sec) const {
225   unsigned Flags = toSection(Sec)->Flags;
226   return Flags & (XCOFF::STYP_BSS | XCOFF::STYP_TBSS);
227 }
228 
229 bool XCOFFObjectFile::isSectionVirtual(DataRefImpl Sec) const {
230   bool Result = false;
231   llvm_unreachable("Not yet implemented!");
232   return Result;
233 }
234 
235 relocation_iterator XCOFFObjectFile::section_rel_begin(DataRefImpl Sec) const {
236   llvm_unreachable("Not yet implemented!");
237   return relocation_iterator(RelocationRef());
238 }
239 
240 relocation_iterator XCOFFObjectFile::section_rel_end(DataRefImpl Sec) const {
241   llvm_unreachable("Not yet implemented!");
242   return relocation_iterator(RelocationRef());
243 }
244 
245 void XCOFFObjectFile::moveRelocationNext(DataRefImpl &Rel) const {
246   llvm_unreachable("Not yet implemented!");
247   return;
248 }
249 
250 uint64_t XCOFFObjectFile::getRelocationOffset(DataRefImpl Rel) const {
251   llvm_unreachable("Not yet implemented!");
252   uint64_t Result = 0;
253   return Result;
254 }
255 
256 symbol_iterator XCOFFObjectFile::getRelocationSymbol(DataRefImpl Rel) const {
257   llvm_unreachable("Not yet implemented!");
258   return symbol_iterator(SymbolRef());
259 }
260 
261 uint64_t XCOFFObjectFile::getRelocationType(DataRefImpl Rel) const {
262   llvm_unreachable("Not yet implemented!");
263   uint64_t Result = 0;
264   return Result;
265 }
266 
267 void XCOFFObjectFile::getRelocationTypeName(
268     DataRefImpl Rel, SmallVectorImpl<char> &Result) const {
269   llvm_unreachable("Not yet implemented!");
270   return;
271 }
272 
273 uint32_t XCOFFObjectFile::getSymbolFlags(DataRefImpl Symb) const {
274   uint32_t Result = 0;
275   llvm_unreachable("Not yet implemented!");
276   return Result;
277 }
278 
279 basic_symbol_iterator XCOFFObjectFile::symbol_begin() const {
280   DataRefImpl SymDRI;
281   SymDRI.p = reinterpret_cast<uintptr_t>(SymbolTblPtr);
282   return basic_symbol_iterator(SymbolRef(SymDRI, this));
283 }
284 
285 basic_symbol_iterator XCOFFObjectFile::symbol_end() const {
286   DataRefImpl SymDRI;
287   SymDRI.p = reinterpret_cast<uintptr_t>(
288       SymbolTblPtr + getLogicalNumberOfSymbolTableEntries());
289   return basic_symbol_iterator(SymbolRef(SymDRI, this));
290 }
291 
292 section_iterator XCOFFObjectFile::section_begin() const {
293   DataRefImpl DRI;
294   DRI.p = reinterpret_cast<uintptr_t>(SectionHdrTablePtr);
295   return section_iterator(SectionRef(DRI, this));
296 }
297 
298 section_iterator XCOFFObjectFile::section_end() const {
299   DataRefImpl DRI;
300   DRI.p =
301       reinterpret_cast<uintptr_t>(SectionHdrTablePtr + getNumberOfSections());
302   return section_iterator(SectionRef(DRI, this));
303 }
304 
305 uint8_t XCOFFObjectFile::getBytesInAddress() const {
306   // Only support 32-bit object files for now ...
307   assert(getFileHeaderSize() == XCOFF32FileHeaderSize);
308   return 4;
309 }
310 
311 StringRef XCOFFObjectFile::getFileFormatName() const {
312   assert(getFileHeaderSize() == XCOFF32FileHeaderSize);
313   return "aixcoff-rs6000";
314 }
315 
316 Triple::ArchType XCOFFObjectFile::getArch() const {
317   assert(getFileHeaderSize() == XCOFF32FileHeaderSize);
318   return Triple::ppc;
319 }
320 
321 SubtargetFeatures XCOFFObjectFile::getFeatures() const {
322   llvm_unreachable("Not yet implemented!");
323   return SubtargetFeatures();
324 }
325 
326 bool XCOFFObjectFile::isRelocatableObject() const {
327   bool Result = false;
328   llvm_unreachable("Not yet implemented!");
329   return Result;
330 }
331 
332 Expected<uint64_t> XCOFFObjectFile::getStartAddress() const {
333   // TODO FIXME Should get from auxiliary_header->o_entry when support for the
334   // auxiliary_header is added.
335   return 0;
336 }
337 
338 std::error_code
339 XCOFFObjectFile::getSectionByNum(int16_t Num,
340                                  const XCOFFSectionHeader *&Result) const {
341   if (Num > 0 && static_cast<uint16_t>(Num) <= getNumberOfSections()) {
342     Result = SectionHdrTablePtr + (Num - 1);
343     return std::error_code();
344   }
345 
346   return object_error::invalid_section_index;
347 }
348 
349 Expected<StringRef>
350 XCOFFObjectFile::getSymbolSectionName(const XCOFFSymbolEntry *SymEntPtr) const {
351   int16_t SectionNum = SymEntPtr->SectionNumber;
352 
353   switch (SectionNum) {
354   case XCOFF::N_DEBUG:
355     return "N_DEBUG";
356   case XCOFF::N_ABS:
357     return "N_ABS";
358   case XCOFF::N_UNDEF:
359     return "N_UNDEF";
360   default: {
361     const XCOFFSectionHeader *SectHeaderPtr;
362     std::error_code EC;
363     if ((EC = getSectionByNum(SectionNum, SectHeaderPtr)))
364       return errorCodeToError(EC);
365     else
366       return generateStringRef(SectHeaderPtr->Name, XCOFF::SectionNameSize);
367   }
368   }
369 }
370 
371 bool XCOFFObjectFile::isReservedSectionNumber(int16_t SectionNumber) {
372   return (SectionNumber <= 0 && SectionNumber >= -2);
373 }
374 
375 uint16_t XCOFFObjectFile::getNumberOfSections() const {
376   return FileHdrPtr->NumberOfSections;
377 }
378 
379 int32_t XCOFFObjectFile::getTimeStamp() const { return FileHdrPtr->TimeStamp; }
380 
381 uint32_t XCOFFObjectFile::getSymbolTableOffset() const {
382   return FileHdrPtr->SymbolTableOffset;
383 }
384 
385 int32_t XCOFFObjectFile::getRawNumberOfSymbolTableEntries() const {
386   return FileHdrPtr->NumberOfSymTableEntries;
387 }
388 
389 uint32_t XCOFFObjectFile::getLogicalNumberOfSymbolTableEntries() const {
390   return (FileHdrPtr->NumberOfSymTableEntries >= 0
391               ? FileHdrPtr->NumberOfSymTableEntries
392               : 0);
393 }
394 
395 uint16_t XCOFFObjectFile::getOptionalHeaderSize() const {
396   return FileHdrPtr->AuxHeaderSize;
397 }
398 
399 XCOFFObjectFile::XCOFFObjectFile(MemoryBufferRef Object, std::error_code &EC)
400     : ObjectFile(Binary::ID_XCOFF32, Object) {
401 
402   // Current location within the file.
403   uint64_t CurPtr = 0;
404 
405   if ((EC = getObject(FileHdrPtr, Data, base() + CurPtr)))
406     return;
407 
408   CurPtr += getFileHeaderSize();
409   // TODO FIXME we don't have support for an optional header yet, so just skip
410   // past it.
411   CurPtr += FileHdrPtr->AuxHeaderSize;
412 
413   if (getNumberOfSections() != 0) {
414     if ((EC = getObject(SectionHdrTablePtr, Data, base() + CurPtr,
415                         getNumberOfSections() * getSectionHeaderSize())))
416       return;
417   }
418 
419   if (getLogicalNumberOfSymbolTableEntries() == 0)
420     return;
421 
422   // Get pointer to the symbol table.
423   CurPtr = FileHdrPtr->SymbolTableOffset;
424   uint64_t SymbolTableSize = (uint64_t)(sizeof(XCOFFSymbolEntry)) *
425                              getLogicalNumberOfSymbolTableEntries();
426 
427   if ((EC = getObject(SymbolTblPtr, Data, base() + CurPtr, SymbolTableSize)))
428     return;
429 
430   // Move pointer to the string table.
431   CurPtr += SymbolTableSize;
432 
433   if (CurPtr + 4 > Data.getBufferSize())
434     return;
435 
436   StringTable.Size = support::endian::read32be(base() + CurPtr);
437 
438   if (StringTable.Size <= 4)
439     return;
440 
441   // Check for whether the String table has the size indicated by length
442   // field
443   if (!checkSize(Data, EC, CurPtr, StringTable.Size))
444     return;
445 
446   StringTable.Data = reinterpret_cast<const char *>(base() + CurPtr);
447   if (StringTable.Data[StringTable.Size - 1] != '\0') {
448     EC = object_error::string_table_non_null_end;
449     return;
450   }
451 }
452 
453 Expected<std::unique_ptr<ObjectFile>>
454 ObjectFile::createXCOFFObjectFile(MemoryBufferRef Object) {
455   StringRef Data = Object.getBuffer();
456   file_magic Type = identify_magic(Data);
457   std::error_code EC;
458   std::unique_ptr<ObjectFile> Ret;
459 
460   if (Type == file_magic::xcoff_object_32) {
461     Ret.reset(new XCOFFObjectFile(Object, EC));
462   } else {
463     llvm_unreachable("Encountered an unexpected binary file type!");
464   }
465 
466   if (EC)
467     return errorCodeToError(EC);
468   return std::move(Ret);
469 }
470 
471 } // namespace object
472 } // namespace llvm
473