blob: 2075881d8e5b527fec74741815ec31b61b41060d [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.
#ifndef SRC_DEVELOPER_DEBUG_ZXDB_SYMBOLS_DWARF_DIE_DECODER_H_
#define SRC_DEVELOPER_DEBUG_ZXDB_SYMBOLS_DWARF_DIE_DECODER_H_
#include <string>
#include <utility>
#include "lib/fit/function.h"
#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 {
class ConstValue;
// 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.
explicit DwarfDieDecoder(llvm::DWARFContext* context);
~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);
void AddConstValue(llvm::dwarf::Attribute attribute, ConstValue* const_value);
// For cross-DIE references. Note that the resulting DIE may not be in the same unit. 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.
// Theoretically 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,
fit::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);
public:
using Dispatch =
std::pair<llvm::dwarf::Attribute, fit::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::DWARFDie& die, int abstract_origin_refs_to_follow);
// Decodes a cross-DIE reference. Return value will be !isValid() on failure.
llvm::DWARFDie DecodeReference(const llvm::DWARFFormValue& form);
llvm::DWARFContext* context_;
// 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
#endif // SRC_DEVELOPER_DEBUG_ZXDB_SYMBOLS_DWARF_DIE_DECODER_H_