diff options
author | Gabriele Svelto <gsvelto@mozilla.com> | 2018-08-04 00:59:34 +0200 |
---|---|---|
committer | Ted Mielczarek <ted.mielczarek@gmail.com> | 2018-08-13 19:12:00 +0000 |
commit | 16e08520e6027df4bf1934abbfd5e1a088ffb69c (patch) | |
tree | e80f0e6a8b46b31a9dd56c12dddebb51e1895795 /src/common/dwarf_cu_to_module.cc | |
parent | Set new ARM64 context flags (diff) | |
download | breakpad-16e08520e6027df4bf1934abbfd5e1a088ffb69c.tar.xz |
Add support for parsing the DW_AT_ranges attributes
This enables the DWARF reader to properly parse DW_AT_ranges attributes
in compilation units and functions. Code covered by a function is now
represented by a vector of ranges instead of a single contiguous range
and DW_AT_ranges entries are used to populate it. All the code and tests
that assumed functions to be contiguous entities has been updated to
reflect the change. DW_AT_ranges attributes found in compilation units
are parsed but no data is generated for them as it is not currently needed.
BUG=754
Change-Id: I310391b525aaba0dd329f1e3187486f2e0c6d442
Reviewed-on: https://chromium-review.googlesource.com/1124721
Reviewed-by: Ted Mielczarek <ted.mielczarek@gmail.com>
Diffstat (limited to 'src/common/dwarf_cu_to_module.cc')
-rw-r--r-- | src/common/dwarf_cu_to_module.cc | 240 |
1 files changed, 179 insertions, 61 deletions
diff --git a/src/common/dwarf_cu_to_module.cc b/src/common/dwarf_cu_to_module.cc index 38fc4c16..f5a03b42 100644 --- a/src/common/dwarf_cu_to_module.cc +++ b/src/common/dwarf_cu_to_module.cc @@ -44,6 +44,7 @@ #include <stdio.h> #include <algorithm> +#include <numeric> #include <utility> #include "common/dwarf_line_to_module.h" @@ -51,6 +52,7 @@ namespace google_breakpad { +using std::accumulate; using std::map; using std::pair; using std::sort; @@ -167,10 +169,15 @@ bool DwarfCUToModule::FileContext::IsUnhandledInterCUReference( // parsing. This is for data shared across the CU's entire DIE tree, // and parameters from the code invoking the CU parser. struct DwarfCUToModule::CUContext { - CUContext(FileContext *file_context_arg, WarningReporter *reporter_arg) + CUContext(FileContext *file_context_arg, WarningReporter *reporter_arg, + RangesHandler *ranges_handler_arg) : file_context(file_context_arg), reporter(reporter_arg), - language(Language::CPlusPlus) {} + ranges_handler(ranges_handler_arg), + language(Language::CPlusPlus), + low_pc(0), + high_pc(0), + ranges(0) {} ~CUContext() { for (vector<Module::Function *>::iterator it = functions.begin(); @@ -185,9 +192,19 @@ struct DwarfCUToModule::CUContext { // For printing error messages. WarningReporter *reporter; + // For reading ranges from the .debug_ranges section + RangesHandler *ranges_handler; + // The source language of this compilation unit. const Language *language; + // Addresses covered by this CU. If high_pc_ is non-zero then the CU covers + // low_pc to high_pc, otherwise ranges is non-zero and low_pc represents + // the base address of the ranges covered by the CU. + uint64 low_pc; + uint64 high_pc; + uint64 ranges; + // The functions defined in this compilation unit. We accumulate // them here during parsing. Then, in DwarfCUToModule::Finish, we // assign them lines and add them to file_context->module. @@ -445,7 +462,7 @@ class DwarfCUToModule::FuncHandler: public GenericDIEHandler { uint64 offset) : GenericDIEHandler(cu_context, parent_context, offset), low_pc_(0), high_pc_(0), high_pc_form_(dwarf2reader::DW_FORM_addr), - abstract_origin_(NULL), inline_(false) { } + ranges_(0), abstract_origin_(NULL), inline_(false) { } void ProcessAttributeUnsigned(enum DwarfAttribute attr, enum DwarfForm form, uint64 data); @@ -465,6 +482,7 @@ class DwarfCUToModule::FuncHandler: public GenericDIEHandler { string name_; uint64 low_pc_, high_pc_; // DW_AT_low_pc, DW_AT_high_pc DwarfForm high_pc_form_; // DW_AT_high_pc can be length or address. + uint64 ranges_; // DW_AT_ranges const AbstractOrigin* abstract_origin_; bool inline_; }; @@ -484,6 +502,9 @@ void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned( high_pc_form_ = form; high_pc_ = data; break; + case dwarf2reader::DW_AT_ranges: + ranges_ = data; + break; default: GenericDIEHandler::ProcessAttributeUnsigned(attr, form, data); @@ -537,17 +558,47 @@ bool DwarfCUToModule::FuncHandler::EndAttributes() { return true; } +static bool IsEmptyRange(vector<Module::Range> ranges) { + uint64 size = accumulate(ranges.cbegin(), ranges.cend(), 0, + [](uint64 total, Module::Range entry) { + return total + entry.size; + } + ); + + return size == 0; +} + void DwarfCUToModule::FuncHandler::Finish() { - // Make high_pc_ an address, if it isn't already. - if (high_pc_form_ != dwarf2reader::DW_FORM_addr) { - high_pc_ += low_pc_; + vector<Module::Range> ranges; + + if (!ranges_) { + // Make high_pc_ an address, if it isn't already. + if (high_pc_form_ != dwarf2reader::DW_FORM_addr) { + high_pc_ += low_pc_; + } + + Module::Range range(low_pc_, high_pc_ - low_pc_); + ranges.push_back(range); + } else { + RangesHandler *ranges_handler = cu_context_->ranges_handler; + + if (ranges_handler) { + if (!ranges_handler->ReadRanges(ranges_, cu_context_->low_pc, &ranges)) { + ranges.clear(); + cu_context_->reporter->MalformedRangeList(ranges_); + } + } else { + cu_context_->reporter->MissingRanges(); + } } // Did we collect the information we need? Not all DWARF function - // entries have low and high addresses (for example, inlined - // functions that were never used), but all the ones we're - // interested in cover a non-empty range of bytes. - if (low_pc_ < high_pc_) { + // entries are non-empty (for example, inlined functions that were never + // used), but all the ones we're interested in cover a non-empty range of + // bytes. + if (!IsEmptyRange(ranges)) { + low_pc_ = ranges.front().address; + // Malformed DWARF may omit the name, but all Module::Functions must // have names. string name; @@ -561,7 +612,7 @@ void DwarfCUToModule::FuncHandler::Finish() { // Create a Module::Function based on the data we've gathered, and // add it to the functions_ list. scoped_ptr<Module::Function> func(new Module::Function(name, low_pc_)); - func->size = high_pc_ - low_pc_; + func->ranges = ranges; func->parameter_size = 0; if (func->address) { // If the function address is zero this is a sign that this function @@ -663,7 +714,7 @@ void DwarfCUToModule::WarningReporter::UncoveredFunction( return; UncoveredHeading(); fprintf(stderr, " function%s: %s\n", - function.size == 0 ? " (zero-length)" : "", + IsEmptyRange(function.ranges) ? " (zero-length)" : "", function.name.c_str()); } @@ -697,11 +748,25 @@ void DwarfCUToModule::WarningReporter::UnhandledInterCUReference( filename_.c_str(), offset, target); } +void DwarfCUToModule::WarningReporter::MalformedRangeList(uint64 offset) { + CUHeading(); + fprintf(stderr, "%s: warning: the range list at offset 0x%llx falls out of " + "the .debug_ranges section.\n", + filename_.c_str(), offset); +} + +void DwarfCUToModule::WarningReporter::MissingRanges() { + CUHeading(); + fprintf(stderr, "%s: warning: A DW_AT_ranges attribute was encountered but " + "the .debug_ranges section is missing.\n", filename_.c_str()); +} + DwarfCUToModule::DwarfCUToModule(FileContext *file_context, LineToModuleHandler *line_reader, + RangesHandler *ranges_handler, WarningReporter *reporter) : line_reader_(line_reader), - cu_context_(new CUContext(file_context, reporter)), + cu_context_(new CUContext(file_context, reporter, ranges_handler)), child_context_(new DIEContext()), has_source_line_info_(false) { } @@ -732,6 +797,16 @@ void DwarfCUToModule::ProcessAttributeUnsigned(enum DwarfAttribute attr, case dwarf2reader::DW_AT_language: // source language of this CU SetLanguage(static_cast<DwarfLanguage>(data)); break; + case dwarf2reader::DW_AT_low_pc: + cu_context_->low_pc = data; + break; + case dwarf2reader::DW_AT_high_pc: + cu_context_->high_pc = data; + break; + case dwarf2reader::DW_AT_ranges: + cu_context_->ranges = data; + break; + default: break; } @@ -841,6 +916,46 @@ void DwarfCUToModule::ReadSourceLines(uint64 offset) { } namespace { +class FunctionRange { + public: + FunctionRange(const Module::Range &range, Module::Function *function) : + address(range.address), size(range.size), function(function) { } + + void AddLine(Module::Line &line) { + function->lines.push_back(line); + } + + Module::Address address; + Module::Address size; + Module::Function *function; +}; + +// Fills an array of ranges with pointers to the functions which owns them. +// The array is sorted in ascending order and the ranges are non-overlapping. + +static void FillSortedFunctionRanges(vector<FunctionRange> &dest_ranges, + vector<Module::Function *> *functions) { + for (vector<Module::Function *>::const_iterator func_it = functions->cbegin(); + func_it != functions->cend(); + func_it++) + { + Module::Function *func = *func_it; + vector<Module::Range> &ranges = func->ranges; + for (vector<Module::Range>::const_iterator ranges_it = ranges.cbegin(); + ranges_it != ranges.cend(); + ++ranges_it) { + FunctionRange range(*ranges_it, func); + dest_ranges.push_back(range); + } + } + + sort(dest_ranges.begin(), dest_ranges.end(), + [](const FunctionRange &fr1, const FunctionRange &fr2) { + return fr1.address < fr2.address; + } + ); +} + // Return true if ADDRESS falls within the range of ITEM. template <class T> inline bool within(const T &item, Module::Address address) { @@ -880,47 +995,50 @@ void DwarfCUToModule::AssignLinesToFunctions() { const Module::Function *last_function_cited = NULL; const Module::Line *last_line_cited = NULL; - // Make a single pass through both vectors from lower to higher - // addresses, populating each Function's lines vector with lines - // from our lines_ vector that fall within the function's address - // range. - vector<Module::Function *>::iterator func_it = functions->begin(); + // Prepare a sorted list of ranges with range-to-function mapping + vector<FunctionRange> sorted_ranges; + FillSortedFunctionRanges(sorted_ranges, functions); + + // Make a single pass through both the range and line vectors from lower to + // higher addresses, populating each range's function lines vector with lines + // from our lines_ vector that fall within the range. + vector<FunctionRange>::iterator range_it = sorted_ranges.begin(); vector<Module::Line>::const_iterator line_it = lines_.begin(); Module::Address current; // Pointers to the referents of func_it and line_it, or NULL if the // iterator is at the end of the sequence. - Module::Function *func; + FunctionRange *range; const Module::Line *line; // Start current at the beginning of the first line or function, // whichever is earlier. - if (func_it != functions->end() && line_it != lines_.end()) { - func = *func_it; + if (range_it != sorted_ranges.end() && line_it != lines_.end()) { + range = &*range_it; line = &*line_it; - current = std::min(func->address, line->address); + current = std::min(range->address, line->address); } else if (line_it != lines_.end()) { - func = NULL; + range = NULL; line = &*line_it; current = line->address; - } else if (func_it != functions->end()) { - func = *func_it; + } else if (range_it != sorted_ranges.end()) { + range = &*range_it; line = NULL; - current = (*func_it)->address; + current = range->address; } else { return; } - while (func || line) { + while (range || line) { // This loop has two invariants that hold at the top. // // First, at least one of the iterators is not at the end of its // sequence, and those that are not refer to the earliest - // function or line that contains or starts after CURRENT. + // range or line that contains or starts after CURRENT. // // Note that every byte is in one of four states: it is covered - // or not covered by a function, and, independently, it is + // or not covered by a range, and, independently, it is // covered or not covered by a line. // // The second invariant is that CURRENT refers to a byte whose @@ -930,7 +1048,7 @@ void DwarfCUToModule::AssignLinesToFunctions() { // // Note that, although each iteration advances CURRENT from one // transition address to the next in each iteration, it might - // not advance the iterators. Suppose we have a function that + // not advance the iterators. Suppose we have a range that // starts with a line, has a gap, and then a second line, and // suppose that we enter an iteration with CURRENT at the end of // the first line. The next transition address is the start of @@ -938,11 +1056,11 @@ void DwarfCUToModule::AssignLinesToFunctions() { // advance CURRENT to that point. At the head of that iteration, // the invariants require that the line iterator be pointing at // the second line. But this is also true at the head of the - // next. And clearly, the iteration must not change the function + // next. And clearly, the iteration must not change the range // iterator. So neither iterator moves. // Assert the first invariant (see above). - assert(!func || current < func->address || within(*func, current)); + assert(!range || current < range->address || within(*range, current)); assert(!line || current < line->address || within(*line, current)); // The next transition after CURRENT. @@ -950,33 +1068,33 @@ void DwarfCUToModule::AssignLinesToFunctions() { // Figure out which state we're in, add lines or warn, and compute // the next transition address. - if (func && current >= func->address) { + if (range && current >= range->address) { if (line && current >= line->address) { - // Covered by both a line and a function. - Module::Address func_left = func->size - (current - func->address); + // Covered by both a line and a range. + Module::Address range_left = range->size - (current - range->address); Module::Address line_left = line->size - (current - line->address); // This may overflow, but things work out. - next_transition = current + std::min(func_left, line_left); + next_transition = current + std::min(range_left, line_left); Module::Line l = *line; l.address = current; l.size = next_transition - current; - func->lines.push_back(l); + range->AddLine(l); last_line_used = line; } else { - // Covered by a function, but no line. - if (func != last_function_cited) { - reporter->UncoveredFunction(*func); - last_function_cited = func; + // Covered by a range, but no line. + if (range->function != last_function_cited) { + reporter->UncoveredFunction(*(range->function)); + last_function_cited = range->function; } - if (line && within(*func, line->address)) + if (line && within(*range, line->address)) next_transition = line->address; else // If this overflows, we'll catch it below. - next_transition = func->address + func->size; + next_transition = range->address + range->size; } } else { if (line && current >= line->address) { - // Covered by a line, but no function. + // Covered by a line, but no range. // // If GCC emits padding after one function to align the start // of the next, then it will attribute the padding @@ -988,27 +1106,27 @@ void DwarfCUToModule::AssignLinesToFunctions() { // start of the next function, then assume this is what // happened, and don't warn. if (line != last_line_cited - && !(func + && !(range && line == last_line_used - && func->address - line->address == line->size)) { + && range->address - line->address == line->size)) { reporter->UncoveredLine(*line); last_line_cited = line; } - if (func && within(*line, func->address)) - next_transition = func->address; + if (range && within(*line, range->address)) + next_transition = range->address; else // If this overflows, we'll catch it below. next_transition = line->address + line->size; } else { - // Covered by neither a function nor a line. By the invariant, - // both func and line begin after CURRENT. The next transition - // is the start of the next function or next line, whichever + // Covered by neither a range nor a line. By the invariant, + // both range and line begin after CURRENT. The next transition + // is the start of the next range or next line, whichever // is earliest. - assert(func || line); - if (func && line) - next_transition = std::min(func->address, line->address); - else if (func) - next_transition = func->address; + assert(range || line); + if (range && line) + next_transition = std::min(range->address, line->address); + else if (range) + next_transition = range->address; else next_transition = line->address; } @@ -1025,11 +1143,11 @@ void DwarfCUToModule::AssignLinesToFunctions() { // then we could go around more than once. We don't worry too much // about what result we produce in that case, just as long as we don't // hang or crash. - while (func_it != functions->end() - && next_transition >= (*func_it)->address - && !within(**func_it, next_transition)) - func_it++; - func = (func_it != functions->end()) ? *func_it : NULL; + while (range_it != sorted_ranges.end() + && next_transition >= range_it->address + && !within(*range_it, next_transition)) + range_it++; + range = (range_it != sorted_ranges.end()) ? &(*range_it) : NULL; while (line_it != lines_.end() && next_transition >= line_it->address && !within(*line_it, next_transition)) |