| // 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. |
| |
| #pragma once |
| |
| #include <functional> |
| #include <string> |
| #include <utility> |
| |
| #include "llvm/ADT/Optional.h" |
| #include "llvm/BinaryFormat/Dwarf.h" |
| #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" |
| #include "src/lib/fxl/macros.h" |
| |
| namespace llvm { |
| class DWARFUnit; |
| class DWARFContext; |
| class DWARFDataExtractor; |
| class DWARFDebugInfoEntry; |
| class DWARFDie; |
| class DWARFFormValue; |
| } // namespace llvm |
| |
| namespace zxdb { |
| |
| // Decodes the desired attributes of a given DWARF Debug Info Entry ("DIE"). |
| // |
| // This transparently follows DW_AT_abstract_origin attributes. This is used |
| // to implement "inheritance" of DIEs. |
| // |
| // To use, create once for the unit and register the output variables with the |
| // Add* functions. Then loop through the relevant entries. In the loop first |
| // reset() the output variables (so you can tell which were set), then call |
| // Decode(). |
| class DwarfDieDecoder { |
| public: |
| // DW_AT_high_pc is special: If it is of class "address", it's an address, |
| // and if it's of class "constant" it's an unsigned integer offset from the |
| // low PC. This struct encodes whether it was a constant or not in the |
| // output. Use with AddHighPC(). |
| struct HighPC { |
| HighPC() = default; |
| HighPC(bool c, uint64_t v) : is_constant(c), value(v) {} |
| |
| bool is_constant = false; |
| uint64_t value = 0; |
| }; |
| |
| // The context and unit must outlive this class. |
| DwarfDieDecoder(llvm::DWARFContext* context, llvm::DWARFUnit* unit); |
| ~DwarfDieDecoder(); |
| |
| // Adds a check for the given attribute. If the attribute is encountered, |
| // the given boolean will be set to true. You can share a bool pointer |
| // between different calls to AddPresenceCheck() to check if any of a set |
| // of attributes is available. It does not check the type of validity of |
| // the attribute. |
| // |
| // The output pointer must remain valid until the last call to Decode() |
| // has returned. |
| void AddPresenceCheck(llvm::dwarf::Attribute attribute, bool* present); |
| |
| // These register for a given attribute, and call the similarly-named |
| // function in llvm::DWARFFormValue to extract the attribute and place it |
| // into the given output variable. |
| // |
| // The output pointers must remain valid until the last call to Decode() |
| // has returned. |
| void AddBool(llvm::dwarf::Attribute attribute, llvm::Optional<bool>* output); |
| void AddUnsignedConstant(llvm::dwarf::Attribute attribute, |
| llvm::Optional<uint64_t>* output); |
| void AddSignedConstant(llvm::dwarf::Attribute attribute, |
| llvm::Optional<int64_t>* output); |
| void AddAddress(llvm::dwarf::Attribute attribute, |
| llvm::Optional<uint64_t>* output); |
| void AddHighPC(llvm::Optional<HighPC>* output); |
| void AddCString(llvm::dwarf::Attribute attribute, |
| llvm::Optional<const char*>* output); |
| void AddLineTableFile(llvm::dwarf::Attribute attribute, |
| llvm::Optional<std::string>* output); |
| |
| // For cross-DIE references. These references can be within the current |
| // unit (byte offsets, not DIE indices), or from within the object file. |
| // To accommodate both, this function will fill in the corresponding output |
| // variable according to the storage form of the attribute. |
| // |
| // Most callers will want to use the next variant which returns a DIE. |
| void AddReference(llvm::dwarf::Attribute attribute, |
| llvm::Optional<uint64_t>* unit_offset, |
| llvm::Optional<uint64_t>* global_offset); |
| |
| // Variant of the above AddReference that automatically converts a reference |
| // to an actual DIE. If the attribute doesn't exist or is invalid, this DIE |
| // will be !isValid(). |
| void AddReference(llvm::dwarf::Attribute attribute, llvm::DWARFDie* output); |
| |
| // Extract a file name. File names (e.g. for DW_AT_decl_file) are not |
| // strings but rather indices into the file name table for the corresponding |
| // unit. This accessor resolves the string automatically. |
| void AddFile(llvm::dwarf::Attribute attribute, |
| llvm::Optional<std::string>* output); |
| |
| // A special handler to get the parent of the most deep abstract origin. |
| // |
| // Most DIEs can have an "abstract origin" which is another DIE that |
| // underlays values. Theroretically abstract origins can be linked into |
| // arbitrarily long chains. In the current Clang this mostly happens for |
| // inlined functions, where the inlined instance references the actual |
| // function definition as its abstract origin. But abstract origins can |
| // theoretically appear almost anywhere. |
| // |
| // Normally this class handles abstract origins transparently when querying |
| // attributes. But the parent DIE is not an attribute so needs to be handled |
| // explicitly. In the example of inlined functions, the parent of the inlined |
| // subroutine DIE will be the block it's inlined into, but the parent of the |
| // abstract origin will be the namespace or class that lexically encloses |
| // that function. |
| // |
| // This function will cause the parent of the deepest abstract origin to be |
| // placed into the given output when the DIE is decoded. |
| // |
| // If there is no abstract origin, this will be filled in with the regular |
| // parent of the DIE. The only case the output should be !isValid() is when |
| // decoding a toplevel DIE with no parent. |
| void AddAbstractParent(llvm::DWARFDie* output); |
| |
| // Extracts data with a custom callback. When the attribute is encountered, |
| // the callback is executed with the associated form value. This can be used |
| // to cover attributes that could be encoded using multiple different |
| // encodings. |
| void AddCustom(llvm::dwarf::Attribute attribute, |
| std::function<void(const llvm::DWARFFormValue&)> callback); |
| |
| // Decode one info entry. Returns true on success, false means the DIE |
| // was corrupt. The outputs for each encountered attribute will be set. |
| bool Decode(const llvm::DWARFDie& die); |
| bool Decode(const llvm::DWARFDebugInfoEntry& die); |
| |
| public: |
| using Dispatch = std::pair<llvm::dwarf::Attribute, |
| std::function<void(const llvm::DWARFFormValue&)>>; |
| |
| // Backend for Decode() above. |
| // |
| // Following abstract origins generates a recursive call. To prevent infinite |
| // recursion for corrupt symbols, this function takes a maximum number of |
| // abstract origin references to follow which is decremented each time a |
| // recursive call is made. When this gets to 0, no more abstract origin |
| // references will be followed. |
| bool DecodeInternal(const llvm::DWARFDebugInfoEntry& die, |
| int abstract_origin_refs_to_follow); |
| |
| llvm::DWARFContext* context_; |
| llvm::DWARFUnit* unit_; |
| llvm::DWARFDataExtractor extractor_; |
| |
| // Normally there will be few attributes and a brute-force search through a |
| // contiguous array will be faster than a map lookup. |
| std::vector<Dispatch> attrs_; |
| |
| // Non-null indicates that the caller has requested the abstract parent (see |
| // AddAbstractParent() above) be computed. This variable will hold the |
| // desired output location for the parent of the decoded DIE. |
| llvm::DWARFDie* abstract_parent_ = nullptr; |
| |
| // Used during decode. This should be cleared each time a new DIE is decoded |
| // and tracks which attributes have been seen across recursive calls of |
| // DecodeInternal (following abstract references). This prevents us from |
| // decoding the same attribute more than once across abstract origins (we |
| // always want the first one). |
| std::vector<llvm::dwarf::Attribute> seen_attrs_; |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(DwarfDieDecoder); |
| }; |
| |
| } // namespace zxdb |