My previous change didn't fix the Windows bot. This patch is an attempt to make guessing the path style more robust by first looking at the compile dir and falling back to the actual file if that's unsuccessful. llvm-svn: 368772
3826 lines
137 KiB
C++
3826 lines
137 KiB
C++
//===-- SymbolFileDWARF.cpp ------------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "SymbolFileDWARF.h"
|
|
|
|
#include "llvm/Support/Casting.h"
|
|
#include "llvm/Support/Threading.h"
|
|
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Core/ModuleList.h"
|
|
#include "lldb/Core/ModuleSpec.h"
|
|
#include "lldb/Core/PluginManager.h"
|
|
#include "lldb/Core/Section.h"
|
|
#include "lldb/Core/StreamFile.h"
|
|
#include "lldb/Core/Value.h"
|
|
#include "lldb/Utility/ArchSpec.h"
|
|
#include "lldb/Utility/RegularExpression.h"
|
|
#include "lldb/Utility/Scalar.h"
|
|
#include "lldb/Utility/StreamString.h"
|
|
#include "lldb/Utility/Timer.h"
|
|
|
|
#include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h"
|
|
#include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h"
|
|
|
|
#include "lldb/Host/FileSystem.h"
|
|
#include "lldb/Host/Host.h"
|
|
|
|
#include "lldb/Interpreter/OptionValueFileSpecList.h"
|
|
#include "lldb/Interpreter/OptionValueProperties.h"
|
|
|
|
#include "lldb/Symbol/Block.h"
|
|
#include "lldb/Symbol/ClangASTContext.h"
|
|
#include "lldb/Symbol/ClangUtil.h"
|
|
#include "lldb/Symbol/CompileUnit.h"
|
|
#include "lldb/Symbol/CompilerDecl.h"
|
|
#include "lldb/Symbol/CompilerDeclContext.h"
|
|
#include "lldb/Symbol/DebugMacros.h"
|
|
#include "lldb/Symbol/LineTable.h"
|
|
#include "lldb/Symbol/LocateSymbolFile.h"
|
|
#include "lldb/Symbol/ObjectFile.h"
|
|
#include "lldb/Symbol/SymbolFile.h"
|
|
#include "lldb/Symbol/TypeMap.h"
|
|
#include "lldb/Symbol/TypeSystem.h"
|
|
#include "lldb/Symbol/VariableList.h"
|
|
|
|
#include "lldb/Target/Language.h"
|
|
#include "lldb/Target/Target.h"
|
|
|
|
#include "AppleDWARFIndex.h"
|
|
#include "DWARFASTParser.h"
|
|
#include "DWARFASTParserClang.h"
|
|
#include "DWARFCompileUnit.h"
|
|
#include "DWARFDebugAbbrev.h"
|
|
#include "DWARFDebugAranges.h"
|
|
#include "DWARFDebugInfo.h"
|
|
#include "DWARFDebugMacro.h"
|
|
#include "DWARFDebugRanges.h"
|
|
#include "DWARFDeclContext.h"
|
|
#include "DWARFFormValue.h"
|
|
#include "DWARFTypeUnit.h"
|
|
#include "DWARFUnit.h"
|
|
#include "DebugNamesDWARFIndex.h"
|
|
#include "LogChannelDWARF.h"
|
|
#include "ManualDWARFIndex.h"
|
|
#include "SymbolFileDWARFDebugMap.h"
|
|
#include "SymbolFileDWARFDwo.h"
|
|
#include "SymbolFileDWARFDwp.h"
|
|
|
|
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
|
|
#include <algorithm>
|
|
#include <map>
|
|
#include <memory>
|
|
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
|
|
//#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN
|
|
|
|
#ifdef ENABLE_DEBUG_PRINTF
|
|
#include <stdio.h>
|
|
#define DEBUG_PRINTF(fmt, ...) printf(fmt, __VA_ARGS__)
|
|
#else
|
|
#define DEBUG_PRINTF(fmt, ...)
|
|
#endif
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
// static inline bool
|
|
// child_requires_parent_class_union_or_struct_to_be_completed (dw_tag_t tag)
|
|
//{
|
|
// switch (tag)
|
|
// {
|
|
// default:
|
|
// break;
|
|
// case DW_TAG_subprogram:
|
|
// case DW_TAG_inlined_subroutine:
|
|
// case DW_TAG_class_type:
|
|
// case DW_TAG_structure_type:
|
|
// case DW_TAG_union_type:
|
|
// return true;
|
|
// }
|
|
// return false;
|
|
//}
|
|
//
|
|
|
|
namespace {
|
|
|
|
#define LLDB_PROPERTIES_symbolfiledwarf
|
|
#include "SymbolFileDWARFProperties.inc"
|
|
|
|
enum {
|
|
#define LLDB_PROPERTIES_symbolfiledwarf
|
|
#include "SymbolFileDWARFPropertiesEnum.inc"
|
|
};
|
|
|
|
class PluginProperties : public Properties {
|
|
public:
|
|
static ConstString GetSettingName() {
|
|
return SymbolFileDWARF::GetPluginNameStatic();
|
|
}
|
|
|
|
PluginProperties() {
|
|
m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
|
|
m_collection_sp->Initialize(g_symbolfiledwarf_properties);
|
|
}
|
|
|
|
FileSpecList GetSymLinkPaths() {
|
|
const OptionValueFileSpecList *option_value =
|
|
m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList(
|
|
nullptr, true, ePropertySymLinkPaths);
|
|
assert(option_value);
|
|
return option_value->GetCurrentValue();
|
|
}
|
|
|
|
bool IgnoreFileIndexes() const {
|
|
return m_collection_sp->GetPropertyAtIndexAsBoolean(
|
|
nullptr, ePropertyIgnoreIndexes, false);
|
|
}
|
|
};
|
|
|
|
typedef std::shared_ptr<PluginProperties> SymbolFileDWARFPropertiesSP;
|
|
|
|
static const SymbolFileDWARFPropertiesSP &GetGlobalPluginProperties() {
|
|
static const auto g_settings_sp(std::make_shared<PluginProperties>());
|
|
return g_settings_sp;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
static const llvm::DWARFDebugLine::LineTable *
|
|
ParseLLVMLineTable(lldb_private::DWARFContext &context,
|
|
llvm::DWARFDebugLine &line, dw_offset_t line_offset,
|
|
dw_offset_t unit_offset) {
|
|
Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
|
|
|
|
llvm::DWARFDataExtractor data = context.getOrLoadLineData().GetAsLLVM();
|
|
llvm::DWARFContext &ctx = context.GetAsLLVM();
|
|
llvm::Expected<const llvm::DWARFDebugLine::LineTable *> line_table =
|
|
line.getOrParseLineTable(
|
|
data, line_offset, ctx, nullptr, [&](llvm::Error e) {
|
|
LLDB_LOG_ERROR(log, std::move(e),
|
|
"SymbolFileDWARF::ParseLineTable failed to parse");
|
|
});
|
|
|
|
if (!line_table) {
|
|
LLDB_LOG_ERROR(log, line_table.takeError(),
|
|
"SymbolFileDWARF::ParseLineTable failed to parse");
|
|
return nullptr;
|
|
}
|
|
return *line_table;
|
|
}
|
|
|
|
static FileSpecList
|
|
ParseSupportFilesFromPrologue(const lldb::ModuleSP &module,
|
|
const llvm::DWARFDebugLine::Prologue &prologue,
|
|
llvm::StringRef compile_dir = {},
|
|
FileSpec first_file = {}) {
|
|
FileSpecList support_files;
|
|
support_files.Append(first_file);
|
|
|
|
llvm::Optional<FileSpec::Style> compile_dir_style =
|
|
FileSpec::GuessPathStyle(compile_dir);
|
|
const size_t number_of_files = prologue.FileNames.size();
|
|
for (size_t idx = 1; idx <= number_of_files; ++idx) {
|
|
std::string original_file;
|
|
if (!prologue.getFileNameByIndex(
|
|
idx, compile_dir,
|
|
llvm::DILineInfoSpecifier::FileLineInfoKind::Default,
|
|
original_file)) {
|
|
// Always add an entry so the indexes remain correct.
|
|
support_files.EmplaceBack();
|
|
continue;
|
|
}
|
|
|
|
FileSpec::Style style = FileSpec::Style::native;
|
|
if (compile_dir_style) {
|
|
style = *compile_dir_style;
|
|
} else if (llvm::Optional<FileSpec::Style> file_style =
|
|
FileSpec::GuessPathStyle(original_file)) {
|
|
style = *file_style;
|
|
}
|
|
|
|
std::string remapped_file;
|
|
if (!prologue.getFileNameByIndex(
|
|
idx, compile_dir,
|
|
llvm::DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
|
|
remapped_file)) {
|
|
// Always add an entry so the indexes remain correct.
|
|
support_files.EmplaceBack(original_file, style);
|
|
continue;
|
|
}
|
|
|
|
module->RemapSourceFile(llvm::StringRef(original_file), remapped_file);
|
|
support_files.EmplaceBack(remapped_file, style);
|
|
}
|
|
|
|
return support_files;
|
|
}
|
|
|
|
FileSpecList SymbolFileDWARF::GetSymlinkPaths() {
|
|
return GetGlobalPluginProperties()->GetSymLinkPaths();
|
|
}
|
|
|
|
void SymbolFileDWARF::Initialize() {
|
|
LogChannelDWARF::Initialize();
|
|
PluginManager::RegisterPlugin(GetPluginNameStatic(),
|
|
GetPluginDescriptionStatic(), CreateInstance,
|
|
DebuggerInitialize);
|
|
}
|
|
|
|
void SymbolFileDWARF::DebuggerInitialize(Debugger &debugger) {
|
|
if (!PluginManager::GetSettingForSymbolFilePlugin(
|
|
debugger, PluginProperties::GetSettingName())) {
|
|
const bool is_global_setting = true;
|
|
PluginManager::CreateSettingForSymbolFilePlugin(
|
|
debugger, GetGlobalPluginProperties()->GetValueProperties(),
|
|
ConstString("Properties for the dwarf symbol-file plug-in."),
|
|
is_global_setting);
|
|
}
|
|
}
|
|
|
|
void SymbolFileDWARF::Terminate() {
|
|
PluginManager::UnregisterPlugin(CreateInstance);
|
|
LogChannelDWARF::Terminate();
|
|
}
|
|
|
|
lldb_private::ConstString SymbolFileDWARF::GetPluginNameStatic() {
|
|
static ConstString g_name("dwarf");
|
|
return g_name;
|
|
}
|
|
|
|
const char *SymbolFileDWARF::GetPluginDescriptionStatic() {
|
|
return "DWARF and DWARF3 debug symbol file reader.";
|
|
}
|
|
|
|
SymbolFile *SymbolFileDWARF::CreateInstance(ObjectFileSP objfile_sp) {
|
|
return new SymbolFileDWARF(std::move(objfile_sp),
|
|
/*dwo_section_list*/ nullptr);
|
|
}
|
|
|
|
TypeList &SymbolFileDWARF::GetTypeList() {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
if (SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile())
|
|
return debug_map_symfile->GetTypeList();
|
|
return SymbolFile::GetTypeList();
|
|
}
|
|
void SymbolFileDWARF::GetTypes(const DWARFDIE &die, dw_offset_t min_die_offset,
|
|
dw_offset_t max_die_offset, uint32_t type_mask,
|
|
TypeSet &type_set) {
|
|
if (die) {
|
|
const dw_offset_t die_offset = die.GetOffset();
|
|
|
|
if (die_offset >= max_die_offset)
|
|
return;
|
|
|
|
if (die_offset >= min_die_offset) {
|
|
const dw_tag_t tag = die.Tag();
|
|
|
|
bool add_type = false;
|
|
|
|
switch (tag) {
|
|
case DW_TAG_array_type:
|
|
add_type = (type_mask & eTypeClassArray) != 0;
|
|
break;
|
|
case DW_TAG_unspecified_type:
|
|
case DW_TAG_base_type:
|
|
add_type = (type_mask & eTypeClassBuiltin) != 0;
|
|
break;
|
|
case DW_TAG_class_type:
|
|
add_type = (type_mask & eTypeClassClass) != 0;
|
|
break;
|
|
case DW_TAG_structure_type:
|
|
add_type = (type_mask & eTypeClassStruct) != 0;
|
|
break;
|
|
case DW_TAG_union_type:
|
|
add_type = (type_mask & eTypeClassUnion) != 0;
|
|
break;
|
|
case DW_TAG_enumeration_type:
|
|
add_type = (type_mask & eTypeClassEnumeration) != 0;
|
|
break;
|
|
case DW_TAG_subroutine_type:
|
|
case DW_TAG_subprogram:
|
|
case DW_TAG_inlined_subroutine:
|
|
add_type = (type_mask & eTypeClassFunction) != 0;
|
|
break;
|
|
case DW_TAG_pointer_type:
|
|
add_type = (type_mask & eTypeClassPointer) != 0;
|
|
break;
|
|
case DW_TAG_rvalue_reference_type:
|
|
case DW_TAG_reference_type:
|
|
add_type = (type_mask & eTypeClassReference) != 0;
|
|
break;
|
|
case DW_TAG_typedef:
|
|
add_type = (type_mask & eTypeClassTypedef) != 0;
|
|
break;
|
|
case DW_TAG_ptr_to_member_type:
|
|
add_type = (type_mask & eTypeClassMemberPointer) != 0;
|
|
break;
|
|
}
|
|
|
|
if (add_type) {
|
|
const bool assert_not_being_parsed = true;
|
|
Type *type = ResolveTypeUID(die, assert_not_being_parsed);
|
|
if (type) {
|
|
if (type_set.find(type) == type_set.end())
|
|
type_set.insert(type);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (DWARFDIE child_die = die.GetFirstChild(); child_die.IsValid();
|
|
child_die = child_die.GetSibling()) {
|
|
GetTypes(child_die, min_die_offset, max_die_offset, type_mask, type_set);
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t SymbolFileDWARF::GetTypes(SymbolContextScope *sc_scope,
|
|
TypeClass type_mask, TypeList &type_list)
|
|
|
|
{
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
TypeSet type_set;
|
|
|
|
CompileUnit *comp_unit = nullptr;
|
|
DWARFUnit *dwarf_cu = nullptr;
|
|
if (sc_scope)
|
|
comp_unit = sc_scope->CalculateSymbolContextCompileUnit();
|
|
|
|
if (comp_unit) {
|
|
dwarf_cu = GetDWARFCompileUnit(comp_unit);
|
|
if (dwarf_cu == nullptr)
|
|
return 0;
|
|
GetTypes(dwarf_cu->DIE(), dwarf_cu->GetOffset(),
|
|
dwarf_cu->GetNextUnitOffset(), type_mask, type_set);
|
|
} else {
|
|
DWARFDebugInfo *info = DebugInfo();
|
|
if (info) {
|
|
const size_t num_cus = info->GetNumUnits();
|
|
for (size_t cu_idx = 0; cu_idx < num_cus; ++cu_idx) {
|
|
dwarf_cu = info->GetUnitAtIndex(cu_idx);
|
|
if (dwarf_cu) {
|
|
GetTypes(dwarf_cu->DIE(), 0, UINT32_MAX, type_mask, type_set);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::set<CompilerType> compiler_type_set;
|
|
size_t num_types_added = 0;
|
|
for (Type *type : type_set) {
|
|
CompilerType compiler_type = type->GetForwardCompilerType();
|
|
if (compiler_type_set.find(compiler_type) == compiler_type_set.end()) {
|
|
compiler_type_set.insert(compiler_type);
|
|
type_list.Insert(type->shared_from_this());
|
|
++num_types_added;
|
|
}
|
|
}
|
|
return num_types_added;
|
|
}
|
|
|
|
// Gets the first parent that is a lexical block, function or inlined
|
|
// subroutine, or compile unit.
|
|
DWARFDIE
|
|
SymbolFileDWARF::GetParentSymbolContextDIE(const DWARFDIE &child_die) {
|
|
DWARFDIE die;
|
|
for (die = child_die.GetParent(); die; die = die.GetParent()) {
|
|
dw_tag_t tag = die.Tag();
|
|
|
|
switch (tag) {
|
|
case DW_TAG_compile_unit:
|
|
case DW_TAG_partial_unit:
|
|
case DW_TAG_subprogram:
|
|
case DW_TAG_inlined_subroutine:
|
|
case DW_TAG_lexical_block:
|
|
return die;
|
|
}
|
|
}
|
|
return DWARFDIE();
|
|
}
|
|
|
|
SymbolFileDWARF::SymbolFileDWARF(ObjectFileSP objfile_sp,
|
|
SectionList *dwo_section_list)
|
|
: SymbolFile(std::move(objfile_sp)),
|
|
UserID(0x7fffffff00000000), // Used by SymbolFileDWARFDebugMap to
|
|
// when this class parses .o files to
|
|
// contain the .o file index/ID
|
|
m_debug_map_module_wp(), m_debug_map_symfile(nullptr),
|
|
m_context(m_objfile_sp->GetModule()->GetSectionList(), dwo_section_list),
|
|
m_data_debug_loc(), m_abbr(), m_info(), m_fetched_external_modules(false),
|
|
m_supports_DW_AT_APPLE_objc_complete_type(eLazyBoolCalculate),
|
|
m_unique_ast_type_map() {}
|
|
|
|
SymbolFileDWARF::~SymbolFileDWARF() {}
|
|
|
|
static ConstString GetDWARFMachOSegmentName() {
|
|
static ConstString g_dwarf_section_name("__DWARF");
|
|
return g_dwarf_section_name;
|
|
}
|
|
|
|
UniqueDWARFASTTypeMap &SymbolFileDWARF::GetUniqueDWARFASTTypeMap() {
|
|
SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
|
|
if (debug_map_symfile)
|
|
return debug_map_symfile->GetUniqueDWARFASTTypeMap();
|
|
else
|
|
return m_unique_ast_type_map;
|
|
}
|
|
|
|
llvm::Expected<TypeSystem &>
|
|
SymbolFileDWARF::GetTypeSystemForLanguage(LanguageType language) {
|
|
if (SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile())
|
|
return debug_map_symfile->GetTypeSystemForLanguage(language);
|
|
|
|
auto type_system_or_err =
|
|
m_objfile_sp->GetModule()->GetTypeSystemForLanguage(language);
|
|
if (type_system_or_err) {
|
|
type_system_or_err->SetSymbolFile(this);
|
|
}
|
|
return type_system_or_err;
|
|
}
|
|
|
|
void SymbolFileDWARF::InitializeObject() {
|
|
Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
|
|
|
|
if (!GetGlobalPluginProperties()->IgnoreFileIndexes()) {
|
|
DWARFDataExtractor apple_names, apple_namespaces, apple_types, apple_objc;
|
|
LoadSectionData(eSectionTypeDWARFAppleNames, apple_names);
|
|
LoadSectionData(eSectionTypeDWARFAppleNamespaces, apple_namespaces);
|
|
LoadSectionData(eSectionTypeDWARFAppleTypes, apple_types);
|
|
LoadSectionData(eSectionTypeDWARFAppleObjC, apple_objc);
|
|
|
|
m_index = AppleDWARFIndex::Create(
|
|
*GetObjectFile()->GetModule(), apple_names, apple_namespaces,
|
|
apple_types, apple_objc, m_context.getOrLoadStrData());
|
|
|
|
if (m_index)
|
|
return;
|
|
|
|
DWARFDataExtractor debug_names;
|
|
LoadSectionData(eSectionTypeDWARFDebugNames, debug_names);
|
|
if (debug_names.GetByteSize() > 0) {
|
|
llvm::Expected<std::unique_ptr<DebugNamesDWARFIndex>> index_or =
|
|
DebugNamesDWARFIndex::Create(
|
|
*GetObjectFile()->GetModule(), debug_names,
|
|
m_context.getOrLoadStrData(), DebugInfo());
|
|
if (index_or) {
|
|
m_index = std::move(*index_or);
|
|
return;
|
|
}
|
|
LLDB_LOG_ERROR(log, index_or.takeError(),
|
|
"Unable to read .debug_names data: {0}");
|
|
}
|
|
}
|
|
|
|
m_index = llvm::make_unique<ManualDWARFIndex>(*GetObjectFile()->GetModule(),
|
|
DebugInfo());
|
|
}
|
|
|
|
bool SymbolFileDWARF::SupportedVersion(uint16_t version) {
|
|
return version >= 2 && version <= 5;
|
|
}
|
|
|
|
uint32_t SymbolFileDWARF::CalculateAbilities() {
|
|
uint32_t abilities = 0;
|
|
if (m_objfile_sp != nullptr) {
|
|
const Section *section = nullptr;
|
|
const SectionList *section_list = m_objfile_sp->GetSectionList();
|
|
if (section_list == nullptr)
|
|
return 0;
|
|
|
|
uint64_t debug_abbrev_file_size = 0;
|
|
uint64_t debug_info_file_size = 0;
|
|
uint64_t debug_line_file_size = 0;
|
|
|
|
section = section_list->FindSectionByName(GetDWARFMachOSegmentName()).get();
|
|
|
|
if (section)
|
|
section_list = §ion->GetChildren();
|
|
|
|
section =
|
|
section_list->FindSectionByType(eSectionTypeDWARFDebugInfo, true).get();
|
|
if (section != nullptr) {
|
|
debug_info_file_size = section->GetFileSize();
|
|
|
|
section =
|
|
section_list->FindSectionByType(eSectionTypeDWARFDebugAbbrev, true)
|
|
.get();
|
|
if (section)
|
|
debug_abbrev_file_size = section->GetFileSize();
|
|
|
|
DWARFDebugAbbrev *abbrev = DebugAbbrev();
|
|
if (abbrev) {
|
|
std::set<dw_form_t> invalid_forms;
|
|
abbrev->GetUnsupportedForms(invalid_forms);
|
|
if (!invalid_forms.empty()) {
|
|
StreamString error;
|
|
error.Printf("unsupported DW_FORM value%s:",
|
|
invalid_forms.size() > 1 ? "s" : "");
|
|
for (auto form : invalid_forms)
|
|
error.Printf(" %#x", form);
|
|
m_objfile_sp->GetModule()->ReportWarning(
|
|
"%s", error.GetString().str().c_str());
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
section =
|
|
section_list->FindSectionByType(eSectionTypeDWARFDebugLine, true)
|
|
.get();
|
|
if (section)
|
|
debug_line_file_size = section->GetFileSize();
|
|
} else {
|
|
const char *symfile_dir_cstr =
|
|
m_objfile_sp->GetFileSpec().GetDirectory().GetCString();
|
|
if (symfile_dir_cstr) {
|
|
if (strcasestr(symfile_dir_cstr, ".dsym")) {
|
|
if (m_objfile_sp->GetType() == ObjectFile::eTypeDebugInfo) {
|
|
// We have a dSYM file that didn't have a any debug info. If the
|
|
// string table has a size of 1, then it was made from an
|
|
// executable with no debug info, or from an executable that was
|
|
// stripped.
|
|
section =
|
|
section_list->FindSectionByType(eSectionTypeDWARFDebugStr, true)
|
|
.get();
|
|
if (section && section->GetFileSize() == 1) {
|
|
m_objfile_sp->GetModule()->ReportWarning(
|
|
"empty dSYM file detected, dSYM was created with an "
|
|
"executable with no debug info.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (debug_abbrev_file_size > 0 && debug_info_file_size > 0)
|
|
abilities |= CompileUnits | Functions | Blocks | GlobalVariables |
|
|
LocalVariables | VariableTypes;
|
|
|
|
if (debug_line_file_size > 0)
|
|
abilities |= LineTables;
|
|
}
|
|
return abilities;
|
|
}
|
|
|
|
const DWARFDataExtractor &
|
|
SymbolFileDWARF::GetCachedSectionData(lldb::SectionType sect_type,
|
|
DWARFDataSegment &data_segment) {
|
|
llvm::call_once(data_segment.m_flag, [this, sect_type, &data_segment] {
|
|
this->LoadSectionData(sect_type, std::ref(data_segment.m_data));
|
|
});
|
|
return data_segment.m_data;
|
|
}
|
|
|
|
void SymbolFileDWARF::LoadSectionData(lldb::SectionType sect_type,
|
|
DWARFDataExtractor &data) {
|
|
ModuleSP module_sp(m_objfile_sp->GetModule());
|
|
const SectionList *section_list = module_sp->GetSectionList();
|
|
if (!section_list)
|
|
return;
|
|
|
|
SectionSP section_sp(section_list->FindSectionByType(sect_type, true));
|
|
if (!section_sp)
|
|
return;
|
|
|
|
data.Clear();
|
|
m_objfile_sp->ReadSectionData(section_sp.get(), data);
|
|
}
|
|
|
|
const DWARFDataExtractor &SymbolFileDWARF::DebugLocData() {
|
|
const DWARFDataExtractor &debugLocData = get_debug_loc_data();
|
|
if (debugLocData.GetByteSize() > 0)
|
|
return debugLocData;
|
|
return get_debug_loclists_data();
|
|
}
|
|
|
|
const DWARFDataExtractor &SymbolFileDWARF::get_debug_loc_data() {
|
|
return GetCachedSectionData(eSectionTypeDWARFDebugLoc, m_data_debug_loc);
|
|
}
|
|
|
|
const DWARFDataExtractor &SymbolFileDWARF::get_debug_loclists_data() {
|
|
return GetCachedSectionData(eSectionTypeDWARFDebugLocLists,
|
|
m_data_debug_loclists);
|
|
}
|
|
|
|
DWARFDebugAbbrev *SymbolFileDWARF::DebugAbbrev() {
|
|
if (m_abbr)
|
|
return m_abbr.get();
|
|
|
|
const DWARFDataExtractor &debug_abbrev_data = m_context.getOrLoadAbbrevData();
|
|
if (debug_abbrev_data.GetByteSize() == 0)
|
|
return nullptr;
|
|
|
|
auto abbr = llvm::make_unique<DWARFDebugAbbrev>();
|
|
llvm::Error error = abbr->parse(debug_abbrev_data);
|
|
if (error) {
|
|
Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
|
|
LLDB_LOG_ERROR(log, std::move(error),
|
|
"Unable to read .debug_abbrev section: {0}");
|
|
return nullptr;
|
|
}
|
|
|
|
m_abbr = std::move(abbr);
|
|
return m_abbr.get();
|
|
}
|
|
|
|
const DWARFDebugAbbrev *SymbolFileDWARF::DebugAbbrev() const {
|
|
return m_abbr.get();
|
|
}
|
|
|
|
DWARFDebugInfo *SymbolFileDWARF::DebugInfo() {
|
|
if (m_info == nullptr) {
|
|
static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
|
|
Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION,
|
|
static_cast<void *>(this));
|
|
if (m_context.getOrLoadDebugInfoData().GetByteSize() > 0)
|
|
m_info = llvm::make_unique<DWARFDebugInfo>(*this, m_context);
|
|
}
|
|
return m_info.get();
|
|
}
|
|
|
|
const DWARFDebugInfo *SymbolFileDWARF::DebugInfo() const {
|
|
return m_info.get();
|
|
}
|
|
|
|
DWARFUnit *
|
|
SymbolFileDWARF::GetDWARFCompileUnit(lldb_private::CompileUnit *comp_unit) {
|
|
if (!comp_unit)
|
|
return nullptr;
|
|
|
|
DWARFDebugInfo *info = DebugInfo();
|
|
if (info) {
|
|
// The compile unit ID is the index of the DWARF unit.
|
|
DWARFUnit *dwarf_cu = info->GetUnitAtIndex(comp_unit->GetID());
|
|
if (dwarf_cu && dwarf_cu->GetUserData() == nullptr)
|
|
dwarf_cu->SetUserData(comp_unit);
|
|
return dwarf_cu;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
DWARFDebugRanges *SymbolFileDWARF::GetDebugRanges() {
|
|
if (!m_ranges) {
|
|
static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
|
|
Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION,
|
|
static_cast<void *>(this));
|
|
|
|
if (m_context.getOrLoadRangesData().GetByteSize() > 0)
|
|
m_ranges.reset(new DWARFDebugRanges());
|
|
|
|
if (m_ranges)
|
|
m_ranges->Extract(m_context);
|
|
}
|
|
return m_ranges.get();
|
|
}
|
|
|
|
DWARFDebugRngLists *SymbolFileDWARF::GetDebugRngLists() {
|
|
if (!m_rnglists) {
|
|
static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
|
|
Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION,
|
|
static_cast<void *>(this));
|
|
|
|
if (m_context.getOrLoadRngListsData().GetByteSize() > 0)
|
|
m_rnglists.reset(new DWARFDebugRngLists());
|
|
|
|
if (m_rnglists)
|
|
m_rnglists->Extract(m_context);
|
|
}
|
|
return m_rnglists.get();
|
|
}
|
|
|
|
lldb::CompUnitSP SymbolFileDWARF::ParseCompileUnit(DWARFCompileUnit &dwarf_cu) {
|
|
CompUnitSP cu_sp;
|
|
CompileUnit *comp_unit = (CompileUnit *)dwarf_cu.GetUserData();
|
|
if (comp_unit) {
|
|
// We already parsed this compile unit, had out a shared pointer to it
|
|
cu_sp = comp_unit->shared_from_this();
|
|
} else {
|
|
if (&dwarf_cu.GetSymbolFileDWARF() != this) {
|
|
return dwarf_cu.GetSymbolFileDWARF().ParseCompileUnit(dwarf_cu);
|
|
} else if (dwarf_cu.GetOffset() == 0 && GetDebugMapSymfile()) {
|
|
// Let the debug map create the compile unit
|
|
cu_sp = m_debug_map_symfile->GetCompileUnit(this);
|
|
dwarf_cu.SetUserData(cu_sp.get());
|
|
} else {
|
|
ModuleSP module_sp(m_objfile_sp->GetModule());
|
|
if (module_sp) {
|
|
const DWARFDIE cu_die = dwarf_cu.DIE();
|
|
if (cu_die) {
|
|
FileSpec cu_file_spec(cu_die.GetName(), dwarf_cu.GetPathStyle());
|
|
if (cu_file_spec) {
|
|
// If we have a full path to the compile unit, we don't need to
|
|
// resolve the file. This can be expensive e.g. when the source
|
|
// files are NFS mounted.
|
|
cu_file_spec.MakeAbsolute(dwarf_cu.GetCompilationDirectory());
|
|
|
|
std::string remapped_file;
|
|
if (module_sp->RemapSourceFile(cu_file_spec.GetPath(),
|
|
remapped_file))
|
|
cu_file_spec.SetFile(remapped_file, FileSpec::Style::native);
|
|
}
|
|
|
|
LanguageType cu_language = DWARFUnit::LanguageTypeFromDWARF(
|
|
cu_die.GetAttributeValueAsUnsigned(DW_AT_language, 0));
|
|
|
|
bool is_optimized = dwarf_cu.GetIsOptimized();
|
|
BuildCuTranslationTable();
|
|
cu_sp = std::make_shared<CompileUnit>(
|
|
module_sp, &dwarf_cu, cu_file_spec,
|
|
*GetDWARFUnitIndex(dwarf_cu.GetID()), cu_language,
|
|
is_optimized ? eLazyBoolYes : eLazyBoolNo);
|
|
|
|
dwarf_cu.SetUserData(cu_sp.get());
|
|
|
|
SetCompileUnitAtIndex(dwarf_cu.GetID(), cu_sp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return cu_sp;
|
|
}
|
|
|
|
void SymbolFileDWARF::BuildCuTranslationTable() {
|
|
if (!m_lldb_cu_to_dwarf_unit.empty())
|
|
return;
|
|
|
|
DWARFDebugInfo *info = DebugInfo();
|
|
if (!info)
|
|
return;
|
|
|
|
if (!info->ContainsTypeUnits()) {
|
|
// We can use a 1-to-1 mapping. No need to build a translation table.
|
|
return;
|
|
}
|
|
for (uint32_t i = 0, num = info->GetNumUnits(); i < num; ++i) {
|
|
if (auto *cu = llvm::dyn_cast<DWARFCompileUnit>(info->GetUnitAtIndex(i))) {
|
|
cu->SetID(m_lldb_cu_to_dwarf_unit.size());
|
|
m_lldb_cu_to_dwarf_unit.push_back(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
llvm::Optional<uint32_t> SymbolFileDWARF::GetDWARFUnitIndex(uint32_t cu_idx) {
|
|
BuildCuTranslationTable();
|
|
if (m_lldb_cu_to_dwarf_unit.empty())
|
|
return cu_idx;
|
|
if (cu_idx >= m_lldb_cu_to_dwarf_unit.size())
|
|
return llvm::None;
|
|
return m_lldb_cu_to_dwarf_unit[cu_idx];
|
|
}
|
|
|
|
uint32_t SymbolFileDWARF::CalculateNumCompileUnits() {
|
|
DWARFDebugInfo *info = DebugInfo();
|
|
if (!info)
|
|
return 0;
|
|
BuildCuTranslationTable();
|
|
return m_lldb_cu_to_dwarf_unit.empty() ? info->GetNumUnits()
|
|
: m_lldb_cu_to_dwarf_unit.size();
|
|
}
|
|
|
|
CompUnitSP SymbolFileDWARF::ParseCompileUnitAtIndex(uint32_t cu_idx) {
|
|
ASSERT_MODULE_LOCK(this);
|
|
DWARFDebugInfo *info = DebugInfo();
|
|
if (!info)
|
|
return {};
|
|
|
|
if (llvm::Optional<uint32_t> dwarf_idx = GetDWARFUnitIndex(cu_idx)) {
|
|
if (auto *dwarf_cu = llvm::cast_or_null<DWARFCompileUnit>(
|
|
info->GetUnitAtIndex(*dwarf_idx)))
|
|
return ParseCompileUnit(*dwarf_cu);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
Function *SymbolFileDWARF::ParseFunction(CompileUnit &comp_unit,
|
|
const DWARFDIE &die) {
|
|
ASSERT_MODULE_LOCK(this);
|
|
if (!die.IsValid())
|
|
return nullptr;
|
|
|
|
auto type_system_or_err =
|
|
GetTypeSystemForLanguage(die.GetCU()->GetLanguageType());
|
|
if (auto err = type_system_or_err.takeError()) {
|
|
LLDB_LOG_ERROR(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_SYMBOLS),
|
|
std::move(err), "Unable to parse function");
|
|
return nullptr;
|
|
}
|
|
DWARFASTParser *dwarf_ast = type_system_or_err->GetDWARFParser();
|
|
if (!dwarf_ast)
|
|
return nullptr;
|
|
|
|
return dwarf_ast->ParseFunctionFromDWARF(comp_unit, die);
|
|
}
|
|
|
|
bool SymbolFileDWARF::FixupAddress(Address &addr) {
|
|
SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
|
|
if (debug_map_symfile) {
|
|
return debug_map_symfile->LinkOSOAddress(addr);
|
|
}
|
|
// This is a normal DWARF file, no address fixups need to happen
|
|
return true;
|
|
}
|
|
lldb::LanguageType SymbolFileDWARF::ParseLanguage(CompileUnit &comp_unit) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
|
|
if (dwarf_cu)
|
|
return dwarf_cu->GetLanguageType();
|
|
else
|
|
return eLanguageTypeUnknown;
|
|
}
|
|
|
|
size_t SymbolFileDWARF::ParseFunctions(CompileUnit &comp_unit) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
|
|
if (!dwarf_cu)
|
|
return 0;
|
|
|
|
size_t functions_added = 0;
|
|
std::vector<DWARFDIE> function_dies;
|
|
dwarf_cu->AppendDIEsWithTag(DW_TAG_subprogram, function_dies);
|
|
for (const DWARFDIE &die : function_dies) {
|
|
if (comp_unit.FindFunctionByUID(die.GetID()))
|
|
continue;
|
|
if (ParseFunction(comp_unit, die))
|
|
++functions_added;
|
|
}
|
|
// FixupTypes();
|
|
return functions_added;
|
|
}
|
|
|
|
bool SymbolFileDWARF::ParseSupportFiles(CompileUnit &comp_unit,
|
|
FileSpecList &support_files) {
|
|
if (!comp_unit.GetLineTable())
|
|
ParseLineTable(comp_unit);
|
|
return true;
|
|
}
|
|
|
|
FileSpec SymbolFileDWARF::GetFile(DWARFUnit &unit, size_t file_idx) {
|
|
if (auto *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(&unit)) {
|
|
if (CompileUnit *lldb_cu = GetCompUnitForDWARFCompUnit(*dwarf_cu))
|
|
return lldb_cu->GetSupportFiles().GetFileSpecAtIndex(file_idx);
|
|
return FileSpec();
|
|
}
|
|
|
|
auto &tu = llvm::cast<DWARFTypeUnit>(unit);
|
|
return GetTypeUnitSupportFiles(tu).GetFileSpecAtIndex(file_idx);
|
|
}
|
|
|
|
const FileSpecList &
|
|
SymbolFileDWARF::GetTypeUnitSupportFiles(DWARFTypeUnit &tu) {
|
|
static FileSpecList empty_list;
|
|
|
|
dw_offset_t offset = tu.GetLineTableOffset();
|
|
if (offset == DW_INVALID_OFFSET ||
|
|
offset == llvm::DenseMapInfo<dw_offset_t>::getEmptyKey() ||
|
|
offset == llvm::DenseMapInfo<dw_offset_t>::getTombstoneKey())
|
|
return empty_list;
|
|
|
|
// Many type units can share a line table, so parse the support file list
|
|
// once, and cache it based on the offset field.
|
|
auto iter_bool = m_type_unit_support_files.try_emplace(offset);
|
|
FileSpecList &list = iter_bool.first->second;
|
|
if (iter_bool.second) {
|
|
uint64_t line_table_offset = offset;
|
|
llvm::DWARFDataExtractor data = m_context.getOrLoadLineData().GetAsLLVM();
|
|
llvm::DWARFContext &ctx = m_context.GetAsLLVM();
|
|
llvm::DWARFDebugLine::Prologue prologue;
|
|
llvm::Error error = prologue.parse(data, &line_table_offset, ctx);
|
|
if (error) {
|
|
Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
|
|
LLDB_LOG_ERROR(log, std::move(error),
|
|
"SymbolFileDWARF::GetTypeUnitSupportFiles failed to parse "
|
|
"the line table prologue");
|
|
} else {
|
|
list =
|
|
ParseSupportFilesFromPrologue(GetObjectFile()->GetModule(), prologue);
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
bool SymbolFileDWARF::ParseIsOptimized(CompileUnit &comp_unit) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
|
|
if (dwarf_cu)
|
|
return dwarf_cu->GetIsOptimized();
|
|
return false;
|
|
}
|
|
|
|
bool SymbolFileDWARF::ParseImportedModules(
|
|
const lldb_private::SymbolContext &sc,
|
|
std::vector<SourceModule> &imported_modules) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
assert(sc.comp_unit);
|
|
DWARFUnit *dwarf_cu = GetDWARFCompileUnit(sc.comp_unit);
|
|
if (!dwarf_cu)
|
|
return false;
|
|
if (!ClangModulesDeclVendor::LanguageSupportsClangModules(
|
|
sc.comp_unit->GetLanguage()))
|
|
return false;
|
|
UpdateExternalModuleListIfNeeded();
|
|
|
|
const DWARFDIE die = dwarf_cu->DIE();
|
|
if (!die)
|
|
return false;
|
|
|
|
for (DWARFDIE child_die = die.GetFirstChild(); child_die;
|
|
child_die = child_die.GetSibling()) {
|
|
if (child_die.Tag() != DW_TAG_imported_declaration)
|
|
continue;
|
|
|
|
DWARFDIE module_die = child_die.GetReferencedDIE(DW_AT_import);
|
|
if (module_die.Tag() != DW_TAG_module)
|
|
continue;
|
|
|
|
if (const char *name =
|
|
module_die.GetAttributeValueAsString(DW_AT_name, nullptr)) {
|
|
SourceModule module;
|
|
module.path.push_back(ConstString(name));
|
|
|
|
DWARFDIE parent_die = module_die;
|
|
while ((parent_die = parent_die.GetParent())) {
|
|
if (parent_die.Tag() != DW_TAG_module)
|
|
break;
|
|
if (const char *name =
|
|
parent_die.GetAttributeValueAsString(DW_AT_name, nullptr))
|
|
module.path.push_back(ConstString(name));
|
|
}
|
|
std::reverse(module.path.begin(), module.path.end());
|
|
if (const char *include_path = module_die.GetAttributeValueAsString(
|
|
DW_AT_LLVM_include_path, nullptr))
|
|
module.search_path = ConstString(include_path);
|
|
if (const char *sysroot = module_die.GetAttributeValueAsString(
|
|
DW_AT_LLVM_isysroot, nullptr))
|
|
module.sysroot = ConstString(sysroot);
|
|
imported_modules.push_back(module);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SymbolFileDWARF::ParseLineTable(CompileUnit &comp_unit) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
if (comp_unit.GetLineTable() != nullptr)
|
|
return true;
|
|
|
|
DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
|
|
if (!dwarf_cu)
|
|
return false;
|
|
|
|
const DWARFBaseDIE dwarf_cu_die = dwarf_cu->GetUnitDIEOnly();
|
|
if (!dwarf_cu_die)
|
|
return false;
|
|
|
|
const dw_offset_t cu_line_offset = dwarf_cu_die.GetAttributeValueAsUnsigned(
|
|
DW_AT_stmt_list, DW_INVALID_OFFSET);
|
|
if (cu_line_offset == DW_INVALID_OFFSET)
|
|
return false;
|
|
|
|
llvm::DWARFDebugLine line;
|
|
const llvm::DWARFDebugLine::LineTable *line_table = ParseLLVMLineTable(
|
|
m_context, line, cu_line_offset, dwarf_cu->GetOffset());
|
|
|
|
if (!line_table)
|
|
return false;
|
|
|
|
// FIXME: Rather than parsing the whole line table and then copying it over
|
|
// into LLDB, we should explore using a callback to populate the line table
|
|
// while we parse to reduce memory usage.
|
|
std::unique_ptr<LineTable> line_table_up =
|
|
llvm::make_unique<LineTable>(&comp_unit);
|
|
LineSequence *sequence = line_table_up->CreateLineSequenceContainer();
|
|
for (auto &row : line_table->Rows) {
|
|
line_table_up->AppendLineEntryToSequence(
|
|
sequence, row.Address.Address, row.Line, row.Column, row.File,
|
|
row.IsStmt, row.BasicBlock, row.PrologueEnd, row.EpilogueBegin,
|
|
row.EndSequence);
|
|
if (row.EndSequence) {
|
|
line_table_up->InsertSequence(sequence);
|
|
sequence = line_table_up->CreateLineSequenceContainer();
|
|
}
|
|
}
|
|
|
|
if (SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile()) {
|
|
// We have an object file that has a line table with addresses that are not
|
|
// linked. We need to link the line table and convert the addresses that
|
|
// are relative to the .o file into addresses for the main executable.
|
|
comp_unit.SetLineTable(
|
|
debug_map_symfile->LinkOSOLineTable(this, line_table_up.get()));
|
|
} else {
|
|
comp_unit.SetLineTable(line_table_up.release());
|
|
}
|
|
|
|
comp_unit.SetSupportFiles(ParseSupportFilesFromPrologue(
|
|
comp_unit.GetModule(), line_table->Prologue,
|
|
dwarf_cu->GetCompilationDirectory().GetCString(), FileSpec(comp_unit)));
|
|
|
|
return true;
|
|
}
|
|
|
|
lldb_private::DebugMacrosSP
|
|
SymbolFileDWARF::ParseDebugMacros(lldb::offset_t *offset) {
|
|
auto iter = m_debug_macros_map.find(*offset);
|
|
if (iter != m_debug_macros_map.end())
|
|
return iter->second;
|
|
|
|
const DWARFDataExtractor &debug_macro_data = m_context.getOrLoadMacroData();
|
|
if (debug_macro_data.GetByteSize() == 0)
|
|
return DebugMacrosSP();
|
|
|
|
lldb_private::DebugMacrosSP debug_macros_sp(new lldb_private::DebugMacros());
|
|
m_debug_macros_map[*offset] = debug_macros_sp;
|
|
|
|
const DWARFDebugMacroHeader &header =
|
|
DWARFDebugMacroHeader::ParseHeader(debug_macro_data, offset);
|
|
DWARFDebugMacroEntry::ReadMacroEntries(
|
|
debug_macro_data, m_context.getOrLoadStrData(), header.OffsetIs64Bit(),
|
|
offset, this, debug_macros_sp);
|
|
|
|
return debug_macros_sp;
|
|
}
|
|
|
|
bool SymbolFileDWARF::ParseDebugMacros(CompileUnit &comp_unit) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
|
|
DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
|
|
if (dwarf_cu == nullptr)
|
|
return false;
|
|
|
|
const DWARFBaseDIE dwarf_cu_die = dwarf_cu->GetUnitDIEOnly();
|
|
if (!dwarf_cu_die)
|
|
return false;
|
|
|
|
lldb::offset_t sect_offset =
|
|
dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_macros, DW_INVALID_OFFSET);
|
|
if (sect_offset == DW_INVALID_OFFSET)
|
|
sect_offset = dwarf_cu_die.GetAttributeValueAsUnsigned(DW_AT_GNU_macros,
|
|
DW_INVALID_OFFSET);
|
|
if (sect_offset == DW_INVALID_OFFSET)
|
|
return false;
|
|
|
|
comp_unit.SetDebugMacros(ParseDebugMacros(§_offset));
|
|
|
|
return true;
|
|
}
|
|
|
|
size_t SymbolFileDWARF::ParseBlocksRecursive(
|
|
lldb_private::CompileUnit &comp_unit, Block *parent_block,
|
|
const DWARFDIE &orig_die, addr_t subprogram_low_pc, uint32_t depth) {
|
|
size_t blocks_added = 0;
|
|
DWARFDIE die = orig_die;
|
|
while (die) {
|
|
dw_tag_t tag = die.Tag();
|
|
|
|
switch (tag) {
|
|
case DW_TAG_inlined_subroutine:
|
|
case DW_TAG_subprogram:
|
|
case DW_TAG_lexical_block: {
|
|
Block *block = nullptr;
|
|
if (tag == DW_TAG_subprogram) {
|
|
// Skip any DW_TAG_subprogram DIEs that are inside of a normal or
|
|
// inlined functions. These will be parsed on their own as separate
|
|
// entities.
|
|
|
|
if (depth > 0)
|
|
break;
|
|
|
|
block = parent_block;
|
|
} else {
|
|
BlockSP block_sp(new Block(die.GetID()));
|
|
parent_block->AddChild(block_sp);
|
|
block = block_sp.get();
|
|
}
|
|
DWARFRangeList ranges;
|
|
const char *name = nullptr;
|
|
const char *mangled_name = nullptr;
|
|
|
|
int decl_file = 0;
|
|
int decl_line = 0;
|
|
int decl_column = 0;
|
|
int call_file = 0;
|
|
int call_line = 0;
|
|
int call_column = 0;
|
|
if (die.GetDIENamesAndRanges(name, mangled_name, ranges, decl_file,
|
|
decl_line, decl_column, call_file, call_line,
|
|
call_column, nullptr)) {
|
|
if (tag == DW_TAG_subprogram) {
|
|
assert(subprogram_low_pc == LLDB_INVALID_ADDRESS);
|
|
subprogram_low_pc = ranges.GetMinRangeBase(0);
|
|
} else if (tag == DW_TAG_inlined_subroutine) {
|
|
// We get called here for inlined subroutines in two ways. The first
|
|
// time is when we are making the Function object for this inlined
|
|
// concrete instance. Since we're creating a top level block at
|
|
// here, the subprogram_low_pc will be LLDB_INVALID_ADDRESS. So we
|
|
// need to adjust the containing address. The second time is when we
|
|
// are parsing the blocks inside the function that contains the
|
|
// inlined concrete instance. Since these will be blocks inside the
|
|
// containing "real" function the offset will be for that function.
|
|
if (subprogram_low_pc == LLDB_INVALID_ADDRESS) {
|
|
subprogram_low_pc = ranges.GetMinRangeBase(0);
|
|
}
|
|
}
|
|
|
|
const size_t num_ranges = ranges.GetSize();
|
|
for (size_t i = 0; i < num_ranges; ++i) {
|
|
const DWARFRangeList::Entry &range = ranges.GetEntryRef(i);
|
|
const addr_t range_base = range.GetRangeBase();
|
|
if (range_base >= subprogram_low_pc)
|
|
block->AddRange(Block::Range(range_base - subprogram_low_pc,
|
|
range.GetByteSize()));
|
|
else {
|
|
GetObjectFile()->GetModule()->ReportError(
|
|
"0x%8.8" PRIx64 ": adding range [0x%" PRIx64 "-0x%" PRIx64
|
|
") which has a base that is less than the function's low PC "
|
|
"0x%" PRIx64 ". Please file a bug and attach the file at the "
|
|
"start of this error message",
|
|
block->GetID(), range_base, range.GetRangeEnd(),
|
|
subprogram_low_pc);
|
|
}
|
|
}
|
|
block->FinalizeRanges();
|
|
|
|
if (tag != DW_TAG_subprogram &&
|
|
(name != nullptr || mangled_name != nullptr)) {
|
|
std::unique_ptr<Declaration> decl_up;
|
|
if (decl_file != 0 || decl_line != 0 || decl_column != 0)
|
|
decl_up.reset(new Declaration(
|
|
comp_unit.GetSupportFiles().GetFileSpecAtIndex(decl_file),
|
|
decl_line, decl_column));
|
|
|
|
std::unique_ptr<Declaration> call_up;
|
|
if (call_file != 0 || call_line != 0 || call_column != 0)
|
|
call_up.reset(new Declaration(
|
|
comp_unit.GetSupportFiles().GetFileSpecAtIndex(call_file),
|
|
call_line, call_column));
|
|
|
|
block->SetInlinedFunctionInfo(name, mangled_name, decl_up.get(),
|
|
call_up.get());
|
|
}
|
|
|
|
++blocks_added;
|
|
|
|
if (die.HasChildren()) {
|
|
blocks_added +=
|
|
ParseBlocksRecursive(comp_unit, block, die.GetFirstChild(),
|
|
subprogram_low_pc, depth + 1);
|
|
}
|
|
}
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Only parse siblings of the block if we are not at depth zero. A depth of
|
|
// zero indicates we are currently parsing the top level DW_TAG_subprogram
|
|
// DIE
|
|
|
|
if (depth == 0)
|
|
die.Clear();
|
|
else
|
|
die = die.GetSibling();
|
|
}
|
|
return blocks_added;
|
|
}
|
|
|
|
bool SymbolFileDWARF::ClassOrStructIsVirtual(const DWARFDIE &parent_die) {
|
|
if (parent_die) {
|
|
for (DWARFDIE die = parent_die.GetFirstChild(); die;
|
|
die = die.GetSibling()) {
|
|
dw_tag_t tag = die.Tag();
|
|
bool check_virtuality = false;
|
|
switch (tag) {
|
|
case DW_TAG_inheritance:
|
|
case DW_TAG_subprogram:
|
|
check_virtuality = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (check_virtuality) {
|
|
if (die.GetAttributeValueAsUnsigned(DW_AT_virtuality, 0) != 0)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SymbolFileDWARF::ParseDeclsForContext(CompilerDeclContext decl_ctx) {
|
|
auto *type_system = decl_ctx.GetTypeSystem();
|
|
if (!type_system)
|
|
return;
|
|
DWARFASTParser *ast_parser = type_system->GetDWARFParser();
|
|
std::vector<DWARFDIE> decl_ctx_die_list =
|
|
ast_parser->GetDIEForDeclContext(decl_ctx);
|
|
|
|
for (DWARFDIE decl_ctx_die : decl_ctx_die_list)
|
|
for (DWARFDIE decl = decl_ctx_die.GetFirstChild(); decl;
|
|
decl = decl.GetSibling())
|
|
ast_parser->GetDeclForUIDFromDWARF(decl);
|
|
}
|
|
|
|
user_id_t SymbolFileDWARF::GetUID(DIERef ref) {
|
|
if (GetDebugMapSymfile())
|
|
return GetID() | ref.die_offset();
|
|
|
|
return user_id_t(GetDwoNum().getValueOr(0x7fffffff)) << 32 |
|
|
ref.die_offset() |
|
|
(lldb::user_id_t(ref.section() == DIERef::Section::DebugTypes) << 63);
|
|
}
|
|
|
|
llvm::Optional<SymbolFileDWARF::DecodedUID>
|
|
SymbolFileDWARF::DecodeUID(lldb::user_id_t uid) {
|
|
// This method can be called without going through the symbol vendor so we
|
|
// need to lock the module.
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
// Anytime we get a "lldb::user_id_t" from an lldb_private::SymbolFile API we
|
|
// must make sure we use the correct DWARF file when resolving things. On
|
|
// MacOSX, when using SymbolFileDWARFDebugMap, we will use multiple
|
|
// SymbolFileDWARF classes, one for each .o file. We can often end up with
|
|
// references to other DWARF objects and we must be ready to receive a
|
|
// "lldb::user_id_t" that specifies a DIE from another SymbolFileDWARF
|
|
// instance.
|
|
if (SymbolFileDWARFDebugMap *debug_map = GetDebugMapSymfile()) {
|
|
SymbolFileDWARF *dwarf = debug_map->GetSymbolFileByOSOIndex(
|
|
debug_map->GetOSOIndexFromUserID(uid));
|
|
return DecodedUID{
|
|
*dwarf, {llvm::None, DIERef::Section::DebugInfo, dw_offset_t(uid)}};
|
|
}
|
|
dw_offset_t die_offset = uid;
|
|
if (die_offset == DW_INVALID_OFFSET)
|
|
return llvm::None;
|
|
|
|
DIERef::Section section =
|
|
uid >> 63 ? DIERef::Section::DebugTypes : DIERef::Section::DebugInfo;
|
|
|
|
llvm::Optional<uint32_t> dwo_num = uid >> 32 & 0x7fffffff;
|
|
if (*dwo_num == 0x7fffffff)
|
|
dwo_num = llvm::None;
|
|
|
|
return DecodedUID{*this, {dwo_num, section, die_offset}};
|
|
}
|
|
|
|
DWARFDIE
|
|
SymbolFileDWARF::GetDIE(lldb::user_id_t uid) {
|
|
// This method can be called without going through the symbol vendor so we
|
|
// need to lock the module.
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
|
|
llvm::Optional<DecodedUID> decoded = DecodeUID(uid);
|
|
|
|
if (decoded)
|
|
return decoded->dwarf.GetDIE(decoded->ref);
|
|
|
|
return DWARFDIE();
|
|
}
|
|
|
|
CompilerDecl SymbolFileDWARF::GetDeclForUID(lldb::user_id_t type_uid) {
|
|
// This method can be called without going through the symbol vendor so we
|
|
// need to lock the module.
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
// Anytime we have a lldb::user_id_t, we must get the DIE by calling
|
|
// SymbolFileDWARF::GetDIE(). See comments inside the
|
|
// SymbolFileDWARF::GetDIE() for details.
|
|
if (DWARFDIE die = GetDIE(type_uid))
|
|
return die.GetDecl();
|
|
return CompilerDecl();
|
|
}
|
|
|
|
CompilerDeclContext
|
|
SymbolFileDWARF::GetDeclContextForUID(lldb::user_id_t type_uid) {
|
|
// This method can be called without going through the symbol vendor so we
|
|
// need to lock the module.
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
// Anytime we have a lldb::user_id_t, we must get the DIE by calling
|
|
// SymbolFileDWARF::GetDIE(). See comments inside the
|
|
// SymbolFileDWARF::GetDIE() for details.
|
|
if (DWARFDIE die = GetDIE(type_uid))
|
|
return die.GetDeclContext();
|
|
return CompilerDeclContext();
|
|
}
|
|
|
|
CompilerDeclContext
|
|
SymbolFileDWARF::GetDeclContextContainingUID(lldb::user_id_t type_uid) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
// Anytime we have a lldb::user_id_t, we must get the DIE by calling
|
|
// SymbolFileDWARF::GetDIE(). See comments inside the
|
|
// SymbolFileDWARF::GetDIE() for details.
|
|
if (DWARFDIE die = GetDIE(type_uid))
|
|
return die.GetContainingDeclContext();
|
|
return CompilerDeclContext();
|
|
}
|
|
|
|
Type *SymbolFileDWARF::ResolveTypeUID(lldb::user_id_t type_uid) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
// Anytime we have a lldb::user_id_t, we must get the DIE by calling
|
|
// SymbolFileDWARF::GetDIE(). See comments inside the
|
|
// SymbolFileDWARF::GetDIE() for details.
|
|
if (DWARFDIE type_die = GetDIE(type_uid))
|
|
return type_die.ResolveType();
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
llvm::Optional<SymbolFile::ArrayInfo>
|
|
SymbolFileDWARF::GetDynamicArrayInfoForUID(
|
|
lldb::user_id_t type_uid, const lldb_private::ExecutionContext *exe_ctx) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
if (DWARFDIE type_die = GetDIE(type_uid))
|
|
return DWARFASTParser::ParseChildArrayInfo(type_die, exe_ctx);
|
|
else
|
|
return llvm::None;
|
|
}
|
|
|
|
Type *SymbolFileDWARF::ResolveTypeUID(const DIERef &die_ref) {
|
|
return ResolveType(GetDIE(die_ref), true);
|
|
}
|
|
|
|
Type *SymbolFileDWARF::ResolveTypeUID(const DWARFDIE &die,
|
|
bool assert_not_being_parsed) {
|
|
if (die) {
|
|
Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO));
|
|
if (log)
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log, "SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s'",
|
|
die.GetOffset(), die.GetTagAsCString(), die.GetName());
|
|
|
|
// We might be coming in in the middle of a type tree (a class within a
|
|
// class, an enum within a class), so parse any needed parent DIEs before
|
|
// we get to this one...
|
|
DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(die);
|
|
if (decl_ctx_die) {
|
|
if (log) {
|
|
switch (decl_ctx_die.Tag()) {
|
|
case DW_TAG_structure_type:
|
|
case DW_TAG_union_type:
|
|
case DW_TAG_class_type: {
|
|
// Get the type, which could be a forward declaration
|
|
if (log)
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::ResolveTypeUID (die = 0x%8.8x) %s '%s' "
|
|
"resolve parent forward type for 0x%8.8x",
|
|
die.GetOffset(), die.GetTagAsCString(), die.GetName(),
|
|
decl_ctx_die.GetOffset());
|
|
} break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return ResolveType(die);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// This function is used when SymbolFileDWARFDebugMap owns a bunch of
|
|
// SymbolFileDWARF objects to detect if this DWARF file is the one that can
|
|
// resolve a compiler_type.
|
|
bool SymbolFileDWARF::HasForwardDeclForClangType(
|
|
const CompilerType &compiler_type) {
|
|
CompilerType compiler_type_no_qualifiers =
|
|
ClangUtil::RemoveFastQualifiers(compiler_type);
|
|
if (GetForwardDeclClangTypeToDie().count(
|
|
compiler_type_no_qualifiers.GetOpaqueQualType())) {
|
|
return true;
|
|
}
|
|
TypeSystem *type_system = compiler_type.GetTypeSystem();
|
|
|
|
ClangASTContext *clang_type_system =
|
|
llvm::dyn_cast_or_null<ClangASTContext>(type_system);
|
|
if (!clang_type_system)
|
|
return false;
|
|
DWARFASTParserClang *ast_parser =
|
|
static_cast<DWARFASTParserClang *>(clang_type_system->GetDWARFParser());
|
|
return ast_parser->GetClangASTImporter().CanImport(compiler_type);
|
|
}
|
|
|
|
bool SymbolFileDWARF::CompleteType(CompilerType &compiler_type) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
|
|
ClangASTContext *clang_type_system =
|
|
llvm::dyn_cast_or_null<ClangASTContext>(compiler_type.GetTypeSystem());
|
|
if (clang_type_system) {
|
|
DWARFASTParserClang *ast_parser =
|
|
static_cast<DWARFASTParserClang *>(clang_type_system->GetDWARFParser());
|
|
if (ast_parser &&
|
|
ast_parser->GetClangASTImporter().CanImport(compiler_type))
|
|
return ast_parser->GetClangASTImporter().CompleteType(compiler_type);
|
|
}
|
|
|
|
// We have a struct/union/class/enum that needs to be fully resolved.
|
|
CompilerType compiler_type_no_qualifiers =
|
|
ClangUtil::RemoveFastQualifiers(compiler_type);
|
|
auto die_it = GetForwardDeclClangTypeToDie().find(
|
|
compiler_type_no_qualifiers.GetOpaqueQualType());
|
|
if (die_it == GetForwardDeclClangTypeToDie().end()) {
|
|
// We have already resolved this type...
|
|
return true;
|
|
}
|
|
|
|
DWARFDIE dwarf_die = GetDIE(die_it->getSecond());
|
|
if (dwarf_die) {
|
|
// Once we start resolving this type, remove it from the forward
|
|
// declaration map in case anyone child members or other types require this
|
|
// type to get resolved. The type will get resolved when all of the calls
|
|
// to SymbolFileDWARF::ResolveClangOpaqueTypeDefinition are done.
|
|
GetForwardDeclClangTypeToDie().erase(die_it);
|
|
|
|
Type *type = GetDIEToType().lookup(dwarf_die.GetDIE());
|
|
|
|
Log *log(LogChannelDWARF::GetLogIfAny(DWARF_LOG_DEBUG_INFO |
|
|
DWARF_LOG_TYPE_COMPLETION));
|
|
if (log)
|
|
GetObjectFile()->GetModule()->LogMessageVerboseBacktrace(
|
|
log, "0x%8.8" PRIx64 ": %s '%s' resolving forward declaration...",
|
|
dwarf_die.GetID(), dwarf_die.GetTagAsCString(),
|
|
type->GetName().AsCString());
|
|
assert(compiler_type);
|
|
DWARFASTParser *dwarf_ast = dwarf_die.GetDWARFParser();
|
|
if (dwarf_ast)
|
|
return dwarf_ast->CompleteTypeFromDWARF(dwarf_die, type, compiler_type);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Type *SymbolFileDWARF::ResolveType(const DWARFDIE &die,
|
|
bool assert_not_being_parsed,
|
|
bool resolve_function_context) {
|
|
if (die) {
|
|
Type *type = GetTypeForDIE(die, resolve_function_context).get();
|
|
|
|
if (assert_not_being_parsed) {
|
|
if (type != DIE_IS_BEING_PARSED)
|
|
return type;
|
|
|
|
GetObjectFile()->GetModule()->ReportError(
|
|
"Parsing a die that is being parsed die: 0x%8.8x: %s %s",
|
|
die.GetOffset(), die.GetTagAsCString(), die.GetName());
|
|
|
|
} else
|
|
return type;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
CompileUnit *
|
|
SymbolFileDWARF::GetCompUnitForDWARFCompUnit(DWARFCompileUnit &dwarf_cu) {
|
|
// Check if the symbol vendor already knows about this compile unit?
|
|
if (dwarf_cu.GetUserData() == nullptr) {
|
|
// The symbol vendor doesn't know about this compile unit, we need to parse
|
|
// and add it to the symbol vendor object.
|
|
return ParseCompileUnit(dwarf_cu).get();
|
|
}
|
|
return (CompileUnit *)dwarf_cu.GetUserData();
|
|
}
|
|
|
|
size_t SymbolFileDWARF::GetObjCMethodDIEOffsets(ConstString class_name,
|
|
DIEArray &method_die_offsets) {
|
|
method_die_offsets.clear();
|
|
m_index->GetObjCMethods(class_name, method_die_offsets);
|
|
return method_die_offsets.size();
|
|
}
|
|
|
|
bool SymbolFileDWARF::GetFunction(const DWARFDIE &die, SymbolContext &sc) {
|
|
sc.Clear(false);
|
|
|
|
if (die && llvm::isa<DWARFCompileUnit>(die.GetCU())) {
|
|
// Check if the symbol vendor already knows about this compile unit?
|
|
sc.comp_unit =
|
|
GetCompUnitForDWARFCompUnit(llvm::cast<DWARFCompileUnit>(*die.GetCU()));
|
|
|
|
sc.function = sc.comp_unit->FindFunctionByUID(die.GetID()).get();
|
|
if (sc.function == nullptr)
|
|
sc.function = ParseFunction(*sc.comp_unit, die);
|
|
|
|
if (sc.function) {
|
|
sc.module_sp = sc.function->CalculateSymbolContextModule();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
lldb::ModuleSP SymbolFileDWARF::GetDWOModule(ConstString name) {
|
|
UpdateExternalModuleListIfNeeded();
|
|
const auto &pos = m_external_type_modules.find(name);
|
|
if (pos != m_external_type_modules.end())
|
|
return pos->second;
|
|
else
|
|
return lldb::ModuleSP();
|
|
}
|
|
|
|
DWARFDIE
|
|
SymbolFileDWARF::GetDIE(const DIERef &die_ref) {
|
|
if (die_ref.dwo_num()) {
|
|
return DebugInfo()
|
|
->GetUnitAtIndex(*die_ref.dwo_num())
|
|
->GetDwoSymbolFile()
|
|
->GetDIE(die_ref);
|
|
}
|
|
|
|
DWARFDebugInfo *debug_info = DebugInfo();
|
|
if (debug_info)
|
|
return debug_info->GetDIE(die_ref);
|
|
else
|
|
return DWARFDIE();
|
|
}
|
|
|
|
std::unique_ptr<SymbolFileDWARFDwo>
|
|
SymbolFileDWARF::GetDwoSymbolFileForCompileUnit(
|
|
DWARFUnit &unit, const DWARFDebugInfoEntry &cu_die) {
|
|
// If we are using a dSYM file, we never want the standard DWO files since
|
|
// the -gmodules support uses the same DWO machanism to specify full debug
|
|
// info files for modules.
|
|
if (GetDebugMapSymfile())
|
|
return nullptr;
|
|
|
|
DWARFCompileUnit *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(&unit);
|
|
// Only compile units can be split into two parts.
|
|
if (!dwarf_cu)
|
|
return nullptr;
|
|
|
|
const char *dwo_name =
|
|
cu_die.GetAttributeValueAsString(dwarf_cu, DW_AT_GNU_dwo_name, nullptr);
|
|
if (!dwo_name)
|
|
return nullptr;
|
|
|
|
SymbolFileDWARFDwp *dwp_symfile = GetDwpSymbolFile();
|
|
if (dwp_symfile) {
|
|
uint64_t dwo_id =
|
|
cu_die.GetAttributeValueAsUnsigned(dwarf_cu, DW_AT_GNU_dwo_id, 0);
|
|
std::unique_ptr<SymbolFileDWARFDwo> dwo_symfile =
|
|
dwp_symfile->GetSymbolFileForDwoId(*dwarf_cu, dwo_id);
|
|
if (dwo_symfile)
|
|
return dwo_symfile;
|
|
}
|
|
|
|
FileSpec dwo_file(dwo_name);
|
|
FileSystem::Instance().Resolve(dwo_file);
|
|
if (dwo_file.IsRelative()) {
|
|
const char *comp_dir =
|
|
cu_die.GetAttributeValueAsString(dwarf_cu, DW_AT_comp_dir, nullptr);
|
|
if (!comp_dir)
|
|
return nullptr;
|
|
|
|
dwo_file.SetFile(comp_dir, FileSpec::Style::native);
|
|
FileSystem::Instance().Resolve(dwo_file);
|
|
dwo_file.AppendPathComponent(dwo_name);
|
|
}
|
|
|
|
if (!FileSystem::Instance().Exists(dwo_file))
|
|
return nullptr;
|
|
|
|
const lldb::offset_t file_offset = 0;
|
|
DataBufferSP dwo_file_data_sp;
|
|
lldb::offset_t dwo_file_data_offset = 0;
|
|
ObjectFileSP dwo_obj_file = ObjectFile::FindPlugin(
|
|
GetObjectFile()->GetModule(), &dwo_file, file_offset,
|
|
FileSystem::Instance().GetByteSize(dwo_file), dwo_file_data_sp,
|
|
dwo_file_data_offset);
|
|
if (dwo_obj_file == nullptr)
|
|
return nullptr;
|
|
|
|
return llvm::make_unique<SymbolFileDWARFDwo>(dwo_obj_file, *dwarf_cu);
|
|
}
|
|
|
|
void SymbolFileDWARF::UpdateExternalModuleListIfNeeded() {
|
|
if (m_fetched_external_modules)
|
|
return;
|
|
m_fetched_external_modules = true;
|
|
|
|
DWARFDebugInfo *debug_info = DebugInfo();
|
|
|
|
const uint32_t num_compile_units = GetNumCompileUnits();
|
|
for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) {
|
|
DWARFUnit *dwarf_cu = debug_info->GetUnitAtIndex(cu_idx);
|
|
|
|
const DWARFBaseDIE die = dwarf_cu->GetUnitDIEOnly();
|
|
if (die && !die.HasChildren()) {
|
|
const char *name = die.GetAttributeValueAsString(DW_AT_name, nullptr);
|
|
|
|
if (name) {
|
|
ConstString const_name(name);
|
|
if (m_external_type_modules.find(const_name) ==
|
|
m_external_type_modules.end()) {
|
|
ModuleSP module_sp;
|
|
const char *dwo_path =
|
|
die.GetAttributeValueAsString(DW_AT_GNU_dwo_name, nullptr);
|
|
if (dwo_path) {
|
|
ModuleSpec dwo_module_spec;
|
|
dwo_module_spec.GetFileSpec().SetFile(dwo_path,
|
|
FileSpec::Style::native);
|
|
if (dwo_module_spec.GetFileSpec().IsRelative()) {
|
|
const char *comp_dir =
|
|
die.GetAttributeValueAsString(DW_AT_comp_dir, nullptr);
|
|
if (comp_dir) {
|
|
dwo_module_spec.GetFileSpec().SetFile(comp_dir,
|
|
FileSpec::Style::native);
|
|
FileSystem::Instance().Resolve(dwo_module_spec.GetFileSpec());
|
|
dwo_module_spec.GetFileSpec().AppendPathComponent(dwo_path);
|
|
}
|
|
}
|
|
dwo_module_spec.GetArchitecture() =
|
|
m_objfile_sp->GetModule()->GetArchitecture();
|
|
|
|
// When LLDB loads "external" modules it looks at the presence of
|
|
// DW_AT_GNU_dwo_name. However, when the already created module
|
|
// (corresponding to .dwo itself) is being processed, it will see
|
|
// the presence of DW_AT_GNU_dwo_name (which contains the name of
|
|
// dwo file) and will try to call ModuleList::GetSharedModule
|
|
// again. In some cases (i.e. for empty files) Clang 4.0 generates
|
|
// a *.dwo file which has DW_AT_GNU_dwo_name, but no
|
|
// DW_AT_comp_dir. In this case the method
|
|
// ModuleList::GetSharedModule will fail and the warning will be
|
|
// printed. However, as one can notice in this case we don't
|
|
// actually need to try to load the already loaded module
|
|
// (corresponding to .dwo) so we simply skip it.
|
|
if (m_objfile_sp->GetFileSpec().GetFileNameExtension() == ".dwo" &&
|
|
llvm::StringRef(m_objfile_sp->GetFileSpec().GetPath())
|
|
.endswith(dwo_module_spec.GetFileSpec().GetPath())) {
|
|
continue;
|
|
}
|
|
|
|
Status error = ModuleList::GetSharedModule(
|
|
dwo_module_spec, module_sp, nullptr, nullptr, nullptr);
|
|
if (!module_sp) {
|
|
GetObjectFile()->GetModule()->ReportWarning(
|
|
"0x%8.8x: unable to locate module needed for external types: "
|
|
"%s\nerror: %s\nDebugging will be degraded due to missing "
|
|
"types. Rebuilding your project will regenerate the needed "
|
|
"module files.",
|
|
die.GetOffset(),
|
|
dwo_module_spec.GetFileSpec().GetPath().c_str(),
|
|
error.AsCString("unknown error"));
|
|
}
|
|
}
|
|
m_external_type_modules[const_name] = module_sp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SymbolFileDWARF::GlobalVariableMap &SymbolFileDWARF::GetGlobalAranges() {
|
|
if (!m_global_aranges_up) {
|
|
m_global_aranges_up.reset(new GlobalVariableMap());
|
|
|
|
ModuleSP module_sp = GetObjectFile()->GetModule();
|
|
if (module_sp) {
|
|
const size_t num_cus = module_sp->GetNumCompileUnits();
|
|
for (size_t i = 0; i < num_cus; ++i) {
|
|
CompUnitSP cu_sp = module_sp->GetCompileUnitAtIndex(i);
|
|
if (cu_sp) {
|
|
VariableListSP globals_sp = cu_sp->GetVariableList(true);
|
|
if (globals_sp) {
|
|
const size_t num_globals = globals_sp->GetSize();
|
|
for (size_t g = 0; g < num_globals; ++g) {
|
|
VariableSP var_sp = globals_sp->GetVariableAtIndex(g);
|
|
if (var_sp && !var_sp->GetLocationIsConstantValueData()) {
|
|
const DWARFExpression &location = var_sp->LocationExpression();
|
|
Value location_result;
|
|
Status error;
|
|
if (location.Evaluate(nullptr, LLDB_INVALID_ADDRESS, nullptr,
|
|
nullptr, location_result, &error)) {
|
|
if (location_result.GetValueType() ==
|
|
Value::eValueTypeFileAddress) {
|
|
lldb::addr_t file_addr =
|
|
location_result.GetScalar().ULongLong();
|
|
lldb::addr_t byte_size = 1;
|
|
if (var_sp->GetType())
|
|
byte_size =
|
|
var_sp->GetType()->GetByteSize().getValueOr(0);
|
|
m_global_aranges_up->Append(GlobalVariableMap::Entry(
|
|
file_addr, byte_size, var_sp.get()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
m_global_aranges_up->Sort();
|
|
}
|
|
return *m_global_aranges_up;
|
|
}
|
|
|
|
uint32_t SymbolFileDWARF::ResolveSymbolContext(const Address &so_addr,
|
|
SymbolContextItem resolve_scope,
|
|
SymbolContext &sc) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
|
|
Timer scoped_timer(func_cat,
|
|
"SymbolFileDWARF::"
|
|
"ResolveSymbolContext (so_addr = { "
|
|
"section = %p, offset = 0x%" PRIx64
|
|
" }, resolve_scope = 0x%8.8x)",
|
|
static_cast<void *>(so_addr.GetSection().get()),
|
|
so_addr.GetOffset(), resolve_scope);
|
|
uint32_t resolved = 0;
|
|
if (resolve_scope &
|
|
(eSymbolContextCompUnit | eSymbolContextFunction | eSymbolContextBlock |
|
|
eSymbolContextLineEntry | eSymbolContextVariable)) {
|
|
lldb::addr_t file_vm_addr = so_addr.GetFileAddress();
|
|
|
|
DWARFDebugInfo *debug_info = DebugInfo();
|
|
if (debug_info) {
|
|
llvm::Expected<DWARFDebugAranges &> aranges =
|
|
debug_info->GetCompileUnitAranges();
|
|
if (!aranges) {
|
|
Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
|
|
LLDB_LOG_ERROR(log, aranges.takeError(),
|
|
"SymbolFileDWARF::ResolveSymbolContext failed to get cu "
|
|
"aranges. {0}");
|
|
return 0;
|
|
}
|
|
|
|
const dw_offset_t cu_offset = aranges->FindAddress(file_vm_addr);
|
|
if (cu_offset == DW_INVALID_OFFSET) {
|
|
// Global variables are not in the compile unit address ranges. The
|
|
// only way to currently find global variables is to iterate over the
|
|
// .debug_pubnames or the __apple_names table and find all items in
|
|
// there that point to DW_TAG_variable DIEs and then find the address
|
|
// that matches.
|
|
if (resolve_scope & eSymbolContextVariable) {
|
|
GlobalVariableMap &map = GetGlobalAranges();
|
|
const GlobalVariableMap::Entry *entry =
|
|
map.FindEntryThatContains(file_vm_addr);
|
|
if (entry && entry->data) {
|
|
Variable *variable = entry->data;
|
|
SymbolContextScope *scc = variable->GetSymbolContextScope();
|
|
if (scc) {
|
|
scc->CalculateSymbolContext(&sc);
|
|
sc.variable = variable;
|
|
}
|
|
return sc.GetResolvedMask();
|
|
}
|
|
}
|
|
} else {
|
|
uint32_t cu_idx = DW_INVALID_INDEX;
|
|
if (auto *dwarf_cu = llvm::dyn_cast_or_null<DWARFCompileUnit>(
|
|
debug_info->GetUnitAtOffset(DIERef::Section::DebugInfo,
|
|
cu_offset, &cu_idx))) {
|
|
sc.comp_unit = GetCompUnitForDWARFCompUnit(*dwarf_cu);
|
|
if (sc.comp_unit) {
|
|
resolved |= eSymbolContextCompUnit;
|
|
|
|
bool force_check_line_table = false;
|
|
if (resolve_scope &
|
|
(eSymbolContextFunction | eSymbolContextBlock)) {
|
|
DWARFDIE function_die = dwarf_cu->LookupAddress(file_vm_addr);
|
|
DWARFDIE block_die;
|
|
if (function_die) {
|
|
sc.function =
|
|
sc.comp_unit->FindFunctionByUID(function_die.GetID()).get();
|
|
if (sc.function == nullptr)
|
|
sc.function = ParseFunction(*sc.comp_unit, function_die);
|
|
|
|
if (sc.function && (resolve_scope & eSymbolContextBlock))
|
|
block_die = function_die.LookupDeepestBlock(file_vm_addr);
|
|
} else {
|
|
// We might have had a compile unit that had discontiguous
|
|
// address ranges where the gaps are symbols that don't have
|
|
// any debug info. Discontiguous compile unit address ranges
|
|
// should only happen when there aren't other functions from
|
|
// other compile units in these gaps. This helps keep the size
|
|
// of the aranges down.
|
|
force_check_line_table = true;
|
|
}
|
|
|
|
if (sc.function != nullptr) {
|
|
resolved |= eSymbolContextFunction;
|
|
|
|
if (resolve_scope & eSymbolContextBlock) {
|
|
Block &block = sc.function->GetBlock(true);
|
|
|
|
if (block_die)
|
|
sc.block = block.FindBlockByID(block_die.GetID());
|
|
else
|
|
sc.block = block.FindBlockByID(function_die.GetID());
|
|
if (sc.block)
|
|
resolved |= eSymbolContextBlock;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((resolve_scope & eSymbolContextLineEntry) ||
|
|
force_check_line_table) {
|
|
LineTable *line_table = sc.comp_unit->GetLineTable();
|
|
if (line_table != nullptr) {
|
|
// And address that makes it into this function should be in
|
|
// terms of this debug file if there is no debug map, or it
|
|
// will be an address in the .o file which needs to be fixed up
|
|
// to be in terms of the debug map executable. Either way,
|
|
// calling FixupAddress() will work for us.
|
|
Address exe_so_addr(so_addr);
|
|
if (FixupAddress(exe_so_addr)) {
|
|
if (line_table->FindLineEntryByAddress(exe_so_addr,
|
|
sc.line_entry)) {
|
|
resolved |= eSymbolContextLineEntry;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (force_check_line_table &&
|
|
!(resolved & eSymbolContextLineEntry)) {
|
|
// We might have had a compile unit that had discontiguous
|
|
// address ranges where the gaps are symbols that don't have any
|
|
// debug info. Discontiguous compile unit address ranges should
|
|
// only happen when there aren't other functions from other
|
|
// compile units in these gaps. This helps keep the size of the
|
|
// aranges down.
|
|
sc.comp_unit = nullptr;
|
|
resolved &= ~eSymbolContextCompUnit;
|
|
}
|
|
} else {
|
|
GetObjectFile()->GetModule()->ReportWarning(
|
|
"0x%8.8x: compile unit %u failed to create a valid "
|
|
"lldb_private::CompileUnit class.",
|
|
cu_offset, cu_idx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return resolved;
|
|
}
|
|
|
|
uint32_t SymbolFileDWARF::ResolveSymbolContext(const FileSpec &file_spec,
|
|
uint32_t line,
|
|
bool check_inlines,
|
|
SymbolContextItem resolve_scope,
|
|
SymbolContextList &sc_list) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
const uint32_t prev_size = sc_list.GetSize();
|
|
if (resolve_scope & eSymbolContextCompUnit) {
|
|
for (uint32_t cu_idx = 0, num_cus = GetNumCompileUnits(); cu_idx < num_cus;
|
|
++cu_idx) {
|
|
CompileUnit *dc_cu = ParseCompileUnitAtIndex(cu_idx).get();
|
|
if (!dc_cu)
|
|
continue;
|
|
|
|
const bool full_match = (bool)file_spec.GetDirectory();
|
|
bool file_spec_matches_cu_file_spec =
|
|
FileSpec::Equal(file_spec, *dc_cu, full_match);
|
|
if (check_inlines || file_spec_matches_cu_file_spec) {
|
|
SymbolContext sc(m_objfile_sp->GetModule());
|
|
sc.comp_unit = dc_cu;
|
|
uint32_t file_idx = UINT32_MAX;
|
|
|
|
// If we are looking for inline functions only and we don't find it
|
|
// in the support files, we are done.
|
|
if (check_inlines) {
|
|
file_idx =
|
|
sc.comp_unit->GetSupportFiles().FindFileIndex(1, file_spec, true);
|
|
if (file_idx == UINT32_MAX)
|
|
continue;
|
|
}
|
|
|
|
if (line != 0) {
|
|
LineTable *line_table = sc.comp_unit->GetLineTable();
|
|
|
|
if (line_table != nullptr && line != 0) {
|
|
// We will have already looked up the file index if we are
|
|
// searching for inline entries.
|
|
if (!check_inlines)
|
|
file_idx = sc.comp_unit->GetSupportFiles().FindFileIndex(
|
|
1, file_spec, true);
|
|
|
|
if (file_idx != UINT32_MAX) {
|
|
uint32_t found_line;
|
|
uint32_t line_idx = line_table->FindLineEntryIndexByFileIndex(
|
|
0, file_idx, line, false, &sc.line_entry);
|
|
found_line = sc.line_entry.line;
|
|
|
|
while (line_idx != UINT32_MAX) {
|
|
sc.function = nullptr;
|
|
sc.block = nullptr;
|
|
if (resolve_scope &
|
|
(eSymbolContextFunction | eSymbolContextBlock)) {
|
|
const lldb::addr_t file_vm_addr =
|
|
sc.line_entry.range.GetBaseAddress().GetFileAddress();
|
|
if (file_vm_addr != LLDB_INVALID_ADDRESS) {
|
|
DWARFDIE function_die =
|
|
GetDWARFCompileUnit(dc_cu)->LookupAddress(file_vm_addr);
|
|
DWARFDIE block_die;
|
|
if (function_die) {
|
|
sc.function =
|
|
sc.comp_unit->FindFunctionByUID(function_die.GetID())
|
|
.get();
|
|
if (sc.function == nullptr)
|
|
sc.function =
|
|
ParseFunction(*sc.comp_unit, function_die);
|
|
|
|
if (sc.function && (resolve_scope & eSymbolContextBlock))
|
|
block_die =
|
|
function_die.LookupDeepestBlock(file_vm_addr);
|
|
}
|
|
|
|
if (sc.function != nullptr) {
|
|
Block &block = sc.function->GetBlock(true);
|
|
|
|
if (block_die)
|
|
sc.block = block.FindBlockByID(block_die.GetID());
|
|
else if (function_die)
|
|
sc.block = block.FindBlockByID(function_die.GetID());
|
|
}
|
|
}
|
|
}
|
|
|
|
sc_list.Append(sc);
|
|
line_idx = line_table->FindLineEntryIndexByFileIndex(
|
|
line_idx + 1, file_idx, found_line, true, &sc.line_entry);
|
|
}
|
|
}
|
|
} else if (file_spec_matches_cu_file_spec && !check_inlines) {
|
|
// only append the context if we aren't looking for inline call
|
|
// sites by file and line and if the file spec matches that of
|
|
// the compile unit
|
|
sc_list.Append(sc);
|
|
}
|
|
} else if (file_spec_matches_cu_file_spec && !check_inlines) {
|
|
// only append the context if we aren't looking for inline call
|
|
// sites by file and line and if the file spec matches that of
|
|
// the compile unit
|
|
sc_list.Append(sc);
|
|
}
|
|
|
|
if (!check_inlines)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return sc_list.GetSize() - prev_size;
|
|
}
|
|
|
|
void SymbolFileDWARF::PreloadSymbols() {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
m_index->Preload();
|
|
}
|
|
|
|
std::recursive_mutex &SymbolFileDWARF::GetModuleMutex() const {
|
|
lldb::ModuleSP module_sp(m_debug_map_module_wp.lock());
|
|
if (module_sp)
|
|
return module_sp->GetMutex();
|
|
return GetObjectFile()->GetModule()->GetMutex();
|
|
}
|
|
|
|
bool SymbolFileDWARF::DeclContextMatchesThisSymbolFile(
|
|
const lldb_private::CompilerDeclContext *decl_ctx) {
|
|
if (decl_ctx == nullptr || !decl_ctx->IsValid()) {
|
|
// Invalid namespace decl which means we aren't matching only things in
|
|
// this symbol file, so return true to indicate it matches this symbol
|
|
// file.
|
|
return true;
|
|
}
|
|
|
|
TypeSystem *decl_ctx_type_system = decl_ctx->GetTypeSystem();
|
|
auto type_system_or_err = GetTypeSystemForLanguage(
|
|
decl_ctx_type_system->GetMinimumLanguage(nullptr));
|
|
if (auto err = type_system_or_err.takeError()) {
|
|
LLDB_LOG_ERROR(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_SYMBOLS),
|
|
std::move(err),
|
|
"Unable to match namespace decl using TypeSystem");
|
|
return false;
|
|
}
|
|
|
|
if (decl_ctx_type_system == &type_system_or_err.get())
|
|
return true; // The type systems match, return true
|
|
|
|
// The namespace AST was valid, and it does not match...
|
|
Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
|
|
|
|
if (log)
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log, "Valid namespace does not match symbol file");
|
|
|
|
return false;
|
|
}
|
|
|
|
uint32_t SymbolFileDWARF::FindGlobalVariables(
|
|
ConstString name, const CompilerDeclContext *parent_decl_ctx,
|
|
uint32_t max_matches, VariableList &variables) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
|
|
|
|
if (log)
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::FindGlobalVariables (name=\"%s\", "
|
|
"parent_decl_ctx=%p, max_matches=%u, variables)",
|
|
name.GetCString(), static_cast<const void *>(parent_decl_ctx),
|
|
max_matches);
|
|
|
|
if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
|
|
return 0;
|
|
|
|
DWARFDebugInfo *info = DebugInfo();
|
|
if (info == nullptr)
|
|
return 0;
|
|
|
|
// Remember how many variables are in the list before we search.
|
|
const uint32_t original_size = variables.GetSize();
|
|
|
|
llvm::StringRef basename;
|
|
llvm::StringRef context;
|
|
bool name_is_mangled = (bool)Mangled(name);
|
|
|
|
if (!CPlusPlusLanguage::ExtractContextAndIdentifier(name.GetCString(),
|
|
context, basename))
|
|
basename = name.GetStringRef();
|
|
|
|
DIEArray die_offsets;
|
|
m_index->GetGlobalVariables(ConstString(basename), die_offsets);
|
|
const size_t num_die_matches = die_offsets.size();
|
|
if (num_die_matches) {
|
|
SymbolContext sc;
|
|
sc.module_sp = m_objfile_sp->GetModule();
|
|
assert(sc.module_sp);
|
|
|
|
// Loop invariant: Variables up to this index have been checked for context
|
|
// matches.
|
|
uint32_t pruned_idx = original_size;
|
|
|
|
bool done = false;
|
|
for (size_t i = 0; i < num_die_matches && !done; ++i) {
|
|
const DIERef &die_ref = die_offsets[i];
|
|
DWARFDIE die = GetDIE(die_ref);
|
|
|
|
if (die) {
|
|
switch (die.Tag()) {
|
|
default:
|
|
case DW_TAG_subprogram:
|
|
case DW_TAG_inlined_subroutine:
|
|
case DW_TAG_try_block:
|
|
case DW_TAG_catch_block:
|
|
break;
|
|
|
|
case DW_TAG_variable: {
|
|
auto *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(die.GetCU());
|
|
if (!dwarf_cu)
|
|
continue;
|
|
sc.comp_unit = GetCompUnitForDWARFCompUnit(*dwarf_cu);
|
|
|
|
if (parent_decl_ctx) {
|
|
DWARFASTParser *dwarf_ast = die.GetDWARFParser();
|
|
if (dwarf_ast) {
|
|
CompilerDeclContext actual_parent_decl_ctx =
|
|
dwarf_ast->GetDeclContextContainingUIDFromDWARF(die);
|
|
if (!actual_parent_decl_ctx ||
|
|
actual_parent_decl_ctx != *parent_decl_ctx)
|
|
continue;
|
|
}
|
|
}
|
|
|
|
ParseVariables(sc, die, LLDB_INVALID_ADDRESS, false, false,
|
|
&variables);
|
|
while (pruned_idx < variables.GetSize()) {
|
|
VariableSP var_sp = variables.GetVariableAtIndex(pruned_idx);
|
|
if (name_is_mangled ||
|
|
var_sp->GetName().GetStringRef().contains(name.GetStringRef()))
|
|
++pruned_idx;
|
|
else
|
|
variables.RemoveVariableAtIndex(pruned_idx);
|
|
}
|
|
|
|
if (variables.GetSize() - original_size >= max_matches)
|
|
done = true;
|
|
} break;
|
|
}
|
|
} else {
|
|
m_index->ReportInvalidDIERef(die_ref, name.GetStringRef());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return the number of variable that were appended to the list
|
|
const uint32_t num_matches = variables.GetSize() - original_size;
|
|
if (log && num_matches > 0) {
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::FindGlobalVariables (name=\"%s\", "
|
|
"parent_decl_ctx=%p, max_matches=%u, variables) => %u",
|
|
name.GetCString(), static_cast<const void *>(parent_decl_ctx),
|
|
max_matches, num_matches);
|
|
}
|
|
return num_matches;
|
|
}
|
|
|
|
uint32_t SymbolFileDWARF::FindGlobalVariables(const RegularExpression ®ex,
|
|
uint32_t max_matches,
|
|
VariableList &variables) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
|
|
|
|
if (log) {
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::FindGlobalVariables (regex=\"%s\", "
|
|
"max_matches=%u, variables)",
|
|
regex.GetText().str().c_str(), max_matches);
|
|
}
|
|
|
|
DWARFDebugInfo *info = DebugInfo();
|
|
if (info == nullptr)
|
|
return 0;
|
|
|
|
// Remember how many variables are in the list before we search.
|
|
const uint32_t original_size = variables.GetSize();
|
|
|
|
DIEArray die_offsets;
|
|
m_index->GetGlobalVariables(regex, die_offsets);
|
|
|
|
SymbolContext sc;
|
|
sc.module_sp = m_objfile_sp->GetModule();
|
|
assert(sc.module_sp);
|
|
|
|
const size_t num_matches = die_offsets.size();
|
|
if (num_matches) {
|
|
for (size_t i = 0; i < num_matches; ++i) {
|
|
const DIERef &die_ref = die_offsets[i];
|
|
DWARFDIE die = GetDIE(die_ref);
|
|
|
|
if (die) {
|
|
DWARFCompileUnit *dwarf_cu =
|
|
llvm::dyn_cast<DWARFCompileUnit>(die.GetCU());
|
|
if (!dwarf_cu)
|
|
continue;
|
|
sc.comp_unit = GetCompUnitForDWARFCompUnit(*dwarf_cu);
|
|
|
|
ParseVariables(sc, die, LLDB_INVALID_ADDRESS, false, false, &variables);
|
|
|
|
if (variables.GetSize() - original_size >= max_matches)
|
|
break;
|
|
} else
|
|
m_index->ReportInvalidDIERef(die_ref, regex.GetText());
|
|
}
|
|
}
|
|
|
|
// Return the number of variable that were appended to the list
|
|
return variables.GetSize() - original_size;
|
|
}
|
|
|
|
bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die,
|
|
bool include_inlines,
|
|
SymbolContextList &sc_list) {
|
|
SymbolContext sc;
|
|
|
|
if (!orig_die)
|
|
return false;
|
|
|
|
// If we were passed a die that is not a function, just return false...
|
|
if (!(orig_die.Tag() == DW_TAG_subprogram ||
|
|
(include_inlines && orig_die.Tag() == DW_TAG_inlined_subroutine)))
|
|
return false;
|
|
|
|
DWARFDIE die = orig_die;
|
|
DWARFDIE inlined_die;
|
|
if (die.Tag() == DW_TAG_inlined_subroutine) {
|
|
inlined_die = die;
|
|
|
|
while (true) {
|
|
die = die.GetParent();
|
|
|
|
if (die) {
|
|
if (die.Tag() == DW_TAG_subprogram)
|
|
break;
|
|
} else
|
|
break;
|
|
}
|
|
}
|
|
assert(die && die.Tag() == DW_TAG_subprogram);
|
|
if (GetFunction(die, sc)) {
|
|
Address addr;
|
|
// Parse all blocks if needed
|
|
if (inlined_die) {
|
|
Block &function_block = sc.function->GetBlock(true);
|
|
sc.block = function_block.FindBlockByID(inlined_die.GetID());
|
|
if (sc.block == nullptr)
|
|
sc.block = function_block.FindBlockByID(inlined_die.GetOffset());
|
|
if (sc.block == nullptr || !sc.block->GetStartAddress(addr))
|
|
addr.Clear();
|
|
} else {
|
|
sc.block = nullptr;
|
|
addr = sc.function->GetAddressRange().GetBaseAddress();
|
|
}
|
|
|
|
if (addr.IsValid()) {
|
|
sc_list.Append(sc);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool SymbolFileDWARF::DIEInDeclContext(const CompilerDeclContext *decl_ctx,
|
|
const DWARFDIE &die) {
|
|
// If we have no parent decl context to match this DIE matches, and if the
|
|
// parent decl context isn't valid, we aren't trying to look for any
|
|
// particular decl context so any die matches.
|
|
if (decl_ctx == nullptr || !decl_ctx->IsValid())
|
|
return true;
|
|
|
|
if (die) {
|
|
DWARFASTParser *dwarf_ast = die.GetDWARFParser();
|
|
if (dwarf_ast) {
|
|
CompilerDeclContext actual_decl_ctx =
|
|
dwarf_ast->GetDeclContextContainingUIDFromDWARF(die);
|
|
if (actual_decl_ctx)
|
|
return decl_ctx->IsContainedInLookup(actual_decl_ctx);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint32_t SymbolFileDWARF::FindFunctions(
|
|
ConstString name, const CompilerDeclContext *parent_decl_ctx,
|
|
FunctionNameType name_type_mask, bool include_inlines, bool append,
|
|
SymbolContextList &sc_list) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
|
|
Timer scoped_timer(func_cat, "SymbolFileDWARF::FindFunctions (name = '%s')",
|
|
name.AsCString());
|
|
|
|
// eFunctionNameTypeAuto should be pre-resolved by a call to
|
|
// Module::LookupInfo::LookupInfo()
|
|
assert((name_type_mask & eFunctionNameTypeAuto) == 0);
|
|
|
|
Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
|
|
|
|
if (log) {
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::FindFunctions (name=\"%s\", "
|
|
"name_type_mask=0x%x, append=%u, sc_list)",
|
|
name.GetCString(), name_type_mask, append);
|
|
}
|
|
|
|
// If we aren't appending the results to this list, then clear the list
|
|
if (!append)
|
|
sc_list.Clear();
|
|
|
|
if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
|
|
return 0;
|
|
|
|
// If name is empty then we won't find anything.
|
|
if (name.IsEmpty())
|
|
return 0;
|
|
|
|
// Remember how many sc_list are in the list before we search in case we are
|
|
// appending the results to a variable list.
|
|
|
|
const uint32_t original_size = sc_list.GetSize();
|
|
|
|
llvm::DenseSet<const DWARFDebugInfoEntry *> resolved_dies;
|
|
DIEArray offsets;
|
|
CompilerDeclContext empty_decl_ctx;
|
|
if (!parent_decl_ctx)
|
|
parent_decl_ctx = &empty_decl_ctx;
|
|
|
|
std::vector<DWARFDIE> dies;
|
|
m_index->GetFunctions(name, *this, *parent_decl_ctx, name_type_mask, dies);
|
|
for (const DWARFDIE &die : dies) {
|
|
if (resolved_dies.insert(die.GetDIE()).second)
|
|
ResolveFunction(die, include_inlines, sc_list);
|
|
}
|
|
|
|
// Return the number of variable that were appended to the list
|
|
const uint32_t num_matches = sc_list.GetSize() - original_size;
|
|
|
|
if (log && num_matches > 0) {
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::FindFunctions (name=\"%s\", "
|
|
"name_type_mask=0x%x, include_inlines=%d, append=%u, sc_list) => "
|
|
"%u",
|
|
name.GetCString(), name_type_mask, include_inlines, append,
|
|
num_matches);
|
|
}
|
|
return num_matches;
|
|
}
|
|
|
|
uint32_t SymbolFileDWARF::FindFunctions(const RegularExpression ®ex,
|
|
bool include_inlines, bool append,
|
|
SymbolContextList &sc_list) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
|
|
Timer scoped_timer(func_cat, "SymbolFileDWARF::FindFunctions (regex = '%s')",
|
|
regex.GetText().str().c_str());
|
|
|
|
Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
|
|
|
|
if (log) {
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::FindFunctions (regex=\"%s\", append=%u, sc_list)",
|
|
regex.GetText().str().c_str(), append);
|
|
}
|
|
|
|
// If we aren't appending the results to this list, then clear the list
|
|
if (!append)
|
|
sc_list.Clear();
|
|
|
|
DWARFDebugInfo *info = DebugInfo();
|
|
if (!info)
|
|
return 0;
|
|
|
|
// Remember how many sc_list are in the list before we search in case we are
|
|
// appending the results to a variable list.
|
|
uint32_t original_size = sc_list.GetSize();
|
|
|
|
DIEArray offsets;
|
|
m_index->GetFunctions(regex, offsets);
|
|
|
|
llvm::DenseSet<const DWARFDebugInfoEntry *> resolved_dies;
|
|
for (DIERef ref : offsets) {
|
|
DWARFDIE die = info->GetDIE(ref);
|
|
if (!die) {
|
|
m_index->ReportInvalidDIERef(ref, regex.GetText());
|
|
continue;
|
|
}
|
|
if (resolved_dies.insert(die.GetDIE()).second)
|
|
ResolveFunction(die, include_inlines, sc_list);
|
|
}
|
|
|
|
// Return the number of variable that were appended to the list
|
|
return sc_list.GetSize() - original_size;
|
|
}
|
|
|
|
void SymbolFileDWARF::GetMangledNamesForFunction(
|
|
const std::string &scope_qualified_name,
|
|
std::vector<ConstString> &mangled_names) {
|
|
DWARFDebugInfo *info = DebugInfo();
|
|
uint32_t num_comp_units = 0;
|
|
if (info)
|
|
num_comp_units = info->GetNumUnits();
|
|
|
|
for (uint32_t i = 0; i < num_comp_units; i++) {
|
|
DWARFUnit *cu = info->GetUnitAtIndex(i);
|
|
if (cu == nullptr)
|
|
continue;
|
|
|
|
SymbolFileDWARFDwo *dwo = cu->GetDwoSymbolFile();
|
|
if (dwo)
|
|
dwo->GetMangledNamesForFunction(scope_qualified_name, mangled_names);
|
|
}
|
|
|
|
for (lldb::user_id_t uid :
|
|
m_function_scope_qualified_name_map.lookup(scope_qualified_name)) {
|
|
DWARFDIE die = GetDIE(uid);
|
|
mangled_names.push_back(ConstString(die.GetMangledName()));
|
|
}
|
|
}
|
|
|
|
uint32_t SymbolFileDWARF::FindTypes(
|
|
ConstString name, const CompilerDeclContext *parent_decl_ctx, bool append,
|
|
uint32_t max_matches,
|
|
llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files,
|
|
TypeMap &types) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
// If we aren't appending the results to this list, then clear the list
|
|
if (!append)
|
|
types.Clear();
|
|
|
|
// Make sure we haven't already searched this SymbolFile before...
|
|
if (searched_symbol_files.count(this))
|
|
return 0;
|
|
else
|
|
searched_symbol_files.insert(this);
|
|
|
|
DWARFDebugInfo *info = DebugInfo();
|
|
if (info == nullptr)
|
|
return 0;
|
|
|
|
Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
|
|
|
|
if (log) {
|
|
if (parent_decl_ctx)
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx = "
|
|
"%p (\"%s\"), append=%u, max_matches=%u, type_list)",
|
|
name.GetCString(), static_cast<const void *>(parent_decl_ctx),
|
|
parent_decl_ctx->GetName().AsCString("<NULL>"), append, max_matches);
|
|
else
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx = "
|
|
"NULL, append=%u, max_matches=%u, type_list)",
|
|
name.GetCString(), append, max_matches);
|
|
}
|
|
|
|
if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
|
|
return 0;
|
|
|
|
DIEArray die_offsets;
|
|
m_index->GetTypes(name, die_offsets);
|
|
const size_t num_die_matches = die_offsets.size();
|
|
|
|
if (num_die_matches) {
|
|
const uint32_t initial_types_size = types.GetSize();
|
|
for (size_t i = 0; i < num_die_matches; ++i) {
|
|
const DIERef &die_ref = die_offsets[i];
|
|
DWARFDIE die = GetDIE(die_ref);
|
|
|
|
if (die) {
|
|
if (!DIEInDeclContext(parent_decl_ctx, die))
|
|
continue; // The containing decl contexts don't match
|
|
|
|
Type *matching_type = ResolveType(die, true, true);
|
|
if (matching_type) {
|
|
// We found a type pointer, now find the shared pointer form our type
|
|
// list
|
|
types.InsertUnique(matching_type->shared_from_this());
|
|
if (types.GetSize() >= max_matches)
|
|
break;
|
|
}
|
|
} else {
|
|
m_index->ReportInvalidDIERef(die_ref, name.GetStringRef());
|
|
}
|
|
}
|
|
const uint32_t num_matches = types.GetSize() - initial_types_size;
|
|
if (log && num_matches) {
|
|
if (parent_decl_ctx) {
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx "
|
|
"= %p (\"%s\"), append=%u, max_matches=%u, type_list) => %u",
|
|
name.GetCString(), static_cast<const void *>(parent_decl_ctx),
|
|
parent_decl_ctx->GetName().AsCString("<NULL>"), append, max_matches,
|
|
num_matches);
|
|
} else {
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::FindTypes (sc, name=\"%s\", parent_decl_ctx "
|
|
"= NULL, append=%u, max_matches=%u, type_list) => %u",
|
|
name.GetCString(), append, max_matches, num_matches);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Next search through the reachable Clang modules. This only applies for
|
|
// DWARF objects compiled with -gmodules that haven't been processed by
|
|
// dsymutil.
|
|
if (num_die_matches < max_matches) {
|
|
UpdateExternalModuleListIfNeeded();
|
|
|
|
for (const auto &pair : m_external_type_modules) {
|
|
ModuleSP external_module_sp = pair.second;
|
|
if (external_module_sp) {
|
|
SymbolFile *sym_file = external_module_sp->GetSymbolFile();
|
|
if (sym_file) {
|
|
const uint32_t num_external_matches =
|
|
sym_file->FindTypes(name, parent_decl_ctx, append, max_matches,
|
|
searched_symbol_files, types);
|
|
if (num_external_matches)
|
|
return num_external_matches;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return num_die_matches;
|
|
}
|
|
|
|
size_t SymbolFileDWARF::FindTypes(const std::vector<CompilerContext> &context,
|
|
bool append, TypeMap &types) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
if (!append)
|
|
types.Clear();
|
|
|
|
if (context.empty())
|
|
return 0;
|
|
|
|
ConstString name = context.back().name;
|
|
|
|
if (!name)
|
|
return 0;
|
|
|
|
DIEArray die_offsets;
|
|
m_index->GetTypes(name, die_offsets);
|
|
const size_t num_die_matches = die_offsets.size();
|
|
|
|
if (num_die_matches) {
|
|
size_t num_matches = 0;
|
|
for (size_t i = 0; i < num_die_matches; ++i) {
|
|
const DIERef &die_ref = die_offsets[i];
|
|
DWARFDIE die = GetDIE(die_ref);
|
|
|
|
if (die) {
|
|
std::vector<CompilerContext> die_context;
|
|
die.GetDeclContext(die_context);
|
|
if (die_context != context)
|
|
continue;
|
|
|
|
Type *matching_type = ResolveType(die, true, true);
|
|
if (matching_type) {
|
|
// We found a type pointer, now find the shared pointer form our type
|
|
// list
|
|
types.InsertUnique(matching_type->shared_from_this());
|
|
++num_matches;
|
|
}
|
|
} else {
|
|
m_index->ReportInvalidDIERef(die_ref, name.GetStringRef());
|
|
}
|
|
}
|
|
return num_matches;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
CompilerDeclContext
|
|
SymbolFileDWARF::FindNamespace(ConstString name,
|
|
const CompilerDeclContext *parent_decl_ctx) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS));
|
|
|
|
if (log) {
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log, "SymbolFileDWARF::FindNamespace (sc, name=\"%s\")",
|
|
name.GetCString());
|
|
}
|
|
|
|
CompilerDeclContext namespace_decl_ctx;
|
|
|
|
if (!DeclContextMatchesThisSymbolFile(parent_decl_ctx))
|
|
return namespace_decl_ctx;
|
|
|
|
DWARFDebugInfo *info = DebugInfo();
|
|
if (info) {
|
|
DIEArray die_offsets;
|
|
m_index->GetNamespaces(name, die_offsets);
|
|
const size_t num_matches = die_offsets.size();
|
|
if (num_matches) {
|
|
for (size_t i = 0; i < num_matches; ++i) {
|
|
const DIERef &die_ref = die_offsets[i];
|
|
DWARFDIE die = GetDIE(die_ref);
|
|
|
|
if (die) {
|
|
if (!DIEInDeclContext(parent_decl_ctx, die))
|
|
continue; // The containing decl contexts don't match
|
|
|
|
DWARFASTParser *dwarf_ast = die.GetDWARFParser();
|
|
if (dwarf_ast) {
|
|
namespace_decl_ctx = dwarf_ast->GetDeclContextForUIDFromDWARF(die);
|
|
if (namespace_decl_ctx)
|
|
break;
|
|
}
|
|
} else {
|
|
m_index->ReportInvalidDIERef(die_ref, name.GetStringRef());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (log && namespace_decl_ctx) {
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::FindNamespace (sc, name=\"%s\") => "
|
|
"CompilerDeclContext(%p/%p) \"%s\"",
|
|
name.GetCString(),
|
|
static_cast<const void *>(namespace_decl_ctx.GetTypeSystem()),
|
|
static_cast<const void *>(namespace_decl_ctx.GetOpaqueDeclContext()),
|
|
namespace_decl_ctx.GetName().AsCString("<NULL>"));
|
|
}
|
|
|
|
return namespace_decl_ctx;
|
|
}
|
|
|
|
TypeSP SymbolFileDWARF::GetTypeForDIE(const DWARFDIE &die,
|
|
bool resolve_function_context) {
|
|
TypeSP type_sp;
|
|
if (die) {
|
|
Type *type_ptr = GetDIEToType().lookup(die.GetDIE());
|
|
if (type_ptr == nullptr) {
|
|
SymbolContextScope *scope;
|
|
if (auto *dwarf_cu = llvm::dyn_cast<DWARFCompileUnit>(die.GetCU()))
|
|
scope = GetCompUnitForDWARFCompUnit(*dwarf_cu);
|
|
else
|
|
scope = GetObjectFile()->GetModule().get();
|
|
assert(scope);
|
|
SymbolContext sc(scope);
|
|
const DWARFDebugInfoEntry *parent_die = die.GetParent().GetDIE();
|
|
while (parent_die != nullptr) {
|
|
if (parent_die->Tag() == DW_TAG_subprogram)
|
|
break;
|
|
parent_die = parent_die->GetParent();
|
|
}
|
|
SymbolContext sc_backup = sc;
|
|
if (resolve_function_context && parent_die != nullptr &&
|
|
!GetFunction(DWARFDIE(die.GetCU(), parent_die), sc))
|
|
sc = sc_backup;
|
|
|
|
type_sp = ParseType(sc, die, nullptr);
|
|
} else if (type_ptr != DIE_IS_BEING_PARSED) {
|
|
// Grab the existing type from the master types lists
|
|
type_sp = type_ptr->shared_from_this();
|
|
}
|
|
}
|
|
return type_sp;
|
|
}
|
|
|
|
DWARFDIE
|
|
SymbolFileDWARF::GetDeclContextDIEContainingDIE(const DWARFDIE &orig_die) {
|
|
if (orig_die) {
|
|
DWARFDIE die = orig_die;
|
|
|
|
while (die) {
|
|
// If this is the original DIE that we are searching for a declaration
|
|
// for, then don't look in the cache as we don't want our own decl
|
|
// context to be our decl context...
|
|
if (orig_die != die) {
|
|
switch (die.Tag()) {
|
|
case DW_TAG_compile_unit:
|
|
case DW_TAG_partial_unit:
|
|
case DW_TAG_namespace:
|
|
case DW_TAG_structure_type:
|
|
case DW_TAG_union_type:
|
|
case DW_TAG_class_type:
|
|
case DW_TAG_lexical_block:
|
|
case DW_TAG_subprogram:
|
|
return die;
|
|
case DW_TAG_inlined_subroutine: {
|
|
DWARFDIE abs_die = die.GetReferencedDIE(DW_AT_abstract_origin);
|
|
if (abs_die) {
|
|
return abs_die;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification);
|
|
if (spec_die) {
|
|
DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(spec_die);
|
|
if (decl_ctx_die)
|
|
return decl_ctx_die;
|
|
}
|
|
|
|
DWARFDIE abs_die = die.GetReferencedDIE(DW_AT_abstract_origin);
|
|
if (abs_die) {
|
|
DWARFDIE decl_ctx_die = GetDeclContextDIEContainingDIE(abs_die);
|
|
if (decl_ctx_die)
|
|
return decl_ctx_die;
|
|
}
|
|
|
|
die = die.GetParent();
|
|
}
|
|
}
|
|
return DWARFDIE();
|
|
}
|
|
|
|
Symbol *SymbolFileDWARF::GetObjCClassSymbol(ConstString objc_class_name) {
|
|
Symbol *objc_class_symbol = nullptr;
|
|
if (m_objfile_sp) {
|
|
Symtab *symtab = m_objfile_sp->GetSymtab();
|
|
if (symtab) {
|
|
objc_class_symbol = symtab->FindFirstSymbolWithNameAndType(
|
|
objc_class_name, eSymbolTypeObjCClass, Symtab::eDebugNo,
|
|
Symtab::eVisibilityAny);
|
|
}
|
|
}
|
|
return objc_class_symbol;
|
|
}
|
|
|
|
// Some compilers don't emit the DW_AT_APPLE_objc_complete_type attribute. If
|
|
// they don't then we can end up looking through all class types for a complete
|
|
// type and never find the full definition. We need to know if this attribute
|
|
// is supported, so we determine this here and cache th result. We also need to
|
|
// worry about the debug map
|
|
// DWARF file
|
|
// if we are doing darwin DWARF in .o file debugging.
|
|
bool SymbolFileDWARF::Supports_DW_AT_APPLE_objc_complete_type(DWARFUnit *cu) {
|
|
if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolCalculate) {
|
|
m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolNo;
|
|
if (cu && cu->Supports_DW_AT_APPLE_objc_complete_type())
|
|
m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes;
|
|
else {
|
|
DWARFDebugInfo *debug_info = DebugInfo();
|
|
const uint32_t num_compile_units = GetNumCompileUnits();
|
|
for (uint32_t cu_idx = 0; cu_idx < num_compile_units; ++cu_idx) {
|
|
DWARFUnit *dwarf_cu = debug_info->GetUnitAtIndex(cu_idx);
|
|
if (dwarf_cu != cu &&
|
|
dwarf_cu->Supports_DW_AT_APPLE_objc_complete_type()) {
|
|
m_supports_DW_AT_APPLE_objc_complete_type = eLazyBoolYes;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolNo &&
|
|
GetDebugMapSymfile())
|
|
return m_debug_map_symfile->Supports_DW_AT_APPLE_objc_complete_type(this);
|
|
}
|
|
return m_supports_DW_AT_APPLE_objc_complete_type == eLazyBoolYes;
|
|
}
|
|
|
|
// This function can be used when a DIE is found that is a forward declaration
|
|
// DIE and we want to try and find a type that has the complete definition.
|
|
TypeSP SymbolFileDWARF::FindCompleteObjCDefinitionTypeForDIE(
|
|
const DWARFDIE &die, ConstString type_name, bool must_be_implementation) {
|
|
|
|
TypeSP type_sp;
|
|
|
|
if (!type_name || (must_be_implementation && !GetObjCClassSymbol(type_name)))
|
|
return type_sp;
|
|
|
|
DIEArray die_offsets;
|
|
m_index->GetCompleteObjCClass(type_name, must_be_implementation, die_offsets);
|
|
|
|
const size_t num_matches = die_offsets.size();
|
|
|
|
if (num_matches) {
|
|
for (size_t i = 0; i < num_matches; ++i) {
|
|
const DIERef &die_ref = die_offsets[i];
|
|
DWARFDIE type_die = GetDIE(die_ref);
|
|
|
|
if (type_die) {
|
|
bool try_resolving_type = false;
|
|
|
|
// Don't try and resolve the DIE we are looking for with the DIE
|
|
// itself!
|
|
if (type_die != die) {
|
|
switch (type_die.Tag()) {
|
|
case DW_TAG_class_type:
|
|
case DW_TAG_structure_type:
|
|
try_resolving_type = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (try_resolving_type) {
|
|
if (must_be_implementation &&
|
|
type_die.Supports_DW_AT_APPLE_objc_complete_type())
|
|
try_resolving_type = type_die.GetAttributeValueAsUnsigned(
|
|
DW_AT_APPLE_objc_complete_type, 0);
|
|
|
|
if (try_resolving_type) {
|
|
Type *resolved_type = ResolveType(type_die, false, true);
|
|
if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) {
|
|
DEBUG_PRINTF("resolved 0x%8.8" PRIx64 " from %s to 0x%8.8" PRIx64
|
|
" (cu 0x%8.8" PRIx64 ")\n",
|
|
die.GetID(),
|
|
m_objfile_sp->GetFileSpec().GetFilename().AsCString(
|
|
"<Unknown>"),
|
|
type_die.GetID(), type_cu->GetID());
|
|
|
|
if (die)
|
|
GetDIEToType()[die.GetDIE()] = resolved_type;
|
|
type_sp = resolved_type->shared_from_this();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
m_index->ReportInvalidDIERef(die_ref, type_name.GetStringRef());
|
|
}
|
|
}
|
|
}
|
|
return type_sp;
|
|
}
|
|
|
|
// This function helps to ensure that the declaration contexts match for two
|
|
// different DIEs. Often times debug information will refer to a forward
|
|
// declaration of a type (the equivalent of "struct my_struct;". There will
|
|
// often be a declaration of that type elsewhere that has the full definition.
|
|
// When we go looking for the full type "my_struct", we will find one or more
|
|
// matches in the accelerator tables and we will then need to make sure the
|
|
// type was in the same declaration context as the original DIE. This function
|
|
// can efficiently compare two DIEs and will return true when the declaration
|
|
// context matches, and false when they don't.
|
|
bool SymbolFileDWARF::DIEDeclContextsMatch(const DWARFDIE &die1,
|
|
const DWARFDIE &die2) {
|
|
if (die1 == die2)
|
|
return true;
|
|
|
|
std::vector<DWARFDIE> decl_ctx_1;
|
|
std::vector<DWARFDIE> decl_ctx_2;
|
|
// The declaration DIE stack is a stack of the declaration context DIEs all
|
|
// the way back to the compile unit. If a type "T" is declared inside a class
|
|
// "B", and class "B" is declared inside a class "A" and class "A" is in a
|
|
// namespace "lldb", and the namespace is in a compile unit, there will be a
|
|
// stack of DIEs:
|
|
//
|
|
// [0] DW_TAG_class_type for "B"
|
|
// [1] DW_TAG_class_type for "A"
|
|
// [2] DW_TAG_namespace for "lldb"
|
|
// [3] DW_TAG_compile_unit or DW_TAG_partial_unit for the source file.
|
|
//
|
|
// We grab both contexts and make sure that everything matches all the way
|
|
// back to the compiler unit.
|
|
|
|
// First lets grab the decl contexts for both DIEs
|
|
decl_ctx_1 = die1.GetDeclContextDIEs();
|
|
decl_ctx_2 = die2.GetDeclContextDIEs();
|
|
// Make sure the context arrays have the same size, otherwise we are done
|
|
const size_t count1 = decl_ctx_1.size();
|
|
const size_t count2 = decl_ctx_2.size();
|
|
if (count1 != count2)
|
|
return false;
|
|
|
|
// Make sure the DW_TAG values match all the way back up the compile unit. If
|
|
// they don't, then we are done.
|
|
DWARFDIE decl_ctx_die1;
|
|
DWARFDIE decl_ctx_die2;
|
|
size_t i;
|
|
for (i = 0; i < count1; i++) {
|
|
decl_ctx_die1 = decl_ctx_1[i];
|
|
decl_ctx_die2 = decl_ctx_2[i];
|
|
if (decl_ctx_die1.Tag() != decl_ctx_die2.Tag())
|
|
return false;
|
|
}
|
|
#ifndef NDEBUG
|
|
|
|
// Make sure the top item in the decl context die array is always
|
|
// DW_TAG_compile_unit or DW_TAG_partial_unit. If it isn't then
|
|
// something went wrong in the DWARFDIE::GetDeclContextDIEs()
|
|
// function.
|
|
dw_tag_t cu_tag = decl_ctx_1[count1 - 1].Tag();
|
|
UNUSED_IF_ASSERT_DISABLED(cu_tag);
|
|
assert(cu_tag == DW_TAG_compile_unit || cu_tag == DW_TAG_partial_unit);
|
|
|
|
#endif
|
|
// Always skip the compile unit when comparing by only iterating up to "count
|
|
// - 1". Here we compare the names as we go.
|
|
for (i = 0; i < count1 - 1; i++) {
|
|
decl_ctx_die1 = decl_ctx_1[i];
|
|
decl_ctx_die2 = decl_ctx_2[i];
|
|
const char *name1 = decl_ctx_die1.GetName();
|
|
const char *name2 = decl_ctx_die2.GetName();
|
|
// If the string was from a DW_FORM_strp, then the pointer will often be
|
|
// the same!
|
|
if (name1 == name2)
|
|
continue;
|
|
|
|
// Name pointers are not equal, so only compare the strings if both are not
|
|
// NULL.
|
|
if (name1 && name2) {
|
|
// If the strings don't compare, we are done...
|
|
if (strcmp(name1, name2) != 0)
|
|
return false;
|
|
} else {
|
|
// One name was NULL while the other wasn't
|
|
return false;
|
|
}
|
|
}
|
|
// We made it through all of the checks and the declaration contexts are
|
|
// equal.
|
|
return true;
|
|
}
|
|
|
|
TypeSP SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(
|
|
const DWARFDeclContext &dwarf_decl_ctx) {
|
|
TypeSP type_sp;
|
|
|
|
const uint32_t dwarf_decl_ctx_count = dwarf_decl_ctx.GetSize();
|
|
if (dwarf_decl_ctx_count > 0) {
|
|
const ConstString type_name(dwarf_decl_ctx[0].name);
|
|
const dw_tag_t tag = dwarf_decl_ctx[0].tag;
|
|
|
|
if (type_name) {
|
|
Log *log(LogChannelDWARF::GetLogIfAny(DWARF_LOG_TYPE_COMPLETION |
|
|
DWARF_LOG_LOOKUPS));
|
|
if (log) {
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(tag=%"
|
|
"s, qualified-name='%s')",
|
|
DW_TAG_value_to_name(dwarf_decl_ctx[0].tag),
|
|
dwarf_decl_ctx.GetQualifiedName());
|
|
}
|
|
|
|
DIEArray die_offsets;
|
|
m_index->GetTypes(dwarf_decl_ctx, die_offsets);
|
|
const size_t num_matches = die_offsets.size();
|
|
|
|
// Get the type system that we are looking to find a type for. We will
|
|
// use this to ensure any matches we find are in a language that this
|
|
// type system supports
|
|
const LanguageType language = dwarf_decl_ctx.GetLanguage();
|
|
TypeSystem *type_system = nullptr;
|
|
if (language != eLanguageTypeUnknown) {
|
|
auto type_system_or_err = GetTypeSystemForLanguage(language);
|
|
if (auto err = type_system_or_err.takeError()) {
|
|
LLDB_LOG_ERROR(
|
|
lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_SYMBOLS),
|
|
std::move(err), "Cannot get TypeSystem for language {}",
|
|
Language::GetNameForLanguageType(language));
|
|
} else {
|
|
type_system = &type_system_or_err.get();
|
|
}
|
|
}
|
|
if (num_matches) {
|
|
for (size_t i = 0; i < num_matches; ++i) {
|
|
const DIERef &die_ref = die_offsets[i];
|
|
DWARFDIE type_die = GetDIE(die_ref);
|
|
|
|
if (type_die) {
|
|
// Make sure type_die's langauge matches the type system we are
|
|
// looking for. We don't want to find a "Foo" type from Java if we
|
|
// are looking for a "Foo" type for C, C++, ObjC, or ObjC++.
|
|
if (type_system &&
|
|
!type_system->SupportsLanguage(type_die.GetLanguage()))
|
|
continue;
|
|
bool try_resolving_type = false;
|
|
|
|
// Don't try and resolve the DIE we are looking for with the DIE
|
|
// itself!
|
|
const dw_tag_t type_tag = type_die.Tag();
|
|
// Make sure the tags match
|
|
if (type_tag == tag) {
|
|
// The tags match, lets try resolving this type
|
|
try_resolving_type = true;
|
|
} else {
|
|
// The tags don't match, but we need to watch our for a forward
|
|
// declaration for a struct and ("struct foo") ends up being a
|
|
// class ("class foo { ... };") or vice versa.
|
|
switch (type_tag) {
|
|
case DW_TAG_class_type:
|
|
// We had a "class foo", see if we ended up with a "struct foo
|
|
// { ... };"
|
|
try_resolving_type = (tag == DW_TAG_structure_type);
|
|
break;
|
|
case DW_TAG_structure_type:
|
|
// We had a "struct foo", see if we ended up with a "class foo
|
|
// { ... };"
|
|
try_resolving_type = (tag == DW_TAG_class_type);
|
|
break;
|
|
default:
|
|
// Tags don't match, don't event try to resolve using this type
|
|
// whose name matches....
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (try_resolving_type) {
|
|
DWARFDeclContext type_dwarf_decl_ctx;
|
|
type_die.GetDWARFDeclContext(type_dwarf_decl_ctx);
|
|
|
|
if (log) {
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::"
|
|
"FindDefinitionTypeForDWARFDeclContext(tag=%s, "
|
|
"qualified-name='%s') trying die=0x%8.8x (%s)",
|
|
DW_TAG_value_to_name(dwarf_decl_ctx[0].tag),
|
|
dwarf_decl_ctx.GetQualifiedName(), type_die.GetOffset(),
|
|
type_dwarf_decl_ctx.GetQualifiedName());
|
|
}
|
|
|
|
// Make sure the decl contexts match all the way up
|
|
if (dwarf_decl_ctx == type_dwarf_decl_ctx) {
|
|
Type *resolved_type = ResolveType(type_die, false);
|
|
if (resolved_type && resolved_type != DIE_IS_BEING_PARSED) {
|
|
type_sp = resolved_type->shared_from_this();
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
if (log) {
|
|
std::string qualified_name;
|
|
type_die.GetQualifiedName(qualified_name);
|
|
GetObjectFile()->GetModule()->LogMessage(
|
|
log,
|
|
"SymbolFileDWARF::"
|
|
"FindDefinitionTypeForDWARFDeclContext(tag=%s, "
|
|
"qualified-name='%s') ignoring die=0x%8.8x (%s)",
|
|
DW_TAG_value_to_name(dwarf_decl_ctx[0].tag),
|
|
dwarf_decl_ctx.GetQualifiedName(), type_die.GetOffset(),
|
|
qualified_name.c_str());
|
|
}
|
|
}
|
|
} else {
|
|
m_index->ReportInvalidDIERef(die_ref, type_name.GetStringRef());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return type_sp;
|
|
}
|
|
|
|
TypeSP SymbolFileDWARF::ParseType(const SymbolContext &sc, const DWARFDIE &die,
|
|
bool *type_is_new_ptr) {
|
|
if (!die)
|
|
return {};
|
|
|
|
auto type_system_or_err =
|
|
GetTypeSystemForLanguage(die.GetCU()->GetLanguageType());
|
|
if (auto err = type_system_or_err.takeError()) {
|
|
LLDB_LOG_ERROR(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_SYMBOLS),
|
|
std::move(err), "Unable to parse type");
|
|
return {};
|
|
}
|
|
|
|
DWARFASTParser *dwarf_ast = type_system_or_err->GetDWARFParser();
|
|
if (!dwarf_ast)
|
|
return {};
|
|
|
|
Log *log = LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_INFO);
|
|
TypeSP type_sp = dwarf_ast->ParseTypeFromDWARF(sc, die, log, type_is_new_ptr);
|
|
if (type_sp) {
|
|
GetTypeList().Insert(type_sp);
|
|
|
|
if (die.Tag() == DW_TAG_subprogram) {
|
|
std::string scope_qualified_name(GetDeclContextForUID(die.GetID())
|
|
.GetScopeQualifiedName()
|
|
.AsCString(""));
|
|
if (scope_qualified_name.size()) {
|
|
m_function_scope_qualified_name_map[scope_qualified_name].insert(
|
|
die.GetID());
|
|
}
|
|
}
|
|
}
|
|
|
|
return type_sp;
|
|
}
|
|
|
|
size_t SymbolFileDWARF::ParseTypes(const SymbolContext &sc,
|
|
const DWARFDIE &orig_die,
|
|
bool parse_siblings, bool parse_children) {
|
|
size_t types_added = 0;
|
|
DWARFDIE die = orig_die;
|
|
while (die) {
|
|
bool type_is_new = false;
|
|
if (ParseType(sc, die, &type_is_new).get()) {
|
|
if (type_is_new)
|
|
++types_added;
|
|
}
|
|
|
|
if (parse_children && die.HasChildren()) {
|
|
if (die.Tag() == DW_TAG_subprogram) {
|
|
SymbolContext child_sc(sc);
|
|
child_sc.function = sc.comp_unit->FindFunctionByUID(die.GetID()).get();
|
|
types_added += ParseTypes(child_sc, die.GetFirstChild(), true, true);
|
|
} else
|
|
types_added += ParseTypes(sc, die.GetFirstChild(), true, true);
|
|
}
|
|
|
|
if (parse_siblings)
|
|
die = die.GetSibling();
|
|
else
|
|
die.Clear();
|
|
}
|
|
return types_added;
|
|
}
|
|
|
|
size_t SymbolFileDWARF::ParseBlocksRecursive(Function &func) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
CompileUnit *comp_unit = func.GetCompileUnit();
|
|
lldbassert(comp_unit);
|
|
|
|
DWARFUnit *dwarf_cu = GetDWARFCompileUnit(comp_unit);
|
|
if (!dwarf_cu)
|
|
return 0;
|
|
|
|
size_t functions_added = 0;
|
|
const dw_offset_t function_die_offset = func.GetID();
|
|
DWARFDIE function_die = dwarf_cu->GetDIE(function_die_offset);
|
|
if (function_die) {
|
|
ParseBlocksRecursive(*comp_unit, &func.GetBlock(false), function_die,
|
|
LLDB_INVALID_ADDRESS, 0);
|
|
}
|
|
|
|
return functions_added;
|
|
}
|
|
|
|
size_t SymbolFileDWARF::ParseTypes(CompileUnit &comp_unit) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
size_t types_added = 0;
|
|
DWARFUnit *dwarf_cu = GetDWARFCompileUnit(&comp_unit);
|
|
if (dwarf_cu) {
|
|
DWARFDIE dwarf_cu_die = dwarf_cu->DIE();
|
|
if (dwarf_cu_die && dwarf_cu_die.HasChildren()) {
|
|
SymbolContext sc;
|
|
sc.comp_unit = &comp_unit;
|
|
types_added = ParseTypes(sc, dwarf_cu_die.GetFirstChild(), true, true);
|
|
}
|
|
}
|
|
|
|
return types_added;
|
|
}
|
|
|
|
size_t SymbolFileDWARF::ParseVariablesForContext(const SymbolContext &sc) {
|
|
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
|
|
if (sc.comp_unit != nullptr) {
|
|
DWARFDebugInfo *info = DebugInfo();
|
|
if (info == nullptr)
|
|
return 0;
|
|
|
|
if (sc.function) {
|
|
DWARFDIE function_die = GetDIE(sc.function->GetID());
|
|
|
|
const dw_addr_t func_lo_pc = function_die.GetAttributeValueAsAddress(
|
|
DW_AT_low_pc, LLDB_INVALID_ADDRESS);
|
|
if (func_lo_pc != LLDB_INVALID_ADDRESS) {
|
|
const size_t num_variables = ParseVariables(
|
|
sc, function_die.GetFirstChild(), func_lo_pc, true, true);
|
|
|
|
// Let all blocks know they have parse all their variables
|
|
sc.function->GetBlock(false).SetDidParseVariables(true, true);
|
|
return num_variables;
|
|
}
|
|
} else if (sc.comp_unit) {
|
|
DWARFUnit *dwarf_cu = info->GetUnitAtIndex(sc.comp_unit->GetID());
|
|
|
|
if (dwarf_cu == nullptr)
|
|
return 0;
|
|
|
|
uint32_t vars_added = 0;
|
|
VariableListSP variables(sc.comp_unit->GetVariableList(false));
|
|
|
|
if (variables.get() == nullptr) {
|
|
variables = std::make_shared<VariableList>();
|
|
sc.comp_unit->SetVariableList(variables);
|
|
|
|
DIEArray die_offsets;
|
|
m_index->GetGlobalVariables(dwarf_cu->GetNonSkeletonUnit(),
|
|
die_offsets);
|
|
const size_t num_matches = die_offsets.size();
|
|
if (num_matches) {
|
|
for (size_t i = 0; i < num_matches; ++i) {
|
|
const DIERef &die_ref = die_offsets[i];
|
|
DWARFDIE die = GetDIE(die_ref);
|
|
if (die) {
|
|
VariableSP var_sp(
|
|
ParseVariableDIE(sc, die, LLDB_INVALID_ADDRESS));
|
|
if (var_sp) {
|
|
variables->AddVariableIfUnique(var_sp);
|
|
++vars_added;
|
|
}
|
|
} else
|
|
m_index->ReportInvalidDIERef(die_ref, "");
|
|
}
|
|
}
|
|
}
|
|
return vars_added;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc,
|
|
const DWARFDIE &die,
|
|
const lldb::addr_t func_low_pc) {
|
|
if (die.GetDWARF() != this)
|
|
return die.GetDWARF()->ParseVariableDIE(sc, die, func_low_pc);
|
|
|
|
VariableSP var_sp;
|
|
if (!die)
|
|
return var_sp;
|
|
|
|
var_sp = GetDIEToVariable()[die.GetDIE()];
|
|
if (var_sp)
|
|
return var_sp; // Already been parsed!
|
|
|
|
const dw_tag_t tag = die.Tag();
|
|
ModuleSP module = GetObjectFile()->GetModule();
|
|
|
|
if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) ||
|
|
(tag == DW_TAG_formal_parameter && sc.function)) {
|
|
DWARFAttributes attributes;
|
|
const size_t num_attributes = die.GetAttributes(attributes);
|
|
DWARFDIE spec_die;
|
|
if (num_attributes > 0) {
|
|
const char *name = nullptr;
|
|
const char *mangled = nullptr;
|
|
Declaration decl;
|
|
uint32_t i;
|
|
DWARFFormValue type_die_form;
|
|
DWARFExpression location;
|
|
bool is_external = false;
|
|
bool is_artificial = false;
|
|
bool location_is_const_value_data = false;
|
|
bool has_explicit_location = false;
|
|
DWARFFormValue const_value;
|
|
Variable::RangeList scope_ranges;
|
|
// AccessType accessibility = eAccessNone;
|
|
|
|
for (i = 0; i < num_attributes; ++i) {
|
|
dw_attr_t attr = attributes.AttributeAtIndex(i);
|
|
DWARFFormValue form_value;
|
|
|
|
if (attributes.ExtractFormValueAtIndex(i, form_value)) {
|
|
switch (attr) {
|
|
case DW_AT_decl_file:
|
|
decl.SetFile(sc.comp_unit->GetSupportFiles().GetFileSpecAtIndex(
|
|
form_value.Unsigned()));
|
|
break;
|
|
case DW_AT_decl_line:
|
|
decl.SetLine(form_value.Unsigned());
|
|
break;
|
|
case DW_AT_decl_column:
|
|
decl.SetColumn(form_value.Unsigned());
|
|
break;
|
|
case DW_AT_name:
|
|
name = form_value.AsCString();
|
|
break;
|
|
case DW_AT_linkage_name:
|
|
case DW_AT_MIPS_linkage_name:
|
|
mangled = form_value.AsCString();
|
|
break;
|
|
case DW_AT_type:
|
|
type_die_form = form_value;
|
|
break;
|
|
case DW_AT_external:
|
|
is_external = form_value.Boolean();
|
|
break;
|
|
case DW_AT_const_value:
|
|
// If we have already found a DW_AT_location attribute, ignore this
|
|
// attribute.
|
|
if (!has_explicit_location) {
|
|
location_is_const_value_data = true;
|
|
// The constant value will be either a block, a data value or a
|
|
// string.
|
|
auto debug_info_data = die.GetData();
|
|
if (DWARFFormValue::IsBlockForm(form_value.Form())) {
|
|
// Retrieve the value as a block expression.
|
|
uint32_t block_offset =
|
|
form_value.BlockData() - debug_info_data.GetDataStart();
|
|
uint32_t block_length = form_value.Unsigned();
|
|
location = DWARFExpression(module, debug_info_data, die.GetCU(),
|
|
block_offset, block_length);
|
|
} else if (DWARFFormValue::IsDataForm(form_value.Form())) {
|
|
// Retrieve the value as a data expression.
|
|
uint32_t data_offset = attributes.DIEOffsetAtIndex(i);
|
|
if (auto data_length = form_value.GetFixedSize())
|
|
location =
|
|
DWARFExpression(module, debug_info_data, die.GetCU(),
|
|
data_offset, *data_length);
|
|
else {
|
|
const uint8_t *data_pointer = form_value.BlockData();
|
|
if (data_pointer) {
|
|
form_value.Unsigned();
|
|
} else if (DWARFFormValue::IsDataForm(form_value.Form())) {
|
|
// we need to get the byte size of the type later after we
|
|
// create the variable
|
|
const_value = form_value;
|
|
}
|
|
}
|
|
} else {
|
|
// Retrieve the value as a string expression.
|
|
if (form_value.Form() == DW_FORM_strp) {
|
|
uint32_t data_offset = attributes.DIEOffsetAtIndex(i);
|
|
if (auto data_length = form_value.GetFixedSize())
|
|
location =
|
|
DWARFExpression(module, debug_info_data, die.GetCU(),
|
|
data_offset, *data_length);
|
|
} else {
|
|
const char *str = form_value.AsCString();
|
|
uint32_t string_offset =
|
|
str - (const char *)debug_info_data.GetDataStart();
|
|
uint32_t string_length = strlen(str) + 1;
|
|
location =
|
|
DWARFExpression(module, debug_info_data, die.GetCU(),
|
|
string_offset, string_length);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case DW_AT_location: {
|
|
location_is_const_value_data = false;
|
|
has_explicit_location = true;
|
|
if (DWARFFormValue::IsBlockForm(form_value.Form())) {
|
|
auto data = die.GetData();
|
|
|
|
uint32_t block_offset =
|
|
form_value.BlockData() - data.GetDataStart();
|
|
uint32_t block_length = form_value.Unsigned();
|
|
location = DWARFExpression(module, data, die.GetCU(),
|
|
block_offset, block_length);
|
|
} else {
|
|
const DWARFDataExtractor &debug_loc_data = DebugLocData();
|
|
const dw_offset_t debug_loc_offset = form_value.Unsigned();
|
|
|
|
size_t loc_list_length = DWARFExpression::LocationListSize(
|
|
die.GetCU(), debug_loc_data, debug_loc_offset);
|
|
if (loc_list_length > 0) {
|
|
location = DWARFExpression(module, debug_loc_data, die.GetCU(),
|
|
debug_loc_offset, loc_list_length);
|
|
assert(func_low_pc != LLDB_INVALID_ADDRESS);
|
|
location.SetLocationListSlide(
|
|
func_low_pc -
|
|
attributes.CompileUnitAtIndex(i)->GetBaseAddress());
|
|
}
|
|
}
|
|
} break;
|
|
case DW_AT_specification:
|
|
spec_die = form_value.Reference();
|
|
break;
|
|
case DW_AT_start_scope:
|
|
// TODO: Implement this.
|
|
break;
|
|
case DW_AT_artificial:
|
|
is_artificial = form_value.Boolean();
|
|
break;
|
|
case DW_AT_accessibility:
|
|
break; // accessibility =
|
|
// DW_ACCESS_to_AccessType(form_value.Unsigned()); break;
|
|
case DW_AT_declaration:
|
|
case DW_AT_description:
|
|
case DW_AT_endianity:
|
|
case DW_AT_segment:
|
|
case DW_AT_visibility:
|
|
default:
|
|
case DW_AT_abstract_origin:
|
|
case DW_AT_sibling:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
const DWARFDIE parent_context_die = GetDeclContextDIEContainingDIE(die);
|
|
const dw_tag_t parent_tag = die.GetParent().Tag();
|
|
bool is_static_member =
|
|
(parent_tag == DW_TAG_compile_unit ||
|
|
parent_tag == DW_TAG_partial_unit) &&
|
|
(parent_context_die.Tag() == DW_TAG_class_type ||
|
|
parent_context_die.Tag() == DW_TAG_structure_type);
|
|
|
|
ValueType scope = eValueTypeInvalid;
|
|
|
|
const DWARFDIE sc_parent_die = GetParentSymbolContextDIE(die);
|
|
SymbolContextScope *symbol_context_scope = nullptr;
|
|
|
|
bool has_explicit_mangled = mangled != nullptr;
|
|
if (!mangled) {
|
|
// LLDB relies on the mangled name (DW_TAG_linkage_name or
|
|
// DW_AT_MIPS_linkage_name) to generate fully qualified names
|
|
// of global variables with commands like "frame var j". For
|
|
// example, if j were an int variable holding a value 4 and
|
|
// declared in a namespace B which in turn is contained in a
|
|
// namespace A, the command "frame var j" returns
|
|
// "(int) A::B::j = 4".
|
|
// If the compiler does not emit a linkage name, we should be
|
|
// able to generate a fully qualified name from the
|
|
// declaration context.
|
|
if ((parent_tag == DW_TAG_compile_unit ||
|
|
parent_tag == DW_TAG_partial_unit) &&
|
|
Language::LanguageIsCPlusPlus(die.GetLanguage())) {
|
|
DWARFDeclContext decl_ctx;
|
|
|
|
die.GetDWARFDeclContext(decl_ctx);
|
|
mangled = decl_ctx.GetQualifiedNameAsConstString().GetCString();
|
|
}
|
|
}
|
|
|
|
if (tag == DW_TAG_formal_parameter)
|
|
scope = eValueTypeVariableArgument;
|
|
else {
|
|
// DWARF doesn't specify if a DW_TAG_variable is a local, global
|
|
// or static variable, so we have to do a little digging:
|
|
// 1) DW_AT_linkage_name implies static lifetime (but may be missing)
|
|
// 2) An empty DW_AT_location is an (optimized-out) static lifetime var.
|
|
// 3) DW_AT_location containing a DW_OP_addr implies static lifetime.
|
|
// Clang likes to combine small global variables into the same symbol
|
|
// with locations like: DW_OP_addr(0x1000), DW_OP_constu(2), DW_OP_plus
|
|
// so we need to look through the whole expression.
|
|
bool is_static_lifetime =
|
|
has_explicit_mangled ||
|
|
(has_explicit_location && !location.IsValid());
|
|
// Check if the location has a DW_OP_addr with any address value...
|
|
lldb::addr_t location_DW_OP_addr = LLDB_INVALID_ADDRESS;
|
|
if (!location_is_const_value_data) {
|
|
bool op_error = false;
|
|
location_DW_OP_addr = location.GetLocation_DW_OP_addr(0, op_error);
|
|
if (op_error) {
|
|
StreamString strm;
|
|
location.DumpLocationForAddress(&strm, eDescriptionLevelFull, 0, 0,
|
|
nullptr);
|
|
GetObjectFile()->GetModule()->ReportError(
|
|
"0x%8.8x: %s has an invalid location: %s", die.GetOffset(),
|
|
die.GetTagAsCString(), strm.GetData());
|
|
}
|
|
if (location_DW_OP_addr != LLDB_INVALID_ADDRESS)
|
|
is_static_lifetime = true;
|
|
}
|
|
SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile();
|
|
if (debug_map_symfile)
|
|
// Set the module of the expression to the linked module
|
|
// instead of the oject file so the relocated address can be
|
|
// found there.
|
|
location.SetModule(debug_map_symfile->GetObjectFile()->GetModule());
|
|
|
|
if (is_static_lifetime) {
|
|
if (is_external)
|
|
scope = eValueTypeVariableGlobal;
|
|
else
|
|
scope = eValueTypeVariableStatic;
|
|
|
|
if (debug_map_symfile) {
|
|
// When leaving the DWARF in the .o files on darwin, when we have a
|
|
// global variable that wasn't initialized, the .o file might not
|
|
// have allocated a virtual address for the global variable. In
|
|
// this case it will have created a symbol for the global variable
|
|
// that is undefined/data and external and the value will be the
|
|
// byte size of the variable. When we do the address map in
|
|
// SymbolFileDWARFDebugMap we rely on having an address, we need to
|
|
// do some magic here so we can get the correct address for our
|
|
// global variable. The address for all of these entries will be
|
|
// zero, and there will be an undefined symbol in this object file,
|
|
// and the executable will have a matching symbol with a good
|
|
// address. So here we dig up the correct address and replace it in
|
|
// the location for the variable, and set the variable's symbol
|
|
// context scope to be that of the main executable so the file
|
|
// address will resolve correctly.
|
|
bool linked_oso_file_addr = false;
|
|
if (is_external && location_DW_OP_addr == 0) {
|
|
// we have a possible uninitialized extern global
|
|
ConstString const_name(mangled ? mangled : name);
|
|
ObjectFile *debug_map_objfile =
|
|
debug_map_symfile->GetObjectFile();
|
|
if (debug_map_objfile) {
|
|
Symtab *debug_map_symtab = debug_map_objfile->GetSymtab();
|
|
if (debug_map_symtab) {
|
|
Symbol *exe_symbol =
|
|
debug_map_symtab->FindFirstSymbolWithNameAndType(
|
|
const_name, eSymbolTypeData, Symtab::eDebugYes,
|
|
Symtab::eVisibilityExtern);
|
|
if (exe_symbol) {
|
|
if (exe_symbol->ValueIsAddress()) {
|
|
const addr_t exe_file_addr =
|
|
exe_symbol->GetAddressRef().GetFileAddress();
|
|
if (exe_file_addr != LLDB_INVALID_ADDRESS) {
|
|
if (location.Update_DW_OP_addr(exe_file_addr)) {
|
|
linked_oso_file_addr = true;
|
|
symbol_context_scope = exe_symbol;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!linked_oso_file_addr) {
|
|
// The DW_OP_addr is not zero, but it contains a .o file address
|
|
// which needs to be linked up correctly.
|
|
const lldb::addr_t exe_file_addr =
|
|
debug_map_symfile->LinkOSOFileAddress(this,
|
|
location_DW_OP_addr);
|
|
if (exe_file_addr != LLDB_INVALID_ADDRESS) {
|
|
// Update the file address for this variable
|
|
location.Update_DW_OP_addr(exe_file_addr);
|
|
} else {
|
|
// Variable didn't make it into the final executable
|
|
return var_sp;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (location_is_const_value_data)
|
|
scope = eValueTypeVariableStatic;
|
|
else {
|
|
scope = eValueTypeVariableLocal;
|
|
if (debug_map_symfile) {
|
|
// We need to check for TLS addresses that we need to fixup
|
|
if (location.ContainsThreadLocalStorage()) {
|
|
location.LinkThreadLocalStorage(
|
|
debug_map_symfile->GetObjectFile()->GetModule(),
|
|
[this, debug_map_symfile](
|
|
lldb::addr_t unlinked_file_addr) -> lldb::addr_t {
|
|
return debug_map_symfile->LinkOSOFileAddress(
|
|
this, unlinked_file_addr);
|
|
});
|
|
scope = eValueTypeVariableThreadLocal;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (symbol_context_scope == nullptr) {
|
|
switch (parent_tag) {
|
|
case DW_TAG_subprogram:
|
|
case DW_TAG_inlined_subroutine:
|
|
case DW_TAG_lexical_block:
|
|
if (sc.function) {
|
|
symbol_context_scope = sc.function->GetBlock(true).FindBlockByID(
|
|
sc_parent_die.GetID());
|
|
if (symbol_context_scope == nullptr)
|
|
symbol_context_scope = sc.function;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
symbol_context_scope = sc.comp_unit;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (symbol_context_scope) {
|
|
SymbolFileTypeSP type_sp(
|
|
new SymbolFileType(*this, GetUID(type_die_form.Reference())));
|
|
|
|
if (const_value.Form() && type_sp && type_sp->GetType())
|
|
location.UpdateValue(const_value.Unsigned(),
|
|
type_sp->GetType()->GetByteSize().getValueOr(0),
|
|
die.GetCU()->GetAddressByteSize());
|
|
|
|
var_sp = std::make_shared<Variable>(
|
|
die.GetID(), name, mangled, type_sp, scope, symbol_context_scope,
|
|
scope_ranges, &decl, location, is_external, is_artificial,
|
|
is_static_member);
|
|
|
|
var_sp->SetLocationIsConstantValueData(location_is_const_value_data);
|
|
} else {
|
|
// Not ready to parse this variable yet. It might be a global or static
|
|
// variable that is in a function scope and the function in the symbol
|
|
// context wasn't filled in yet
|
|
return var_sp;
|
|
}
|
|
}
|
|
// Cache var_sp even if NULL (the variable was just a specification or was
|
|
// missing vital information to be able to be displayed in the debugger
|
|
// (missing location due to optimization, etc)) so we don't re-parse this
|
|
// DIE over and over later...
|
|
GetDIEToVariable()[die.GetDIE()] = var_sp;
|
|
if (spec_die)
|
|
GetDIEToVariable()[spec_die.GetDIE()] = var_sp;
|
|
}
|
|
return var_sp;
|
|
}
|
|
|
|
DWARFDIE
|
|
SymbolFileDWARF::FindBlockContainingSpecification(
|
|
const DIERef &func_die_ref, dw_offset_t spec_block_die_offset) {
|
|
// Give the concrete function die specified by "func_die_offset", find the
|
|
// concrete block whose DW_AT_specification or DW_AT_abstract_origin points
|
|
// to "spec_block_die_offset"
|
|
return FindBlockContainingSpecification(DebugInfo()->GetDIE(func_die_ref),
|
|
spec_block_die_offset);
|
|
}
|
|
|
|
DWARFDIE
|
|
SymbolFileDWARF::FindBlockContainingSpecification(
|
|
const DWARFDIE &die, dw_offset_t spec_block_die_offset) {
|
|
if (die) {
|
|
switch (die.Tag()) {
|
|
case DW_TAG_subprogram:
|
|
case DW_TAG_inlined_subroutine:
|
|
case DW_TAG_lexical_block: {
|
|
if (die.GetReferencedDIE(DW_AT_specification).GetOffset() ==
|
|
spec_block_die_offset)
|
|
return die;
|
|
|
|
if (die.GetReferencedDIE(DW_AT_abstract_origin).GetOffset() ==
|
|
spec_block_die_offset)
|
|
return die;
|
|
} break;
|
|
}
|
|
|
|
// Give the concrete function die specified by "func_die_offset", find the
|
|
// concrete block whose DW_AT_specification or DW_AT_abstract_origin points
|
|
// to "spec_block_die_offset"
|
|
for (DWARFDIE child_die = die.GetFirstChild(); child_die;
|
|
child_die = child_die.GetSibling()) {
|
|
DWARFDIE result_die =
|
|
FindBlockContainingSpecification(child_die, spec_block_die_offset);
|
|
if (result_die)
|
|
return result_die;
|
|
}
|
|
}
|
|
|
|
return DWARFDIE();
|
|
}
|
|
|
|
size_t SymbolFileDWARF::ParseVariables(const SymbolContext &sc,
|
|
const DWARFDIE &orig_die,
|
|
const lldb::addr_t func_low_pc,
|
|
bool parse_siblings, bool parse_children,
|
|
VariableList *cc_variable_list) {
|
|
if (!orig_die)
|
|
return 0;
|
|
|
|
VariableListSP variable_list_sp;
|
|
|
|
size_t vars_added = 0;
|
|
DWARFDIE die = orig_die;
|
|
while (die) {
|
|
dw_tag_t tag = die.Tag();
|
|
|
|
// Check to see if we have already parsed this variable or constant?
|
|
VariableSP var_sp = GetDIEToVariable()[die.GetDIE()];
|
|
if (var_sp) {
|
|
if (cc_variable_list)
|
|
cc_variable_list->AddVariableIfUnique(var_sp);
|
|
} else {
|
|
// We haven't already parsed it, lets do that now.
|
|
if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) ||
|
|
(tag == DW_TAG_formal_parameter && sc.function)) {
|
|
if (variable_list_sp.get() == nullptr) {
|
|
DWARFDIE sc_parent_die = GetParentSymbolContextDIE(orig_die);
|
|
dw_tag_t parent_tag = sc_parent_die.Tag();
|
|
switch (parent_tag) {
|
|
case DW_TAG_compile_unit:
|
|
case DW_TAG_partial_unit:
|
|
if (sc.comp_unit != nullptr) {
|
|
variable_list_sp = sc.comp_unit->GetVariableList(false);
|
|
if (variable_list_sp.get() == nullptr) {
|
|
variable_list_sp = std::make_shared<VariableList>();
|
|
}
|
|
} else {
|
|
GetObjectFile()->GetModule()->ReportError(
|
|
"parent 0x%8.8" PRIx64 " %s with no valid compile unit in "
|
|
"symbol context for 0x%8.8" PRIx64 " %s.\n",
|
|
sc_parent_die.GetID(), sc_parent_die.GetTagAsCString(),
|
|
orig_die.GetID(), orig_die.GetTagAsCString());
|
|
}
|
|
break;
|
|
|
|
case DW_TAG_subprogram:
|
|
case DW_TAG_inlined_subroutine:
|
|
case DW_TAG_lexical_block:
|
|
if (sc.function != nullptr) {
|
|
// Check to see if we already have parsed the variables for the
|
|
// given scope
|
|
|
|
Block *block = sc.function->GetBlock(true).FindBlockByID(
|
|
sc_parent_die.GetID());
|
|
if (block == nullptr) {
|
|
// This must be a specification or abstract origin with a
|
|
// concrete block counterpart in the current function. We need
|
|
// to find the concrete block so we can correctly add the
|
|
// variable to it
|
|
const DWARFDIE concrete_block_die =
|
|
FindBlockContainingSpecification(
|
|
GetDIE(sc.function->GetID()),
|
|
sc_parent_die.GetOffset());
|
|
if (concrete_block_die)
|
|
block = sc.function->GetBlock(true).FindBlockByID(
|
|
concrete_block_die.GetID());
|
|
}
|
|
|
|
if (block != nullptr) {
|
|
const bool can_create = false;
|
|
variable_list_sp = block->GetBlockVariableList(can_create);
|
|
if (variable_list_sp.get() == nullptr) {
|
|
variable_list_sp = std::make_shared<VariableList>();
|
|
block->SetVariableList(variable_list_sp);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
GetObjectFile()->GetModule()->ReportError(
|
|
"didn't find appropriate parent DIE for variable list for "
|
|
"0x%8.8" PRIx64 " %s.\n",
|
|
orig_die.GetID(), orig_die.GetTagAsCString());
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (variable_list_sp) {
|
|
VariableSP var_sp(ParseVariableDIE(sc, die, func_low_pc));
|
|
if (var_sp) {
|
|
variable_list_sp->AddVariableIfUnique(var_sp);
|
|
if (cc_variable_list)
|
|
cc_variable_list->AddVariableIfUnique(var_sp);
|
|
++vars_added;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool skip_children = (sc.function == nullptr && tag == DW_TAG_subprogram);
|
|
|
|
if (!skip_children && parse_children && die.HasChildren()) {
|
|
vars_added += ParseVariables(sc, die.GetFirstChild(), func_low_pc, true,
|
|
true, cc_variable_list);
|
|
}
|
|
|
|
if (parse_siblings)
|
|
die = die.GetSibling();
|
|
else
|
|
die.Clear();
|
|
}
|
|
return vars_added;
|
|
}
|
|
|
|
/// Collect call graph edges present in a function DIE.
|
|
static std::vector<lldb_private::CallEdge>
|
|
CollectCallEdges(DWARFDIE function_die) {
|
|
// Check if the function has a supported call site-related attribute.
|
|
// TODO: In the future it may be worthwhile to support call_all_source_calls.
|
|
uint64_t has_call_edges =
|
|
function_die.GetAttributeValueAsUnsigned(DW_AT_call_all_calls, 0);
|
|
if (!has_call_edges)
|
|
return {};
|
|
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
|
|
LLDB_LOG(log, "CollectCallEdges: Found call site info in {0}",
|
|
function_die.GetPubname());
|
|
|
|
// Scan the DIE for TAG_call_site entries.
|
|
// TODO: A recursive scan of all blocks in the subprogram is needed in order
|
|
// to be DWARF5-compliant. This may need to be done lazily to be performant.
|
|
// For now, assume that all entries are nested directly under the subprogram
|
|
// (this is the kind of DWARF LLVM produces) and parse them eagerly.
|
|
std::vector<CallEdge> call_edges;
|
|
for (DWARFDIE child = function_die.GetFirstChild(); child.IsValid();
|
|
child = child.GetSibling()) {
|
|
if (child.Tag() != DW_TAG_call_site)
|
|
continue;
|
|
|
|
// Extract DW_AT_call_origin (the call target's DIE).
|
|
DWARFDIE call_origin = child.GetReferencedDIE(DW_AT_call_origin);
|
|
if (!call_origin.IsValid()) {
|
|
LLDB_LOG(log, "CollectCallEdges: Invalid call origin in {0}",
|
|
function_die.GetPubname());
|
|
continue;
|
|
}
|
|
|
|
// Extract DW_AT_call_return_pc (the PC the call returns to) if it's
|
|
// available. It should only ever be unavailable for tail call edges, in
|
|
// which case use LLDB_INVALID_ADDRESS.
|
|
addr_t return_pc = child.GetAttributeValueAsAddress(DW_AT_call_return_pc,
|
|
LLDB_INVALID_ADDRESS);
|
|
|
|
LLDB_LOG(log, "CollectCallEdges: Found call origin: {0} (retn-PC: {1:x})",
|
|
call_origin.GetPubname(), return_pc);
|
|
call_edges.emplace_back(call_origin.GetMangledName(), return_pc);
|
|
}
|
|
return call_edges;
|
|
}
|
|
|
|
std::vector<lldb_private::CallEdge>
|
|
SymbolFileDWARF::ParseCallEdgesInFunction(UserID func_id) {
|
|
DWARFDIE func_die = GetDIE(func_id.GetID());
|
|
if (func_die.IsValid())
|
|
return CollectCallEdges(func_die);
|
|
return {};
|
|
}
|
|
|
|
// PluginInterface protocol
|
|
ConstString SymbolFileDWARF::GetPluginName() { return GetPluginNameStatic(); }
|
|
|
|
uint32_t SymbolFileDWARF::GetPluginVersion() { return 1; }
|
|
|
|
void SymbolFileDWARF::Dump(lldb_private::Stream &s) {
|
|
SymbolFile::Dump(s);
|
|
m_index->Dump(s);
|
|
}
|
|
|
|
void SymbolFileDWARF::DumpClangAST(Stream &s) {
|
|
auto ts_or_err = GetTypeSystemForLanguage(eLanguageTypeC_plus_plus);
|
|
if (!ts_or_err)
|
|
return;
|
|
ClangASTContext *clang =
|
|
llvm::dyn_cast_or_null<ClangASTContext>(&ts_or_err.get());
|
|
if (!clang)
|
|
return;
|
|
clang->Dump(s);
|
|
}
|
|
|
|
SymbolFileDWARFDebugMap *SymbolFileDWARF::GetDebugMapSymfile() {
|
|
if (m_debug_map_symfile == nullptr && !m_debug_map_module_wp.expired()) {
|
|
lldb::ModuleSP module_sp(m_debug_map_module_wp.lock());
|
|
if (module_sp) {
|
|
m_debug_map_symfile =
|
|
(SymbolFileDWARFDebugMap *)module_sp->GetSymbolFile();
|
|
}
|
|
}
|
|
return m_debug_map_symfile;
|
|
}
|
|
|
|
DWARFExpression::LocationListFormat
|
|
SymbolFileDWARF::GetLocationListFormat() const {
|
|
if (m_data_debug_loclists.m_data.GetByteSize() > 0)
|
|
return DWARFExpression::LocLists;
|
|
return DWARFExpression::RegularLocationList;
|
|
}
|
|
|
|
SymbolFileDWARFDwp *SymbolFileDWARF::GetDwpSymbolFile() {
|
|
llvm::call_once(m_dwp_symfile_once_flag, [this]() {
|
|
ModuleSpec module_spec;
|
|
module_spec.GetFileSpec() = m_objfile_sp->GetFileSpec();
|
|
module_spec.GetSymbolFileSpec() =
|
|
FileSpec(m_objfile_sp->GetFileSpec().GetPath() + ".dwp");
|
|
|
|
FileSpecList search_paths = Target::GetDefaultDebugFileSearchPaths();
|
|
FileSpec dwp_filespec =
|
|
Symbols::LocateExecutableSymbolFile(module_spec, search_paths);
|
|
if (FileSystem::Instance().Exists(dwp_filespec)) {
|
|
m_dwp_symfile = SymbolFileDWARFDwp::Create(GetObjectFile()->GetModule(),
|
|
dwp_filespec);
|
|
}
|
|
});
|
|
return m_dwp_symfile.get();
|
|
}
|