blob: fc3779638eca0f760b4b7bd2aea27b3c88e2a4bf [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.
#include "garnet/bin/zxdb/symbols/dwarf_expr_eval.h"
#include "garnet/bin/zxdb/symbols/arch.h"
#include "garnet/bin/zxdb/symbols/mock_symbol_data_provider.h"
#include "garnet/lib/debug_ipc/helper/platform_message_loop.h"
#include "gtest/gtest.h"
#include "lib/fxl/memory/weak_ptr.h"
#include "llvm/BinaryFormat/Dwarf.h"
namespace zxdb {
namespace {
// Base address of the imaginary module. Relative addresses will be relative to
// this number.
constexpr TargetPointer kModuleBase = 0x78000000;
class DwarfExprEvalTest : public testing::Test {
public:
DwarfExprEvalTest()
: provider_(fxl::MakeRefCounted<MockSymbolDataProvider>()) {
loop_.Init();
}
~DwarfExprEvalTest() { loop_.Cleanup(); }
DwarfExprEval& eval() { return eval_; }
fxl::RefPtr<MockSymbolDataProvider> provider() { return provider_; }
const SymbolContext symbol_context() const { return symbol_context_; }
debug_ipc::MessageLoop& loop() { return loop_; }
// If expected_message is non-null, this error message will be expected on
// failure. The expected result will only be checked on success, true, and
// the expected_message will only be checked on failure.
void DoEvalTest(const std::vector<uint8_t> data, bool expected_success,
DwarfExprEval::Completion expected_completion,
uint64_t expected_result,
DwarfExprEval::ResultType expected_result_type,
const char* expected_message = nullptr);
private:
DwarfExprEval eval_;
debug_ipc::PlatformMessageLoop loop_;
fxl::RefPtr<MockSymbolDataProvider> provider_;
SymbolContext symbol_context_ = SymbolContext(kModuleBase);
};
void DwarfExprEvalTest::DoEvalTest(
const std::vector<uint8_t> data, bool expected_success,
DwarfExprEval::Completion expected_completion, uint64_t expected_result,
DwarfExprEval::ResultType expected_result_type,
const char* expected_message) {
bool callback_issued = false;
EXPECT_EQ(
expected_completion,
eval_.Eval(provider(), symbol_context_, data,
[&callback_issued, expected_success, expected_completion,
expected_result, expected_result_type,
expected_message](DwarfExprEval* eval, const Err& err) {
EXPECT_TRUE(eval->is_complete());
EXPECT_EQ(expected_success, !err.has_error()) << err.msg();
if (err.ok()) {
EXPECT_EQ(expected_result, eval->GetResult());
EXPECT_EQ(expected_result_type, eval->GetResultType());
} else if (expected_message) {
EXPECT_EQ(expected_message, err.msg());
}
callback_issued = true;
// When we're doing an async completion, need to exit the
// message loop to continue with the test.
if (expected_completion == DwarfExprEval::Completion::kAsync)
debug_ipc::MessageLoop::Current()->QuitNow();
}));
if (expected_completion == DwarfExprEval::Completion::kAsync) {
// In the async case the message loop needs to be run to get the result.
EXPECT_FALSE(eval_.is_complete());
EXPECT_FALSE(callback_issued);
// Ensure the callback was made after running the loop.
loop_.Run();
}
EXPECT_TRUE(eval_.is_complete());
EXPECT_TRUE(callback_issued);
}
} // namespace
TEST_F(DwarfExprEvalTest, NoResult) {
const char kNoResults[] = "DWARF expression produced no results.";
// Empty expression.
DoEvalTest({}, false, DwarfExprEval::Completion::kSync, 0,
DwarfExprEval::ResultType::kPointer, kNoResults);
// Nonempty expression that produces no results.
DoEvalTest({llvm::dwarf::DW_OP_nop}, false, DwarfExprEval::Completion::kSync,
0, DwarfExprEval::ResultType::kPointer, kNoResults);
}
TEST_F(DwarfExprEvalTest, MarkValue) {
// A computation without "stack_value" should report the result type as a
// pointers.
DoEvalTest({llvm::dwarf::DW_OP_lit4}, true, DwarfExprEval::Completion::kSync,
4u, DwarfExprEval::ResultType::kPointer);
// "stack value" should mark the result as a stack value and terminate the
// computation, skipping the last instruction.
DoEvalTest({llvm::dwarf::DW_OP_lit4, llvm::dwarf::DW_OP_stack_value,
llvm::dwarf::DW_OP_lit5},
true, DwarfExprEval::Completion::kSync, 4u,
DwarfExprEval::ResultType::kValue);
}
// Tests that we can recover from infinite loops and destroy the evaluator
// when it's got an asynchronous operation pending.
TEST_F(DwarfExprEvalTest, InfiniteLoop) {
// This expression loops back to the beginning infinitely.
std::vector<uint8_t> loop_data = {llvm::dwarf::DW_OP_skip, 0xfd, 0xff};
std::unique_ptr<DwarfExprEval> eval = std::make_unique<DwarfExprEval>();
bool callback_issued = false;
eval->Eval(provider(), symbol_context(), loop_data,
[&callback_issued](DwarfExprEval* eval, const Err& err) {
callback_issued = true;
});
// Let the message loop process messages for a few times so the evaluator can
// run.
loop().PostTask(FROM_HERE,
[]() { debug_ipc::MessageLoop::Current()->QuitNow(); });
loop().Run();
loop().PostTask(FROM_HERE,
[]() { debug_ipc::MessageLoop::Current()->QuitNow(); });
loop().Run();
// Reset the evaluator, this should cancel everything.
eval.reset();
// This should not crash (the evaluator may have posted a pending task
// that will get executed when we run the loop again, and it should notice
// the object is gone).
loop().PostTask(FROM_HERE,
[]() { debug_ipc::MessageLoop::Current()->QuitNow(); });
loop().Run();
// Callback should never have been issued.
EXPECT_FALSE(callback_issued);
}
// Tests synchronously reading a single register.
TEST_F(DwarfExprEvalTest, SyncRegister) {
constexpr uint64_t kValue = 0x1234567890123;
provider()->AddRegisterValue(0, true, kValue);
DoEvalTest({llvm::dwarf::DW_OP_reg0}, true, DwarfExprEval::Completion::kSync,
kValue, DwarfExprEval::ResultType::kValue);
}
// Tests the encoding form of registers as parameters to an operation rather
// than the version encoded in the operation.
//
// Also tests DW_OP_nop.
TEST_F(DwarfExprEvalTest, SyncRegisterAsNumber) {
constexpr uint64_t kValue = 0x1234567890123;
provider()->AddRegisterValue(1, true, kValue);
// Use "regx" which will read the register number as a ULEB following it.
// The byte is the ULEB-encoded version of 1 (high bit set to indicate it's
// the last byte).
std::vector<uint8_t> expr_data;
expr_data.push_back(llvm::dwarf::DW_OP_nop);
expr_data.push_back(llvm::dwarf::DW_OP_regx);
expr_data.push_back(0b10000001);
DoEvalTest(expr_data, true, DwarfExprEval::Completion::kSync, kValue,
DwarfExprEval::ResultType::kValue);
}
// Tests asynchronously reading a single register.
TEST_F(DwarfExprEvalTest, AsyncRegister) {
constexpr uint64_t kValue = 0x1234567890123;
provider()->AddRegisterValue(0, false, kValue);
DoEvalTest({llvm::dwarf::DW_OP_reg0}, true, DwarfExprEval::Completion::kAsync,
kValue, DwarfExprEval::ResultType::kValue);
}
// Tests synchronously hitting an invalid opcode.
TEST_F(DwarfExprEvalTest, SyncInvalidOp) {
// Make a program that consists only of a user-defined opcode (not supported).
// Can't use DW_OP_lo_user because that's a GNU TLS extension we know about.
DoEvalTest({llvm::dwarf::DW_OP_lo_user + 1}, false,
DwarfExprEval::Completion::kSync, 0,
DwarfExprEval::ResultType::kValue,
"Invalid opcode 0xe1 in DWARF expression.");
}
// Tests synchronously hitting an invalid opcode (async error handling).
TEST_F(DwarfExprEvalTest, AsyncInvalidOp) {
constexpr uint64_t kValue = 0x1234567890123;
provider()->AddRegisterValue(0, false, kValue);
// Make a program that consists of getting an async register and then
// executing an invalid opcode. Can't use DW_OP_lo_user because that's a GNU
// TLS extension we know about.
std::vector<uint8_t> expr_data;
expr_data.push_back(llvm::dwarf::DW_OP_reg0);
expr_data.push_back(llvm::dwarf::DW_OP_lo_user + 1);
DoEvalTest(expr_data, false, DwarfExprEval::Completion::kAsync, 0,
DwarfExprEval::ResultType::kPointer,
"Invalid opcode 0xe1 in DWARF expression.");
}
// Tests the special opcodes that also encode a 0-31 literal.
TEST_F(DwarfExprEvalTest, LiteralOp) {
DoEvalTest({llvm::dwarf::DW_OP_lit4}, true, DwarfExprEval::Completion::kSync,
4u, DwarfExprEval::ResultType::kPointer);
}
// Tests that reading fixed-length constant without enough room fails.
TEST_F(DwarfExprEvalTest, Const4ReadOffEnd) {
DoEvalTest({llvm::dwarf::DW_OP_const4u, 0xf0}, false,
DwarfExprEval::Completion::kSync, 0,
DwarfExprEval::ResultType::kPointer,
"Bad number format in DWARF expression.");
}
// Tests that reading a ULEB number without enough room fails.
TEST_F(DwarfExprEvalTest, ConstReadOffEnd) {
// Note that LLVM allows LEB numbers to run off the end, and in that case
// just stops reading data and reports the bits read.
DoEvalTest({llvm::dwarf::DW_OP_constu}, false,
DwarfExprEval::Completion::kSync, 0,
DwarfExprEval::ResultType::kPointer,
"Bad number format in DWARF expression.");
}
TEST_F(DwarfExprEvalTest, Addr) {
// This encodes the relative address 0x4000.
DoEvalTest({llvm::dwarf::DW_OP_addr, 0, 0x40, 0, 0, 0, 0, 0, 0}, true,
DwarfExprEval::Completion::kSync, kModuleBase + 0x4000,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Breg) {
provider()->AddRegisterValue(0, true, 100);
provider()->AddRegisterValue(9, false, 200);
// reg0 (=100) + 129 = 229 (synchronous).
// Note: 129 in SLEB is 0x81, 0x01 (example in DWARF spec).
DoEvalTest({llvm::dwarf::DW_OP_breg0, 0x81, 0x01}, true,
DwarfExprEval::Completion::kSync, 229u,
DwarfExprEval::ResultType::kPointer);
// reg9 (=200) - 127 = 73 (asynchronous).
// -127 in SLEB is 0x81, 0x7f (example in DWARF spec).
DoEvalTest({llvm::dwarf::DW_OP_breg9, 0x81, 0x7f}, true,
DwarfExprEval::Completion::kAsync, 73u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Bregx) {
provider()->AddRegisterValue(0, true, 100);
provider()->AddRegisterValue(9, false, 200);
// reg0 (=100) + 129 = 229 (synchronous).
// Note: 129 in SLEB is 0x81, 0x01 (example in DWARF spec).
DoEvalTest({llvm::dwarf::DW_OP_bregx, 0x00, 0x81, 0x01}, true,
DwarfExprEval::Completion::kSync, 229u,
DwarfExprEval::ResultType::kPointer);
// reg9 (=200) - 127 = 73 (asynchronous).
// -127 in SLEB is 0x81, 0x7f (example in DWARF spec).
DoEvalTest({llvm::dwarf::DW_OP_bregx, 0x09, 0x81, 0x7f}, true,
DwarfExprEval::Completion::kAsync, 73u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Const1s) {
DoEvalTest({llvm::dwarf::DW_OP_const1s, static_cast<uint8_t>(-3)}, true,
DwarfExprEval::Completion::kSync, static_cast<uint64_t>(-3),
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Const1u) {
DoEvalTest({llvm::dwarf::DW_OP_const1u, 0xf0}, true,
DwarfExprEval::Completion::kSync, 0xf0,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Const2s) {
DoEvalTest({llvm::dwarf::DW_OP_const2s, static_cast<uint8_t>(-3), 0xff}, true,
DwarfExprEval::Completion::kSync, static_cast<uint64_t>(-3),
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Const2u) {
DoEvalTest({llvm::dwarf::DW_OP_const2u, 0x01, 0xf0}, true,
DwarfExprEval::Completion::kSync, 0xf001,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Const4s) {
DoEvalTest(
{llvm::dwarf::DW_OP_const4s, static_cast<uint8_t>(-3), 0xff, 0xff, 0xff},
true, DwarfExprEval::Completion::kSync, static_cast<uint64_t>(-3),
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Const4u) {
DoEvalTest({llvm::dwarf::DW_OP_const4u, 0x03, 0x02, 0x01, 0xf0}, true,
DwarfExprEval::Completion::kSync, 0xf0010203,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Const8s) {
DoEvalTest({llvm::dwarf::DW_OP_const8s, static_cast<uint8_t>(-3), 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff},
true, DwarfExprEval::Completion::kSync, static_cast<uint64_t>(-3),
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Const8u) {
DoEvalTest({llvm::dwarf::DW_OP_const8u, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02,
0x01, 0xf0},
true, DwarfExprEval::Completion::kSync, 0xf001020304050607u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Consts) {
// -127 in SLEB is 0x81, 0x7f (example in DWARF spec).
DoEvalTest({llvm::dwarf::DW_OP_consts, 0x81, 0x7f}, true,
DwarfExprEval::Completion::kSync, static_cast<uint64_t>(-127),
DwarfExprEval::ResultType::kPointer);
}
// Tests both "constu" and "drop".
TEST_F(DwarfExprEvalTest, ConstuDrop) {
// 129 in ULEB is 0x81, 0x01 (example in DWARF spec).
DoEvalTest({llvm::dwarf::DW_OP_constu, 0x81, 0x01, llvm::dwarf::DW_OP_lit0,
llvm::dwarf::DW_OP_drop},
true, DwarfExprEval::Completion::kSync, 129u,
DwarfExprEval::ResultType::kPointer);
}
// Tests both "dup" and "add".
TEST_F(DwarfExprEvalTest, DupAdd) {
DoEvalTest({llvm::dwarf::DW_OP_lit8, llvm::dwarf::DW_OP_dup,
llvm::dwarf::DW_OP_plus},
true, DwarfExprEval::Completion::kSync, 16u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Neg) {
// Negate one should give -1 casted to unsigned.
DoEvalTest({llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_neg}, true,
DwarfExprEval::Completion::kSync, 0xffffffffffffffffu,
DwarfExprEval::ResultType::kPointer);
// Double negate should come back to 1.
DoEvalTest(
{llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_neg, llvm::dwarf::DW_OP_neg},
true, DwarfExprEval::Completion::kSync, 1u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Not) {
DoEvalTest({llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_not}, true,
DwarfExprEval::Completion::kSync, 0xfffffffffffffffeu,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Or) {
// 8 | 1 = 9.
DoEvalTest(
{llvm::dwarf::DW_OP_lit8, llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_or},
true, DwarfExprEval::Completion::kSync, 9u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Mul) {
// 8 * 9 = 72.
DoEvalTest({llvm::dwarf::DW_OP_lit8, llvm::dwarf::DW_OP_lit9,
llvm::dwarf::DW_OP_mul},
true, DwarfExprEval::Completion::kSync, 72u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Minus) {
// 8 - 2 = 6.
DoEvalTest({llvm::dwarf::DW_OP_lit8, llvm::dwarf::DW_OP_lit2,
llvm::dwarf::DW_OP_minus},
true, DwarfExprEval::Completion::kSync, 6u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Over) {
// Stack of (1, 2), this pushes "1" on the top.
DoEvalTest({llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_lit2,
llvm::dwarf::DW_OP_over},
true, DwarfExprEval::Completion::kSync, 1u,
DwarfExprEval::ResultType::kPointer);
// Same operation with a drop to check the next-to-top item.
DoEvalTest({llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_lit2,
llvm::dwarf::DW_OP_over, llvm::dwarf::DW_OP_drop},
true, DwarfExprEval::Completion::kSync, 2u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Pick) {
// Stack of 1, 2, 3. Pick 0 -> 3.
DoEvalTest({llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_lit2,
llvm::dwarf::DW_OP_lit3, llvm::dwarf::DW_OP_pick, 0},
true, DwarfExprEval::Completion::kSync, 3u,
DwarfExprEval::ResultType::kPointer);
// Stack of 1, 2, 3. Pick 2 -> 1.
DoEvalTest({llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_lit2,
llvm::dwarf::DW_OP_lit3, llvm::dwarf::DW_OP_pick, 2},
true, DwarfExprEval::Completion::kSync, 1u,
DwarfExprEval::ResultType::kPointer);
// Stack of 1, 2, 3. Pick 3 -> error.
DoEvalTest({llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_lit2,
llvm::dwarf::DW_OP_lit3, llvm::dwarf::DW_OP_pick, 3},
false, DwarfExprEval::Completion::kSync, 0u,
DwarfExprEval::ResultType::kPointer,
"Stack underflow for DWARF expression.");
}
TEST_F(DwarfExprEvalTest, Swap) {
// 1, 2, swap -> 2, 1
DoEvalTest({llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_lit2,
llvm::dwarf::DW_OP_swap},
true, DwarfExprEval::Completion::kSync, 1u,
DwarfExprEval::ResultType::kPointer);
DoEvalTest({llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_lit2,
llvm::dwarf::DW_OP_swap, llvm::dwarf::DW_OP_drop},
true, DwarfExprEval::Completion::kSync, 2u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Rot) {
// 1, 2, 3, rot -> 3, 1, 2 (test with 0, 1, and 2 "drops" to check all 3
// stack elements).
DoEvalTest({llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_lit2,
llvm::dwarf::DW_OP_lit3, llvm::dwarf::DW_OP_rot},
true, DwarfExprEval::Completion::kSync, 2u,
DwarfExprEval::ResultType::kPointer);
DoEvalTest({llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_lit2,
llvm::dwarf::DW_OP_lit3, llvm::dwarf::DW_OP_rot,
llvm::dwarf::DW_OP_drop},
true, DwarfExprEval::Completion::kSync, 1u,
DwarfExprEval::ResultType::kPointer);
DoEvalTest({llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_lit2,
llvm::dwarf::DW_OP_lit3, llvm::dwarf::DW_OP_rot,
llvm::dwarf::DW_OP_drop, llvm::dwarf::DW_OP_drop},
true, DwarfExprEval::Completion::kSync, 3u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Abs) {
// Abs of 1 -> 1.
DoEvalTest({llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_abs}, true,
DwarfExprEval::Completion::kSync, 1u,
DwarfExprEval::ResultType::kPointer);
// Abs of -1 -> 1.
DoEvalTest(
{llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_neg, llvm::dwarf::DW_OP_abs},
true, DwarfExprEval::Completion::kSync, 1u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, And) {
// 3 (=0b11) & 5 (=0b101) = 1
DoEvalTest({llvm::dwarf::DW_OP_lit3, llvm::dwarf::DW_OP_lit5,
llvm::dwarf::DW_OP_and},
true, DwarfExprEval::Completion::kSync, 1u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Div) {
// 8 / -2 = -4.
DoEvalTest({llvm::dwarf::DW_OP_lit8, llvm::dwarf::DW_OP_lit2,
llvm::dwarf::DW_OP_neg, llvm::dwarf::DW_OP_div},
true, DwarfExprEval::Completion::kSync, static_cast<uint64_t>(-4),
DwarfExprEval::ResultType::kPointer);
// Divide by zero should give an error.
DoEvalTest({llvm::dwarf::DW_OP_lit8, llvm::dwarf::DW_OP_lit0,
llvm::dwarf::DW_OP_div},
false, DwarfExprEval::Completion::kSync, 0,
DwarfExprEval::ResultType::kPointer,
"DWARF expression divided by zero.");
}
TEST_F(DwarfExprEvalTest, Mod) {
// 7 % 2 = 1
DoEvalTest({llvm::dwarf::DW_OP_lit7, llvm::dwarf::DW_OP_lit2,
llvm::dwarf::DW_OP_mod},
true, DwarfExprEval::Completion::kSync, 1,
DwarfExprEval::ResultType::kPointer);
// Modulo 0 should give an error
DoEvalTest({llvm::dwarf::DW_OP_lit7, llvm::dwarf::DW_OP_lit0,
llvm::dwarf::DW_OP_mod},
false, DwarfExprEval::Completion::kSync, 0,
DwarfExprEval::ResultType::kPointer,
"DWARF expression divided by zero.");
}
TEST_F(DwarfExprEvalTest, PlusUconst) {
// 7 + 129 = 136. 129 in ULEB is 0x81, 0x01 (example in DWARF spec).
DoEvalTest(
{llvm::dwarf::DW_OP_lit7, llvm::dwarf::DW_OP_plus_uconst, 0x81, 0x01},
true, DwarfExprEval::Completion::kSync, 136u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Shr) {
// 8 >> 1 = 4
DoEvalTest({llvm::dwarf::DW_OP_lit8, llvm::dwarf::DW_OP_lit1,
llvm::dwarf::DW_OP_shr},
true, DwarfExprEval::Completion::kSync, 4u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Shra) {
// -7 (=0b1111...1111001) >> 2 = -2 (=0b1111...1110)
DoEvalTest({llvm::dwarf::DW_OP_lit7, llvm::dwarf::DW_OP_neg,
llvm::dwarf::DW_OP_lit2, llvm::dwarf::DW_OP_shra},
true, DwarfExprEval::Completion::kSync, static_cast<uint64_t>(-2),
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Shl) {
// 8 << 1 = 16
DoEvalTest({llvm::dwarf::DW_OP_lit8, llvm::dwarf::DW_OP_lit1,
llvm::dwarf::DW_OP_shl},
true, DwarfExprEval::Completion::kSync, 16u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Xor) {
// 7 (=0b111) ^ 9 (=0b1001) = 14 (=0b1110)
DoEvalTest({llvm::dwarf::DW_OP_lit7, llvm::dwarf::DW_OP_lit9,
llvm::dwarf::DW_OP_xor},
true, DwarfExprEval::Completion::kSync, 14u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Skip) {
// Skip 0 (execute next instruction which just gives a constant).
DoEvalTest({llvm::dwarf::DW_OP_skip, 0, 0, llvm::dwarf::DW_OP_lit9}, true,
DwarfExprEval::Completion::kSync, 9u,
DwarfExprEval::ResultType::kPointer);
// Skip 1 (skip over user-defined instruction which would normally give an
// error).
DoEvalTest({llvm::dwarf::DW_OP_skip, 1, 0, llvm::dwarf::DW_OP_lo_user,
llvm::dwarf::DW_OP_lit9},
true, DwarfExprEval::Completion::kSync, 9u,
DwarfExprEval::ResultType::kPointer);
// Skip to the end should just terminate the program. The result when nothing
// is left on the stack is 0.
DoEvalTest({llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_skip, 1, 0,
llvm::dwarf::DW_OP_nop},
true, DwarfExprEval::Completion::kSync, 0,
DwarfExprEval::ResultType::kPointer);
// Skip before the beginning is an error.
DoEvalTest({llvm::dwarf::DW_OP_skip, 0, 0xff}, false,
DwarfExprEval::Completion::kSync, 0,
DwarfExprEval::ResultType::kPointer,
"DWARF expression skips out-of-bounds.");
}
TEST_F(DwarfExprEvalTest, Bra) {
// 0 @ top of stack means don't take the branch. This jumps out of bounds
// which should not be taken.
DoEvalTest({llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_bra, 0xff, 0,
llvm::dwarf::DW_OP_lit9},
true, DwarfExprEval::Completion::kSync, 9u,
DwarfExprEval::ResultType::kPointer);
// Nonzero means take the branch. This jumps over a user-defined instruction
// which would give an error if executed.
DoEvalTest({llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_bra, 1, 0,
llvm::dwarf::DW_OP_lo_user, llvm::dwarf::DW_OP_lit9},
true, DwarfExprEval::Completion::kSync, 9u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Eq) {
DoEvalTest(
{llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_eq},
true, DwarfExprEval::Completion::kSync, 1u,
DwarfExprEval::ResultType::kPointer);
DoEvalTest(
{llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_eq},
true, DwarfExprEval::Completion::kSync, 0u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Ge) {
DoEvalTest(
{llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_ge},
true, DwarfExprEval::Completion::kSync, 1u,
DwarfExprEval::ResultType::kPointer);
DoEvalTest(
{llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_ge},
true, DwarfExprEval::Completion::kSync, 0u,
DwarfExprEval::ResultType::kPointer);
DoEvalTest(
{llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_ge},
true, DwarfExprEval::Completion::kSync, 1u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Gt) {
DoEvalTest(
{llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_gt},
true, DwarfExprEval::Completion::kSync, 0u,
DwarfExprEval::ResultType::kPointer);
DoEvalTest(
{llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_gt},
true, DwarfExprEval::Completion::kSync, 0u,
DwarfExprEval::ResultType::kPointer);
DoEvalTest(
{llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_gt},
true, DwarfExprEval::Completion::kSync, 1u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Le) {
DoEvalTest(
{llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_le},
true, DwarfExprEval::Completion::kSync, 1u,
DwarfExprEval::ResultType::kPointer);
DoEvalTest(
{llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_le},
true, DwarfExprEval::Completion::kSync, 1u,
DwarfExprEval::ResultType::kPointer);
DoEvalTest(
{llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_le},
true, DwarfExprEval::Completion::kSync, 0u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Lt) {
DoEvalTest(
{llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_lt},
true, DwarfExprEval::Completion::kSync, 0u,
DwarfExprEval::ResultType::kPointer);
DoEvalTest(
{llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_lt},
true, DwarfExprEval::Completion::kSync, 1u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Ne) {
DoEvalTest(
{llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_ne},
true, DwarfExprEval::Completion::kSync, 0u,
DwarfExprEval::ResultType::kPointer);
DoEvalTest(
{llvm::dwarf::DW_OP_lit0, llvm::dwarf::DW_OP_lit1, llvm::dwarf::DW_OP_ne},
true, DwarfExprEval::Completion::kSync, 1u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Fbreg) {
constexpr uint64_t kBase = 0x1000000;
provider()->set_bp(kBase);
DoEvalTest({llvm::dwarf::DW_OP_fbreg, 0}, true,
DwarfExprEval::Completion::kSync, kBase,
DwarfExprEval::ResultType::kPointer);
// Note: 129 in SLEB is 0x81, 0x01 (example in DWARF spec).
DoEvalTest({llvm::dwarf::DW_OP_fbreg, 0x81, 0x01}, true,
DwarfExprEval::Completion::kSync, kBase + 129u,
DwarfExprEval::ResultType::kPointer);
}
TEST_F(DwarfExprEvalTest, Deref) {
// This is a real program Clang generated. 0x58 = -40 in SLEB128 so:
// *[reg6 - 40] - 0x30
const std::vector<uint8_t> program = {
llvm::dwarf::DW_OP_breg6, 0x58, llvm::dwarf::DW_OP_deref,
llvm::dwarf::DW_OP_constu, 0x30, llvm::dwarf::DW_OP_minus};
constexpr uint64_t kReg6 = 0x1000;
provider()->AddRegisterValue(6, true, kReg6);
constexpr int64_t kOffsetFromReg6 = -40;
// Contents of the data at [reg6 - 40]
constexpr uint64_t kMemoryContents = 0x5000000000;
std::vector<uint8_t> mem;
mem.resize(sizeof(kMemoryContents));
memcpy(&mem[0], &kMemoryContents, sizeof(kMemoryContents));
provider()->AddMemory(kReg6 + kOffsetFromReg6, mem);
DoEvalTest(program, true, DwarfExprEval::Completion::kAsync,
kMemoryContents - 0x30, DwarfExprEval::ResultType::kPointer);
}
} // namespace zxdb