summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lldb/packages/Python/lldbsuite/test/functionalities/postmortem/wow64_minidump/TestWow64MiniDump.py76
-rw-r--r--lldb/packages/Python/lldbsuite/test/functionalities/postmortem/wow64_minidump/fizzbuzz.cpp31
-rw-r--r--lldb/packages/Python/lldbsuite/test/functionalities/postmortem/wow64_minidump/fizzbuzz_wow64.dmpbin0 -> 9280561 bytes
-rw-r--r--lldb/source/Plugins/Process/Windows/Common/NtStructures.h32
-rw-r--r--lldb/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp69
5 files changed, 200 insertions, 8 deletions
diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/wow64_minidump/TestWow64MiniDump.py b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/wow64_minidump/TestWow64MiniDump.py
new file mode 100644
index 00000000000..08debab538f
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/wow64_minidump/TestWow64MiniDump.py
@@ -0,0 +1,76 @@
+"""
+Test basics of a mini dump taken of a 32-bit process running in WoW64
+
+WoW64 is the subsystem that lets 32-bit processes run in 64-bit Windows. If you
+capture a mini dump of a process running under WoW64 with a 64-bit debugger, you
+end up with a dump of the WoW64 layer. In that case, LLDB must do extra work to
+get the 32-bit register contexts.
+"""
+
+from __future__ import print_function
+from six import iteritems
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+class Wow64MiniDumpTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ @skipUnlessWindows # for now mini-dump debugging is limited to Windows hosts
+ @no_debug_info_test
+ def test_wow64_mini_dump(self):
+ """Test that lldb can read the process information from the minidump."""
+ # target create -c fizzbuzz_wow64.dmp
+ target = self.dbg.CreateTarget("")
+ process = target.LoadCore("fizzbuzz_wow64.dmp")
+ self.assertTrue(process, PROCESS_IS_VALID)
+ self.assertEqual(process.GetNumThreads(), 1)
+ self.assertEqual(process.GetProcessID(), 0x1E9C)
+
+ @skipUnlessWindows # for now mini-dump debugging is limited to Windows hosts
+ @no_debug_info_test
+ def test_thread_info_in_wow64_mini_dump(self):
+ """Test that lldb can read the thread information from the minidump."""
+ # target create -c fizzbuzz_wow64.dmp
+ target = self.dbg.CreateTarget("")
+ process = target.LoadCore("fizzbuzz_wow64.dmp")
+ # This process crashed due to an access violation (0xc0000005), but the
+ # minidump doesn't have an exception record--perhaps the crash handler
+ # ate it.
+ # TODO: See if we can recover the exception information from the TEB,
+ # which, according to Windbg, has a pointer to an exception list.
+
+ # In the dump, none of the threads are stopped, so we cannot use
+ # lldbutil.get_stopped_thread.
+ thread = process.GetThreadAtIndex(0)
+ self.assertEqual(thread.GetStopReason(), lldb.eStopReasonNone)
+
+ @skipUnlessWindows # for now mini-dump debugging is limited to Windows hosts
+ @no_debug_info_test
+ def test_stack_info_in_wow64_mini_dump(self):
+ """Test that we can see a trivial stack in a VS-generate mini dump."""
+ # target create -c fizzbuzz_no_heap.dmp
+ target = self.dbg.CreateTarget("")
+ process = target.LoadCore("fizzbuzz_wow64.dmp")
+ self.assertGreaterEqual(process.GetNumThreads(), 1)
+ # This process crashed due to an access violation (0xc0000005), but the
+ # minidump doesn't have an exception record--perhaps the crash handler
+ # ate it.
+ # TODO: See if we can recover the exception information from the TEB,
+ # which, according to Windbg, has a pointer to an exception list.
+
+ # In the dump, none of the threads are stopped, so we cannot use
+ # lldbutil.get_stopped_thread.
+ thread = process.GetThreadAtIndex(0)
+ # The crash is in main, so there should be at least one frame on the stack.
+ self.assertGreaterEqual(thread.GetNumFrames(), 1)
+ frame = thread.GetFrameAtIndex(0)
+ self.assertTrue(frame.IsValid())
+ pc = frame.GetPC()
+ eip = frame.FindRegister("pc")
+ self.assertTrue(eip.IsValid())
+ self.assertEqual(pc, eip.GetValueAsUnsigned())
diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/wow64_minidump/fizzbuzz.cpp b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/wow64_minidump/fizzbuzz.cpp
new file mode 100644
index 00000000000..295d4a1f24d
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/wow64_minidump/fizzbuzz.cpp
@@ -0,0 +1,31 @@
+// A sample program for getting minidumps on Windows.
+
+#include <iostream>
+
+bool
+fizz(int x)
+{
+ return x % 3 == 0;
+}
+
+bool
+buzz(int x)
+{
+ return x % 5 == 0;
+}
+
+int
+main()
+{
+ int *buggy = 0;
+
+ for (int i = 1; i <= 100; ++i)
+ {
+ if (fizz(i)) std::cout << "fizz";
+ if (buzz(i)) std::cout << "buzz";
+ if (!fizz(i) && !buzz(i)) std::cout << i;
+ std::cout << '\n';
+ }
+
+ return *buggy;
+}
diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/wow64_minidump/fizzbuzz_wow64.dmp b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/wow64_minidump/fizzbuzz_wow64.dmp
new file mode 100644
index 00000000000..3d97186f2cd
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test/functionalities/postmortem/wow64_minidump/fizzbuzz_wow64.dmp
Binary files differ
diff --git a/lldb/source/Plugins/Process/Windows/Common/NtStructures.h b/lldb/source/Plugins/Process/Windows/Common/NtStructures.h
new file mode 100644
index 00000000000..6c688d9068d
--- /dev/null
+++ b/lldb/source/Plugins/Process/Windows/Common/NtStructures.h
@@ -0,0 +1,32 @@
+//===-- NtStructures.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_Plugins_Process_Windows_Common_NtStructures_h_
+#define liblldb_Plugins_Process_Windows_Common_NtStructures_h_
+
+#include "lldb/Host/windows/windows.h"
+
+// 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;
+};
+
+#endif
diff --git a/lldb/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp b/lldb/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp
index fbc96f085ed..87dc18d2e03 100644
--- a/lldb/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp
+++ b/lldb/source/Plugins/Process/Windows/MiniDump/ProcessWinMiniDump.cpp
@@ -35,6 +35,9 @@
#include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h"
+#include "Plugins/Process/Windows/Common/NtStructures.h"
+#include "Plugins/Process/Windows/Common/ProcessWindowsLog.h"
+
#include "ExceptionRecord.h"
#include "ThreadWinMiniDump.h"
@@ -83,6 +86,7 @@ public:
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
@@ -195,7 +199,47 @@ ProcessWinMiniDump::UpdateThreadList(ThreadList &old_thread_list, ThreadList &ne
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).
+ // See: https://msdn.microsoft.com/en-us/library/ms681670.aspx
+ 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);
@@ -347,11 +391,8 @@ ProcessWinMiniDump::GetArchitecture()
return ArchSpec();
}
-
-ProcessWinMiniDump::Data::Data() :
- m_dump_file(INVALID_HANDLE_VALUE),
- m_mapping(NULL),
- m_base_addr(nullptr)
+ProcessWinMiniDump::Data::Data()
+ : m_dump_file(INVALID_HANDLE_VALUE), m_mapping(NULL), m_base_addr(nullptr), m_is_wow64(false)
{
}
@@ -381,7 +422,8 @@ ProcessWinMiniDump::FindMemoryRange(lldb::addr_t addr, Range *range_out) const
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;
@@ -485,6 +527,11 @@ ProcessWinMiniDump::ReadExceptionRecord()
{
m_data_up->m_exception_sp.reset(new ExceptionRecord(exception_stream_ptr->ExceptionRecord, exception_stream_ptr->ThreadId));
}
+ else
+ {
+ WINLOG_IFALL(WINDOWS_LOG_PROCESS, "Minidump has no exception record.");
+ // TODO: See if we can recover the exception from the TEB.
+ }
}
void
@@ -516,7 +563,13 @@ ProcessWinMiniDump::ReadModuleList()
{
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)
OpenPOWER on IntegriCloud