[llvm-readobj][XCOFF] Add support for `--needed-libs` option.
authorEsme-Yi <esme.yi@ibm.com>
Thu, 26 Aug 2021 07:17:06 +0000 (07:17 +0000)
committerEsme-Yi <esme.yi@ibm.com>
Thu, 26 Aug 2021 07:17:06 +0000 (07:17 +0000)
Summary: This patch is trying to add support for llvm-readobj
--needed-libs option under XCOFF.
For XCOFF, the needed libraries can be found from the Import
File ID Name Table of the Loader Section.
Currently, I am using binary inputs in the test since yaml2obj
does not yet support for writing the Loader Section and the
import file table.

Reviewed By: jhenderson

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

llvm/include/llvm/Object/XCOFFObjectFile.h
llvm/lib/Object/XCOFFObjectFile.cpp
llvm/test/tools/llvm-readobj/XCOFF/Inputs/needed-libs-32.o [new file with mode: 0755]
llvm/test/tools/llvm-readobj/XCOFF/Inputs/needed-libs-64.o [new file with mode: 0755]
llvm/test/tools/llvm-readobj/XCOFF/Inputs/needed-libs-empty.o [new file with mode: 0644]
llvm/test/tools/llvm-readobj/XCOFF/needed-libs.test [new file with mode: 0644]
llvm/tools/llvm-readobj/XCOFFDumper.cpp

index c51c438..8765832 100644 (file)
@@ -97,6 +97,31 @@ struct XCOFFSectionHeader64 : XCOFFSectionHeader<XCOFFSectionHeader64> {
   char Padding[4];
 };
 
+struct LoaderSectionHeader32 {
+  support::ubig32_t Version;
+  support::ubig32_t NumberOfSymTabEnt;
+  support::ubig32_t NumberOfRelTabEnt;
+  support::ubig32_t LengthOfImpidStrTbl;
+  support::ubig32_t NumberOfImpid;
+  support::big32_t OffsetToImpid;
+  support::ubig32_t LengthOfStrTbl;
+  support::big32_t OffsetToStrTbl;
+};
+
+struct LoaderSectionHeader64 {
+  support::ubig32_t Version;
+  support::ubig32_t NumberOfSymTabEnt;
+  support::ubig32_t NumberOfRelTabEnt;
+  support::ubig32_t LengthOfImpidStrTbl;
+  support::ubig32_t NumberOfImpid;
+  support::ubig32_t LengthOfStrTbl;
+  support::big64_t OffsetToImpid;
+  support::big64_t OffsetToStrTbl;
+  support::big64_t OffsetToSymTbl;
+  char Padding[16];
+  support::big32_t OffsetToRelEnt;
+};
+
 struct XCOFFStringTable {
   uint32_t Size;
   const char *Data;
@@ -290,6 +315,7 @@ private:
   const XCOFFSectionHeader64 *toSection64(DataRefImpl Ref) const;
   uintptr_t getSectionHeaderTableAddress() const;
   uintptr_t getEndOfSymbolTableAddress() const;
+  Expected<uintptr_t> getLoaderSectionAddress() const;
 
   // This returns a pointer to the start of the storage for the name field of
   // the 32-bit or 64-bit SectionHeader struct. This string is *not* necessarily
@@ -429,6 +455,9 @@ public:
   template <typename Shdr, typename Reloc>
   Expected<ArrayRef<Reloc>> relocations(const Shdr &Sec) const;
 
+  // Loader section related interfaces.
+  Expected<StringRef> getImportFileTable() const;
+
   // This function returns string table entry.
   Expected<StringRef> getStringTableEntry(uint32_t Offset) const;
 
index 7ec418c..dfb48e6 100644 (file)
@@ -307,6 +307,38 @@ uint64_t XCOFFObjectFile::getSectionAlignment(DataRefImpl Sec) const {
   return Result;
 }
 
+Expected<uintptr_t> XCOFFObjectFile::getLoaderSectionAddress() const {
+  uint64_t OffsetToLoaderSection = 0;
+  uint64_t SizeOfLoaderSection = 0;
+
+  if (is64Bit()) {
+    for (const auto &Sec64 : sections64())
+      if (Sec64.getSectionType() == XCOFF::STYP_LOADER) {
+        OffsetToLoaderSection = Sec64.FileOffsetToRawData;
+        SizeOfLoaderSection = Sec64.SectionSize;
+        break;
+      }
+  } else {
+    for (const auto &Sec32 : sections32())
+      if (Sec32.getSectionType() == XCOFF::STYP_LOADER) {
+        OffsetToLoaderSection = Sec32.FileOffsetToRawData;
+        SizeOfLoaderSection = Sec32.SectionSize;
+        break;
+      }
+  }
+
+  // No loader section is not an error.
+  if (!SizeOfLoaderSection)
+    return 0;
+
+  uintptr_t LoderSectionStart =
+      reinterpret_cast<uintptr_t>(base() + OffsetToLoaderSection);
+  if (Error E =
+          Binary::checkOffset(Data, LoderSectionStart, SizeOfLoaderSection))
+    return std::move(E);
+  return LoderSectionStart;
+}
+
 bool XCOFFObjectFile::isSectionCompressed(DataRefImpl Sec) const {
   return false;
 }
@@ -794,6 +826,47 @@ XCOFFObjectFile::parseStringTable(const XCOFFObjectFile *Obj, uint64_t Offset) {
   return XCOFFStringTable{Size, StringTablePtr};
 }
 
+// This function returns the import file table. Each entry in the import file
+// table consists of: "path_name\0base_name\0archive_member_name\0".
+Expected<StringRef> XCOFFObjectFile::getImportFileTable() const {
+  Expected<uintptr_t> LoaderSectionAddrOrError = getLoaderSectionAddress();
+  if (!LoaderSectionAddrOrError)
+    return LoaderSectionAddrOrError.takeError();
+
+  uintptr_t LoaderSectionAddr = LoaderSectionAddrOrError.get();
+  if (!LoaderSectionAddr)
+    return StringRef();
+
+  uint64_t OffsetToImportFileTable = 0;
+  uint64_t LengthOfImportFileTable = 0;
+  if (is64Bit()) {
+    const LoaderSectionHeader64 *LoaderSec64 =
+        viewAs<LoaderSectionHeader64>(LoaderSectionAddr);
+    OffsetToImportFileTable = LoaderSec64->OffsetToImpid;
+    LengthOfImportFileTable = LoaderSec64->LengthOfImpidStrTbl;
+  } else {
+    const LoaderSectionHeader32 *LoaderSec32 =
+        viewAs<LoaderSectionHeader32>(LoaderSectionAddr);
+    OffsetToImportFileTable = LoaderSec32->OffsetToImpid;
+    LengthOfImportFileTable = LoaderSec32->LengthOfImpidStrTbl;
+  }
+
+  auto ImportTableOrErr = getObject<char>(
+      Data,
+      reinterpret_cast<void *>(LoaderSectionAddr + OffsetToImportFileTable),
+      LengthOfImportFileTable);
+  if (Error E = ImportTableOrErr.takeError())
+    return std::move(E);
+
+  const char *ImportTablePtr = ImportTableOrErr.get();
+  if (ImportTablePtr[LengthOfImportFileTable - 1] != '\0')
+    return createStringError(
+        object_error::parse_failed,
+        "the import file table must end with a null terminator");
+
+  return StringRef(ImportTablePtr, LengthOfImportFileTable);
+}
+
 Expected<std::unique_ptr<XCOFFObjectFile>>
 XCOFFObjectFile::create(unsigned Type, MemoryBufferRef MBR) {
   // Can't use std::make_unique because of the private constructor.
diff --git a/llvm/test/tools/llvm-readobj/XCOFF/Inputs/needed-libs-32.o b/llvm/test/tools/llvm-readobj/XCOFF/Inputs/needed-libs-32.o
new file mode 100755 (executable)
index 0000000..6137aee
Binary files /dev/null and b/llvm/test/tools/llvm-readobj/XCOFF/Inputs/needed-libs-32.o differ
diff --git a/llvm/test/tools/llvm-readobj/XCOFF/Inputs/needed-libs-64.o b/llvm/test/tools/llvm-readobj/XCOFF/Inputs/needed-libs-64.o
new file mode 100755 (executable)
index 0000000..98504eb
Binary files /dev/null and b/llvm/test/tools/llvm-readobj/XCOFF/Inputs/needed-libs-64.o differ
diff --git a/llvm/test/tools/llvm-readobj/XCOFF/Inputs/needed-libs-empty.o b/llvm/test/tools/llvm-readobj/XCOFF/Inputs/needed-libs-empty.o
new file mode 100644 (file)
index 0000000..98f2ce1
Binary files /dev/null and b/llvm/test/tools/llvm-readobj/XCOFF/Inputs/needed-libs-empty.o differ
diff --git a/llvm/test/tools/llvm-readobj/XCOFF/needed-libs.test b/llvm/test/tools/llvm-readobj/XCOFF/needed-libs.test
new file mode 100644 (file)
index 0000000..3dea70a
--- /dev/null
@@ -0,0 +1,29 @@
+## In this test we check the --needed-libs option.
+
+# RUN: llvm-readobj --needed-libs %p/Inputs/needed-libs-32.o \
+# RUN:   %p/Inputs/needed-libs-64.o %p/Inputs/needed-libs-empty.o |\
+# RUN:   FileCheck %s --strict-whitespace --match-full-lines
+
+## Check 32-bit.
+
+#      CHECK:NeededLibraries [
+# CHECK-NEXT:  BASE             MEMBER
+# CHECK-NEXT:  libc.a           shr.o
+# CHECK-NEXT:  libpthreads.a    shr_xpg5.o
+# CHECK-NEXT:  libabcdefghijk.a 
+# CHECK-NEXT:]
+
+## Check 64-bit.
+
+#      CHECK:NeededLibraries [
+# CHECK-NEXT:  BASE               MEMBER
+# CHECK-NEXT:  libc.a             shr_64.o
+# CHECK-NEXT:  libpthreads.a      shr_xpg5_64.o
+# CHECK-NEXT:  libabcdefghijk64.a 
+# CHECK-NEXT:]
+
+## Check no lib.
+
+#      CHECK:NeededLibraries [
+# CHECK-NEXT:  BASE          MEMBER
+# CHECK-NEXT:]
index 9371901..3d9e9e6 100644 (file)
@@ -13,6 +13,7 @@
 #include "ObjDumper.h"
 #include "llvm-readobj.h"
 #include "llvm/Object/XCOFFObjectFile.h"
+#include "llvm/Support/FormattedStream.h"
 #include "llvm/Support/ScopedPrinter.h"
 
 using namespace llvm;
@@ -494,7 +495,43 @@ void XCOFFDumper::printStackMap() const {
 }
 
 void XCOFFDumper::printNeededLibraries() {
-  llvm_unreachable("Unimplemented functionality for XCOFFDumper");
+  ListScope D(W, "NeededLibraries");
+  auto ImportFilesOrError = Obj.getImportFileTable();
+  if (!ImportFilesOrError) {
+    reportUniqueWarning(ImportFilesOrError.takeError());
+    return;
+  }
+
+  StringRef ImportFileTable = ImportFilesOrError.get();
+  const char *CurrentStr = ImportFileTable.data();
+  const char *TableEnd = ImportFileTable.end();
+  // Default column width for names is 13 even if no names are that long.
+  size_t BaseWidth = 13;
+
+  // Get the max width of BASE columns.
+  for (size_t StrIndex = 0; CurrentStr < TableEnd; ++StrIndex) {
+    size_t CurrentLen = strlen(CurrentStr);
+    CurrentStr += strlen(CurrentStr) + 1;
+    if (StrIndex % 3 == 1)
+      BaseWidth = std::max(BaseWidth, CurrentLen);
+  }
+
+  auto &OS = static_cast<formatted_raw_ostream &>(W.startLine());
+  // Each entry consists of 3 strings: the path_name, base_name and
+  // archive_member_name. The first entry is a default LIBPATH value and other
+  // entries have no path_name. We just dump the base_name and
+  // archive_member_name here.
+  OS << left_justify("BASE", BaseWidth)  << " MEMBER\n";
+  CurrentStr = ImportFileTable.data();
+  for (size_t StrIndex = 0; CurrentStr < TableEnd;
+       ++StrIndex, CurrentStr += strlen(CurrentStr) + 1) {
+    if (StrIndex >= 3 && StrIndex % 3 != 0) {
+      if (StrIndex % 3 == 1)
+        OS << "  " << left_justify(CurrentStr, BaseWidth) << " ";
+      else
+        OS << CurrentStr << "\n";
+    }
+  }
 }
 
 static const EnumEntry<XCOFF::SectionTypeFlags> SectionTypeFlagsNames[] = {