https://github.com/JDevlieghere updated 
https://github.com/llvm/llvm-project/pull/168802

>From 95123303a761c83ce9b3ac372b62efed73c57c7b Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <[email protected]>
Date: Wed, 19 Nov 2025 17:07:47 -0800
Subject: [PATCH] [lldb] Add VirtualDataExtractor for virtual address
 translation

Introduce VirtualDataExtractor, a DataExtractor subclass that enables
reading data at virtual addresses by translating them to physical buffer
offsets using a lookup table. The lookup table maps virtual address
ranges to physical offsets and enforces boundaries to prevent reads from
crossing entry limits.

The new class inherits from DataExtractor, overriding GetData and
PeekData to provide transparent virtual address translation for most of
the DataExtractor methods. The exception are the unchecked methods, that
bypass those methods and are overloaded as well.
---
 lldb/include/lldb/Utility/DataExtractor.h     |   6 +-
 .../lldb/Utility/VirtualDataExtractor.h       |  82 ++
 lldb/source/Utility/CMakeLists.txt            |   1 +
 lldb/source/Utility/VirtualDataExtractor.cpp  | 164 ++++
 lldb/unittests/Utility/CMakeLists.txt         |   1 +
 .../Utility/VirtualDataExtractorTest.cpp      | 708 ++++++++++++++++++
 6 files changed, 960 insertions(+), 2 deletions(-)
 create mode 100644 lldb/include/lldb/Utility/VirtualDataExtractor.h
 create mode 100644 lldb/source/Utility/VirtualDataExtractor.cpp
 create mode 100644 lldb/unittests/Utility/VirtualDataExtractorTest.cpp

diff --git a/lldb/include/lldb/Utility/DataExtractor.h 
b/lldb/include/lldb/Utility/DataExtractor.h
index b4960f5e87c85..fe217795ff3b1 100644
--- a/lldb/include/lldb/Utility/DataExtractor.h
+++ b/lldb/include/lldb/Utility/DataExtractor.h
@@ -334,7 +334,8 @@ class DataExtractor {
   /// \return
   ///     A pointer to the bytes in this object's data if the offset
   ///     and length are valid, or nullptr otherwise.
-  const void *GetData(lldb::offset_t *offset_ptr, lldb::offset_t length) const 
{
+  virtual const void *GetData(lldb::offset_t *offset_ptr,
+                              lldb::offset_t length) const {
     const uint8_t *ptr = PeekData(*offset_ptr, length);
     if (ptr)
       *offset_ptr += length;
@@ -829,7 +830,8 @@ class DataExtractor {
   ///     A non-nullptr data pointer if \a offset is a valid offset and
   ///     there are \a length bytes available at that offset, nullptr
   ///     otherwise.
-  const uint8_t *PeekData(lldb::offset_t offset, lldb::offset_t length) const {
+  virtual const uint8_t *PeekData(lldb::offset_t offset,
+                                  lldb::offset_t length) const {
     if (ValidOffsetForDataOfSize(offset, length))
       return m_start + offset;
     return nullptr;
diff --git a/lldb/include/lldb/Utility/VirtualDataExtractor.h 
b/lldb/include/lldb/Utility/VirtualDataExtractor.h
new file mode 100644
index 0000000000000..a57d83dde21be
--- /dev/null
+++ b/lldb/include/lldb/Utility/VirtualDataExtractor.h
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_UTILITY_VIRTUALDATAEXTRACTOR_H
+#define LLDB_UTILITY_VIRTUALDATAEXTRACTOR_H
+
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/RangeMap.h"
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+
+/// A DataExtractor subclass that allows reading data at virtual addresses
+/// using a lookup table that maps virtual address ranges to physical offsets.
+///
+/// This class maintains a lookup table where each entry contains:
+/// - base: starting virtual address for this entry
+/// - size: size of this entry in bytes
+/// - data: physical offset in the underlying data buffer
+///
+/// Reads are translated from virtual addresses to physical offsets using
+/// this lookup table. Reads cannot cross entry boundaries and this is
+/// enforced with assertions.
+class VirtualDataExtractor : public DataExtractor {
+public:
+  /// Type alias for the range map used internally.
+  /// Maps virtual addresses (base) to physical offsets (data).
+  using LookupTable =
+      RangeDataVector<lldb::offset_t, lldb::offset_t, lldb::offset_t>;
+
+  VirtualDataExtractor() = default;
+
+  VirtualDataExtractor(const void *data, lldb::offset_t data_length,
+                       lldb::ByteOrder byte_order, uint32_t addr_size,
+                       LookupTable lookup_table);
+
+  VirtualDataExtractor(const lldb::DataBufferSP &data_sp,
+                       lldb::ByteOrder byte_order, uint32_t addr_size,
+                       LookupTable lookup_table);
+
+  const void *GetData(lldb::offset_t *offset_ptr,
+                      lldb::offset_t length) const override;
+
+  const uint8_t *PeekData(lldb::offset_t offset,
+                          lldb::offset_t length) const override;
+
+  uint8_t GetU8_unchecked(lldb::offset_t *offset_ptr) const;
+
+  uint16_t GetU16_unchecked(lldb::offset_t *offset_ptr) const;
+
+  uint32_t GetU32_unchecked(lldb::offset_t *offset_ptr) const;
+
+  uint64_t GetU64_unchecked(lldb::offset_t *offset_ptr) const;
+
+  uint64_t GetMaxU64_unchecked(lldb::offset_t *offset_ptr,
+                               size_t byte_size) const;
+
+  uint64_t GetAddress_unchecked(lldb::offset_t *offset_ptr) const;
+
+  const LookupTable &GetLookupTable() const { return m_lookup_table; }
+
+protected:
+  /// Find the lookup entry that contains the given virtual address.
+  const LookupTable::Entry *FindEntry(lldb::offset_t virtual_addr) const;
+
+  /// Validate that a read at a virtual address is within bounds and
+  /// does not cross entry boundaries.
+  bool ValidateVirtualRead(lldb::offset_t virtual_addr,
+                           lldb::offset_t length) const;
+
+private:
+  LookupTable m_lookup_table;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_UTILITY_VIRTUALDATAEXTRACTOR_H
diff --git a/lldb/source/Utility/CMakeLists.txt 
b/lldb/source/Utility/CMakeLists.txt
index 1dd4d63f7016f..4696ed4690d37 100644
--- a/lldb/source/Utility/CMakeLists.txt
+++ b/lldb/source/Utility/CMakeLists.txt
@@ -78,6 +78,7 @@ add_lldb_library(lldbUtility NO_INTERNAL_DEPENDENCIES
   UserIDResolver.cpp
   VASprintf.cpp
   VMRange.cpp
+  VirtualDataExtractor.cpp
   XcodeSDK.cpp
   ZipFile.cpp
 
diff --git a/lldb/source/Utility/VirtualDataExtractor.cpp 
b/lldb/source/Utility/VirtualDataExtractor.cpp
new file mode 100644
index 0000000000000..537ba3930a91a
--- /dev/null
+++ b/lldb/source/Utility/VirtualDataExtractor.cpp
@@ -0,0 +1,164 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <cassert>
+
+using namespace lldb;
+using namespace lldb_private;
+
+VirtualDataExtractor::VirtualDataExtractor(const void *data,
+                                           offset_t data_length,
+                                           ByteOrder byte_order,
+                                           uint32_t addr_size,
+                                           LookupTable lookup_table)
+    : DataExtractor(data, data_length, byte_order, addr_size),
+      m_lookup_table(std::move(lookup_table)) {
+  m_lookup_table.Sort();
+}
+
+VirtualDataExtractor::VirtualDataExtractor(const DataBufferSP &data_sp,
+                                           ByteOrder byte_order,
+                                           uint32_t addr_size,
+                                           LookupTable lookup_table)
+    : DataExtractor(data_sp, byte_order, addr_size),
+      m_lookup_table(std::move(lookup_table)) {
+  m_lookup_table.Sort();
+}
+
+const VirtualDataExtractor::LookupTable::Entry *
+VirtualDataExtractor::FindEntry(offset_t virtual_addr) const {
+  // Use RangeDataVector's binary search instead of linear search.
+  return m_lookup_table.FindEntryThatContains(virtual_addr);
+}
+
+bool VirtualDataExtractor::ValidateVirtualRead(offset_t virtual_addr,
+                                               offset_t length) const {
+  const LookupTable::Entry *entry = FindEntry(virtual_addr);
+  if (!entry)
+    return false;
+
+  // Assert that the read does not cross entry boundaries.
+  // RangeData.Contains() checks if a range is fully contained.
+  assert(entry->Contains(LookupTable::Range(virtual_addr, length)) &&
+         "Read crosses lookup table entry boundary");
+
+  // Also validate that the physical offset is within the data buffer.
+  // RangeData.data contains the physical offset.
+  offset_t physical_offset = entry->data + (virtual_addr - entry->base);
+  return ValidOffsetForDataOfSize(physical_offset, length);
+}
+
+const void *VirtualDataExtractor::GetData(offset_t *offset_ptr,
+                                          offset_t length) const {
+  // Override to treat offset as virtual address.
+  if (!offset_ptr)
+    return nullptr;
+
+  offset_t virtual_addr = *offset_ptr;
+
+  if (!ValidateVirtualRead(virtual_addr, length))
+    return nullptr;
+
+  const LookupTable::Entry *entry = FindEntry(virtual_addr);
+  assert(entry && "ValidateVirtualRead should have found an entry");
+
+  offset_t physical_offset = entry->data + (virtual_addr - entry->base);
+  // Use base class PeekData directly to avoid recursion.
+  const void *result = DataExtractor::PeekData(physical_offset, length);
+
+  if (result) {
+    // Advance the virtual offset pointer.
+    *offset_ptr += length;
+  }
+
+  return result;
+}
+
+const uint8_t *VirtualDataExtractor::PeekData(offset_t offset,
+                                              offset_t length) const {
+  // Override to treat offset as virtual address.
+  if (!ValidateVirtualRead(offset, length))
+    return nullptr;
+
+  const LookupTable::Entry *entry = FindEntry(offset);
+  assert(entry && "ValidateVirtualRead should have found an entry");
+
+  offset_t physical_offset = entry->data + (offset - entry->base);
+  // Use the base class PeekData with the physical offset.
+  return DataExtractor::PeekData(physical_offset, length);
+}
+
+uint8_t VirtualDataExtractor::GetU8_unchecked(offset_t *offset_ptr) const {
+  offset_t virtual_addr = *offset_ptr;
+  const LookupTable::Entry *entry = FindEntry(virtual_addr);
+  assert(entry && "Unchecked methods require valid virtual address");
+
+  offset_t physical_offset = entry->data + (virtual_addr - entry->base);
+  uint8_t result = DataExtractor::GetU8_unchecked(&physical_offset);
+  *offset_ptr += 1;
+  return result;
+}
+
+uint16_t VirtualDataExtractor::GetU16_unchecked(offset_t *offset_ptr) const {
+  offset_t virtual_addr = *offset_ptr;
+  const LookupTable::Entry *entry = FindEntry(virtual_addr);
+  assert(entry && "Unchecked methods require valid virtual address");
+
+  offset_t physical_offset = entry->data + (virtual_addr - entry->base);
+  uint16_t result = DataExtractor::GetU16_unchecked(&physical_offset);
+  *offset_ptr += 2;
+  return result;
+}
+
+uint32_t VirtualDataExtractor::GetU32_unchecked(offset_t *offset_ptr) const {
+  offset_t virtual_addr = *offset_ptr;
+  const LookupTable::Entry *entry = FindEntry(virtual_addr);
+  assert(entry && "Unchecked methods require valid virtual address");
+
+  offset_t physical_offset = entry->data + (virtual_addr - entry->base);
+  uint32_t result = DataExtractor::GetU32_unchecked(&physical_offset);
+  *offset_ptr += 4;
+  return result;
+}
+
+uint64_t VirtualDataExtractor::GetU64_unchecked(offset_t *offset_ptr) const {
+  offset_t virtual_addr = *offset_ptr;
+  const LookupTable::Entry *entry = FindEntry(virtual_addr);
+  assert(entry && "Unchecked methods require valid virtual address");
+
+  offset_t physical_offset = entry->data + (virtual_addr - entry->base);
+  uint64_t result = DataExtractor::GetU64_unchecked(&physical_offset);
+  *offset_ptr += 8;
+  return result;
+}
+
+uint64_t VirtualDataExtractor::GetMaxU64_unchecked(offset_t *offset_ptr,
+                                                   size_t byte_size) const {
+  offset_t virtual_addr = *offset_ptr;
+  const LookupTable::Entry *entry = FindEntry(virtual_addr);
+  assert(entry && "Unchecked methods require valid virtual address");
+
+  offset_t physical_offset = entry->data + (virtual_addr - entry->base);
+  uint64_t result =
+      DataExtractor::GetMaxU64_unchecked(&physical_offset, byte_size);
+  *offset_ptr += byte_size;
+  return result;
+}
+
+uint64_t
+VirtualDataExtractor::GetAddress_unchecked(offset_t *offset_ptr) const {
+  offset_t virtual_addr = *offset_ptr;
+  const LookupTable::Entry *entry = FindEntry(virtual_addr);
+  assert(entry && "Unchecked methods require valid virtual address");
+
+  offset_t physical_offset = entry->data + (virtual_addr - entry->base);
+  uint64_t result = DataExtractor::GetAddress_unchecked(&physical_offset);
+  *offset_ptr += m_addr_size;
+  return result;
+}
diff --git a/lldb/unittests/Utility/CMakeLists.txt 
b/lldb/unittests/Utility/CMakeLists.txt
index aed4177f5edee..77b52079cf32b 100644
--- a/lldb/unittests/Utility/CMakeLists.txt
+++ b/lldb/unittests/Utility/CMakeLists.txt
@@ -48,6 +48,7 @@ add_lldb_unittest(UtilityTests
   UserIDResolverTest.cpp
   UUIDTest.cpp
   VASprintfTest.cpp
+  VirtualDataExtractorTest.cpp
   VMRangeTest.cpp
   XcodeSDKTest.cpp
 
diff --git a/lldb/unittests/Utility/VirtualDataExtractorTest.cpp 
b/lldb/unittests/Utility/VirtualDataExtractorTest.cpp
new file mode 100644
index 0000000000000..cb9edbc8950d9
--- /dev/null
+++ b/lldb/unittests/Utility/VirtualDataExtractorTest.cpp
@@ -0,0 +1,708 @@
+//===----------------------------------------------------------------------===//
+//
+// 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;
+
+TEST(VirtualDataExtractorTest, BasicConstruction) {
+  // Create a simple data buffer.
+  uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+
+  // Create a lookup table that maps virtual addresses to physical offsets.
+  VirtualDataExtractor::LookupTable lookup_table;
+  // Virtual address 0x1000-0x1008 maps to physical offset 0-8.
+  // Entry(base=virtual_offset, size, data=physical_offset).
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 8, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  EXPECT_EQ(extractor.GetByteSize(), 8U);
+}
+
+TEST(VirtualDataExtractorTest, GetDataAtVirtualOffset) {
+  uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 8, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 4, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  // 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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 4, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 4, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 8, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 8, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 8,
+                                 std::move(lookup_table));
+
+  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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 4, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 4, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderBig, 4,
+                                 std::move(lookup_table));
+
+  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.
+  };
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  // Map different virtual address ranges to different physical offsets.
+  // Entry(base=virtual_offset, size, data=physical_offset).
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(
+      0x1000, 4, 0)); // Virt 0x1000-0x1004 -> phys 0-4.
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(
+      0x2000, 4, 4)); // Virt 0x2000-0x2004 -> phys 4-8.
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(
+      0x3000, 4, 8)); // Virt 0x3000-0x3004 -> phys 8-12.
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  // 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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  // Create non-contiguous virtual address mapping.
+  // Entry(base=virtual_offset, size, data=physical_offset).
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(
+      0x1000, 2, 0)); // Virt 0x1000-0x1002 -> phys 0-2.
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(
+      0x5000, 2, 2)); // Virt 0x5000-0x5002 -> phys 2-4.
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  // 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));
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 4, 0));
+
+  VirtualDataExtractor extractor(data_sp, eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  offset_t virtual_offset = 0x1000;
+  EXPECT_EQ(extractor.GetU32(&virtual_offset), 0x04030201U);
+}
+
+TEST(VirtualDataExtractorTest, LookupTableAccess) {
+  uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 2, 0));
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x2000, 2, 2));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  const auto &table = extractor.GetLookupTable();
+  EXPECT_EQ(table.GetSize(), 2U);
+  EXPECT_FALSE(table.IsEmpty());
+}
+
+TEST(VirtualDataExtractorTest, NullPointerHandling) {
+  uint8_t buffer[] = {0x01, 0x02, 0x03, 0x04};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 4, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  // 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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  // Map virtual address 0x1000 to physical offset 4 (skipping first 4 bytes).
+  // Entry(base=virtual_offset, size, data=physical_offset).
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 4, 4));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 4, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 4, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 8, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 8, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 8,
+                                 std::move(lookup_table));
+
+  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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 8, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  // 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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 4, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 4, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderBig, 4,
+                                 std::move(lookup_table));
+
+  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'};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 6, 0));
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x2000, 12, 6));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  // 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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 4, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 8, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 8,
+                                 std::move(lookup_table));
+
+  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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 7, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  // 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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 4, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  // 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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 8, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  // 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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 8, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  // 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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 8, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  // 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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 16, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 8,
+                                 std::move(lookup_table));
+
+  // 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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 8, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  // 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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 4, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  // 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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  // First entry: virtual 0x1000-0x1004 maps to physical 0-4.
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 4, 0));
+  // Second entry: virtual 0x2000-0x2004 maps to physical 4-8.
+  // Note: there's a gap in virtual addresses (0x1004-0x2000).
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x2000, 4, 4));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  // 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};
+
+  VirtualDataExtractor::LookupTable lookup_table;
+  lookup_table.Append(VirtualDataExtractor::LookupTable::Entry(0x1000, 4, 0));
+
+  VirtualDataExtractor extractor(buffer, sizeof(buffer), eByteOrderLittle, 4,
+                                 std::move(lookup_table));
+
+  // 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);
+}

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to