blob: a3e27716189d8c1891b2fb216cb49306d5259e7c [file] [log] [blame]
// Copyright 2019 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/developer/debug/zxdb/symbols/dwarf_location.h"
#include <gtest/gtest.h>
namespace zxdb {
TEST(DwarfLocation, Dwarf4UnitRelative) {
// This has the first range with an empty expression, the second with an empty range. These do
// not get added to the result.
std::vector<uint8_t> data{
0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Begin of range 0.
0xf6, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // End of range 0.
0x00, 0x00, // Expression 0 byte length.
0xf8, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Begin of range 1.
0xf8, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // End of range 1.
0x03, 0x00, // Expression 1 byte length.
0x7f, 0xd8, 0x75, // Expression 1.
0xf8, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Begin of range 2.
0x51, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // End of range 1.
0x03, 0x00, // Expression 2 byte length.
0x7f, 0xd8, 0x75, // Expression 2.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // End of expression marker (0, 0).
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
constexpr TargetPointer kUnitBaseAddress = 0x1000000;
VariableLocation result = DecodeDwarf4LocationList(kUnitBaseAddress, data, UncachedLazySymbol());
EXPECT_FALSE(result.is_null());
ASSERT_EQ(1u, result.locations().size()); // Only the last range was nonempty.
EXPECT_EQ(AddressRange(kUnitBaseAddress + 0xdf8, kUnitBaseAddress + 0x1051),
result.locations()[0].range);
std::vector<uint8_t> expected_expr{0x7f, 0xd8, 0x75};
EXPECT_EQ(expected_expr, result.locations()[0].expression.data());
}
TEST(DwarfLocation, Dwarf4NewBaseAddress) {
// This is a real location list generated by Clang.
std::vector<uint8_t> data{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Base address selector.
0x20, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // New base addr.
0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Begin of range 0.
0xf6, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // End of range 0.
0x03, 0x00, // Expression 0 byte length.
0x7f, 0xd8, 0x75, // Expression 0.
0xf8, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Begin of range 1.
0x51, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // End of range 1.
0x03, 0x00, // Expression 1 byte length.
0x7f, 0xd8, 0x75, // Expression 1.
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // End of expression marker (0, 0).
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
VariableLocation result = DecodeDwarf4LocationList(0, data, UncachedLazySymbol());
EXPECT_FALSE(result.is_null());
ASSERT_EQ(2u, result.locations().size());
EXPECT_EQ(AddressRange(0x2206au, 0x22e16u), result.locations()[0].range);
std::vector<uint8_t> expected_expr{0x7f, 0xd8, 0x75};
EXPECT_EQ(expected_expr, result.locations()[0].expression.data());
EXPECT_EQ(AddressRange(0x22e18u, 0x23071u), result.locations()[1].range);
EXPECT_EQ(expected_expr, result.locations()[1].expression.data());
// Now test a truncated list.
result = DecodeDwarf4LocationList(
0, containers::array_view<uint8_t>(data.data(), data.size() - 4), UncachedLazySymbol());
EXPECT_TRUE(result.is_null());
}
TEST(DwarfLocation, Dwarf4BadExpr) {
// This expression has a byte length that's too long for the data.
std::vector<uint8_t> data{0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Begin of range.
0xf6, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // End of range.
0x00, 0x80, // Expression byte length.
0x7f, 0xd8, 0x75}; // Expression (shorter than byte length).
VariableLocation result = DecodeDwarf4LocationList(0, data, UncachedLazySymbol());
EXPECT_TRUE(result.is_null());
}
// The instructions tested here are addresses encoded directly in the location list (not the "x"
// variants that refer to the .debug_addr table).
TEST(DwarfLocation, Dwarf5NonSplit) {
std::vector<uint8_t> data{
0x04, 0x0f, 0x53, // DW_LLE_offset_pair (0x0f, 0x53). [0]
0x04, 0x76, 0xb0, 0x7f, 0x06, // Counted location (4 bytes).
0x07, // DW_LLE_start_end (empty range, skipped).
0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Start address = 0x400.
0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // End address = 0x400.
0x02, 0x74, 0x00, // Counted location (2 bytes).
0x07, // DW_LLE_start_end (empty expr, skipped).
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Start address = 0x500.
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // End address = 0x600.
0x00, // Counted location (0 bytes).
0x08, // DW_LLE_start_length [1]
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Start address = 0x0807060504030201.
0x80, 0x01, // ULEB128 length = 0x80.
0x02, 0x74, 0x00, // Counted location (2 bytes).
0x06, // DW_LLE_base_address.
0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // New base address value = 0xa0.
0x04, 0x53, 0x5f, // DW_LLE_offset_pair (0x53, 0x5f). [2]
0x02, 0x74, 0x00, // Counted location (2 bytes).
0x07, // DW_LLE_start_end. [3]
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Start address = 0x200.
0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // End address = 0x300.
0x02, 0x74, 0x00, // Counted location (2 bytes).
0x00}; // DW_LLE_end_of_list.
// The address callback should never be called since there are no .debug_addr references.
fit::function<std::optional<TargetPointer>(uint64_t)> index_to_addr =
[](uint64_t) -> std::optional<TargetPointer> {
ADD_FAILURE() << "No .debug_add references";
return std::nullopt;
};
constexpr uint64_t kUnitOffset = 0x4a;
VariableLocation result =
DecodeDwarf5LocationList(kUnitOffset, data, index_to_addr, UncachedLazySymbol());
EXPECT_FALSE(result.is_null());
ASSERT_EQ(4u, result.locations().size());
// [0] entry uses offset-pair which is relative to the unit's kUnitOffset.
EXPECT_EQ(AddressRange(kUnitOffset + 0x0f, kUnitOffset + 0x53), result.locations()[0].range);
std::vector<uint8_t> expected_expr0{0x76, 0xb0, 0x7f, 0x06};
EXPECT_EQ(expected_expr0, result.locations()[0].expression.data());
// [1] entry uses absolute start + length.
EXPECT_EQ(AddressRange(0x0807060504030201ull, 0x0807060504030281ull),
result.locations()[1].range);
std::vector<uint8_t> expected_expr1{0x74, 0x00};
EXPECT_EQ(expected_expr1, result.locations()[1].expression.data());
// [2] entry uses offset pair which is relative to the DW_LLE_base_address, same expr as [1].
const uint64_t kNewBaseAddress = 0xa0;
EXPECT_EQ(AddressRange(kNewBaseAddress + 0x53, kNewBaseAddress + 0x5f),
result.locations()[2].range);
EXPECT_EQ(expected_expr1, result.locations()[2].expression.data());
// [3] entry uses absolute start/end.
EXPECT_EQ(AddressRange(0x200ull, 0x300ull), result.locations()[3].range);
EXPECT_EQ(expected_expr1, result.locations()[3].expression.data());
}
// The instructions here are indices into the .debug_addr table.
TEST(DwarfLocation, Dwarf5AddrLookup) {
std::vector<uint8_t> data{0x02, // DW_LLE_startx_endx (empty range, skipped).
0x00, // Start address index = 0x0.
0x00, // End address index = 0x0.
0x02, 0x74, 0x00, // Counted location (2 bytes).
0x02, // DW_LLE_startx_endx (empty expr, skipped).
0x00, // Start address index = 0;
0x01, // End address index = 1;
0x00, // Counted location (0 bytes).
0x01, // DW_LLE_base_addressx.
0x02, // New base address index = 2.
0x03, // DW_LLE_startx_length [0]
0x03, // Start address index = 3.
0x80, 0x01, // ULEB128 length = 0x80.
0x02, 0x74, 0x00, // Counted location (2 bytes).
0x04, 0x53, 0x5f, // DW_LLE_offset_pair (0x53, 0x5f). [1]
0x02, 0x74, 0x00, // Counted location (2 bytes).
0x02, // DW_LLE_startx_endx. [2]
0x04, // Start address index = 4.
0x05, // End address index = 5;
0x02, 0x74, 0x00, // Counted location (2 bytes).
0x05, // DW_LLE_default_location [3]
0x02, 0xaa, 0xbb, // Counted location (2 bytes).
0x00}; // DW_LLE_end_of_list.
// The address indices above index into this table.
std::vector<TargetPointer> address_table{
0x10000, // [0]
0x10010, // [1]
0x10020, // [2]
0x10030, // [3]
0x10040, // [4]
0x10050, // [5]
};
// Maps address indices to addresses.
fit::function<std::optional<TargetPointer>(uint64_t)> index_to_addr =
[address_table](uint64_t i) -> std::optional<TargetPointer> {
if (i >= address_table.size()) {
ADD_FAILURE(); // Expect all indices to be in range.
return std::nullopt;
}
return address_table[i];
};
VariableLocation result = DecodeDwarf5LocationList(0, data, index_to_addr, UncachedLazySymbol());
EXPECT_FALSE(result.is_null());
ASSERT_EQ(3u, result.locations().size());
// [0] entry uses startx/length.
EXPECT_EQ(AddressRange(address_table[3], address_table[3] + 0x80), result.locations()[0].range);
std::vector<uint8_t> expected_expr0{0x74, 0x00};
EXPECT_EQ(expected_expr0, result.locations()[0].expression.data());
// [1] entry uses offset-pair which is relative to the previous base.
const uint64_t kNewBaseAddress = address_table[2];
EXPECT_EQ(AddressRange(kNewBaseAddress + 0x53, kNewBaseAddress + 0x5f),
result.locations()[1].range);
EXPECT_EQ(expected_expr0, result.locations()[1].expression.data());
// [2] entry uses absolute start + end.
EXPECT_EQ(AddressRange(address_table[4], address_table[5]), result.locations()[2].range);
EXPECT_EQ(expected_expr0, result.locations()[2].expression.data());
// Default location.
std::vector<uint8_t> expected_default_expr{0xaa, 0xbb};
ASSERT_TRUE(result.default_expr());
EXPECT_EQ(expected_default_expr, result.default_expr()->data());
}
TEST(DwarfLocation, Dwarf5UnterminatedTable) {
std::vector<uint8_t> data{
0x02, // DW_LLE_startx_endx (empty range, skipped).
0x00, // Start address index = 0x0.
0x00, // End address index = 0x0.
0x02, 0x74, 0x00, // Counted location (2 bytes).
};
// Just return some arbitrary address index, this will be discarded.
fit::function<std::optional<TargetPointer>(uint64_t)> index_to_addr =
[](uint64_t) -> std::optional<TargetPointer> { return 0x100; };
VariableLocation result = DecodeDwarf5LocationList(0, data, index_to_addr, UncachedLazySymbol());
EXPECT_TRUE(result.is_null());
}
TEST(DwarfLocation, Dwarf5BadAddressIndex) {
std::vector<uint8_t> data{0x02, // DW_LLE_startx_endx (empty range, skipped).
0x00, // Start address index = 0x5.
0x00, // End address index = 0x6.
0x02, 0x74, 0x00, // Counted location (2 bytes).
0x00}; // DW_LLE_end_of_list.
// This address index lookup always reports failure.
fit::function<std::optional<TargetPointer>(uint64_t)> index_to_addr =
[](uint64_t) -> std::optional<TargetPointer> { return std::nullopt; };
VariableLocation result = DecodeDwarf5LocationList(0, data, index_to_addr, UncachedLazySymbol());
EXPECT_TRUE(result.is_null());
}
} // namespace zxdb