| //===-- ProcessMachCore.cpp ------------------------------------------*- C++ |
| //-*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| // C Includes |
| #include <errno.h> |
| #include <stdlib.h> |
| |
| // C++ Includes |
| #include "llvm/Support/MathExtras.h" |
| #include "llvm/Support/Threading.h" |
| #include <mutex> |
| |
| // Other libraries and framework includes |
| #include "lldb/Core/Debugger.h" |
| #include "lldb/Core/Module.h" |
| #include "lldb/Core/ModuleSpec.h" |
| #include "lldb/Core/PluginManager.h" |
| #include "lldb/Core/Section.h" |
| #include "lldb/Core/State.h" |
| #include "lldb/Host/Host.h" |
| #include "lldb/Symbol/ObjectFile.h" |
| #include "lldb/Target/MemoryRegionInfo.h" |
| #include "lldb/Target/Target.h" |
| #include "lldb/Target/Thread.h" |
| #include "lldb/Utility/DataBuffer.h" |
| #include "lldb/Utility/DataBufferLLVM.h" |
| #include "lldb/Utility/Log.h" |
| |
| // Project includes |
| #include "ProcessMachCore.h" |
| #include "Plugins/Process/Utility/StopInfoMachException.h" |
| #include "ThreadMachCore.h" |
| |
| // Needed for the plug-in names for the dynamic loaders. |
| #include "lldb/Utility/SafeMachO.h" |
| |
| #include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h" |
| #include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h" |
| #include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| ConstString ProcessMachCore::GetPluginNameStatic() { |
| static ConstString g_name("mach-o-core"); |
| return g_name; |
| } |
| |
| const char *ProcessMachCore::GetPluginDescriptionStatic() { |
| return "Mach-O core file debugging plug-in."; |
| } |
| |
| void ProcessMachCore::Terminate() { |
| PluginManager::UnregisterPlugin(ProcessMachCore::CreateInstance); |
| } |
| |
| lldb::ProcessSP ProcessMachCore::CreateInstance(lldb::TargetSP target_sp, |
| ListenerSP listener_sp, |
| const FileSpec *crash_file) { |
| lldb::ProcessSP process_sp; |
| if (crash_file) { |
| const size_t header_size = sizeof(llvm::MachO::mach_header); |
| auto data_sp = |
| DataBufferLLVM::CreateSliceFromPath(crash_file->GetPath(), header_size, 0); |
| if (data_sp && data_sp->GetByteSize() == header_size) { |
| DataExtractor data(data_sp, lldb::eByteOrderLittle, 4); |
| |
| lldb::offset_t data_offset = 0; |
| llvm::MachO::mach_header mach_header; |
| if (ObjectFileMachO::ParseHeader(data, &data_offset, mach_header)) { |
| if (mach_header.filetype == llvm::MachO::MH_CORE) |
| process_sp.reset( |
| new ProcessMachCore(target_sp, listener_sp, *crash_file)); |
| } |
| } |
| } |
| return process_sp; |
| } |
| |
| bool ProcessMachCore::CanDebug(lldb::TargetSP target_sp, |
| bool plugin_specified_by_name) { |
| if (plugin_specified_by_name) |
| return true; |
| |
| // For now we are just making sure the file exists for a given module |
| if (!m_core_module_sp && m_core_file.Exists()) { |
| // Don't add the Target's architecture to the ModuleSpec - we may be |
| // working with a core file that doesn't have the correct cpusubtype in the |
| // header but we should still try to use it - |
| // ModuleSpecList::FindMatchingModuleSpec enforces a strict arch mach. |
| ModuleSpec core_module_spec(m_core_file); |
| Status error(ModuleList::GetSharedModule(core_module_spec, m_core_module_sp, |
| NULL, NULL, NULL)); |
| |
| if (m_core_module_sp) { |
| ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); |
| if (core_objfile && core_objfile->GetType() == ObjectFile::eTypeCoreFile) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| //---------------------------------------------------------------------- |
| // ProcessMachCore constructor |
| //---------------------------------------------------------------------- |
| ProcessMachCore::ProcessMachCore(lldb::TargetSP target_sp, |
| ListenerSP listener_sp, |
| const FileSpec &core_file) |
| : Process(target_sp, listener_sp), m_core_aranges(), m_core_range_infos(), |
| m_core_module_sp(), m_core_file(core_file), |
| m_dyld_addr(LLDB_INVALID_ADDRESS), |
| m_mach_kernel_addr(LLDB_INVALID_ADDRESS), m_dyld_plugin_name() {} |
| |
| //---------------------------------------------------------------------- |
| // Destructor |
| //---------------------------------------------------------------------- |
| ProcessMachCore::~ProcessMachCore() { |
| Clear(); |
| // We need to call finalize on the process before destroying ourselves to |
| // make sure all of the broadcaster cleanup goes as planned. If we destruct |
| // this class, then Process::~Process() might have problems trying to fully |
| // destroy the broadcaster. |
| Finalize(); |
| } |
| |
| //---------------------------------------------------------------------- |
| // PluginInterface |
| //---------------------------------------------------------------------- |
| ConstString ProcessMachCore::GetPluginName() { return GetPluginNameStatic(); } |
| |
| uint32_t ProcessMachCore::GetPluginVersion() { return 1; } |
| |
| bool ProcessMachCore::GetDynamicLoaderAddress(lldb::addr_t addr) { |
| Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER | |
| LIBLLDB_LOG_PROCESS)); |
| llvm::MachO::mach_header header; |
| Status error; |
| if (DoReadMemory(addr, &header, sizeof(header), error) != sizeof(header)) |
| return false; |
| if (header.magic == llvm::MachO::MH_CIGAM || |
| header.magic == llvm::MachO::MH_CIGAM_64) { |
| header.magic = llvm::ByteSwap_32(header.magic); |
| header.cputype = llvm::ByteSwap_32(header.cputype); |
| header.cpusubtype = llvm::ByteSwap_32(header.cpusubtype); |
| header.filetype = llvm::ByteSwap_32(header.filetype); |
| header.ncmds = llvm::ByteSwap_32(header.ncmds); |
| header.sizeofcmds = llvm::ByteSwap_32(header.sizeofcmds); |
| header.flags = llvm::ByteSwap_32(header.flags); |
| } |
| |
| // TODO: swap header if needed... |
| // printf("0x%16.16" PRIx64 ": magic = 0x%8.8x, file_type= %u\n", vaddr, |
| // header.magic, header.filetype); |
| if (header.magic == llvm::MachO::MH_MAGIC || |
| header.magic == llvm::MachO::MH_MAGIC_64) { |
| // Check MH_EXECUTABLE to see if we can find the mach image that contains |
| // the shared library list. The dynamic loader (dyld) is what contains the |
| // list for user applications, and the mach kernel contains a global that |
| // has the list of kexts to load |
| switch (header.filetype) { |
| case llvm::MachO::MH_DYLINKER: |
| // printf("0x%16.16" PRIx64 ": file_type = MH_DYLINKER\n", vaddr); |
| // Address of dyld "struct mach_header" in the core file |
| if (log) |
| log->Printf("ProcessMachCore::GetDynamicLoaderAddress found a user " |
| "process dyld binary image at 0x%" PRIx64, |
| addr); |
| m_dyld_addr = addr; |
| return true; |
| |
| case llvm::MachO::MH_EXECUTE: |
| // printf("0x%16.16" PRIx64 ": file_type = MH_EXECUTE\n", vaddr); |
| // Check MH_EXECUTABLE file types to see if the dynamic link object flag |
| // is NOT set. If it isn't, then we have a mach_kernel. |
| if ((header.flags & llvm::MachO::MH_DYLDLINK) == 0) { |
| if (log) |
| log->Printf("ProcessMachCore::GetDynamicLoaderAddress found a mach " |
| "kernel binary image at 0x%" PRIx64, |
| addr); |
| // Address of the mach kernel "struct mach_header" in the core file. |
| m_mach_kernel_addr = addr; |
| return true; |
| } |
| break; |
| } |
| } |
| return false; |
| } |
| |
| //---------------------------------------------------------------------- |
| // Process Control |
| //---------------------------------------------------------------------- |
| Status ProcessMachCore::DoLoadCore() { |
| Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER | |
| LIBLLDB_LOG_PROCESS)); |
| Status error; |
| if (!m_core_module_sp) { |
| error.SetErrorString("invalid core module"); |
| return error; |
| } |
| |
| ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); |
| if (core_objfile == NULL) { |
| error.SetErrorString("invalid core object file"); |
| return error; |
| } |
| |
| if (core_objfile->GetNumThreadContexts() == 0) { |
| error.SetErrorString("core file doesn't contain any LC_THREAD load " |
| "commands, or the LC_THREAD architecture is not " |
| "supported in this lldb"); |
| return error; |
| } |
| |
| SectionList *section_list = core_objfile->GetSectionList(); |
| if (section_list == NULL) { |
| error.SetErrorString("core file has no sections"); |
| return error; |
| } |
| |
| const uint32_t num_sections = section_list->GetNumSections(0); |
| if (num_sections == 0) { |
| error.SetErrorString("core file has no sections"); |
| return error; |
| } |
| |
| SetCanJIT(false); |
| |
| llvm::MachO::mach_header header; |
| DataExtractor data(&header, sizeof(header), |
| m_core_module_sp->GetArchitecture().GetByteOrder(), |
| m_core_module_sp->GetArchitecture().GetAddressByteSize()); |
| |
| bool ranges_are_sorted = true; |
| addr_t vm_addr = 0; |
| for (uint32_t i = 0; i < num_sections; ++i) { |
| Section *section = section_list->GetSectionAtIndex(i).get(); |
| if (section) { |
| lldb::addr_t section_vm_addr = section->GetFileAddress(); |
| FileRange file_range(section->GetFileOffset(), section->GetFileSize()); |
| VMRangeToFileOffset::Entry range_entry( |
| section_vm_addr, section->GetByteSize(), file_range); |
| |
| if (vm_addr > section_vm_addr) |
| ranges_are_sorted = false; |
| vm_addr = section->GetFileAddress(); |
| VMRangeToFileOffset::Entry *last_entry = m_core_aranges.Back(); |
| // printf ("LC_SEGMENT[%u] arange=[0x%16.16" PRIx64 " - |
| // 0x%16.16" PRIx64 "), frange=[0x%8.8x - 0x%8.8x)\n", |
| // i, |
| // range_entry.GetRangeBase(), |
| // range_entry.GetRangeEnd(), |
| // range_entry.data.GetRangeBase(), |
| // range_entry.data.GetRangeEnd()); |
| |
| if (last_entry && |
| last_entry->GetRangeEnd() == range_entry.GetRangeBase() && |
| last_entry->data.GetRangeEnd() == range_entry.data.GetRangeBase()) { |
| last_entry->SetRangeEnd(range_entry.GetRangeEnd()); |
| last_entry->data.SetRangeEnd(range_entry.data.GetRangeEnd()); |
| // puts("combine"); |
| } else { |
| m_core_aranges.Append(range_entry); |
| } |
| // Some core files don't fill in the permissions correctly. If that is |
| // the case assume read + execute so clients don't think the memory is |
| // not readable, or executable. The memory isn't writable since this |
| // plug-in doesn't implement DoWriteMemory. |
| uint32_t permissions = section->GetPermissions(); |
| if (permissions == 0) |
| permissions = lldb::ePermissionsReadable | lldb::ePermissionsExecutable; |
| m_core_range_infos.Append(VMRangeToPermissions::Entry( |
| section_vm_addr, section->GetByteSize(), permissions)); |
| } |
| } |
| if (!ranges_are_sorted) { |
| m_core_aranges.Sort(); |
| m_core_range_infos.Sort(); |
| } |
| |
| |
| bool found_main_binary_definitively = false; |
| |
| addr_t objfile_binary_addr; |
| UUID objfile_binary_uuid; |
| if (core_objfile->GetCorefileMainBinaryInfo (objfile_binary_addr, objfile_binary_uuid)) |
| { |
| if (objfile_binary_addr != LLDB_INVALID_ADDRESS) |
| { |
| m_mach_kernel_addr = objfile_binary_addr; |
| found_main_binary_definitively = true; |
| if (log) |
| log->Printf ("ProcessMachCore::DoLoadCore: using kernel address 0x%" PRIx64 |
| " from LC_NOTE 'main bin spec' load command.", m_mach_kernel_addr); |
| } |
| } |
| |
| // This checks for the presence of an LC_IDENT string in a core file; |
| // LC_IDENT is very obsolete and should not be used in new code, but if the |
| // load command is present, let's use the contents. |
| std::string corefile_identifier = core_objfile->GetIdentifierString(); |
| if (found_main_binary_definitively == false |
| && corefile_identifier.find("Darwin Kernel") != std::string::npos) { |
| UUID uuid; |
| addr_t addr = LLDB_INVALID_ADDRESS; |
| if (corefile_identifier.find("UUID=") != std::string::npos) { |
| size_t p = corefile_identifier.find("UUID=") + strlen("UUID="); |
| std::string uuid_str = corefile_identifier.substr(p, 36); |
| uuid.SetFromStringRef(uuid_str); |
| } |
| if (corefile_identifier.find("stext=") != std::string::npos) { |
| size_t p = corefile_identifier.find("stext=") + strlen("stext="); |
| if (corefile_identifier[p] == '0' && corefile_identifier[p + 1] == 'x') { |
| errno = 0; |
| addr = ::strtoul(corefile_identifier.c_str() + p, NULL, 16); |
| if (errno != 0 || addr == 0) |
| addr = LLDB_INVALID_ADDRESS; |
| } |
| } |
| if (uuid.IsValid() && addr != LLDB_INVALID_ADDRESS) { |
| m_mach_kernel_addr = addr; |
| found_main_binary_definitively = true; |
| if (log) |
| log->Printf("ProcessMachCore::DoLoadCore: Using the kernel address 0x%" PRIx64 |
| " from LC_IDENT/LC_NOTE 'kern ver str' string: '%s'", addr, corefile_identifier.c_str()); |
| } |
| } |
| |
| if (found_main_binary_definitively == false |
| && (m_dyld_addr == LLDB_INVALID_ADDRESS |
| || m_mach_kernel_addr == LLDB_INVALID_ADDRESS)) { |
| // We need to locate the main executable in the memory ranges we have in |
| // the core file. We need to search for both a user-process dyld binary |
| // and a kernel binary in memory; we must look at all the pages in the |
| // binary so we don't miss one or the other. Step through all memory |
| // segments searching for a kernel binary and for a user process dyld -- |
| // we'll decide which to prefer later if both are present. |
| |
| const size_t num_core_aranges = m_core_aranges.GetSize(); |
| for (size_t i = 0; i < num_core_aranges; ++i) { |
| const VMRangeToFileOffset::Entry *entry = |
| m_core_aranges.GetEntryAtIndex(i); |
| lldb::addr_t section_vm_addr_start = entry->GetRangeBase(); |
| lldb::addr_t section_vm_addr_end = entry->GetRangeEnd(); |
| for (lldb::addr_t section_vm_addr = section_vm_addr_start; |
| section_vm_addr < section_vm_addr_end; section_vm_addr += 0x1000) { |
| GetDynamicLoaderAddress(section_vm_addr); |
| } |
| } |
| } |
| |
| if (found_main_binary_definitively == false |
| && m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { |
| // In the case of multiple kernel images found in the core file via |
| // exhaustive search, we may not pick the correct one. See if the |
| // DynamicLoaderDarwinKernel's search heuristics might identify the correct |
| // one. Most of the time, I expect the address from SearchForDarwinKernel() |
| // will be the same as the address we found via exhaustive search. |
| |
| if (GetTarget().GetArchitecture().IsValid() == false && |
| m_core_module_sp.get()) { |
| GetTarget().SetArchitecture(m_core_module_sp->GetArchitecture()); |
| } |
| |
| // SearchForDarwinKernel will end up calling back into this this class in |
| // the GetImageInfoAddress method which will give it the |
| // m_mach_kernel_addr/m_dyld_addr it already has. Save that aside and set |
| // m_mach_kernel_addr/m_dyld_addr to an invalid address temporarily so |
| // DynamicLoaderDarwinKernel does a real search for the kernel using its |
| // own heuristics. |
| |
| addr_t saved_mach_kernel_addr = m_mach_kernel_addr; |
| addr_t saved_user_dyld_addr = m_dyld_addr; |
| m_mach_kernel_addr = LLDB_INVALID_ADDRESS; |
| m_dyld_addr = LLDB_INVALID_ADDRESS; |
| |
| addr_t better_kernel_address = |
| DynamicLoaderDarwinKernel::SearchForDarwinKernel(this); |
| |
| m_mach_kernel_addr = saved_mach_kernel_addr; |
| m_dyld_addr = saved_user_dyld_addr; |
| |
| if (better_kernel_address != LLDB_INVALID_ADDRESS) { |
| if (log) |
| log->Printf("ProcessMachCore::DoLoadCore: Using the kernel address " |
| "from DynamicLoaderDarwinKernel"); |
| m_mach_kernel_addr = better_kernel_address; |
| } |
| } |
| |
| // If we found both a user-process dyld and a kernel binary, we need to |
| // decide which to prefer. |
| if (GetCorefilePreference() == eKernelCorefile) { |
| if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { |
| if (log) |
| log->Printf("ProcessMachCore::DoLoadCore: Using kernel corefile image " |
| "at 0x%" PRIx64, |
| m_mach_kernel_addr); |
| m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); |
| } else if (m_dyld_addr != LLDB_INVALID_ADDRESS) { |
| if (log) |
| log->Printf("ProcessMachCore::DoLoadCore: Using user process dyld " |
| "image at 0x%" PRIx64, |
| m_dyld_addr); |
| m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); |
| } |
| } else { |
| if (m_dyld_addr != LLDB_INVALID_ADDRESS) { |
| if (log) |
| log->Printf("ProcessMachCore::DoLoadCore: Using user process dyld " |
| "image at 0x%" PRIx64, |
| m_dyld_addr); |
| m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); |
| } else if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { |
| if (log) |
| log->Printf("ProcessMachCore::DoLoadCore: Using kernel corefile image " |
| "at 0x%" PRIx64, |
| m_mach_kernel_addr); |
| m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); |
| } |
| } |
| |
| if (m_dyld_plugin_name != DynamicLoaderMacOSXDYLD::GetPluginNameStatic()) { |
| // For non-user process core files, the permissions on the core file |
| // segments are usually meaningless, they may be just "read", because we're |
| // dealing with kernel coredumps or early startup coredumps and the dumper |
| // is grabbing pages of memory without knowing what they are. If they |
| // aren't marked as "exeuctable", that can break the unwinder which will |
| // check a pc value to see if it is in an executable segment and stop the |
| // backtrace early if it is not ("executable" and "unknown" would both be |
| // fine, but "not executable" will break the unwinder). |
| size_t core_range_infos_size = m_core_range_infos.GetSize(); |
| for (size_t i = 0; i < core_range_infos_size; i++) { |
| VMRangeToPermissions::Entry *ent = |
| m_core_range_infos.GetMutableEntryAtIndex(i); |
| ent->data = lldb::ePermissionsReadable | lldb::ePermissionsExecutable; |
| } |
| } |
| |
| // Even if the architecture is set in the target, we need to override it to |
| // match the core file which is always single arch. |
| ArchSpec arch(m_core_module_sp->GetArchitecture()); |
| if (arch.GetCore() == ArchSpec::eCore_x86_32_i486) { |
| arch = Platform::GetAugmentedArchSpec(GetTarget().GetPlatform().get(), "i386"); |
| } |
| if (arch.IsValid()) |
| GetTarget().SetArchitecture(arch); |
| |
| return error; |
| } |
| |
| lldb_private::DynamicLoader *ProcessMachCore::GetDynamicLoader() { |
| if (m_dyld_ap.get() == NULL) |
| m_dyld_ap.reset(DynamicLoader::FindPlugin( |
| this, |
| m_dyld_plugin_name.IsEmpty() ? NULL : m_dyld_plugin_name.GetCString())); |
| return m_dyld_ap.get(); |
| } |
| |
| bool ProcessMachCore::UpdateThreadList(ThreadList &old_thread_list, |
| ThreadList &new_thread_list) { |
| if (old_thread_list.GetSize(false) == 0) { |
| // Make up the thread the first time this is called so we can setup our one |
| // and only core thread state. |
| ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); |
| |
| if (core_objfile) { |
| const uint32_t num_threads = core_objfile->GetNumThreadContexts(); |
| for (lldb::tid_t tid = 0; tid < num_threads; ++tid) { |
| ThreadSP thread_sp(new ThreadMachCore(*this, tid)); |
| new_thread_list.AddThread(thread_sp); |
| } |
| } |
| } else { |
| const uint32_t num_threads = old_thread_list.GetSize(false); |
| for (uint32_t i = 0; i < num_threads; ++i) |
| new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false)); |
| } |
| return new_thread_list.GetSize(false) > 0; |
| } |
| |
| void ProcessMachCore::RefreshStateAfterStop() { |
| // Let all threads recover from stopping and do any clean up based on the |
| // previous thread state (if any). |
| m_thread_list.RefreshStateAfterStop(); |
| // SetThreadStopInfo (m_last_stop_packet); |
| } |
| |
| Status ProcessMachCore::DoDestroy() { return Status(); } |
| |
| //------------------------------------------------------------------ |
| // Process Queries |
| //------------------------------------------------------------------ |
| |
| bool ProcessMachCore::IsAlive() { return true; } |
| |
| bool ProcessMachCore::WarnBeforeDetach() const { return false; } |
| |
| //------------------------------------------------------------------ |
| // Process Memory |
| //------------------------------------------------------------------ |
| size_t ProcessMachCore::ReadMemory(addr_t addr, void *buf, size_t size, |
| Status &error) { |
| // Don't allow the caching that lldb_private::Process::ReadMemory does since |
| // in core files we have it all cached our our core file anyway. |
| return DoReadMemory(addr, buf, size, error); |
| } |
| |
| size_t ProcessMachCore::DoReadMemory(addr_t addr, void *buf, size_t size, |
| Status &error) { |
| ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); |
| size_t bytes_read = 0; |
| |
| if (core_objfile) { |
| //---------------------------------------------------------------------- |
| // Segments are not always contiguous in mach-o core files. We have core |
| // files that have segments like: |
| // Address Size File off File size |
| // ---------- ---------- ---------- ---------- |
| // LC_SEGMENT 0x000f6000 0x00001000 0x1d509ee8 0x00001000 --- --- 0 |
| // 0x00000000 __TEXT LC_SEGMENT 0x0f600000 0x00100000 0x1d50aee8 0x00100000 |
| // --- --- 0 0x00000000 __TEXT LC_SEGMENT 0x000f7000 0x00001000 |
| // 0x1d60aee8 0x00001000 --- --- 0 0x00000000 __TEXT |
| // |
| // Any if the user executes the following command: |
| // |
| // (lldb) mem read 0xf6ff0 |
| // |
| // We would attempt to read 32 bytes from 0xf6ff0 but would only get 16 |
| // unless we loop through consecutive memory ranges that are contiguous in |
| // the address space, but not in the file data. |
| //---------------------------------------------------------------------- |
| while (bytes_read < size) { |
| const addr_t curr_addr = addr + bytes_read; |
| const VMRangeToFileOffset::Entry *core_memory_entry = |
| m_core_aranges.FindEntryThatContains(curr_addr); |
| |
| if (core_memory_entry) { |
| const addr_t offset = curr_addr - core_memory_entry->GetRangeBase(); |
| const addr_t bytes_left = core_memory_entry->GetRangeEnd() - curr_addr; |
| const size_t bytes_to_read = |
| std::min(size - bytes_read, (size_t)bytes_left); |
| const size_t curr_bytes_read = core_objfile->CopyData( |
| core_memory_entry->data.GetRangeBase() + offset, bytes_to_read, |
| (char *)buf + bytes_read); |
| if (curr_bytes_read == 0) |
| break; |
| bytes_read += curr_bytes_read; |
| } else { |
| // Only set the error if we didn't read any bytes |
| if (bytes_read == 0) |
| error.SetErrorStringWithFormat( |
| "core file does not contain 0x%" PRIx64, curr_addr); |
| break; |
| } |
| } |
| } |
| |
| return bytes_read; |
| } |
| |
| Status ProcessMachCore::GetMemoryRegionInfo(addr_t load_addr, |
| MemoryRegionInfo ®ion_info) { |
| region_info.Clear(); |
| const VMRangeToPermissions::Entry *permission_entry = |
| m_core_range_infos.FindEntryThatContainsOrFollows(load_addr); |
| if (permission_entry) { |
| if (permission_entry->Contains(load_addr)) { |
| region_info.GetRange().SetRangeBase(permission_entry->GetRangeBase()); |
| region_info.GetRange().SetRangeEnd(permission_entry->GetRangeEnd()); |
| const Flags permissions(permission_entry->data); |
| region_info.SetReadable(permissions.Test(ePermissionsReadable) |
| ? MemoryRegionInfo::eYes |
| : MemoryRegionInfo::eNo); |
| region_info.SetWritable(permissions.Test(ePermissionsWritable) |
| ? MemoryRegionInfo::eYes |
| : MemoryRegionInfo::eNo); |
| region_info.SetExecutable(permissions.Test(ePermissionsExecutable) |
| ? MemoryRegionInfo::eYes |
| : MemoryRegionInfo::eNo); |
| region_info.SetMapped(MemoryRegionInfo::eYes); |
| } else if (load_addr < permission_entry->GetRangeBase()) { |
| region_info.GetRange().SetRangeBase(load_addr); |
| region_info.GetRange().SetRangeEnd(permission_entry->GetRangeBase()); |
| region_info.SetReadable(MemoryRegionInfo::eNo); |
| region_info.SetWritable(MemoryRegionInfo::eNo); |
| region_info.SetExecutable(MemoryRegionInfo::eNo); |
| region_info.SetMapped(MemoryRegionInfo::eNo); |
| } |
| return Status(); |
| } |
| |
| region_info.GetRange().SetRangeBase(load_addr); |
| region_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); |
| region_info.SetReadable(MemoryRegionInfo::eNo); |
| region_info.SetWritable(MemoryRegionInfo::eNo); |
| region_info.SetExecutable(MemoryRegionInfo::eNo); |
| region_info.SetMapped(MemoryRegionInfo::eNo); |
| return Status(); |
| } |
| |
| void ProcessMachCore::Clear() { m_thread_list.Clear(); } |
| |
| void ProcessMachCore::Initialize() { |
| static llvm::once_flag g_once_flag; |
| |
| llvm::call_once(g_once_flag, []() { |
| PluginManager::RegisterPlugin(GetPluginNameStatic(), |
| GetPluginDescriptionStatic(), CreateInstance); |
| }); |
| } |
| |
| addr_t ProcessMachCore::GetImageInfoAddress() { |
| // If we found both a user-process dyld and a kernel binary, we need to |
| // decide which to prefer. |
| if (GetCorefilePreference() == eKernelCorefile) { |
| if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { |
| return m_mach_kernel_addr; |
| } |
| return m_dyld_addr; |
| } else { |
| if (m_dyld_addr != LLDB_INVALID_ADDRESS) { |
| return m_dyld_addr; |
| } |
| return m_mach_kernel_addr; |
| } |
| } |
| |
| lldb_private::ObjectFile *ProcessMachCore::GetCoreObjectFile() { |
| return m_core_module_sp->GetObjectFile(); |
| } |