forked from OSchip/llvm-project
Add the RelocateOrLoadKernel and LoadKernel methods to ProcessGDBRemote::DoRemoteConnect().
When attaching to a remote system that does not look like a typical vendor system, and no executable binary was specified to lldb, check a couple of fixed locations where kernels running in ASLR mode (slid in memory to a random address) store their load addr when booted in debug mode, and relocate the symbols or load the kernel wholesale from the host computer if we can find it. <rdar://problem/7714201> llvm-svn: 164888
This commit is contained in:
parent
5f9791fd2f
commit
d1fae144ba
|
@ -2547,7 +2547,7 @@ public:
|
|||
/// Read a NULL terminated C string from memory
|
||||
///
|
||||
/// This function will read a cache page at a time until the NULL
|
||||
/// C stirng terminator is found. It will stop reading if the NULL
|
||||
/// C string terminator is found. It will stop reading if the NULL
|
||||
/// termination byte isn't found before reading \a cstr_max_len
|
||||
/// bytes, and the results are always guaranteed to be NULL
|
||||
/// terminated (at most cstr_max_len - 1 bytes will be read).
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "lldb/Core/StreamString.h"
|
||||
#include "lldb/Core/Timer.h"
|
||||
#include "lldb/Core/Value.h"
|
||||
#include "lldb/Host/Symbols.h"
|
||||
#include "lldb/Host/TimeValue.h"
|
||||
#include "lldb/Symbol/ObjectFile.h"
|
||||
#include "lldb/Target/DynamicLoader.h"
|
||||
|
@ -427,24 +428,7 @@ ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url)
|
|||
return error;
|
||||
StartAsyncThread ();
|
||||
|
||||
const ArchSpec &gdb_remote_arch = m_gdb_comm.GetHostArchitecture();
|
||||
if (gdb_remote_arch.IsValid() && gdb_remote_arch.GetTriple().getVendor() == llvm::Triple::Apple)
|
||||
{
|
||||
Module *exe_module = GetTarget().GetExecutableModulePointer();
|
||||
|
||||
ObjectFile *exe_objfile = exe_module->GetObjectFile();
|
||||
|
||||
// If the remote system is an Apple device and we don't have an exec file
|
||||
// OR we have an exec file and it is a kernel, look for the kernel's load address
|
||||
// in memory and load/relocate the kernel symbols as appropriate.
|
||||
if (exe_objfile == NULL
|
||||
|| (exe_objfile->GetType() == ObjectFile::eTypeExecutable &&
|
||||
exe_objfile->GetStrata() == ObjectFile::eStrataKernel))
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
RelocateOrLoadKernel (strm);
|
||||
|
||||
lldb::pid_t pid = m_gdb_comm.GetCurrentProcessID ();
|
||||
if (pid == LLDB_INVALID_PROCESS_ID)
|
||||
|
@ -483,6 +467,195 @@ ProcessGDBRemote::DoConnectRemote (Stream *strm, const char *remote_url)
|
|||
return error;
|
||||
}
|
||||
|
||||
// When we are establishing a connection to a remote system and we have no executable specified,
|
||||
// or the executable is a kernel, we may be looking at a KASLR situation (where the kernel has been
|
||||
// slid in memory.)
|
||||
//
|
||||
// This function does several things:
|
||||
//
|
||||
// 1. If a non-kernel executable is provided, do nothing. If the executable provided is a kernel and
|
||||
// it loaded at a non-slid address (FileAddress == LoadAddress), do nothing.
|
||||
//
|
||||
// 2. When in debug mode the kernel will record its actual load address at a fixed address in memory.
|
||||
// Check those addresses, see if there is a kernel binary at them.
|
||||
//
|
||||
// 3. If we find a kernel in memory and it matches the executable provided, adjust to the slide.
|
||||
//
|
||||
// 4. If lldb was given no executable at startup, or the one we find in memory does not match the one
|
||||
// provided, try to locate a copy of the correct kernel on the host system. Else read it out of memory.
|
||||
//
|
||||
// gdb would take an additional series of steps where it would scan through memory looking for a kernel
|
||||
// to find a slid kernel that was not booted in debug mode (these were also needed back when the kernel
|
||||
// didn't record its load address anywhere). With luck we won't need to pull those in to lldb.
|
||||
//
|
||||
// The obvious location for all of this code would be in the DynamicLoaderDarwinKernel -- but if we're started
|
||||
// without any executable binary provided, we won't know to use that plugin.
|
||||
|
||||
void
|
||||
ProcessGDBRemote::RelocateOrLoadKernel (Stream *strm)
|
||||
{
|
||||
// early return if this isn't an "unknown" system (kernel debugging doesn't have a system type)
|
||||
const ArchSpec &gdb_remote_arch = m_gdb_comm.GetHostArchitecture();
|
||||
if (!gdb_remote_arch.IsValid() || gdb_remote_arch.GetTriple().getVendor() != llvm::Triple::UnknownVendor)
|
||||
return;
|
||||
|
||||
Module *exe_module = GetTarget().GetExecutableModulePointer();
|
||||
ObjectFile *exe_objfile = NULL;
|
||||
if (exe_module)
|
||||
exe_objfile = exe_module->GetObjectFile();
|
||||
|
||||
// early return if we have an executable and it is not a kernel--this is very unlikely to be a kernel debug session.
|
||||
if (exe_objfile
|
||||
&& (exe_objfile->GetType() != ObjectFile::eTypeExecutable
|
||||
|| exe_objfile->GetStrata() != ObjectFile::eStrataKernel))
|
||||
return;
|
||||
|
||||
// See if the kernel is in memory at the File address (slide == 0) -- no work needed, if so.
|
||||
if (exe_objfile && exe_objfile->GetHeaderAddress().IsValid())
|
||||
{
|
||||
ModuleSP memory_module_sp;
|
||||
memory_module_sp = ReadModuleFromMemory (exe_module->GetFileSpec(), exe_objfile->GetHeaderAddress().GetFileAddress(), false, false);
|
||||
if (memory_module_sp.get()
|
||||
&& memory_module_sp->GetUUID().IsValid()
|
||||
&& memory_module_sp->GetUUID() == exe_module->GetUUID())
|
||||
{
|
||||
bool changed = false;
|
||||
exe_module->SetLoadAddress (GetTarget(), 0, changed);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// See if the kernel's load address is stored in the kernel's low globals page; this is
|
||||
// done when a debug boot-arg has been set.
|
||||
|
||||
Error error;
|
||||
uint8_t buf[24];
|
||||
ModuleSP memory_module_sp;
|
||||
addr_t kernel_addr = LLDB_INVALID_ADDRESS;
|
||||
|
||||
// First try the 32-bit
|
||||
if (memory_module_sp.get() == NULL)
|
||||
{
|
||||
DataExtractor data4 (buf, sizeof(buf), gdb_remote_arch.GetByteOrder(), 4);
|
||||
if (DoReadMemory (0xffff0110, buf, 4, error) == 4)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
kernel_addr = data4.GetU32(&offset);
|
||||
memory_module_sp = ReadModuleFromMemory (FileSpec("mach_kernel", false), kernel_addr, false, false);
|
||||
if (!memory_module_sp.get()
|
||||
|| !memory_module_sp->GetUUID().IsValid()
|
||||
|| memory_module_sp->GetObjectFile() == NULL
|
||||
|| memory_module_sp->GetObjectFile()->GetType() != ObjectFile::eTypeExecutable
|
||||
|| memory_module_sp->GetObjectFile()->GetStrata() != ObjectFile::eStrataKernel)
|
||||
{
|
||||
memory_module_sp.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now try the 64-bit location
|
||||
if (memory_module_sp.get() == NULL)
|
||||
{
|
||||
DataExtractor data8 (buf, sizeof(buf), gdb_remote_arch.GetByteOrder(), 8);
|
||||
if (DoReadMemory (0xffffff8000002010ULL, buf, 8, error) == 8)
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
kernel_addr = data8.GetU32(&offset);
|
||||
memory_module_sp = ReadModuleFromMemory (FileSpec("mach_kernel", false), kernel_addr, false, false);
|
||||
if (!memory_module_sp.get()
|
||||
|| !memory_module_sp->GetUUID().IsValid()
|
||||
|| memory_module_sp->GetObjectFile() == NULL
|
||||
|| memory_module_sp->GetObjectFile()->GetType() != ObjectFile::eTypeExecutable
|
||||
|| memory_module_sp->GetObjectFile()->GetStrata() != ObjectFile::eStrataKernel)
|
||||
{
|
||||
memory_module_sp.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (memory_module_sp.get())
|
||||
{
|
||||
if (strm)
|
||||
{
|
||||
char uuidbuf[64];
|
||||
strm->Printf ("Kernel UUID: %s\n", memory_module_sp->GetUUID().GetAsCString (uuidbuf, sizeof (uuidbuf)));
|
||||
strm->Printf ("Load Address: 0x%llx\n", kernel_addr);
|
||||
strm->Flush ();
|
||||
}
|
||||
|
||||
// Did the user already give us the correct binary? Don't re-load it if so, just set the load addr.
|
||||
if (exe_module && exe_objfile && exe_module->GetUUID() == memory_module_sp->GetUUID())
|
||||
{
|
||||
bool changed = false;
|
||||
addr_t slide = kernel_addr - exe_objfile->GetHeaderAddress().GetFileAddress();
|
||||
exe_module->SetLoadAddress (GetTarget(), slide, changed);
|
||||
if (changed)
|
||||
{
|
||||
ModuleList modlist;
|
||||
modlist.Append (GetTarget().GetExecutableModule());
|
||||
GetTarget().ModulesDidLoad (modlist);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// OK try to find a kernel on the local system, or get it from memory
|
||||
LoadKernel (strm, memory_module_sp->GetUUID(), kernel_addr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ProcessGDBRemote::LoadKernel (Stream *strm, UUID kernel_uuid, addr_t kernel_load_addr)
|
||||
{
|
||||
|
||||
// First try to find the kernel binary by calling Symbols::DownloadObjectAndSymbolFile
|
||||
ModuleSpec sym_spec;
|
||||
sym_spec.GetUUID() = kernel_uuid;
|
||||
if (Symbols::DownloadObjectAndSymbolFile (sym_spec)
|
||||
&& sym_spec.GetArchitecture().IsValid()
|
||||
&& sym_spec.GetSymbolFileSpec().Exists())
|
||||
{
|
||||
ModuleSP kernel_sp = GetTarget().GetSharedModule (sym_spec);
|
||||
if (kernel_sp.get())
|
||||
{
|
||||
GetTarget().SetExecutableModule(kernel_sp, false);
|
||||
if (kernel_sp->GetObjectFile() && kernel_sp->GetObjectFile()->GetHeaderAddress().IsValid())
|
||||
{
|
||||
addr_t slide = kernel_load_addr - kernel_sp->GetObjectFile()->GetHeaderAddress().GetFileAddress();
|
||||
bool changed = false;
|
||||
kernel_sp->SetLoadAddress (GetTarget(), slide, changed);
|
||||
if (changed)
|
||||
{
|
||||
ModuleList modlist;
|
||||
modlist.Append (kernel_sp);
|
||||
GetTarget().ModulesDidLoad (modlist);
|
||||
}
|
||||
if (strm)
|
||||
{
|
||||
strm->Printf ("Loaded kernel file %s/%s\n",
|
||||
kernel_sp->GetFileSpec().GetDirectory().AsCString(),
|
||||
kernel_sp->GetFileSpec().GetFilename().AsCString());
|
||||
strm->Flush ();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing better, load the kernel binary out of memory - this is likely slow and may not get us symbols.
|
||||
ModuleSP memory_module_sp = ReadModuleFromMemory (FileSpec("mach_kernel", false), kernel_load_addr, true, true);
|
||||
if (memory_module_sp.get()
|
||||
&& memory_module_sp->GetUUID().IsValid()
|
||||
&& memory_module_sp->GetObjectFile()
|
||||
&& memory_module_sp->GetObjectFile()->GetType() == ObjectFile::eTypeExecutable
|
||||
&& memory_module_sp->GetObjectFile()->GetStrata() == ObjectFile::eStrataKernel)
|
||||
{
|
||||
bool changed;
|
||||
uint64_t slide = kernel_load_addr - memory_module_sp->GetObjectFile()->GetHeaderAddress().GetFileAddress();
|
||||
memory_module_sp->SetLoadAddress (GetTarget(), slide, changed);
|
||||
GetTarget().SetExecutableModule(memory_module_sp, false);
|
||||
}
|
||||
}
|
||||
|
||||
Error
|
||||
ProcessGDBRemote::WillLaunchOrAttach ()
|
||||
{
|
||||
|
|
|
@ -293,6 +293,13 @@ protected:
|
|||
lldb_private::Mutex::Locker locker (m_last_stop_packet_mutex);
|
||||
m_last_stop_packet = response;
|
||||
}
|
||||
|
||||
void
|
||||
RelocateOrLoadKernel (lldb_private::Stream *strm);
|
||||
|
||||
void
|
||||
LoadKernel (lldb_private::Stream *strm, lldb_private::UUID kernel_uuid, lldb::addr_t kernel_load_addr);
|
||||
|
||||
//------------------------------------------------------------------
|
||||
/// Broadcaster event bits definitions.
|
||||
//------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in New Issue