blob: ca99175ce9f769f2bbef2224a1487e3cc7036d3a [file] [log] [blame]
//===-- 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"
// Other libraries and framework includes
#include "lldb/Core/Debugger.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/Section.h"
#include "lldb/Core/State.h"
#include "lldb/Host/Host.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
// Project includes
#include "ProcessMachCore.h"
#include "ThreadMachCore.h"
#include "StopInfoMachException.h"
// Needed for the plug-in names for the dynamic loaders.
#include "lldb/Utility/SafeMachO.h"
#include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h"
#include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.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 (Target &target, Listener &listener, const FileSpec *crash_file)
{
lldb::ProcessSP process_sp;
if (crash_file)
{
const size_t header_size = sizeof(llvm::MachO::mach_header);
lldb::DataBufferSP data_sp (crash_file->ReadFileContents(0, header_size));
if (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, listener, *crash_file));
}
}
}
return process_sp;
}
bool
ProcessMachCore::CanDebug(Target &target, 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())
{
ModuleSpec core_module_spec(m_core_file, target.GetArchitecture());
Error error (ModuleList::GetSharedModule (core_module_spec,
m_core_module_sp,
NULL,
NULL,
NULL));
if (m_core_module_sp)
{
const llvm::Triple &triple_ref = m_core_module_sp->GetArchitecture().GetTriple();
if (triple_ref.getVendor() == llvm::Triple::Apple)
{
ObjectFile *core_objfile = m_core_module_sp->GetObjectFile();
if (core_objfile && core_objfile->GetType() == ObjectFile::eTypeCoreFile)
return true;
}
}
}
return false;
}
//----------------------------------------------------------------------
// ProcessMachCore constructor
//----------------------------------------------------------------------
ProcessMachCore::ProcessMachCore(Target& target, Listener &listener, const FileSpec &core_file) :
Process (target, listener),
m_core_aranges (),
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)
{
llvm::MachO::mach_header header;
Error 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
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)
{
// Address of the mach kernel "struct mach_header" in the core file.
m_mach_kernel_addr = addr;
return true;
}
break;
}
}
return false;
}
//----------------------------------------------------------------------
// Process Control
//----------------------------------------------------------------------
Error
ProcessMachCore::DoLoadCore ()
{
Error 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);
}
}
}
if (!ranges_are_sorted)
{
m_core_aranges.Sort();
}
if (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. If we find a user-process dyld binary, stop
// searching -- that's the one we'll prefer over the mach kernel.
const size_t num_core_aranges = m_core_aranges.GetSize();
for (size_t i=0; i<num_core_aranges && m_dyld_addr == LLDB_INVALID_ADDRESS; ++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 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)
{
m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic();
}
else if (m_dyld_addr != LLDB_INVALID_ADDRESS)
{
m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic();
}
}
else
{
if (m_dyld_addr != LLDB_INVALID_ADDRESS)
{
m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic();
}
else if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS)
{
m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic();
}
}
// 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.SetTriple ("i386", m_target.GetPlatform().get());
}
if (arch.IsValid())
m_target.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));
}
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);
}
Error
ProcessMachCore::DoDestroy ()
{
return Error();
}
//------------------------------------------------------------------
// 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, Error &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, Error &error)
{
ObjectFile *core_objfile = m_core_module_sp->GetObjectFile();
if (core_objfile)
{
const VMRangeToFileOffset::Entry *core_memory_entry = m_core_aranges.FindEntryThatContains (addr);
if (core_memory_entry)
{
const addr_t offset = addr - core_memory_entry->GetRangeBase();
const addr_t bytes_left = core_memory_entry->GetRangeEnd() - addr;
size_t bytes_to_read = size;
if (bytes_to_read > bytes_left)
bytes_to_read = bytes_left;
return core_objfile->CopyData (core_memory_entry->data.GetRangeBase() + offset, bytes_to_read, buf);
}
else
{
error.SetErrorStringWithFormat ("core file does not contain 0x%" PRIx64, addr);
}
}
return 0;
}
void
ProcessMachCore::Clear()
{
m_thread_list.Clear();
}
void
ProcessMachCore::Initialize()
{
static bool g_initialized = false;
if (g_initialized == false)
{
g_initialized = true;
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();
}