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/MC/SubtargetFeature.h"
15#include <cstddef>
16#include <cstring>
17
18namespace llvm {
19namespace object {
20
21static const uint8_t FunctionSym = 0x20;
22static const uint8_t SymTypeMask = 0x07;
23static const uint16_t NoRelMask = 0x0001;
24
25// Checks that [Ptr, Ptr + Size) bytes fall inside the memory buffer
26// 'M'. Returns a pointer to the underlying object on success.
27template <typename T>
28static Expected<const T *> getObject(MemoryBufferRef M, const void *Ptr,
29                                     const uint64_t Size = sizeof(T)) {
30  uintptr_t Addr = uintptr_t(Ptr);
31  if (Error E = Binary::checkOffset(M, Addr, Size))
32    return std::move(E);
33  return reinterpret_cast<const T *>(Addr);
34}
35
36static uintptr_t getWithOffset(uintptr_t Base, ptrdiff_t Offset) {
37  return reinterpret_cast<uintptr_t>(reinterpret_cast<const char *>(Base) +
38                                     Offset);
39}
40
41template <typename T> static const T *viewAs(uintptr_t in) {
42  return reinterpret_cast<const T *>(in);
43}
44
45static StringRef generateXCOFFFixedNameStringRef(const char *Name) {
46  auto NulCharPtr =
47      static_cast<const char *>(memchr(Name, '\0', XCOFF::NameSize));
48  return NulCharPtr ? StringRef(Name, NulCharPtr - Name)
49                    : StringRef(Name, XCOFF::NameSize);
50}
51
52template <typename T> StringRef XCOFFSectionHeader<T>::getName() const {
53  const T &DerivedXCOFFSectionHeader = static_cast<const T &>(*this);
54  return generateXCOFFFixedNameStringRef(DerivedXCOFFSectionHeader.Name);
55}
56
57template <typename T> uint16_t XCOFFSectionHeader<T>::getSectionType() const {
58  const T &DerivedXCOFFSectionHeader = static_cast<const T &>(*this);
59  return DerivedXCOFFSectionHeader.Flags & SectionFlagsTypeMask;
60}
61
62template <typename T>
63bool XCOFFSectionHeader<T>::isReservedSectionType() const {
64  return getSectionType() & SectionFlagsReservedMask;
65}
66
67bool XCOFFRelocation32::isRelocationSigned() const {
68  return Info & XR_SIGN_INDICATOR_MASK;
69}
70
71bool XCOFFRelocation32::isFixupIndicated() const {
72  return Info & XR_FIXUP_INDICATOR_MASK;
73}
74
75uint8_t XCOFFRelocation32::getRelocatedLength() const {
76  // The relocation encodes the bit length being relocated minus 1. Add back
77  // the 1 to get the actual length being relocated.
78  return (Info & XR_BIASED_LENGTH_MASK) + 1;
79}
80
81void XCOFFObjectFile::checkSectionAddress(uintptr_t Addr,
82                                          uintptr_t TableAddress) const {
83  if (Addr < TableAddress)
84    report_fatal_error("Section header outside of section header table.");
85
86  uintptr_t Offset = Addr - TableAddress;
87  if (Offset >= getSectionHeaderSize() * getNumberOfSections())
88    report_fatal_error("Section header outside of section header table.");
89
90  if (Offset % getSectionHeaderSize() != 0)
91    report_fatal_error(
92        "Section header pointer does not point to a valid section header.");
93}
94
95const XCOFFSectionHeader32 *
96XCOFFObjectFile::toSection32(DataRefImpl Ref) const {
97  assert(!is64Bit() && "32-bit interface called on 64-bit object file.");
98#ifndef NDEBUG
99  checkSectionAddress(Ref.p, getSectionHeaderTableAddress());
100#endif
101  return viewAs<XCOFFSectionHeader32>(Ref.p);
102}
103
104const XCOFFSectionHeader64 *
105XCOFFObjectFile::toSection64(DataRefImpl Ref) const {
106  assert(is64Bit() && "64-bit interface called on a 32-bit object file.");
107#ifndef NDEBUG
108  checkSectionAddress(Ref.p, getSectionHeaderTableAddress());
109#endif
110  return viewAs<XCOFFSectionHeader64>(Ref.p);
111}
112
113const XCOFFSymbolEntry *XCOFFObjectFile::toSymbolEntry(DataRefImpl Ref) const {
114  assert(!is64Bit() && "Symbol table support not implemented for 64-bit.");
115  assert(Ref.p != 0 && "Symbol table pointer can not be nullptr!");
116#ifndef NDEBUG
117  checkSymbolEntryPointer(Ref.p);
118#endif
119  auto SymEntPtr = viewAs<XCOFFSymbolEntry>(Ref.p);
120  return SymEntPtr;
121}
122
123const XCOFFFileHeader32 *XCOFFObjectFile::fileHeader32() const {
124  assert(!is64Bit() && "32-bit interface called on 64-bit object file.");
125  return static_cast<const XCOFFFileHeader32 *>(FileHeader);
126}
127
128const XCOFFFileHeader64 *XCOFFObjectFile::fileHeader64() const {
129  assert(is64Bit() && "64-bit interface called on a 32-bit object file.");
130  return static_cast<const XCOFFFileHeader64 *>(FileHeader);
131}
132
133const XCOFFSectionHeader32 *
134XCOFFObjectFile::sectionHeaderTable32() const {
135  assert(!is64Bit() && "32-bit interface called on 64-bit object file.");
136  return static_cast<const XCOFFSectionHeader32 *>(SectionHeaderTable);
137}
138
139const XCOFFSectionHeader64 *
140XCOFFObjectFile::sectionHeaderTable64() const {
141  assert(is64Bit() && "64-bit interface called on a 32-bit object file.");
142  return static_cast<const XCOFFSectionHeader64 *>(SectionHeaderTable);
143}
144
145void XCOFFObjectFile::moveSymbolNext(DataRefImpl &Symb) const {
146  const XCOFFSymbolEntry *SymEntPtr = toSymbolEntry(Symb);
147  SymEntPtr += SymEntPtr->NumberOfAuxEntries + 1;
148#ifndef NDEBUG
149  // This function is used by basic_symbol_iterator, which allows to
150  // point to the end-of-symbol-table address.
151  if (reinterpret_cast<uintptr_t>(SymEntPtr) != getEndOfSymbolTableAddress())
152    checkSymbolEntryPointer(reinterpret_cast<uintptr_t>(SymEntPtr));
153#endif
154  Symb.p = reinterpret_cast<uintptr_t>(SymEntPtr);
155}
156
157Expected<StringRef>
158XCOFFObjectFile::getStringTableEntry(uint32_t Offset) const {
159  // The byte offset is relative to the start of the string table.
160  // A byte offset value of 0 is a null or zero-length symbol
161  // name. A byte offset in the range 1 to 3 (inclusive) points into the length
162  // field; as a soft-error recovery mechanism, we treat such cases as having an
163  // offset of 0.
164  if (Offset < 4)
165    return StringRef(nullptr, 0);
166
167  if (StringTable.Data != nullptr && StringTable.Size > Offset)
168    return (StringTable.Data + Offset);
169
170  return make_error<GenericBinaryError>("Bad offset for string table entry",
171                                        object_error::parse_failed);
172}
173
174Expected<StringRef>
175XCOFFObjectFile::getCFileName(const XCOFFFileAuxEnt *CFileEntPtr) const {
176  if (CFileEntPtr->NameInStrTbl.Magic !=
177      XCOFFSymbolEntry::NAME_IN_STR_TBL_MAGIC)
178    return generateXCOFFFixedNameStringRef(CFileEntPtr->Name);
179  return getStringTableEntry(CFileEntPtr->NameInStrTbl.Offset);
180}
181
182Expected<StringRef> XCOFFObjectFile::getSymbolName(DataRefImpl Symb) const {
183  const XCOFFSymbolEntry *SymEntPtr = toSymbolEntry(Symb);
184
185  // A storage class value with the high-order bit on indicates that the name is
186  // a symbolic debugger stabstring.
187  if (SymEntPtr->StorageClass & 0x80)
188    return StringRef("Unimplemented Debug Name");
189
190  if (SymEntPtr->NameInStrTbl.Magic != XCOFFSymbolEntry::NAME_IN_STR_TBL_MAGIC)
191    return generateXCOFFFixedNameStringRef(SymEntPtr->SymbolName);
192
193  return getStringTableEntry(SymEntPtr->NameInStrTbl.Offset);
194}
195
196Expected<uint64_t> XCOFFObjectFile::getSymbolAddress(DataRefImpl Symb) const {
197  assert(!is64Bit() && "Symbol table support not implemented for 64-bit.");
198  return toSymbolEntry(Symb)->Value;
199}
200
201uint64_t XCOFFObjectFile::getSymbolValueImpl(DataRefImpl Symb) const {
202  assert(!is64Bit() && "Symbol table support not implemented for 64-bit.");
203  return toSymbolEntry(Symb)->Value;
204}
205
206uint64_t XCOFFObjectFile::getCommonSymbolSizeImpl(DataRefImpl Symb) const {
207  uint64_t Result = 0;
208  llvm_unreachable("Not yet implemented!");
209  return Result;
210}
211
212Expected<SymbolRef::Type>
213XCOFFObjectFile::getSymbolType(DataRefImpl Symb) const {
214  llvm_unreachable("Not yet implemented!");
215  return SymbolRef::ST_Other;
216}
217
218Expected<section_iterator>
219XCOFFObjectFile::getSymbolSection(DataRefImpl Symb) const {
220  const XCOFFSymbolEntry *SymEntPtr = toSymbolEntry(Symb);
221  int16_t SectNum = SymEntPtr->SectionNumber;
222
223  if (isReservedSectionNumber(SectNum))
224    return section_end();
225
226  Expected<DataRefImpl> ExpSec = getSectionByNum(SectNum);
227  if (!ExpSec)
228    return ExpSec.takeError();
229
230  return section_iterator(SectionRef(ExpSec.get(), this));
231}
232
233void XCOFFObjectFile::moveSectionNext(DataRefImpl &Sec) const {
234  const char *Ptr = reinterpret_cast<const char *>(Sec.p);
235  Sec.p = reinterpret_cast<uintptr_t>(Ptr + getSectionHeaderSize());
236}
237
238Expected<StringRef> XCOFFObjectFile::getSectionName(DataRefImpl Sec) const {
239  return generateXCOFFFixedNameStringRef(getSectionNameInternal(Sec));
240}
241
242uint64_t XCOFFObjectFile::getSectionAddress(DataRefImpl Sec) const {
243  // Avoid ternary due to failure to convert the ubig32_t value to a unit64_t
244  // with MSVC.
245  if (is64Bit())
246    return toSection64(Sec)->VirtualAddress;
247
248  return toSection32(Sec)->VirtualAddress;
249}
250
251uint64_t XCOFFObjectFile::getSectionIndex(DataRefImpl Sec) const {
252  // Section numbers in XCOFF are numbered beginning at 1. A section number of
253  // zero is used to indicate that a symbol is being imported or is undefined.
254  if (is64Bit())
255    return toSection64(Sec) - sectionHeaderTable64() + 1;
256  else
257    return toSection32(Sec) - sectionHeaderTable32() + 1;
258}
259
260uint64_t XCOFFObjectFile::getSectionSize(DataRefImpl Sec) const {
261  // Avoid ternary due to failure to convert the ubig32_t value to a unit64_t
262  // with MSVC.
263  if (is64Bit())
264    return toSection64(Sec)->SectionSize;
265
266  return toSection32(Sec)->SectionSize;
267}
268
269Expected<ArrayRef<uint8_t>>
270XCOFFObjectFile::getSectionContents(DataRefImpl Sec) const {
271  if (isSectionVirtual(Sec))
272    return ArrayRef<uint8_t>();
273
274  uint64_t OffsetToRaw;
275  if (is64Bit())
276    OffsetToRaw = toSection64(Sec)->FileOffsetToRawData;
277  else
278    OffsetToRaw = toSection32(Sec)->FileOffsetToRawData;
279
280  const uint8_t * ContentStart = base() + OffsetToRaw;
281  uint64_t SectionSize = getSectionSize(Sec);
282  if (checkOffset(Data, uintptr_t(ContentStart), SectionSize))
283    return make_error<BinaryError>();
284
285  return makeArrayRef(ContentStart,SectionSize);
286}
287
288uint64_t XCOFFObjectFile::getSectionAlignment(DataRefImpl Sec) const {
289  uint64_t Result = 0;
290  llvm_unreachable("Not yet implemented!");
291  return Result;
292}
293
294bool XCOFFObjectFile::isSectionCompressed(DataRefImpl Sec) const {
295  bool Result = false;
296  llvm_unreachable("Not yet implemented!");
297  return Result;
298}
299
300bool XCOFFObjectFile::isSectionText(DataRefImpl Sec) const {
301  return getSectionFlags(Sec) & XCOFF::STYP_TEXT;
302}
303
304bool XCOFFObjectFile::isSectionData(DataRefImpl Sec) const {
305  uint32_t Flags = getSectionFlags(Sec);
306  return Flags & (XCOFF::STYP_DATA | XCOFF::STYP_TDATA);
307}
308
309bool XCOFFObjectFile::isSectionBSS(DataRefImpl Sec) const {
310  uint32_t Flags = getSectionFlags(Sec);
311  return Flags & (XCOFF::STYP_BSS | XCOFF::STYP_TBSS);
312}
313
314bool XCOFFObjectFile::isSectionVirtual(DataRefImpl Sec) const {
315  return is64Bit() ? toSection64(Sec)->FileOffsetToRawData == 0
316                   : toSection32(Sec)->FileOffsetToRawData == 0;
317}
318
319relocation_iterator XCOFFObjectFile::section_rel_begin(DataRefImpl Sec) const {
320  if (is64Bit())
321    report_fatal_error("64-bit support not implemented yet");
322  const XCOFFSectionHeader32 *SectionEntPtr = toSection32(Sec);
323  auto RelocationsOrErr = relocations(*SectionEntPtr);
324  if (Error E = RelocationsOrErr.takeError())
325    return relocation_iterator(RelocationRef());
326  DataRefImpl Ret;
327  Ret.p = reinterpret_cast<uintptr_t>(&*RelocationsOrErr.get().begin());
328  return relocation_iterator(RelocationRef(Ret, this));
329}
330
331relocation_iterator XCOFFObjectFile::section_rel_end(DataRefImpl Sec) const {
332  if (is64Bit())
333    report_fatal_error("64-bit support not implemented yet");
334  const XCOFFSectionHeader32 *SectionEntPtr = toSection32(Sec);
335  auto RelocationsOrErr = relocations(*SectionEntPtr);
336  if (Error E = RelocationsOrErr.takeError())
337    return relocation_iterator(RelocationRef());
338  DataRefImpl Ret;
339  Ret.p = reinterpret_cast<uintptr_t>(&*RelocationsOrErr.get().end());
340  return relocation_iterator(RelocationRef(Ret, this));
341}
342
343void XCOFFObjectFile::moveRelocationNext(DataRefImpl &Rel) const {
344  Rel.p = reinterpret_cast<uintptr_t>(viewAs<XCOFFRelocation32>(Rel.p) + 1);
345}
346
347uint64_t XCOFFObjectFile::getRelocationOffset(DataRefImpl Rel) const {
348  if (is64Bit())
349    report_fatal_error("64-bit support not implemented yet");
350  const XCOFFRelocation32 *Reloc = viewAs<XCOFFRelocation32>(Rel.p);
351  const XCOFFSectionHeader32 *Sec32 = sectionHeaderTable32();
352  const uint32_t RelocAddress = Reloc->VirtualAddress;
353  const uint16_t NumberOfSections = getNumberOfSections();
354  for (uint16_t i = 0; i < NumberOfSections; ++i) {
355    // Find which section this relocation is belonging to, and get the
356    // relocation offset relative to the start of the section.
357    if (Sec32->VirtualAddress <= RelocAddress &&
358        RelocAddress < Sec32->VirtualAddress + Sec32->SectionSize) {
359      return RelocAddress - Sec32->VirtualAddress;
360    }
361    ++Sec32;
362  }
363  return InvalidRelocOffset;
364}
365
366symbol_iterator XCOFFObjectFile::getRelocationSymbol(DataRefImpl Rel) const {
367  if (is64Bit())
368    report_fatal_error("64-bit support not implemented yet");
369  const XCOFFRelocation32 *Reloc = viewAs<XCOFFRelocation32>(Rel.p);
370  const uint32_t Index = Reloc->SymbolIndex;
371
372  if (Index >= getLogicalNumberOfSymbolTableEntries32())
373    return symbol_end();
374
375  DataRefImpl SymDRI;
376  SymDRI.p = reinterpret_cast<uintptr_t>(getPointerToSymbolTable() + Index);
377  return symbol_iterator(SymbolRef(SymDRI, this));
378}
379
380uint64_t XCOFFObjectFile::getRelocationType(DataRefImpl Rel) const {
381  if (is64Bit())
382    report_fatal_error("64-bit support not implemented yet");
383  return viewAs<XCOFFRelocation32>(Rel.p)->Type;
384}
385
386void XCOFFObjectFile::getRelocationTypeName(
387    DataRefImpl Rel, SmallVectorImpl<char> &Result) const {
388  if (is64Bit())
389    report_fatal_error("64-bit support not implemented yet");
390  const XCOFFRelocation32 *Reloc = viewAs<XCOFFRelocation32>(Rel.p);
391  StringRef Res = XCOFF::getRelocationTypeString(Reloc->Type);
392  Result.append(Res.begin(), Res.end());
393}
394
395Expected<uint32_t> XCOFFObjectFile::getSymbolFlags(DataRefImpl Symb) const {
396  uint32_t Result = 0;
397  llvm_unreachable("Not yet implemented!");
398  return Result;
399}
400
401basic_symbol_iterator XCOFFObjectFile::symbol_begin() const {
402  if (is64Bit())
403    report_fatal_error("64-bit support not implemented yet");
404  DataRefImpl SymDRI;
405  SymDRI.p = reinterpret_cast<uintptr_t>(SymbolTblPtr);
406  return basic_symbol_iterator(SymbolRef(SymDRI, this));
407}
408
409basic_symbol_iterator XCOFFObjectFile::symbol_end() const {
410  if (is64Bit())
411    report_fatal_error("64-bit support not implemented yet");
412  DataRefImpl SymDRI;
413  SymDRI.p = reinterpret_cast<uintptr_t>(
414      SymbolTblPtr + getLogicalNumberOfSymbolTableEntries32());
415  return basic_symbol_iterator(SymbolRef(SymDRI, this));
416}
417
418section_iterator XCOFFObjectFile::section_begin() const {
419  DataRefImpl DRI;
420  DRI.p = getSectionHeaderTableAddress();
421  return section_iterator(SectionRef(DRI, this));
422}
423
424section_iterator XCOFFObjectFile::section_end() const {
425  DataRefImpl DRI;
426  DRI.p = getWithOffset(getSectionHeaderTableAddress(),
427                        getNumberOfSections() * getSectionHeaderSize());
428  return section_iterator(SectionRef(DRI, this));
429}
430
431uint8_t XCOFFObjectFile::getBytesInAddress() const { return is64Bit() ? 8 : 4; }
432
433StringRef XCOFFObjectFile::getFileFormatName() const {
434  return is64Bit() ? "aix5coff64-rs6000" : "aixcoff-rs6000";
435}
436
437Triple::ArchType XCOFFObjectFile::getArch() const {
438  return is64Bit() ? Triple::ppc64 : Triple::ppc;
439}
440
441SubtargetFeatures XCOFFObjectFile::getFeatures() const {
442  return SubtargetFeatures();
443}
444
445bool XCOFFObjectFile::isRelocatableObject() const {
446  if (is64Bit())
447    report_fatal_error("64-bit support not implemented yet");
448  return !(fileHeader32()->Flags & NoRelMask);
449}
450
451Expected<uint64_t> XCOFFObjectFile::getStartAddress() const {
452  // TODO FIXME Should get from auxiliary_header->o_entry when support for the
453  // auxiliary_header is added.
454  return 0;
455}
456
457size_t XCOFFObjectFile::getFileHeaderSize() const {
458  return is64Bit() ? sizeof(XCOFFFileHeader64) : sizeof(XCOFFFileHeader32);
459}
460
461size_t XCOFFObjectFile::getSectionHeaderSize() const {
462  return is64Bit() ? sizeof(XCOFFSectionHeader64) :
463                     sizeof(XCOFFSectionHeader32);
464}
465
466bool XCOFFObjectFile::is64Bit() const {
467  return Binary::ID_XCOFF64 == getType();
468}
469
470uint16_t XCOFFObjectFile::getMagic() const {
471  return is64Bit() ? fileHeader64()->Magic : fileHeader32()->Magic;
472}
473
474Expected<DataRefImpl> XCOFFObjectFile::getSectionByNum(int16_t Num) const {
475  if (Num <= 0 || Num > getNumberOfSections())
476    return errorCodeToError(object_error::invalid_section_index);
477
478  DataRefImpl DRI;
479  DRI.p = getWithOffset(getSectionHeaderTableAddress(),
480                        getSectionHeaderSize() * (Num - 1));
481  return DRI;
482}
483
484Expected<StringRef>
485XCOFFObjectFile::getSymbolSectionName(const XCOFFSymbolEntry *SymEntPtr) const {
486  assert(!is64Bit() && "Symbol table support not implemented for 64-bit.");
487  int16_t SectionNum = SymEntPtr->SectionNumber;
488
489  switch (SectionNum) {
490  case XCOFF::N_DEBUG:
491    return "N_DEBUG";
492  case XCOFF::N_ABS:
493    return "N_ABS";
494  case XCOFF::N_UNDEF:
495    return "N_UNDEF";
496  default:
497    Expected<DataRefImpl> SecRef = getSectionByNum(SectionNum);
498    if (SecRef)
499      return generateXCOFFFixedNameStringRef(
500          getSectionNameInternal(SecRef.get()));
501    return SecRef.takeError();
502  }
503}
504
505bool XCOFFObjectFile::isReservedSectionNumber(int16_t SectionNumber) {
506  return (SectionNumber <= 0 && SectionNumber >= -2);
507}
508
509uint16_t XCOFFObjectFile::getNumberOfSections() const {
510  return is64Bit() ? fileHeader64()->NumberOfSections
511                   : fileHeader32()->NumberOfSections;
512}
513
514int32_t XCOFFObjectFile::getTimeStamp() const {
515  return is64Bit() ? fileHeader64()->TimeStamp : fileHeader32()->TimeStamp;
516}
517
518uint16_t XCOFFObjectFile::getOptionalHeaderSize() const {
519  return is64Bit() ? fileHeader64()->AuxHeaderSize
520                   : fileHeader32()->AuxHeaderSize;
521}
522
523uint32_t XCOFFObjectFile::getSymbolTableOffset32() const {
524  return fileHeader32()->SymbolTableOffset;
525}
526
527int32_t XCOFFObjectFile::getRawNumberOfSymbolTableEntries32() const {
528  // As far as symbol table size is concerned, if this field is negative it is
529  // to be treated as a 0. However since this field is also used for printing we
530  // don't want to truncate any negative values.
531  return fileHeader32()->NumberOfSymTableEntries;
532}
533
534uint32_t XCOFFObjectFile::getLogicalNumberOfSymbolTableEntries32() const {
535  return (fileHeader32()->NumberOfSymTableEntries >= 0
536              ? fileHeader32()->NumberOfSymTableEntries
537              : 0);
538}
539
540uint64_t XCOFFObjectFile::getSymbolTableOffset64() const {
541  return fileHeader64()->SymbolTableOffset;
542}
543
544uint32_t XCOFFObjectFile::getNumberOfSymbolTableEntries64() const {
545  return fileHeader64()->NumberOfSymTableEntries;
546}
547
548uintptr_t XCOFFObjectFile::getEndOfSymbolTableAddress() const {
549  uint32_t NumberOfSymTableEntries =
550      is64Bit() ? getNumberOfSymbolTableEntries64()
551                : getLogicalNumberOfSymbolTableEntries32();
552  return getWithOffset(reinterpret_cast<uintptr_t>(SymbolTblPtr),
553                       XCOFF::SymbolTableEntrySize * NumberOfSymTableEntries);
554}
555
556void XCOFFObjectFile::checkSymbolEntryPointer(uintptr_t SymbolEntPtr) const {
557  if (SymbolEntPtr < reinterpret_cast<uintptr_t>(SymbolTblPtr))
558    report_fatal_error("Symbol table entry is outside of symbol table.");
559
560  if (SymbolEntPtr >= getEndOfSymbolTableAddress())
561    report_fatal_error("Symbol table entry is outside of symbol table.");
562
563  ptrdiff_t Offset = reinterpret_cast<const char *>(SymbolEntPtr) -
564                     reinterpret_cast<const char *>(SymbolTblPtr);
565
566  if (Offset % XCOFF::SymbolTableEntrySize != 0)
567    report_fatal_error(
568        "Symbol table entry position is not valid inside of symbol table.");
569}
570
571uint32_t XCOFFObjectFile::getSymbolIndex(uintptr_t SymbolEntPtr) const {
572  return (reinterpret_cast<const char *>(SymbolEntPtr) -
573          reinterpret_cast<const char *>(SymbolTblPtr)) /
574         XCOFF::SymbolTableEntrySize;
575}
576
577Expected<StringRef>
578XCOFFObjectFile::getSymbolNameByIndex(uint32_t Index) const {
579  if (is64Bit())
580    report_fatal_error("64-bit symbol table support not implemented yet.");
581
582  if (Index >= getLogicalNumberOfSymbolTableEntries32())
583    return errorCodeToError(object_error::invalid_symbol_index);
584
585  DataRefImpl SymDRI;
586  SymDRI.p = reinterpret_cast<uintptr_t>(getPointerToSymbolTable() + Index);
587  return getSymbolName(SymDRI);
588}
589
590uint16_t XCOFFObjectFile::getFlags() const {
591  return is64Bit() ? fileHeader64()->Flags : fileHeader32()->Flags;
592}
593
594const char *XCOFFObjectFile::getSectionNameInternal(DataRefImpl Sec) const {
595  return is64Bit() ? toSection64(Sec)->Name : toSection32(Sec)->Name;
596}
597
598uintptr_t XCOFFObjectFile::getSectionHeaderTableAddress() const {
599  return reinterpret_cast<uintptr_t>(SectionHeaderTable);
600}
601
602int32_t XCOFFObjectFile::getSectionFlags(DataRefImpl Sec) const {
603  return is64Bit() ? toSection64(Sec)->Flags : toSection32(Sec)->Flags;
604}
605
606XCOFFObjectFile::XCOFFObjectFile(unsigned int Type, MemoryBufferRef Object)
607    : ObjectFile(Type, Object) {
608  assert(Type == Binary::ID_XCOFF32 || Type == Binary::ID_XCOFF64);
609}
610
611ArrayRef<XCOFFSectionHeader64> XCOFFObjectFile::sections64() const {
612  assert(is64Bit() && "64-bit interface called for non 64-bit file.");
613  const XCOFFSectionHeader64 *TablePtr = sectionHeaderTable64();
614  return ArrayRef<XCOFFSectionHeader64>(TablePtr,
615                                        TablePtr + getNumberOfSections());
616}
617
618ArrayRef<XCOFFSectionHeader32> XCOFFObjectFile::sections32() const {
619  assert(!is64Bit() && "32-bit interface called for non 32-bit file.");
620  const XCOFFSectionHeader32 *TablePtr = sectionHeaderTable32();
621  return ArrayRef<XCOFFSectionHeader32>(TablePtr,
622                                        TablePtr + getNumberOfSections());
623}
624
625// In an XCOFF32 file, when the field value is 65535, then an STYP_OVRFLO
626// section header contains the actual count of relocation entries in the s_paddr
627// field. STYP_OVRFLO headers contain the section index of their corresponding
628// sections as their raw "NumberOfRelocations" field value.
629Expected<uint32_t> XCOFFObjectFile::getLogicalNumberOfRelocationEntries(
630    const XCOFFSectionHeader32 &Sec) const {
631
632  uint16_t SectionIndex = &Sec - sectionHeaderTable32() + 1;
633
634  if (Sec.NumberOfRelocations < XCOFF::RelocOverflow)
635    return Sec.NumberOfRelocations;
636  for (const auto &Sec : sections32()) {
637    if (Sec.Flags == XCOFF::STYP_OVRFLO &&
638        Sec.NumberOfRelocations == SectionIndex)
639      return Sec.PhysicalAddress;
640  }
641  return errorCodeToError(object_error::parse_failed);
642}
643
644Expected<ArrayRef<XCOFFRelocation32>>
645XCOFFObjectFile::relocations(const XCOFFSectionHeader32 &Sec) const {
646  uintptr_t RelocAddr = getWithOffset(reinterpret_cast<uintptr_t>(FileHeader),
647                                      Sec.FileOffsetToRelocationInfo);
648  auto NumRelocEntriesOrErr = getLogicalNumberOfRelocationEntries(Sec);
649  if (Error E = NumRelocEntriesOrErr.takeError())
650    return std::move(E);
651
652  uint32_t NumRelocEntries = NumRelocEntriesOrErr.get();
653
654  assert(sizeof(XCOFFRelocation32) == XCOFF::RelocationSerializationSize32);
655  auto RelocationOrErr =
656      getObject<XCOFFRelocation32>(Data, reinterpret_cast<void *>(RelocAddr),
657                                   NumRelocEntries * sizeof(XCOFFRelocation32));
658  if (Error E = RelocationOrErr.takeError())
659    return std::move(E);
660
661  const XCOFFRelocation32 *StartReloc = RelocationOrErr.get();
662
663  return ArrayRef<XCOFFRelocation32>(StartReloc, StartReloc + NumRelocEntries);
664}
665
666Expected<XCOFFStringTable>
667XCOFFObjectFile::parseStringTable(const XCOFFObjectFile *Obj, uint64_t Offset) {
668  // If there is a string table, then the buffer must contain at least 4 bytes
669  // for the string table's size. Not having a string table is not an error.
670  if (Error E = Binary::checkOffset(
671          Obj->Data, reinterpret_cast<uintptr_t>(Obj->base() + Offset), 4)) {
672    consumeError(std::move(E));
673    return XCOFFStringTable{0, nullptr};
674  }
675
676  // Read the size out of the buffer.
677  uint32_t Size = support::endian::read32be(Obj->base() + Offset);
678
679  // If the size is less then 4, then the string table is just a size and no
680  // string data.
681  if (Size <= 4)
682    return XCOFFStringTable{4, nullptr};
683
684  auto StringTableOrErr =
685      getObject<char>(Obj->Data, Obj->base() + Offset, Size);
686  if (Error E = StringTableOrErr.takeError())
687    return std::move(E);
688
689  const char *StringTablePtr = StringTableOrErr.get();
690  if (StringTablePtr[Size - 1] != '\0')
691    return errorCodeToError(object_error::string_table_non_null_end);
692
693  return XCOFFStringTable{Size, StringTablePtr};
694}
695
696Expected<std::unique_ptr<XCOFFObjectFile>>
697XCOFFObjectFile::create(unsigned Type, MemoryBufferRef MBR) {
698  // Can't use std::make_unique because of the private constructor.
699  std::unique_ptr<XCOFFObjectFile> Obj;
700  Obj.reset(new XCOFFObjectFile(Type, MBR));
701
702  uint64_t CurOffset = 0;
703  const auto *Base = Obj->base();
704  MemoryBufferRef Data = Obj->Data;
705
706  // Parse file header.
707  auto FileHeaderOrErr =
708      getObject<void>(Data, Base + CurOffset, Obj->getFileHeaderSize());
709  if (Error E = FileHeaderOrErr.takeError())
710    return std::move(E);
711  Obj->FileHeader = FileHeaderOrErr.get();
712
713  CurOffset += Obj->getFileHeaderSize();
714  // TODO FIXME we don't have support for an optional header yet, so just skip
715  // past it.
716  CurOffset += Obj->getOptionalHeaderSize();
717
718  // Parse the section header table if it is present.
719  if (Obj->getNumberOfSections()) {
720    auto SecHeadersOrErr = getObject<void>(Data, Base + CurOffset,
721                                           Obj->getNumberOfSections() *
722                                               Obj->getSectionHeaderSize());
723    if (Error E = SecHeadersOrErr.takeError())
724      return std::move(E);
725    Obj->SectionHeaderTable = SecHeadersOrErr.get();
726  }
727
728  // 64-bit object supports only file header and section headers for now.
729  if (Obj->is64Bit())
730    return std::move(Obj);
731
732  // If there is no symbol table we are done parsing the memory buffer.
733  if (Obj->getLogicalNumberOfSymbolTableEntries32() == 0)
734    return std::move(Obj);
735
736  // Parse symbol table.
737  CurOffset = Obj->fileHeader32()->SymbolTableOffset;
738  uint64_t SymbolTableSize = (uint64_t)(sizeof(XCOFFSymbolEntry)) *
739                             Obj->getLogicalNumberOfSymbolTableEntries32();
740  auto SymTableOrErr =
741      getObject<XCOFFSymbolEntry>(Data, Base + CurOffset, SymbolTableSize);
742  if (Error E = SymTableOrErr.takeError())
743    return std::move(E);
744  Obj->SymbolTblPtr = SymTableOrErr.get();
745  CurOffset += SymbolTableSize;
746
747  // Parse String table.
748  Expected<XCOFFStringTable> StringTableOrErr =
749      parseStringTable(Obj.get(), CurOffset);
750  if (Error E = StringTableOrErr.takeError())
751    return std::move(E);
752  Obj->StringTable = StringTableOrErr.get();
753
754  return std::move(Obj);
755}
756
757Expected<std::unique_ptr<ObjectFile>>
758ObjectFile::createXCOFFObjectFile(MemoryBufferRef MemBufRef,
759                                  unsigned FileType) {
760  return XCOFFObjectFile::create(FileType, MemBufRef);
761}
762
763XCOFF::StorageClass XCOFFSymbolRef::getStorageClass() const {
764  return OwningObjectPtr->toSymbolEntry(SymEntDataRef)->StorageClass;
765}
766
767uint8_t XCOFFSymbolRef::getNumberOfAuxEntries() const {
768  return OwningObjectPtr->toSymbolEntry(SymEntDataRef)->NumberOfAuxEntries;
769}
770
771// TODO: The function needs to return an error if there is no csect auxiliary
772// entry.
773const XCOFFCsectAuxEnt32 *XCOFFSymbolRef::getXCOFFCsectAuxEnt32() const {
774  assert(!OwningObjectPtr->is64Bit() &&
775         "32-bit interface called on 64-bit object file.");
776  assert(hasCsectAuxEnt() && "No Csect Auxiliary Entry is found.");
777
778  // In XCOFF32, the csect auxilliary entry is always the last auxiliary
779  // entry for the symbol.
780  uintptr_t AuxAddr = getWithOffset(
781      SymEntDataRef.p, XCOFF::SymbolTableEntrySize * getNumberOfAuxEntries());
782
783#ifndef NDEBUG
784  OwningObjectPtr->checkSymbolEntryPointer(AuxAddr);
785#endif
786
787  return reinterpret_cast<const XCOFFCsectAuxEnt32 *>(AuxAddr);
788}
789
790uint16_t XCOFFSymbolRef::getType() const {
791  return OwningObjectPtr->toSymbolEntry(SymEntDataRef)->SymbolType;
792}
793
794int16_t XCOFFSymbolRef::getSectionNumber() const {
795  return OwningObjectPtr->toSymbolEntry(SymEntDataRef)->SectionNumber;
796}
797
798// TODO: The function name needs to be changed to express the purpose of the
799// function.
800bool XCOFFSymbolRef::hasCsectAuxEnt() const {
801  XCOFF::StorageClass SC = getStorageClass();
802  return (SC == XCOFF::C_EXT || SC == XCOFF::C_WEAKEXT ||
803          SC == XCOFF::C_HIDEXT);
804}
805
806bool XCOFFSymbolRef::isFunction() const {
807  if (OwningObjectPtr->is64Bit())
808    report_fatal_error("64-bit support is unimplemented yet.");
809
810  if (getType() & FunctionSym)
811    return true;
812
813  if (!hasCsectAuxEnt())
814    return false;
815
816  const XCOFFCsectAuxEnt32 *CsectAuxEnt = getXCOFFCsectAuxEnt32();
817
818  // A function definition should be a label definition.
819  if ((CsectAuxEnt->SymbolAlignmentAndType & SymTypeMask) != XCOFF::XTY_LD)
820    return false;
821
822  if (CsectAuxEnt->StorageMappingClass != XCOFF::XMC_PR)
823    return false;
824
825  int16_t SectNum = getSectionNumber();
826  Expected<DataRefImpl> SI = OwningObjectPtr->getSectionByNum(SectNum);
827  if (!SI)
828    return false;
829
830  return (OwningObjectPtr->getSectionFlags(SI.get()) & XCOFF::STYP_TEXT);
831}
832
833// Explictly instantiate template classes.
834template struct XCOFFSectionHeader<XCOFFSectionHeader32>;
835template struct XCOFFSectionHeader<XCOFFSectionHeader64>;
836
837} // namespace object
838} // namespace llvm
839