[Lldb-commits] [lldb] r164888 - in /lldb/trunk: include/lldb/Target/Process.h source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp source/Plugins/Process/gdb-remote/ProcessGDBRemote.h

Jason Molenda jmolenda at apple.com
Sat Sep 29 01:03:34 PDT 2012


Author: jmolenda
Date: Sat Sep 29 03:03:33 2012
New Revision: 164888

URL: http://llvm.org/viewvc/llvm-project?rev=164888&view=rev
Log:
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>

Modified:
    lldb/trunk/include/lldb/Target/Process.h
    lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
    lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h

Modified: lldb/trunk/include/lldb/Target/Process.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/Process.h?rev=164888&r1=164887&r2=164888&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Target/Process.h (original)
+++ lldb/trunk/include/lldb/Target/Process.h Sat Sep 29 03:03:33 2012
@@ -2547,7 +2547,7 @@
     /// 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).

Modified: lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp?rev=164888&r1=164887&r2=164888&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp Sat Sep 29 03:03:33 2012
@@ -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 @@
         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 @@
     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 ()
 {

Modified: lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h?rev=164888&r1=164887&r2=164888&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h Sat Sep 29 03:03:33 2012
@@ -293,6 +293,13 @@
         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.
     //------------------------------------------------------------------





More information about the lldb-commits mailing list