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