blob: 77b7219268edf56c3c9d7a92f3759e95349c61e7 [file] [log] [blame]
// Copyright 2018 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 "garnet/bin/zxdb/symbols/dwarf_die_decoder.h"
#include "garnet/public/lib/fxl/logging.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
namespace zxdb {
DwarfDieDecoder::DwarfDieDecoder(llvm::DWARFContext* context,
llvm::DWARFUnit* unit)
: context_(context),
unit_(unit),
extractor_(unit_->getDebugInfoExtractor()) {}
DwarfDieDecoder::~DwarfDieDecoder() = default;
void DwarfDieDecoder::AddPresenceCheck(llvm::dwarf::Attribute attribute,
bool* present) {
attrs_.emplace_back(
attribute, [present](const llvm::DWARFFormValue&) { *present = true; });
}
void DwarfDieDecoder::AddUnsignedConstant(llvm::dwarf::Attribute attribute,
llvm::Optional<uint64_t>* output) {
attrs_.emplace_back(attribute, [output](const llvm::DWARFFormValue& form) {
*output = form.getAsUnsignedConstant();
});
}
void DwarfDieDecoder::AddSignedConstant(llvm::dwarf::Attribute attribute,
llvm::Optional<int64_t>* output) {
attrs_.emplace_back(attribute, [output](const llvm::DWARFFormValue& form) {
*output = form.getAsSignedConstant();
});
}
void DwarfDieDecoder::AddAddress(llvm::dwarf::Attribute attribute,
llvm::Optional<uint64_t>* output) {
attrs_.emplace_back(attribute, [output](const llvm::DWARFFormValue& form) {
*output = form.getAsAddress();
});
}
void DwarfDieDecoder::AddHighPC(llvm::Optional<HighPC>* output) {
attrs_.emplace_back(
llvm::dwarf::DW_AT_high_pc, [output](const llvm::DWARFFormValue& form) {
if (form.isFormClass(llvm::DWARFFormValue::FC_Constant)) {
auto as_constant = form.getAsUnsignedConstant();
if (as_constant)
*output = HighPC(true, *as_constant);
} else if (form.isFormClass(llvm::DWARFFormValue::FC_Address)) {
auto as_addr = form.getAsAddress();
if (as_addr)
*output = HighPC(false, *as_addr);
}
});
}
void DwarfDieDecoder::AddCString(llvm::dwarf::Attribute attribute,
llvm::Optional<const char*>* output) {
attrs_.emplace_back(attribute, [output](const llvm::DWARFFormValue& form) {
*output = form.getAsCString();
});
}
void DwarfDieDecoder::AddLineTableFile(llvm::dwarf::Attribute attribute,
llvm::Optional<std::string>* output) {
const llvm::DWARFDebugLine::LineTable* line_table =
context_->getLineTableForUnit(unit_);
const char* compilation_dir = unit_->getCompilationDir();
if (line_table) {
attrs_.emplace_back(attribute, [output, compilation_dir, line_table](
const llvm::DWARFFormValue& form) {
output->emplace();
line_table->getFileNameByIndex(
form.getAsUnsignedConstant().getValue(), compilation_dir,
llvm::DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
output->getValue());
});
}
}
void DwarfDieDecoder::AddReference(llvm::dwarf::Attribute attribute,
llvm::Optional<uint64_t>* unit_offset,
llvm::Optional<uint64_t>* global_offset) {
attrs_.emplace_back(attribute, [unit_offset, global_offset](
const llvm::DWARFFormValue& form) {
switch (form.getForm()) {
case llvm::dwarf::DW_FORM_ref1:
case llvm::dwarf::DW_FORM_ref2:
case llvm::dwarf::DW_FORM_ref4:
case llvm::dwarf::DW_FORM_ref8:
case llvm::dwarf::DW_FORM_ref_udata:
// A DWARF "form" is the way a value is encoded in the file. These
// are all relative location of DIEs within the same unit.
*unit_offset = form.getAsReferenceUVal();
break;
case llvm::dwarf::DW_FORM_ref_addr:
// This is an absolute DIE address which can be used across units.
*global_offset = form.getAsReferenceUVal();
break;
default:
// Note that we don't handle DW_FORM_ref_sig8, DW_FORM_ref_sup4, or
// DW_FORM_ref_sup8. The "sig8" one requries a different type encoding
// that our Clang toolchain doesn't seem to generate. The "sup4/8" ones
// require a shared separate symbol file we don't use.
break;
}
});
}
void DwarfDieDecoder::AddReference(llvm::dwarf::Attribute attribute,
llvm::DWARFDie* output) {
attrs_.emplace_back(
attribute, [this, output](const llvm::DWARFFormValue& form) {
// See above version for comments.
switch (form.getForm()) {
case llvm::dwarf::DW_FORM_ref1:
case llvm::dwarf::DW_FORM_ref2:
case llvm::dwarf::DW_FORM_ref4:
case llvm::dwarf::DW_FORM_ref8:
case llvm::dwarf::DW_FORM_ref_udata: {
auto ref_value = form.getAsReferenceUVal();
if (ref_value)
*output = unit_->getDIEForOffset(unit_->getOffset() + *ref_value);
break;
}
case llvm::dwarf::DW_FORM_ref_addr: {
auto ref_value = form.getAsReferenceUVal();
if (ref_value)
*output = unit_->getDIEForOffset(*ref_value);
break;
}
default:
// See above version for some comments.
break;
}
});
}
void DwarfDieDecoder::AddFile(llvm::dwarf::Attribute attribute,
llvm::Optional<std::string>* output) {
attrs_.emplace_back(
attribute, [this, output](const llvm::DWARFFormValue& form) {
llvm::Optional<uint64_t> file_index = form.getAsUnsignedConstant();
if (!file_index)
return;
const llvm::DWARFDebugLine::LineTable* line_table =
context_->getLineTableForUnit(unit_);
const char* compilation_dir = unit_->getCompilationDir();
std::string file_name;
if (line_table->getFileNameByIndex(
*file_index, compilation_dir,
llvm::DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
file_name))
*output = std::move(file_name);
});
}
void DwarfDieDecoder::AddCustom(
llvm::dwarf::Attribute attribute,
std::function<void(const llvm::DWARFFormValue&)> callback) {
attrs_.emplace_back(attribute, std::move(callback));
}
bool DwarfDieDecoder::Decode(const llvm::DWARFDie& die) {
return Decode(*die.getDebugInfoEntry());
}
bool DwarfDieDecoder::Decode(const llvm::DWARFDebugInfoEntry& die) {
// This indicates the abbreviation. Each DIE starts with an abbreviation
// code. The is the number that the DWARFAbbreviationDeclaration was derived
// from above. We have to read it again to skip the offset over the number.
//
// - A zero abbreviation code indicates a null DIE which is used to mark
// the end of a sequence of siblings.
//
// - Otherwise this is a tag of an entry in the .debug_abbrev table (each
// entry in that table declares its own tag so it's not an index or an
// offset). The abbreviation entry indicates the attributes that this
// type of DIE contains, plus the data format for each.
const llvm::DWARFAbbreviationDeclaration* abbrev =
die.getAbbreviationDeclarationPtr();
if (!abbrev)
return false;
uint32_t offset = die.getOffset();
// Skip over the abbreviationcode. We don't actually need this (the abbrev
// pointer above is derived from this) but we need to move offset past it.
uint32_t abbr_code = extractor_.getULEB128(&offset);
if (!abbr_code) {
FXL_NOTREACHED(); // Should have gotten a null abbrev for this above.
return false;
}
bool decoded_any = false;
for (const llvm::DWARFAbbreviationDeclaration::AttributeSpec& spec :
abbrev->attributes()) {
bool decoded_current = false;
llvm::DWARFFormValue form_value(spec.Form);
for (const Dispatch& dispatch : attrs_) {
if (spec.Attr != dispatch.first)
continue;
// Found the attribute, dispatch it and mark it read.
form_value.extractValue(extractor_, &offset, unit_->getFormParams(),
unit_);
dispatch.second(form_value);
decoded_current = true;
decoded_any = true;
break;
}
if (!decoded_current) {
// When the attribute wasn't read, skip over it to go to the next.
form_value.skipValue(extractor_, &offset, unit_->getFormParams());
}
}
return true;
}
} // namespace zxdb