diff --git a/lldb/include/lldb/Symbol/Symtab.h b/lldb/include/lldb/Symbol/Symtab.h --- a/lldb/include/lldb/Symbol/Symtab.h +++ b/lldb/include/lldb/Symbol/Symtab.h @@ -38,6 +38,7 @@ void PreloadSymbols(); void Reserve(size_t count); Symbol *Resize(size_t count); + Symbol *Extend(size_t count); uint32_t AddSymbol(const Symbol &symbol); size_t GetNumSymbols() const; void SectionFileAddressesChanged(); @@ -55,6 +56,8 @@ Symbol *FindSymbolWithType(lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, uint32_t &start_idx); + bool HasSymbolWithType(lldb::SymbolType symbol_type, Debug symbol_debug_type, + Visibility symbol_visibility) const; /// Get the parent symbol for the given symbol. /// /// Many symbols in symbol tables are scoped by other symbols that diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -1555,6 +1555,7 @@ strm.Indent(" Size: "); strm.Printf("0x%16.16" PRIx64 "\n", symbol->GetByteSize()); } + strm.IndentLess(); } } } diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h --- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h +++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h @@ -225,12 +225,6 @@ data_dirs; // will contain num_data_dir_entries entries } coff_opt_header_t; - enum coff_data_dir_type { - coff_data_dir_export_table = 0, - coff_data_dir_import_table = 1, - coff_data_dir_exception_table = 3 - }; - typedef struct section_header { char name[8] = {}; uint32_t vmsize = 0; // Virtual Size @@ -244,29 +238,6 @@ uint32_t flags = 0; } section_header_t; - typedef struct coff_symbol { - char name[8] = {}; - uint32_t value = 0; - uint16_t sect = 0; - uint16_t type = 0; - uint8_t storage = 0; - uint8_t naux = 0; - } coff_symbol_t; - - typedef struct export_directory_entry { - uint32_t characteristics = 0; - uint32_t time_date_stamp = 0; - uint16_t major_version = 0; - uint16_t minor_version = 0; - uint32_t name = 0; - uint32_t base = 0; - uint32_t number_of_functions = 0; - uint32_t number_of_names = 0; - uint32_t address_of_functions = 0; - uint32_t address_of_names = 0; - uint32_t address_of_name_ordinals = 0; - } export_directory_entry; - static bool ParseDOSHeader(lldb_private::DataExtractor &data, dos_header_t &dos_header); static bool ParseCOFFHeader(lldb_private::DataExtractor &data, @@ -297,6 +268,12 @@ private: bool CreateBinary(); + void AppendFromCOFFSymbolTable(lldb_private::SectionList *sect_list, + const llvm::StringMap &exported_syms, + lldb_private::Symtab &symtab); + void AppendFromExportTable(lldb_private::SectionList *sect_list, + llvm::StringMap &exported_syms, + lldb_private::Symtab &symtab); dos_header_t m_dos_header; coff_header_t m_coff_header; diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp --- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp +++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp @@ -30,14 +30,20 @@ #include "lldb/Utility/StreamString.h" #include "lldb/Utility/Timer.h" #include "lldb/Utility/UUID.h" -#include "llvm/BinaryFormat/COFF.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFFImportFile.h" #include "llvm/Support/CRC.h" #include "llvm/Support/Error.h" +#include "llvm/Support/FormatAdapters.h" #include "llvm/Support/Host.h" #include "llvm/Support/MemoryBuffer.h" +#include + #define IMAGE_DOS_SIGNATURE 0x5A4D // MZ #define IMAGE_NT_SIGNATURE 0x00004550 // PE00 #define OPT_HEADER_MAGIC_PE32 0x010b @@ -760,131 +766,173 @@ void ObjectFilePECOFF::ParseSymtab(Symtab &symtab) { SectionList *sect_list = GetSectionList(); - const uint32_t num_syms = m_coff_header.nsyms; - if (m_file && num_syms > 0 && m_coff_header.symoff > 0) { - const uint32_t symbol_size = 18; - const size_t symbol_data_size = num_syms * symbol_size; - // Include the 4-byte string table size at the end of the symbols - DataExtractor symtab_data = - ReadImageData(m_coff_header.symoff, symbol_data_size + 4); - lldb::offset_t offset = symbol_data_size; - const uint32_t strtab_size = symtab_data.GetU32(&offset); - if (strtab_size > 0) { - DataExtractor strtab_data = ReadImageData( - m_coff_header.symoff + symbol_data_size, strtab_size); - - offset = 0; - std::string symbol_name; - Symbol *symbols = symtab.Resize(num_syms); - for (uint32_t i = 0; i < num_syms; ++i) { - coff_symbol_t symbol; - const uint32_t symbol_offset = offset; - const char *symbol_name_cstr = nullptr; - // If the first 4 bytes of the symbol string are zero, then they - // are followed by a 4-byte string table offset. Else these - // 8 bytes contain the symbol name - if (symtab_data.GetU32(&offset) == 0) { - // Long string that doesn't fit into the symbol table name, so - // now we must read the 4 byte string table offset - uint32_t strtab_offset = symtab_data.GetU32(&offset); - symbol_name_cstr = strtab_data.PeekCStr(strtab_offset); - symbol_name.assign(symbol_name_cstr); - } else { - // Short string that fits into the symbol table name which is 8 - // bytes - offset += sizeof(symbol.name) - 4; // Skip remaining - symbol_name_cstr = symtab_data.PeekCStr(symbol_offset); - if (symbol_name_cstr == nullptr) - break; - symbol_name.assign(symbol_name_cstr, sizeof(symbol.name)); - } - symbol.value = symtab_data.GetU32(&offset); - symbol.sect = symtab_data.GetU16(&offset); - symbol.type = symtab_data.GetU16(&offset); - symbol.storage = symtab_data.GetU8(&offset); - symbol.naux = symtab_data.GetU8(&offset); - symbols[i].GetMangled().SetValue(ConstString(symbol_name.c_str())); - if ((int16_t)symbol.sect >= 1) { - Address symbol_addr(sect_list->FindSectionByID(symbol.sect), - symbol.value); - symbols[i].GetAddressRef() = symbol_addr; - symbols[i].SetType(MapSymbolType(symbol.type)); - } + llvm::StringMap exported_syms; + AppendFromExportTable(sect_list, exported_syms, symtab); + AppendFromCOFFSymbolTable(sect_list, exported_syms, symtab); +} - if (symbol.naux > 0) { - i += symbol.naux; - offset += symbol.naux * symbol_size; - } +void ObjectFilePECOFF::AppendFromCOFFSymbolTable( + SectionList *sect_list, const llvm::StringMap &exported_syms, + Symtab &symtab) { + Log *log = GetLog(LLDBLog::Object); + const uint32_t num_syms = m_binary->getNumberOfSymbols(); + if (num_syms == 0) + return; + // Check that this is not a bigobj + if (m_binary->getSymbolTableEntrySize() != + sizeof(llvm::object::coff_symbol16)) + return; + + Symbol *symbols = symtab.Extend(num_syms); + uint32_t i = 0; + for (const auto &sym_ref : m_binary->symbols()) { + const auto coff_sym_ref = m_binary->getCOFFSymbol(sym_ref); + auto name_or_error = sym_ref.getName(); + if (auto err = name_or_error.takeError()) { + LLDB_LOG(log, + "ObjectFilePECOFF::AppendFromCOFFSymbolTable - failed to get " + "symbol table entry name: {0}", + llvm::fmt_consume(std::move(err))); + continue; + } + const llvm::StringRef sym_name = *name_or_error; + symbols[i].GetMangled().SetValue(ConstString(sym_name)); + int16_t section_number = + static_cast(coff_sym_ref.getSectionNumber()); + if (section_number >= 1) { + symbols[i].GetAddressRef() = Address( + sect_list->FindSectionByID(section_number), coff_sym_ref.getValue()); + symbols[i].SetType(MapSymbolType(coff_sym_ref.getType())); + if (symbols[i].GetType() == lldb::eSymbolTypeInvalid) + if (sym_name.startswith("__imp_") || sym_name.startswith(".refptr.")) + symbols[i].SetType(lldb::eSymbolTypeData); + } else if (section_number == -1) { + symbols[i].GetAddressRef() = Address(coff_sym_ref.getValue()); + symbols[i].SetType(lldb::eSymbolTypeAbsolute); + } + + // Handle duplicate of an exported symbol + const auto exported_it = exported_syms.find(sym_name); + if (exported_it != exported_syms.end()) { + Symbol *exported = symtab.SymbolAtIndex(exported_it->getValue()); + if (exported->GetType() != lldb::eSymbolTypeReExported && + exported->GetAddressRef() == symbols[i].GetAddressRef()) { + symbols[i].SetID(exported->GetID()); + symbols[i].SetExternal(true); + // Previously we assumed an exported symbol is always code, now we + // fix it up if otherwise. + if (symbols[i].GetType() == lldb::eSymbolTypeInvalid) + symbols[i].SetType(lldb::eSymbolTypeData); + exported->SetType(symbols[i].GetType()); + // We don't want the symbol to be duplicated (e.g. when running + // `disas -n func`), but we also want to keep this entry, so we mark it + // as additional. + symbols[i].SetType(lldb::eSymbolTypeAdditional); } } + i++; } + // Trim excess entries if we skipped any. + if (i < num_syms) + symtab.Resize(symtab.GetIndexForSymbol(&symbols[i])); +} - // Read export header - if (coff_data_dir_export_table < m_coff_header_opt.data_dirs.size() && - m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmsize > 0 && - m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmaddr > 0) { - export_directory_entry export_table; - uint32_t data_start = - m_coff_header_opt.data_dirs[coff_data_dir_export_table].vmaddr; - - DataExtractor symtab_data = ReadImageDataByRVA( - data_start, m_coff_header_opt.data_dirs[0].vmsize); - lldb::offset_t offset = 0; - - // Read export_table header - export_table.characteristics = symtab_data.GetU32(&offset); - export_table.time_date_stamp = symtab_data.GetU32(&offset); - export_table.major_version = symtab_data.GetU16(&offset); - export_table.minor_version = symtab_data.GetU16(&offset); - export_table.name = symtab_data.GetU32(&offset); - export_table.base = symtab_data.GetU32(&offset); - export_table.number_of_functions = symtab_data.GetU32(&offset); - export_table.number_of_names = symtab_data.GetU32(&offset); - export_table.address_of_functions = symtab_data.GetU32(&offset); - export_table.address_of_names = symtab_data.GetU32(&offset); - export_table.address_of_name_ordinals = symtab_data.GetU32(&offset); - - bool has_ordinal = export_table.address_of_name_ordinals != 0; - - lldb::offset_t name_offset = export_table.address_of_names - data_start; - lldb::offset_t name_ordinal_offset = - export_table.address_of_name_ordinals - data_start; - - Symbol *symbols = symtab.Resize(export_table.number_of_names); - - std::string symbol_name; - - // Read each export table entry - for (size_t i = 0; i < export_table.number_of_names; ++i) { - uint32_t name_ordinal = - has_ordinal ? symtab_data.GetU16(&name_ordinal_offset) : i; - uint32_t name_address = symtab_data.GetU32(&name_offset); - - const char *symbol_name_cstr = - symtab_data.PeekCStr(name_address - data_start); - symbol_name.assign(symbol_name_cstr); - - lldb::offset_t function_offset = export_table.address_of_functions - - data_start + - sizeof(uint32_t) * name_ordinal; - uint32_t function_rva = symtab_data.GetU32(&function_offset); +void ObjectFilePECOFF::AppendFromExportTable( + SectionList *sect_list, llvm::StringMap &exported_syms, + Symtab &symtab) { + Log *log = GetLog(LLDBLog::Object); + const auto *export_table = m_binary->getExportTable(); + if (!export_table) + return; + const uint32_t num_syms = export_table->AddressTableEntries; + if (num_syms == 0) + return; + Symbol *symbols = symtab.Extend(num_syms); + uint32_t i = 0; + // Read each export table entry, ordered by ordinal instead of by name. + for (const auto &entry : m_binary->export_directories()) { + llvm::StringRef sym_name; + if (auto err = entry.getSymbolName(sym_name)) { + LLDB_LOG(log, + "ObjectFilePECOFF::AppendFromExportTable - failed to get export " + "table entry name: {0}", + llvm::fmt_consume(std::move(err))); + continue; + } + // Note: symbol name may be empty if it is only exported by ordinal. + symbols[i].GetMangled().SetValue(ConstString(sym_name)); + + uint32_t ordinal; + llvm::cantFail(entry.getOrdinal(ordinal)); + symbols[i].SetID(ordinal); + + bool is_forwarder; + llvm::cantFail(entry.isForwarder(is_forwarder)); + if (is_forwarder) { + // Forwarded export symbol name in the form of `DLLNAME.funcname` or + // `DLLNAME.#42`. + llvm::StringRef forwarder_name; + if (auto err = entry.getForwardTo(forwarder_name)) { + LLDB_LOG(log, + "ObjectFilePECOFF::AppendFromExportTable - failed to get " + "forwarder name of forwarder export '{0}': {1}", + sym_name, llvm::fmt_consume(std::move(err))); + continue; + } + size_t dot_pos = forwarder_name.find_first_of('.'); + if (dot_pos == llvm::StringRef::npos) { + LLDB_LOG(log, + "ObjectFilePECOFF::AppendFromExportTable - forwarder export " + "'{0}' has invalid forwarder name '{1}'", + sym_name, forwarder_name); + symbols[i].SetType(lldb::eSymbolTypeInvalid); + } else { + llvm::StringRef reexport_name = forwarder_name.drop_front(dot_pos + 1); + // FIXME: How to handle forwarded ordinal number starting with `#`? + symbols[i].SetReExportedSymbolName(ConstString(reexport_name)); + + llvm::SmallString<128> dll_name{forwarder_name.take_front(dot_pos), + ".dll"}; + FileSpec dll_spec(dll_name, FileSpec::Style::windows); + size_t index = m_deps_filespec->FindFileIndex(0, dll_spec, false); + if (index != UINT32_MAX) + symbols[i].SetReExportedSymbolSharedLibrary( + m_deps_filespec->GetFileSpecAtIndex(index)); + } + } else { + uint32_t function_rva; + if (auto err = entry.getExportRVA(function_rva)) { + LLDB_LOG(log, + "ObjectFilePECOFF::AppendFromExportTable - failed to get " + "address of export entry '{0}': {1}", + sym_name, llvm::fmt_consume(std::move(err))); + continue; + } Address symbol_addr(m_coff_header_opt.image_base + function_rva, sect_list); - symbols[i].GetMangled().SetValue(ConstString(symbol_name.c_str())); symbols[i].GetAddressRef() = symbol_addr; + // Treat the exported symbol as code for now. An exported symbol may + // actually be data, but this is relatively rare and we cannot tell + // from just the export table. symbols[i].SetType(lldb::eSymbolTypeCode); - symbols[i].SetDebug(true); + symbols[i].SetExternal(true); } + if (!sym_name.empty()) + exported_syms[sym_name] = symtab.GetIndexForSymbol(&symbols[i]); + i++; } + // Trim excess entries if we skipped any. + if (i < num_syms) + symtab.Resize(symtab.GetIndexForSymbol(&symbols[i])); } std::unique_ptr ObjectFilePECOFF::CreateCallFrameInfo() { - if (coff_data_dir_exception_table >= m_coff_header_opt.data_dirs.size()) + if (llvm::COFF::EXCEPTION_TABLE >= m_coff_header_opt.data_dirs.size()) return {}; data_directory data_dir_exception = - m_coff_header_opt.data_dirs[coff_data_dir_exception_table]; + m_coff_header_opt.data_dirs[llvm::COFF::EXCEPTION_TABLE]; if (!data_dir_exception.vmaddr) return {}; @@ -1068,17 +1116,7 @@ m_deps_filespec = FileSpecList(); - for (const auto &entry : m_binary->import_directories()) { - llvm::StringRef dll_name; - // Report a bogus entry. - if (llvm::Error e = entry.getName(dll_name)) { - LLDB_LOGF(log, - "ObjectFilePECOFF::ParseDependentModules() - failed to get " - "import directory entry name: %s", - llvm::toString(std::move(e)).c_str()); - continue; - } - + auto add_dll = [this](llvm::StringRef dll_name) { // At this moment we only have the base name of the DLL. The full path can // only be seen after the dynamic loading. Our best guess is Try to get it // with the help of the object file's directory. @@ -1092,7 +1130,42 @@ // Known DLLs or DLL not found in the object file directory. m_deps_filespec->EmplaceBack(dll_name); } + }; + + for (const auto &entry : m_binary->import_directories()) { + llvm::StringRef dll_name; + // Report a bogus entry. + if (llvm::Error e = entry.getName(dll_name)) { + LLDB_LOGF(log, + "ObjectFilePECOFF::ParseDependentModules() - failed to get " + "import directory entry name: %s", + llvm::toString(std::move(e)).c_str()); + continue; + } + add_dll(dll_name); } + + // Forwarder exports may reference DLLs not imported by self. + for (const auto &entry : m_binary->export_directories()) { + bool is_forwarder; + llvm::cantFail(entry.isForwarder(is_forwarder)); + if (!is_forwarder) + continue; + llvm::StringRef forwarder_name; + if (auto err = entry.getForwardTo(forwarder_name)) { + llvm::consumeError(std::move(err)); + continue; + } + size_t dot_pos = forwarder_name.find_first_of('.'); + if (dot_pos == llvm::StringRef::npos) + continue; + llvm::SmallString<128> dll_name{forwarder_name.take_front(dot_pos), ".dll"}; + FileSpec dll_spec(dll_name, FileSpec::Style::windows); + size_t index = m_deps_filespec->FindFileIndex(0, dll_spec, false); + if (index == UINT32_MAX) + add_dll(dll_name); + } + return m_deps_filespec->GetSize(); } diff --git a/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp b/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp --- a/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp +++ b/lldb/source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp @@ -81,6 +81,11 @@ abilities |= Functions; } + if (symtab->HasSymbolWithType(eSymbolTypeReExported, Symtab::eDebugAny, + Symtab::eVisibilityAny)) { + abilities |= Functions; + } + if (symtab->AppendSymbolIndexesWithType(eSymbolTypeData, m_data_indexes)) { symtab->SortSymbolIndexesByValue(m_data_indexes, true); diff --git a/lldb/source/Symbol/Symtab.cpp b/lldb/source/Symbol/Symtab.cpp --- a/lldb/source/Symbol/Symtab.cpp +++ b/lldb/source/Symbol/Symtab.cpp @@ -61,6 +61,14 @@ return m_symbols.empty() ? nullptr : &m_symbols[0]; } +Symbol *Symtab::Extend(size_t count) { + // Clients should grab the mutex from this symbol table and lock it manually + // when calling this function to avoid performance issues. + auto old_size = m_symbols.size(); + m_symbols.resize(old_size + count); + return m_symbols.empty() ? nullptr : &m_symbols[old_size]; +} + uint32_t Symtab::AddSymbol(const Symbol &symbol) { // Clients should grab the mutex from this symbol table and lock it manually // when calling this function to avoid performance issues. @@ -809,6 +817,22 @@ return nullptr; } +bool Symtab::HasSymbolWithType(SymbolType symbol_type, Debug symbol_debug_type, + Visibility symbol_visibility) const { + std::lock_guard guard(m_mutex); + + const size_t count = m_symbols.size(); + for (size_t idx = 0; idx < count; ++idx) { + if (symbol_type == eSymbolTypeAny || + m_symbols[idx].GetType() == symbol_type) { + if (CheckSymbolAtIndex(idx, symbol_debug_type, symbol_visibility)) { + return true; + } + } + } + return false; +} + void Symtab::FindAllSymbolsWithNameAndType(ConstString name, SymbolType symbol_type, diff --git a/llvm/include/llvm/Object/COFF.h b/llvm/include/llvm/Object/COFF.h --- a/llvm/include/llvm/Object/COFF.h +++ b/llvm/include/llvm/Object/COFF.h @@ -914,6 +914,10 @@ uint32_t getStringTableSize() const { return StringTableSize; } + const export_directory_table_entry *getExportTable() const { + return ExportDirectory; + } + const coff_load_configuration32 *getLoadConfig32() const { assert(!is64()); return reinterpret_cast(LoadConfig);