1 //===-- IRExecutionUnit.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 // C Includes
11 // C++ Includes
12 // Other libraries and framework includes
13 #include "llvm/ExecutionEngine/ExecutionEngine.h"
14 #include "llvm/IR/LLVMContext.h"
15 #include "llvm/IR/Module.h"
16 #include "llvm/Support/SourceMgr.h"
17 // Project includes
18 #include "lldb/Core/DataBufferHeap.h"
19 #include "lldb/Core/DataExtractor.h"
20 #include "lldb/Core/Disassembler.h"
21 #include "lldb/Core/Log.h"
22 #include "lldb/Expression/IRExecutionUnit.h"
23 #include "lldb/Target/ExecutionContext.h"
24 #include "lldb/Target/Target.h"
25 
26 using namespace lldb_private;
27 
28 IRExecutionUnit::IRExecutionUnit (std::unique_ptr<llvm::LLVMContext> &context_ap,
29                                   std::unique_ptr<llvm::Module> &module_ap,
30                                   ConstString &name,
31                                   const lldb::TargetSP &target_sp,
32                                   std::vector<std::string> &cpu_features) :
33     IRMemoryMap(target_sp),
34     m_context_ap(context_ap.release()),
35     m_module_ap(module_ap.release()),
36     m_module(m_module_ap.get()),
37     m_cpu_features(cpu_features),
38     m_name(name),
39     m_did_jit(false),
40     m_function_load_addr(LLDB_INVALID_ADDRESS),
41     m_function_end_load_addr(LLDB_INVALID_ADDRESS)
42 {
43 }
44 
45 lldb::addr_t
46 IRExecutionUnit::WriteNow (const uint8_t *bytes,
47                            size_t size,
48                            Error &error)
49 {
50     lldb::addr_t allocation_process_addr = Malloc (size,
51                                                    8,
52                                                    lldb::ePermissionsWritable | lldb::ePermissionsReadable,
53                                                    eAllocationPolicyMirror,
54                                                    error);
55 
56     if (!error.Success())
57         return LLDB_INVALID_ADDRESS;
58 
59     WriteMemory(allocation_process_addr, bytes, size, error);
60 
61     if (!error.Success())
62     {
63         Error err;
64         Free (allocation_process_addr, err);
65 
66         return LLDB_INVALID_ADDRESS;
67     }
68 
69     if (Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS))
70     {
71         DataBufferHeap my_buffer(size, 0);
72         Error err;
73         ReadMemory(my_buffer.GetBytes(), allocation_process_addr, size, err);
74 
75         if (err.Success())
76         {
77             DataExtractor my_extractor(my_buffer.GetBytes(), my_buffer.GetByteSize(), lldb::eByteOrderBig, 8);
78             my_extractor.PutToLog(log, 0, my_buffer.GetByteSize(), allocation_process_addr, 16, DataExtractor::TypeUInt8);
79         }
80     }
81 
82     return allocation_process_addr;
83 }
84 
85 void
86 IRExecutionUnit::FreeNow (lldb::addr_t allocation)
87 {
88     if (allocation == LLDB_INVALID_ADDRESS)
89         return;
90 
91     Error err;
92 
93     Free(allocation, err);
94 }
95 
96 Error
97 IRExecutionUnit::DisassembleFunction (Stream &stream,
98                                       lldb::ProcessSP &process_wp)
99 {
100     Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));
101 
102     ExecutionContext exe_ctx(process_wp);
103 
104     Error ret;
105 
106     ret.Clear();
107 
108     lldb::addr_t func_local_addr = LLDB_INVALID_ADDRESS;
109     lldb::addr_t func_remote_addr = LLDB_INVALID_ADDRESS;
110 
111     for (JittedFunction &function : m_jitted_functions)
112     {
113         if (strstr(function.m_name.c_str(), m_name.AsCString()))
114         {
115             func_local_addr = function.m_local_addr;
116             func_remote_addr = function.m_remote_addr;
117         }
118     }
119 
120     if (func_local_addr == LLDB_INVALID_ADDRESS)
121     {
122         ret.SetErrorToGenericError();
123         ret.SetErrorStringWithFormat("Couldn't find function %s for disassembly", m_name.AsCString());
124         return ret;
125     }
126 
127     if (log)
128         log->Printf("Found function, has local address 0x%" PRIx64 " and remote address 0x%" PRIx64, (uint64_t)func_local_addr, (uint64_t)func_remote_addr);
129 
130     std::pair <lldb::addr_t, lldb::addr_t> func_range;
131 
132     func_range = GetRemoteRangeForLocal(func_local_addr);
133 
134     if (func_range.first == 0 && func_range.second == 0)
135     {
136         ret.SetErrorToGenericError();
137         ret.SetErrorStringWithFormat("Couldn't find code range for function %s", m_name.AsCString());
138         return ret;
139     }
140 
141     if (log)
142         log->Printf("Function's code range is [0x%" PRIx64 "+0x%" PRIx64 "]", func_range.first, func_range.second);
143 
144     Target *target = exe_ctx.GetTargetPtr();
145     if (!target)
146     {
147         ret.SetErrorToGenericError();
148         ret.SetErrorString("Couldn't find the target");
149         return ret;
150     }
151 
152     lldb::DataBufferSP buffer_sp(new DataBufferHeap(func_range.second, 0));
153 
154     Process *process = exe_ctx.GetProcessPtr();
155     Error err;
156     process->ReadMemory(func_remote_addr, buffer_sp->GetBytes(), buffer_sp->GetByteSize(), err);
157 
158     if (!err.Success())
159     {
160         ret.SetErrorToGenericError();
161         ret.SetErrorStringWithFormat("Couldn't read from process: %s", err.AsCString("unknown error"));
162         return ret;
163     }
164 
165     ArchSpec arch(target->GetArchitecture());
166 
167     const char *plugin_name = NULL;
168     const char *flavor_string = NULL;
169     lldb::DisassemblerSP disassembler_sp = Disassembler::FindPlugin(arch, flavor_string, plugin_name);
170 
171     if (!disassembler_sp)
172     {
173         ret.SetErrorToGenericError();
174         ret.SetErrorStringWithFormat("Unable to find disassembler plug-in for %s architecture.", arch.GetArchitectureName());
175         return ret;
176     }
177 
178     if (!process)
179     {
180         ret.SetErrorToGenericError();
181         ret.SetErrorString("Couldn't find the process");
182         return ret;
183     }
184 
185     DataExtractor extractor(buffer_sp,
186                             process->GetByteOrder(),
187                             target->GetArchitecture().GetAddressByteSize());
188 
189     if (log)
190     {
191         log->Printf("Function data has contents:");
192         extractor.PutToLog (log,
193                             0,
194                             extractor.GetByteSize(),
195                             func_remote_addr,
196                             16,
197                             DataExtractor::TypeUInt8);
198     }
199 
200     disassembler_sp->DecodeInstructions (Address (func_remote_addr), extractor, 0, UINT32_MAX, false, false);
201 
202     InstructionList &instruction_list = disassembler_sp->GetInstructionList();
203     const uint32_t max_opcode_byte_size = instruction_list.GetMaxOpcocdeByteSize();
204 
205     for (size_t instruction_index = 0, num_instructions = instruction_list.GetSize();
206          instruction_index < num_instructions;
207          ++instruction_index)
208     {
209         Instruction *instruction = instruction_list.GetInstructionAtIndex(instruction_index).get();
210         instruction->Dump (&stream,
211                            max_opcode_byte_size,
212                            true,
213                            true,
214                            &exe_ctx);
215         stream.PutChar('\n');
216     }
217     // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions.
218     // I'll fix that but for now, just clear the list and it will go away nicely.
219     disassembler_sp->GetInstructionList().Clear();
220     return ret;
221 }
222 
223 static void ReportInlineAsmError(const llvm::SMDiagnostic &diagnostic, void *Context, unsigned LocCookie)
224 {
225     Error *err = static_cast<Error*>(Context);
226 
227     if (err && err->Success())
228     {
229         err->SetErrorToGenericError();
230         err->SetErrorStringWithFormat("Inline assembly error: %s", diagnostic.getMessage().str().c_str());
231     }
232 }
233 
234 void
235 IRExecutionUnit::GetRunnableInfo(Error &error,
236                                  lldb::addr_t &func_addr,
237                                  lldb::addr_t &func_end)
238 {
239     lldb::ProcessSP process_sp(GetProcessWP().lock());
240 
241     func_addr = LLDB_INVALID_ADDRESS;
242     func_end = LLDB_INVALID_ADDRESS;
243 
244     if (!process_sp)
245     {
246         error.SetErrorToGenericError();
247         error.SetErrorString("Couldn't write the JIT compiled code into the process because the process is invalid");
248         return;
249     }
250 
251     if (m_did_jit)
252     {
253         func_addr = m_function_load_addr;
254         func_end = m_function_end_load_addr;
255 
256         return;
257     };
258 
259     m_did_jit = true;
260 
261     Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));
262 
263     std::string error_string;
264 
265     if (log)
266     {
267         std::string s;
268         llvm::raw_string_ostream oss(s);
269 
270         m_module->print(oss, NULL);
271 
272         oss.flush();
273 
274         log->Printf ("Module being sent to JIT: \n%s", s.c_str());
275     }
276 
277     llvm::Triple triple(m_module->getTargetTriple());
278     llvm::Function *function = m_module->getFunction (m_name.AsCString());
279     llvm::Reloc::Model relocModel;
280     llvm::CodeModel::Model codeModel;
281 
282     if (triple.isOSBinFormatELF())
283     {
284         relocModel = llvm::Reloc::Static;
285         // This will be small for 32-bit and large for 64-bit.
286         codeModel = llvm::CodeModel::JITDefault;
287     }
288     else
289     {
290         relocModel = llvm::Reloc::PIC_;
291         codeModel = llvm::CodeModel::Small;
292     }
293 
294     m_module_ap->getContext().setInlineAsmDiagnosticHandler(ReportInlineAsmError, &error);
295 
296     llvm::EngineBuilder builder(m_module_ap.get());
297 
298     builder.setEngineKind(llvm::EngineKind::JIT)
299     .setErrorStr(&error_string)
300     .setRelocationModel(relocModel)
301     .setJITMemoryManager(new MemoryManager(*this))
302     .setOptLevel(llvm::CodeGenOpt::Less)
303     .setAllocateGVsWithCode(true)
304     .setCodeModel(codeModel)
305     .setUseMCJIT(true);
306 
307     llvm::StringRef mArch;
308     llvm::StringRef mCPU;
309     llvm::SmallVector<std::string, 0> mAttrs;
310 
311     for (std::string &feature : m_cpu_features)
312         mAttrs.push_back(feature);
313 
314     llvm::TargetMachine *target_machine = builder.selectTarget(triple,
315                                                                mArch,
316                                                                mCPU,
317                                                                mAttrs);
318 
319     m_execution_engine_ap.reset(builder.create(target_machine));
320 
321     if (!m_execution_engine_ap.get())
322     {
323         error.SetErrorToGenericError();
324         error.SetErrorStringWithFormat("Couldn't JIT the function: %s", error_string.c_str());
325         return;
326     }
327     else
328     {
329         m_module_ap.release(); // ownership was transferred
330     }
331 
332     m_execution_engine_ap->DisableLazyCompilation();
333 
334     // We don't actually need the function pointer here, this just forces it to get resolved.
335 
336     void *fun_ptr = m_execution_engine_ap->getPointerToFunction(function);
337 
338     if (!error.Success())
339     {
340         // We got an error through our callback!
341         return;
342     }
343 
344     if (!function)
345     {
346         error.SetErrorToGenericError();
347         error.SetErrorStringWithFormat("Couldn't find '%s' in the JITted module", m_name.AsCString());
348         return;
349     }
350 
351     if (!fun_ptr)
352     {
353         error.SetErrorToGenericError();
354         error.SetErrorStringWithFormat("'%s' was in the JITted module but wasn't lowered", m_name.AsCString());
355         return;
356     }
357 
358     m_jitted_functions.push_back (JittedFunction(m_name.AsCString(), (lldb::addr_t)fun_ptr));
359 
360     CommitAllocations(process_sp);
361     ReportAllocations(*m_execution_engine_ap);
362     WriteData(process_sp);
363 
364     for (JittedFunction &jitted_function : m_jitted_functions)
365     {
366         jitted_function.m_remote_addr = GetRemoteAddressForLocal (jitted_function.m_local_addr);
367 
368         if (!jitted_function.m_name.compare(m_name.AsCString()))
369         {
370             AddrRange func_range = GetRemoteRangeForLocal(jitted_function.m_local_addr);
371             m_function_end_load_addr = func_range.first + func_range.second;
372             m_function_load_addr = jitted_function.m_remote_addr;
373         }
374     }
375 
376     if (log)
377     {
378         log->Printf("Code can be run in the target.");
379 
380         StreamString disassembly_stream;
381 
382         Error err = DisassembleFunction(disassembly_stream, process_sp);
383 
384         if (!err.Success())
385         {
386             log->Printf("Couldn't disassemble function : %s", err.AsCString("unknown error"));
387         }
388         else
389         {
390             log->Printf("Function disassembly:\n%s", disassembly_stream.GetData());
391         }
392 
393         log->Printf("Sections: ");
394         for (AllocationRecord &record : m_records)
395         {
396             if (record.m_process_address != LLDB_INVALID_ADDRESS)
397             {
398                 record.dump(log);
399 
400                 DataBufferHeap my_buffer(record.m_size, 0);
401                 Error err;
402                 ReadMemory(my_buffer.GetBytes(), record.m_process_address, record.m_size, err);
403 
404                 if (err.Success())
405                 {
406                     DataExtractor my_extractor(my_buffer.GetBytes(), my_buffer.GetByteSize(), lldb::eByteOrderBig, 8);
407                     my_extractor.PutToLog(log, 0, my_buffer.GetByteSize(), record.m_process_address, 16, DataExtractor::TypeUInt8);
408                 }
409             }
410         }
411     }
412 
413     func_addr = m_function_load_addr;
414     func_end = m_function_end_load_addr;
415 
416     return;
417 }
418 
419 IRExecutionUnit::~IRExecutionUnit ()
420 {
421     m_module_ap.reset();
422     m_execution_engine_ap.reset();
423     m_context_ap.reset();
424 }
425 
426 IRExecutionUnit::MemoryManager::MemoryManager (IRExecutionUnit &parent) :
427     m_default_mm_ap (llvm::JITMemoryManager::CreateDefaultMemManager()),
428     m_parent (parent)
429 {
430 }
431 
432 void
433 IRExecutionUnit::MemoryManager::setMemoryWritable ()
434 {
435     m_default_mm_ap->setMemoryWritable();
436 }
437 
438 void
439 IRExecutionUnit::MemoryManager::setMemoryExecutable ()
440 {
441     m_default_mm_ap->setMemoryExecutable();
442 }
443 
444 
445 uint8_t *
446 IRExecutionUnit::MemoryManager::startFunctionBody(const llvm::Function *F,
447                                                   uintptr_t &ActualSize)
448 {
449     return m_default_mm_ap->startFunctionBody(F, ActualSize);
450 }
451 
452 uint8_t *
453 IRExecutionUnit::MemoryManager::allocateStub(const llvm::GlobalValue* F,
454                                              unsigned StubSize,
455                                              unsigned Alignment)
456 {
457     Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));
458 
459     uint8_t *return_value = m_default_mm_ap->allocateStub(F, StubSize, Alignment);
460 
461     m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value,
462                                                   lldb::ePermissionsReadable | lldb::ePermissionsWritable,
463                                                   StubSize,
464                                                   Alignment));
465 
466     if (log)
467     {
468         log->Printf("IRExecutionUnit::allocateStub (F=%p, StubSize=%u, Alignment=%u) = %p",
469                     F, StubSize, Alignment, return_value);
470     }
471 
472     return return_value;
473 }
474 
475 void
476 IRExecutionUnit::MemoryManager::endFunctionBody(const llvm::Function *F,
477                                                 uint8_t *FunctionStart,
478                                                 uint8_t *FunctionEnd)
479 {
480     m_default_mm_ap->endFunctionBody(F, FunctionStart, FunctionEnd);
481 }
482 
483 uint8_t *
484 IRExecutionUnit::MemoryManager::allocateSpace(intptr_t Size, unsigned Alignment)
485 {
486     Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));
487 
488     uint8_t *return_value = m_default_mm_ap->allocateSpace(Size, Alignment);
489 
490     m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value,
491                                                   lldb::ePermissionsReadable | lldb::ePermissionsWritable,
492                                                   Size,
493                                                   Alignment));
494 
495     if (log)
496     {
497         log->Printf("IRExecutionUnit::allocateSpace(Size=%" PRIu64 ", Alignment=%u) = %p",
498                                (uint64_t)Size, Alignment, return_value);
499     }
500 
501     return return_value;
502 }
503 
504 uint8_t *
505 IRExecutionUnit::MemoryManager::allocateCodeSection(uintptr_t Size,
506                                                     unsigned Alignment,
507                                                     unsigned SectionID,
508                                                     llvm::StringRef SectionName)
509 {
510     Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));
511 
512     uint8_t *return_value = m_default_mm_ap->allocateCodeSection(Size, Alignment, SectionID, SectionName);
513 
514     m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value,
515                                                   lldb::ePermissionsReadable | lldb::ePermissionsExecutable,
516                                                   Size,
517                                                   Alignment,
518                                                   SectionID));
519 
520     if (log)
521     {
522         log->Printf("IRExecutionUnit::allocateCodeSection(Size=0x%" PRIx64 ", Alignment=%u, SectionID=%u) = %p",
523                     (uint64_t)Size, Alignment, SectionID, return_value);
524     }
525 
526     return return_value;
527 }
528 
529 uint8_t *
530 IRExecutionUnit::MemoryManager::allocateDataSection(uintptr_t Size,
531                                                     unsigned Alignment,
532                                                     unsigned SectionID,
533                                                     llvm::StringRef SectionName,
534                                                     bool IsReadOnly)
535 {
536     Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));
537 
538     uint8_t *return_value = m_default_mm_ap->allocateDataSection(Size, Alignment, SectionID, SectionName, IsReadOnly);
539 
540     m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value,
541                                                   lldb::ePermissionsReadable | lldb::ePermissionsWritable,
542                                                   Size,
543                                                   Alignment,
544                                                   SectionID));
545     if (log)
546     {
547         log->Printf("IRExecutionUnit::allocateDataSection(Size=0x%" PRIx64 ", Alignment=%u, SectionID=%u) = %p",
548                     (uint64_t)Size, Alignment, SectionID, return_value);
549     }
550 
551     return return_value;
552 }
553 
554 uint8_t *
555 IRExecutionUnit::MemoryManager::allocateGlobal(uintptr_t Size,
556                                                unsigned Alignment)
557 {
558     Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));
559 
560     uint8_t *return_value = m_default_mm_ap->allocateGlobal(Size, Alignment);
561 
562     m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value,
563                                                   lldb::ePermissionsReadable | lldb::ePermissionsWritable,
564                                                   Size,
565                                                   Alignment));
566 
567     if (log)
568     {
569         log->Printf("IRExecutionUnit::allocateGlobal(Size=0x%" PRIx64 ", Alignment=%u) = %p",
570                     (uint64_t)Size, Alignment, return_value);
571     }
572 
573     return return_value;
574 }
575 
576 void
577 IRExecutionUnit::MemoryManager::deallocateFunctionBody(void *Body)
578 {
579     m_default_mm_ap->deallocateFunctionBody(Body);
580 }
581 
582 lldb::addr_t
583 IRExecutionUnit::GetRemoteAddressForLocal (lldb::addr_t local_address)
584 {
585     Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));
586 
587     for (AllocationRecord &record : m_records)
588     {
589         if (local_address >= record.m_host_address &&
590             local_address < record.m_host_address + record.m_size)
591         {
592             if (record.m_process_address == LLDB_INVALID_ADDRESS)
593                 return LLDB_INVALID_ADDRESS;
594 
595             lldb::addr_t ret = record.m_process_address + (local_address - record.m_host_address);
596 
597             if (log)
598             {
599                 log->Printf("IRExecutionUnit::GetRemoteAddressForLocal() found 0x%" PRIx64 " in [0x%" PRIx64 "..0x%" PRIx64 "], and returned 0x%" PRIx64 " from [0x%" PRIx64 "..0x%" PRIx64 "].",
600                             local_address,
601                             (uint64_t)record.m_host_address,
602                             (uint64_t)record.m_host_address + (uint64_t)record.m_size,
603                             ret,
604                             record.m_process_address,
605                             record.m_process_address + record.m_size);
606             }
607 
608             return ret;
609         }
610     }
611 
612     return LLDB_INVALID_ADDRESS;
613 }
614 
615 IRExecutionUnit::AddrRange
616 IRExecutionUnit::GetRemoteRangeForLocal (lldb::addr_t local_address)
617 {
618     for (AllocationRecord &record : m_records)
619     {
620         if (local_address >= record.m_host_address &&
621             local_address < record.m_host_address + record.m_size)
622         {
623             if (record.m_process_address == LLDB_INVALID_ADDRESS)
624                 return AddrRange(0, 0);
625 
626             return AddrRange(record.m_process_address, record.m_size);
627         }
628     }
629 
630     return AddrRange (0, 0);
631 }
632 
633 bool
634 IRExecutionUnit::CommitAllocations (lldb::ProcessSP &process_sp)
635 {
636     bool ret = true;
637 
638     lldb_private::Error err;
639 
640     for (AllocationRecord &record : m_records)
641     {
642         if (record.m_process_address != LLDB_INVALID_ADDRESS)
643             continue;
644 
645 
646         record.m_process_address = Malloc(record.m_size,
647                                           record.m_alignment,
648                                           record.m_permissions,
649                                           eAllocationPolicyProcessOnly,
650                                           err);
651 
652         if (!err.Success())
653         {
654             ret = false;
655             break;
656         }
657     }
658 
659     if (!ret)
660     {
661         for (AllocationRecord &record : m_records)
662         {
663             if (record.m_process_address != LLDB_INVALID_ADDRESS)
664             {
665                 Free(record.m_process_address, err);
666                 record.m_process_address = LLDB_INVALID_ADDRESS;
667             }
668         }
669     }
670 
671     return ret;
672 }
673 
674 void
675 IRExecutionUnit::ReportAllocations (llvm::ExecutionEngine &engine)
676 {
677     for (AllocationRecord &record : m_records)
678     {
679         if (record.m_process_address == LLDB_INVALID_ADDRESS)
680             continue;
681 
682         if (record.m_section_id == eSectionIDInvalid)
683             continue;
684 
685         engine.mapSectionAddress((void*)record.m_host_address, record.m_process_address);
686     }
687 
688     // Trigger re-application of relocations.
689     engine.finalizeObject();
690 }
691 
692 bool
693 IRExecutionUnit::WriteData (lldb::ProcessSP &process_sp)
694 {
695     for (AllocationRecord &record : m_records)
696     {
697         if (record.m_process_address == LLDB_INVALID_ADDRESS)
698             return false;
699 
700         lldb_private::Error err;
701 
702         WriteMemory (record.m_process_address, (uint8_t*)record.m_host_address, record.m_size, err);
703     }
704 
705     return true;
706 }
707 
708 void
709 IRExecutionUnit::AllocationRecord::dump (Log *log)
710 {
711     if (!log)
712         return;
713 
714     log->Printf("[0x%llx+0x%llx]->0x%llx (alignment %d, section ID %d)",
715                 (unsigned long long)m_host_address,
716                 (unsigned long long)m_size,
717                 (unsigned long long)m_process_address,
718                 (unsigned)m_alignment,
719                 (unsigned)m_section_id);
720 }
721