From d1fae144ba649e48d90b2b8337e294cae2fd7100 Mon Sep 17 00:00:00 2001 From: Jason Molenda Date: Sat, 29 Sep 2012 08:03:33 +0000 Subject: [PATCH] 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. llvm-svn: 164888 --- lldb/include/lldb/Target/Process.h | 2 +- .../Process/gdb-remote/ProcessGDBRemote.cpp | 209 +++++++++++++++++++-- .../Plugins/Process/gdb-remote/ProcessGDBRemote.h | 7 + 3 files changed, 199 insertions(+), 19 deletions(-) diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 178552d..7921ca8 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -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). diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index f29a167..d39d1a5 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -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 () { diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index 4a71615..4732fc3 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -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. //------------------------------------------------------------------ -- 2.7.4