| // 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_symbol_factory.h" |
| |
| #include <algorithm> |
| |
| #include "llvm/DebugInfo/DWARF/DWARFContext.h" |
| #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" |
| #include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" |
| #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" |
| #include "llvm/DebugInfo/DWARF/DWARFSection.h" |
| #include "llvm/DebugInfo/DWARF/DWARFUnit.h" |
| #include "src/developer/debug/shared/logging/logging.h" |
| #include "src/developer/debug/zxdb/symbols/array_type.h" |
| #include "src/developer/debug/zxdb/symbols/base_type.h" |
| #include "src/developer/debug/zxdb/symbols/call_site.h" |
| #include "src/developer/debug/zxdb/symbols/call_site_parameter.h" |
| #include "src/developer/debug/zxdb/symbols/code_block.h" |
| #include "src/developer/debug/zxdb/symbols/collection.h" |
| #include "src/developer/debug/zxdb/symbols/compile_unit.h" |
| #include "src/developer/debug/zxdb/symbols/data_member.h" |
| #include "src/developer/debug/zxdb/symbols/dwarf_abstract_child_iterator.h" |
| #include "src/developer/debug/zxdb/symbols/dwarf_binary.h" |
| #include "src/developer/debug/zxdb/symbols/dwarf_die_decoder.h" |
| #include "src/developer/debug/zxdb/symbols/dwarf_location.h" |
| #include "src/developer/debug/zxdb/symbols/enumeration.h" |
| #include "src/developer/debug/zxdb/symbols/function.h" |
| #include "src/developer/debug/zxdb/symbols/function_type.h" |
| #include "src/developer/debug/zxdb/symbols/inherited_from.h" |
| #include "src/developer/debug/zxdb/symbols/member_ptr.h" |
| #include "src/developer/debug/zxdb/symbols/modified_type.h" |
| #include "src/developer/debug/zxdb/symbols/module_symbols_impl.h" |
| #include "src/developer/debug/zxdb/symbols/namespace.h" |
| #include "src/developer/debug/zxdb/symbols/symbol.h" |
| #include "src/developer/debug/zxdb/symbols/template_parameter.h" |
| #include "src/developer/debug/zxdb/symbols/variable.h" |
| #include "src/developer/debug/zxdb/symbols/variant.h" |
| #include "src/developer/debug/zxdb/symbols/variant_part.h" |
| |
| namespace zxdb { |
| |
| namespace { |
| |
| // Converts an llvm::Optional to a std::optional. |
| template <typename T> |
| std::optional<T> ToStdOptional(const llvm::Optional<T>& o) { |
| if (!o) |
| return std::nullopt; |
| return *o; |
| } |
| |
| // Generates ranges for a CodeBlock. The attributes may be not present, this function will compute |
| // what it can given the information (which may be an empty vector). |
| AddressRanges GetCodeRanges(const llvm::DWARFDie& die) { |
| AddressRanges::RangeVector code_ranges; |
| |
| // It would be trivially more efficient to get the DW_AT_ranges, etc. attributes out when we're |
| // iterating through the DIE. But the address ranges have many different forms and also vary |
| // between DWARF versions 4 and 5. It's easier to let LLVM deal with this complexity. |
| auto expected_ranges = die.getAddressRanges(); |
| if (!expected_ranges || expected_ranges->empty()) |
| return AddressRanges(); |
| |
| code_ranges.reserve(expected_ranges->size()); |
| for (const llvm::DWARFAddressRange& range : *expected_ranges) { |
| // A zero DW_AT_low_pc means the code is removed during the linking, either due to garbage |
| // collection (of unused functions) or identical code folding. Functions inlined and not used |
| // outside their compilation units will also get removed. |
| if (range.valid() && range.LowPC) |
| code_ranges.emplace_back(range.LowPC, range.HighPC); |
| } |
| |
| // Can't trust DWARF to have stored them in any particular order. |
| return AddressRanges(AddressRanges::kNonCanonical, std::move(code_ranges)); |
| } |
| |
| // Extracts a FileLine if possible from the given input. If the optional values aren't present, or |
| // are empty, returns an empty FileLine. |
| FileLine MakeFileLine(llvm::DWARFUnit* unit, const llvm::Optional<std::string>& file, |
| const llvm::Optional<uint64_t>& line, std::string compilation_dir) { |
| if (compilation_dir.empty() && unit->getCompilationDir()) |
| compilation_dir = unit->getCompilationDir(); |
| if (file && !file->empty() && line && *line > 0) |
| return FileLine(*file, compilation_dir, static_cast<int>(*line)); |
| return FileLine(); |
| } |
| |
| // Extracts the subrange size from an array subrange DIE. Returns the value on success, nullopt on |
| // failure. |
| std::optional<size_t> ReadArraySubrange(llvm::DWARFContext* context, |
| const llvm::DWARFDie& subrange_die) { |
| // Extract the DW_AT_count attribute which Clang generates, and DW_AT_upper_bound which GCC |
| // generated. |
| DwarfDieDecoder range_decoder(context); |
| |
| llvm::Optional<uint64_t> count; |
| range_decoder.AddUnsignedConstant(llvm::dwarf::DW_AT_count, &count); |
| |
| llvm::Optional<uint64_t> upper_bound; |
| range_decoder.AddUnsignedConstant(llvm::dwarf::DW_AT_upper_bound, &upper_bound); |
| |
| if (!range_decoder.Decode(subrange_die) || (!count && !upper_bound)) |
| return std::nullopt; |
| |
| if (count) |
| return static_cast<size_t>(*count); |
| return static_cast<size_t>(*upper_bound); |
| } |
| |
| void DisplayDebugTypesSectionWarning() { |
| FX_FIRST_N(1, LOGS(Warn)) |
| << "Separated .debug_types section is not supported yet. Please consider to remove " |
| "`-fdebug-types-section` from the compiler flags or add `-fno-debug-types-section` if " |
| "it's enabled by default. (fxbug.dev/97388)"; |
| } |
| |
| } // namespace |
| |
| DwarfSymbolFactory::DwarfSymbolFactory(fxl::WeakPtr<ModuleSymbolsImpl> symbols) |
| : symbols_(std::move(symbols)) {} |
| DwarfSymbolFactory::~DwarfSymbolFactory() = default; |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::CreateSymbol(uint64_t factory_data) { |
| if (!symbols_) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| llvm::DWARFDie die = GetLLVMContext()->getDIEForOffset(factory_data); |
| if (!die.isValid()) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| return DecodeSymbol(die); |
| } |
| |
| llvm::DWARFContext* DwarfSymbolFactory::GetLLVMContext() { |
| return symbols_->binary()->GetLLVMContext(); |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeSymbol(const llvm::DWARFDie& die) { |
| DwarfTag tag = static_cast<DwarfTag>(die.getTag()); |
| if (DwarfTagIsTypeModifier(tag)) |
| return DecodeModifiedType(die); |
| |
| fxl::RefPtr<Symbol> symbol; |
| switch (tag) { |
| case DwarfTag::kArrayType: |
| symbol = DecodeArrayType(die); |
| break; |
| case DwarfTag::kBaseType: |
| symbol = DecodeBaseType(die); |
| break; |
| case DwarfTag::kCallSite: |
| symbol = DecodeCallSite(die); |
| break; |
| case DwarfTag::kCallSiteParameter: |
| symbol = DecodeCallSiteParameter(die); |
| break; |
| case DwarfTag::kCompileUnit: |
| symbol = DecodeCompileUnit(die); |
| break; |
| case DwarfTag::kEnumerationType: |
| symbol = DecodeEnum(die); |
| break; |
| case DwarfTag::kFormalParameter: |
| case DwarfTag::kVariable: |
| symbol = DecodeVariable(die); |
| break; |
| case DwarfTag::kSubroutineType: |
| symbol = DecodeFunctionType(die); |
| break; |
| case DwarfTag::kImportedDeclaration: |
| symbol = DecodeImportedDeclaration(die); |
| break; |
| case DwarfTag::kInheritance: |
| symbol = DecodeInheritedFrom(die); |
| break; |
| case DwarfTag::kLexicalBlock: |
| symbol = DecodeLexicalBlock(die); |
| break; |
| case DwarfTag::kMember: |
| symbol = DecodeDataMember(die); |
| break; |
| case DwarfTag::kNamespace: |
| symbol = DecodeNamespace(die); |
| break; |
| case DwarfTag::kPtrToMemberType: |
| symbol = DecodeMemberPtr(die); |
| break; |
| case DwarfTag::kInlinedSubroutine: |
| case DwarfTag::kSubprogram: |
| symbol = DecodeFunction(die, tag); |
| break; |
| case DwarfTag::kStructureType: |
| case DwarfTag::kClassType: |
| case DwarfTag::kUnionType: |
| symbol = DecodeCollection(die); |
| break; |
| case DwarfTag::kTemplateTypeParameter: |
| case DwarfTag::kTemplateValueParameter: |
| symbol = DecodeTemplateParameter(die, tag); |
| break; |
| case DwarfTag::kVariantPart: |
| symbol = DecodeVariantPart(die); |
| break; |
| case DwarfTag::kVariant: |
| symbol = DecodeVariant(die); |
| break; |
| case DwarfTag::kUnspecifiedType: |
| symbol = DecodeUnspecifiedType(die); |
| break; |
| default: |
| // All unhandled Tag types get a Symbol that has the correct tag, but no other data. |
| symbol = fxl::MakeRefCounted<Symbol>(static_cast<DwarfTag>(die.getTag())); |
| } |
| |
| // Set the parent block if it hasn't been set already by the type-specific factory. In particular, |
| // we want the function/variable specification's parent block if there was a specification since |
| // it will contain the namespace and class stuff. |
| if (!symbol->parent()) { |
| llvm::DWARFDie parent = die.getParent(); |
| if (parent) |
| symbol->set_parent(MakeUncachedLazy(parent)); |
| } |
| |
| return symbol; |
| } |
| |
| LazySymbol DwarfSymbolFactory::MakeLazy(const llvm::DWARFDie& die) { |
| return LazySymbol(fxl::RefPtr<SymbolFactory>(this), die.getOffset()); |
| } |
| |
| LazySymbol DwarfSymbolFactory::MakeLazy(uint64_t die_offset) { |
| return LazySymbol(fxl::RefPtr<SymbolFactory>(this), die_offset); |
| } |
| |
| UncachedLazySymbol DwarfSymbolFactory::MakeUncachedLazy(const llvm::DWARFDie& die) { |
| return UncachedLazySymbol(fxl::RefPtr<SymbolFactory>(this), die.getOffset()); |
| } |
| |
| UncachedLazySymbol DwarfSymbolFactory::MakeUncachedLazy(uint64_t die_offset) { |
| return UncachedLazySymbol(fxl::RefPtr<SymbolFactory>(this), die_offset); |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeFunction(const llvm::DWARFDie& die, DwarfTag tag, |
| bool is_specification) { |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| llvm::DWARFDie parent; |
| decoder.AddAbstractParent(&parent); |
| |
| llvm::DWARFDie specification; |
| decoder.AddReference(llvm::dwarf::DW_AT_specification, &specification); |
| |
| llvm::Optional<const char*> name; |
| decoder.AddCString(llvm::dwarf::DW_AT_name, &name); |
| |
| llvm::Optional<const char*> linkage_name; |
| decoder.AddCString(llvm::dwarf::DW_AT_linkage_name, &linkage_name); |
| |
| llvm::DWARFDie return_type; |
| decoder.AddReference(llvm::dwarf::DW_AT_type, &return_type); |
| |
| // Declaration location. |
| llvm::Optional<std::string> decl_file; |
| llvm::Optional<uint64_t> decl_line; |
| decoder.AddFile(llvm::dwarf::DW_AT_decl_file, &decl_file); |
| decoder.AddUnsignedConstant(llvm::dwarf::DW_AT_decl_line, &decl_line); |
| |
| // Call location (inline functions only). |
| llvm::Optional<std::string> call_file; |
| llvm::Optional<uint64_t> call_line; |
| if (tag == DwarfTag::kInlinedSubroutine) { |
| decoder.AddFile(llvm::dwarf::DW_AT_call_file, &call_file); |
| decoder.AddUnsignedConstant(llvm::dwarf::DW_AT_call_line, &call_line); |
| } |
| |
| VariableLocation frame_base; |
| decoder.AddCustom(llvm::dwarf::DW_AT_frame_base, |
| [&frame_base, source = MakeUncachedLazy(die)]( |
| llvm::DWARFUnit* unit, const llvm::DWARFFormValue& value) { |
| frame_base = DecodeVariableLocation(unit, value, source); |
| }); |
| |
| llvm::DWARFDie object_ptr; |
| decoder.AddReference(llvm::dwarf::DW_AT_object_pointer, &object_ptr); |
| |
| if (!decoder.Decode(die)) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| fxl::RefPtr<Function> function; |
| |
| // If this DIE has a link to a function specification (and we haven't already followed such a |
| // link), first read that in to get things like the mangled name, parent context, and declaration |
| // locations. Then we'll overlay our values on that object. |
| if (!is_specification && specification) { |
| auto spec = DecodeFunction(specification, tag, true); |
| Function* spec_function = spec->As<Function>(); |
| // If the specification is invalid, just ignore it and read out the values that we can find in |
| // this DIE. An empty one will be created below. |
| if (spec_function) |
| function = fxl::RefPtr<Function>(spec_function); |
| } |
| if (!function) |
| function = fxl::MakeRefCounted<Function>(tag); |
| |
| if (name) |
| function->set_assigned_name(*name); |
| if (linkage_name) |
| function->set_linkage_name(*linkage_name); |
| function->set_code_ranges(GetCodeRanges(die)); |
| if (decl_file) { |
| function->set_decl_line( |
| MakeFileLine(die.getDwarfUnit(), decl_file, decl_line, symbols_->GetBuildDir())); |
| } |
| function->set_call_line( |
| MakeFileLine(die.getDwarfUnit(), call_file, call_line, symbols_->GetBuildDir())); |
| if (return_type) |
| function->set_return_type(MakeLazy(return_type)); |
| function->set_frame_base(std::move(frame_base)); |
| if (object_ptr) |
| function->set_object_pointer(MakeLazy(object_ptr)); |
| |
| // Handle sub-DIEs: parameters, child blocks, and variables. |
| // |
| // Note: this partially duplicates a similar loop in DecodeLexicalBlock(). |
| std::vector<LazySymbol> parameters; |
| std::vector<LazySymbol> inner_blocks; |
| std::vector<LazySymbol> variables; |
| std::vector<LazySymbol> template_params; |
| std::vector<LazySymbol> call_sites; |
| for (const llvm::DWARFDie& child : DwarfAbstractChildIterator(die)) { |
| switch (child.getTag()) { |
| case llvm::dwarf::DW_TAG_formal_parameter: |
| parameters.push_back(MakeLazy(child)); |
| break; |
| case llvm::dwarf::DW_TAG_variable: |
| variables.push_back(MakeLazy(child)); |
| break; |
| case llvm::dwarf::DW_TAG_inlined_subroutine: |
| case llvm::dwarf::DW_TAG_lexical_block: |
| inner_blocks.push_back(MakeLazy(child)); |
| break; |
| case llvm::dwarf::DW_TAG_template_type_parameter: |
| case llvm::dwarf::DW_TAG_template_value_parameter: |
| template_params.push_back(MakeLazy(child)); |
| break; |
| case llvm::dwarf::DW_TAG_call_site: |
| call_sites.push_back(MakeLazy(child)); |
| break; |
| default: |
| break; // Skip everything else. |
| } |
| } |
| function->set_parameters(std::move(parameters)); |
| function->set_inner_blocks(std::move(inner_blocks)); |
| function->set_variables(std::move(variables)); |
| function->set_template_params(std::move(template_params)); |
| function->set_call_sites(std::move(call_sites)); |
| |
| if (parent && !function->parent()) { |
| // Set the parent symbol when it hasn't already been set. We always want the specification's |
| // parent instead of the implementation block's parent (if they're different) because the |
| // namespace and enclosing class information comes from the declaration. |
| // |
| // If this is already set, it means we recursively followed the specification which already set |
| // it. |
| function->set_parent(MakeUncachedLazy(parent)); |
| } |
| |
| if (tag == DwarfTag::kInlinedSubroutine) { |
| // In contrast to the logic for parent() above, the direct containing block of the inlined |
| // subroutine will save the CodeBlock inlined functions are embedded in. |
| llvm::DWARFDie direct_parent = die.getParent(); |
| if (direct_parent) |
| function->set_containing_block(MakeUncachedLazy(direct_parent)); |
| } |
| |
| return function; |
| } |
| |
| // We expect array types to have two things: |
| // - An attribute linking to the underlying type of the array. |
| // - One or more DW_TAG_subrange_type children that hold the size of the array in a DW_AT_count |
| // attribute. |
| // |
| // The subrange child is weird because the subrange links to its own type. LLVM generates a |
| // synthetic type __ARRAY_SIZE_TYPE__ that the DW_TAG_subrange_count DIE references from DW_AT_type |
| // attribute. We ignore this and only use the count. |
| // |
| // One might expect 2-dimensional arrays to be expressed as an array of one dimension where the |
| // contained type is an array of another. But both Clang and GCC generate one array entry with two |
| // subrange children. The order of these represents the declaration order in the code. |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeArrayType(const llvm::DWARFDie& die) { |
| // Extract the type attribute from the root DIE (should be a DW_TAG_array_type). |
| DwarfDieDecoder array_decoder(GetLLVMContext()); |
| llvm::DWARFDie type; |
| array_decoder.AddReference(llvm::dwarf::DW_AT_type, &type); |
| if (!array_decoder.Decode(die) || !type) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| // Need the concrete symbol for the contained type for the array constructor. |
| auto contained = DecodeSymbol(type); |
| if (!contained) |
| return fxl::MakeRefCounted<Symbol>(); |
| Type* contained_type = contained->As<Type>(); |
| if (!contained_type) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| // Find all subranges stored in the declaration order. More than one means a multi-dimensional |
| // array. |
| std::vector<std::optional<size_t>> subrange_sizes; |
| for (const llvm::DWARFDie& child : die) { |
| if (child.getTag() == llvm::dwarf::DW_TAG_subrange_type) |
| subrange_sizes.push_back(ReadArraySubrange(GetLLVMContext(), child)); |
| } |
| |
| // Require a subrange with a count in it. If we find cases where this isn't the case, we could add |
| // support for array types with unknown lengths, but currently ArrayType requires a size. |
| if (subrange_sizes.empty()) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| // Work backwards in the array dimensions generating nested array definitions. The innermost |
| // definition refers to the contained type. |
| fxl::RefPtr<Type> cur(contained_type); |
| for (int i = static_cast<int>(subrange_sizes.size()) - 1; i >= 0; i--) { |
| auto new_array = fxl::MakeRefCounted<ArrayType>(std::move(cur), subrange_sizes[i]); |
| cur = std::move(new_array); |
| } |
| return cur; |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeBaseType(const llvm::DWARFDie& die) { |
| // This object and its setup could be cached for better performance. |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| // Types must always use the parent of the abstract origin (if it exists) so they can be nested in |
| // the correct namespace. |
| llvm::DWARFDie parent; |
| decoder.AddAbstractParent(&parent); |
| |
| llvm::Optional<const char*> name; |
| decoder.AddCString(llvm::dwarf::DW_AT_name, &name); |
| |
| llvm::Optional<uint64_t> encoding; |
| decoder.AddUnsignedConstant(llvm::dwarf::DW_AT_encoding, &encoding); |
| |
| llvm::Optional<uint64_t> byte_size; |
| decoder.AddUnsignedConstant(llvm::dwarf::DW_AT_byte_size, &byte_size); |
| |
| if (!decoder.Decode(die)) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| auto base_type = fxl::MakeRefCounted<BaseType>(); |
| if (name) |
| base_type->set_assigned_name(*name); |
| if (encoding) |
| base_type->set_base_type(static_cast<int>(*encoding)); |
| if (byte_size) |
| base_type->set_byte_size(static_cast<uint32_t>(*byte_size)); |
| |
| if (parent) |
| base_type->set_parent(MakeUncachedLazy(parent)); |
| |
| return base_type; |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeCallSite(const llvm::DWARFDie& die) { |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| llvm::Optional<uint64_t> return_pc; |
| decoder.AddAddress(llvm::dwarf::DW_AT_call_return_pc, &return_pc); |
| |
| if (!decoder.Decode(die)) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| // Decode parameters. |
| std::vector<LazySymbol> parameters; |
| for (const llvm::DWARFDie& child : die) { |
| if (child.getTag() == llvm::dwarf::DW_TAG_call_site_parameter) |
| parameters.push_back(MakeLazy(child)); |
| } |
| |
| return fxl::MakeRefCounted<CallSite>(ToStdOptional(return_pc), std::move(parameters)); |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeCallSiteParameter(const llvm::DWARFDie& die) { |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| // We currently assume that the location and value for call site parameters are always blocks |
| // having inline location expressions. Technically, these are location expressions that could |
| // be an offset to a location list, but our current supported compilers don't express it this way, |
| // and there would seem to no point in doing so for this case. |
| |
| llvm::Optional<std::vector<uint8_t>> location; |
| decoder.AddBlock(llvm::dwarf::DW_AT_location, &location); |
| |
| llvm::Optional<std::vector<uint8_t>> value; |
| decoder.AddBlock(llvm::dwarf::DW_AT_call_value, &value); |
| |
| if (!decoder.Decode(die)) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| // Decode the register number. We currently expect the location to be exactly one DWARF opcode |
| // indicating the register (see call_site_parameter.h for more). |
| std::optional<uint32_t> register_num; |
| if (location && location->size() == 1) { |
| uint8_t location_op = location->at(0); |
| if (location_op >= llvm::dwarf::DW_OP_reg0 && location_op <= llvm::dwarf::DW_OP_reg31) |
| register_num = location_op - llvm::dwarf::DW_OP_reg0; |
| } |
| |
| // Convert a nonexistent value to one with an empty block. These are equivalent for our needs. |
| if (!value) |
| value.emplace(); |
| |
| return fxl::MakeRefCounted<CallSiteParameter>( |
| register_num, DwarfExpr(std::move(*value), MakeUncachedLazy(die))); |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeCollection(const llvm::DWARFDie& die) { |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| // Types must always use the parent of the abstract origin (if it exists) so they can be nested in |
| // the correct namespace. |
| llvm::DWARFDie parent; |
| decoder.AddAbstractParent(&parent); |
| |
| // TODO(fxbug.dev/97388): Support DW_AT_signature. |
| bool has_signature = false; |
| decoder.AddCustom(llvm::dwarf::DW_AT_signature, |
| [&has_signature](auto, auto) { has_signature = true; }); |
| |
| llvm::Optional<const char*> name; |
| decoder.AddCString(llvm::dwarf::DW_AT_name, &name); |
| |
| llvm::Optional<uint64_t> byte_size; |
| decoder.AddUnsignedConstant(llvm::dwarf::DW_AT_byte_size, &byte_size); |
| |
| llvm::Optional<bool> is_declaration; |
| decoder.AddBool(llvm::dwarf::DW_AT_declaration, &is_declaration); |
| |
| llvm::Optional<uint64_t> calling_convention; |
| decoder.AddUnsignedConstant(llvm::dwarf::DW_AT_calling_convention, &calling_convention); |
| |
| if (has_signature) |
| DisplayDebugTypesSectionWarning(); |
| |
| if (!decoder.Decode(die) || has_signature) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| auto result = fxl::MakeRefCounted<Collection>(static_cast<DwarfTag>(die.getTag())); |
| if (name) |
| result->set_assigned_name(*name); |
| if (byte_size) |
| result->set_byte_size(static_cast<uint32_t>(*byte_size)); |
| |
| // Handle sub-DIEs: data members and inheritance. |
| std::vector<LazySymbol> data; |
| std::vector<LazySymbol> inheritance; |
| std::vector<LazySymbol> template_params; |
| LazySymbol variant_part; |
| for (const llvm::DWARFDie& child : die) { |
| switch (child.getTag()) { |
| case llvm::dwarf::DW_TAG_inheritance: |
| inheritance.push_back(MakeLazy(child)); |
| break; |
| case llvm::dwarf::DW_TAG_member: |
| data.push_back(MakeLazy(child)); |
| break; |
| case llvm::dwarf::DW_TAG_variant_part: |
| // Currently we only support one variant_part per struct. This could be expanded to a vector |
| // if a compiler generates such a structure. |
| variant_part = MakeLazy(child); |
| break; |
| case llvm::dwarf::DW_TAG_template_type_parameter: |
| case llvm::dwarf::DW_TAG_template_value_parameter: |
| template_params.push_back(MakeLazy(child)); |
| break; |
| default: |
| break; // Skip everything else. |
| } |
| } |
| result->set_data_members(std::move(data)); |
| result->set_inherited_from(std::move(inheritance)); |
| result->set_template_params(std::move(template_params)); |
| result->set_variant_part(variant_part); |
| if (is_declaration) |
| result->set_is_declaration(*is_declaration); |
| |
| if (calling_convention) { |
| if (*calling_convention == Collection::kNormalCall || |
| *calling_convention == Collection::kPassByReference || |
| *calling_convention == Collection::kPassByValue) { |
| result->set_calling_convention( |
| static_cast<Collection::CallingConvention>(*calling_convention)); |
| } |
| } |
| |
| if (parent) |
| result->set_parent(MakeUncachedLazy(parent)); |
| |
| return result; |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeCompileUnit(const llvm::DWARFDie& die) { |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| llvm::Optional<const char*> name; |
| decoder.AddCString(llvm::dwarf::DW_AT_name, &name); |
| |
| llvm::Optional<uint64_t> language; |
| decoder.AddUnsignedConstant(llvm::dwarf::DW_AT_language, &language); |
| |
| llvm::Optional<uint64_t> addr_base; |
| decoder.AddSectionOffset(llvm::dwarf::DW_AT_addr_base, &addr_base); |
| |
| if (!decoder.Decode(die)) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| std::string name_str; |
| if (name) |
| name_str = *name; |
| |
| DwarfLang lang_enum = DwarfLang::kNone; |
| if (language && *language >= 0 && *language < static_cast<int>(DwarfLang::kLast)) |
| lang_enum = static_cast<DwarfLang>(*language); |
| |
| // Convert addr_base to std optional. |
| std::optional<uint64_t> addr_base_opt; |
| if (addr_base) |
| addr_base_opt = *addr_base; |
| |
| // We know the symbols_ is valid, that was checked on entry to CreateSymbol(). |
| return fxl::MakeRefCounted<CompileUnit>(static_cast<ModuleSymbols*>(symbols_.get())->GetWeakPtr(), |
| lang_enum, std::move(name_str), addr_base_opt); |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeDataMember(const llvm::DWARFDie& die) { |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| llvm::Optional<const char*> name; |
| decoder.AddCString(llvm::dwarf::DW_AT_name, &name); |
| |
| llvm::DWARFDie type; |
| decoder.AddReference(llvm::dwarf::DW_AT_type, &type); |
| |
| llvm::Optional<bool> artificial; |
| decoder.AddBool(llvm::dwarf::DW_AT_artificial, &artificial); |
| |
| llvm::Optional<bool> external; |
| decoder.AddBool(llvm::dwarf::DW_AT_external, &external); |
| |
| llvm::Optional<uint64_t> member_offset; |
| decoder.AddUnsignedConstant(llvm::dwarf::DW_AT_data_member_location, &member_offset); |
| |
| llvm::Optional<uint64_t> byte_size; |
| decoder.AddUnsignedConstant(llvm::dwarf::DW_AT_byte_size, &byte_size); |
| |
| llvm::Optional<uint64_t> bit_size; |
| decoder.AddUnsignedConstant(llvm::dwarf::DW_AT_bit_size, &bit_size); |
| llvm::Optional<int64_t> bit_offset; |
| decoder.AddSignedConstant(llvm::dwarf::DW_AT_bit_offset, &bit_offset); |
| llvm::Optional<uint64_t> data_bit_offset; |
| decoder.AddUnsignedConstant(llvm::dwarf::DW_AT_data_bit_offset, &data_bit_offset); |
| |
| ConstValue const_value; |
| decoder.AddConstValue(llvm::dwarf::DW_AT_const_value, &const_value); |
| |
| if (!decoder.Decode(die)) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| auto result = fxl::MakeRefCounted<DataMember>(); |
| if (name) |
| result->set_assigned_name(*name); |
| if (type) |
| result->set_type(MakeLazy(type)); |
| if (artificial) |
| result->set_artificial(*artificial); |
| if (external) |
| result->set_is_external(*external); |
| if (member_offset) |
| result->set_member_location(static_cast<uint32_t>(*member_offset)); |
| if (byte_size) |
| result->set_byte_size(static_cast<uint32_t>(*byte_size)); |
| if (bit_offset) |
| result->set_bit_offset(static_cast<int32_t>(*bit_offset)); |
| if (bit_size) |
| result->set_bit_size(static_cast<uint32_t>(*bit_size)); |
| if (data_bit_offset) |
| result->set_data_bit_offset(static_cast<uint32_t>(*data_bit_offset)); |
| result->set_const_value(std::move(const_value)); |
| |
| return result; |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeEnum(const llvm::DWARFDie& die) { |
| DwarfDieDecoder main_decoder(GetLLVMContext()); |
| |
| // Types must always use the parent of the abstract origin (if it exists) so they can be nested in |
| // the correct namespace. |
| llvm::DWARFDie parent; |
| main_decoder.AddAbstractParent(&parent); |
| |
| // TODO(fxbug.dev/97388): Support DW_AT_signature. |
| bool has_signature = false; |
| main_decoder.AddCustom(llvm::dwarf::DW_AT_signature, |
| [&has_signature](auto, auto) { has_signature = true; }); |
| |
| // Name is optional (enums can be anonymous). |
| llvm::Optional<const char*> type_name; |
| main_decoder.AddCString(llvm::dwarf::DW_AT_name, &type_name); |
| |
| llvm::Optional<uint64_t> byte_size; |
| main_decoder.AddUnsignedConstant(llvm::dwarf::DW_AT_byte_size, &byte_size); |
| |
| llvm::Optional<bool> is_declaration; |
| main_decoder.AddBool(llvm::dwarf::DW_AT_declaration, &is_declaration); |
| |
| // The type is optional for an enumeration. |
| llvm::DWARFDie type; |
| main_decoder.AddReference(llvm::dwarf::DW_AT_type, &type); |
| |
| // For decoding the individual enum values. |
| DwarfDieDecoder enumerator_decoder(GetLLVMContext()); |
| |
| llvm::Optional<const char*> enumerator_name; |
| enumerator_decoder.AddCString(llvm::dwarf::DW_AT_name, &enumerator_name); |
| |
| // Enum values can be signed or unsigned. This is determined by looking at the form of the storage |
| // for the underlying types. Since there are many values, we set the "signed" flag if any of them |
| // were signed, since a small positive integer could be represented either way but a signed value |
| // must be encoded differently. |
| // |
| // This could be enhanced by using ConstValues directly. See the enumeration header file for more. |
| llvm::Optional<uint64_t> enumerator_value; |
| bool is_signed = false; |
| enumerator_decoder.AddCustom( |
| llvm::dwarf::DW_AT_const_value, |
| [&enumerator_value, &is_signed](llvm::DWARFUnit*, const llvm::DWARFFormValue& value) { |
| if (value.getForm() == llvm::dwarf::DW_FORM_udata) { |
| enumerator_value = value.getAsUnsignedConstant(); |
| } else if (value.getForm() == llvm::dwarf::DW_FORM_sdata) { |
| // Cast signed values to unsigned. |
| if (auto signed_value = value.getAsSignedConstant()) { |
| is_signed = true; |
| enumerator_value = static_cast<uint64_t>(*signed_value); |
| } |
| // Else case is corrupted symbols or an unsupported format, just |
| // ignore this one. |
| } |
| }); |
| |
| if (has_signature) |
| DisplayDebugTypesSectionWarning(); |
| |
| if (!main_decoder.Decode(die) || has_signature) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| FX_CHECK(byte_size.hasValue()); |
| |
| Enumeration::Map map; |
| for (const llvm::DWARFDie& child : die) { |
| if (child.getTag() != llvm::dwarf::DW_TAG_enumerator) |
| continue; |
| |
| enumerator_name.reset(); |
| enumerator_value.reset(); |
| if (!enumerator_decoder.Decode(child)) |
| continue; |
| if (enumerator_name && enumerator_value) |
| map[*enumerator_value] = std::string(*enumerator_name); |
| } |
| |
| LazySymbol lazy_type; |
| if (type) |
| lazy_type = MakeLazy(type); |
| const char* type_name_str = type_name ? *type_name : ""; |
| auto result = fxl::MakeRefCounted<Enumeration>(type_name_str, std::move(lazy_type), *byte_size, |
| is_signed, std::move(map)); |
| if (parent) |
| result->set_parent(MakeUncachedLazy(parent)); |
| if (is_declaration) |
| result->set_is_declaration(*is_declaration); |
| return result; |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeFunctionType(const llvm::DWARFDie& die) { |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| // Types must always use the parent of the abstract origin (if it exists) so they can be nested in |
| // the correct namespace. |
| llvm::DWARFDie parent; |
| decoder.AddAbstractParent(&parent); |
| |
| llvm::DWARFDie return_type; |
| decoder.AddReference(llvm::dwarf::DW_AT_type, &return_type); |
| |
| if (!decoder.Decode(die)) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| // Handle sub-DIEs (this only has parameters). |
| std::vector<LazySymbol> parameters; |
| for (const llvm::DWARFDie& child : die) { |
| switch (child.getTag()) { |
| case llvm::dwarf::DW_TAG_formal_parameter: |
| parameters.push_back(MakeLazy(child)); |
| break; |
| default: |
| break; // Skip everything else. |
| } |
| } |
| |
| LazySymbol lazy_return_type; |
| if (return_type) |
| lazy_return_type = MakeLazy(return_type); |
| |
| auto function = fxl::MakeRefCounted<FunctionType>(lazy_return_type, std::move(parameters)); |
| if (parent) |
| function->set_parent(MakeUncachedLazy(parent)); |
| return function; |
| } |
| |
| // Imported declarations are "using" statements that don't provide a new name like |
| // "using std::vector;". |
| // |
| // Type renames like "using Foo = std::vector;" is encoded as a typedef. |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeImportedDeclaration(const llvm::DWARFDie& die) { |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| llvm::DWARFDie imported; |
| decoder.AddReference(llvm::dwarf::DW_AT_import, &imported); |
| |
| if (!decoder.Decode(die) || !imported) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| return fxl::MakeRefCounted<ModifiedType>(DwarfTag::kImportedDeclaration, MakeLazy(imported)); |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeInheritedFrom(const llvm::DWARFDie& die) { |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| llvm::DWARFDie type; |
| decoder.AddReference(llvm::dwarf::DW_AT_type, &type); |
| |
| // The DW_AT_data_member_location can either be a constant or an expression. |
| llvm::Optional<uint64_t> member_offset; |
| std::vector<uint8_t> offset_expression; |
| decoder.AddCustom(llvm::dwarf::DW_AT_data_member_location, |
| [&member_offset, &offset_expression](llvm::DWARFUnit* unit, |
| const llvm::DWARFFormValue& form) { |
| if (form.isFormClass(llvm::DWARFFormValue::FC_Exprloc)) { |
| // Location expression. |
| llvm::ArrayRef<uint8_t> block = *form.getAsBlock(); |
| offset_expression.assign(block.data(), block.data() + block.size()); |
| } else if (form.isFormClass(llvm::DWARFFormValue::FC_Constant)) { |
| // Constant value. |
| member_offset = form.getAsUnsignedConstant(); |
| } |
| // Otherwise leave both empty. |
| }); |
| |
| if (!decoder.Decode(die)) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| LazySymbol lazy_type; |
| if (type) |
| lazy_type = MakeLazy(type); |
| |
| if (member_offset) |
| return fxl::MakeRefCounted<InheritedFrom>(std::move(lazy_type), *member_offset); |
| if (!offset_expression.empty()) { |
| return fxl::MakeRefCounted<InheritedFrom>(std::move(lazy_type), |
| DwarfExpr(std::move(offset_expression))); |
| } |
| return fxl::MakeRefCounted<Symbol>(); // Missing location. |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeLexicalBlock(const llvm::DWARFDie& die) { |
| auto block = fxl::MakeRefCounted<CodeBlock>(DwarfTag::kLexicalBlock); |
| block->set_code_ranges(GetCodeRanges(die)); |
| |
| // Handle sub-DIEs: child blocks and variables. |
| // |
| // Note: this partially duplicates a similar loop in DecodeFunction(). |
| std::vector<LazySymbol> inner_blocks; |
| std::vector<LazySymbol> variables; |
| std::vector<LazySymbol> call_sites; |
| for (const llvm::DWARFDie& child : die) { |
| switch (child.getTag()) { |
| case llvm::dwarf::DW_TAG_variable: |
| variables.push_back(MakeLazy(child)); |
| break; |
| case llvm::dwarf::DW_TAG_inlined_subroutine: |
| case llvm::dwarf::DW_TAG_lexical_block: |
| inner_blocks.push_back(MakeLazy(child)); |
| break; |
| case llvm::dwarf::DW_TAG_call_site: |
| call_sites.push_back(MakeLazy(child)); |
| break; |
| default: |
| break; // Skip everything else. |
| } |
| } |
| block->set_inner_blocks(std::move(inner_blocks)); |
| block->set_variables(std::move(variables)); |
| block->set_call_sites(std::move(call_sites)); |
| |
| return block; |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeMemberPtr(const llvm::DWARFDie& die) { |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| llvm::DWARFDie container_type; |
| decoder.AddReference(llvm::dwarf::DW_AT_containing_type, &container_type); |
| llvm::DWARFDie type; |
| decoder.AddReference(llvm::dwarf::DW_AT_type, &type); |
| |
| if (!decoder.Decode(die) || !container_type || !type) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| auto member_ptr = fxl::MakeRefCounted<MemberPtr>(MakeLazy(container_type), MakeLazy(type)); |
| return member_ptr; |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeModifiedType(const llvm::DWARFDie& die) { |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| // Types must always use the parent of the abstract origin (if it exists) so they can be nested in |
| // the correct namespace. |
| llvm::DWARFDie parent; |
| decoder.AddAbstractParent(&parent); |
| |
| llvm::Optional<const char*> name; |
| decoder.AddCString(llvm::dwarf::DW_AT_name, &name); |
| |
| llvm::DWARFDie modified; |
| decoder.AddReference(llvm::dwarf::DW_AT_type, &modified); |
| |
| if (!decoder.Decode(die)) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| // Modified type may be null for "void*". |
| LazySymbol lazy_modified; |
| if (modified) |
| lazy_modified = MakeLazy(modified); |
| |
| auto result = fxl::MakeRefCounted<ModifiedType>(static_cast<DwarfTag>(die.getTag()), |
| std::move(lazy_modified)); |
| if (name) |
| result->set_assigned_name(*name); |
| |
| if (parent) |
| result->set_parent(MakeUncachedLazy(parent)); |
| |
| return result; |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeNamespace(const llvm::DWARFDie& die) { |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| // Types must always use the parent of the abstract origin (if it exists) so they can be nested in |
| // the correct namespace. |
| llvm::DWARFDie parent; |
| decoder.AddAbstractParent(&parent); |
| |
| llvm::Optional<const char*> name; |
| decoder.AddCString(llvm::dwarf::DW_AT_name, &name); |
| |
| if (!decoder.Decode(die)) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| auto result = fxl::MakeRefCounted<Namespace>(); |
| if (name) |
| result->set_assigned_name(*name); |
| |
| if (parent) |
| result->set_parent(MakeUncachedLazy(parent)); |
| return result; |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeTemplateParameter(const llvm::DWARFDie& die, |
| DwarfTag tag) { |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| llvm::Optional<const char*> name; |
| decoder.AddCString(llvm::dwarf::DW_AT_name, &name); |
| |
| llvm::DWARFDie type; |
| decoder.AddReference(llvm::dwarf::DW_AT_type, &type); |
| |
| // DW_TAG_template_value_parameter ones will also have a value if we need it in the future. |
| |
| if (!decoder.Decode(die) || !name || !type) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| return fxl::MakeRefCounted<TemplateParameter>(*name, MakeLazy(type), |
| tag == DwarfTag::kTemplateValueParameter); |
| } |
| |
| // Clang and GCC use "unspecified" types to encode "decltype(nullptr)". When used as a variable this |
| // appears as a pointer with 0 value, despite not having any declared size in the symbols. |
| // Therefore, we make up a byte size equal to the pointer size (8 bytes on our 64-bit systems). |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeUnspecifiedType(const llvm::DWARFDie& die) { |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| // Types must always use the parent of the abstract origin (if it exists) so |
| // they can be nested in the correct namespace. |
| llvm::DWARFDie parent; |
| decoder.AddAbstractParent(&parent); |
| |
| llvm::Optional<const char*> name; |
| decoder.AddCString(llvm::dwarf::DW_AT_name, &name); |
| |
| if (!decoder.Decode(die)) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| auto result = fxl::MakeRefCounted<Type>(DwarfTag::kUnspecifiedType); |
| if (name) |
| result->set_assigned_name(*name); |
| result->set_byte_size(8); // Assume pointer. |
| if (parent) |
| result->set_parent(MakeUncachedLazy(parent)); |
| |
| return result; |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeVariable(const llvm::DWARFDie& die, |
| bool is_specification) { |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| llvm::DWARFDie specification; |
| decoder.AddReference(llvm::dwarf::DW_AT_specification, &specification); |
| |
| llvm::Optional<const char*> name; |
| decoder.AddCString(llvm::dwarf::DW_AT_name, &name); |
| |
| VariableLocation location; |
| decoder.AddCustom(llvm::dwarf::DW_AT_location, |
| [&location, source = MakeUncachedLazy(die)](llvm::DWARFUnit* unit, |
| const llvm::DWARFFormValue& value) { |
| location = DecodeVariableLocation(unit, value, source); |
| }); |
| |
| llvm::DWARFDie type; |
| decoder.AddReference(llvm::dwarf::DW_AT_type, &type); |
| |
| llvm::Optional<bool> external; |
| decoder.AddBool(llvm::dwarf::DW_AT_external, &external); |
| |
| llvm::Optional<bool> artificial; |
| decoder.AddBool(llvm::dwarf::DW_AT_artificial, &artificial); |
| |
| ConstValue const_value; |
| decoder.AddConstValue(llvm::dwarf::DW_AT_const_value, &const_value); |
| |
| if (!decoder.Decode(die)) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| fxl::RefPtr<Variable> variable; |
| |
| // If this DIE has a link to a specification (and we haven't already followed such a link), first |
| // read that in to get things like the mangled name, parent context, and declaration locations. |
| // Then we'll overlay our values on that object. |
| if (!is_specification && specification) { |
| auto spec = DecodeVariable(specification, true); |
| Variable* spec_variable = spec->As<Variable>(); |
| // If the specification is invalid, just ignore it and read out the values |
| // that we can find in this DIE. An empty one will be created below. |
| if (spec_variable) |
| variable = fxl::RefPtr<Variable>(spec_variable); |
| } |
| if (!variable) { |
| variable = fxl::MakeRefCounted<Variable>(static_cast<DwarfTag>(die.getTag())); |
| } |
| |
| if (name) |
| variable->set_assigned_name(*name); |
| if (type) |
| variable->set_type(MakeLazy(type)); |
| if (external) |
| variable->set_is_external(*external); |
| if (artificial) |
| variable->set_artificial(*artificial); |
| variable->set_location(std::move(location)); |
| variable->set_const_value(std::move(const_value)); |
| |
| if (!variable->parent()) { |
| // Set the parent symbol when it hasn't already been set. As with functions, we always want the |
| // specification's parent. See DecodeFunction(). |
| llvm::DWARFDie parent = die.getParent(); |
| if (parent) |
| variable->set_parent(MakeUncachedLazy(parent)); |
| } |
| return variable; |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeVariant(const llvm::DWARFDie& die) { |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| // Assume unsigned discriminant values since this is always true for our current uses. See |
| // Variant::discr_value() comment for more. |
| llvm::Optional<uint64_t> discr_value; |
| decoder.AddUnsignedConstant(llvm::dwarf::DW_AT_discr_value, &discr_value); |
| |
| if (!decoder.Decode(die)) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| // Collect the data members. |
| std::vector<LazySymbol> members; |
| for (const llvm::DWARFDie& child : die) { |
| if (child.getTag() == llvm::dwarf::DW_TAG_member) |
| members.push_back(MakeLazy(child)); |
| } |
| |
| // Convert from LLVM-style to C++-style optional. |
| std::optional<uint64_t> discr; |
| if (discr_value) |
| discr = *discr_value; |
| |
| return fxl::MakeRefCounted<Variant>(discr, std::move(members)); |
| } |
| |
| fxl::RefPtr<Symbol> DwarfSymbolFactory::DecodeVariantPart(const llvm::DWARFDie& die) { |
| DwarfDieDecoder decoder(GetLLVMContext()); |
| |
| // The discriminant is the DataMember in the variant whose value indicates which variant currently |
| // applies. |
| llvm::DWARFDie discriminant; |
| decoder.AddReference(llvm::dwarf::DW_AT_discr, &discriminant); |
| |
| if (!decoder.Decode(die) || !discriminant) |
| return fxl::MakeRefCounted<Symbol>(); |
| |
| // Look for variants in this variant_part. It will also have a data member for the discriminant |
| // but we will have already found that above via reference. |
| std::vector<LazySymbol> variants; |
| for (const llvm::DWARFDie& child : die) { |
| if (child.getTag() == llvm::dwarf::DW_TAG_variant) |
| variants.push_back(MakeLazy(child)); |
| } |
| |
| return fxl::MakeRefCounted<VariantPart>(MakeLazy(discriminant), std::move(variants)); |
| } |
| |
| } // namespace zxdb |