asmith created this revision.
asmith added reviewers: rnk, zturner, aleksandr.urakov, lldb-commits.
Herald added subscribers: teemperor, mgrang, mgorny.

This parses entries in pecoff import tables for imported DLLs and
is intended as the first step to allow LLDB to load a PE's shared
modules when creating a target on the LLDB console.


Repository:
  rLLDB LLDB

https://reviews.llvm.org/D53094

Files:
  source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
  source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h
  unittests/ObjectFile/CMakeLists.txt
  unittests/ObjectFile/PECOFF/CMakeLists.txt
  unittests/ObjectFile/PECOFF/Inputs/BasicsTest.cpp
  unittests/ObjectFile/PECOFF/Inputs/BasicsTest.exe
  unittests/ObjectFile/PECOFF/Inputs/DllA.cpp
  unittests/ObjectFile/PECOFF/Inputs/DllA.dll
  unittests/ObjectFile/PECOFF/Inputs/DllB.cpp
  unittests/ObjectFile/PECOFF/Inputs/DllB.dll
  unittests/ObjectFile/PECOFF/ObjectFilePECOFFTests.cpp

Index: unittests/ObjectFile/PECOFF/ObjectFilePECOFFTests.cpp
===================================================================
--- /dev/null
+++ unittests/ObjectFile/PECOFF/ObjectFilePECOFFTests.cpp
@@ -0,0 +1,134 @@
+//===-- ObjectFilePECOFFTests.cpp -------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h"
+#include "TestingSupport/TestUtilities.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Utility/FileSpec.h"
+#include "llvm/Support/Path.h"
+#include "gtest/gtest.h"
+
+using namespace lldb_private;
+
+class ObjectFilePECOFFTests : public testing::Test {
+public:
+  void SetUp() override {
+    HostInfo::Initialize();
+    ObjectFilePECOFF::Initialize();
+
+    m_BasicsTest_exe = GetInputFilePath("BasicsTest.exe");
+    m_DllA = GetInputFilePath("DllA.dll");
+    m_DllB = GetInputFilePath("DllB.dll");
+  }
+
+  void TearDown() override {
+    ObjectFilePECOFF::Terminate();
+    HostInfo::Terminate();
+  }
+
+protected:
+  std::string m_BasicsTest_exe;
+  std::string m_DllA;
+  std::string m_DllB;
+};
+
+TEST_F(ObjectFilePECOFFTests, TestPECOFFBasics) {
+  ModuleSpec spec{FileSpec(m_BasicsTest_exe, false)};
+  auto module = std::make_shared<Module>(spec);
+  auto list = module->GetSectionList();
+  ASSERT_NE(nullptr, list);
+
+  EXPECT_NE(nullptr, list->FindSectionByName(ConstString(".text")));
+  EXPECT_NE(nullptr, list->FindSectionByName(ConstString(".data")));
+  EXPECT_NE(nullptr, list->FindSectionByName(ConstString(".reloc")));
+  EXPECT_NE(nullptr, list->FindSectionByName(ConstString(".pdata")));
+
+  auto obj = module->GetObjectFile();
+  ASSERT_NE(nullptr, obj);
+
+  EXPECT_STREQ("pe-coff", obj->GetPluginName().AsCString());
+  EXPECT_TRUE(obj->IsExecutable());
+
+  // TODO: Check UUID
+
+  // BasicsTest.exe is compiled without debug information. No exports either.
+  auto fspecs = obj->GetDebugSymbolFilePaths();
+  EXPECT_EQ(0u, fspecs.GetSize());
+
+  auto symtab = obj->GetSymtab();
+  ASSERT_NE(nullptr, symtab);
+
+  EXPECT_EQ(0u, symtab->GetNumSymbols());
+}
+
+TEST_F(ObjectFilePECOFFTests, TestDLL) {
+  ModuleSpec spec{FileSpec(m_DllA, false)};
+  auto module = std::make_shared<Module>(spec);
+  auto list = module->GetSectionList();
+  ASSERT_NE(nullptr, list);
+
+  auto text = list->FindSectionByName(ConstString(".text"));
+  ASSERT_NE(nullptr, text);
+
+  auto obj = module->GetObjectFile();
+  ASSERT_NE(nullptr, obj);
+
+  EXPECT_FALSE(obj->IsExecutable());
+
+  {
+    ModuleSpecList specs;
+    auto fspec = FileSpec(m_DllA, false);
+    ASSERT_EQ(1u, obj->GetModuleSpecifications(fspec, 0, 0, specs));
+  }
+
+  auto symtab = obj->GetSymtab();
+  ASSERT_NE(nullptr, symtab);
+
+  ASSERT_EQ(1u, symtab->GetNumSymbols());
+
+  // DllA.dll is compiled without any debug information.
+  // We expect the symtab is searched.
+  auto symbol =
+      module->FindFirstSymbolWithNameAndType(ConstString("?DllFuncA@@YAHH@Z"));
+  ASSERT_NE(nullptr, symbol);
+
+  // We expect the symbol is valid.
+  EXPECT_GT(symbol->GetByteSize(), 0);
+  EXPECT_EQ(text, symbol->GetAddress().GetSection());
+
+  // We expect the symbol from symtab has no debug information.
+  auto Symbol = *symbol;
+  EXPECT_EQ(nullptr, Symbol.CalculateSymbolContextFunction());
+  EXPECT_EQ(0u, Symbol.GetPrologueByteSize());
+}
+
+TEST_F(ObjectFilePECOFFTests, TestDependModules) {
+  ModuleSpec spec{FileSpec(m_BasicsTest_exe, false)};
+  auto module = std::make_shared<Module>(spec);
+  auto obj = module->GetObjectFile();
+  ASSERT_NE(nullptr, obj);
+
+  FileSpecList deps;
+  auto num_of_deps = obj->GetDependentModules(deps);
+  ASSERT_EQ(3u, num_of_deps);
+
+  std::vector<std::string> Deps;
+  for (uint32_t i = 0; i < num_of_deps; ++i)
+    Deps.push_back(deps.GetFileSpecAtIndex(i).GetFilename().AsCString());
+  llvm::sort(Deps.begin(), Deps.end());
+
+  EXPECT_STREQ("DllA.dll", Deps[0].c_str());
+  EXPECT_STREQ("DllB.dll", Deps[1].c_str());
+  EXPECT_STREQ("KERNEL32.dll", Deps[2].c_str());
+}
Index: unittests/ObjectFile/PECOFF/Inputs/DllB.cpp
===================================================================
--- /dev/null
+++ unittests/ObjectFile/PECOFF/Inputs/DllB.cpp
@@ -0,0 +1,6 @@
+// compile with "clang-cl /LDd DllB.cpp"
+
+int __declspec(dllexport) DllFuncB(int n) {
+  int rt = n + n;  
+  return rt;
+}
Index: unittests/ObjectFile/PECOFF/Inputs/DllA.cpp
===================================================================
--- /dev/null
+++ unittests/ObjectFile/PECOFF/Inputs/DllA.cpp
@@ -0,0 +1,6 @@
+// Compile with "clang-cl /LDd DllA.cpp"
+
+int __declspec(dllexport) DllFuncA(int n) {
+  int rt = n * n;  
+  return rt;
+}
Index: unittests/ObjectFile/PECOFF/Inputs/BasicsTest.cpp
===================================================================
--- /dev/null
+++ unittests/ObjectFile/PECOFF/Inputs/BasicsTest.cpp
@@ -0,0 +1,15 @@
+// compile with "clang-cl BasicsTest.cpp DllA.lib DllB.lib"
+
+int __declspec(dllimport) DllFuncA(int n);
+int __declspec(dllimport) DllFuncB(int n);
+
+int GlobalVar = 10;
+
+int main(int argc, char ** argv) {
+  int x = DllFuncA(4);  // x = 16
+  int y = DllFuncB(8);  // y = 16
+  int z = DllFuncB(16);
+  --GlobalVar;
+  z += GlobalVar;       // Z = 41
+  return (x + y + z);   // return 73
+}
Index: unittests/ObjectFile/PECOFF/CMakeLists.txt
===================================================================
--- /dev/null
+++ unittests/ObjectFile/PECOFF/CMakeLists.txt
@@ -0,0 +1,15 @@
+add_lldb_unittest(ObjectFilePECOFFTests
+  ObjectFilePECOFFTests.cpp
+
+  LINK_LIBS
+    lldbPluginObjectFilePECOFF
+    lldbUtilityHelpers
+    lldbCore
+  )
+
+set(test_inputs
+  DllA.dll
+  DllB.dll
+  BasicsTest.exe
+  )
+add_unittest_inputs(ObjectFilePECOFFTests "${test_inputs}")
Index: unittests/ObjectFile/CMakeLists.txt
===================================================================
--- unittests/ObjectFile/CMakeLists.txt
+++ unittests/ObjectFile/CMakeLists.txt
@@ -1 +1,2 @@
 add_subdirectory(ELF)
+add_subdirectory(PECOFF)
Index: source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h
===================================================================
--- source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h
+++ source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h
@@ -215,6 +215,20 @@
   typedef enum coff_data_dir_type {
     coff_data_dir_export_table = 0,
     coff_data_dir_import_table = 1,
+    coff_data_dir_resource_table,
+    coff_data_dir_exception_table,
+    coff_data_dir_certificate_table,
+    coff_data_dir_base_relocation_table,
+    coff_data_dir_debug,
+    coff_data_dir_architecture,
+    coff_data_dir_global_ptr,
+    coff_data_dir_tls_table,
+    coff_data_dir_load_config_table,
+    coff_data_dir_bound_import,
+    coff_data_dir_iat,
+    coff_data_dir_delay_import_descriptor,
+    coff_data_dir_clr_runtime_header,
+    coff_data_dir_reserved
   } coff_data_dir_type;
 
   typedef struct section_header {
@@ -253,22 +267,34 @@
     uint32_t address_of_name_ordinals;
   } export_directory_entry;
 
+  typedef struct import_directory_entry {
+    uint32_t lookup_table_rva;
+    uint32_t time_date_stamp;
+    uint32_t forwarder_chain;
+    uint32_t name_rva;
+    uint32_t address_table_rva;
+  } import_directory_entry;
+
   static bool ParseDOSHeader(lldb_private::DataExtractor &data,
                              dos_header_t &dos_header);
   static bool ParseCOFFHeader(lldb_private::DataExtractor &data,
                               lldb::offset_t *offset_ptr,
                               coff_header_t &coff_header);
   bool ParseCOFFOptionalHeader(lldb::offset_t *offset_ptr);
   bool ParseSectionHeaders(uint32_t offset);
 
+  uint32_t ParseDependentModules();
+
   static void DumpDOSHeader(lldb_private::Stream *s,
                             const dos_header_t &header);
   static void DumpCOFFHeader(lldb_private::Stream *s,
                              const coff_header_t &header);
   static void DumpOptCOFFHeader(lldb_private::Stream *s,
                                 const coff_opt_header_t &header);
   void DumpSectionHeaders(lldb_private::Stream *s);
   void DumpSectionHeader(lldb_private::Stream *s, const section_header_t &sh);
+  void DumpDependentModules(lldb_private::Stream *s);
+
   bool GetSectionName(std::string &sect_name, const section_header_t &sect);
 
   typedef std::vector<section_header_t> SectionHeaderColl;
@@ -282,6 +308,7 @@
   SectionHeaderColl m_sect_headers;
   lldb::addr_t m_image_base;
   lldb_private::Address m_entry_point_address;
+  mutable std::unique_ptr<lldb_private::FileSpecList> m_filespec_ap;
 };
 
 #endif // liblldb_ObjectFilePECOFF_h_
Index: source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
===================================================================
--- source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
+++ source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
@@ -131,9 +131,7 @@
           specs.Append(ModuleSpec(file, spec));
           spec.SetTriple("i686-pc-windows");
           specs.Append(ModuleSpec(file, spec));
-        }
-        else if (coff_header.machine == MachineArmNt)
-        {
+        } else if (coff_header.machine == MachineArmNt) {
           spec.SetTriple("arm-pc-windows");
           specs.Append(ModuleSpec(file, spec));
         }
@@ -176,7 +174,7 @@
                                    lldb::offset_t length)
     : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset),
       m_dos_header(), m_coff_header(), m_coff_header_opt(), m_sect_headers(),
-      m_entry_point_address() {
+      m_entry_point_address(), m_filespec_ap() {
   ::memset(&m_dos_header, 0, sizeof(m_dos_header));
   ::memset(&m_coff_header, 0, sizeof(m_coff_header));
   ::memset(&m_coff_header_opt, 0, sizeof(m_coff_header_opt));
@@ -431,7 +429,7 @@
 
 DataExtractor ObjectFilePECOFF::ReadImageData(uint32_t offset, size_t size) {
   if (m_file) {
-    // A bit of a hack, but we intend to write to this buffer, so we can't 
+    // A bit of a hack, but we intend to write to this buffer, so we can't
     // mmap it.
     auto buffer_sp = MapFileData(m_file, size, offset);
     return DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize());
@@ -799,8 +797,94 @@
 
 bool ObjectFilePECOFF::GetUUID(UUID *uuid) { return false; }
 
+uint32_t ObjectFilePECOFF::ParseDependentModules() {
+  if (m_filespec_ap.get())
+    return m_filespec_ap->GetSize();
+
+  m_filespec_ap.reset(new FileSpecList());
+
+  ModuleSP module_sp(GetModule());
+  if (!module_sp)
+    return 0;
+
+  std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
+
+  // Parse import table.
+  if (coff_data_dir_import_table < m_coff_header_opt.data_dirs.size() &&
+      m_coff_header_opt.data_dirs[coff_data_dir_import_table].vmaddr &&
+      m_coff_header_opt.data_dirs[coff_data_dir_import_table].vmsize) {
+
+    auto data_dir = m_coff_header_opt.data_dirs[coff_data_dir_import_table];
+
+    auto table_start = data_dir.vmaddr;
+    auto table_size = data_dir.vmsize;
+    // Resolve the offset of the table start address.
+    auto table_offset = data_dir.vmaddr;
+    if (m_file) {
+      Address address(m_coff_header_opt.image_base + table_start,
+                      GetSectionList());
+      table_offset =
+          address.GetSection()->GetFileOffset() + address.GetOffset();
+    }
+
+    DataExtractor table_data = ReadImageData(table_offset, table_size);
+
+    // Read import directory table entries
+    uint32_t entry_size = sizeof(import_directory_entry);
+    uint32_t num_entries = table_size / entry_size;
+    lldb::offset_t offset = 0;
+    for (uint32_t idx = 0; idx < num_entries; ++idx) {
+      import_directory_entry entry;
+
+      // Read the entry.
+      entry.lookup_table_rva = table_data.GetU32(&offset);
+      entry.time_date_stamp = table_data.GetU32(&offset);
+      entry.forwarder_chain = table_data.GetU32(&offset);
+      entry.name_rva = table_data.GetU32(&offset); // RVA to the image base.
+      entry.address_table_rva = table_data.GetU32(&offset);
+
+      // The last one is a null entry.
+      if (entry.name_rva == 0)
+        continue;
+
+      // Resolve the offset of the dll name address.
+      auto dll_name_address_rva = entry.name_rva;
+      if (m_file) {
+        Address address(m_coff_header_opt.image_base + dll_name_address_rva,
+                        GetSectionList());
+        dll_name_address_rva =
+            address.GetSection()->GetFileOffset() + address.GetOffset();
+      }
+      // No exact name length. It is a guess.
+      DataExtractor name_data = ReadImageData(dll_name_address_rva, MAX_PATH);
+      const char *dll_name_cstr = name_data.PeekCStr(0);
+
+      // Note that at this moment we only have the base name of the dll.
+      m_filespec_ap->Append(FileSpec(dll_name_cstr, true));
+    }
+  }
+
+  // Parse delay import table.
+  if (coff_data_dir_delay_import_descriptor <
+          m_coff_header_opt.data_dirs.size() &&
+      m_coff_header_opt.data_dirs[coff_data_dir_delay_import_descriptor]
+          .vmaddr &&
+      m_coff_header_opt.data_dirs[coff_data_dir_delay_import_descriptor]
+          .vmsize) {
+    // To be implemented.
+  }
+
+  return m_filespec_ap->GetSize();
+}
+
 uint32_t ObjectFilePECOFF::GetDependentModules(FileSpecList &files) {
-  return 0;
+  auto num_modules = ParseDependentModules();
+  auto original_size = files.GetSize();
+
+  for (unsigned i = 0; i < num_modules; ++i)
+    files.AppendIfUnique(m_filespec_ap->GetFileSpecAtIndex(i));
+
+  return files.GetSize() - original_size;
 }
 
 lldb_private::Address ObjectFilePECOFF::GetEntryPointAddress() {
@@ -857,6 +941,9 @@
     s->EOL();
     DumpSectionHeaders(s);
     s->EOL();
+
+    DumpDependentModules(s);
+    s->EOL();
   }
 }
 
@@ -1004,6 +1091,22 @@
   }
 }
 
+//----------------------------------------------------------------------
+// DumpDependentModules
+//
+// Dump all of the dependent modules to the specified output stream
+//----------------------------------------------------------------------
+void ObjectFilePECOFF::DumpDependentModules(lldb_private::Stream *s) {
+  auto num_modules = ParseDependentModules();
+  if (num_modules > 0) {
+    s->PutCString("Dependent Modules\n");
+    for (unsigned i = 0; i < num_modules; ++i) {
+      auto spec = m_filespec_ap->GetFileSpecAtIndex(i);
+      s->Printf("  %s\n", spec.GetFilename().GetCString());
+    }
+  }
+}
+
 bool ObjectFilePECOFF::IsWindowsSubsystem() {
   switch (m_coff_header_opt.subsystem) {
   case llvm::COFF::IMAGE_SUBSYSTEM_NATIVE:
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to