| //===-- DWARFUnit.cpp -------------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "DWARFUnit.h" |
| |
| #include "Plugins/Language/ObjC/ObjCLanguage.h" |
| #include "lldb/Core/Module.h" |
| #include "lldb/Symbol/ObjectFile.h" |
| |
| #include "DWARFCompileUnit.h" |
| #include "DWARFDebugInfo.h" |
| #include "LogChannelDWARF.h" |
| #include "SymbolFileDWARFDwo.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| using namespace std; |
| |
| extern int g_verbose; |
| |
| DWARFUnit::DWARFUnit() {} |
| |
| DWARFUnit::~DWARFUnit() {} |
| |
| size_t DWARFUnit::ExtractDIEsIfNeeded(bool cu_die_only) { |
| return Data().ExtractDIEsIfNeeded(cu_die_only); |
| } |
| |
| DWARFDIE DWARFUnit::LookupAddress(const dw_addr_t address) { |
| return Data().LookupAddress(address); |
| } |
| |
| size_t DWARFUnit::AppendDIEsWithTag(const dw_tag_t tag, |
| DWARFDIECollection &dies, |
| uint32_t depth) const { |
| return Data().AppendDIEsWithTag(tag, dies, depth); |
| } |
| |
| bool DWARFUnit::Verify(Stream *s) const { |
| return Data().Verify(s); |
| } |
| |
| void DWARFUnit::Dump(Stream *s) const { |
| Data().Dump(s); |
| } |
| |
| lldb::user_id_t DWARFUnit::GetID() const { |
| return Data().GetID(); |
| } |
| |
| uint32_t DWARFUnit::Size() const { return IsDWARF64() ? 23 : 11; } |
| |
| dw_offset_t DWARFUnit::GetNextCompileUnitOffset() const { |
| return m_offset + (IsDWARF64() ? 12 : 4) + GetLength(); |
| } |
| |
| size_t DWARFUnit::GetDebugInfoSize() const { |
| return (IsDWARF64() ? 12 : 4) + GetLength() - Size(); |
| } |
| |
| uint32_t DWARFUnit::GetLength() const { return Data().m_length; } |
| uint16_t DWARFUnit::GetVersion() const { return Data().m_version; } |
| |
| const DWARFAbbreviationDeclarationSet *DWARFUnit::GetAbbreviations() const { |
| return Data().m_abbrevs; |
| } |
| |
| dw_offset_t DWARFUnit::GetAbbrevOffset() const { |
| return Data().GetAbbrevOffset(); |
| } |
| |
| uint8_t DWARFUnit::GetAddressByteSize() const { return Data().m_addr_size; } |
| |
| dw_addr_t DWARFUnit::GetBaseAddress() const { return Data().m_base_addr; } |
| |
| dw_addr_t DWARFUnit::GetAddrBase() const { return Data().m_addr_base; } |
| |
| dw_addr_t DWARFUnit::GetRangesBase() const { return Data().m_ranges_base; } |
| |
| void DWARFUnit::SetAddrBase(dw_addr_t addr_base, |
| dw_addr_t ranges_base, |
| dw_offset_t base_obj_offset) { |
| Data().SetAddrBase(addr_base, ranges_base, base_obj_offset); |
| } |
| |
| void DWARFUnit::ClearDIEs(bool keep_compile_unit_die) { |
| Data().ClearDIEs(keep_compile_unit_die); |
| } |
| |
| void DWARFUnit::BuildAddressRangeTable(SymbolFileDWARF *dwarf2Data, |
| DWARFDebugAranges *debug_aranges) { |
| Data().BuildAddressRangeTable(dwarf2Data, debug_aranges); |
| } |
| |
| lldb::ByteOrder DWARFUnit::GetByteOrder() const { |
| return Data().m_dwarf2Data->GetObjectFile()->GetByteOrder(); |
| } |
| |
| TypeSystem *DWARFUnit::GetTypeSystem() { |
| return Data().GetTypeSystem(); |
| } |
| |
| DWARFFormValue::FixedFormSizes DWARFUnit::GetFixedFormSizes() { |
| return DWARFFormValue::GetFixedFormSizesForAddressSize(GetAddressByteSize(), |
| IsDWARF64()); |
| } |
| |
| void DWARFUnit::SetBaseAddress(dw_addr_t base_addr) { |
| Data().m_base_addr = base_addr; |
| } |
| |
| DWARFDIE DWARFUnit::GetCompileUnitDIEOnly() { |
| return Data().GetCompileUnitDIEOnly(); |
| } |
| |
| DWARFDIE DWARFUnit::DIE() { |
| return Data().DIE(); |
| } |
| |
| bool DWARFUnit::HasDIEsParsed() const { return Data().m_die_array.size() > 1; } |
| |
| //---------------------------------------------------------------------- |
| // Compare function DWARFDebugAranges::Range structures |
| //---------------------------------------------------------------------- |
| static bool CompareDIEOffset(const DWARFDebugInfoEntry &die, |
| const dw_offset_t die_offset) { |
| return die.GetOffset() < die_offset; |
| } |
| |
| //---------------------------------------------------------------------- |
| // GetDIE() |
| // |
| // Get the DIE (Debug Information Entry) with the specified offset by |
| // first checking if the DIE is contained within this compile unit and |
| // grabbing the DIE from this compile unit. Otherwise we grab the DIE |
| // from the DWARF file. |
| //---------------------------------------------------------------------- |
| DWARFDIE |
| DWARFUnit::GetDIE(dw_offset_t die_offset) { |
| if (die_offset != DW_INVALID_OFFSET) { |
| if (GetDwoSymbolFile()) |
| return GetDwoSymbolFile()->GetCompileUnit()->GetDIE(die_offset); |
| |
| if (ContainsDIEOffset(die_offset)) { |
| ExtractDIEsIfNeeded(false); |
| DWARFDebugInfoEntry::iterator end = Data().m_die_array.end(); |
| DWARFDebugInfoEntry::iterator pos = lower_bound( |
| Data().m_die_array.begin(), end, die_offset, CompareDIEOffset); |
| if (pos != end) { |
| if (die_offset == (*pos).GetOffset()) |
| return DWARFDIE(this, &(*pos)); |
| } |
| } else { |
| // Don't specify the compile unit offset as we don't know it because the |
| // DIE belongs to |
| // a different compile unit in the same symbol file. |
| return Data().m_dwarf2Data->DebugInfo()->GetDIEForDIEOffset(die_offset); |
| } |
| } |
| return DWARFDIE(); // Not found |
| } |
| |
| static uint8_t g_default_addr_size = 4; |
| |
| uint8_t DWARFUnit::GetAddressByteSize(const DWARFUnit *cu) { |
| if (cu) |
| return cu->GetAddressByteSize(); |
| return DWARFCompileUnit::GetDefaultAddressSize(); |
| } |
| |
| bool DWARFUnit::IsDWARF64(const DWARFUnit *cu) { |
| if (cu) |
| return cu->IsDWARF64(); |
| return false; |
| } |
| |
| uint8_t DWARFUnit::GetDefaultAddressSize() { |
| return g_default_addr_size; |
| } |
| |
| void DWARFUnit::SetDefaultAddressSize(uint8_t addr_size) { |
| g_default_addr_size = addr_size; |
| } |
| |
| void *DWARFUnit::GetUserData() const { return Data().m_user_data; } |
| |
| void DWARFUnit::SetUserData(void *d) { |
| Data().SetUserData(d); |
| } |
| |
| bool DWARFUnit::Supports_DW_AT_APPLE_objc_complete_type() { |
| if (GetProducer() == eProducerLLVMGCC) |
| return false; |
| return true; |
| } |
| |
| bool DWARFUnit::DW_AT_decl_file_attributes_are_invalid() { |
| // llvm-gcc makes completely invalid decl file attributes and won't ever |
| // be fixed, so we need to know to ignore these. |
| return GetProducer() == eProducerLLVMGCC; |
| } |
| |
| bool DWARFUnit::Supports_unnamed_objc_bitfields() { |
| if (GetProducer() == eProducerClang) { |
| const uint32_t major_version = GetProducerVersionMajor(); |
| if (major_version > 425 || |
| (major_version == 425 && GetProducerVersionUpdate() >= 13)) |
| return true; |
| else |
| return false; |
| } |
| return true; // Assume all other compilers didn't have incorrect ObjC bitfield |
| // info |
| } |
| |
| SymbolFileDWARF *DWARFUnit::GetSymbolFileDWARF() const { |
| return Data().m_dwarf2Data; |
| } |
| |
| DWARFProducer DWARFUnit::GetProducer() { |
| return Data().GetProducer(); |
| } |
| |
| uint32_t DWARFUnit::GetProducerVersionMajor() { |
| return Data().GetProducerVersionMajor(); |
| } |
| |
| uint32_t DWARFUnit::GetProducerVersionMinor() { |
| return Data().GetProducerVersionMinor(); |
| } |
| |
| uint32_t DWARFUnit::GetProducerVersionUpdate() { |
| return Data().GetProducerVersionUpdate(); |
| } |
| |
| LanguageType DWARFUnit::LanguageTypeFromDWARF(uint64_t val) { |
| // Note: user languages between lo_user and hi_user |
| // must be handled explicitly here. |
| switch (val) { |
| case DW_LANG_Mips_Assembler: |
| return eLanguageTypeMipsAssembler; |
| case DW_LANG_GOOGLE_RenderScript: |
| return eLanguageTypeExtRenderScript; |
| default: |
| return static_cast<LanguageType>(val); |
| } |
| } |
| |
| LanguageType DWARFUnit::GetLanguageType() { |
| return Data().GetLanguageType(); |
| } |
| |
| bool DWARFUnit::IsDWARF64() const { return Data().m_is_dwarf64; } |
| |
| bool DWARFUnit::GetIsOptimized() { |
| return Data().GetIsOptimized(); |
| } |
| |
| SymbolFileDWARFDwo *DWARFUnit::GetDwoSymbolFile() const { |
| return Data().m_dwo_symbol_file.get(); |
| } |
| |
| dw_offset_t DWARFUnit::GetBaseObjOffset() const { |
| return Data().m_base_obj_offset; |
| } |
| |
| const DWARFDebugInfoEntry *DWARFUnit::GetCompileUnitDIEPtrOnly() { |
| return Data().GetCompileUnitDIEPtrOnly(); |
| } |
| |
| const DWARFDebugInfoEntry *DWARFUnit::DIEPtr() { |
| return Data().DIEPtr(); |
| } |
| |
| void DWARFUnit::Index(NameToDIE &func_basenames, |
| NameToDIE &func_fullnames, NameToDIE &func_methods, |
| NameToDIE &func_selectors, |
| NameToDIE &objc_class_selectors, |
| NameToDIE &globals, NameToDIE &types, |
| NameToDIE &namespaces) { |
| assert(!Data().m_dwarf2Data->GetBaseCompileUnit() && |
| "DWARFUnit associated with .dwo or .dwp " |
| "should not be indexed directly"); |
| |
| Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_LOOKUPS)); |
| |
| if (log) { |
| Data().m_dwarf2Data->GetObjectFile()->GetModule()->LogMessage( |
| log, |
| "DWARFUnit::Index() for compile unit at .debug_info[0x%8.8x]", |
| GetOffset()); |
| } |
| |
| const LanguageType cu_language = GetLanguageType(); |
| DWARFFormValue::FixedFormSizes fixed_form_sizes = |
| DWARFFormValue::GetFixedFormSizesForAddressSize(GetAddressByteSize(), |
| IsDWARF64()); |
| |
| IndexPrivate(this, cu_language, fixed_form_sizes, GetOffset(), func_basenames, |
| func_fullnames, func_methods, func_selectors, |
| objc_class_selectors, globals, types, namespaces); |
| |
| SymbolFileDWARFDwo *dwo_symbol_file = GetDwoSymbolFile(); |
| if (dwo_symbol_file) { |
| IndexPrivate( |
| dwo_symbol_file->GetCompileUnit(), cu_language, fixed_form_sizes, |
| GetOffset(), func_basenames, func_fullnames, func_methods, |
| func_selectors, objc_class_selectors, globals, types, namespaces); |
| } |
| } |
| |
| void DWARFUnit::IndexPrivate( |
| DWARFUnit *dwarf_cu, const LanguageType cu_language, |
| const DWARFFormValue::FixedFormSizes &fixed_form_sizes, |
| const dw_offset_t cu_offset, NameToDIE &func_basenames, |
| NameToDIE &func_fullnames, NameToDIE &func_methods, |
| NameToDIE &func_selectors, NameToDIE &objc_class_selectors, |
| NameToDIE &globals, NameToDIE &types, NameToDIE &namespaces) { |
| DWARFDebugInfoEntry::const_iterator pos; |
| DWARFDebugInfoEntry::const_iterator begin = |
| dwarf_cu->Data().m_die_array.begin(); |
| DWARFDebugInfoEntry::const_iterator end = dwarf_cu->Data().m_die_array.end(); |
| for (pos = begin; pos != end; ++pos) { |
| const DWARFDebugInfoEntry &die = *pos; |
| |
| const dw_tag_t tag = die.Tag(); |
| |
| switch (tag) { |
| case DW_TAG_array_type: |
| case DW_TAG_base_type: |
| case DW_TAG_class_type: |
| case DW_TAG_constant: |
| case DW_TAG_enumeration_type: |
| case DW_TAG_inlined_subroutine: |
| case DW_TAG_namespace: |
| case DW_TAG_string_type: |
| case DW_TAG_structure_type: |
| case DW_TAG_subprogram: |
| case DW_TAG_subroutine_type: |
| case DW_TAG_typedef: |
| case DW_TAG_union_type: |
| case DW_TAG_unspecified_type: |
| case DW_TAG_variable: |
| break; |
| |
| default: |
| continue; |
| } |
| |
| DWARFAttributes attributes; |
| const char *name = NULL; |
| const char *mangled_cstr = NULL; |
| bool is_declaration = false; |
| // bool is_artificial = false; |
| bool has_address = false; |
| bool has_location_or_const_value = false; |
| bool is_global_or_static_variable = false; |
| |
| DWARFFormValue specification_die_form; |
| const size_t num_attributes = |
| die.GetAttributes(dwarf_cu, fixed_form_sizes, attributes); |
| if (num_attributes > 0) { |
| for (uint32_t i = 0; i < num_attributes; ++i) { |
| dw_attr_t attr = attributes.AttributeAtIndex(i); |
| DWARFFormValue form_value; |
| switch (attr) { |
| case DW_AT_name: |
| if (attributes.ExtractFormValueAtIndex(i, form_value)) |
| name = form_value.AsCString(); |
| break; |
| |
| case DW_AT_declaration: |
| if (attributes.ExtractFormValueAtIndex(i, form_value)) |
| is_declaration = form_value.Unsigned() != 0; |
| break; |
| |
| // case DW_AT_artificial: |
| // if (attributes.ExtractFormValueAtIndex(i, |
| // form_value)) |
| // is_artificial = form_value.Unsigned() != 0; |
| // break; |
| |
| case DW_AT_MIPS_linkage_name: |
| case DW_AT_linkage_name: |
| if (attributes.ExtractFormValueAtIndex(i, form_value)) |
| mangled_cstr = form_value.AsCString(); |
| break; |
| |
| case DW_AT_low_pc: |
| case DW_AT_high_pc: |
| case DW_AT_ranges: |
| has_address = true; |
| break; |
| |
| case DW_AT_entry_pc: |
| has_address = true; |
| break; |
| |
| case DW_AT_location: |
| case DW_AT_const_value: |
| has_location_or_const_value = true; |
| if (tag == DW_TAG_variable) { |
| const DWARFDebugInfoEntry *parent_die = die.GetParent(); |
| while (parent_die != NULL) { |
| switch (parent_die->Tag()) { |
| case DW_TAG_subprogram: |
| case DW_TAG_lexical_block: |
| case DW_TAG_inlined_subroutine: |
| // Even if this is a function level static, we don't add it. We |
| // could theoretically |
| // add these if we wanted to by introspecting into the |
| // DW_AT_location and seeing |
| // if the location describes a hard coded address, but we dont |
| // want the performance |
| // penalty of that right now. |
| is_global_or_static_variable = false; |
| // if |
| // (attributes.ExtractFormValueAtIndex(dwarf2Data, |
| // i, form_value)) |
| // { |
| // // If we have valid block |
| // data, then we have location |
| // expression bytes |
| // // that are fixed (not a |
| // location list). |
| // const uint8_t *block_data = |
| // form_value.BlockData(); |
| // if (block_data) |
| // { |
| // uint32_t block_length = |
| // form_value.Unsigned(); |
| // if (block_length == 1 + |
| // attributes.CompileUnitAtIndex(i)->GetAddressByteSize()) |
| // { |
| // if (block_data[0] == |
| // DW_OP_addr) |
| // add_die = true; |
| // } |
| // } |
| // } |
| parent_die = NULL; // Terminate the while loop. |
| break; |
| |
| case DW_TAG_compile_unit: |
| is_global_or_static_variable = true; |
| parent_die = NULL; // Terminate the while loop. |
| break; |
| |
| default: |
| parent_die = |
| parent_die->GetParent(); // Keep going in the while loop. |
| break; |
| } |
| } |
| } |
| break; |
| |
| case DW_AT_specification: |
| if (attributes.ExtractFormValueAtIndex(i, form_value)) |
| specification_die_form = form_value; |
| break; |
| } |
| } |
| } |
| |
| switch (tag) { |
| case DW_TAG_subprogram: |
| if (has_address) { |
| if (name) { |
| ObjCLanguage::MethodName objc_method(name, true); |
| if (objc_method.IsValid(true)) { |
| ConstString objc_class_name_with_category( |
| objc_method.GetClassNameWithCategory()); |
| ConstString objc_selector_name(objc_method.GetSelector()); |
| ConstString objc_fullname_no_category_name( |
| objc_method.GetFullNameWithoutCategory(true)); |
| ConstString objc_class_name_no_category(objc_method.GetClassName()); |
| func_fullnames.Insert(ConstString(name), |
| DIERef(cu_offset, die.GetOffset())); |
| if (objc_class_name_with_category) |
| objc_class_selectors.Insert(objc_class_name_with_category, |
| DIERef(cu_offset, die.GetOffset())); |
| if (objc_class_name_no_category && |
| objc_class_name_no_category != objc_class_name_with_category) |
| objc_class_selectors.Insert(objc_class_name_no_category, |
| DIERef(cu_offset, die.GetOffset())); |
| if (objc_selector_name) |
| func_selectors.Insert(objc_selector_name, |
| DIERef(cu_offset, die.GetOffset())); |
| if (objc_fullname_no_category_name) |
| func_fullnames.Insert(objc_fullname_no_category_name, |
| DIERef(cu_offset, die.GetOffset())); |
| } |
| // If we have a mangled name, then the DW_AT_name attribute |
| // is usually the method name without the class or any parameters |
| const DWARFDebugInfoEntry *parent = die.GetParent(); |
| bool is_method = false; |
| if (parent) { |
| dw_tag_t parent_tag = parent->Tag(); |
| if (parent_tag == DW_TAG_class_type || |
| parent_tag == DW_TAG_structure_type) { |
| is_method = true; |
| } else { |
| if (specification_die_form.IsValid()) { |
| DWARFDIE specification_die = |
| dwarf_cu->GetSymbolFileDWARF()->DebugInfo()->GetDIE( |
| DIERef(specification_die_form)); |
| if (specification_die.GetParent().IsStructOrClass()) |
| is_method = true; |
| } |
| } |
| } |
| |
| if (is_method) |
| func_methods.Insert(ConstString(name), |
| DIERef(cu_offset, die.GetOffset())); |
| else |
| func_basenames.Insert(ConstString(name), |
| DIERef(cu_offset, die.GetOffset())); |
| |
| if (!is_method && !mangled_cstr && !objc_method.IsValid(true)) |
| func_fullnames.Insert(ConstString(name), |
| DIERef(cu_offset, die.GetOffset())); |
| } |
| if (mangled_cstr) { |
| // Make sure our mangled name isn't the same string table entry |
| // as our name. If it starts with '_', then it is ok, else compare |
| // the string to make sure it isn't the same and we don't end up |
| // with duplicate entries |
| if (name && name != mangled_cstr && |
| ((mangled_cstr[0] == '_') || |
| (::strcmp(name, mangled_cstr) != 0))) { |
| Mangled mangled(ConstString(mangled_cstr), true); |
| func_fullnames.Insert(mangled.GetMangledName(), |
| DIERef(cu_offset, die.GetOffset())); |
| ConstString demangled = mangled.GetDemangledName(cu_language); |
| if (demangled) |
| func_fullnames.Insert(demangled, |
| DIERef(cu_offset, die.GetOffset())); |
| } |
| } |
| } |
| break; |
| |
| case DW_TAG_inlined_subroutine: |
| if (has_address) { |
| if (name) |
| func_basenames.Insert(ConstString(name), |
| DIERef(cu_offset, die.GetOffset())); |
| if (mangled_cstr) { |
| // Make sure our mangled name isn't the same string table entry |
| // as our name. If it starts with '_', then it is ok, else compare |
| // the string to make sure it isn't the same and we don't end up |
| // with duplicate entries |
| if (name && name != mangled_cstr && |
| ((mangled_cstr[0] == '_') || |
| (::strcmp(name, mangled_cstr) != 0))) { |
| Mangled mangled(ConstString(mangled_cstr), true); |
| func_fullnames.Insert(mangled.GetMangledName(), |
| DIERef(cu_offset, die.GetOffset())); |
| ConstString demangled = mangled.GetDemangledName(cu_language); |
| if (demangled) |
| func_fullnames.Insert(demangled, |
| DIERef(cu_offset, die.GetOffset())); |
| } |
| } else |
| func_fullnames.Insert(ConstString(name), |
| DIERef(cu_offset, die.GetOffset())); |
| } |
| break; |
| |
| case DW_TAG_array_type: |
| case DW_TAG_base_type: |
| case DW_TAG_class_type: |
| case DW_TAG_constant: |
| case DW_TAG_enumeration_type: |
| case DW_TAG_string_type: |
| case DW_TAG_structure_type: |
| case DW_TAG_subroutine_type: |
| case DW_TAG_typedef: |
| case DW_TAG_union_type: |
| case DW_TAG_unspecified_type: |
| if (name && !is_declaration) |
| types.Insert(ConstString(name), DIERef(cu_offset, die.GetOffset())); |
| if (mangled_cstr && !is_declaration) |
| types.Insert(ConstString(mangled_cstr), |
| DIERef(cu_offset, die.GetOffset())); |
| break; |
| |
| case DW_TAG_namespace: |
| if (name) |
| namespaces.Insert(ConstString(name), |
| DIERef(cu_offset, die.GetOffset())); |
| break; |
| |
| case DW_TAG_variable: |
| if (name && has_location_or_const_value && is_global_or_static_variable) { |
| globals.Insert(ConstString(name), DIERef(cu_offset, die.GetOffset())); |
| // Be sure to include variables by their mangled and demangled |
| // names if they have any since a variable can have a basename |
| // "i", a mangled named "_ZN12_GLOBAL__N_11iE" and a demangled |
| // mangled name "(anonymous namespace)::i"... |
| |
| // Make sure our mangled name isn't the same string table entry |
| // as our name. If it starts with '_', then it is ok, else compare |
| // the string to make sure it isn't the same and we don't end up |
| // with duplicate entries |
| if (mangled_cstr && name != mangled_cstr && |
| ((mangled_cstr[0] == '_') || (::strcmp(name, mangled_cstr) != 0))) { |
| Mangled mangled(ConstString(mangled_cstr), true); |
| globals.Insert(mangled.GetMangledName(), |
| DIERef(cu_offset, die.GetOffset())); |
| ConstString demangled = mangled.GetDemangledName(cu_language); |
| if (demangled) |
| globals.Insert(demangled, DIERef(cu_offset, die.GetOffset())); |
| } |
| } |
| break; |
| |
| default: |
| continue; |
| } |
| } |
| } |