blob: 522e8c6512b7476e64c7c2c466cc716f12aecbd3 [file] [log] [blame]
// Copyright 2022 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 "src/lib/unwinder/dwarf_expr.h"
#include <cstdint>
#include <gtest/gtest.h>
#include "src/lib/unwinder/memory.h"
#include "src/lib/unwinder/registers.h"
namespace unwinder {
namespace {
Error EvaluateWithError(const std::vector<uint8_t>& expr,
const std::map<RegisterID, uint64_t>& register_values,
std::vector<uint64_t> stack, uint64_t& result) {
LocalMemory mem;
Registers regs(Registers::Arch::kArm64);
for (auto [reg, val] : register_values) {
Error err = regs.Set(reg, val);
EXPECT_TRUE(err.ok()) << err.msg();
}
DwarfExpr dwarf_expr(&mem, reinterpret_cast<uint64_t>(expr.data()), expr.size());
return dwarf_expr.Eval(&mem, regs, std::move(stack), result);
}
uint64_t Evaluate(const std::vector<uint8_t>& expr,
const std::map<RegisterID, uint64_t>& register_values = {},
std::vector<uint64_t> stack = {}) {
uint64_t res;
Error err = EvaluateWithError(expr, register_values, std::move(stack), res);
EXPECT_TRUE(err.ok()) << err.msg();
return res;
}
TEST(DwarfExpr, Const) {
EXPECT_EQ(10u, Evaluate({}, {}, {10}));
EXPECT_EQ(20u, Evaluate({DW_OP_lit20}));
EXPECT_EQ(30u, Evaluate({DW_OP_const2s, 30, 0}));
EXPECT_EQ(40u, Evaluate({DW_OP_constu, 40}));
}
TEST(DwarfExpr, Comparison) {
EXPECT_EQ(0u, Evaluate({DW_OP_lit1, DW_OP_lit2, DW_OP_eq}));
EXPECT_EQ(1u, Evaluate({DW_OP_lit1, DW_OP_lit2, DW_OP_le}));
EXPECT_EQ(0u, Evaluate({DW_OP_lit1, DW_OP_lit2, DW_OP_gt}));
}
TEST(DwarfExpr, Arithmetic) {
// 1 + 1
EXPECT_EQ(2u, Evaluate({DW_OP_lit1, DW_OP_plus}, {}, {1}));
// 10 - (3 * 3) / 4
EXPECT_EQ(8u, Evaluate({DW_OP_lit10, DW_OP_lit3, DW_OP_lit3, DW_OP_mul, DW_OP_lit4, DW_OP_div,
DW_OP_minus}));
// 0 + 30 + 70
EXPECT_EQ(100u, Evaluate({DW_OP_lit30, DW_OP_plus_uconst, 70, DW_OP_plus}, {}, {0}));
// Invalid
uint64_t res;
EXPECT_TRUE(EvaluateWithError({DW_OP_eq}, {}, {0}, res).has_err());
}
TEST(DwarfExpr, StackOperations) {
// Dup.
EXPECT_EQ(3u, Evaluate({DW_OP_lit1, DW_OP_lit2, DW_OP_lit3, DW_OP_dup}));
// Swap and minus.
EXPECT_EQ(1u, Evaluate({DW_OP_lit1, DW_OP_lit2, DW_OP_swap, DW_OP_minus}));
// Pick.
EXPECT_EQ(1u, Evaluate({DW_OP_lit1, DW_OP_lit2, DW_OP_lit3, DW_OP_pick, 2}));
// Over.
EXPECT_EQ(2u, Evaluate({DW_OP_lit1, DW_OP_lit2, DW_OP_lit3, DW_OP_over}));
// Stack too shallow.
uint64_t res;
EXPECT_TRUE(EvaluateWithError({DW_OP_drop}, {}, {0}, res).has_err());
}
TEST(DwarfExpr, Breg) {
// breg18 - 8
EXPECT_EQ(0x1000u, Evaluate({DW_OP_breg18, (-8) & 0x7f}, {{RegisterID::kArm64_x18, 0x1008}}));
// *(breg0 + 0) & *(breg16 + 8)
uint64_t data[] = {0xBEEF0812, 0xBEEF0724};
EXPECT_EQ(0xBEEF0000u,
Evaluate({DW_OP_breg0, 0, DW_OP_deref, DW_OP_breg16, 8, DW_OP_deref, DW_OP_and},
{
{RegisterID::kArm64_x0, reinterpret_cast<uint64_t>(data)},
{RegisterID::kArm64_x16, reinterpret_cast<uint64_t>(data)},
}));
// breg3 not present.
uint64_t res;
EXPECT_TRUE(EvaluateWithError({DW_OP_breg3, 0}, {}, {0}, res).has_err());
}
TEST(DwarfExpr, ControlFlow) {
// breg0 + 1 <= 1 ? breg1 + 0 : breg2 + 0
std::vector<uint8_t> expr = {DW_OP_breg0, 1, DW_OP_lit1, DW_OP_le, DW_OP_bra, 5, 0,
DW_OP_breg2, 0, DW_OP_skip, 2, 0, DW_OP_breg1, 0};
EXPECT_EQ(10u, Evaluate(expr, {
{RegisterID::kArm64_x0, 0},
{RegisterID::kArm64_x1, 10},
{RegisterID::kArm64_x2, 20},
}));
EXPECT_EQ(20u, Evaluate(expr, {
{RegisterID::kArm64_x0, 1},
{RegisterID::kArm64_x1, 10},
{RegisterID::kArm64_x2, 20},
}));
// Condition with wrong offset.
uint64_t res;
EXPECT_TRUE(
EvaluateWithError({DW_OP_bra, 3, 0, DW_OP_lit10, DW_OP_lit20}, {}, {1}, res).has_err());
}
} // namespace
} // namespace unwinder