| //===-- DWARFDebugInfo.cpp --------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "SymbolFileDWARF.h" |
| |
| #include <algorithm> |
| #include <set> |
| |
| #include "lldb/Host/PosixApi.h" |
| #include "lldb/Symbol/ObjectFile.h" |
| #include "lldb/Utility/RegularExpression.h" |
| #include "lldb/Utility/Stream.h" |
| |
| #include "DWARFCompileUnit.h" |
| #include "DWARFDebugAranges.h" |
| #include "DWARFDebugAranges.h" |
| #include "DWARFDebugInfo.h" |
| #include "DWARFDebugInfoEntry.h" |
| #include "DWARFFormValue.h" |
| #include "LogChannelDWARF.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| using namespace std; |
| |
| //---------------------------------------------------------------------- |
| // Constructor |
| //---------------------------------------------------------------------- |
| DWARFDebugInfo::DWARFDebugInfo() |
| : m_dwarf2Data(NULL), m_compile_units(), m_cu_aranges_ap() {} |
| |
| //---------------------------------------------------------------------- |
| // SetDwarfData |
| //---------------------------------------------------------------------- |
| void DWARFDebugInfo::SetDwarfData(SymbolFileDWARF *dwarf2Data) { |
| m_dwarf2Data = dwarf2Data; |
| m_compile_units.clear(); |
| } |
| |
| DWARFDebugAranges &DWARFDebugInfo::GetCompileUnitAranges() { |
| if (m_cu_aranges_ap.get() == NULL && m_dwarf2Data) { |
| Log *log(LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_ARANGES)); |
| |
| m_cu_aranges_ap.reset(new DWARFDebugAranges()); |
| const DWARFDataExtractor &debug_aranges_data = |
| m_dwarf2Data->get_debug_aranges_data(); |
| if (debug_aranges_data.GetByteSize() > 0) { |
| if (log) |
| log->Printf( |
| "DWARFDebugInfo::GetCompileUnitAranges() for \"%s\" from " |
| ".debug_aranges", |
| m_dwarf2Data->GetObjectFile()->GetFileSpec().GetPath().c_str()); |
| m_cu_aranges_ap->Extract(debug_aranges_data); |
| } |
| |
| // Make a list of all CUs represented by the arange data in the file. |
| std::set<dw_offset_t> cus_with_data; |
| for (size_t n = 0; n < m_cu_aranges_ap.get()->GetNumRanges(); n++) { |
| dw_offset_t offset = m_cu_aranges_ap.get()->OffsetAtIndex(n); |
| if (offset != DW_INVALID_OFFSET) |
| cus_with_data.insert(offset); |
| } |
| |
| // Manually build arange data for everything that wasn't in the |
| // .debug_aranges table. |
| bool printed = false; |
| const size_t num_compile_units = GetNumCompileUnits(); |
| for (size_t idx = 0; idx < num_compile_units; ++idx) { |
| DWARFUnit *cu = GetCompileUnitAtIndex(idx); |
| |
| dw_offset_t offset = cu->GetOffset(); |
| if (cus_with_data.find(offset) == cus_with_data.end()) { |
| if (log) { |
| if (!printed) |
| log->Printf( |
| "DWARFDebugInfo::GetCompileUnitAranges() for \"%s\" by parsing", |
| m_dwarf2Data->GetObjectFile()->GetFileSpec().GetPath().c_str()); |
| printed = true; |
| } |
| cu->BuildAddressRangeTable(m_dwarf2Data, m_cu_aranges_ap.get()); |
| } |
| } |
| |
| const bool minimize = true; |
| m_cu_aranges_ap->Sort(minimize); |
| } |
| return *m_cu_aranges_ap.get(); |
| } |
| |
| void DWARFDebugInfo::ParseCompileUnitHeadersIfNeeded() { |
| if (m_compile_units.empty()) { |
| if (m_dwarf2Data != NULL) { |
| lldb::offset_t offset = 0; |
| DWARFUnitSP cu_sp; |
| while ((cu_sp = DWARFCompileUnit::Extract(m_dwarf2Data, &offset))) { |
| m_compile_units.push_back(cu_sp); |
| |
| offset = cu_sp->GetNextCompileUnitOffset(); |
| } |
| } |
| } |
| } |
| |
| size_t DWARFDebugInfo::GetNumCompileUnits() { |
| ParseCompileUnitHeadersIfNeeded(); |
| return m_compile_units.size(); |
| } |
| |
| DWARFUnit *DWARFDebugInfo::GetCompileUnitAtIndex(uint32_t idx) { |
| DWARFUnit *cu = NULL; |
| if (idx < GetNumCompileUnits()) |
| cu = m_compile_units[idx].get(); |
| return cu; |
| } |
| |
| bool DWARFDebugInfo::ContainsCompileUnit(const DWARFUnit *cu) const { |
| // Not a verify efficient function, but it is handy for use in assertions |
| // to make sure that a compile unit comes from a debug information file. |
| CompileUnitColl::const_iterator end_pos = m_compile_units.end(); |
| CompileUnitColl::const_iterator pos; |
| |
| for (pos = m_compile_units.begin(); pos != end_pos; ++pos) { |
| if (pos->get() == cu) |
| return true; |
| } |
| return false; |
| } |
| |
| bool DWARFDebugInfo::OffsetLessThanCompileUnitOffset( |
| dw_offset_t offset, const DWARFUnitSP &cu_sp) { |
| return offset < cu_sp->GetOffset(); |
| } |
| |
| DWARFUnit *DWARFDebugInfo::GetCompileUnit(dw_offset_t cu_offset, |
| uint32_t *idx_ptr) { |
| DWARFUnitSP cu_sp; |
| uint32_t cu_idx = DW_INVALID_INDEX; |
| if (cu_offset != DW_INVALID_OFFSET) { |
| ParseCompileUnitHeadersIfNeeded(); |
| |
| // Watch out for single compile unit executable as they are pretty common |
| const size_t num_cus = m_compile_units.size(); |
| if (num_cus == 1) { |
| if (m_compile_units[0]->GetOffset() == cu_offset) { |
| cu_sp = m_compile_units[0]; |
| cu_idx = 0; |
| } |
| } else if (num_cus) { |
| CompileUnitColl::const_iterator end_pos = m_compile_units.end(); |
| CompileUnitColl::const_iterator begin_pos = m_compile_units.begin(); |
| CompileUnitColl::const_iterator pos = std::upper_bound( |
| begin_pos, end_pos, cu_offset, OffsetLessThanCompileUnitOffset); |
| if (pos != begin_pos) { |
| --pos; |
| if ((*pos)->GetOffset() == cu_offset) { |
| cu_sp = *pos; |
| cu_idx = std::distance(begin_pos, pos); |
| } |
| } |
| } |
| } |
| if (idx_ptr) |
| *idx_ptr = cu_idx; |
| return cu_sp.get(); |
| } |
| |
| DWARFUnit *DWARFDebugInfo::GetCompileUnit(const DIERef &die_ref) { |
| if (die_ref.cu_offset == DW_INVALID_OFFSET) |
| return GetCompileUnitContainingDIEOffset(die_ref.die_offset); |
| else |
| return GetCompileUnit(die_ref.cu_offset); |
| } |
| |
| DWARFUnit * |
| DWARFDebugInfo::GetCompileUnitContainingDIEOffset(dw_offset_t die_offset) { |
| ParseCompileUnitHeadersIfNeeded(); |
| |
| DWARFUnitSP cu_sp; |
| |
| // Watch out for single compile unit executable as they are pretty common |
| const size_t num_cus = m_compile_units.size(); |
| if (num_cus == 1) { |
| if (m_compile_units[0]->ContainsDIEOffset(die_offset)) |
| return m_compile_units[0].get(); |
| } else if (num_cus) { |
| CompileUnitColl::const_iterator end_pos = m_compile_units.end(); |
| CompileUnitColl::const_iterator begin_pos = m_compile_units.begin(); |
| CompileUnitColl::const_iterator pos = std::upper_bound( |
| begin_pos, end_pos, die_offset, OffsetLessThanCompileUnitOffset); |
| if (pos != begin_pos) { |
| --pos; |
| if ((*pos)->ContainsDIEOffset(die_offset)) |
| return (*pos).get(); |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| DWARFDIE |
| DWARFDebugInfo::GetDIEForDIEOffset(dw_offset_t die_offset) { |
| DWARFUnit *cu = GetCompileUnitContainingDIEOffset(die_offset); |
| if (cu) |
| return cu->GetDIE(die_offset); |
| return DWARFDIE(); |
| } |
| |
| //---------------------------------------------------------------------- |
| // GetDIE() |
| // |
| // Get the DIE (Debug Information Entry) with the specified offset. |
| //---------------------------------------------------------------------- |
| DWARFDIE |
| DWARFDebugInfo::GetDIE(const DIERef &die_ref) { |
| DWARFUnit *cu = GetCompileUnit(die_ref); |
| if (cu) |
| return cu->GetDIE(die_ref.die_offset); |
| return DWARFDIE(); // Not found |
| } |
| |
| //---------------------------------------------------------------------- |
| // Parse |
| // |
| // Parses the .debug_info section and uses the .debug_abbrev section |
| // and various other sections in the SymbolFileDWARF class and calls the |
| // supplied callback function each time a compile unit header, or debug |
| // information entry is successfully parsed. This function can be used |
| // for different tasks such as parsing the file contents into a |
| // structured data, dumping, verifying and much more. |
| //---------------------------------------------------------------------- |
| void DWARFDebugInfo::Parse(SymbolFileDWARF *dwarf2Data, Callback callback, |
| void *userData) { |
| if (dwarf2Data) { |
| lldb::offset_t offset = 0; |
| uint32_t depth = 0; |
| DWARFDebugInfoEntry die; |
| |
| DWARFUnitSP cu; |
| while ((cu = DWARFCompileUnit::Extract(dwarf2Data, &offset))) { |
| const dw_offset_t next_cu_offset = cu->GetNextCompileUnitOffset(); |
| |
| depth = 0; |
| // Call the callback function with no DIE pointer for the compile unit |
| // and get the offset that we are to continue to parse from |
| offset = callback(dwarf2Data, cu.get(), NULL, offset, depth, userData); |
| |
| // Make sure we are within our compile unit |
| if (offset < next_cu_offset) { |
| // We are in our compile unit, parse starting at the offset |
| // we were told to parse |
| bool done = false; |
| while (!done && die.Extract(dwarf2Data, cu.get(), &offset)) { |
| // Call the callback function with DIE pointer that falls within the |
| // compile unit |
| offset = |
| callback(dwarf2Data, cu.get(), &die, offset, depth, userData); |
| |
| if (die.IsNULL()) { |
| if (depth) |
| --depth; |
| else |
| done = true; // We are done with this compile unit! |
| } else if (die.HasChildren()) |
| ++depth; |
| } |
| } |
| |
| // Make sure the offset returned is valid, and if not stop parsing. |
| // Returning DW_INVALID_OFFSET from this callback is a good way to end |
| // all parsing |
| if (!dwarf2Data->get_debug_info_data().ValidOffset(offset)) |
| break; |
| |
| // Make sure we start on a proper |
| offset = next_cu_offset; |
| } |
| } |
| } |
| |
| typedef struct DumpInfo { |
| DumpInfo(Stream *init_strm, uint32_t off, uint32_t depth) |
| : strm(init_strm), die_offset(off), recurse_depth(depth), |
| found_depth(UINT32_MAX), found_die(false), ancestors() {} |
| Stream *strm; |
| const uint32_t die_offset; |
| const uint32_t recurse_depth; |
| uint32_t found_depth; |
| bool found_die; |
| std::vector<DWARFDebugInfoEntry> ancestors; |
| |
| DISALLOW_COPY_AND_ASSIGN(DumpInfo); |
| } DumpInfo; |
| |
| //---------------------------------------------------------------------- |
| // DumpCallback |
| // |
| // A callback function for the static DWARFDebugInfo::Parse() function |
| // that gets called each time a compile unit header or debug information |
| // entry is successfully parsed. |
| // |
| // This function dump DWARF information and obey recurse depth and |
| // whether a single DIE is to be dumped (or all of the data). |
| //---------------------------------------------------------------------- |
| static dw_offset_t DumpCallback(SymbolFileDWARF *dwarf2Data, |
| DWARFUnit *cu, DWARFDebugInfoEntry *die, |
| const dw_offset_t next_offset, |
| const uint32_t curr_depth, void *userData) { |
| DumpInfo *dumpInfo = (DumpInfo *)userData; |
| Stream *s = dumpInfo->strm; |
| bool show_parents = |
| s->GetFlags().Test(DWARFDebugInfo::eDumpFlag_ShowAncestors); |
| |
| if (die) { |
| // Are we dumping everything? |
| if (dumpInfo->die_offset == DW_INVALID_OFFSET) { |
| // Yes we are dumping everything. Obey our recurse level though |
| if (curr_depth < dumpInfo->recurse_depth) |
| die->Dump(dwarf2Data, cu, *s, 0); |
| } else { |
| // We are dumping a specific DIE entry by offset |
| if (dumpInfo->die_offset == die->GetOffset()) { |
| // We found the DIE we were looking for, dump it! |
| if (show_parents) { |
| s->SetIndentLevel(0); |
| const uint32_t num_ancestors = dumpInfo->ancestors.size(); |
| if (num_ancestors > 0) { |
| for (uint32_t i = 0; i < num_ancestors - 1; ++i) { |
| dumpInfo->ancestors[i].Dump(dwarf2Data, cu, *s, 0); |
| s->IndentMore(); |
| } |
| } |
| } |
| |
| dumpInfo->found_depth = curr_depth; |
| |
| die->Dump(dwarf2Data, cu, *s, 0); |
| |
| // Note that we found the DIE we were looking for |
| dumpInfo->found_die = true; |
| |
| // Since we are dumping a single DIE, if there are no children we are |
| // done! |
| if (!die->HasChildren() || dumpInfo->recurse_depth == 0) |
| return DW_INVALID_OFFSET; // Return an invalid address to end parsing |
| } else if (dumpInfo->found_die) { |
| // Are we done with all the children? |
| if (curr_depth <= dumpInfo->found_depth) |
| return DW_INVALID_OFFSET; |
| |
| // We have already found our DIE and are printing it's children. Obey |
| // our recurse depth and return an invalid offset if we get done |
| // dumping all of the children |
| if (dumpInfo->recurse_depth == UINT32_MAX || |
| curr_depth <= dumpInfo->found_depth + dumpInfo->recurse_depth) |
| die->Dump(dwarf2Data, cu, *s, 0); |
| } else if (dumpInfo->die_offset > die->GetOffset()) { |
| if (show_parents) |
| dumpInfo->ancestors.back() = *die; |
| } |
| } |
| |
| // Keep up with our indent level |
| if (die->IsNULL()) { |
| if (show_parents) |
| dumpInfo->ancestors.pop_back(); |
| |
| if (curr_depth <= 1) |
| return cu->GetNextCompileUnitOffset(); |
| else |
| s->IndentLess(); |
| } else if (die->HasChildren()) { |
| if (show_parents) { |
| DWARFDebugInfoEntry null_die; |
| dumpInfo->ancestors.push_back(null_die); |
| } |
| s->IndentMore(); |
| } |
| } else { |
| if (cu == NULL) |
| s->PutCString("NULL - cu"); |
| // We have a compile unit, reset our indent level to zero just in case |
| s->SetIndentLevel(0); |
| |
| // See if we are dumping everything? |
| if (dumpInfo->die_offset == DW_INVALID_OFFSET) { |
| // We are dumping everything |
| if (cu) { |
| cu->Dump(s); |
| return cu->GetFirstDIEOffset(); // Return true to parse all DIEs in this |
| // Compile Unit |
| } else { |
| return DW_INVALID_OFFSET; |
| } |
| } else { |
| if (show_parents) { |
| dumpInfo->ancestors.clear(); |
| dumpInfo->ancestors.resize(1); |
| } |
| |
| // We are dumping only a single DIE possibly with it's children and |
| // we must find it's compile unit before we can dump it properly |
| if (cu && dumpInfo->die_offset < cu->GetFirstDIEOffset()) { |
| // Not found, maybe the DIE offset provided wasn't correct? |
| // *ostrm_ptr << "DIE at offset " << HEX32 << dumpInfo->die_offset << " |
| // was not found." << endl; |
| return DW_INVALID_OFFSET; |
| } else { |
| // See if the DIE is in this compile unit? |
| if (cu && dumpInfo->die_offset < cu->GetNextCompileUnitOffset()) { |
| return next_offset; |
| // // We found our compile unit that contains our DIE, just skip to |
| // dumping the requested DIE... |
| // return dumpInfo->die_offset; |
| } else { |
| // Skip to the next compile unit as the DIE isn't in the current one! |
| if (cu) { |
| return cu->GetNextCompileUnitOffset(); |
| } else { |
| return DW_INVALID_OFFSET; |
| } |
| } |
| } |
| } |
| } |
| |
| // Just return the current offset to parse the next CU or DIE entry |
| return next_offset; |
| } |
| |
| //---------------------------------------------------------------------- |
| // Dump |
| // |
| // Dump the information in the .debug_info section to the specified |
| // ostream. If die_offset is valid, a single DIE will be dumped. If the |
| // die_offset is invalid, all the DWARF information will be dumped. Both |
| // cases will obey a "recurse_depth" or how deep to traverse into the |
| // children of each DIE entry. A recurse_depth of zero will dump all |
| // compile unit headers. A recurse_depth of 1 will dump all compile unit |
| // headers and the DW_TAG_compile unit tags. A depth of 2 will also |
| // dump all types and functions. |
| //---------------------------------------------------------------------- |
| void DWARFDebugInfo::Dump(Stream *s, SymbolFileDWARF *dwarf2Data, |
| const uint32_t die_offset, |
| const uint32_t recurse_depth) { |
| DumpInfo dumpInfo(s, die_offset, recurse_depth); |
| s->PutCString(".debug_info contents"); |
| if (dwarf2Data->get_debug_info_data().GetByteSize() > 0) { |
| if (die_offset == DW_INVALID_OFFSET) |
| s->PutCString(":\n"); |
| else { |
| s->Printf(" for DIE entry at .debug_info[0x%8.8x]", die_offset); |
| if (recurse_depth != UINT32_MAX) |
| s->Printf(" recursing %u levels deep.", recurse_depth); |
| s->EOL(); |
| } |
| } else { |
| s->PutCString(": < EMPTY >\n"); |
| return; |
| } |
| DWARFDebugInfo::Parse(dwarf2Data, DumpCallback, &dumpInfo); |
| } |
| |
| //---------------------------------------------------------------------- |
| // Dump |
| // |
| // Dump the contents of this DWARFDebugInfo object as has been parsed |
| // and/or modified after it has been parsed. |
| //---------------------------------------------------------------------- |
| void DWARFDebugInfo::Dump(Stream *s, const uint32_t die_offset, |
| const uint32_t recurse_depth) { |
| DumpInfo dumpInfo(s, die_offset, recurse_depth); |
| |
| s->PutCString("Dumping .debug_info section from internal representation\n"); |
| |
| CompileUnitColl::const_iterator pos; |
| uint32_t curr_depth = 0; |
| ParseCompileUnitHeadersIfNeeded(); |
| for (pos = m_compile_units.begin(); pos != m_compile_units.end(); ++pos) { |
| DWARFUnit *cu = pos->get(); |
| DumpCallback(m_dwarf2Data, cu, NULL, 0, curr_depth, &dumpInfo); |
| |
| const DWARFDIE die = cu->DIE(); |
| if (die) |
| die.Dump(s, recurse_depth); |
| } |
| } |