Index: include/lldb/Symbol/Symtab.h =================================================================== --- include/lldb/Symbol/Symtab.h +++ include/lldb/Symbol/Symtab.h @@ -56,6 +56,10 @@ Symbol *FindSymbolWithType(lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, uint32_t &start_idx); + + bool HasSymbolWithTypeAndFlags(lldb::SymbolType symbol_type, + uint32_t flags_value) const; + //---------------------------------------------------------------------- /// Get the parent symbol for the given symbol. /// Index: packages/Python/lldbsuite/test/macosx/debug_map/Makefile =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/macosx/debug_map/Makefile @@ -0,0 +1,27 @@ +LEVEL = ../../make + +# Python script can pass parameters CFLAGS, CFLAGS_CUx, CFLAGS_MAIN and +# LD_EXTRAS. For example: +# +# self.build(dictionary={ +# 'CFLAGS_CU1': '-flto', +# 'CFLAGS_CU2': '-flto', +# 'LD_EXTRAS': '-flto -Xlinker -object_path_lto -Xlinker test_lt12_lto.o' +# }) + +include $(LEVEL)/Makefile.rules + +main.o: main.c + $(CC) -c $(CFLAGS) $(CFLAGS_MAIN) -o main.o $(SRCDIR)/main.c + +cu1.o: cu1.c + $(CC) -c $(CFLAGS) $(CFLAGS_CU1) -o cu1.o $(SRCDIR)/cu1.c + +cu2.o: cu2.c + $(CC) -c $(CFLAGS) $(CFLAGS_CU2) -o cu2.o $(SRCDIR)/cu2.c + +cu3.o: cu3.c + $(CC) -c $(CFLAGS) $(CFLAGS_CU3) -o cu3.o $(SRCDIR)/cu3.c + +a.out: main.o cu1.o cu2.o cu3.o + $(LD) main.o cu1.o cu2.o cu3.o -L. $(LDFLAGS) -o a.out Index: packages/Python/lldbsuite/test/macosx/debug_map/TestDebugMap.py =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/macosx/debug_map/TestDebugMap.py @@ -0,0 +1,118 @@ +""" +Test breakpoint resolution in DWARF debug map for different LTO and line-tables-only combinations +""" + +from __future__ import print_function + +import os +import time +import re +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * + +class DebugMapTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + # If your test case doesn't stress debug info, set this to true. + # That way it won't be run once for each debug info format. + NO_DEBUG_INFO_TESTCASE = False + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + def run_to_bp(self, file_name): + file = lldb.SBFileSpec(file_name) + line_hint = "Set a breakpoint here" + return lldbutil.run_to_source_breakpoint(self, line_hint, file) + + def assertFunctionIs(self, halt, name): + # halt = (target, process, thread, bkpt) + self.assertTrue(halt[2].GetFrameAtIndex(0).GetFunctionName(), name) + self.runCmd("continue") + + def check_all_breakpoints(self): + halt1 = self.run_to_bp("cu1.c") + self.assertFunctionIs(halt1, "cu1_main") + + halt2 = self.run_to_bp("cu2.c") + self.assertFunctionIs(halt2, "cu2_main") + + halt3 = self.run_to_bp("cu3.c") + self.assertFunctionIs(halt3, "cu3_main") + + def test_bc(self): + """Debug map with full debug info and all CUs in one entry""" + self.build(dictionary={ + 'CFLAGS': '-flto', + 'LD_EXTRAS': '-flto -Xlinker -object_path_lto -Xlinker test_bc_lto.o' + }) + self.check_all_breakpoints() + + def test_lt(self): + """Debug map with line tables only and one CU per entry""" + self.build(dictionary={ + 'CFLAGS': '-gline-tables-only', + 'LD_EXTRAS': '-gline-tables-only' + }) + self.check_all_breakpoints() + + def test_bc12(self): + """Debug map with full debug info and order of entries: main.o, test_lt12_lto.o, cu3.o""" + # Note: actual order of entries is expected to become: main.o, cu3.o, test_lt12_lto.o + self.build(dictionary={ + 'CFLAGS_CU1': '-flto', + 'CFLAGS_CU2': '-flto', + 'LD_EXTRAS': '-flto -Xlinker -object_path_lto -Xlinker test_lt12_lto.o' + }) + self.check_all_breakpoints() + + def test_bc23(self): + """Debug map with full debug info and order of entries: test_lt01_lto.o, cu2.o, cu3.o""" + # Note: actual order of entries is expected to become: cu2.o, cu3.o, test_lt01_lto.o + self.build(dictionary={ + 'CFLAGS_CU1': '-flto', + 'CFLAGS_MAIN': '-flto', + 'LD_EXTRAS': '-flto -Xlinker -object_path_lto -Xlinker test_lt01_lto.o' + }) + self.check_all_breakpoints() + + def test_bc12_lt12(self): + """Debug map with line tables only in LTO sources""" + self.build(dictionary={ + 'CFLAGS_CU1': '-flto -gline-tables-only', + 'CFLAGS_CU2': '-flto -gline-tables-only', + 'LD_EXTRAS': '-flto -Xlinker -object_path_lto -Xlinker test_bc12_lt12_lto.o' + }) + self.check_all_breakpoints() + + def test_bc12_lt2(self): + """Debug map with line tables only in one of the LTO sources""" + self.build(dictionary={ + 'CFLAGS_CU1': '-flto', + 'CFLAGS_CU2': '-flto -gline-tables-only', + 'LD_EXTRAS': '-flto -Xlinker -object_path_lto -Xlinker test_bc12_lt2_lto.o' + }) + self.check_all_breakpoints() + + def test_bc12_lt3(self): + """Debug map with line tables only in a non-LTO entry""" + self.build(dictionary={ + 'CFLAGS_CU1': '-flto', + 'CFLAGS_CU2': '-flto', + 'CFLAGS_CU3': '-gline-tables-only', + 'LD_EXTRAS': '-flto -Xlinker -object_path_lto -Xlinker test_bc12_lt3_lto.o' + }) + self.check_all_breakpoints() + + def test_bc12_stripped(self): + """Debug map with missing LTO sources""" + self.build(dictionary={ + 'CFLAGS_CU1': '-flto', + 'CFLAGS_CU2': '-flto', + 'LD_EXTRAS': '-flto' + }) + only_halt = self.run_to_bp("cu3.c") + self.assertFunctionIs(only_halt, "cu3_main") Index: packages/Python/lldbsuite/test/macosx/debug_map/cu1.h =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/macosx/debug_map/cu1.h @@ -0,0 +1 @@ +int cu1_main(int n); Index: packages/Python/lldbsuite/test/macosx/debug_map/cu1.c =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/macosx/debug_map/cu1.c @@ -0,0 +1,5 @@ +#include "cu1.h" + +int cu1_main(int n) { + return n + 1; // Set a breakpoint here +} Index: packages/Python/lldbsuite/test/macosx/debug_map/cu2.h =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/macosx/debug_map/cu2.h @@ -0,0 +1 @@ +int cu2_main(int n); Index: packages/Python/lldbsuite/test/macosx/debug_map/cu2.c =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/macosx/debug_map/cu2.c @@ -0,0 +1,5 @@ +#include "cu2.h" + +int cu2_main(int n) { + return n + 2; // Set a breakpoint here +} Index: packages/Python/lldbsuite/test/macosx/debug_map/cu3.h =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/macosx/debug_map/cu3.h @@ -0,0 +1 @@ +int cu3_main(int n); Index: packages/Python/lldbsuite/test/macosx/debug_map/cu3.c =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/macosx/debug_map/cu3.c @@ -0,0 +1,5 @@ +#include "cu3.h" + +int cu3_main(int n) { + return n - 3; // Set a breakpoint here +} Index: packages/Python/lldbsuite/test/macosx/debug_map/main.c =================================================================== --- /dev/null +++ packages/Python/lldbsuite/test/macosx/debug_map/main.c @@ -0,0 +1,10 @@ +#include "cu1.h" +#include "cu2.h" +#include "cu3.h" + +int main(int argc, char **argv) { + int cu1_res = cu1_main(argc - 1); // +1 + int cu2_res = cu2_main(cu1_res); // +2 + int cu3_res = cu3_main(cu2_res); // -3 + return cu3_res; +} Index: source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp +++ source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -426,8 +426,17 @@ const dw_offset_t cu_offset = GetOffset(); if (die) { DWARFRangeList ranges; + + // DW_AT_low/high_pc attributes of DW_TAG_compile_unit are no suitable + // fallback for deducing code address ranges. It can have outdated data or + // result in overlapping ranges, e.g. if the CUs actual code is interleaved. + // Instead BuildAddressRangeTable() below will index the DWARF and generate + // the address ranges table manually, based on DW_AT_low/high_pc attributes + // of nested DW_TAG_subprogram tags. + static constexpr bool check_hi_lo_pc = false; const size_t num_ranges = - die->GetAttributeAddressRanges(dwarf, this, ranges, false); + die->GetAttributeAddressRanges(dwarf, this, ranges, check_hi_lo_pc); + if (num_ranges > 0) { // This compile unit has DW_AT_ranges, assume this is correct if it is // present since clang no longer makes .debug_aranges by default and it @@ -477,7 +486,7 @@ } } } else - debug_map_sym_file->AddOSOARanges(dwarf, debug_aranges); + debug_map_sym_file->AddOSOARanges(dwarf, cu_offset, debug_aranges); } } Index: source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h =================================================================== --- source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -275,7 +275,7 @@ lldb_private::CompileUnit * GetCompUnitForDWARFCompUnit(DWARFUnit *dwarf_cu, - uint32_t cu_idx = UINT32_MAX); + uint32_t cu_idx = DW_INVALID_INDEX); virtual size_t GetObjCMethodDIEOffsets(lldb_private::ConstString class_name, DIEArray &method_die_offsets); Index: source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -778,9 +778,25 @@ if (dwarf_cu->GetSymbolFileDWARF() != this) { return dwarf_cu->GetSymbolFileDWARF()->ParseCompileUnit(dwarf_cu, cu_idx); - } else if (dwarf_cu->GetOffset() == 0 && GetDebugMapSymfile()) { + } else if (SymbolFileDWARFDebugMap *debug_map = GetDebugMapSymfile()) { + assert(debug_map == m_debug_map_symfile && + "debug_map is a nested instance derived from SymbolFile, " + "NOT SymbolFileDWARF"); + // Let the debug map create the compile unit - cu_sp = m_debug_map_symfile->GetCompileUnit(this); + if (debug_map->GetNumCompileUnits() == 1) { + cu_sp = debug_map->GetCompileUnit(this); + } else if (cu_idx != UINT32_MAX) { + cu_sp = debug_map->GetCompileUnitByIndex(this, cu_idx); + } else { + lldb::offset_t cu_offset = dwarf_cu->GetOffset(); + if (cu_offset != DW_INVALID_OFFSET) { + cu_sp = debug_map->GetCompileUnitByOffset(this, cu_offset); + } else { + llvm_unreachable("Need more info to find the correct CU"); + } + } + dwarf_cu->SetUserData(cu_sp.get()); } else { ModuleSP module_sp(m_obj_file->GetModule()); Index: source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h =================================================================== --- source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h +++ source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h @@ -131,7 +131,11 @@ uint32_t GetPluginVersion() override; protected: - enum { kHaveInitializedOSOs = (1 << 0), kNumFlags }; + enum { + kHaveInitializedOSOs = (1 << 0), + kHaveCheckedCUInfos = (1 << 1), + kNumFlags + }; friend class DebugMapModule; friend struct DIERef; @@ -172,6 +176,7 @@ first_symbol_id(UINT32_MAX), last_symbol_id(UINT32_MAX), file_range_map(), file_range_map_valid(false) {} + bool Init(lldb_private::Symbol *so, lldb_private::Symbol *oso); const FileRangeMap &GetFileRangeMap(SymbolFileDWARFDebugMap *exe_symfile); }; @@ -179,6 +184,9 @@ // Protected Member Functions //------------------------------------------------------------------ void InitOSO(); + bool HasCompileUnits(); + void ReportErrorInitOSO(lldb_private::Symbol *so_symbol, + lldb_private::Symbol *oso_symbol, uint32_t oso_idx); static uint32_t GetOSOIndexFromUserID(lldb::user_id_t uid) { return (uint32_t)((uid >> 32ull) - 1ull); @@ -247,6 +255,11 @@ lldb::CompUnitSP GetCompileUnit(SymbolFileDWARF *oso_dwarf); + lldb::CompUnitSP GetCompileUnitByIndex(SymbolFileDWARF *oso_dwarf, + uint32_t cu_idx); + lldb::CompUnitSP GetCompileUnitByOffset(SymbolFileDWARF *oso_dwarf, + dw_offset_t cu_offset); + CompileUnitInfo *GetCompileUnitInfo(SymbolFileDWARF *oso_dwarf); lldb::TypeSP @@ -297,6 +310,8 @@ // Member Variables //------------------------------------------------------------------ std::bitset m_flags; + bool m_has_compile_unit_infos; + std::vector m_oso_compile_unit_offset; std::vector m_compile_unit_infos; std::vector m_func_indexes; // Sorted by address std::vector m_glob_indexes; @@ -373,7 +388,7 @@ LinkOSOLineTable(SymbolFileDWARF *oso_symfile, lldb_private::LineTable *line_table); - size_t AddOSOARanges(SymbolFileDWARF *dwarf2Data, + size_t AddOSOARanges(SymbolFileDWARF *dwarf2Data, dw_offset_t cu_offset, DWARFDebugAranges *debug_aranges); }; Index: source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp =================================================================== --- source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp +++ source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -37,6 +37,7 @@ #include "lldb/Symbol/VariableList.h" #include "llvm/Support/ScopedPrinter.h" +#include "DWARFCompileUnit.h" #include "LogChannelDWARF.h" #include "SymbolFileDWARF.h" @@ -169,6 +170,23 @@ return file_range_map; } +bool SymbolFileDWARFDebugMap::CompileUnitInfo::Init(Symbol *so, Symbol *oso) { + if (!so || so->GetType() != eSymbolTypeSourceFile) + return false; + + if (!oso || oso->GetType() != eSymbolTypeObjectFile) + return false; + + if (so->GetSiblingIndex() == UINT32_MAX) + return false; + + llvm::StringRef so_file_name = so->GetName().GetStringRef(); + so_file.SetFile(so_file_name, false, FileSpec::Style::native); + oso_mod_time = llvm::sys::toTimePoint(oso->GetIntegerValue(0)); + oso_path = oso->GetName(); + return true; +} + class DebugMapModule : public Module { public: DebugMapModule(const ModuleSP &exe_module_sp, uint32_t cu_idx, @@ -251,28 +269,16 @@ } SymbolFileDWARFDebugMap::SymbolFileDWARFDebugMap(ObjectFile *ofile) - : SymbolFile(ofile), m_flags(), m_compile_unit_infos(), m_func_indexes(), - m_glob_indexes(), + : SymbolFile(ofile), m_flags(), m_has_compile_unit_infos(false), + m_compile_unit_infos(), m_func_indexes(), m_glob_indexes(), m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate) {} SymbolFileDWARFDebugMap::~SymbolFileDWARFDebugMap() {} void SymbolFileDWARFDebugMap::InitializeObject() {} -void SymbolFileDWARFDebugMap::InitOSO() { - if (m_flags.test(kHaveInitializedOSOs)) - return; - - m_flags.set(kHaveInitializedOSOs); - - // If the object file has been stripped, there is no sense in looking further - // as all of the debug symbols for the debug map will not be available - if (m_obj_file->IsStripped()) - return; - - // Also make sure the file type is some sort of executable. Core files, debug - // info files (dSYM), object files (.o files), and stub libraries all can - switch (m_obj_file->GetType()) { +static bool IsExecutableType(ObjectFile::Type type) { + switch (type) { case ObjectFile::eTypeInvalid: case ObjectFile::eTypeCoreFile: case ObjectFile::eTypeDebugInfo: @@ -280,32 +286,46 @@ case ObjectFile::eTypeStubLibrary: case ObjectFile::eTypeUnknown: case ObjectFile::eTypeJIT: - return; + return false; case ObjectFile::eTypeExecutable: case ObjectFile::eTypeDynamicLinker: case ObjectFile::eTypeSharedLibrary: - break; + return true; } +} - // In order to get the abilities of this plug-in, we look at the list of - // N_OSO entries (object files) from the symbol table and make sure that - // these files exist and also contain valid DWARF. If we get any of that then - // we return the abilities of the first N_OSO's DWARF. +static uint32_t GetOSOSymbolFlags() { + // When a mach-o symbol is encoded, the n_type field is encoded in bits + // 23:16, and the n_desc field is encoded in bits 15:0. + // + // N_OSO object file symbols have a flags value as follows: + // bits 23:16 == 0x66 (N_OSO) + // bits 15: 0 == 0x0001 (specifies this is a debug map object file) + return 0x660001u; +} + +void SymbolFileDWARFDebugMap::InitOSO() { + if (m_flags.test(kHaveInitializedOSOs)) + return; + + m_flags.set(kHaveInitializedOSOs); + + // If the object file has been stripped, there is no sense in looking further + // as all of the debug symbols for the debug map will not be available + if (m_obj_file->IsStripped()) + return; + + // Also make sure the file type is some sort of executable. + if (!IsExecutableType(m_obj_file->GetType())) + return; Symtab *symtab = m_obj_file->GetSymtab(); if (symtab) { Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_MAP)); std::vector oso_indexes; - // When a mach-o symbol is encoded, the n_type field is encoded in bits - // 23:16, and the n_desc field is encoded in bits 15:0. - // - // To find all N_OSO entries that are part of the DWARF + debug map we find - // only object file symbols with the flags value as follows: bits 23:16 == - // 0x66 (N_OSO) bits 15: 0 == 0x0001 (specifies this is a debug map object - // file) - const uint32_t k_oso_symbol_flags_value = 0x660001u; + const uint32_t k_oso_symbol_flags_value = GetOSOSymbolFlags(); const uint32_t oso_index_count = symtab->AppendSymbolIndexesWithTypeAndFlagsValue( @@ -340,67 +360,97 @@ } m_debug_map.Sort(); - m_compile_unit_infos.resize(oso_index_count); + assert(m_compile_unit_infos.empty()); + assert(m_oso_compile_unit_offset.empty()); for (uint32_t i = 0; i < oso_index_count; ++i) { const uint32_t so_idx = oso_indexes[i] - 1; const uint32_t oso_idx = oso_indexes[i]; - const Symbol *so_symbol = symtab->SymbolAtIndex(so_idx); - const Symbol *oso_symbol = symtab->SymbolAtIndex(oso_idx); - if (so_symbol && oso_symbol && - so_symbol->GetType() == eSymbolTypeSourceFile && - oso_symbol->GetType() == eSymbolTypeObjectFile) { - m_compile_unit_infos[i].so_file.SetFile( - so_symbol->GetName().AsCString(), false, FileSpec::Style::native); - m_compile_unit_infos[i].oso_path = oso_symbol->GetName(); - m_compile_unit_infos[i].oso_mod_time = - llvm::sys::toTimePoint(oso_symbol->GetIntegerValue(0)); + Symbol *so_symbol = symtab->SymbolAtIndex(so_idx); + Symbol *oso_symbol = symtab->SymbolAtIndex(oso_idx); + + // oso_info is the basis for the individual cu_infos below. + CompileUnitInfo oso_info; + if (!oso_info.Init(so_symbol, oso_symbol)) { + ReportErrorInitOSO(so_symbol, oso_symbol, oso_idx); + continue; + } + + SymbolFileDWARF *sym_file = GetSymbolFileByCompUnitInfo(&oso_info); + if (!sym_file) + continue; + + const auto &debug_info_data = sym_file->get_debug_info_data(); + auto ExtractNext = [sym_file, &debug_info_data](lldb::offset_t offs) { + return DWARFCompileUnit::Extract(sym_file, debug_info_data, &offs); + }; + + for (DWARFUnitSP cu_sp = ExtractNext(0); cu_sp != nullptr; + cu_sp = ExtractNext(cu_sp->GetNextCompileUnitOffset())) { + // FIXME: Multiple OSO entries with multiple CUs may be a rare, but + // while I am here, I should store CU OFFSETS BY OSO ENTRY! It may + // move to m_oso_entry_info. + m_oso_compile_unit_offset.push_back(cu_sp->GetOffset()); + + // Make a copy of the oso_info and add compile unit specific data. + // + // FIXME: There is no CU-specific data currently, because symbol + // ranges are the same for all CUs and the offset is stored + // separately. It may be turned into a m_oso_entry_info. + m_compile_unit_infos.push_back(oso_info); + CompileUnitInfo &cu_info = m_compile_unit_infos.back(); + uint32_t sibling_idx = so_symbol->GetSiblingIndex(); - // The sibling index can't be less that or equal to the current index - // "i" - if (sibling_idx == UINT32_MAX) { - m_obj_file->GetModule()->ReportError( - "N_SO in symbol with UID %u has invalid sibling in debug map, " - "please file a bug and attach the binary listed in this error", - so_symbol->GetID()); - } else { - const Symbol *last_symbol = symtab->SymbolAtIndex(sibling_idx - 1); - m_compile_unit_infos[i].first_symbol_index = so_idx; - m_compile_unit_infos[i].last_symbol_index = sibling_idx - 1; - m_compile_unit_infos[i].first_symbol_id = so_symbol->GetID(); - m_compile_unit_infos[i].last_symbol_id = last_symbol->GetID(); - - if (log) - log->Printf("Initialized OSO 0x%8.8x: file=%s", i, - oso_symbol->GetName().GetCString()); - } - } else { - if (oso_symbol == NULL) - m_obj_file->GetModule()->ReportError( - "N_OSO symbol[%u] can't be found, please file a bug and attach " - "the binary listed in this error", - oso_idx); - else if (so_symbol == NULL) - m_obj_file->GetModule()->ReportError( - "N_SO not found for N_OSO symbol[%u], please file a bug and " - "attach the binary listed in this error", - oso_idx); - else if (so_symbol->GetType() != eSymbolTypeSourceFile) - m_obj_file->GetModule()->ReportError( - "N_SO has incorrect symbol type (%u) for N_OSO symbol[%u], " - "please file a bug and attach the binary listed in this error", - so_symbol->GetType(), oso_idx); - else if (oso_symbol->GetType() != eSymbolTypeSourceFile) - m_obj_file->GetModule()->ReportError( - "N_OSO has incorrect symbol type (%u) for N_OSO symbol[%u], " - "please file a bug and attach the binary listed in this error", - oso_symbol->GetType(), oso_idx); + const Symbol *last_symbol = symtab->SymbolAtIndex(sibling_idx - 1); + + // For (regular) objects with only one compile unit, there is a + // bidirectional mapping between a symtab slice and a compile unit. + // LTO objects do not allow such a mapping and so we assign the full + // symtab range to all compile units of the same oso_entry. + cu_info.first_symbol_index = so_idx; + cu_info.last_symbol_index = sibling_idx - 1; + cu_info.first_symbol_id = so_symbol->GetID(); + cu_info.last_symbol_id = last_symbol->GetID(); } + + if (log) + log->Printf("Initialized OSO 0x%8.8x: file=%s", i, + oso_symbol->GetName().GetCString()); } } } } +void SymbolFileDWARFDebugMap::ReportErrorInitOSO(Symbol *so_symbol, + Symbol *oso_symbol, + uint32_t oso_idx) { + if (oso_symbol == NULL) + m_obj_file->GetModule()->ReportError( + "N_OSO symbol[%u] can't be found, please file a bug and attach " + "the binary listed in this error", + oso_idx); + else if (so_symbol == NULL) + m_obj_file->GetModule()->ReportError( + "N_SO not found for N_OSO symbol[%u], please file a bug and " + "attach the binary listed in this error", + oso_idx); + else if (so_symbol->GetType() != eSymbolTypeSourceFile) + m_obj_file->GetModule()->ReportError( + "N_SO has incorrect symbol type (%u) for N_OSO symbol[%u], " + "please file a bug and attach the binary listed in this error", + so_symbol->GetType(), oso_idx); + else if (oso_symbol->GetType() != eSymbolTypeSourceFile) + m_obj_file->GetModule()->ReportError( + "N_OSO has incorrect symbol type (%u) for N_OSO symbol[%u], " + "please file a bug and attach the binary listed in this error", + oso_symbol->GetType(), oso_idx); + else if (so_symbol->GetSiblingIndex() == UINT32_MAX) + m_obj_file->GetModule()->ReportError( + "N_SO in symbol with UID %u has invalid sibling in debug map, " + "please file a bug and attach the binary listed in this error", + so_symbol->GetID()); +} + Module *SymbolFileDWARFDebugMap::GetModuleByOSOIndex(uint32_t oso_idx) { const uint32_t cu_count = GetNumCompileUnits(); if (oso_idx < cu_count) @@ -547,20 +597,40 @@ // N_OSO entries (object files) from the symbol table and make sure that // these files exist and also contain valid DWARF. If we get any of that then // we return the abilities of the first N_OSO's DWARF. + if (HasCompileUnits()) + return SymbolFile::CompileUnits | SymbolFile::Functions | + SymbolFile::Blocks | SymbolFile::GlobalVariables | + SymbolFile::LocalVariables | SymbolFile::VariableTypes | + SymbolFile::LineTables; - const uint32_t oso_index_count = GetNumCompileUnits(); - if (oso_index_count > 0) { - InitOSO(); - if (!m_compile_unit_infos.empty()) { - return SymbolFile::CompileUnits | SymbolFile::Functions | - SymbolFile::Blocks | SymbolFile::GlobalVariables | - SymbolFile::LocalVariables | SymbolFile::VariableTypes | - SymbolFile::LineTables; - } - } return 0; } +bool SymbolFileDWARFDebugMap::HasCompileUnits() { + if (m_flags.test(kHaveCheckedCUInfos)) + return m_has_compile_unit_infos; + + m_flags.set(kHaveCheckedCUInfos); + + // If the object file has been stripped, there is no sense in looking further + // as all of the debug symbols for the debug map will not be available. + // + // FIXME: This should be renamed to IsStrippedOrMissing() + if (m_obj_file->IsStripped()) + return false; + + if (!IsExecutableType(m_obj_file->GetType())) + return false; + + assert(m_has_compile_unit_infos == false && "Arrive here only once"); + + if (Symtab *symtab = m_obj_file->GetSymtab()) + m_has_compile_unit_infos = symtab->HasSymbolWithTypeAndFlags( + eSymbolTypeObjectFile, GetOSOSymbolFlags()); + + return m_has_compile_unit_infos; +} + uint32_t SymbolFileDWARFDebugMap::GetNumCompileUnits() { InitOSO(); return m_compile_unit_infos.size(); @@ -575,12 +645,10 @@ if (oso_module) { FileSpec so_file_spec; if (GetFileSpecForSO(cu_idx, so_file_spec)) { - // User zero as the ID to match the compile unit at offset zero in each - // .o file since each .o file can only have one compile unit for now. - lldb::user_id_t cu_id = 0; - m_compile_unit_infos[cu_idx].compile_unit_sp.reset( - new CompileUnit(m_obj_file->GetModule(), NULL, so_file_spec, cu_id, - eLanguageTypeUnknown, eLazyBoolCalculate)); + lldb::user_id_t cu_offset = m_oso_compile_unit_offset[cu_idx]; + m_compile_unit_infos[cu_idx].compile_unit_sp.reset(new CompileUnit( + m_obj_file->GetModule(), NULL, so_file_spec, cu_offset, + eLanguageTypeUnknown, eLazyBoolCalculate)); if (m_compile_unit_infos[cu_idx].compile_unit_sp) { // Let our symbol vendor know about this compile unit @@ -734,6 +802,15 @@ if (sc.symbol != NULL) { resolved_flags |= eSymbolContextSymbol; + // FIXME: Search by symbol Index/ID will NOT return the correct info for + // objects with multiple compile units. We cannot safely match slices of + // the symtab with individual units (it's very likely that optimization + // mixed it up), and so all compile units from such objects share the + // full symbol range. + // + // For the moment this is not a problem here, because we only use the + // compile unit info to find the module, which is invariant across all + // compile units of one object file. uint32_t oso_idx = 0; CompileUnitInfo *comp_unit_info = GetCompileUnitInfoForSymbolWithID(sc.symbol->GetID(), &oso_idx); @@ -1244,6 +1321,32 @@ llvm_unreachable("this shouldn't happen"); } +lldb::CompUnitSP +SymbolFileDWARFDebugMap::GetCompileUnitByIndex(SymbolFileDWARF *oso_dwarf, + uint32_t cu_idx) { + assert(cu_idx < GetNumCompileUnits() && "CU index out of range"); + auto &cu_info = m_compile_unit_infos[cu_idx]; + if (!cu_info.compile_unit_sp) + cu_info.compile_unit_sp = ParseCompileUnitAtIndex(cu_idx); + + return cu_info.compile_unit_sp; +} + +lldb::CompUnitSP +SymbolFileDWARFDebugMap::GetCompileUnitByOffset(SymbolFileDWARF *oso_dwarf, + dw_offset_t cu_offset) { + auto begin = m_oso_compile_unit_offset.begin(); + auto end = m_oso_compile_unit_offset.end(); + auto it = std::lower_bound(begin, end, cu_offset); + + if (it != end) { + uint32_t cu_idx = std::distance(begin, it); + return GetCompileUnitByIndex(oso_dwarf, cu_idx); + } + + return nullptr; +} + SymbolFileDWARFDebugMap::CompileUnitInfo * SymbolFileDWARFDebugMap::GetCompileUnitInfo(SymbolFileDWARF *oso_dwarf) { if (oso_dwarf) { @@ -1380,6 +1483,16 @@ addr_module->GetSymbolVendor()->GetSymbolFile())); if (cu_info) { const lldb::addr_t oso_file_addr = addr.GetFileAddress(); + + // FIXME: Search by symbol Index/ID will NOT return the correct info for + // objects with multiple compile units. We cannot safely match slices of + // the symtab with individual units (it's very likely that optimization + // mixed it up), and so all compile units from such objects share the + // full symbol range. + // + // For the moment this is not a problem here, because we only use the + // compile unit info to find the module, which is invariant across all + // compile units of one object file. const FileRangeMap::Entry *oso_range_entry = cu_info->GetFileRangeMap(this).FindEntryThatContains(oso_file_addr); if (oso_range_entry) { @@ -1407,7 +1520,16 @@ size_t SymbolFileDWARFDebugMap::AddOSOARanges(SymbolFileDWARF *dwarf2Data, + dw_offset_t cu_offset, DWARFDebugAranges *debug_aranges) { + // For OSO debug map entries with multiple CUs, we can't map between symtab + // entries and CUs. Thus, each CU's FileRangeMap will be populated from the + // entire symtab range of that OSO debug map entry. The loop below would add + // invalid entries (from other CUs of this OSO debug map entry) and we had to + // test each of them in SymbolFileDWARF::ResolveSymbolContext(). + if (m_compile_unit_infos.size() > 1) + return 0; + size_t num_line_entries_added = 0; if (debug_aranges && dwarf2Data) { CompileUnitInfo *compile_unit_info = GetCompileUnitInfo(dwarf2Data); @@ -1417,7 +1539,7 @@ for (size_t idx = 0; idx < file_range_map.GetSize(); idx++) { const FileRangeMap::Entry *entry = file_range_map.GetEntryAtIndex(idx); if (entry) { - debug_aranges->AppendRange(dwarf2Data->GetID(), entry->GetRangeBase(), + debug_aranges->AppendRange(cu_offset, entry->GetRangeBase(), entry->GetRangeEnd()); num_line_entries_added++; } Index: source/Symbol/Symtab.cpp =================================================================== --- source/Symbol/Symtab.cpp +++ source/Symbol/Symtab.cpp @@ -509,6 +509,16 @@ return indexes.size() - prev_size; } +bool Symtab::HasSymbolWithTypeAndFlags(lldb::SymbolType symbol_type, + uint32_t flags_value) const { + std::lock_guard guard(m_mutex); + for (const Symbol &symbol : m_symbols) + if (symbol.GetType() == symbol_type && symbol.GetFlags() == flags_value) + return true; + + return false; +} + uint32_t Symtab::AppendSymbolIndexesWithType(SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility,