[Object][XCOFF] Add intial support for section header table.
authorSean Fertile <sfertile@ca.ibm.com>
Thu, 25 Apr 2019 21:36:04 +0000 (21:36 +0000)
committerSean Fertile <sfertile@ca.ibm.com>
Thu, 25 Apr 2019 21:36:04 +0000 (21:36 +0000)
Adds a representation of the section header table to XCOFFObjectFile,
and implements enough to dump the section headers with llvm-obdump.

Differential Revision: https://reviews.llvm.org/D60784

llvm-svn: 359244

llvm/include/llvm/BinaryFormat/XCOFF.h [new file with mode: 0644]
llvm/include/llvm/Object/XCOFFObjectFile.h
llvm/lib/Object/XCOFFObjectFile.cpp
llvm/test/tools/llvm-objdump/Inputs/xcoff-long-sec-names.o [new file with mode: 0644]
llvm/test/tools/llvm-objdump/Inputs/xcoff-section-headers-truncate.o [new file with mode: 0644]
llvm/test/tools/llvm-objdump/Inputs/xcoff-section-headers.o [new file with mode: 0644]
llvm/test/tools/llvm-objdump/xcoff-section-headers.test [new file with mode: 0644]

diff --git a/llvm/include/llvm/BinaryFormat/XCOFF.h b/llvm/include/llvm/BinaryFormat/XCOFF.h
new file mode 100644 (file)
index 0000000..32c0232
--- /dev/null
@@ -0,0 +1,43 @@
+//===-- llvm/BinaryFormat/XCOFF.h - The XCOFF file format -------*- C++/-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines manifest constants for the XCOFF object file format.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_BINARYFORMAT_XCOFF_H
+#define LLVM_BINARYFORMAT_XCOFF_H
+
+namespace llvm {
+namespace XCOFF {
+
+// Constants used in the XCOFF definition.
+enum { SectionNameSize = 8 };
+
+// Flags for defining the section type. Used for the s_flags field of
+// the section header structure. Defined in the system header `scnhdr.h`.
+enum SectionTypeFlags {
+  STYP_PAD = 0x0008,
+  STYP_DWARF = 0x0010,
+  STYP_TEXT = 0x0020,
+  STYP_DATA = 0x0040,
+  STYP_BSS = 0x0080,
+  STYP_EXCEPT = 0x0100,
+  STYP_INFO = 0x0200,
+  STYP_TDATA = 0x0400,
+  STYP_TBSS = 0x0800,
+  STYP_LOADER = 0x1000,
+  STYP_DEBUG = 0x2000,
+  STYP_TYPCHK = 0x4000,
+  STYP_OVRFLO = 0x8000
+};
+
+} // end namespace XCOFF
+} // end namespace llvm
+
+#endif
index 4689b3f..f35b36e 100644 (file)
@@ -16,6 +16,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/iterator_range.h"
 #include "llvm/BinaryFormat/Magic.h"
+#include "llvm/BinaryFormat/XCOFF.h"
 #include "llvm/MC/SubtargetFeature.h"
 #include "llvm/Object/Binary.h"
 #include "llvm/Object/Error.h"
@@ -47,9 +48,30 @@ struct XCOFFFileHeader {
   support::ubig16_t Flags;
 };
 
+struct XCOFFSectionHeader {
+  char Name[XCOFF::SectionNameSize];
+  support::ubig32_t PhysicalAddress;
+  support::ubig32_t VirtualAddress;
+  support::ubig32_t SectionSize;
+  support::ubig32_t FileOffsetToRawData;
+  support::ubig32_t FileOffsetToRelocationInfo;
+  support::ubig32_t FileOffsetToLineNumberInfo;
+  support::ubig16_t NumberOfRelocations;
+  support::ubig16_t NumberOfLineNumbers;
+  support::big32_t Flags;
+};
+
 class XCOFFObjectFile : public ObjectFile {
 private:
   const XCOFFFileHeader *FileHdrPtr = nullptr;
+  const XCOFFSectionHeader *SectionHdrTablePtr = nullptr;
+
+  size_t getFileHeaderSize() const;
+  size_t getSectionHeaderSize() const;
+
+  const XCOFFSectionHeader *toSection(DataRefImpl Ref) const;
+
+  uint16_t getNumberOfSections() const;
 
 public:
   void moveSymbolNext(DataRefImpl &Symb) const override;
@@ -95,13 +117,12 @@ public:
   StringRef getFileFormatName() const override;
   Triple::ArchType getArch() const override;
   SubtargetFeatures getFeatures() const override;
+  Expected<uint64_t> getStartAddress() const override;
   bool isRelocatableObject() const override;
 
-public:
   XCOFFObjectFile(MemoryBufferRef Object, std::error_code &EC);
 
   const XCOFFFileHeader *getFileHeader() const { return FileHdrPtr; }
-
 }; // XCOFFObjectFile
 
 } // namespace object
index 2903251..e740855 100644 (file)
 namespace llvm {
 namespace object {
 
+enum { XCOFF32FileHeaderSize = 20 };
+static_assert(sizeof(XCOFFFileHeader) == XCOFF32FileHeaderSize,
+              "Wrong size for XCOFF file header.");
+
 // Sets Obj unless any bytes in [addr, addr + size) fall outsize of m.
 // Returns unexpected_eof on error.
 template <typename T>
@@ -35,6 +39,40 @@ static std::error_code getObject(const T *&Obj, MemoryBufferRef M,
   return std::error_code();
 }
 
+template <typename T> static const T *viewAs(uintptr_t in) {
+  return reinterpret_cast<const T *>(in);
+}
+
+const XCOFFSectionHeader *XCOFFObjectFile::toSection(DataRefImpl Ref) const {
+  auto Sec = viewAs<XCOFFSectionHeader>(Ref.p);
+#ifndef NDEBUG
+  if (Sec < SectionHdrTablePtr ||
+      Sec >= (SectionHdrTablePtr + getNumberOfSections()))
+    report_fatal_error("Section header outside of section header table.");
+
+  uintptr_t Offset = uintptr_t(Sec) - uintptr_t(SectionHdrTablePtr);
+  if (Offset % getSectionHeaderSize() != 0)
+    report_fatal_error(
+        "Section header pointer does not point to a valid section header.");
+#endif
+  return Sec;
+}
+
+// The next 2 functions are not exactly necessary yet, but they are useful to
+// abstract over the size difference between XCOFF32 and XCOFF64 structure
+// definitions.
+size_t XCOFFObjectFile::getFileHeaderSize() const {
+  return sizeof(XCOFFFileHeader);
+}
+
+size_t XCOFFObjectFile::getSectionHeaderSize() const {
+  return sizeof(XCOFFSectionHeader);
+}
+
+uint16_t XCOFFObjectFile::getNumberOfSections() const {
+  return FileHdrPtr->NumberOfSections;
+}
+
 void XCOFFObjectFile::moveSymbolNext(DataRefImpl &Symb) const {
   llvm_unreachable("Not yet implemented!");
   return;
@@ -77,32 +115,32 @@ XCOFFObjectFile::getSymbolSection(DataRefImpl Symb) const {
 }
 
 void XCOFFObjectFile::moveSectionNext(DataRefImpl &Sec) const {
-  llvm_unreachable("Not yet implemented!");
-  return;
+  const char *Ptr = reinterpret_cast<const char *>(Sec.p);
+  Sec.p = reinterpret_cast<uintptr_t>(Ptr + getSectionHeaderSize());
 }
 
 std::error_code XCOFFObjectFile::getSectionName(DataRefImpl Sec,
                                                 StringRef &Res) const {
-  llvm_unreachable("Not yet implemented!");
+  const char *Name = toSection(Sec)->Name;
+  auto NulCharPtr =
+      static_cast<const char *>(memchr(Name, '\0', XCOFF::SectionNameSize));
+  Res = NulCharPtr ? StringRef(Name, NulCharPtr - Name)
+                   : StringRef(Name, XCOFF::SectionNameSize);
   return std::error_code();
 }
 
 uint64_t XCOFFObjectFile::getSectionAddress(DataRefImpl Sec) const {
-  uint64_t Result = 0;
-  llvm_unreachable("Not yet implemented!");
-  return Result;
+  return toSection(Sec)->VirtualAddress;
 }
 
 uint64_t XCOFFObjectFile::getSectionIndex(DataRefImpl Sec) const {
-  uint64_t Result = 0;
-  llvm_unreachable("Not yet implemented!");
-  return Result;
+  // Section numbers in XCOFF are numbered beginning at 1. A section number of
+  // zero is used to indicate that a symbol is being imported or is undefined.
+  return toSection(Sec) - SectionHdrTablePtr + 1;
 }
 
 uint64_t XCOFFObjectFile::getSectionSize(DataRefImpl Sec) const {
-  uint64_t Result = 0;
-  llvm_unreachable("Not yet implemented!");
-  return Result;
+  return toSection(Sec)->SectionSize;
 }
 
 std::error_code XCOFFObjectFile::getSectionContents(DataRefImpl Sec,
@@ -124,21 +162,17 @@ bool XCOFFObjectFile::isSectionCompressed(DataRefImpl Sec) const {
 }
 
 bool XCOFFObjectFile::isSectionText(DataRefImpl Sec) const {
-  bool Result = false;
-  llvm_unreachable("Not yet implemented!");
-  return Result;
+  return toSection(Sec)->Flags & XCOFF::STYP_TEXT;
 }
 
 bool XCOFFObjectFile::isSectionData(DataRefImpl Sec) const {
-  bool Result = false;
-  llvm_unreachable("Not yet implemented!");
-  return Result;
+  unsigned Flags = toSection(Sec)->Flags;
+  return Flags & (XCOFF::STYP_DATA | XCOFF::STYP_TDATA);
 }
 
 bool XCOFFObjectFile::isSectionBSS(DataRefImpl Sec) const {
-  bool Result = false;
-  llvm_unreachable("Not yet implemented!");
-  return Result;
+  unsigned Flags = toSection(Sec)->Flags;
+  return Flags & (XCOFF::STYP_BSS | XCOFF::STYP_TBSS);
 }
 
 bool XCOFFObjectFile::isSectionVirtual(DataRefImpl Sec) const {
@@ -202,13 +236,16 @@ basic_symbol_iterator XCOFFObjectFile::symbol_end() const {
 }
 
 section_iterator XCOFFObjectFile::section_begin() const {
-  llvm_unreachable("Not yet implemented!");
-  return section_iterator(SectionRef());
+  DataRefImpl DRI;
+  DRI.p = reinterpret_cast<uintptr_t>(SectionHdrTablePtr);
+  return section_iterator(SectionRef(DRI, this));
 }
 
 section_iterator XCOFFObjectFile::section_end() const {
-  llvm_unreachable("Not yet implemented!");
-  return section_iterator(SectionRef());
+  DataRefImpl DRI;
+  DRI.p =
+      reinterpret_cast<uintptr_t>(SectionHdrTablePtr + getNumberOfSections());
+  return section_iterator(SectionRef(DRI, this));
 }
 
 uint8_t XCOFFObjectFile::getBytesInAddress() const {
@@ -218,13 +255,13 @@ uint8_t XCOFFObjectFile::getBytesInAddress() const {
 }
 
 StringRef XCOFFObjectFile::getFileFormatName() const {
-  llvm_unreachable("Not yet implemented!");
-  return "";
+  assert(getFileHeaderSize() == XCOFF32FileHeaderSize);
+  return "aixcoff-rs6000";
 }
 
 Triple::ArchType XCOFFObjectFile::getArch() const {
-  llvm_unreachable("Not yet implemented!");
-  return Triple::UnknownArch;
+  assert(getFileHeaderSize() == XCOFF32FileHeaderSize);
+  return Triple::ppc;
 }
 
 SubtargetFeatures XCOFFObjectFile::getFeatures() const {
@@ -238,6 +275,12 @@ bool XCOFFObjectFile::isRelocatableObject() const {
   return Result;
 }
 
+Expected<uint64_t> XCOFFObjectFile::getStartAddress() const {
+  // TODO FIXME Should get from auxiliary_header->o_entry when support for the
+  // auxiliary_header is added.
+  return 0;
+}
+
 XCOFFObjectFile::XCOFFObjectFile(MemoryBufferRef Object, std::error_code &EC)
     : ObjectFile(Binary::ID_XCOFF32, Object) {
 
@@ -246,6 +289,17 @@ XCOFFObjectFile::XCOFFObjectFile(MemoryBufferRef Object, std::error_code &EC)
 
   if ((EC = getObject(FileHdrPtr, Data, base() + CurPtr)))
     return;
+
+  CurPtr += getFileHeaderSize();
+  // TODO FIXME we don't have support for an optional header yet, so just skip
+  // past it.
+  CurPtr += FileHdrPtr->AuxHeaderSize;
+
+  if (getNumberOfSections() != 0) {
+    if ((EC = getObject(SectionHdrTablePtr, Data, base() + CurPtr,
+                        getNumberOfSections() * getSectionHeaderSize())))
+      return;
+  }
 }
 
 Expected<std::unique_ptr<ObjectFile>>
diff --git a/llvm/test/tools/llvm-objdump/Inputs/xcoff-long-sec-names.o b/llvm/test/tools/llvm-objdump/Inputs/xcoff-long-sec-names.o
new file mode 100644 (file)
index 0000000..3dfc498
Binary files /dev/null and b/llvm/test/tools/llvm-objdump/Inputs/xcoff-long-sec-names.o differ
diff --git a/llvm/test/tools/llvm-objdump/Inputs/xcoff-section-headers-truncate.o b/llvm/test/tools/llvm-objdump/Inputs/xcoff-section-headers-truncate.o
new file mode 100644 (file)
index 0000000..7667c6f
Binary files /dev/null and b/llvm/test/tools/llvm-objdump/Inputs/xcoff-section-headers-truncate.o differ
diff --git a/llvm/test/tools/llvm-objdump/Inputs/xcoff-section-headers.o b/llvm/test/tools/llvm-objdump/Inputs/xcoff-section-headers.o
new file mode 100644 (file)
index 0000000..0090c7d
Binary files /dev/null and b/llvm/test/tools/llvm-objdump/Inputs/xcoff-section-headers.o differ
diff --git a/llvm/test/tools/llvm-objdump/xcoff-section-headers.test b/llvm/test/tools/llvm-objdump/xcoff-section-headers.test
new file mode 100644 (file)
index 0000000..86d956c
--- /dev/null
@@ -0,0 +1,43 @@
+# RUN: llvm-objdump --section-headers %p/Inputs/xcoff-section-headers.o | \
+# RUN: FileCheck %s
+
+# RUN: llvm-objdump --section-headers %p/Inputs/xcoff-long-sec-names.o | \
+# RUN: FileCheck --check-prefix=LONG %s
+
+# RUN: not llvm-objdump --section-headers 2>&1 \
+# RUN: %p/Inputs/xcoff-section-headers-truncate.o | FileCheck \
+# RUN: --check-prefix=ERROR %s
+
+# ERROR: The end of the file was unexpectedly encountered
+
+# CHECK: xcoff-section-headers.o:      file format aixcoff-rs6000
+# CHECK: Sections:
+# CHECK: Idx Name          Size     VMA          Type
+# CHECK:   1 .text         00000080 0000000000000000 TEXT
+# CHECK:   2 .data         00000024 0000000000000080 DATA
+# CHECK:   3 .bss          00000004 00000000000000a4 BSS
+# CHECK:   4 .tdata        00000008 0000000000000000 DATA
+# CHECK:   5 .tbss         00000004 0000000000000008 BSS
+
+# xcoff-section-headers.o Compiled with IBM XL C/C++ for AIX, V16.1.0
+# test.c:
+# int a;
+# int b = 12345;
+# __thread int c;
+# __thread double d = 3.14159;
+#
+# int func(void)  {
+#   return a;
+# }
+
+# LONG: xcoff-long-sec-names.o:      file format aixcoff-rs6000
+# LONG: Sections:
+# LONG: Idx Name          Size     VMA          Type
+# LONG: 1 .dwarnge      00000004 0000000000000000
+# LONG: 2 .dwpbnms      00000004 0000000000000000
+# LONG: 3 .dwpbtyp      00000004 0000000000000000
+
+# xcoff-long-sec-names.o was generated by assembling the following .s file:
+#  .dwsect 0x30000 # .dwpbnms section
+#  .dwsect 0x40000 # .dwpbtyp section
+#  .dwsect 0x50000 # .dwarnge section