//===-- 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(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; } } }