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 §_name, const section_header_t §);
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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits