| 1 | //===-- DWARFDebugInfo.cpp ------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "SymbolFileDWARF.h" |
| 10 | |
| 11 | #include <algorithm> |
| 12 | #include <set> |
| 13 | |
| 14 | #include "lldb/Host/PosixApi.h" |
| 15 | #include "lldb/Symbol/ObjectFile.h" |
| 16 | #include "lldb/Utility/RegularExpression.h" |
| 17 | #include "lldb/Utility/Stream.h" |
| 18 | #include "llvm/Support/Casting.h" |
| 19 | |
| 20 | #include "DWARFCompileUnit.h" |
| 21 | #include "DWARFContext.h" |
| 22 | #include "DWARFDebugAranges.h" |
| 23 | #include "DWARFDebugInfo.h" |
| 24 | #include "DWARFDebugInfoEntry.h" |
| 25 | #include "DWARFFormValue.h" |
| 26 | #include "DWARFTypeUnit.h" |
| 27 | #include "LogChannelDWARF.h" |
| 28 | |
| 29 | using namespace lldb; |
| 30 | using namespace lldb_private; |
| 31 | using namespace lldb_private::plugin::dwarf; |
| 32 | |
| 33 | // Constructor |
| 34 | DWARFDebugInfo::DWARFDebugInfo(SymbolFileDWARF &dwarf, DWARFContext &context) |
| 35 | : m_dwarf(dwarf), m_context(context), m_units(), m_cu_aranges_up() {} |
| 36 | |
| 37 | const DWARFDebugAranges &DWARFDebugInfo::GetCompileUnitAranges() { |
| 38 | if (m_cu_aranges_up) |
| 39 | return *m_cu_aranges_up; |
| 40 | |
| 41 | m_cu_aranges_up = std::make_unique<DWARFDebugAranges>(); |
| 42 | const DWARFDataExtractor &debug_aranges_data = |
| 43 | m_context.getOrLoadArangesData(); |
| 44 | |
| 45 | // Extract what we can from the .debug_aranges first. |
| 46 | m_cu_aranges_up->extract(debug_aranges_data); |
| 47 | |
| 48 | // Make a list of all CUs represented by the .debug_aranges data. |
| 49 | std::set<dw_offset_t> cus_with_data; |
| 50 | for (size_t n = 0; n < m_cu_aranges_up->GetNumRanges(); n++) { |
| 51 | dw_offset_t offset = m_cu_aranges_up->OffsetAtIndex(idx: n); |
| 52 | if (offset != DW_INVALID_OFFSET) |
| 53 | cus_with_data.insert(x: offset); |
| 54 | } |
| 55 | |
| 56 | // Manually build arange data for everything that wasn't in .debug_aranges. |
| 57 | // The .debug_aranges accelerator is not guaranteed to be complete. |
| 58 | // Tools such as dsymutil can provide stronger guarantees than required by the |
| 59 | // standard. Without that guarantee, we have to iterate over every CU in the |
| 60 | // .debug_info and make sure there's a corresponding entry in the table and if |
| 61 | // not, add one for every subprogram. |
| 62 | ObjectFile *OF = m_dwarf.GetObjectFile(); |
| 63 | if (!OF || !OF->CanTrustAddressRanges()) { |
| 64 | const size_t num_units = GetNumUnits(); |
| 65 | for (size_t idx = 0; idx < num_units; ++idx) { |
| 66 | DWARFUnit *cu = GetUnitAtIndex(idx); |
| 67 | |
| 68 | dw_offset_t offset = cu->GetOffset(); |
| 69 | if (cus_with_data.find(x: offset) == cus_with_data.end()) |
| 70 | cu->BuildAddressRangeTable(debug_aranges: m_cu_aranges_up.get()); |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | const bool minimize = true; |
| 75 | m_cu_aranges_up->Sort(minimize); |
| 76 | return *m_cu_aranges_up; |
| 77 | } |
| 78 | |
| 79 | void DWARFDebugInfo::ParseUnitsFor(DIERef::Section section) { |
| 80 | DWARFDataExtractor data = section == DIERef::Section::DebugTypes |
| 81 | ? m_context.getOrLoadDebugTypesData() |
| 82 | : m_context.getOrLoadDebugInfoData(); |
| 83 | lldb::offset_t offset = 0; |
| 84 | while (data.ValidOffset(offset)) { |
| 85 | const lldb::offset_t = offset; |
| 86 | llvm::Expected<DWARFUnitSP> expected_unit_sp = |
| 87 | DWARFUnit::extract(dwarf2Data&: m_dwarf, uid: m_units.size(), debug_info: data, section, offset_ptr: &offset); |
| 88 | |
| 89 | if (!expected_unit_sp) { |
| 90 | Log *log = GetLog(mask: DWARFLog::DebugInfo); |
| 91 | if (log) |
| 92 | LLDB_LOG(log, "Unable to extract DWARFUnitHeader at {0:x}: {1}" , |
| 93 | unit_header_offset, |
| 94 | llvm::toString(expected_unit_sp.takeError())); |
| 95 | else |
| 96 | llvm::consumeError(Err: expected_unit_sp.takeError()); |
| 97 | return; |
| 98 | } |
| 99 | |
| 100 | DWARFUnitSP unit_sp = *expected_unit_sp; |
| 101 | |
| 102 | // If it didn't return an error, then it should be returning a valid Unit. |
| 103 | assert((bool)unit_sp); |
| 104 | |
| 105 | // Keep a map of DWO ID back to the skeleton units. Sometimes accelerator |
| 106 | // table lookups can cause the DWO files to be accessed before the skeleton |
| 107 | // compile unit is parsed, so we keep a map to allow us to match up the DWO |
| 108 | // file to the back to the skeleton compile units. |
| 109 | if (unit_sp->GetUnitType() == lldb_private::dwarf::DW_UT_skeleton) { |
| 110 | if (std::optional<uint64_t> unit_dwo_id = unit_sp->GetHeaderDWOId()) |
| 111 | m_dwarf5_dwo_id_to_skeleton_unit[*unit_dwo_id] = unit_sp.get(); |
| 112 | } |
| 113 | |
| 114 | m_units.push_back(x: unit_sp); |
| 115 | offset = unit_sp->GetNextUnitOffset(); |
| 116 | |
| 117 | if (auto *type_unit = llvm::dyn_cast<DWARFTypeUnit>(Val: unit_sp.get())) { |
| 118 | m_type_hash_to_unit_index.emplace_back(args: type_unit->GetTypeHash(), |
| 119 | args: unit_sp->GetID()); |
| 120 | } |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | DWARFUnit *DWARFDebugInfo::GetSkeletonUnit(DWARFUnit *dwo_unit) { |
| 125 | // If this isn't a DWO unit, don't try and find the skeleton unit. |
| 126 | if (!dwo_unit->IsDWOUnit()) |
| 127 | return nullptr; |
| 128 | |
| 129 | auto dwo_id = dwo_unit->GetDWOId(); |
| 130 | if (!dwo_id.has_value()) |
| 131 | return nullptr; |
| 132 | |
| 133 | // Parse the unit headers so that m_dwarf5_dwo_id_to_skeleton_unit is filled |
| 134 | // in with all of the DWARF5 skeleton compile units DWO IDs since it is easy |
| 135 | // to access the DWO IDs in the DWARFUnitHeader for each DWARFUnit. |
| 136 | ParseUnitHeadersIfNeeded(); |
| 137 | |
| 138 | // Find the value in our cache and return it we we find it. This cache may |
| 139 | // only contain DWARF5 units. |
| 140 | auto iter = m_dwarf5_dwo_id_to_skeleton_unit.find(Val: *dwo_id); |
| 141 | if (iter != m_dwarf5_dwo_id_to_skeleton_unit.end()) |
| 142 | return iter->second; |
| 143 | |
| 144 | // DWARF5 unit headers have the DWO ID and should have already been in the map |
| 145 | // so if it wasn't found in the above find() call, then we didn't find it and |
| 146 | // don't need to do the more expensive DWARF4 search. |
| 147 | if (dwo_unit->GetVersion() >= 5) |
| 148 | return nullptr; |
| 149 | |
| 150 | // Parse all DWO IDs from all DWARF4 and earlier compile units that have DWO |
| 151 | // IDs. It is more expensive to get the DWO IDs from DWARF4 compile units as |
| 152 | // we need to parse the unit DIE and extract the DW_AT_dwo_id or |
| 153 | // DW_AT_GNU_dwo_id attribute values, so do this only if we didn't find our |
| 154 | // match above search and only for DWARF4 and earlier compile units. |
| 155 | llvm::call_once(flag&: m_dwarf4_dwo_id_to_skeleton_unit_once_flag, F: [this]() { |
| 156 | for (uint32_t i = 0, num = GetNumUnits(); i < num; ++i) { |
| 157 | if (DWARFUnit *unit = GetUnitAtIndex(idx: i)) { |
| 158 | if (unit->GetVersion() < 5) { |
| 159 | if (std::optional<uint64_t> unit_dwo_id = unit->GetDWOId()) |
| 160 | m_dwarf4_dwo_id_to_skeleton_unit[*unit_dwo_id] = unit; |
| 161 | } |
| 162 | } |
| 163 | } |
| 164 | }); |
| 165 | |
| 166 | // Search the DWARF4 DWO results that we parsed lazily. |
| 167 | iter = m_dwarf4_dwo_id_to_skeleton_unit.find(Val: *dwo_id); |
| 168 | if (iter != m_dwarf4_dwo_id_to_skeleton_unit.end()) |
| 169 | return iter->second; |
| 170 | return nullptr; |
| 171 | } |
| 172 | |
| 173 | void DWARFDebugInfo::() { |
| 174 | llvm::call_once(flag&: m_units_once_flag, F: [&] { |
| 175 | ParseUnitsFor(section: DIERef::Section::DebugInfo); |
| 176 | ParseUnitsFor(section: DIERef::Section::DebugTypes); |
| 177 | llvm::sort(C&: m_type_hash_to_unit_index, Comp: llvm::less_first()); |
| 178 | }); |
| 179 | } |
| 180 | |
| 181 | size_t DWARFDebugInfo::GetNumUnits() { |
| 182 | ParseUnitHeadersIfNeeded(); |
| 183 | return m_units.size(); |
| 184 | } |
| 185 | |
| 186 | DWARFUnit *DWARFDebugInfo::GetUnitAtIndex(size_t idx) { |
| 187 | DWARFUnit *cu = nullptr; |
| 188 | if (idx < GetNumUnits()) |
| 189 | cu = m_units[idx].get(); |
| 190 | return cu; |
| 191 | } |
| 192 | |
| 193 | uint32_t DWARFDebugInfo::FindUnitIndex(DIERef::Section section, |
| 194 | dw_offset_t offset) { |
| 195 | ParseUnitHeadersIfNeeded(); |
| 196 | |
| 197 | // llvm::lower_bound is not used as for DIE offsets it would still return |
| 198 | // index +1 and GetOffset() returning index itself would be a special case. |
| 199 | auto pos = llvm::upper_bound( |
| 200 | Range&: m_units, Value: std::make_pair(x&: section, y&: offset), |
| 201 | C: [](const std::pair<DIERef::Section, dw_offset_t> &lhs, |
| 202 | const DWARFUnitSP &rhs) { |
| 203 | return lhs < std::make_pair(x: rhs->GetDebugSection(), y: rhs->GetOffset()); |
| 204 | }); |
| 205 | uint32_t idx = std::distance(first: m_units.begin(), last: pos); |
| 206 | if (idx == 0) |
| 207 | return DW_INVALID_INDEX; |
| 208 | return idx - 1; |
| 209 | } |
| 210 | |
| 211 | DWARFUnit *DWARFDebugInfo::GetUnitAtOffset(DIERef::Section section, |
| 212 | dw_offset_t cu_offset, |
| 213 | uint32_t *idx_ptr) { |
| 214 | uint32_t idx = FindUnitIndex(section, offset: cu_offset); |
| 215 | DWARFUnit *result = GetUnitAtIndex(idx); |
| 216 | if (result && result->GetOffset() != cu_offset) { |
| 217 | result = nullptr; |
| 218 | idx = DW_INVALID_INDEX; |
| 219 | } |
| 220 | if (idx_ptr) |
| 221 | *idx_ptr = idx; |
| 222 | return result; |
| 223 | } |
| 224 | |
| 225 | DWARFUnit * |
| 226 | DWARFDebugInfo::GetUnitContainingDIEOffset(DIERef::Section section, |
| 227 | dw_offset_t die_offset) { |
| 228 | uint32_t idx = FindUnitIndex(section, offset: die_offset); |
| 229 | DWARFUnit *result = GetUnitAtIndex(idx); |
| 230 | if (result && !result->ContainsDIEOffset(die_offset)) |
| 231 | return nullptr; |
| 232 | return result; |
| 233 | } |
| 234 | |
| 235 | const std::shared_ptr<SymbolFileDWARFDwo> &DWARFDebugInfo::GetDwpSymbolFile() { |
| 236 | return m_dwarf.GetDwpSymbolFile(); |
| 237 | } |
| 238 | |
| 239 | DWARFTypeUnit *DWARFDebugInfo::GetTypeUnitForHash(uint64_t hash) { |
| 240 | auto pos = llvm::lower_bound(Range&: m_type_hash_to_unit_index, |
| 241 | Value: std::make_pair(x&: hash, y: 0u), C: llvm::less_first()); |
| 242 | if (pos == m_type_hash_to_unit_index.end() || pos->first != hash) |
| 243 | return nullptr; |
| 244 | return llvm::cast<DWARFTypeUnit>(Val: GetUnitAtIndex(idx: pos->second)); |
| 245 | } |
| 246 | |
| 247 | bool DWARFDebugInfo::ContainsTypeUnits() { |
| 248 | ParseUnitHeadersIfNeeded(); |
| 249 | return !m_type_hash_to_unit_index.empty(); |
| 250 | } |
| 251 | |
| 252 | // GetDIE() |
| 253 | // |
| 254 | // Get the DIE (Debug Information Entry) with the specified offset. |
| 255 | DWARFDIE |
| 256 | DWARFDebugInfo::GetDIE(DIERef::Section section, dw_offset_t die_offset) { |
| 257 | if (DWARFUnit *cu = GetUnitContainingDIEOffset(section, die_offset)) |
| 258 | return cu->GetNonSkeletonUnit().GetDIE(die_offset); |
| 259 | return DWARFDIE(); // Not found |
| 260 | } |
| 261 | |