File: DWARFDebugInfo.cpp

package info (click to toggle)
llvm-toolchain-19 1%3A19.1.7-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,998,520 kB
  • sloc: cpp: 6,951,680; ansic: 1,486,157; asm: 913,598; python: 232,024; f90: 80,126; objc: 75,281; lisp: 37,276; pascal: 16,990; sh: 10,009; ml: 5,058; perl: 4,724; awk: 3,523; makefile: 3,167; javascript: 2,504; xml: 892; fortran: 664; cs: 573
file content (260 lines) | stat: -rw-r--r-- 9,535 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
//===-- DWARFDebugInfo.cpp ------------------------------------------------===//
//
// 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 <algorithm>
#include <set>

#include "lldb/Host/PosixApi.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Utility/RegularExpression.h"
#include "lldb/Utility/Stream.h"
#include "llvm/Support/Casting.h"

#include "DWARFCompileUnit.h"
#include "DWARFContext.h"
#include "DWARFDebugAranges.h"
#include "DWARFDebugInfo.h"
#include "DWARFDebugInfoEntry.h"
#include "DWARFFormValue.h"
#include "DWARFTypeUnit.h"
#include "LogChannelDWARF.h"

using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::plugin::dwarf;

// Constructor
DWARFDebugInfo::DWARFDebugInfo(SymbolFileDWARF &dwarf, DWARFContext &context)
    : m_dwarf(dwarf), m_context(context), m_units(), m_cu_aranges_up() {}

const DWARFDebugAranges &DWARFDebugInfo::GetCompileUnitAranges() {
  if (m_cu_aranges_up)
    return *m_cu_aranges_up;

  m_cu_aranges_up = std::make_unique<DWARFDebugAranges>();
  const DWARFDataExtractor &debug_aranges_data =
      m_context.getOrLoadArangesData();

  // Extract what we can from the .debug_aranges first.
  m_cu_aranges_up->extract(debug_aranges_data);

  // Make a list of all CUs represented by the .debug_aranges data.
  std::set<dw_offset_t> cus_with_data;
  for (size_t n = 0; n < m_cu_aranges_up->GetNumRanges(); n++) {
    dw_offset_t offset = m_cu_aranges_up->OffsetAtIndex(n);
    if (offset != DW_INVALID_OFFSET)
      cus_with_data.insert(offset);
  }

  // Manually build arange data for everything that wasn't in .debug_aranges.
  // The .debug_aranges accelerator is not guaranteed to be complete.
  // Tools such as dsymutil can provide stronger guarantees than required by the
  // standard. Without that guarantee, we have to iterate over every CU in the
  // .debug_info and make sure there's a corresponding entry in the table and if
  // not, add one for every subprogram.
  ObjectFile *OF = m_dwarf.GetObjectFile();
  if (!OF || !OF->CanTrustAddressRanges()) {
    const size_t num_units = GetNumUnits();
    for (size_t idx = 0; idx < num_units; ++idx) {
      DWARFUnit *cu = GetUnitAtIndex(idx);

      dw_offset_t offset = cu->GetOffset();
      if (cus_with_data.find(offset) == cus_with_data.end())
        cu->BuildAddressRangeTable(m_cu_aranges_up.get());
    }
  }

  const bool minimize = true;
  m_cu_aranges_up->Sort(minimize);
  return *m_cu_aranges_up;
}

void DWARFDebugInfo::ParseUnitsFor(DIERef::Section section) {
  DWARFDataExtractor data = section == DIERef::Section::DebugTypes
                                ? m_context.getOrLoadDebugTypesData()
                                : m_context.getOrLoadDebugInfoData();
  lldb::offset_t offset = 0;
  while (data.ValidOffset(offset)) {
    const lldb::offset_t unit_header_offset = offset;
    llvm::Expected<DWARFUnitSP> expected_unit_sp =
        DWARFUnit::extract(m_dwarf, m_units.size(), data, section, &offset);

    if (!expected_unit_sp) {
      Log *log = GetLog(DWARFLog::DebugInfo);
      if (log)
        LLDB_LOG(log, "Unable to extract DWARFUnitHeader at {0:x}: {1}",
                 unit_header_offset,
                 llvm::toString(expected_unit_sp.takeError()));
      else
        llvm::consumeError(expected_unit_sp.takeError());
      return;
    }

    DWARFUnitSP unit_sp = *expected_unit_sp;

    // If it didn't return an error, then it should be returning a valid Unit.
    assert((bool)unit_sp);

    // Keep a map of DWO ID back to the skeleton units. Sometimes accelerator
    // table lookups can cause the DWO files to be accessed before the skeleton
    // compile unit is parsed, so we keep a map to allow us to match up the DWO
    // file to the back to the skeleton compile units.
    if (unit_sp->GetUnitType() == lldb_private::dwarf::DW_UT_skeleton) {
      if (std::optional<uint64_t> unit_dwo_id = unit_sp->GetHeaderDWOId())
        m_dwarf5_dwo_id_to_skeleton_unit[*unit_dwo_id] = unit_sp.get();
    }

    m_units.push_back(unit_sp);
    offset = unit_sp->GetNextUnitOffset();

    if (auto *type_unit = llvm::dyn_cast<DWARFTypeUnit>(unit_sp.get())) {
      m_type_hash_to_unit_index.emplace_back(type_unit->GetTypeHash(),
                                             unit_sp->GetID());
    }
  }
}

DWARFUnit *DWARFDebugInfo::GetSkeletonUnit(DWARFUnit *dwo_unit) {
  // If this isn't a DWO unit, don't try and find the skeleton unit.
  if (!dwo_unit->IsDWOUnit())
    return nullptr;

  auto dwo_id = dwo_unit->GetDWOId();
  if (!dwo_id.has_value())
    return nullptr;

  // Parse the unit headers so that m_dwarf5_dwo_id_to_skeleton_unit is filled
  // in with all of the DWARF5 skeleton compile units DWO IDs since it is easy
  // to access the DWO IDs in the DWARFUnitHeader for each DWARFUnit.
  ParseUnitHeadersIfNeeded();

  // Find the value in our cache and return it we we find it. This cache may
  // only contain DWARF5 units.
  auto iter = m_dwarf5_dwo_id_to_skeleton_unit.find(*dwo_id);
  if (iter != m_dwarf5_dwo_id_to_skeleton_unit.end())
    return iter->second;

  // DWARF5 unit headers have the DWO ID and should have already been in the map
  // so if it wasn't found in the above find() call, then we didn't find it and
  // don't need to do the more expensive DWARF4 search.
  if (dwo_unit->GetVersion() >= 5)
    return nullptr;

  // Parse all DWO IDs from all DWARF4 and earlier compile units that have DWO
  // IDs. It is more expensive to get the DWO IDs from DWARF4 compile units as
  // we need to parse the unit DIE and extract the DW_AT_dwo_id or
  // DW_AT_GNU_dwo_id attribute values, so do this only if we didn't find our
  // match above search and only for DWARF4 and earlier compile units.
  llvm::call_once(m_dwarf4_dwo_id_to_skeleton_unit_once_flag, [this]() {
    for (uint32_t i = 0, num = GetNumUnits(); i < num; ++i) {
      if (DWARFUnit *unit = GetUnitAtIndex(i)) {
        if (unit->GetVersion() < 5) {
          if (std::optional<uint64_t> unit_dwo_id = unit->GetDWOId())
            m_dwarf4_dwo_id_to_skeleton_unit[*unit_dwo_id] = unit;
        }
      }
    }
  });

  // Search the DWARF4 DWO results that we parsed lazily.
  iter = m_dwarf4_dwo_id_to_skeleton_unit.find(*dwo_id);
  if (iter != m_dwarf4_dwo_id_to_skeleton_unit.end())
    return iter->second;
  return nullptr;
}

void DWARFDebugInfo::ParseUnitHeadersIfNeeded() {
  llvm::call_once(m_units_once_flag, [&] {
    ParseUnitsFor(DIERef::Section::DebugInfo);
    ParseUnitsFor(DIERef::Section::DebugTypes);
    llvm::sort(m_type_hash_to_unit_index, llvm::less_first());
  });
}

size_t DWARFDebugInfo::GetNumUnits() {
  ParseUnitHeadersIfNeeded();
  return m_units.size();
}

DWARFUnit *DWARFDebugInfo::GetUnitAtIndex(size_t idx) {
  DWARFUnit *cu = nullptr;
  if (idx < GetNumUnits())
    cu = m_units[idx].get();
  return cu;
}

uint32_t DWARFDebugInfo::FindUnitIndex(DIERef::Section section,
                                       dw_offset_t offset) {
  ParseUnitHeadersIfNeeded();

  // llvm::lower_bound is not used as for DIE offsets it would still return
  // index +1 and GetOffset() returning index itself would be a special case.
  auto pos = llvm::upper_bound(
      m_units, std::make_pair(section, offset),
      [](const std::pair<DIERef::Section, dw_offset_t> &lhs,
         const DWARFUnitSP &rhs) {
        return lhs < std::make_pair(rhs->GetDebugSection(), rhs->GetOffset());
      });
  uint32_t idx = std::distance(m_units.begin(), pos);
  if (idx == 0)
    return DW_INVALID_INDEX;
  return idx - 1;
}

DWARFUnit *DWARFDebugInfo::GetUnitAtOffset(DIERef::Section section,
                                           dw_offset_t cu_offset,
                                           uint32_t *idx_ptr) {
  uint32_t idx = FindUnitIndex(section, cu_offset);
  DWARFUnit *result = GetUnitAtIndex(idx);
  if (result && result->GetOffset() != cu_offset) {
    result = nullptr;
    idx = DW_INVALID_INDEX;
  }
  if (idx_ptr)
    *idx_ptr = idx;
  return result;
}

DWARFUnit *
DWARFDebugInfo::GetUnitContainingDIEOffset(DIERef::Section section,
                                           dw_offset_t die_offset) {
  uint32_t idx = FindUnitIndex(section, die_offset);
  DWARFUnit *result = GetUnitAtIndex(idx);
  if (result && !result->ContainsDIEOffset(die_offset))
    return nullptr;
  return result;
}

const std::shared_ptr<SymbolFileDWARFDwo> &DWARFDebugInfo::GetDwpSymbolFile() {
  return m_dwarf.GetDwpSymbolFile();
}

DWARFTypeUnit *DWARFDebugInfo::GetTypeUnitForHash(uint64_t hash) {
  auto pos = llvm::lower_bound(m_type_hash_to_unit_index,
                               std::make_pair(hash, 0u), llvm::less_first());
  if (pos == m_type_hash_to_unit_index.end() || pos->first != hash)
    return nullptr;
  return llvm::cast<DWARFTypeUnit>(GetUnitAtIndex(pos->second));
}

bool DWARFDebugInfo::ContainsTypeUnits() {
  ParseUnitHeadersIfNeeded();
  return !m_type_hash_to_unit_index.empty();
}

// GetDIE()
//
// Get the DIE (Debug Information Entry) with the specified offset.
DWARFDIE
DWARFDebugInfo::GetDIE(DIERef::Section section, dw_offset_t die_offset) {
  if (DWARFUnit *cu = GetUnitContainingDIEOffset(section, die_offset))
    return cu->GetNonSkeletonUnit().GetDIE(die_offset);
  return DWARFDIE(); // Not found
}