1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
|
//===----------------------------------------------------------------------===//
//
// 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();
}
VirtualDataExtractor::VirtualDataExtractor(const DataBufferSP &data_sp,
LookupTable lookup_table)
: DataExtractor(data_sp), 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;
}
offset_t VirtualDataExtractor::SetData(const void *bytes, lldb::offset_t length,
lldb::ByteOrder byte_order) {
// Invoked from the base class ctor.
if (!m_data_sp || m_start == nullptr)
return DataExtractor::SetData(bytes, length, byte_order);
// A no-op SetData that is setting the same data buffer again.
if (!m_data_sp && m_start == bytes && length == GetVirtualByteSize())
return GetVirtualByteSize();
assert("SetData(1) called on VirtualDataExtractor that already had data" &&
false);
DataExtractor::SetData(bytes, length, byte_order);
ResetLookupTableToMatchPhysical();
return GetVirtualByteSize();
}
offset_t VirtualDataExtractor::SetData(const DataExtractor &data,
lldb::offset_t offset,
lldb::offset_t length) {
// Invoked from the base class ctor
if (!m_data_sp || m_start == nullptr)
return DataExtractor::SetData(data, offset, length);
// A no-op SetData that is setting the same data buffer again
if (m_data_sp && data.GetSharedDataBuffer().get() == m_data_sp.get() &&
offset == 0 && length == GetVirtualByteSize())
return GetVirtualByteSize();
assert("SetData(2) called on VirtualDataExtractor that already had data" &&
false);
DataExtractor::SetData(data, offset, length);
ResetLookupTableToMatchPhysical();
return GetVirtualByteSize();
}
offset_t VirtualDataExtractor::SetData(const lldb::DataBufferSP &data_sp,
lldb::offset_t offset,
lldb::offset_t length) {
// Invoked from the base class ctor
if (!m_data_sp || m_start == nullptr)
return DataExtractor::SetData(data_sp, offset, length);
// A no-op SetData that is setting the same data buffer again
if (m_data_sp && data_sp.get() == m_data_sp.get() && offset == 0 &&
length == GetVirtualByteSize())
return GetVirtualByteSize();
assert("SetData(3) called on VirtualDataExtractor that already had data" &&
false);
DataExtractor::SetData(data_sp, offset, length);
ResetLookupTableToMatchPhysical();
return GetVirtualByteSize();
}
void VirtualDataExtractor::ResetLookupTableToMatchPhysical() {
// calling SetData on a VirtualDataExtractor that already has a
// data buffer means the LookupTable needs to be either replaced, or
// if we assume the buffer is a subset of the original, we need to
// update all the entries to have correct new offsets into the buffer
// and remove entries that are outside the new range.
// For now, zero out the LookupTable and behave as if this is a simple
// DataExtractor.
m_lookup_table.Clear();
m_lookup_table.Append(
VirtualDataExtractor::LookupTable::Entry{0, GetPhysicalByteSize(), 0});
}
uint64_t VirtualDataExtractor::GetVirtualByteSize() const {
offset_t lowest = -1ULL;
offset_t highest = 0;
for (const auto ent : m_lookup_table) {
lowest = std::min(lowest, ent.base);
highest = std::max(highest, ent.base + ent.size);
}
return highest - lowest;
}
uint64_t VirtualDataExtractor::GetPhysicalByteSize() const {
return m_end - m_start;
}
offset_t VirtualDataExtractor::VirtualBytesLeft(offset_t virtual_offset) const {
const offset_t size = GetVirtualByteSize();
if (size > virtual_offset)
return size - virtual_offset;
return 0;
}
offset_t
VirtualDataExtractor::PhysicalBytesLeft(offset_t physical_offset) const {
const offset_t size = m_end - m_start;
if (size > physical_offset)
return size - physical_offset;
return 0;
}
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;
}
DataExtractorSP
VirtualDataExtractor::GetSubsetExtractorSP(offset_t virtual_offset,
offset_t virtual_length) {
const LookupTable::Entry *entry = FindEntry(virtual_offset);
assert(
entry &&
"VirtualDataExtractor subset extractor requires valid virtual address");
if (!entry)
return {};
// Entry::data is the offset into the DataBuffer's actual start/end range
// Entry::base is the virtual address at the start of this region of data
offset_t offset_into_entry_range = virtual_offset - entry->base;
assert(
offset_into_entry_range + virtual_length <= entry->size &&
"VirtualDataExtractor subset may not span multiple LookupTable entries");
if (offset_into_entry_range + virtual_length > entry->size)
return {};
// We could support a Subset VirtualDataExtractor which covered
// multiple LookupTable virtual entries, but we'd need to mutate
// all of the LookupTable entries that were properly included in
// the Subset, a bit tricky. So we won't implement that until it's
// needed.
offset_t physical_start = entry->data + offset_into_entry_range;
std::shared_ptr<DataExtractor> new_sp = std::make_shared<DataExtractor>(
GetSharedDataBuffer(), GetByteOrder(), GetAddressByteSize());
new_sp->SetData(GetSharedDataBuffer(), physical_start, virtual_length);
return new_sp;
}
// Return a DataExtractorSP that contains a single LookupTable's entry; all
// bytes are guaranteed to be readable.
DataExtractorSP
VirtualDataExtractor::GetSubsetExtractorSP(offset_t virtual_offset) {
const LookupTable::Entry *entry = FindEntry(virtual_offset);
assert(
entry &&
"VirtualDataExtractor subset extractor requires valid virtual address");
if (!entry)
return {};
// Entry::data is the offset into the DataBuffer's actual start/end range
// Entry::base is the virtual address at the start of this region of data
offset_t offset_into_entry_range = virtual_offset - entry->base;
offset_t physical_start = entry->data + offset_into_entry_range;
std::shared_ptr<DataExtractor> new_sp = std::make_shared<DataExtractor>(
GetSharedDataBuffer(), GetByteOrder(), GetAddressByteSize());
new_sp->SetData(GetSharedDataBuffer(), physical_start,
entry->size - offset_into_entry_range);
return new_sp;
}
// Return an ArrayRef to the first contiguous region of the LookupTable
// only. The LookupTable entries may have gaps of unmapped data, and we
// can't include those in the ArrayRef or something may touch those pages.
llvm::ArrayRef<uint8_t> VirtualDataExtractor::GetData() const {
const LookupTable::Entry *entry = FindEntry(0);
assert(entry &&
"VirtualDataExtractor GetData requires valid virtual address");
if (!entry)
return {};
return {m_start + static_cast<size_t>(entry->data), static_cast<size_t>(entry->size)};
}
|