1 | //===-- IRMemoryMap.cpp ---------------------------------------------------===// |
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/Expression/IRMemoryMap.h" |
10 | #include "lldb/Target/MemoryRegionInfo.h" |
11 | #include "lldb/Target/Process.h" |
12 | #include "lldb/Target/Target.h" |
13 | #include "lldb/Utility/DataBufferHeap.h" |
14 | #include "lldb/Utility/DataExtractor.h" |
15 | #include "lldb/Utility/LLDBAssert.h" |
16 | #include "lldb/Utility/LLDBLog.h" |
17 | #include "lldb/Utility/Log.h" |
18 | #include "lldb/Utility/Scalar.h" |
19 | #include "lldb/Utility/Status.h" |
20 | |
21 | using namespace lldb_private; |
22 | |
23 | IRMemoryMap::IRMemoryMap(lldb::TargetSP target_sp) : m_target_wp(target_sp) { |
24 | if (target_sp) |
25 | m_process_wp = target_sp->GetProcessSP(); |
26 | } |
27 | |
28 | IRMemoryMap::~IRMemoryMap() { |
29 | lldb::ProcessSP process_sp = m_process_wp.lock(); |
30 | |
31 | if (process_sp) { |
32 | AllocationMap::iterator iter; |
33 | |
34 | Status err; |
35 | |
36 | while ((iter = m_allocations.begin()) != m_allocations.end()) { |
37 | err.Clear(); |
38 | if (iter->second.m_leak) |
39 | m_allocations.erase(position: iter); |
40 | else |
41 | Free(process_address: iter->first, error&: err); |
42 | } |
43 | } |
44 | } |
45 | |
46 | lldb::addr_t IRMemoryMap::FindSpace(size_t size) { |
47 | // The FindSpace algorithm's job is to find a region of memory that the |
48 | // underlying process is unlikely to be using. |
49 | // |
50 | // The memory returned by this function will never be written to. The only |
51 | // point is that it should not shadow process memory if possible, so that |
52 | // expressions processing real values from the process do not use the wrong |
53 | // data. |
54 | // |
55 | // If the process can in fact allocate memory (CanJIT() lets us know this) |
56 | // then this can be accomplished just be allocating memory in the inferior. |
57 | // Then no guessing is required. |
58 | |
59 | lldb::TargetSP target_sp = m_target_wp.lock(); |
60 | lldb::ProcessSP process_sp = m_process_wp.lock(); |
61 | |
62 | const bool process_is_alive = process_sp && process_sp->IsAlive(); |
63 | |
64 | lldb::addr_t ret = LLDB_INVALID_ADDRESS; |
65 | if (size == 0) |
66 | return ret; |
67 | |
68 | if (process_is_alive && process_sp->CanJIT()) { |
69 | Status alloc_error; |
70 | |
71 | ret = process_sp->AllocateMemory(size, permissions: lldb::ePermissionsReadable | |
72 | lldb::ePermissionsWritable, |
73 | error&: alloc_error); |
74 | |
75 | if (!alloc_error.Success()) |
76 | return LLDB_INVALID_ADDRESS; |
77 | else |
78 | return ret; |
79 | } |
80 | |
81 | // At this point we know that we need to hunt. |
82 | // |
83 | // First, go to the end of the existing allocations we've made if there are |
84 | // any allocations. Otherwise start at the beginning of memory. |
85 | |
86 | if (m_allocations.empty()) { |
87 | ret = 0x0; |
88 | } else { |
89 | auto back = m_allocations.rbegin(); |
90 | lldb::addr_t addr = back->first; |
91 | size_t alloc_size = back->second.m_size; |
92 | ret = llvm::alignTo(Value: addr + alloc_size, Align: 4096); |
93 | } |
94 | |
95 | uint64_t end_of_memory; |
96 | switch (GetAddressByteSize()) { |
97 | case 2: |
98 | end_of_memory = 0xffffull; |
99 | break; |
100 | case 4: |
101 | end_of_memory = 0xffffffffull; |
102 | break; |
103 | case 8: |
104 | end_of_memory = 0xffffffffffffffffull; |
105 | break; |
106 | default: |
107 | lldbassert(false && "Invalid address size." ); |
108 | return LLDB_INVALID_ADDRESS; |
109 | } |
110 | |
111 | // Now, if it's possible to use the GetMemoryRegionInfo API to detect mapped |
112 | // regions, walk forward through memory until a region is found that has |
113 | // adequate space for our allocation. |
114 | if (process_is_alive) { |
115 | MemoryRegionInfo region_info; |
116 | Status err = process_sp->GetMemoryRegionInfo(load_addr: ret, range_info&: region_info); |
117 | if (err.Success()) { |
118 | while (true) { |
119 | if (region_info.GetReadable() != MemoryRegionInfo::OptionalBool::eNo || |
120 | region_info.GetWritable() != MemoryRegionInfo::OptionalBool::eNo || |
121 | region_info.GetExecutable() != |
122 | MemoryRegionInfo::OptionalBool::eNo) { |
123 | if (region_info.GetRange().GetRangeEnd() - 1 >= end_of_memory) { |
124 | ret = LLDB_INVALID_ADDRESS; |
125 | break; |
126 | } else { |
127 | ret = region_info.GetRange().GetRangeEnd(); |
128 | } |
129 | } else if (ret + size < region_info.GetRange().GetRangeEnd()) { |
130 | return ret; |
131 | } else { |
132 | // ret stays the same. We just need to walk a bit further. |
133 | } |
134 | |
135 | err = process_sp->GetMemoryRegionInfo( |
136 | load_addr: region_info.GetRange().GetRangeEnd(), range_info&: region_info); |
137 | if (err.Fail()) { |
138 | lldbassert(0 && "GetMemoryRegionInfo() succeeded, then failed" ); |
139 | ret = LLDB_INVALID_ADDRESS; |
140 | break; |
141 | } |
142 | } |
143 | } |
144 | } |
145 | |
146 | // We've tried our algorithm, and it didn't work. Now we have to reset back |
147 | // to the end of the allocations we've already reported, or use a 'sensible' |
148 | // default if this is our first allocation. |
149 | if (m_allocations.empty()) { |
150 | uint64_t alloc_address = target_sp->GetExprAllocAddress(); |
151 | if (alloc_address > 0) { |
152 | if (alloc_address >= end_of_memory) { |
153 | lldbassert(0 && "The allocation address for expression evaluation must " |
154 | "be within process address space" ); |
155 | return LLDB_INVALID_ADDRESS; |
156 | } |
157 | ret = alloc_address; |
158 | } else { |
159 | uint32_t address_byte_size = GetAddressByteSize(); |
160 | if (address_byte_size != UINT32_MAX) { |
161 | switch (address_byte_size) { |
162 | case 2: |
163 | ret = 0x8000ull; |
164 | break; |
165 | case 4: |
166 | ret = 0xee000000ull; |
167 | break; |
168 | case 8: |
169 | ret = 0xdead0fff00000000ull; |
170 | break; |
171 | default: |
172 | lldbassert(false && "Invalid address size." ); |
173 | return LLDB_INVALID_ADDRESS; |
174 | } |
175 | } |
176 | } |
177 | } else { |
178 | auto back = m_allocations.rbegin(); |
179 | lldb::addr_t addr = back->first; |
180 | size_t alloc_size = back->second.m_size; |
181 | uint64_t align = target_sp->GetExprAllocAlign(); |
182 | if (align == 0) |
183 | align = 4096; |
184 | ret = llvm::alignTo(Value: addr + alloc_size, Align: align); |
185 | } |
186 | |
187 | return ret; |
188 | } |
189 | |
190 | IRMemoryMap::AllocationMap::iterator |
191 | IRMemoryMap::FindAllocation(lldb::addr_t addr, size_t size) { |
192 | if (addr == LLDB_INVALID_ADDRESS) |
193 | return m_allocations.end(); |
194 | |
195 | AllocationMap::iterator iter = m_allocations.lower_bound(x: addr); |
196 | |
197 | if (iter == m_allocations.end() || iter->first > addr) { |
198 | if (iter == m_allocations.begin()) |
199 | return m_allocations.end(); |
200 | iter--; |
201 | } |
202 | |
203 | if (iter->first <= addr && iter->first + iter->second.m_size >= addr + size) |
204 | return iter; |
205 | |
206 | return m_allocations.end(); |
207 | } |
208 | |
209 | bool IRMemoryMap::IntersectsAllocation(lldb::addr_t addr, size_t size) const { |
210 | if (addr == LLDB_INVALID_ADDRESS) |
211 | return false; |
212 | |
213 | AllocationMap::const_iterator iter = m_allocations.lower_bound(x: addr); |
214 | |
215 | // Since we only know that the returned interval begins at a location greater |
216 | // than or equal to where the given interval begins, it's possible that the |
217 | // given interval intersects either the returned interval or the previous |
218 | // interval. Thus, we need to check both. Note that we only need to check |
219 | // these two intervals. Since all intervals are disjoint it is not possible |
220 | // that an adjacent interval does not intersect, but a non-adjacent interval |
221 | // does intersect. |
222 | if (iter != m_allocations.end()) { |
223 | if (AllocationsIntersect(addr1: addr, size1: size, addr2: iter->second.m_process_start, |
224 | size2: iter->second.m_size)) |
225 | return true; |
226 | } |
227 | |
228 | if (iter != m_allocations.begin()) { |
229 | --iter; |
230 | if (AllocationsIntersect(addr1: addr, size1: size, addr2: iter->second.m_process_start, |
231 | size2: iter->second.m_size)) |
232 | return true; |
233 | } |
234 | |
235 | return false; |
236 | } |
237 | |
238 | bool IRMemoryMap::AllocationsIntersect(lldb::addr_t addr1, size_t size1, |
239 | lldb::addr_t addr2, size_t size2) { |
240 | // Given two half open intervals [A, B) and [X, Y), the only 6 permutations |
241 | // that satisfy A<B and X<Y are the following: |
242 | // A B X Y |
243 | // A X B Y (intersects) |
244 | // A X Y B (intersects) |
245 | // X A B Y (intersects) |
246 | // X A Y B (intersects) |
247 | // X Y A B |
248 | // The first is B <= X, and the last is Y <= A. So the condition is !(B <= X |
249 | // || Y <= A)), or (X < B && A < Y) |
250 | return (addr2 < (addr1 + size1)) && (addr1 < (addr2 + size2)); |
251 | } |
252 | |
253 | lldb::ByteOrder IRMemoryMap::GetByteOrder() { |
254 | lldb::ProcessSP process_sp = m_process_wp.lock(); |
255 | |
256 | if (process_sp) |
257 | return process_sp->GetByteOrder(); |
258 | |
259 | lldb::TargetSP target_sp = m_target_wp.lock(); |
260 | |
261 | if (target_sp) |
262 | return target_sp->GetArchitecture().GetByteOrder(); |
263 | |
264 | return lldb::eByteOrderInvalid; |
265 | } |
266 | |
267 | uint32_t IRMemoryMap::GetAddressByteSize() { |
268 | lldb::ProcessSP process_sp = m_process_wp.lock(); |
269 | |
270 | if (process_sp) |
271 | return process_sp->GetAddressByteSize(); |
272 | |
273 | lldb::TargetSP target_sp = m_target_wp.lock(); |
274 | |
275 | if (target_sp) |
276 | return target_sp->GetArchitecture().GetAddressByteSize(); |
277 | |
278 | return UINT32_MAX; |
279 | } |
280 | |
281 | ExecutionContextScope *IRMemoryMap::GetBestExecutionContextScope() const { |
282 | lldb::ProcessSP process_sp = m_process_wp.lock(); |
283 | |
284 | if (process_sp) |
285 | return process_sp.get(); |
286 | |
287 | lldb::TargetSP target_sp = m_target_wp.lock(); |
288 | |
289 | if (target_sp) |
290 | return target_sp.get(); |
291 | |
292 | return nullptr; |
293 | } |
294 | |
295 | IRMemoryMap::Allocation::Allocation(lldb::addr_t process_alloc, |
296 | lldb::addr_t process_start, size_t size, |
297 | uint32_t permissions, uint8_t alignment, |
298 | AllocationPolicy policy) |
299 | : m_process_alloc(process_alloc), m_process_start(process_start), |
300 | m_size(size), m_policy(policy), m_leak(false), m_permissions(permissions), |
301 | m_alignment(alignment) { |
302 | switch (policy) { |
303 | default: |
304 | llvm_unreachable("Invalid AllocationPolicy" ); |
305 | case eAllocationPolicyHostOnly: |
306 | case eAllocationPolicyMirror: |
307 | m_data.SetByteSize(size); |
308 | break; |
309 | case eAllocationPolicyProcessOnly: |
310 | break; |
311 | } |
312 | } |
313 | |
314 | lldb::addr_t IRMemoryMap::Malloc(size_t size, uint8_t alignment, |
315 | uint32_t permissions, AllocationPolicy policy, |
316 | bool zero_memory, Status &error) { |
317 | lldb_private::Log *log(GetLog(mask: LLDBLog::Expressions)); |
318 | error.Clear(); |
319 | |
320 | lldb::ProcessSP process_sp; |
321 | lldb::addr_t allocation_address = LLDB_INVALID_ADDRESS; |
322 | lldb::addr_t aligned_address = LLDB_INVALID_ADDRESS; |
323 | |
324 | size_t allocation_size; |
325 | |
326 | if (size == 0) { |
327 | // FIXME: Malloc(0) should either return an invalid address or assert, in |
328 | // order to cut down on unnecessary allocations. |
329 | allocation_size = alignment; |
330 | } else { |
331 | // Round up the requested size to an aligned value. |
332 | allocation_size = llvm::alignTo(Value: size, Align: alignment); |
333 | |
334 | // The process page cache does not see the requested alignment. We can't |
335 | // assume its result will be any more than 1-byte aligned. To work around |
336 | // this, request `alignment - 1` additional bytes. |
337 | allocation_size += alignment - 1; |
338 | } |
339 | |
340 | switch (policy) { |
341 | default: |
342 | error.SetErrorToGenericError(); |
343 | error.SetErrorString("Couldn't malloc: invalid allocation policy" ); |
344 | return LLDB_INVALID_ADDRESS; |
345 | case eAllocationPolicyHostOnly: |
346 | allocation_address = FindSpace(size: allocation_size); |
347 | if (allocation_address == LLDB_INVALID_ADDRESS) { |
348 | error.SetErrorToGenericError(); |
349 | error.SetErrorString("Couldn't malloc: address space is full" ); |
350 | return LLDB_INVALID_ADDRESS; |
351 | } |
352 | break; |
353 | case eAllocationPolicyMirror: |
354 | process_sp = m_process_wp.lock(); |
355 | LLDB_LOGF(log, |
356 | "IRMemoryMap::%s process_sp=0x%" PRIxPTR |
357 | ", process_sp->CanJIT()=%s, process_sp->IsAlive()=%s" , |
358 | __FUNCTION__, reinterpret_cast<uintptr_t>(process_sp.get()), |
359 | process_sp && process_sp->CanJIT() ? "true" : "false" , |
360 | process_sp && process_sp->IsAlive() ? "true" : "false" ); |
361 | if (process_sp && process_sp->CanJIT() && process_sp->IsAlive()) { |
362 | if (!zero_memory) |
363 | allocation_address = |
364 | process_sp->AllocateMemory(size: allocation_size, permissions, error); |
365 | else |
366 | allocation_address = |
367 | process_sp->CallocateMemory(size: allocation_size, permissions, error); |
368 | |
369 | if (!error.Success()) |
370 | return LLDB_INVALID_ADDRESS; |
371 | } else { |
372 | LLDB_LOGF(log, |
373 | "IRMemoryMap::%s switching to eAllocationPolicyHostOnly " |
374 | "due to failed condition (see previous expr log message)" , |
375 | __FUNCTION__); |
376 | policy = eAllocationPolicyHostOnly; |
377 | allocation_address = FindSpace(size: allocation_size); |
378 | if (allocation_address == LLDB_INVALID_ADDRESS) { |
379 | error.SetErrorToGenericError(); |
380 | error.SetErrorString("Couldn't malloc: address space is full" ); |
381 | return LLDB_INVALID_ADDRESS; |
382 | } |
383 | } |
384 | break; |
385 | case eAllocationPolicyProcessOnly: |
386 | process_sp = m_process_wp.lock(); |
387 | if (process_sp) { |
388 | if (process_sp->CanJIT() && process_sp->IsAlive()) { |
389 | if (!zero_memory) |
390 | allocation_address = |
391 | process_sp->AllocateMemory(size: allocation_size, permissions, error); |
392 | else |
393 | allocation_address = |
394 | process_sp->CallocateMemory(size: allocation_size, permissions, error); |
395 | |
396 | if (!error.Success()) |
397 | return LLDB_INVALID_ADDRESS; |
398 | } else { |
399 | error.SetErrorToGenericError(); |
400 | error.SetErrorString( |
401 | "Couldn't malloc: process doesn't support allocating memory" ); |
402 | return LLDB_INVALID_ADDRESS; |
403 | } |
404 | } else { |
405 | error.SetErrorToGenericError(); |
406 | error.SetErrorString("Couldn't malloc: process doesn't exist, and this " |
407 | "memory must be in the process" ); |
408 | return LLDB_INVALID_ADDRESS; |
409 | } |
410 | break; |
411 | } |
412 | |
413 | lldb::addr_t mask = alignment - 1; |
414 | aligned_address = (allocation_address + mask) & (~mask); |
415 | |
416 | m_allocations.emplace( |
417 | args: std::piecewise_construct, args: std::forward_as_tuple(args&: aligned_address), |
418 | args: std::forward_as_tuple(args&: allocation_address, args&: aligned_address, |
419 | args&: allocation_size, args&: permissions, args&: alignment, args&: policy)); |
420 | |
421 | if (zero_memory) { |
422 | Status write_error; |
423 | std::vector<uint8_t> zero_buf(size, 0); |
424 | WriteMemory(process_address: aligned_address, bytes: zero_buf.data(), size, error&: write_error); |
425 | } |
426 | |
427 | if (log) { |
428 | const char *policy_string; |
429 | |
430 | switch (policy) { |
431 | default: |
432 | policy_string = "<invalid policy>" ; |
433 | break; |
434 | case eAllocationPolicyHostOnly: |
435 | policy_string = "eAllocationPolicyHostOnly" ; |
436 | break; |
437 | case eAllocationPolicyProcessOnly: |
438 | policy_string = "eAllocationPolicyProcessOnly" ; |
439 | break; |
440 | case eAllocationPolicyMirror: |
441 | policy_string = "eAllocationPolicyMirror" ; |
442 | break; |
443 | } |
444 | |
445 | LLDB_LOGF(log, |
446 | "IRMemoryMap::Malloc (%" PRIu64 ", 0x%" PRIx64 ", 0x%" PRIx64 |
447 | ", %s) -> 0x%" PRIx64, |
448 | (uint64_t)allocation_size, (uint64_t)alignment, |
449 | (uint64_t)permissions, policy_string, aligned_address); |
450 | } |
451 | |
452 | return aligned_address; |
453 | } |
454 | |
455 | void IRMemoryMap::Leak(lldb::addr_t process_address, Status &error) { |
456 | error.Clear(); |
457 | |
458 | AllocationMap::iterator iter = m_allocations.find(x: process_address); |
459 | |
460 | if (iter == m_allocations.end()) { |
461 | error.SetErrorToGenericError(); |
462 | error.SetErrorString("Couldn't leak: allocation doesn't exist" ); |
463 | return; |
464 | } |
465 | |
466 | Allocation &allocation = iter->second; |
467 | |
468 | allocation.m_leak = true; |
469 | } |
470 | |
471 | void IRMemoryMap::Free(lldb::addr_t process_address, Status &error) { |
472 | error.Clear(); |
473 | |
474 | AllocationMap::iterator iter = m_allocations.find(x: process_address); |
475 | |
476 | if (iter == m_allocations.end()) { |
477 | error.SetErrorToGenericError(); |
478 | error.SetErrorString("Couldn't free: allocation doesn't exist" ); |
479 | return; |
480 | } |
481 | |
482 | Allocation &allocation = iter->second; |
483 | |
484 | switch (allocation.m_policy) { |
485 | default: |
486 | case eAllocationPolicyHostOnly: { |
487 | lldb::ProcessSP process_sp = m_process_wp.lock(); |
488 | if (process_sp) { |
489 | if (process_sp->CanJIT() && process_sp->IsAlive()) |
490 | process_sp->DeallocateMemory( |
491 | ptr: allocation.m_process_alloc); // FindSpace allocated this for real |
492 | } |
493 | |
494 | break; |
495 | } |
496 | case eAllocationPolicyMirror: |
497 | case eAllocationPolicyProcessOnly: { |
498 | lldb::ProcessSP process_sp = m_process_wp.lock(); |
499 | if (process_sp) |
500 | process_sp->DeallocateMemory(ptr: allocation.m_process_alloc); |
501 | } |
502 | } |
503 | |
504 | if (lldb_private::Log *log = GetLog(mask: LLDBLog::Expressions)) { |
505 | LLDB_LOGF(log, |
506 | "IRMemoryMap::Free (0x%" PRIx64 ") freed [0x%" PRIx64 |
507 | "..0x%" PRIx64 ")" , |
508 | (uint64_t)process_address, iter->second.m_process_start, |
509 | iter->second.m_process_start + iter->second.m_size); |
510 | } |
511 | |
512 | m_allocations.erase(position: iter); |
513 | } |
514 | |
515 | bool IRMemoryMap::GetAllocSize(lldb::addr_t address, size_t &size) { |
516 | AllocationMap::iterator iter = FindAllocation(addr: address, size); |
517 | if (iter == m_allocations.end()) |
518 | return false; |
519 | |
520 | Allocation &al = iter->second; |
521 | |
522 | if (address > (al.m_process_start + al.m_size)) { |
523 | size = 0; |
524 | return false; |
525 | } |
526 | |
527 | if (address > al.m_process_start) { |
528 | int dif = address - al.m_process_start; |
529 | size = al.m_size - dif; |
530 | return true; |
531 | } |
532 | |
533 | size = al.m_size; |
534 | return true; |
535 | } |
536 | |
537 | void IRMemoryMap::WriteMemory(lldb::addr_t process_address, |
538 | const uint8_t *bytes, size_t size, |
539 | Status &error) { |
540 | error.Clear(); |
541 | |
542 | AllocationMap::iterator iter = FindAllocation(addr: process_address, size); |
543 | |
544 | if (iter == m_allocations.end()) { |
545 | lldb::ProcessSP process_sp = m_process_wp.lock(); |
546 | |
547 | if (process_sp) { |
548 | process_sp->WriteMemory(vm_addr: process_address, buf: bytes, size, error); |
549 | return; |
550 | } |
551 | |
552 | error.SetErrorToGenericError(); |
553 | error.SetErrorString("Couldn't write: no allocation contains the target " |
554 | "range and the process doesn't exist" ); |
555 | return; |
556 | } |
557 | |
558 | Allocation &allocation = iter->second; |
559 | |
560 | uint64_t offset = process_address - allocation.m_process_start; |
561 | |
562 | lldb::ProcessSP process_sp; |
563 | |
564 | switch (allocation.m_policy) { |
565 | default: |
566 | error.SetErrorToGenericError(); |
567 | error.SetErrorString("Couldn't write: invalid allocation policy" ); |
568 | return; |
569 | case eAllocationPolicyHostOnly: |
570 | if (!allocation.m_data.GetByteSize()) { |
571 | error.SetErrorToGenericError(); |
572 | error.SetErrorString("Couldn't write: data buffer is empty" ); |
573 | return; |
574 | } |
575 | ::memcpy(dest: allocation.m_data.GetBytes() + offset, src: bytes, n: size); |
576 | break; |
577 | case eAllocationPolicyMirror: |
578 | if (!allocation.m_data.GetByteSize()) { |
579 | error.SetErrorToGenericError(); |
580 | error.SetErrorString("Couldn't write: data buffer is empty" ); |
581 | return; |
582 | } |
583 | ::memcpy(dest: allocation.m_data.GetBytes() + offset, src: bytes, n: size); |
584 | process_sp = m_process_wp.lock(); |
585 | if (process_sp) { |
586 | process_sp->WriteMemory(vm_addr: process_address, buf: bytes, size, error); |
587 | if (!error.Success()) |
588 | return; |
589 | } |
590 | break; |
591 | case eAllocationPolicyProcessOnly: |
592 | process_sp = m_process_wp.lock(); |
593 | if (process_sp) { |
594 | process_sp->WriteMemory(vm_addr: process_address, buf: bytes, size, error); |
595 | if (!error.Success()) |
596 | return; |
597 | } |
598 | break; |
599 | } |
600 | |
601 | if (lldb_private::Log *log = GetLog(mask: LLDBLog::Expressions)) { |
602 | LLDB_LOGF(log, |
603 | "IRMemoryMap::WriteMemory (0x%" PRIx64 ", 0x%" PRIxPTR |
604 | ", 0x%" PRId64 ") went to [0x%" PRIx64 "..0x%" PRIx64 ")" , |
605 | (uint64_t)process_address, reinterpret_cast<uintptr_t>(bytes), (uint64_t)size, |
606 | (uint64_t)allocation.m_process_start, |
607 | (uint64_t)allocation.m_process_start + |
608 | (uint64_t)allocation.m_size); |
609 | } |
610 | } |
611 | |
612 | void IRMemoryMap::WriteScalarToMemory(lldb::addr_t process_address, |
613 | Scalar &scalar, size_t size, |
614 | Status &error) { |
615 | error.Clear(); |
616 | |
617 | if (size == UINT32_MAX) |
618 | size = scalar.GetByteSize(); |
619 | |
620 | if (size > 0) { |
621 | uint8_t buf[32]; |
622 | const size_t mem_size = |
623 | scalar.GetAsMemoryData(dst: buf, dst_len: size, dst_byte_order: GetByteOrder(), error); |
624 | if (mem_size > 0) { |
625 | return WriteMemory(process_address, bytes: buf, size: mem_size, error); |
626 | } else { |
627 | error.SetErrorToGenericError(); |
628 | error.SetErrorString( |
629 | "Couldn't write scalar: failed to get scalar as memory data" ); |
630 | } |
631 | } else { |
632 | error.SetErrorToGenericError(); |
633 | error.SetErrorString("Couldn't write scalar: its size was zero" ); |
634 | } |
635 | } |
636 | |
637 | void IRMemoryMap::WritePointerToMemory(lldb::addr_t process_address, |
638 | lldb::addr_t address, Status &error) { |
639 | error.Clear(); |
640 | |
641 | Scalar scalar(address); |
642 | |
643 | WriteScalarToMemory(process_address, scalar, size: GetAddressByteSize(), error); |
644 | } |
645 | |
646 | void IRMemoryMap::ReadMemory(uint8_t *bytes, lldb::addr_t process_address, |
647 | size_t size, Status &error) { |
648 | error.Clear(); |
649 | |
650 | AllocationMap::iterator iter = FindAllocation(addr: process_address, size); |
651 | |
652 | if (iter == m_allocations.end()) { |
653 | lldb::ProcessSP process_sp = m_process_wp.lock(); |
654 | |
655 | if (process_sp) { |
656 | process_sp->ReadMemory(vm_addr: process_address, buf: bytes, size, error); |
657 | return; |
658 | } |
659 | |
660 | lldb::TargetSP target_sp = m_target_wp.lock(); |
661 | |
662 | if (target_sp) { |
663 | Address absolute_address(process_address); |
664 | target_sp->ReadMemory(addr: absolute_address, dst: bytes, dst_len: size, error, force_live_memory: true); |
665 | return; |
666 | } |
667 | |
668 | error.SetErrorToGenericError(); |
669 | error.SetErrorString("Couldn't read: no allocation contains the target " |
670 | "range, and neither the process nor the target exist" ); |
671 | return; |
672 | } |
673 | |
674 | Allocation &allocation = iter->second; |
675 | |
676 | uint64_t offset = process_address - allocation.m_process_start; |
677 | |
678 | if (offset > allocation.m_size) { |
679 | error.SetErrorToGenericError(); |
680 | error.SetErrorString("Couldn't read: data is not in the allocation" ); |
681 | return; |
682 | } |
683 | |
684 | lldb::ProcessSP process_sp; |
685 | |
686 | switch (allocation.m_policy) { |
687 | default: |
688 | error.SetErrorToGenericError(); |
689 | error.SetErrorString("Couldn't read: invalid allocation policy" ); |
690 | return; |
691 | case eAllocationPolicyHostOnly: |
692 | if (!allocation.m_data.GetByteSize()) { |
693 | error.SetErrorToGenericError(); |
694 | error.SetErrorString("Couldn't read: data buffer is empty" ); |
695 | return; |
696 | } |
697 | if (allocation.m_data.GetByteSize() < offset + size) { |
698 | error.SetErrorToGenericError(); |
699 | error.SetErrorString("Couldn't read: not enough underlying data" ); |
700 | return; |
701 | } |
702 | |
703 | ::memcpy(dest: bytes, src: allocation.m_data.GetBytes() + offset, n: size); |
704 | break; |
705 | case eAllocationPolicyMirror: |
706 | process_sp = m_process_wp.lock(); |
707 | if (process_sp) { |
708 | process_sp->ReadMemory(vm_addr: process_address, buf: bytes, size, error); |
709 | if (!error.Success()) |
710 | return; |
711 | } else { |
712 | if (!allocation.m_data.GetByteSize()) { |
713 | error.SetErrorToGenericError(); |
714 | error.SetErrorString("Couldn't read: data buffer is empty" ); |
715 | return; |
716 | } |
717 | ::memcpy(dest: bytes, src: allocation.m_data.GetBytes() + offset, n: size); |
718 | } |
719 | break; |
720 | case eAllocationPolicyProcessOnly: |
721 | process_sp = m_process_wp.lock(); |
722 | if (process_sp) { |
723 | process_sp->ReadMemory(vm_addr: process_address, buf: bytes, size, error); |
724 | if (!error.Success()) |
725 | return; |
726 | } |
727 | break; |
728 | } |
729 | |
730 | if (lldb_private::Log *log = GetLog(mask: LLDBLog::Expressions)) { |
731 | LLDB_LOGF(log, |
732 | "IRMemoryMap::ReadMemory (0x%" PRIx64 ", 0x%" PRIxPTR |
733 | ", 0x%" PRId64 ") came from [0x%" PRIx64 "..0x%" PRIx64 ")" , |
734 | (uint64_t)process_address, reinterpret_cast<uintptr_t>(bytes), (uint64_t)size, |
735 | (uint64_t)allocation.m_process_start, |
736 | (uint64_t)allocation.m_process_start + |
737 | (uint64_t)allocation.m_size); |
738 | } |
739 | } |
740 | |
741 | void IRMemoryMap::ReadScalarFromMemory(Scalar &scalar, |
742 | lldb::addr_t process_address, |
743 | size_t size, Status &error) { |
744 | error.Clear(); |
745 | |
746 | if (size > 0) { |
747 | DataBufferHeap buf(size, 0); |
748 | ReadMemory(bytes: buf.GetBytes(), process_address, size, error); |
749 | |
750 | if (!error.Success()) |
751 | return; |
752 | |
753 | DataExtractor (buf.GetBytes(), buf.GetByteSize(), GetByteOrder(), |
754 | GetAddressByteSize()); |
755 | |
756 | lldb::offset_t offset = 0; |
757 | |
758 | switch (size) { |
759 | default: |
760 | error.SetErrorToGenericError(); |
761 | error.SetErrorStringWithFormat( |
762 | "Couldn't read scalar: unsupported size %" PRIu64, (uint64_t)size); |
763 | return; |
764 | case 1: |
765 | scalar = extractor.GetU8(offset_ptr: &offset); |
766 | break; |
767 | case 2: |
768 | scalar = extractor.GetU16(offset_ptr: &offset); |
769 | break; |
770 | case 4: |
771 | scalar = extractor.GetU32(offset_ptr: &offset); |
772 | break; |
773 | case 8: |
774 | scalar = extractor.GetU64(offset_ptr: &offset); |
775 | break; |
776 | } |
777 | } else { |
778 | error.SetErrorToGenericError(); |
779 | error.SetErrorString("Couldn't read scalar: its size was zero" ); |
780 | } |
781 | } |
782 | |
783 | void IRMemoryMap::ReadPointerFromMemory(lldb::addr_t *address, |
784 | lldb::addr_t process_address, |
785 | Status &error) { |
786 | error.Clear(); |
787 | |
788 | Scalar pointer_scalar; |
789 | ReadScalarFromMemory(scalar&: pointer_scalar, process_address, size: GetAddressByteSize(), |
790 | error); |
791 | |
792 | if (!error.Success()) |
793 | return; |
794 | |
795 | *address = pointer_scalar.ULongLong(); |
796 | } |
797 | |
798 | void IRMemoryMap::(DataExtractor &, |
799 | lldb::addr_t process_address, size_t size, |
800 | Status &error) { |
801 | error.Clear(); |
802 | |
803 | if (size > 0) { |
804 | AllocationMap::iterator iter = FindAllocation(addr: process_address, size); |
805 | |
806 | if (iter == m_allocations.end()) { |
807 | error.SetErrorToGenericError(); |
808 | error.SetErrorStringWithFormat( |
809 | "Couldn't find an allocation containing [0x%" PRIx64 "..0x%" PRIx64 |
810 | ")" , |
811 | process_address, process_address + size); |
812 | return; |
813 | } |
814 | |
815 | Allocation &allocation = iter->second; |
816 | |
817 | switch (allocation.m_policy) { |
818 | default: |
819 | error.SetErrorToGenericError(); |
820 | error.SetErrorString( |
821 | "Couldn't get memory data: invalid allocation policy" ); |
822 | return; |
823 | case eAllocationPolicyProcessOnly: |
824 | error.SetErrorToGenericError(); |
825 | error.SetErrorString( |
826 | "Couldn't get memory data: memory is only in the target" ); |
827 | return; |
828 | case eAllocationPolicyMirror: { |
829 | lldb::ProcessSP process_sp = m_process_wp.lock(); |
830 | |
831 | if (!allocation.m_data.GetByteSize()) { |
832 | error.SetErrorToGenericError(); |
833 | error.SetErrorString("Couldn't get memory data: data buffer is empty" ); |
834 | return; |
835 | } |
836 | if (process_sp) { |
837 | process_sp->ReadMemory(vm_addr: allocation.m_process_start, |
838 | buf: allocation.m_data.GetBytes(), |
839 | size: allocation.m_data.GetByteSize(), error); |
840 | if (!error.Success()) |
841 | return; |
842 | uint64_t offset = process_address - allocation.m_process_start; |
843 | extractor = DataExtractor(allocation.m_data.GetBytes() + offset, size, |
844 | GetByteOrder(), GetAddressByteSize()); |
845 | return; |
846 | } |
847 | } break; |
848 | case eAllocationPolicyHostOnly: |
849 | if (!allocation.m_data.GetByteSize()) { |
850 | error.SetErrorToGenericError(); |
851 | error.SetErrorString("Couldn't get memory data: data buffer is empty" ); |
852 | return; |
853 | } |
854 | uint64_t offset = process_address - allocation.m_process_start; |
855 | extractor = DataExtractor(allocation.m_data.GetBytes() + offset, size, |
856 | GetByteOrder(), GetAddressByteSize()); |
857 | return; |
858 | } |
859 | } else { |
860 | error.SetErrorToGenericError(); |
861 | error.SetErrorString("Couldn't get memory data: its size was zero" ); |
862 | return; |
863 | } |
864 | } |
865 | |