blob: 5b11243e19b063118293e995d88032aaa8c141cf [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/zxdb/symbols/dwarf_location.h"
#include <limits>
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
#include "llvm/DebugInfo/DWARF/DWARFSection.h"
#include "llvm/DebugInfo/DWARF/DWARFUnit.h"
#include "src/developer/debug/zxdb/common/data_extractor.h"
namespace zxdb {
VariableLocation DecodeVariableLocation(const llvm::DWARFUnit* unit,
const llvm::DWARFFormValue& form) {
if (form.isFormClass(llvm::DWARFFormValue::FC_Block) ||
form.isFormClass(llvm::DWARFFormValue::FC_Exprloc)) {
// These forms are both a block of data which is interpreted as a DWARF expression. There is no
// validity range for this so assume the expression is valid as long as the variable is in
// scope.
llvm::ArrayRef<uint8_t> block = *form.getAsBlock();
return VariableLocation(block.data(), block.size());
}
if (!form.isFormClass(llvm::DWARFFormValue::FC_SectionOffset))
return VariableLocation(); // Unknown type.
// This form is a "section offset" reference to a block in the .debug_loc table that contains a
// list of valid ranges + associated expressions.
llvm::DWARFContext& context = unit->getContext();
const llvm::DWARFObject& object = context.getDWARFObj();
const llvm::DWARFSection& debug_loc_section = object.getLocSection();
if (debug_loc_section.Data.empty()) {
// LLVM dumpLocation() falls back on the DWARFObject::getLocDWOSection() call in this case. We
// don't support DWOs yet so just fail.
return VariableLocation();
}
uint32_t offset = *form.getAsSectionOffset();
if (offset >= debug_loc_section.Data.size())
return VariableLocation(); // Off the end.
containers::array_view<uint8_t> section_data(
reinterpret_cast<const uint8_t*>(debug_loc_section.Data.data()),
debug_loc_section.Data.size());
// Interpret the resulting list.
auto base_address = const_cast<llvm::DWARFUnit*>(unit)->getBaseAddress();
return DecodeLocationList(base_address ? base_address->Address : 0, section_data.subview(offset));
}
VariableLocation DecodeLocationList(TargetPointer unit_base_addr,
containers::array_view<uint8_t> data) {
DataExtractor ext(data);
std::vector<VariableLocation::Entry> entries;
// These location list begin and end values are "relative to the applicable base address of the
// compilation unit referencing this location list."
//
// The "applicable base address" is either the unit's base address, or, if there was a "base
// address selection entry", the nearest preceeding one.
//
// This value tracks the current applicable base address.
TargetPointer base_address = unit_base_addr;
// Base address selection entries are identifier by a start address with the max value.
constexpr TargetPointer kBaseAddressSelectionTag = std::numeric_limits<TargetPointer>::max();
while (!ext.done()) {
auto begin = ext.Read<TargetPointer>();
if (!begin)
return VariableLocation();
auto end = ext.Read<TargetPointer>();
if (!end)
return VariableLocation();
if (*begin == kBaseAddressSelectionTag) {
// New base address, read it and we're done with this entry.
base_address = *end;
continue;
}
if (*begin == 0 && *end == 0)
break; // End-of-list entry.
// Non-"base address selection entries" are followed by a 2-byte length, followed by the
// DWARF expression of that length.
auto expression_len = ext.Read<uint16_t>();
if (!expression_len)
return VariableLocation();
// Empty expressions are valid and just mean to skip this entry, so we don't bother adding it.
if (*expression_len == 0)
continue;
// Expression data.
std::vector<uint8_t> expression;
expression.resize(*expression_len);
if (!ext.ReadBytes(*expression_len, &expression[0]))
return VariableLocation();
if (*begin == *end)
continue; // Empty range, don't bother adding.
VariableLocation::Entry& dest = entries.emplace_back();
dest.begin = base_address + *begin;
dest.end = base_address + *end;
dest.expression = std::move(expression);
}
return VariableLocation(std::move(entries));
}
} // namespace zxdb