| // 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 "src/developer/debug/zxdb/symbols/dwarf_die_decoder.h" |
| |
| #include "src/lib/fxl/logging.h" |
| #include "llvm/DebugInfo/DWARF/DWARFContext.h" |
| #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" |
| |
| namespace zxdb { |
| |
| namespace { |
| |
| // The maximum nesting of abstract origin references we'll follow recursively |
| // before givin up. Prevents blowing out the stack for corrupt symbols. |
| constexpr int kMaxAbstractOriginRefsToFollow = 8; |
| |
| // Decodes a cross-DIE reference. Return value will be !isValid() on failure. |
| llvm::DWARFDie DecodeReference(llvm::DWARFUnit* unit, |
| 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. |
| auto ref_value = form.getAsReferenceUVal(); |
| if (ref_value) |
| return unit->getDIEForOffset(unit->getOffset() + *ref_value); |
| break; |
| } |
| case llvm::dwarf::DW_FORM_ref_addr: { |
| // This is an absolute DIE address which can be used across units. |
| auto ref_value = form.getAsReferenceUVal(); |
| if (ref_value) |
| return unit->getDIEForOffset(*ref_value); |
| 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; |
| } |
| return llvm::DWARFDie(); |
| } |
| |
| } // namespace |
| |
| 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::AddBool(llvm::dwarf::Attribute attribute, |
| llvm::Optional<bool>* output) { |
| attrs_.emplace_back(attribute, [output](const llvm::DWARFFormValue& form) { |
| *output = !!form.getAsUnsignedConstant(); |
| }); |
| } |
| |
| 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) { |
| // See DecodeReference() function above for more info. |
| 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: |
| *unit_offset = form.getAsReferenceUVal(); |
| break; |
| case llvm::dwarf::DW_FORM_ref_addr: |
| *global_offset = form.getAsReferenceUVal(); |
| break; |
| default: |
| break; |
| } |
| }); |
| } |
| |
| void DwarfDieDecoder::AddReference(llvm::dwarf::Attribute attribute, |
| llvm::DWARFDie* output) { |
| attrs_.emplace_back(attribute, |
| [this, output](const llvm::DWARFFormValue& form) { |
| *output = DecodeReference(unit_, form); |
| }); |
| } |
| |
| 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::AddAbstractParent(llvm::DWARFDie* output) { |
| FXL_DCHECK(!abstract_parent_); |
| abstract_parent_ = output; |
| } |
| |
| 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) { |
| seen_attrs_.clear(); |
| return DecodeInternal(die, kMaxAbstractOriginRefsToFollow); |
| } |
| |
| bool DwarfDieDecoder::DecodeInternal( |
| const llvm::DWARFDebugInfoEntry& die, int abstract_origin_refs_to_follow) { |
| // 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; |
| } |
| |
| // Set when we encounter an abstract origin attribute. |
| llvm::DWARFDie abstract_origin; |
| |
| for (const llvm::DWARFAbbreviationDeclaration::AttributeSpec& spec : |
| abbrev->attributes()) { |
| // Set to true when the form_value has been decoded. Otherwise, the value |
| // needs to be skipped to advance through the data. |
| bool decoded_current = false; |
| llvm::DWARFFormValue form_value(spec.Form); |
| |
| // Tracks if the current attribute should be looked up and dispatched. |
| // This loop doesn't return early so the form_value.skipValue() call at the |
| // bottom will be called when necessary (otherwise the loop won't advance). |
| bool needs_dispatch = true; |
| |
| if (spec.Attr == llvm::dwarf::DW_AT_abstract_origin) { |
| // Abtract origins are handled after loop completion. Explicitly don't |
| // check for duplicate attributes in this case so we can follow more than |
| // one link in the chain. |
| form_value.extractValue(extractor_, &offset, unit_->getFormParams(), |
| unit_); |
| abstract_origin = DecodeReference(unit_, form_value); |
| decoded_current = true; |
| } else { |
| // Track attributes that we've already seen and don't decode duplicates |
| // (most DIEs won't have duplicates, this is for when we recursively |
| // underlay values following abstract origins). This is brute-force |
| // because the typical number of attributes is small enough that this |
| // should be more efficient than a set which requires per-element heap |
| // allocations. |
| if (std::find(seen_attrs_.begin(), seen_attrs_.end(), spec.Attr) != |
| seen_attrs_.end()) |
| needs_dispatch = false; |
| else |
| seen_attrs_.push_back(spec.Attr); |
| } |
| |
| if (needs_dispatch) { |
| // Check for a handler for this attribute and dispatch it. |
| for (const Dispatch& dispatch : attrs_) { |
| if (spec.Attr != dispatch.first) |
| continue; |
| |
| // Found the attribute, dispatch it and mark it read. |
| if (!decoded_current) { |
| form_value.extractValue(extractor_, &offset, unit_->getFormParams(), |
| unit_); |
| decoded_current = true; |
| } |
| dispatch.second(form_value); |
| 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()); |
| } |
| } |
| |
| // Recursively decode abstract origins. The attributes on the abstract origin |
| // DIE "underlay" any attributes present on the current one. |
| if (abstract_origin.isValid() && abstract_origin_refs_to_follow > 0) { |
| return DecodeInternal(*abstract_origin.getDebugInfoEntry(), |
| abstract_origin_refs_to_follow - 1); |
| } else { |
| // The deepest DIE in the abstract origin chain was found (which will be |
| // the original DIE itself if there was no abstract origin). |
| if (abstract_parent_) |
| *abstract_parent_ = unit_->getParent(&die); |
| } |
| |
| return true; |
| } |
| |
| } // namespace zxdb |