| // 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 |