| //===----------------------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "lldb/Utility/VirtualDataExtractor.h" |
| #include "lldb/Utility/DataBufferHeap.h" |
| #include "gtest/gtest.h" |
| |
| using namespace lldb_private; |
| using namespace lldb; |
| |
| using Table = VirtualDataExtractor::LookupTable; |
| using Entry = Table::Entry; |
| |
| TEST(VirtualDataExtractorTest, BasicConstruction) { |
| uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)}); |
| |
| EXPECT_EQ(extractor->GetByteSize(), 8U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetDataAtVirtualOffset) { |
| uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)}); |
| |
| offset_t virtual_offset = 0x1000; |
| const void *data = extractor->GetData(&virtual_offset, 4); |
| |
| ASSERT_NE(data, nullptr); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| EXPECT_EQ(memcmp(data, buffer, 4), 0); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetDataAtVirtualOffsetInvalid) { |
| uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)}); |
| |
| // Try to read from an invalid virtual address. |
| offset_t virtual_offset = 0x2000; |
| const void *data = extractor->GetData(&virtual_offset, 4); |
| |
| EXPECT_EQ(data, nullptr); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetU8AtVirtualOffset) { |
| uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)}); |
| |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetU8(&virtual_offset), 0x12U); |
| EXPECT_EQ(virtual_offset, 0x1001U); |
| |
| EXPECT_EQ(extractor->GetU8(&virtual_offset), 0x34U); |
| EXPECT_EQ(virtual_offset, 0x1002U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetU16AtVirtualOffset) { |
| uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)}); |
| |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetU16(&virtual_offset), 0x3412U); |
| EXPECT_EQ(virtual_offset, 0x1002U); |
| |
| EXPECT_EQ(extractor->GetU16(&virtual_offset), 0x7856U); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetU32AtVirtualOffset) { |
| uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)}); |
| |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetU32(&virtual_offset), 0x78563412U); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| |
| EXPECT_EQ(extractor->GetU32(&virtual_offset), 0xF0DEBC9AU); |
| EXPECT_EQ(virtual_offset, 0x1008U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetU64AtVirtualOffset) { |
| uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 8, Table{Entry(0x1000, 8, 0)}); |
| |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetU64(&virtual_offset), 0xF0DEBC9A78563412ULL); |
| EXPECT_EQ(virtual_offset, 0x1008U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetAddressAtVirtualOffset) { |
| uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)}); |
| |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetAddress(&virtual_offset), 0x78563412U); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| } |
| |
| TEST(VirtualDataExtractorTest, BigEndian) { |
| uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderBig, 4, Table{Entry(0x1000, 4, 0)}); |
| |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetU16(&virtual_offset), 0x1234U); |
| EXPECT_EQ(virtual_offset, 0x1002U); |
| |
| EXPECT_EQ(extractor->GetU16(&virtual_offset), 0x5678U); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| } |
| |
| TEST(VirtualDataExtractorTest, MultipleEntries) { |
| // Create a buffer with distinct patterns for each section. |
| uint8_t buffer[] = { |
| 0x01, 0x02, 0x03, 0x04, // Physical offset 0-3. |
| 0x11, 0x12, 0x13, 0x14, // Physical offset 4-7. |
| 0x21, 0x22, 0x23, 0x24 // Physical offset 8-11. |
| }; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, |
| Table{Entry(0x1000, 4, 0), // Virt 0x1000-0x1004 |
| Entry(0x2000, 4, 4), // Virt 0x2000-0x2004 |
| Entry(0x3000, 4, 8)}); // Virt 0x3000-0x3004 |
| |
| // Test reading from first virtual range. |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetU8(&virtual_offset), 0x01U); |
| |
| // Test reading from second virtual range. |
| virtual_offset = 0x2000; |
| EXPECT_EQ(extractor->GetU8(&virtual_offset), 0x11U); |
| |
| // Test reading from third virtual range. |
| virtual_offset = 0x3000; |
| EXPECT_EQ(extractor->GetU8(&virtual_offset), 0x21U); |
| } |
| |
| TEST(VirtualDataExtractorTest, NonContiguousVirtualAddresses) { |
| uint8_t buffer[] = {0xAA, 0xBB, 0xCC, 0xDD}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, |
| Table{Entry(0x1000, 2, 0), // Virt 0x1000-0x1002 |
| Entry(0x5000, 2, 2)}); // Virt 0x5000-0x5002 |
| |
| // Test reading from first virtual range. |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetU16(&virtual_offset), 0xBBAAU); |
| |
| // Test reading from second virtual range (non-contiguous). |
| virtual_offset = 0x5000; |
| EXPECT_EQ(extractor->GetU16(&virtual_offset), 0xDDCCU); |
| |
| // Test that gap between ranges is invalid. |
| virtual_offset = 0x3000; |
| EXPECT_EQ(extractor->GetU8(&virtual_offset), 0U); |
| } |
| |
| TEST(VirtualDataExtractorTest, SharedDataBuffer) { |
| // Test with shared_ptr to DataBuffer. |
| uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04}; |
| auto data_sp = std::make_shared<DataBufferHeap>(buffer, sizeof(buffer)); |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| data_sp, eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)}); |
| |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetU32(&virtual_offset), 0x04030201U); |
| } |
| |
| TEST(VirtualDataExtractorTest, NullPointerHandling) { |
| uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)}); |
| |
| // Test that passing nullptr returns default values. |
| EXPECT_EQ(extractor->GetU8(nullptr), 0U); |
| EXPECT_EQ(extractor->GetU16(nullptr), 0U); |
| EXPECT_EQ(extractor->GetU32(nullptr), 0U); |
| EXPECT_EQ(extractor->GetU64(nullptr), 0U); |
| EXPECT_EQ(extractor->GetAddress(nullptr), 0U); |
| EXPECT_EQ(extractor->GetData(nullptr, 4), nullptr); |
| } |
| |
| TEST(VirtualDataExtractorTest, OffsetMapping) { |
| // Test that virtual to physical offset mapping works correctly. |
| uint8_t buffer[] = {0x00, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD}; |
| |
| // Map virtual address 0x1000 to physical offset 4 (skipping first 4 bytes). |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 4)}); |
| |
| offset_t virtual_offset = 0x1000; |
| // Should read from physical offset 4, not 0. |
| EXPECT_EQ(extractor->GetU32(&virtual_offset), 0xDDCCBBAAU); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetU8Unchecked) { |
| uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)}); |
| |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetU8_unchecked(&virtual_offset), 0x12U); |
| EXPECT_EQ(virtual_offset, 0x1001U); |
| |
| EXPECT_EQ(extractor->GetU8_unchecked(&virtual_offset), 0x34U); |
| EXPECT_EQ(virtual_offset, 0x1002U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetU16Unchecked) { |
| uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)}); |
| |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetU16_unchecked(&virtual_offset), 0x3412U); |
| EXPECT_EQ(virtual_offset, 0x1002U); |
| |
| EXPECT_EQ(extractor->GetU16_unchecked(&virtual_offset), 0x7856U); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetU32Unchecked) { |
| uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)}); |
| |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetU32_unchecked(&virtual_offset), 0x78563412U); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| |
| EXPECT_EQ(extractor->GetU32_unchecked(&virtual_offset), 0xF0DEBC9AU); |
| EXPECT_EQ(virtual_offset, 0x1008U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetU64Unchecked) { |
| uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 8, Table{Entry(0x1000, 8, 0)}); |
| |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetU64_unchecked(&virtual_offset), |
| 0xF0DEBC9A78563412ULL); |
| EXPECT_EQ(virtual_offset, 0x1008U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetMaxU64Unchecked) { |
| uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)}); |
| |
| // Test various byte sizes. |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetMaxU64_unchecked(&virtual_offset, 1), 0x12U); |
| EXPECT_EQ(virtual_offset, 0x1001U); |
| |
| virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetMaxU64_unchecked(&virtual_offset, 2), 0x3412U); |
| EXPECT_EQ(virtual_offset, 0x1002U); |
| |
| virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetMaxU64_unchecked(&virtual_offset, 4), 0x78563412U); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| |
| virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetMaxU64_unchecked(&virtual_offset, 8), |
| 0xF0DEBC9A78563412ULL); |
| EXPECT_EQ(virtual_offset, 0x1008U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetAddressUnchecked) { |
| uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)}); |
| |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetAddress_unchecked(&virtual_offset), 0x78563412U); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| } |
| |
| TEST(VirtualDataExtractorTest, UncheckedWithBigEndian) { |
| uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderBig, 4, Table{Entry(0x1000, 4, 0)}); |
| |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetU16_unchecked(&virtual_offset), 0x1234U); |
| EXPECT_EQ(virtual_offset, 0x1002U); |
| |
| EXPECT_EQ(extractor->GetU16_unchecked(&virtual_offset), 0x5678U); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetCStr) { |
| // Create buffer with null-terminated strings. |
| uint8_t buffer[] = {'H', 'e', 'l', 'l', 'o', '\0', 'W', 'o', |
| 'r', 'l', 'd', '\0', 'F', 'o', 'o', '\0'}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, |
| Table{Entry(0x1000, 6, 0), Entry(0x2000, 12, 6)}); |
| |
| // Test reading first string. |
| offset_t virtual_offset = 0x1000; |
| const char *str1 = extractor->GetCStr(&virtual_offset); |
| ASSERT_NE(str1, nullptr); |
| EXPECT_STREQ(str1, "Hello"); |
| EXPECT_EQ(virtual_offset, 0x1006U); // After "Hello\0" |
| |
| // Test reading second string. |
| virtual_offset = 0x2000; |
| const char *str2 = extractor->GetCStr(&virtual_offset); |
| ASSERT_NE(str2, nullptr); |
| EXPECT_STREQ(str2, "World"); |
| EXPECT_EQ(virtual_offset, 0x2006U); // After "World\0" |
| } |
| |
| TEST(VirtualDataExtractorTest, GetFloat) { |
| // Create buffer with float value (IEEE 754 single precision). |
| // 3.14159f in little endian: 0xDB 0x0F 0x49 0x40 |
| uint8_t buffer[] = {0xDB, 0x0F, 0x49, 0x40}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)}); |
| |
| offset_t virtual_offset = 0x1000; |
| float value = extractor->GetFloat(&virtual_offset); |
| EXPECT_NEAR(value, 3.14159f, 0.00001f); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetDouble) { |
| // Create buffer with double value (IEEE 754 double precision). |
| // 3.14159265358979 in little endian |
| uint8_t buffer[] = {0x18, 0x2D, 0x44, 0x54, 0xFB, 0x21, 0x09, 0x40}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 8, Table{Entry(0x1000, 8, 0)}); |
| |
| offset_t virtual_offset = 0x1000; |
| double value = extractor->GetDouble(&virtual_offset); |
| EXPECT_NEAR(value, 3.14159265358979, 0.00000000000001); |
| EXPECT_EQ(virtual_offset, 0x1008U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetULEB128) { |
| // ULEB128 encoding: 0x624 (1572 decimal) = 0xA4 0x0C |
| uint8_t buffer[] = {0xA4, 0x0C, 0xFF, 0x00, 0x7F, 0x80, 0x01}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 7, 0)}); |
| |
| // Test reading first ULEB128 value (1572). |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetULEB128(&virtual_offset), 1572U); |
| EXPECT_EQ(virtual_offset, 0x1002U); |
| |
| // Test reading second ULEB128 value (127). |
| virtual_offset = 0x1004; |
| EXPECT_EQ(extractor->GetULEB128(&virtual_offset), 127U); |
| EXPECT_EQ(virtual_offset, 0x1005U); |
| |
| // Test reading third ULEB128 value (128). |
| EXPECT_EQ(extractor->GetULEB128(&virtual_offset), 128U); |
| EXPECT_EQ(virtual_offset, 0x1007U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetSLEB128) { |
| // SLEB128 encoding: -123 = 0x85 0x7F, 123 = 0xFB 0x00 |
| uint8_t buffer[] = {0x85, 0x7F, 0xFB, 0x00}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)}); |
| |
| // Test reading negative SLEB128 value (-123). |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetSLEB128(&virtual_offset), -123); |
| EXPECT_EQ(virtual_offset, 0x1002U); |
| |
| // Test reading positive SLEB128 value (123). |
| EXPECT_EQ(extractor->GetSLEB128(&virtual_offset), 123); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetU8Array) { |
| uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)}); |
| |
| // Test reading array of 4 bytes. |
| offset_t virtual_offset = 0x1000; |
| uint8_t dst[4] = {0}; |
| void *result = extractor->GetU8(&virtual_offset, dst, 4); |
| ASSERT_NE(result, nullptr); |
| EXPECT_EQ(dst[0], 0x01U); |
| EXPECT_EQ(dst[1], 0x02U); |
| EXPECT_EQ(dst[2], 0x03U); |
| EXPECT_EQ(dst[3], 0x04U); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetU16Array) { |
| uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)}); |
| |
| // Test reading array of 3 uint16_t values. |
| offset_t virtual_offset = 0x1000; |
| uint16_t dst[3] = {0}; |
| void *result = extractor->GetU16(&virtual_offset, dst, 3); |
| ASSERT_NE(result, nullptr); |
| EXPECT_EQ(dst[0], 0x3412U); |
| EXPECT_EQ(dst[1], 0x7856U); |
| EXPECT_EQ(dst[2], 0xBC9AU); |
| EXPECT_EQ(virtual_offset, 0x1006U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetU32Array) { |
| uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)}); |
| |
| // Test reading array of 2 uint32_t values. |
| offset_t virtual_offset = 0x1000; |
| uint32_t dst[2] = {0}; |
| void *result = extractor->GetU32(&virtual_offset, dst, 2); |
| ASSERT_NE(result, nullptr); |
| EXPECT_EQ(dst[0], 0x78563412U); |
| EXPECT_EQ(dst[1], 0xF0DEBC9AU); |
| EXPECT_EQ(virtual_offset, 0x1008U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetU64Array) { |
| uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, |
| 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 8, Table{Entry(0x1000, 16, 0)}); |
| |
| // Test reading array of 2 uint64_t values. |
| offset_t virtual_offset = 0x1000; |
| uint64_t dst[2] = {0}; |
| void *result = extractor->GetU64(&virtual_offset, dst, 2); |
| ASSERT_NE(result, nullptr); |
| EXPECT_EQ(dst[0], 0x0807060504030201ULL); |
| EXPECT_EQ(dst[1], 0x1817161514131211ULL); |
| EXPECT_EQ(virtual_offset, 0x1010U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetMaxU64WithVariableSizes) { |
| uint8_t buffer[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 8, 0)}); |
| |
| // Test reading 3-byte value. |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetMaxU64(&virtual_offset, 3), 0x563412U); |
| EXPECT_EQ(virtual_offset, 0x1003U); |
| |
| // Test reading 5-byte value. |
| virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetMaxU64(&virtual_offset, 5), 0x9A78563412ULL); |
| EXPECT_EQ(virtual_offset, 0x1005U); |
| } |
| |
| TEST(VirtualDataExtractorTest, GetMaxS64) { |
| // Test with negative number (sign extension). |
| uint8_t buffer[] = {0xFF, 0xFF, 0xFF, 0xFF}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)}); |
| |
| // Test reading 1-byte signed value (-1). |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetMaxS64(&virtual_offset, 1), -1); |
| EXPECT_EQ(virtual_offset, 0x1001U); |
| |
| // Test reading 2-byte signed value (-1). |
| virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetMaxS64(&virtual_offset, 2), -1); |
| EXPECT_EQ(virtual_offset, 0x1002U); |
| } |
| |
| TEST(VirtualDataExtractorTest, CannotReadAcrossEntryBoundaries) { |
| // Create buffer with two separate regions. |
| uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04, 0x11, 0x12, 0x13, 0x14}; |
| |
| // First entry: virtual 0x1000-0x1004 maps to physical 0-4. |
| // Second entry: virtual 0x2000-0x2004 maps to physical 4-8. |
| // Note: there's a gap in virtual addresses (0x1004-0x2000). |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, |
| Table{Entry(0x1000, 4, 0), Entry(0x2000, 4, 4)}); |
| |
| // Verify we can read within the first entry. |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetU32(&virtual_offset), 0x04030201U); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| |
| // Verify we can read within the second entry. |
| virtual_offset = 0x2000; |
| EXPECT_EQ(extractor->GetU32(&virtual_offset), 0x14131211U); |
| EXPECT_EQ(virtual_offset, 0x2004U); |
| |
| // Verify we CANNOT read in the gap between entries. |
| // This address is not in any lookup table entry. |
| virtual_offset = 0x1500; |
| EXPECT_EQ(extractor->GetU8(&virtual_offset), 0U); |
| EXPECT_EQ(virtual_offset, 0x1500U); |
| |
| // Verify we CANNOT read data pointer from the gap. |
| virtual_offset = 0x1800; |
| const void *data = extractor->GetData(&virtual_offset, 1); |
| EXPECT_EQ(data, nullptr); |
| EXPECT_EQ(virtual_offset, 0x1800U); // Offset unchanged. |
| |
| // Verify we can read individual bytes within each entry. |
| virtual_offset = 0x1003; |
| EXPECT_EQ(extractor->GetU8(&virtual_offset), 0x04U); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| |
| // Verify we CANNOT read past the end of an entry. |
| virtual_offset = 0x1004; |
| EXPECT_EQ(extractor->GetU8(&virtual_offset), 0U); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| } |
| |
| TEST(VirtualDataExtractorTest, ReadExactlyAtEntryEnd) { |
| uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04}; |
| |
| lldb::DataExtractorSP extractor = std::make_shared<VirtualDataExtractor>( |
| buffer, sizeof(buffer), eByteOrderLittle, 4, Table{Entry(0x1000, 4, 0)}); |
| |
| // Reading exactly to the end of an entry should work. |
| offset_t virtual_offset = 0x1000; |
| EXPECT_EQ(extractor->GetU32(&virtual_offset), 0x04030201U); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| |
| // But reading one byte past the end should fail. |
| virtual_offset = 0x1004; |
| EXPECT_EQ(extractor->GetU8(&virtual_offset), 0U); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| |
| // Reading from just before the end should work for smaller sizes. |
| virtual_offset = 0x1003; |
| EXPECT_EQ(extractor->GetU8(&virtual_offset), 0x04U); |
| EXPECT_EQ(virtual_offset, 0x1004U); |
| } |