amccarth created this revision.
amccarth added a reviewer: zturner.
amccarth added a subscriber: lldb-commits.

32-bit processes on 64-bit Windows run in a layer called WoW64 
(Windows-on-Windows64).  If you capture a mini dump of such a process from a 
32-bit debugger, you end up with a register context for the 64-bit WoW64 
process rather than the 32-bit one you probably care about.

This detects WoW64 by looking to see if there's a module named wow64.dll 
loaded.  For such processes, it then looks in the 64-bit Thread Environment 
Block (TEB) to locate a copy of the 32-bit CONTEXT record that the plugin needs 
for the register context.

I'll add a test before submitting this, but it seems to work in my manual 
experiments.

http://reviews.llvm.org/D17465

Files:
  source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp

Index: source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp
===================================================================
--- source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp
+++ source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp
@@ -35,6 +35,8 @@
 #include "llvm/Support/Format.h"
 #include "llvm/Support/raw_ostream.h"
 
+#include "Plugins/Process/Windows/Common/ProcessWindowsLog.h"
+
 #include "ExceptionRecord.h"
 #include "ThreadWinMiniDump.h"
 
@@ -83,6 +85,7 @@
     HANDLE m_mapping;  // handle to the file mapping for the minidump file
     void * m_base_addr;  // base memory address of the minidump
     std::shared_ptr<ExceptionRecord> m_exception_sp;
+    bool m_is_wow64;  // minidump is of a 32-bit process captured with a 64-bit debugger
 };
 
 ConstString
@@ -182,6 +185,24 @@
     return m_dyld_ap.get();
 }
 
+
+// This describes the layout of a TEB (Thread Environment Block) for a 64-bit
+// process.  It's adapted from the 32-bit TEB in winternl.h.  Currently, we care
+// only about the position of the TlsSlots.
+struct TEB64 {
+    ULONG64 Reserved1[12];
+    ULONG64 ProcessEnvironmentBlock;
+    ULONG64 Reserved2[399];
+    BYTE Reserved3[1952];
+    ULONG64 TlsSlots[64];
+    BYTE Reserved4[8];
+    ULONG64 Reserved5[26];
+    ULONG64 ReservedForOle;  // Windows 2000 only
+    ULONG64 Reserved6[4];
+    ULONG64 TlsExpansionSlots;
+};
+
+
 bool
 ProcessWinMiniDump::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list)
 {
@@ -195,7 +216,47 @@
             auto thread_sp = std::make_shared<ThreadWinMiniDump>(*this, mini_dump_thread.ThreadId);
             if (mini_dump_thread.ThreadContext.DataSize >= sizeof(CONTEXT))
             {
-                const CONTEXT *context = reinterpret_cast<const CONTEXT *>(static_cast<const char *>(m_data_up->m_base_addr) + mini_dump_thread.ThreadContext.Rva);
+                const CONTEXT *context =
+                    reinterpret_cast<const CONTEXT *>(static_cast<const char *>(m_data_up->m_base_addr) +
+                                                      mini_dump_thread.ThreadContext.Rva);
+
+                if (m_data_up->m_is_wow64)
+                {
+                    // On Windows, a 32-bit process can run on a 64-bit machine under WOW64.
+                    // If the minidump was captured with a 64-bit debugger, then the CONTEXT
+                    // we just grabbed from the mini_dump_thread is the one for the 64-bit
+                    // "native" process rather than the 32-bit "guest" process we care about.
+                    // In this case, we can get the 32-bit CONTEXT from the TEB (Thread
+                    // Environment Block) of the 64-bit process.
+                    Error error;
+                    TEB64 wow64teb = { 0 };
+                    ReadMemory(mini_dump_thread.Teb, &wow64teb, sizeof(wow64teb), error);
+                    if (error.Success())
+                    {
+                        // Slot 1 of the thread-local storage in the 64-bit TEB points to a structure
+                        // that includes the 32-bit CONTEXT (after a ULONG).
+                        const size_t addr = wow64teb.TlsSlots[1];
+                        Range range = {0};
+                        if (FindMemoryRange(addr, &range))
+                        {
+                            lldbassert(range.start <= addr);
+                            const size_t offset = addr - range.start + sizeof(ULONG);
+                            if (offset < range.size)
+                            {
+                                const size_t overlap = range.size - offset;
+                                if (overlap >= sizeof(CONTEXT))
+                                {
+                                    context = reinterpret_cast<const CONTEXT *>(range.ptr + offset);
+                                }
+                            }
+                        }
+                    }
+
+                    // NOTE:  We don't currently use the TEB for anything else.  If we need it in
+                    // the future, the 32-bit TEB is located according to the address stored in the
+                    // first slot of the 64-bit TEB (wow64teb.Reserved1[0]).
+                }
+
                 thread_sp->SetContext(context);
             }
             new_thread_list.AddThread(thread_sp);
@@ -351,7 +412,8 @@
 ProcessWinMiniDump::Data::Data() :
     m_dump_file(INVALID_HANDLE_VALUE),
     m_mapping(NULL),
-    m_base_addr(nullptr)
+    m_base_addr(nullptr),
+    m_is_wow64(false)
 {
 }
 
@@ -381,7 +443,8 @@
     auto mem_list_stream = static_cast<const MINIDUMP_MEMORY_LIST *>(FindDumpStream(MemoryListStream, &stream_size));
     if (mem_list_stream)
     {
-        for (ULONG32 i = 0; i < mem_list_stream->NumberOfMemoryRanges; ++i) {
+        for (ULONG32 i = 0; i < mem_list_stream->NumberOfMemoryRanges; ++i)
+        {
             const MINIDUMP_MEMORY_DESCRIPTOR &mem_desc = mem_list_stream->MemoryRanges[i];
             const MINIDUMP_LOCATION_DESCRIPTOR &loc_desc = mem_desc.Memory;
             const lldb::addr_t range_start = mem_desc.StartOfMemoryRange;
@@ -516,7 +579,13 @@
     {
         const auto &module = module_list_ptr->Modules[i];
         const auto file_name = GetMiniDumpString(m_data_up->m_base_addr, module.ModuleNameRva);
-        ModuleSpec module_spec = FileSpec(file_name, true);
+        const auto file_spec = FileSpec(file_name, true);
+        if (FileSpec::Compare(file_spec, FileSpec("wow64.dll", false), false) == 0)
+        {
+            WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Minidump is for a WOW64 process.");
+            m_data_up->m_is_wow64 = true;
+        }
+        ModuleSpec module_spec = file_spec;
 
         lldb::ModuleSP module_sp = GetTarget().GetSharedModule(module_spec);
         if (!module_sp)
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to