Skip to content

Commit 9438b74

Browse files
authored
[lldb] Add VirtualDataExtractor for virtual address translation (#168802)
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.
1 parent 318d932 commit 9438b74

File tree

7 files changed

+811
-6
lines changed

7 files changed

+811
-6
lines changed

lldb/include/lldb/Utility/DataExtractor.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,8 @@ class DataExtractor {
334334
/// \return
335335
/// A pointer to the bytes in this object's data if the offset
336336
/// and length are valid, or nullptr otherwise.
337-
const void *GetData(lldb::offset_t *offset_ptr, lldb::offset_t length) const {
337+
virtual const void *GetData(lldb::offset_t *offset_ptr,
338+
lldb::offset_t length) const {
338339
const uint8_t *ptr = PeekData(*offset_ptr, length);
339340
if (ptr)
340341
*offset_ptr += length;
@@ -609,17 +610,17 @@ class DataExtractor {
609610
/// The extracted uint8_t value.
610611
uint8_t GetU8(lldb::offset_t *offset_ptr) const;
611612

612-
uint8_t GetU8_unchecked(lldb::offset_t *offset_ptr) const {
613+
virtual uint8_t GetU8_unchecked(lldb::offset_t *offset_ptr) const {
613614
uint8_t val = m_start[*offset_ptr];
614615
*offset_ptr += 1;
615616
return val;
616617
}
617618

618-
uint16_t GetU16_unchecked(lldb::offset_t *offset_ptr) const;
619+
virtual uint16_t GetU16_unchecked(lldb::offset_t *offset_ptr) const;
619620

620-
uint32_t GetU32_unchecked(lldb::offset_t *offset_ptr) const;
621+
virtual uint32_t GetU32_unchecked(lldb::offset_t *offset_ptr) const;
621622

622-
uint64_t GetU64_unchecked(lldb::offset_t *offset_ptr) const;
623+
virtual uint64_t GetU64_unchecked(lldb::offset_t *offset_ptr) const;
623624
/// Extract \a count uint8_t values from \a *offset_ptr.
624625
///
625626
/// Extract \a count uint8_t values from the binary data at the offset
@@ -829,7 +830,8 @@ class DataExtractor {
829830
/// A non-nullptr data pointer if \a offset is a valid offset and
830831
/// there are \a length bytes available at that offset, nullptr
831832
/// otherwise.
832-
const uint8_t *PeekData(lldb::offset_t offset, lldb::offset_t length) const {
833+
virtual const uint8_t *PeekData(lldb::offset_t offset,
834+
lldb::offset_t length) const {
833835
if (ValidOffsetForDataOfSize(offset, length))
834836
return m_start + offset;
835837
return nullptr;

lldb/include/lldb/Utility/RangeMap.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,10 @@ class RangeDataVector {
465465

466466
RangeDataVector(Compare compare = Compare()) : m_compare(compare) {}
467467

468+
RangeDataVector(std::initializer_list<AugmentedEntry> entries,
469+
Compare compare = Compare())
470+
: m_entries(entries), m_compare(compare) {}
471+
468472
~RangeDataVector() = default;
469473

470474
void Append(const Entry &entry) { m_entries.emplace_back(entry); }
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLDB_UTILITY_VIRTUALDATAEXTRACTOR_H
10+
#define LLDB_UTILITY_VIRTUALDATAEXTRACTOR_H
11+
12+
#include "lldb/Utility/DataExtractor.h"
13+
#include "lldb/Utility/RangeMap.h"
14+
#include "lldb/lldb-types.h"
15+
16+
namespace lldb_private {
17+
18+
/// A DataExtractor subclass that allows reading data at virtual addresses
19+
/// using a lookup table that maps virtual address ranges to physical offsets.
20+
///
21+
/// This class maintains a lookup table where each entry contains:
22+
/// - base: starting virtual address for this entry
23+
/// - size: size of this entry in bytes
24+
/// - data: physical offset in the underlying data buffer
25+
///
26+
/// Reads are translated from virtual addresses to physical offsets using
27+
/// this lookup table. Reads cannot cross entry boundaries and this is
28+
/// enforced with assertions.
29+
class VirtualDataExtractor : public DataExtractor {
30+
public:
31+
/// Type alias for the range map used internally.
32+
/// Maps virtual addresses (base) to physical offsets (data).
33+
using LookupTable =
34+
RangeDataVector<lldb::offset_t, lldb::offset_t, lldb::offset_t>;
35+
36+
VirtualDataExtractor() = default;
37+
38+
VirtualDataExtractor(const void *data, lldb::offset_t data_length,
39+
lldb::ByteOrder byte_order, uint32_t addr_size,
40+
LookupTable lookup_table);
41+
42+
VirtualDataExtractor(const lldb::DataBufferSP &data_sp,
43+
lldb::ByteOrder byte_order, uint32_t addr_size,
44+
LookupTable lookup_table);
45+
46+
const void *GetData(lldb::offset_t *offset_ptr,
47+
lldb::offset_t length) const override;
48+
49+
const uint8_t *PeekData(lldb::offset_t offset,
50+
lldb::offset_t length) const override;
51+
52+
/// Unchecked overrides
53+
/// @{
54+
uint8_t GetU8_unchecked(lldb::offset_t *offset_ptr) const override;
55+
uint16_t GetU16_unchecked(lldb::offset_t *offset_ptr) const override;
56+
uint32_t GetU32_unchecked(lldb::offset_t *offset_ptr) const override;
57+
uint64_t GetU64_unchecked(lldb::offset_t *offset_ptr) const override;
58+
/// @}
59+
60+
protected:
61+
/// Find the lookup entry that contains the given virtual address.
62+
const LookupTable::Entry *FindEntry(lldb::offset_t virtual_addr) const;
63+
64+
/// Validate that a read at a virtual address is within bounds and
65+
/// does not cross entry boundaries.
66+
bool ValidateVirtualRead(lldb::offset_t virtual_addr,
67+
lldb::offset_t length) const;
68+
69+
private:
70+
LookupTable m_lookup_table;
71+
};
72+
73+
} // namespace lldb_private
74+
75+
#endif // LLDB_UTILITY_VIRTUALDATAEXTRACTOR_H

lldb/source/Utility/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ add_lldb_library(lldbUtility NO_INTERNAL_DEPENDENCIES
7777
UserIDResolver.cpp
7878
VASprintf.cpp
7979
VMRange.cpp
80+
VirtualDataExtractor.cpp
8081
XcodeSDK.cpp
8182
ZipFile.cpp
8283

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "lldb/Utility/VirtualDataExtractor.h"
10+
#include <cassert>
11+
12+
using namespace lldb;
13+
using namespace lldb_private;
14+
15+
VirtualDataExtractor::VirtualDataExtractor(const void *data,
16+
offset_t data_length,
17+
ByteOrder byte_order,
18+
uint32_t addr_size,
19+
LookupTable lookup_table)
20+
: DataExtractor(data, data_length, byte_order, addr_size),
21+
m_lookup_table(std::move(lookup_table)) {
22+
m_lookup_table.Sort();
23+
}
24+
25+
VirtualDataExtractor::VirtualDataExtractor(const DataBufferSP &data_sp,
26+
ByteOrder byte_order,
27+
uint32_t addr_size,
28+
LookupTable lookup_table)
29+
: DataExtractor(data_sp, byte_order, addr_size),
30+
m_lookup_table(std::move(lookup_table)) {
31+
m_lookup_table.Sort();
32+
}
33+
34+
const VirtualDataExtractor::LookupTable::Entry *
35+
VirtualDataExtractor::FindEntry(offset_t virtual_addr) const {
36+
// Use RangeDataVector's binary search instead of linear search.
37+
return m_lookup_table.FindEntryThatContains(virtual_addr);
38+
}
39+
40+
bool VirtualDataExtractor::ValidateVirtualRead(offset_t virtual_addr,
41+
offset_t length) const {
42+
const LookupTable::Entry *entry = FindEntry(virtual_addr);
43+
if (!entry)
44+
return false;
45+
46+
// Assert that the read does not cross entry boundaries.
47+
// RangeData.Contains() checks if a range is fully contained.
48+
assert(entry->Contains(LookupTable::Range(virtual_addr, length)) &&
49+
"Read crosses lookup table entry boundary");
50+
51+
// Also validate that the physical offset is within the data buffer.
52+
// RangeData.data contains the physical offset.
53+
offset_t physical_offset = entry->data + (virtual_addr - entry->base);
54+
return ValidOffsetForDataOfSize(physical_offset, length);
55+
}
56+
57+
const void *VirtualDataExtractor::GetData(offset_t *offset_ptr,
58+
offset_t length) const {
59+
// Override to treat offset as virtual address.
60+
if (!offset_ptr)
61+
return nullptr;
62+
63+
offset_t virtual_addr = *offset_ptr;
64+
65+
if (!ValidateVirtualRead(virtual_addr, length))
66+
return nullptr;
67+
68+
const LookupTable::Entry *entry = FindEntry(virtual_addr);
69+
assert(entry && "ValidateVirtualRead should have found an entry");
70+
71+
offset_t physical_offset = entry->data + (virtual_addr - entry->base);
72+
// Use base class PeekData directly to avoid recursion.
73+
const void *result = DataExtractor::PeekData(physical_offset, length);
74+
75+
if (result) {
76+
// Advance the virtual offset pointer.
77+
*offset_ptr += length;
78+
}
79+
80+
return result;
81+
}
82+
83+
const uint8_t *VirtualDataExtractor::PeekData(offset_t offset,
84+
offset_t length) const {
85+
// Override to treat offset as virtual address.
86+
if (!ValidateVirtualRead(offset, length))
87+
return nullptr;
88+
89+
const LookupTable::Entry *entry = FindEntry(offset);
90+
assert(entry && "ValidateVirtualRead should have found an entry");
91+
92+
offset_t physical_offset = entry->data + (offset - entry->base);
93+
// Use the base class PeekData with the physical offset.
94+
return DataExtractor::PeekData(physical_offset, length);
95+
}
96+
97+
uint8_t VirtualDataExtractor::GetU8_unchecked(offset_t *offset_ptr) const {
98+
offset_t virtual_addr = *offset_ptr;
99+
const LookupTable::Entry *entry = FindEntry(virtual_addr);
100+
assert(entry && "Unchecked methods require valid virtual address");
101+
102+
offset_t physical_offset = entry->data + (virtual_addr - entry->base);
103+
uint8_t result = DataExtractor::GetU8_unchecked(&physical_offset);
104+
*offset_ptr += 1;
105+
return result;
106+
}
107+
108+
uint16_t VirtualDataExtractor::GetU16_unchecked(offset_t *offset_ptr) const {
109+
offset_t virtual_addr = *offset_ptr;
110+
const LookupTable::Entry *entry = FindEntry(virtual_addr);
111+
assert(entry && "Unchecked methods require valid virtual address");
112+
113+
offset_t physical_offset = entry->data + (virtual_addr - entry->base);
114+
uint16_t result = DataExtractor::GetU16_unchecked(&physical_offset);
115+
*offset_ptr += 2;
116+
return result;
117+
}
118+
119+
uint32_t VirtualDataExtractor::GetU32_unchecked(offset_t *offset_ptr) const {
120+
offset_t virtual_addr = *offset_ptr;
121+
const LookupTable::Entry *entry = FindEntry(virtual_addr);
122+
assert(entry && "Unchecked methods require valid virtual address");
123+
124+
offset_t physical_offset = entry->data + (virtual_addr - entry->base);
125+
uint32_t result = DataExtractor::GetU32_unchecked(&physical_offset);
126+
*offset_ptr += 4;
127+
return result;
128+
}
129+
130+
uint64_t VirtualDataExtractor::GetU64_unchecked(offset_t *offset_ptr) const {
131+
offset_t virtual_addr = *offset_ptr;
132+
const LookupTable::Entry *entry = FindEntry(virtual_addr);
133+
assert(entry && "Unchecked methods require valid virtual address");
134+
135+
offset_t physical_offset = entry->data + (virtual_addr - entry->base);
136+
uint64_t result = DataExtractor::GetU64_unchecked(&physical_offset);
137+
*offset_ptr += 8;
138+
return result;
139+
}

lldb/unittests/Utility/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ add_lldb_unittest(UtilityTests
4747
UserIDResolverTest.cpp
4848
UUIDTest.cpp
4949
VASprintfTest.cpp
50+
VirtualDataExtractorTest.cpp
5051
VMRangeTest.cpp
5152
XcodeSDKTest.cpp
5253

0 commit comments

Comments
 (0)