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 lldb-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits