blob: bbba82299efa0740e1188a901512add73e543070 [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.
#pragma once
#include <stdint.h>
#include <functional>
#include <memory>
#include <vector>
#include "garnet/bin/zxdb/common/err.h"
#include "garnet/bin/zxdb/symbols/arch.h"
#include "garnet/bin/zxdb/symbols/symbol_context.h"
#include "lib/fxl/macros.h"
#include "lib/fxl/memory/ref_ptr.h"
#include "lib/fxl/memory/weak_ptr.h"
namespace llvm {
class DataExtractor;
} // namespace llvm
namespace zxdb {
class SymbolDataProvider;
// This class evaluates DWARF expressions. These expressions are used to encode
// the locations of variables and a few other nontrivial lookups.
//
// This class is complicated by supporting asynchronous interactions with the
// debugged program. This means that accessing register and memory data (which
// may be required to evaluate the expression) may be asynchronous.
//
// eval_ = std::make_unique<DwarfExprEval>();
// eval_.eval(..., [](DwarfExprEval* eval, const Err& err) {
// if (err.has_error()) {
// // Handle error.
// } else {
// ... use eval->GetResult() ...
// }
// });
class DwarfExprEval {
public:
// Type of completion from a call. Async completion will happen in a callback
// in the future.
enum class Completion { kSync, kAsync };
// A DWARF expression can compute either the address of the desired object in
// the debugged programs address space, or it can compute the actual value of
// the object (because it may not exist in memory).
enum class ResultType { kPointer, kValue };
// Storage for opcode data.
using Expression = std::vector<uint8_t>;
using CompletionCallback =
std::function<void(DwarfExprEval* eval, const Err& err)>;
DwarfExprEval();
~DwarfExprEval();
// A complete expression has finished executing but may or may not have had
// an error. A successful expression indicates execution is complete and
// there is a valid result to read.
bool is_complete() const { return is_complete_; }
bool is_success() const { return is_success_; }
// Valid when is_success(), this indicates how to interpret the value from
// GetResult().
ResultType GetResultType() const;
// Valid when is_success(), this returns the result of evaluating the
// expression. The meaning will be dependent on the context of the expression
// being evaluated.
uint64_t GetResult() const;
// This will take a reference to the SymbolDataProvider until the computation
// is complete.
//
// The symbol context is used to evaluate relative addresses. It should be
// the context associated with the module that this expression is from.
//
// The return value will indicate if the request completed synchronously. In
// synchronous completion the callback will have been called reentrantly from
// within the stack of this function. This does not indicate success as it
// could succeed or fail both synchronously and asynchronously.
Completion Eval(fxl::RefPtr<SymbolDataProvider> data_provider,
const SymbolContext& symbol_context, Expression expr,
CompletionCallback cb);
private:
// Evaluates the next phases of the expression until an asynchronous operation
// is required.
void ContinueEval();
// Evaluates a single operation.
Completion EvalOneOp();
// Adds a register's contents + an offset to the stack. Use 0 for the offset
// to get the raw register value.
Completion PushRegisterWithOffset(int dwarf_register_number, int64_t offset);
// Pushes a value on the stack.
void Push(uint64_t value);
// These read constant data from the current index in the stream. The size of
// the data is in byte_size, and the result will be extended to 64 bits
// according to the type.
//
// They return true if the value was read, false if there wasn't enough data
// (they will issue the error internally, the calling code should just return
// on failure).
bool ReadSigned(int byte_size, int64_t* output);
bool ReadUnsigned(int byte_size, uint64_t* output);
// Reads a signed or unsigned LEB constant from the stream. They return true
// if the value was read, false if there wasn't enough data (they will issue
// the error internally, the calling code should just return on failure).
bool ReadLEBSigned(int64_t* output);
bool ReadLEBUnsigned(uint64_t* output);
void ReportError(const std::string& msg);
void ReportError(const Err& err);
void ReportStackUnderflow();
void ReportUnimplementedOpcode(uint8_t op);
// Executes the given unary operation with the top stack entry as the
// parameter and pushes the result.
Completion OpUnary(uint64_t (*op)(uint64_t));
// Executes the given binary operation by popping the top two stack entries
// as parameters (the first is the next-to-top, the second is the top) and
// pushing the result on the stack.
Completion OpBinary(uint64_t (*op)(uint64_t, uint64_t));
// Operations. On call, the expr_index_ will index the byte following the
// opcode, and on return expr_index_ will index the next instruction (any
// parameters will be consumed).
Completion OpAddr();
Completion OpBra();
Completion OpBreg(uint8_t op);
Completion OpDeref();
Completion OpDiv();
Completion OpDrop();
Completion OpDup();
Completion OpFbreg();
Completion OpRegx();
Completion OpBregx();
Completion OpMod();
Completion OpOver();
Completion OpPick();
Completion OpPlusUconst();
Completion OpPushSigned(int byte_count);
Completion OpPushUnsigned(int byte_count);
Completion OpPushLEBSigned();
Completion OpPushLEBUnsigned();
Completion OpRot();
Completion OpSkip();
Completion OpStackValue();
Completion OpSwap();
// Adjusts the instruction offset by the given amount, handling out-of-bounds
// as appropriate. This is the backend for jumps and branches.
void Skip(int64_t amount);
fxl::RefPtr<SymbolDataProvider> data_provider_;
SymbolContext symbol_context_;
// The expression. See also expr_index_.
Expression expr_;
// Index into expr_ of the next thing to read. This is a uint32_t to
// integrate with LLVM DataExtractor.
uint32_t expr_index_ = 0;
CompletionCallback completion_callback_;
// Allocated on the heap to avoid exposing LLVM headers.
std::unique_ptr<llvm::DataExtractor> data_extractor_;
// The result type. Normally expressions compute pointers unless explicitly
// tagged as a value.
ResultType result_type_ = ResultType::kPointer;
// Indicates that execution is complete. When this is true, the callback will
// have been issued. A complete expression could have stopped on error or
// success (see is_success_).
bool is_complete_ = false;
// Indicates that the expression is complete and that there is a result
// value.
bool is_success_ = false;
std::vector<uint64_t> stack_;
fxl::WeakPtrFactory<DwarfExprEval> weak_factory_;
FXL_DISALLOW_COPY_AND_ASSIGN(DwarfExprEval);
};
} // namespace zxdb