1 //===-- IRMemoryMap.cpp -----------------------------------------*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "lldb/Core/DataBufferHeap.h"
11 #include "lldb/Core/DataExtractor.h"
12 #include "lldb/Core/Error.h"
13 #include "lldb/Core/Log.h"
14 #include "lldb/Core/Scalar.h"
15 #include "lldb/Expression/IRMemoryMap.h"
16 #include "lldb/Target/Process.h"
17 #include "lldb/Target/Target.h"
18
19 using namespace lldb_private;
20
IRMemoryMap(lldb::TargetSP target_sp)21 IRMemoryMap::IRMemoryMap (lldb::TargetSP target_sp) :
22 m_target_wp(target_sp)
23 {
24 if (target_sp)
25 m_process_wp = target_sp->GetProcessSP();
26 }
27
~IRMemoryMap()28 IRMemoryMap::~IRMemoryMap ()
29 {
30 lldb::ProcessSP process_sp = m_process_wp.lock();
31
32 if (process_sp)
33 {
34 AllocationMap::iterator iter;
35
36 Error err;
37
38 while ((iter = m_allocations.begin()) != m_allocations.end())
39 {
40 err.Clear();
41 if (iter->second.m_leak)
42 m_allocations.erase(iter);
43 else
44 Free(iter->first, err);
45 }
46 }
47 }
48
49 lldb::addr_t
FindSpace(size_t size)50 IRMemoryMap::FindSpace (size_t size)
51 {
52 lldb::TargetSP target_sp = m_target_wp.lock();
53 lldb::ProcessSP process_sp = m_process_wp.lock();
54
55 lldb::addr_t ret = LLDB_INVALID_ADDRESS;
56
57 if (process_sp && process_sp->CanJIT() && process_sp->IsAlive())
58 {
59 Error alloc_error;
60
61 ret = process_sp->AllocateMemory(size, lldb::ePermissionsReadable | lldb::ePermissionsWritable, alloc_error);
62
63 if (!alloc_error.Success())
64 return LLDB_INVALID_ADDRESS;
65 else
66 return ret;
67 }
68
69 for (int iterations = 0; iterations < 16; ++iterations)
70 {
71 lldb::addr_t candidate = LLDB_INVALID_ADDRESS;
72
73 switch (target_sp->GetArchitecture().GetAddressByteSize())
74 {
75 case 4:
76 {
77 uint32_t random_data = random();
78 candidate = random_data;
79 candidate &= ~0xfffull;
80 break;
81 }
82 case 8:
83 {
84 uint32_t random_low = random();
85 uint32_t random_high = random();
86 candidate = random_high;
87 candidate <<= 32ull;
88 candidate |= random_low;
89 candidate &= ~0xfffull;
90 break;
91 }
92 }
93
94 if (IntersectsAllocation(candidate, size))
95 continue;
96
97 ret = candidate;
98
99 return ret;
100 }
101
102 return ret;
103 }
104
105 IRMemoryMap::AllocationMap::iterator
FindAllocation(lldb::addr_t addr,size_t size)106 IRMemoryMap::FindAllocation (lldb::addr_t addr, size_t size)
107 {
108 if (addr == LLDB_INVALID_ADDRESS)
109 return m_allocations.end();
110
111 AllocationMap::iterator iter = m_allocations.lower_bound (addr);
112
113 if (iter == m_allocations.end() ||
114 iter->first > addr)
115 {
116 if (iter == m_allocations.begin())
117 return m_allocations.end();
118 iter--;
119 }
120
121 if (iter->first <= addr && iter->first + iter->second.m_size >= addr + size)
122 return iter;
123
124 return m_allocations.end();
125 }
126
127 bool
IntersectsAllocation(lldb::addr_t addr,size_t size)128 IRMemoryMap::IntersectsAllocation (lldb::addr_t addr, size_t size)
129 {
130 if (addr == LLDB_INVALID_ADDRESS)
131 return false;
132
133 AllocationMap::iterator iter = m_allocations.lower_bound (addr);
134
135 if (iter == m_allocations.end() ||
136 iter->first > addr)
137 {
138 if (iter == m_allocations.begin())
139 return false;
140
141 iter--;
142 }
143
144 while (iter != m_allocations.end() && iter->second.m_process_alloc < addr + size)
145 {
146 if (iter->second.m_process_start + iter->second.m_size > addr)
147 return true;
148
149 ++iter;
150 }
151
152 return false;
153 }
154
155 lldb::ByteOrder
GetByteOrder()156 IRMemoryMap::GetByteOrder()
157 {
158 lldb::ProcessSP process_sp = m_process_wp.lock();
159
160 if (process_sp)
161 return process_sp->GetByteOrder();
162
163 lldb::TargetSP target_sp = m_target_wp.lock();
164
165 if (target_sp)
166 return target_sp->GetArchitecture().GetByteOrder();
167
168 return lldb::eByteOrderInvalid;
169 }
170
171 uint32_t
GetAddressByteSize()172 IRMemoryMap::GetAddressByteSize()
173 {
174 lldb::ProcessSP process_sp = m_process_wp.lock();
175
176 if (process_sp)
177 return process_sp->GetAddressByteSize();
178
179 lldb::TargetSP target_sp = m_target_wp.lock();
180
181 if (target_sp)
182 return target_sp->GetArchitecture().GetAddressByteSize();
183
184 return UINT32_MAX;
185 }
186
187 ExecutionContextScope *
GetBestExecutionContextScope()188 IRMemoryMap::GetBestExecutionContextScope()
189 {
190 lldb::ProcessSP process_sp = m_process_wp.lock();
191
192 if (process_sp)
193 return process_sp.get();
194
195 lldb::TargetSP target_sp = m_target_wp.lock();
196
197 if (target_sp)
198 return target_sp.get();
199
200 return NULL;
201 }
202
Allocation(lldb::addr_t process_alloc,lldb::addr_t process_start,size_t size,uint32_t permissions,uint8_t alignment,AllocationPolicy policy)203 IRMemoryMap::Allocation::Allocation (lldb::addr_t process_alloc,
204 lldb::addr_t process_start,
205 size_t size,
206 uint32_t permissions,
207 uint8_t alignment,
208 AllocationPolicy policy) :
209 m_process_alloc (process_alloc),
210 m_process_start (process_start),
211 m_size (size),
212 m_permissions (permissions),
213 m_alignment (alignment),
214 m_policy (policy),
215 m_leak (false)
216 {
217 switch (policy)
218 {
219 default:
220 assert (0 && "We cannot reach this!");
221 case eAllocationPolicyHostOnly:
222 m_data.SetByteSize(size);
223 memset(m_data.GetBytes(), 0, size);
224 break;
225 case eAllocationPolicyProcessOnly:
226 break;
227 case eAllocationPolicyMirror:
228 m_data.SetByteSize(size);
229 memset(m_data.GetBytes(), 0, size);
230 break;
231 }
232 }
233
234 lldb::addr_t
Malloc(size_t size,uint8_t alignment,uint32_t permissions,AllocationPolicy policy,Error & error)235 IRMemoryMap::Malloc (size_t size, uint8_t alignment, uint32_t permissions, AllocationPolicy policy, Error &error)
236 {
237 error.Clear();
238
239 lldb::ProcessSP process_sp;
240 lldb::addr_t allocation_address = LLDB_INVALID_ADDRESS;
241 lldb::addr_t aligned_address = LLDB_INVALID_ADDRESS;
242
243 size_t alignment_mask = alignment - 1;
244 size_t allocation_size;
245
246 if (size == 0)
247 allocation_size = alignment;
248 else
249 allocation_size = (size & alignment_mask) ? ((size + alignment) & (~alignment_mask)) : size;
250
251 switch (policy)
252 {
253 default:
254 error.SetErrorToGenericError();
255 error.SetErrorString("Couldn't malloc: invalid allocation policy");
256 return LLDB_INVALID_ADDRESS;
257 case eAllocationPolicyHostOnly:
258 allocation_address = FindSpace(allocation_size);
259 if (allocation_address == LLDB_INVALID_ADDRESS)
260 {
261 error.SetErrorToGenericError();
262 error.SetErrorString("Couldn't malloc: address space is full");
263 return LLDB_INVALID_ADDRESS;
264 }
265 break;
266 case eAllocationPolicyMirror:
267 process_sp = m_process_wp.lock();
268 if (process_sp && process_sp->CanJIT() && process_sp->IsAlive())
269 {
270 allocation_address = process_sp->AllocateMemory(allocation_size, permissions, error);
271 if (!error.Success())
272 return LLDB_INVALID_ADDRESS;
273 }
274 else
275 {
276 policy = eAllocationPolicyHostOnly;
277 allocation_address = FindSpace(allocation_size);
278 if (allocation_address == LLDB_INVALID_ADDRESS)
279 {
280 error.SetErrorToGenericError();
281 error.SetErrorString("Couldn't malloc: address space is full");
282 return LLDB_INVALID_ADDRESS;
283 }
284 }
285 break;
286 case eAllocationPolicyProcessOnly:
287 process_sp = m_process_wp.lock();
288 if (process_sp)
289 {
290 if (process_sp->CanJIT() && process_sp->IsAlive())
291 {
292 allocation_address = process_sp->AllocateMemory(allocation_size, permissions, error);
293 if (!error.Success())
294 return LLDB_INVALID_ADDRESS;
295 }
296 else
297 {
298 error.SetErrorToGenericError();
299 error.SetErrorString("Couldn't malloc: process doesn't support allocating memory");
300 return LLDB_INVALID_ADDRESS;
301 }
302 }
303 else
304 {
305 error.SetErrorToGenericError();
306 error.SetErrorString("Couldn't malloc: process doesn't exist, and this memory must be in the process");
307 return LLDB_INVALID_ADDRESS;
308 }
309 break;
310 }
311
312
313 lldb::addr_t mask = alignment - 1;
314 aligned_address = (allocation_address + mask) & (~mask);
315
316 m_allocations[aligned_address] = Allocation(allocation_address,
317 aligned_address,
318 allocation_size,
319 permissions,
320 alignment,
321 policy);
322
323 if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS))
324 {
325 const char * policy_string;
326
327 switch (policy)
328 {
329 default:
330 policy_string = "<invalid policy>";
331 break;
332 case eAllocationPolicyHostOnly:
333 policy_string = "eAllocationPolicyHostOnly";
334 break;
335 case eAllocationPolicyProcessOnly:
336 policy_string = "eAllocationPolicyProcessOnly";
337 break;
338 case eAllocationPolicyMirror:
339 policy_string = "eAllocationPolicyMirror";
340 break;
341 }
342
343 log->Printf("IRMemoryMap::Malloc (%" PRIu64 ", 0x%" PRIx64 ", 0x%" PRIx64 ", %s) -> 0x%" PRIx64,
344 (uint64_t)allocation_size,
345 (uint64_t)alignment,
346 (uint64_t)permissions,
347 policy_string,
348 aligned_address);
349 }
350
351 return aligned_address;
352 }
353
354 void
Leak(lldb::addr_t process_address,Error & error)355 IRMemoryMap::Leak (lldb::addr_t process_address, Error &error)
356 {
357 error.Clear();
358
359 AllocationMap::iterator iter = m_allocations.find(process_address);
360
361 if (iter == m_allocations.end())
362 {
363 error.SetErrorToGenericError();
364 error.SetErrorString("Couldn't leak: allocation doesn't exist");
365 return;
366 }
367
368 Allocation &allocation = iter->second;
369
370 allocation.m_leak = true;
371 }
372
373 void
Free(lldb::addr_t process_address,Error & error)374 IRMemoryMap::Free (lldb::addr_t process_address, Error &error)
375 {
376 error.Clear();
377
378 AllocationMap::iterator iter = m_allocations.find(process_address);
379
380 if (iter == m_allocations.end())
381 {
382 error.SetErrorToGenericError();
383 error.SetErrorString("Couldn't free: allocation doesn't exist");
384 return;
385 }
386
387 Allocation &allocation = iter->second;
388
389 switch (allocation.m_policy)
390 {
391 default:
392 case eAllocationPolicyHostOnly:
393 {
394 lldb::ProcessSP process_sp = m_process_wp.lock();
395 if (process_sp)
396 {
397 if (process_sp->CanJIT() && process_sp->IsAlive())
398 process_sp->DeallocateMemory(allocation.m_process_alloc); // FindSpace allocated this for real
399 }
400
401 break;
402 }
403 case eAllocationPolicyMirror:
404 case eAllocationPolicyProcessOnly:
405 {
406 lldb::ProcessSP process_sp = m_process_wp.lock();
407 if (process_sp)
408 process_sp->DeallocateMemory(allocation.m_process_alloc);
409 }
410 }
411
412 if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS))
413 {
414 log->Printf("IRMemoryMap::Free (0x%" PRIx64 ") freed [0x%" PRIx64 "..0x%" PRIx64 ")",
415 (uint64_t)process_address,
416 iter->second.m_process_start,
417 iter->second.m_process_start + iter->second.m_size);
418 }
419
420 m_allocations.erase(iter);
421 }
422
423 void
WriteMemory(lldb::addr_t process_address,const uint8_t * bytes,size_t size,Error & error)424 IRMemoryMap::WriteMemory (lldb::addr_t process_address, const uint8_t *bytes, size_t size, Error &error)
425 {
426 error.Clear();
427
428 AllocationMap::iterator iter = FindAllocation(process_address, size);
429
430 if (iter == m_allocations.end())
431 {
432 lldb::ProcessSP process_sp = m_process_wp.lock();
433
434 if (process_sp)
435 {
436 process_sp->WriteMemory(process_address, bytes, size, error);
437 return;
438 }
439
440 error.SetErrorToGenericError();
441 error.SetErrorString("Couldn't write: no allocation contains the target range and the process doesn't exist");
442 return;
443 }
444
445 Allocation &allocation = iter->second;
446
447 uint64_t offset = process_address - allocation.m_process_start;
448
449 lldb::ProcessSP process_sp;
450
451 switch (allocation.m_policy)
452 {
453 default:
454 error.SetErrorToGenericError();
455 error.SetErrorString("Couldn't write: invalid allocation policy");
456 return;
457 case eAllocationPolicyHostOnly:
458 if (!allocation.m_data.GetByteSize())
459 {
460 error.SetErrorToGenericError();
461 error.SetErrorString("Couldn't write: data buffer is empty");
462 return;
463 }
464 ::memcpy (allocation.m_data.GetBytes() + offset, bytes, size);
465 break;
466 case eAllocationPolicyMirror:
467 if (!allocation.m_data.GetByteSize())
468 {
469 error.SetErrorToGenericError();
470 error.SetErrorString("Couldn't write: data buffer is empty");
471 return;
472 }
473 ::memcpy (allocation.m_data.GetBytes() + offset, bytes, size);
474 process_sp = m_process_wp.lock();
475 if (process_sp)
476 {
477 process_sp->WriteMemory(process_address, bytes, size, error);
478 if (!error.Success())
479 return;
480 }
481 break;
482 case eAllocationPolicyProcessOnly:
483 process_sp = m_process_wp.lock();
484 if (process_sp)
485 {
486 process_sp->WriteMemory(process_address, bytes, size, error);
487 if (!error.Success())
488 return;
489 }
490 break;
491 }
492
493 if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS))
494 {
495 log->Printf("IRMemoryMap::WriteMemory (0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRId64 ") went to [0x%" PRIx64 "..0x%" PRIx64 ")",
496 (uint64_t)process_address,
497 (uint64_t)bytes,
498 (uint64_t)size,
499 (uint64_t)allocation.m_process_start,
500 (uint64_t)allocation.m_process_start + (uint64_t)allocation.m_size);
501 }
502 }
503
504 void
WriteScalarToMemory(lldb::addr_t process_address,Scalar & scalar,size_t size,Error & error)505 IRMemoryMap::WriteScalarToMemory (lldb::addr_t process_address, Scalar &scalar, size_t size, Error &error)
506 {
507 error.Clear();
508
509 if (size == UINT32_MAX)
510 size = scalar.GetByteSize();
511
512 if (size > 0)
513 {
514 uint8_t buf[32];
515 const size_t mem_size = scalar.GetAsMemoryData (buf, size, GetByteOrder(), error);
516 if (mem_size > 0)
517 {
518 return WriteMemory(process_address, buf, mem_size, error);
519 }
520 else
521 {
522 error.SetErrorToGenericError();
523 error.SetErrorString ("Couldn't write scalar: failed to get scalar as memory data");
524 }
525 }
526 else
527 {
528 error.SetErrorToGenericError();
529 error.SetErrorString ("Couldn't write scalar: its size was zero");
530 }
531 return;
532 }
533
534 void
WritePointerToMemory(lldb::addr_t process_address,lldb::addr_t address,Error & error)535 IRMemoryMap::WritePointerToMemory (lldb::addr_t process_address, lldb::addr_t address, Error &error)
536 {
537 error.Clear();
538
539 Scalar scalar(address);
540
541 WriteScalarToMemory(process_address, scalar, GetAddressByteSize(), error);
542 }
543
544 void
ReadMemory(uint8_t * bytes,lldb::addr_t process_address,size_t size,Error & error)545 IRMemoryMap::ReadMemory (uint8_t *bytes, lldb::addr_t process_address, size_t size, Error &error)
546 {
547 error.Clear();
548
549 AllocationMap::iterator iter = FindAllocation(process_address, size);
550
551 if (iter == m_allocations.end())
552 {
553 lldb::ProcessSP process_sp = m_process_wp.lock();
554
555 if (process_sp)
556 {
557 process_sp->ReadMemory(process_address, bytes, size, error);
558 return;
559 }
560
561 lldb::TargetSP target_sp = m_target_wp.lock();
562
563 if (target_sp)
564 {
565 Address absolute_address(process_address);
566 target_sp->ReadMemory(absolute_address, false, bytes, size, error);
567 return;
568 }
569
570 error.SetErrorToGenericError();
571 error.SetErrorString("Couldn't read: no allocation contains the target range, and neither the process nor the target exist");
572 return;
573 }
574
575 Allocation &allocation = iter->second;
576
577 uint64_t offset = process_address - allocation.m_process_start;
578
579 lldb::ProcessSP process_sp;
580
581 switch (allocation.m_policy)
582 {
583 default:
584 error.SetErrorToGenericError();
585 error.SetErrorString("Couldn't read: invalid allocation policy");
586 return;
587 case eAllocationPolicyHostOnly:
588 if (!allocation.m_data.GetByteSize())
589 {
590 error.SetErrorToGenericError();
591 error.SetErrorString("Couldn't read: data buffer is empty");
592 return;
593 }
594 ::memcpy (bytes, allocation.m_data.GetBytes() + offset, size);
595 break;
596 case eAllocationPolicyMirror:
597 process_sp = m_process_wp.lock();
598 if (process_sp)
599 {
600 process_sp->ReadMemory(process_address, bytes, size, error);
601 if (!error.Success())
602 return;
603 }
604 else
605 {
606 if (!allocation.m_data.GetByteSize())
607 {
608 error.SetErrorToGenericError();
609 error.SetErrorString("Couldn't read: data buffer is empty");
610 return;
611 }
612 ::memcpy (bytes, allocation.m_data.GetBytes() + offset, size);
613 }
614 break;
615 case eAllocationPolicyProcessOnly:
616 process_sp = m_process_wp.lock();
617 if (process_sp)
618 {
619 process_sp->ReadMemory(process_address, bytes, size, error);
620 if (!error.Success())
621 return;
622 }
623 break;
624 }
625
626 if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS))
627 {
628 log->Printf("IRMemoryMap::ReadMemory (0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRId64 ") came from [0x%" PRIx64 "..0x%" PRIx64 ")",
629 (uint64_t)process_address,
630 (uint64_t)bytes,
631 (uint64_t)size,
632 (uint64_t)allocation.m_process_start,
633 (uint64_t)allocation.m_process_start + (uint64_t)allocation.m_size);
634 }
635 }
636
637 void
ReadScalarFromMemory(Scalar & scalar,lldb::addr_t process_address,size_t size,Error & error)638 IRMemoryMap::ReadScalarFromMemory (Scalar &scalar, lldb::addr_t process_address, size_t size, Error &error)
639 {
640 error.Clear();
641
642 if (size > 0)
643 {
644 DataBufferHeap buf(size, 0);
645 ReadMemory(buf.GetBytes(), process_address, size, error);
646
647 if (!error.Success())
648 return;
649
650 DataExtractor extractor(buf.GetBytes(), buf.GetByteSize(), GetByteOrder(), GetAddressByteSize());
651
652 lldb::offset_t offset = 0;
653
654 switch (size)
655 {
656 default:
657 error.SetErrorToGenericError();
658 error.SetErrorStringWithFormat("Couldn't read scalar: unsupported size %" PRIu64, (uint64_t)size);
659 return;
660 case 1: scalar = extractor.GetU8(&offset); break;
661 case 2: scalar = extractor.GetU16(&offset); break;
662 case 4: scalar = extractor.GetU32(&offset); break;
663 case 8: scalar = extractor.GetU64(&offset); break;
664 }
665 }
666 else
667 {
668 error.SetErrorToGenericError();
669 error.SetErrorString ("Couldn't read scalar: its size was zero");
670 }
671 return;
672 }
673
674 void
ReadPointerFromMemory(lldb::addr_t * address,lldb::addr_t process_address,Error & error)675 IRMemoryMap::ReadPointerFromMemory (lldb::addr_t *address, lldb::addr_t process_address, Error &error)
676 {
677 error.Clear();
678
679 Scalar pointer_scalar;
680 ReadScalarFromMemory(pointer_scalar, process_address, GetAddressByteSize(), error);
681
682 if (!error.Success())
683 return;
684
685 *address = pointer_scalar.ULongLong();
686
687 return;
688 }
689
690 void
GetMemoryData(DataExtractor & extractor,lldb::addr_t process_address,size_t size,Error & error)691 IRMemoryMap::GetMemoryData (DataExtractor &extractor, lldb::addr_t process_address, size_t size, Error &error)
692 {
693 error.Clear();
694
695 if (size > 0)
696 {
697 AllocationMap::iterator iter = FindAllocation(process_address, size);
698
699 if (iter == m_allocations.end())
700 {
701 error.SetErrorToGenericError();
702 error.SetErrorStringWithFormat("Couldn't find an allocation containing [0x%" PRIx64 "..0x%" PRIx64 ")", process_address, process_address + size);
703 return;
704 }
705
706 Allocation &allocation = iter->second;
707
708 switch (allocation.m_policy)
709 {
710 default:
711 error.SetErrorToGenericError();
712 error.SetErrorString("Couldn't get memory data: invalid allocation policy");
713 return;
714 case eAllocationPolicyProcessOnly:
715 error.SetErrorToGenericError();
716 error.SetErrorString("Couldn't get memory data: memory is only in the target");
717 return;
718 case eAllocationPolicyMirror:
719 {
720 lldb::ProcessSP process_sp = m_process_wp.lock();
721
722 if (!allocation.m_data.GetByteSize())
723 {
724 error.SetErrorToGenericError();
725 error.SetErrorString("Couldn't get memory data: data buffer is empty");
726 return;
727 }
728 if (process_sp)
729 {
730 process_sp->ReadMemory(allocation.m_process_start, allocation.m_data.GetBytes(), allocation.m_data.GetByteSize(), error);
731 if (!error.Success())
732 return;
733 uint64_t offset = process_address - allocation.m_process_start;
734 extractor = DataExtractor(allocation.m_data.GetBytes() + offset, size, GetByteOrder(), GetAddressByteSize());
735 return;
736 }
737 }
738 case eAllocationPolicyHostOnly:
739 if (!allocation.m_data.GetByteSize())
740 {
741 error.SetErrorToGenericError();
742 error.SetErrorString("Couldn't get memory data: data buffer is empty");
743 return;
744 }
745 uint64_t offset = process_address - allocation.m_process_start;
746 extractor = DataExtractor(allocation.m_data.GetBytes() + offset, size, GetByteOrder(), GetAddressByteSize());
747 return;
748 }
749 }
750 else
751 {
752 error.SetErrorToGenericError();
753 error.SetErrorString ("Couldn't get memory data: its size was zero");
754 return;
755 }
756 }
757
758
759