Skip to content

Commit 934bf47

Browse files
authored
Merge pull request #11984 from jasonmolenda/impl-virtualdataextractor
[lldb] Add VirtualDataExtractor for virtual address translation (llvm#168802)
2 parents 12154f7 + 6be0b24 commit 934bf47

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
@@ -78,6 +78,7 @@ add_lldb_library(lldbUtility NO_INTERNAL_DEPENDENCIES
7878
UserIDResolver.cpp
7979
VASprintf.cpp
8080
VMRange.cpp
81+
VirtualDataExtractor.cpp
8182
XcodeSDK.cpp
8283
ZipFile.cpp
8384

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
@@ -48,6 +48,7 @@ add_lldb_unittest(UtilityTests
4848
UserIDResolverTest.cpp
4949
UUIDTest.cpp
5050
VASprintfTest.cpp
51+
VirtualDataExtractorTest.cpp
5152
VMRangeTest.cpp
5253
XcodeSDKTest.cpp
5354

0 commit comments

Comments
 (0)