Files
clang-p2996/lldb/source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp
Pavel Labath 7cfa74fc69 [lldb/DWARF] Trust CU DW_AT_low/high_pc information when building address tables
Summary:
The code in DWARFCompileUnit::BuildAddressRangeTable tries hard to avoid
relying on DW_AT_low/high_pc for compile unit range information, and
this logic is a big cause of llvm/lldb divergence in the lowest layers
of dwarf parsing code.

The implicit assumption in that code is that this information (as opposed to
DW_AT_ranges) is unreliable. However, I have not been able to verify
that assumption. It is definitely not true for all present-day
compilers (gcc, clang, icc), and it was also not the case for the
historic compilers that I have been able to get a hold of (thanks Matt
Godbolt).

All compiler included in my research either produced correct
DW_AT_ranges or .debug_aranges entries, or they produced no DW_AT_hi/lo
pc at all. The detailed findings are:
- gcc >= 4.4: produces DW_AT_ranges and .debug_aranges
- 4.1 <= gcc < 4.4: no DW_AT_ranges, no DW_AT_high_pc, .debug_aranges
  present. The upper version range here is uncertain as godbolt.org does
  not have intermediate versions.
- gcc < 4.1: no versions on godbolt.org
- clang >= 3.5: produces DW_AT_ranges, and (optionally) .debug_aranges
- 3.4 <= clang < 3.5: no DW_AT_ranges, no DW_AT_high_pc, .debug_aranges
  present.
- clang <= 3.3: no DW_AT_ranges, no DW_AT_high_pc, no .debug_aranges
- icc >= 16.0.1: produces DW_AT_ranges
- icc < 16.0.1: no functional versions on godbolt.org (some are present
  but fail to compile)

Based on this analysis, I believe it is safe to start trusting
DW_AT_low/high_pc information in dwarf as well as remove the code for
manually reconstructing range information by traversing the DIE
structure, and just keep the line table fallback. The only compilers
where this will change behavior are pre-3.4 clangs, which are almost 7
years old now. However, the functionality should remain unchanged
because we will be able to reconstruct this information from the line
table, which seems to be needed for some line-tables-only scenarios
anyway (haven't researched this too much, but at least some compilers
seem to emit DW_AT_ranges even in these situations).

In addition, benchmarks showed that for these compilers computing the
ranges via line tables is noticably faster than doing so via the DIE
tree.

Other advantages include simplifying the code base, removing some
untested code (the only test changes are recent tests with overly
reduced synthetic dwarf), and increasing llvm convergence.

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D78489
2020-04-23 16:12:41 +02:00

102 lines
3.9 KiB
C++

//===-- DWARFCompileUnit.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 "DWARFCompileUnit.h"
#include "DWARFDebugAranges.h"
#include "SymbolFileDWARFDebugMap.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/LineTable.h"
#include "lldb/Utility/Stream.h"
using namespace lldb;
using namespace lldb_private;
void DWARFCompileUnit::Dump(Stream *s) const {
s->Printf("0x%8.8x: Compile Unit: length = 0x%8.8x, version = 0x%4.4x, "
"abbr_offset = 0x%8.8x, addr_size = 0x%2.2x (next CU at "
"{0x%8.8x})\n",
GetOffset(), GetLength(), GetVersion(), GetAbbrevOffset(),
GetAddressByteSize(), GetNextUnitOffset());
}
void DWARFCompileUnit::BuildAddressRangeTable(
DWARFDebugAranges *debug_aranges) {
// This function is usually called if there in no .debug_aranges section in
// order to produce a compile unit level set of address ranges that is
// accurate.
size_t num_debug_aranges = debug_aranges->GetNumRanges();
// First get the compile unit DIE only and check contains ranges information.
const DWARFDebugInfoEntry *die = GetUnitDIEPtrOnly();
const dw_offset_t cu_offset = GetOffset();
if (die) {
DWARFRangeList ranges;
const size_t num_ranges =
die->GetAttributeAddressRanges(this, ranges, /*check_hi_lo_pc=*/true);
if (num_ranges > 0) {
for (size_t i = 0; i < num_ranges; ++i) {
const DWARFRangeList::Entry &range = ranges.GetEntryRef(i);
debug_aranges->AppendRange(cu_offset, range.GetRangeBase(),
range.GetRangeEnd());
}
return;
}
}
if (debug_aranges->GetNumRanges() == num_debug_aranges) {
// We got nothing from the debug info, maybe we have a line tables only
// situation. Check the line tables and build the arange table from this.
SymbolContext sc;
sc.comp_unit = m_dwarf.GetCompUnitForDWARFCompUnit(*this);
if (sc.comp_unit) {
SymbolFileDWARFDebugMap *debug_map_sym_file =
m_dwarf.GetDebugMapSymfile();
if (debug_map_sym_file == nullptr) {
if (LineTable *line_table = sc.comp_unit->GetLineTable()) {
LineTable::FileAddressRanges file_ranges;
const bool append = true;
const size_t num_ranges =
line_table->GetContiguousFileAddressRanges(file_ranges, append);
for (uint32_t idx = 0; idx < num_ranges; ++idx) {
const LineTable::FileAddressRanges::Entry &range =
file_ranges.GetEntryRef(idx);
debug_aranges->AppendRange(cu_offset, range.GetRangeBase(),
range.GetRangeEnd());
}
}
} else
debug_map_sym_file->AddOSOARanges(&m_dwarf, debug_aranges);
}
}
if (debug_aranges->GetNumRanges() == num_debug_aranges) {
// We got nothing from the functions, maybe we have a line tables only
// situation. Check the line tables and build the arange table from this.
SymbolContext sc;
sc.comp_unit = m_dwarf.GetCompUnitForDWARFCompUnit(*this);
if (sc.comp_unit) {
if (LineTable *line_table = sc.comp_unit->GetLineTable()) {
LineTable::FileAddressRanges file_ranges;
const bool append = true;
const size_t num_ranges =
line_table->GetContiguousFileAddressRanges(file_ranges, append);
for (uint32_t idx = 0; idx < num_ranges; ++idx) {
const LineTable::FileAddressRanges::Entry &range =
file_ranges.GetEntryRef(idx);
debug_aranges->AppendRange(GetOffset(), range.GetRangeBase(),
range.GetRangeEnd());
}
}
}
}
}