Path: blob/main/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp
39644 views
//===-- DebugNamesDWARFIndex.cpp ------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h"9#include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h"10#include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"11#include "Plugins/SymbolFile/DWARF/LogChannelDWARF.h"12#include "Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.h"13#include "lldb/Core/Module.h"14#include "lldb/Utility/RegularExpression.h"15#include "lldb/Utility/Stream.h"16#include "llvm/ADT/Sequence.h"17#include <optional>1819using namespace lldb_private;20using namespace lldb;21using namespace lldb_private::dwarf;22using namespace lldb_private::plugin::dwarf;2324llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>>25DebugNamesDWARFIndex::Create(Module &module, DWARFDataExtractor debug_names,26DWARFDataExtractor debug_str,27SymbolFileDWARF &dwarf) {28auto index_up = std::make_unique<DebugNames>(debug_names.GetAsLLVMDWARF(),29debug_str.GetAsLLVM());30if (llvm::Error E = index_up->extract())31return std::move(E);3233return std::unique_ptr<DebugNamesDWARFIndex>(new DebugNamesDWARFIndex(34module, std::move(index_up), debug_names, debug_str, dwarf));35}3637llvm::DenseSet<uint64_t>38DebugNamesDWARFIndex::GetTypeUnitSignatures(const DebugNames &debug_names) {39llvm::DenseSet<uint64_t> result;40for (const DebugNames::NameIndex &ni : debug_names) {41const uint32_t num_tus = ni.getForeignTUCount();42for (uint32_t tu = 0; tu < num_tus; ++tu)43result.insert(ni.getForeignTUSignature(tu));44}45return result;46}4748llvm::DenseSet<dw_offset_t>49DebugNamesDWARFIndex::GetUnits(const DebugNames &debug_names) {50llvm::DenseSet<dw_offset_t> result;51for (const DebugNames::NameIndex &ni : debug_names) {52const uint32_t num_cus = ni.getCUCount();53for (uint32_t cu = 0; cu < num_cus; ++cu)54result.insert(ni.getCUOffset(cu));55const uint32_t num_tus = ni.getLocalTUCount();56for (uint32_t tu = 0; tu < num_tus; ++tu)57result.insert(ni.getLocalTUOffset(tu));58}59return result;60}6162std::optional<DWARFTypeUnit *>63DebugNamesDWARFIndex::GetForeignTypeUnit(const DebugNames::Entry &entry) const {64std::optional<uint64_t> type_sig = entry.getForeignTUTypeSignature();65if (!type_sig.has_value())66return std::nullopt;6768// Ask the entry for the skeleton compile unit offset and fetch the .dwo69// file from it and get the type unit by signature from there. If we find70// the type unit in the .dwo file, we don't need to check that the71// DW_AT_dwo_name matches because each .dwo file can have its own type unit.72std::optional<uint64_t> cu_offset = entry.getRelatedCUOffset();73if (!cu_offset)74return nullptr; // Return NULL, this is a type unit, but couldn't find it.7576DWARFUnit *cu =77m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo, *cu_offset);78if (!cu)79return nullptr; // Return NULL, this is a type unit, but couldn't find it.8081auto dwp_sp = m_debug_info.GetDwpSymbolFile();82if (!dwp_sp) {83// No .dwp file, we need to load the .dwo file.84DWARFUnit &dwo_cu = cu->GetNonSkeletonUnit();85// We don't need the check if the type unit matches the .dwo file if we have86// a .dwo file (not a .dwp), so we can just return the value here.87if (!dwo_cu.IsDWOUnit())88return nullptr; // We weren't able to load the .dwo file.89return dwo_cu.GetSymbolFileDWARF().DebugInfo().GetTypeUnitForHash(90*type_sig);91}92// We have a .dwp file, just get the type unit from there. We need to verify93// that the type unit that ended up in the final .dwp file is the right type94// unit. Type units have signatures which are the same across multiple .dwo95// files, but only one of those type units will end up in the .dwp file. The96// contents of type units for the same type can be different in different .dwo97// files, which means the DIE offsets might not be the same between two98// different type units. So we need to determine if this accelerator table99// matches the type unit that ended up in the .dwp file. If it doesn't match,100// then we need to ignore this accelerator table entry as the type unit that101// is in the .dwp file will have its own index. In order to determine if the102// type unit that ended up in a .dwp file matches this DebugNames::Entry, we103// need to find the skeleton compile unit for this entry.104DWARFTypeUnit *foreign_tu = dwp_sp->DebugInfo().GetTypeUnitForHash(*type_sig);105if (!foreign_tu)106return nullptr; // Return NULL, this is a type unit, but couldn't find it.107108DWARFBaseDIE cu_die = cu->GetUnitDIEOnly();109DWARFBaseDIE tu_die = foreign_tu->GetUnitDIEOnly();110llvm::StringRef cu_dwo_name =111cu_die.GetAttributeValueAsString(DW_AT_dwo_name, nullptr);112llvm::StringRef tu_dwo_name =113tu_die.GetAttributeValueAsString(DW_AT_dwo_name, nullptr);114if (cu_dwo_name == tu_dwo_name)115return foreign_tu; // We found a match!116return nullptr; // Return NULL, this is a type unit, but couldn't find it.117}118119DWARFUnit *120DebugNamesDWARFIndex::GetNonSkeletonUnit(const DebugNames::Entry &entry) const {121122if (std::optional<DWARFTypeUnit *> foreign_tu = GetForeignTypeUnit(entry))123return foreign_tu.value();124125// Look for a DWARF unit offset (CU offset or local TU offset) as they are126// both offsets into the .debug_info section.127std::optional<uint64_t> unit_offset = entry.getCUOffset();128if (!unit_offset)129unit_offset = entry.getLocalTUOffset();130if (unit_offset) {131if (DWARFUnit *cu = m_debug_info.GetUnitAtOffset(DIERef::Section::DebugInfo,132*unit_offset))133return &cu->GetNonSkeletonUnit();134}135return nullptr;136}137138DWARFDIE DebugNamesDWARFIndex::GetDIE(const DebugNames::Entry &entry) const {139DWARFUnit *unit = GetNonSkeletonUnit(entry);140std::optional<uint64_t> die_offset = entry.getDIEUnitOffset();141if (!unit || !die_offset)142return DWARFDIE();143if (DWARFDIE die = unit->GetDIE(unit->GetOffset() + *die_offset))144return die;145146m_module.ReportErrorIfModifyDetected(147"the DWARF debug information has been modified (bad offset {0:x} in "148"debug_names section)\n",149*die_offset);150return DWARFDIE();151}152153bool DebugNamesDWARFIndex::ProcessEntry(154const DebugNames::Entry &entry,155llvm::function_ref<bool(DWARFDIE die)> callback) {156DWARFDIE die = GetDIE(entry);157if (!die)158return true;159// Clang used to erroneously emit index entries for declaration DIEs in case160// when the definition is in a type unit (llvm.org/pr77696).161if (die.IsStructUnionOrClass() &&162die.GetAttributeValueAsUnsigned(DW_AT_declaration, 0))163return true;164return callback(die);165}166167void DebugNamesDWARFIndex::MaybeLogLookupError(llvm::Error error,168const DebugNames::NameIndex &ni,169llvm::StringRef name) {170// Ignore SentinelErrors, log everything else.171LLDB_LOG_ERROR(172GetLog(DWARFLog::Lookups),173handleErrors(std::move(error), [](const DebugNames::SentinelError &) {}),174"Failed to parse index entries for index at {1:x}, name {2}: {0}",175ni.getUnitOffset(), name);176}177178void DebugNamesDWARFIndex::GetGlobalVariables(179ConstString basename, llvm::function_ref<bool(DWARFDIE die)> callback) {180for (const DebugNames::Entry &entry :181m_debug_names_up->equal_range(basename.GetStringRef())) {182if (entry.tag() != DW_TAG_variable)183continue;184185if (!ProcessEntry(entry, callback))186return;187}188189m_fallback.GetGlobalVariables(basename, callback);190}191192void DebugNamesDWARFIndex::GetGlobalVariables(193const RegularExpression ®ex,194llvm::function_ref<bool(DWARFDIE die)> callback) {195for (const DebugNames::NameIndex &ni: *m_debug_names_up) {196for (DebugNames::NameTableEntry nte: ni) {197Mangled mangled_name(nte.getString());198if (!mangled_name.NameMatches(regex))199continue;200201uint64_t entry_offset = nte.getEntryOffset();202llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);203for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {204if (entry_or->tag() != DW_TAG_variable)205continue;206207if (!ProcessEntry(*entry_or, callback))208return;209}210MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());211}212}213214m_fallback.GetGlobalVariables(regex, callback);215}216217void DebugNamesDWARFIndex::GetGlobalVariables(218DWARFUnit &cu, llvm::function_ref<bool(DWARFDIE die)> callback) {219uint64_t cu_offset = cu.GetOffset();220bool found_entry_for_cu = false;221for (const DebugNames::NameIndex &ni : *m_debug_names_up) {222// Check if this name index contains an entry for the given CU.223bool cu_matches = false;224for (uint32_t i = 0; i < ni.getCUCount(); ++i) {225if (ni.getCUOffset(i) == cu_offset) {226cu_matches = true;227break;228}229}230if (!cu_matches)231continue;232233for (DebugNames::NameTableEntry nte : ni) {234uint64_t entry_offset = nte.getEntryOffset();235llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);236for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {237if (entry_or->tag() != DW_TAG_variable)238continue;239if (entry_or->getCUOffset() != cu_offset)240continue;241242found_entry_for_cu = true;243if (!ProcessEntry(*entry_or, callback))244return;245}246MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());247}248}249// If no name index for that particular CU was found, fallback to250// creating the manual index.251if (!found_entry_for_cu)252m_fallback.GetGlobalVariables(cu, callback);253}254255void DebugNamesDWARFIndex::GetCompleteObjCClass(256ConstString class_name, bool must_be_implementation,257llvm::function_ref<bool(DWARFDIE die)> callback) {258// Keep a list of incomplete types as fallback for when we don't find the259// complete type.260std::vector<DWARFDIE> incomplete_types;261262for (const DebugNames::Entry &entry :263m_debug_names_up->equal_range(class_name.GetStringRef())) {264if (entry.tag() != DW_TAG_structure_type &&265entry.tag() != DW_TAG_class_type)266continue;267268DWARFDIE die = GetDIE(entry);269if (!die) {270// Report invalid271continue;272}273DWARFUnit *cu = die.GetCU();274if (!cu->Supports_DW_AT_APPLE_objc_complete_type()) {275incomplete_types.push_back(die);276continue;277}278279if (die.GetAttributeValueAsUnsigned(DW_AT_APPLE_objc_complete_type, 0)) {280// If we find the complete version we're done.281callback(die);282return;283}284incomplete_types.push_back(die);285}286287for (DWARFDIE die : incomplete_types)288if (!callback(die))289return;290291m_fallback.GetCompleteObjCClass(class_name, must_be_implementation, callback);292}293294namespace {295using Entry = llvm::DWARFDebugNames::Entry;296297/// If `entry` and all of its parents have an `IDX_parent`, use that information298/// to build and return a list of at most `max_parents` parent Entries.299/// `entry` itself is not included in the list.300/// If any parent does not have an `IDX_parent`, or the Entry data is corrupted,301/// nullopt is returned.302std::optional<llvm::SmallVector<Entry, 4>>303getParentChain(Entry entry, uint32_t max_parents) {304llvm::SmallVector<Entry, 4> parent_entries;305306do {307if (!entry.hasParentInformation())308return std::nullopt;309310llvm::Expected<std::optional<Entry>> parent = entry.getParentDIEEntry();311if (!parent) {312// Bad data.313LLDB_LOG_ERROR(314GetLog(DWARFLog::Lookups), parent.takeError(),315"Failed to extract parent entry from a non-empty IDX_parent");316return std::nullopt;317}318319// Last parent in the chain.320if (!parent->has_value())321break;322323parent_entries.push_back(**parent);324entry = **parent;325} while (parent_entries.size() < max_parents);326327return parent_entries;328}329} // namespace330331void DebugNamesDWARFIndex::GetFullyQualifiedType(332const DWARFDeclContext &context,333llvm::function_ref<bool(DWARFDIE die)> callback) {334if (context.GetSize() == 0)335return;336337llvm::StringRef leaf_name = context[0].name;338llvm::SmallVector<llvm::StringRef> parent_names;339for (auto idx : llvm::seq<int>(1, context.GetSize()))340parent_names.emplace_back(context[idx].name);341342// For each entry, grab its parent chain and check if we have a match.343for (const DebugNames::Entry &entry :344m_debug_names_up->equal_range(leaf_name)) {345if (!isType(entry.tag()))346continue;347348// If we get a NULL foreign_tu back, the entry doesn't match the type unit349// in the .dwp file, or we were not able to load the .dwo file or the DWO ID350// didn't match.351std::optional<DWARFTypeUnit *> foreign_tu = GetForeignTypeUnit(entry);352if (foreign_tu && foreign_tu.value() == nullptr)353continue;354355// Grab at most one extra parent, subsequent parents are not necessary to356// test equality.357std::optional<llvm::SmallVector<Entry, 4>> parent_chain =358getParentChain(entry, parent_names.size() + 1);359360if (!parent_chain) {361// Fallback: use the base class implementation.362if (!ProcessEntry(entry, [&](DWARFDIE die) {363return GetFullyQualifiedTypeImpl(context, die, callback);364}))365return;366continue;367}368369if (SameParentChain(parent_names, *parent_chain) &&370!ProcessEntry(entry, callback))371return;372}373}374375bool DebugNamesDWARFIndex::SameParentChain(376llvm::ArrayRef<llvm::StringRef> parent_names,377llvm::ArrayRef<DebugNames::Entry> parent_entries) const {378379if (parent_entries.size() != parent_names.size())380return false;381382auto SameAsEntryATName = [this](llvm::StringRef name,383const DebugNames::Entry &entry) {384// Peek at the AT_name of `entry` and test equality to `name`.385auto maybe_dieoffset = entry.getDIEUnitOffset();386if (!maybe_dieoffset)387return false;388DWARFUnit *unit = GetNonSkeletonUnit(entry);389if (!unit)390return false;391return name == unit->PeekDIEName(unit->GetOffset() + *maybe_dieoffset);392};393394// If the AT_name of any parent fails to match the expected name, we don't395// have a match.396for (auto [parent_name, parent_entry] :397llvm::zip_equal(parent_names, parent_entries))398if (!SameAsEntryATName(parent_name, parent_entry))399return false;400return true;401}402403void DebugNamesDWARFIndex::GetTypes(404ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {405for (const DebugNames::Entry &entry :406m_debug_names_up->equal_range(name.GetStringRef())) {407if (isType(entry.tag())) {408if (!ProcessEntry(entry, callback))409return;410}411}412413m_fallback.GetTypes(name, callback);414}415416void DebugNamesDWARFIndex::GetTypes(417const DWARFDeclContext &context,418llvm::function_ref<bool(DWARFDIE die)> callback) {419auto name = context[0].name;420for (const DebugNames::Entry &entry : m_debug_names_up->equal_range(name)) {421if (entry.tag() == context[0].tag) {422if (!ProcessEntry(entry, callback))423return;424}425}426427m_fallback.GetTypes(context, callback);428}429430void DebugNamesDWARFIndex::GetNamespaces(431ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) {432for (const DebugNames::Entry &entry :433m_debug_names_up->equal_range(name.GetStringRef())) {434lldb_private::dwarf::Tag entry_tag = entry.tag();435if (entry_tag == DW_TAG_namespace ||436entry_tag == DW_TAG_imported_declaration) {437if (!ProcessEntry(entry, callback))438return;439}440}441442m_fallback.GetNamespaces(name, callback);443}444445void DebugNamesDWARFIndex::GetFunctions(446const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf,447const CompilerDeclContext &parent_decl_ctx,448llvm::function_ref<bool(DWARFDIE die)> callback) {449ConstString name = lookup_info.GetLookupName();450std::set<DWARFDebugInfoEntry *> seen;451for (const DebugNames::Entry &entry :452m_debug_names_up->equal_range(name.GetStringRef())) {453Tag tag = entry.tag();454if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine)455continue;456457if (DWARFDIE die = GetDIE(entry)) {458if (!ProcessFunctionDIE(lookup_info, die, parent_decl_ctx,459[&](DWARFDIE die) {460if (!seen.insert(die.GetDIE()).second)461return true;462return callback(die);463}))464return;465}466}467468m_fallback.GetFunctions(lookup_info, dwarf, parent_decl_ctx, callback);469}470471void DebugNamesDWARFIndex::GetFunctions(472const RegularExpression ®ex,473llvm::function_ref<bool(DWARFDIE die)> callback) {474for (const DebugNames::NameIndex &ni: *m_debug_names_up) {475for (DebugNames::NameTableEntry nte: ni) {476if (!regex.Execute(nte.getString()))477continue;478479uint64_t entry_offset = nte.getEntryOffset();480llvm::Expected<DebugNames::Entry> entry_or = ni.getEntry(&entry_offset);481for (; entry_or; entry_or = ni.getEntry(&entry_offset)) {482Tag tag = entry_or->tag();483if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine)484continue;485486if (!ProcessEntry(*entry_or, callback))487return;488}489MaybeLogLookupError(entry_or.takeError(), ni, nte.getString());490}491}492493m_fallback.GetFunctions(regex, callback);494}495496void DebugNamesDWARFIndex::Dump(Stream &s) {497m_fallback.Dump(s);498499std::string data;500llvm::raw_string_ostream os(data);501m_debug_names_up->dump(os);502s.PutCString(os.str());503}504505506