diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 4e697eeba8..1241053232 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -95,6 +95,8 @@ endif () if ( CMAKE_SYSTEM_NAME MATCHES "Windows" ) list(APPEND LLDB_USED_LIBS lldbHostWindows + lldbPluginProcessWindows + lldbPluginDynamicLoaderWindows lldbPluginProcessElfCore lldbPluginJITLoaderGDB Ws2_32 diff --git a/source/Plugins/DynamicLoader/CMakeLists.txt b/source/Plugins/DynamicLoader/CMakeLists.txt index c14740d053..d029beeaa0 100644 --- a/source/Plugins/DynamicLoader/CMakeLists.txt +++ b/source/Plugins/DynamicLoader/CMakeLists.txt @@ -4,5 +4,7 @@ add_subdirectory(Static) if (CMAKE_SYSTEM_NAME MATCHES "Darwin") add_subdirectory(Darwin-Kernel) +elseif (CMAKE_SYSTEM_NAME MATCHES "Windows") + add_subdirectory(Windows) endif() diff --git a/source/Plugins/DynamicLoader/Windows/CMakeLists.txt b/source/Plugins/DynamicLoader/Windows/CMakeLists.txt new file mode 100644 index 0000000000..3b6c514558 --- /dev/null +++ b/source/Plugins/DynamicLoader/Windows/CMakeLists.txt @@ -0,0 +1,5 @@ +set(LLVM_NO_RTTI 1) + +add_lldb_library(lldbPluginDynamicLoaderWindows + DynamicLoaderWindows.cpp + ) diff --git a/source/Plugins/DynamicLoader/Windows/DynamicLoaderWindows.cpp b/source/Plugins/DynamicLoader/Windows/DynamicLoaderWindows.cpp new file mode 100644 index 0000000000..a8f8b22e25 --- /dev/null +++ b/source/Plugins/DynamicLoader/Windows/DynamicLoaderWindows.cpp @@ -0,0 +1,203 @@ +//===-- DynamicLoaderWindows.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanRunToAddress.h" + +#include "DynamicLoaderWindows.h" + +using namespace lldb; +using namespace lldb_private; + +void +DynamicLoaderWindows::Initialize() +{ + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +DynamicLoaderWindows::Terminate() +{ +} + +lldb_private::ConstString +DynamicLoaderWindows::GetPluginName() +{ + return GetPluginNameStatic(); +} + +lldb_private::ConstString +DynamicLoaderWindows::GetPluginNameStatic() +{ + static ConstString g_name("windows-dyld"); + return g_name; +} + +const char * +DynamicLoaderWindows::GetPluginDescriptionStatic() +{ + return "Dynamic loader plug-in that watches for shared library " + "loads/unloads in Windows processes."; +} + +void +DynamicLoaderWindows::GetPluginCommandHelp(const char *command, Stream *strm) +{ +} + +uint32_t +DynamicLoaderWindows::GetPluginVersion() +{ + return 1; +} + +DynamicLoader * +DynamicLoaderWindows::CreateInstance(Process *process, bool force) +{ + bool create = force; + if (!create) + { + const llvm::Triple &triple_ref = process->GetTarget().GetArchitecture().GetTriple(); + if (triple_ref.getOS() == llvm::Triple::Win32 || + triple_ref.getOS() == llvm::Triple::MinGW32) + create = true; + } + + if (create) + return new DynamicLoaderWindows (process); + return NULL; +} + +DynamicLoaderWindows::DynamicLoaderWindows(Process *process) + : DynamicLoader(process), + m_load_offset(LLDB_INVALID_ADDRESS), + m_entry_point(LLDB_INVALID_ADDRESS) +{ +} + +DynamicLoaderWindows::~DynamicLoaderWindows() +{ +} + +void +DynamicLoaderWindows::DidAttach() +{ +} + +void +DynamicLoaderWindows::DidLaunch() +{ +} + +Error +DynamicLoaderWindows::ExecutePluginCommand(Args &command, Stream *strm) +{ + return Error(); +} + +Log * +DynamicLoaderWindows::EnablePluginLogging(Stream *strm, Args &command) +{ + return NULL; +} + +Error +DynamicLoaderWindows::CanLoadImage() +{ + return Error(); +} + +ThreadPlanSP +DynamicLoaderWindows::GetStepThroughTrampolinePlan(Thread &thread, bool stop) +{ + ThreadPlanSP thread_plan_sp; + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + + lldb::addr_t pc = reg_ctx->GetPC(); + ProcessSP process_sp(thread.GetProcess()); + Address pc_addr; + bool addr_valid = false; + uint8_t buffer[16] = { 0 }; // Must be big enough for any single instruction + addr_valid = process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(pc, pc_addr); + + // TODO: Cache it as in ThreadPlanAssemblyTracer::GetDisassembler () + DisassemblerSP disassembler = Disassembler::FindPlugin(thread.GetProcess()->GetTarget().GetArchitecture(), NULL, NULL); + if (disassembler) + { + Error err; + process_sp->ReadMemory(pc, buffer, sizeof(buffer), err); + + if (err.Success()) + { + DataExtractor extractor(buffer, sizeof(buffer), + process_sp->GetByteOrder(), + process_sp->GetAddressByteSize()); + + bool data_from_file = false; + if (addr_valid) + disassembler->DecodeInstructions(pc_addr, extractor, 0, 1, false, data_from_file); + else + disassembler->DecodeInstructions(Address(pc), extractor, 0, 1, false, data_from_file); + + InstructionList &instruction_list = disassembler->GetInstructionList(); + + if (instruction_list.GetSize()) + { + const bool show_bytes = true; + const bool show_address = true; + Instruction *instruction = instruction_list.GetInstructionAtIndex(0).get(); + + ExecutionContext exe_ctx(thread.GetProcess()); + const char* opcode = instruction->GetMnemonic(&exe_ctx); + + if (strcmp(opcode, "jmpl") == 0) + { + const char* operands_str = instruction->GetOperands(&exe_ctx); + + // Detect trampolines with pattern jmpl *0x400800 where 0x400800 contains the DLL function pointer + // TODO1: Detect jmp address without string parsing (from MCInst) + // TODO2: We should check import table for 0x400800 instead of fetching the pointer behind it (in PECOFF) + unsigned long operand_ptr = strtoul(operands_str + 3, NULL, 16); + Error error; + unsigned long operand_value = process_sp->ReadPointerFromMemory(operand_ptr, error); + + Address sc_addr; + if (process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(operand_value, sc_addr)) + { + SymbolContext sc; + thread.GetProcess()->GetTarget().GetImages().ResolveSymbolContextForAddress(sc_addr, eSymbolContextSymbol, sc); + if (sc.symbol != NULL) + { + thread_plan_sp.reset(new ThreadPlanRunToAddress(thread, operand_value, false)); + } + } + } + } + } + } + + return thread_plan_sp; +} diff --git a/source/Plugins/DynamicLoader/Windows/DynamicLoaderWindows.h b/source/Plugins/DynamicLoader/Windows/DynamicLoaderWindows.h new file mode 100644 index 0000000000..5be69527dd --- /dev/null +++ b/source/Plugins/DynamicLoader/Windows/DynamicLoaderWindows.h @@ -0,0 +1,91 @@ +//===-- DynamicLoaderWindows.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_DynamicLoaderWindows_H_ +#define liblldb_DynamicLoaderWindows_H_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Target/DynamicLoader.h" + +class AuxVector; + +class DynamicLoaderWindows : public lldb_private::DynamicLoader +{ +public: + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::DynamicLoader * + CreateInstance(lldb_private::Process *process, bool force); + + DynamicLoaderWindows(lldb_private::Process *process); + + virtual + ~DynamicLoaderWindows(); + + //------------------------------------------------------------------ + // DynamicLoader protocol + //------------------------------------------------------------------ + + virtual void + DidAttach(); + + virtual void + DidLaunch(); + + virtual lldb::ThreadPlanSP + GetStepThroughTrampolinePlan(lldb_private::Thread &thread, + bool stop_others); + + virtual lldb_private::Error + CanLoadImage(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp(const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand(lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging(lldb_private::Stream *strm, lldb_private::Args &command); + +protected: + /// Virtual load address of the inferior process. + lldb::addr_t m_load_offset; + + /// Virtual entry address of the inferior process. + lldb::addr_t m_entry_point; + +private: + DISALLOW_COPY_AND_ASSIGN(DynamicLoaderWindows); +}; + +#endif // liblldb_DynamicLoaderPOSIXDYLD_H_ diff --git a/source/Plugins/DynamicLoader/Windows/Makefile b/source/Plugins/DynamicLoader/Windows/Makefile new file mode 100644 index 0000000000..0aa3c14832 --- /dev/null +++ b/source/Plugins/DynamicLoader/Windows/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/DynamicLoader/Windows/Makefile -------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginDynamicLoaderWindows +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Makefile b/source/Plugins/Makefile index eb15cf9799..32137a683d 100644 --- a/source/Plugins/Makefile +++ b/source/Plugins/Makefile @@ -43,6 +43,8 @@ DIRS += JITLoader/GDB endif ifeq ($(HOST_OS),MingW) +DIRS += Process/Windows +DIRS += DynamicLoader/Windows DIRS += Process/elf-core DIRS += JITLoader/GDB endif diff --git a/source/Plugins/Process/CMakeLists.txt b/source/Plugins/Process/CMakeLists.txt index 8937fb4af3..b6008cfcd5 100644 --- a/source/Plugins/Process/CMakeLists.txt +++ b/source/Plugins/Process/CMakeLists.txt @@ -5,6 +5,7 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") add_subdirectory(FreeBSD) add_subdirectory(POSIX) elseif (CMAKE_SYSTEM_NAME MATCHES "Windows") + add_subdirectory(Windows) elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin") add_subdirectory(POSIX) add_subdirectory(MacOSX-Kernel) diff --git a/source/Plugins/Process/Windows/CMakeLists.txt b/source/Plugins/Process/Windows/CMakeLists.txt new file mode 100644 index 0000000000..f3e9326846 --- /dev/null +++ b/source/Plugins/Process/Windows/CMakeLists.txt @@ -0,0 +1,11 @@ +set(LLVM_NO_RTTI 1) + +include_directories(.) +include_directories(../Utility) + +add_lldb_library(lldbPluginProcessWindows + ProcessWindows.cpp + ThreadWindows.cpp + RegisterContextWindows_i386.cpp + RegisterContextWindowsDebug_i386.cpp + ) diff --git a/source/Plugins/Process/Windows/Makefile b/source/Plugins/Process/Windows/Makefile new file mode 100644 index 0000000000..1f39e00ad2 --- /dev/null +++ b/source/Plugins/Process/Windows/Makefile @@ -0,0 +1,14 @@ +##===- source/Plugins/Process/Windows/Makefile -------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLDB_LEVEL := ../../../.. +LIBRARYNAME := lldbPluginProcessWindows +BUILD_ARCHIVE = 1 + +include $(LLDB_LEVEL)/Makefile diff --git a/source/Plugins/Process/Windows/ProcessWindows.cpp b/source/Plugins/Process/Windows/ProcessWindows.cpp new file mode 100644 index 0000000000..fe2b7b5619 --- /dev/null +++ b/source/Plugins/Process/Windows/ProcessWindows.cpp @@ -0,0 +1,930 @@ +//===-- ProcessWindows.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +#include +#include + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Log.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlan.h" + +#include "ProcessWindows.h" +#include "ThreadWindows.h" + +using namespace lldb; +using namespace lldb_private; + +class ThreadPlanPropagatePendingMessage : public ThreadPlan +{ + ProcessMessage m_pending_message; +public: + ThreadPlanPropagatePendingMessage(Thread &thread, ProcessMessage pending_message) + : ThreadPlan(ThreadPlan::eKindGeneric, + "Windows Propagate Pending Message", + thread, + eVoteNoOpinion, + eVoteNoOpinion), + m_pending_message(pending_message) + { + } + + bool + ValidatePlan(Stream *error) + { + return true; + } + + bool + ShouldStop(Event *event_ptr) + { + SetPlanComplete(); + return true; + } + + void + GetDescription (Stream *s, lldb::DescriptionLevel level) + { + s->Printf("Propagate pending message on resume"); + } + + bool + DoPlanExplainsStop(Event *event_ptr) + { + return true; + } + + lldb::StateType + GetPlanRunState() + { + return eStateRunning; + } + + bool + WillStop() + { + ProcessWindows* process = static_cast(m_thread.GetProcess().get()); + process->SendMessage(m_pending_message); + return true; + } + + bool + DoWillResume(lldb::StateType resume_state, bool current_plan) + { + return false; + } + +private: + DISALLOW_COPY_AND_ASSIGN(ThreadPlanPropagatePendingMessage); +}; + +//------------------------------------------------------------------------------ +// Static functions. + +ProcessSP +ProcessWindows::CreateInstance(Target &target, Listener &listener, const FileSpec *) +{ + return ProcessSP(new ProcessWindows(target, listener)); +} + +void +ProcessWindows::Initialize() +{ + static bool g_initialized = false; + + if (!g_initialized) + { + g_initialized = true; + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); + } +} + +void +ProcessWindows::Terminate() +{ +} + +//------------------------------------------------------------------------------ +// ProcessInterface protocol. + +lldb_private::ConstString +ProcessWindows::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ProcessWindows::GetPluginVersion() +{ + return 1; +} + +lldb_private::ConstString +ProcessWindows::GetPluginNameStatic() +{ + static ConstString g_name("windows"); + return g_name; +} + +const char * +ProcessWindows::GetPluginDescriptionStatic() +{ + return "Process plugin for Windows"; +} + +//------------------------------------------------------------------------------ +// Constructors and destructors. + +ProcessWindows::ProcessWindows(Target& target, Listener &listener) + : Process(target, listener), + m_module(NULL), + m_message_mutex(Mutex::eMutexTypeRecursive), + m_stopping_threads(false), + m_first_breakpoint_reached(false), + m_expect_async_break(false) +{ + m_resume_event = CreateEvent(NULL, FALSE, FALSE, NULL); + m_resumed_event = CreateEvent(NULL, FALSE, FALSE, NULL); +} + +ProcessWindows::~ProcessWindows() +{ + CloseHandle(m_resume_event); + CloseHandle(m_resumed_event); +} + +//------------------------------------------------------------------------------ +// Process protocol. + +bool +ProcessWindows::CanDebug(Target &target, bool plugin_specified_by_name) +{ + return true; +} + +Error +ProcessWindows::WillLaunch(Module* module) +{ + Error error; + return error; +} + +bool +ProcessWindows::CheckPendingMessages() +{ + Error error; + + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + // Check for pending messages. + // Not sure if it's better to check during Process::WillResume() or right before ContinueDebugEvent(). + uint32_t thread_count = m_thread_list.GetSize(false); + for (uint32_t i = 0; i < thread_count; ++i) + { + ThreadWindows *thread = static_cast( + m_thread_list.GetThreadAtIndex(i, false).get()); + + auto resume_state = thread->GetResumeState(); + if ((resume_state == lldb::eStateRunning || resume_state == lldb::eStateStepping) + && thread->m_pending_message.GetKind() != ProcessMessage::eInvalidMessage) + { + // Some pending message was thrown previously but delayed until thread was officially "resumed" + if (log) + log->Printf("ThreadWindows::%s (), replaying pending event for tid = %" PRIi64, __FUNCTION__, GetID()); + ProcessMessage message = thread->m_pending_message; + thread->m_pending_message = ProcessMessage(); + SendMessage(message, true); + + // Generate a continue & a stopped event. + //SetPrivateState(eStateRunning); + //SetPrivateState(eStateStopped); + + return true; + } + } + + return false; +} + +struct LaunchArgs +{ + LaunchArgs(ProcessWindows* process, + const std::string& executable, + const char* working_dir, + const std::string& command, + Flags launchFlags) + : process(process), + executable(executable), + working_dir(strdup(working_dir)), + command(strdup(command.c_str())), + launchFlags(launchFlags), + pid(0) + { + + } + + LaunchArgs(ProcessWindows* process, + lldb::pid_t pid) + : process(process), + working_dir(NULL), + command(NULL), + pid(pid) + { + } + + ~LaunchArgs() + { + free(working_dir); + free(command); + } + + ProcessWindows* process; + std::string executable; + char* working_dir; + char* command; + Flags launchFlags; + lldb::pid_t pid; +}; + +thread_result_t +ProcessWindows::DebuggerThreadFunction (void *arg) +{ + LaunchArgs* launchArgs = (LaunchArgs*)arg; + ProcessWindows* process = launchArgs->process; + + DWORD dwProcessId = 0; + + // Starting the process (delayed until here because debugging loop need to be in the same thread) + if (launchArgs->pid != 0) + { + process->m_hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, launchArgs->pid); + dwProcessId = GetProcessId(process->m_hProcess); + DebugActiveProcess(dwProcessId); + DebugSetProcessKillOnExit(false); + } + else + { + PROCESS_INFORMATION process_information; + STARTUPINFO startupInfo; + memset(&startupInfo, 0, sizeof(startupInfo)); + startupInfo.cb = sizeof(startupInfo); + bool processCreated = CreateProcessA(launchArgs->executable.c_str(), launchArgs->command, NULL, NULL, false, DEBUG_ONLY_THIS_PROCESS, NULL, launchArgs->working_dir, &startupInfo, &process_information); + process->m_hProcess = process_information.hProcess; + dwProcessId = process_information.dwProcessId; + } + + process->SetID((user_id_t)dwProcessId); + + bool bFirstException = true; + + DEBUG_EVENT debug_event = {0}; + DWORD dwContinueStatus = DBG_CONTINUE; + bool bProcessRunning = true; + + // Main debugger loop + // Need to happen on the thread starting the process + while (bProcessRunning) + { + // Wait for next debug event + if (!WaitForDebugEvent(&debug_event, INFINITE)) + return -1; + + switch(debug_event.dwDebugEventCode) + { + case CREATE_PROCESS_DEBUG_EVENT: + { + // Update load address of main module + ModuleSP module = process->GetTarget().GetExecutableModule(); + bool loadAddrChanged; + module->SetLoadAddress(process->GetTarget(), (addr_t) debug_event.u.CreateProcessInfo.lpBaseOfImage, false, loadAddrChanged); + + // Add main thread + if (debug_event.u.CreateProcessInfo.hThread != NULL) + { + ThreadSP thread_sp; + thread_sp.reset(new ThreadWindows(*process, debug_event.u.CreateProcessInfo.hThread)); + + process->m_thread_list.AddThread(thread_sp); + } + + CloseHandle(debug_event.u.CreateProcessInfo.hFile); + + //process->SetPublicState(lldb::eStateStopped); + } + break; + case CREATE_THREAD_DEBUG_EVENT: + { + int suspendCount = SuspendThread(debug_event.u.CreateThread.hThread); + process->SendMessage(ProcessMessage::NewThread((tid_t) debug_event.dwThreadId, (tid_t) debug_event.u.CreateThread.hThread)); + } + break; + case EXIT_THREAD_DEBUG_EVENT: + process->SendMessage(ProcessMessage::Exit((tid_t)debug_event.dwThreadId, debug_event.u.ExitThread.dwExitCode)); + break; + case EXIT_PROCESS_DEBUG_EVENT: + bProcessRunning = false; + break; + case EXCEPTION_DEBUG_EVENT: + switch(debug_event.u.Exception.ExceptionRecord.ExceptionCode) + { + case EXCEPTION_BREAKPOINT: + // Ignore first chance exceptions + //if (debug_event.u.Exception.dwFirstChance == 0 || bFirstException) + { + bFirstException = false; + process->SendMessage(ProcessMessage::Break((tid_t)debug_event.dwThreadId)); + } + break; + case EXCEPTION_SINGLE_STEP: + process->SendMessage(ProcessMessage::Trace((tid_t)debug_event.dwThreadId)); + break; + } + break; + case LOAD_DLL_DEBUG_EVENT: + { + char pathBuffer[MAX_PATH]; + GetFinalPathNameByHandle(debug_event.u.LoadDll.hFile, pathBuffer, MAX_PATH, VOLUME_NAME_DOS); + CloseHandle(debug_event.u.LoadDll.hFile); + + char* path = pathBuffer; + // Not sure why URL starts with \\?\, so let's skip it. + if (strncmp(path, "\\\\?\\", 4) == 0) + path += 4; + + FileSpec file_spec(path, false); + ModuleSpec module_spec(file_spec); + ModuleSP module = process->GetTarget().GetSharedModule(module_spec); + bool loadAddrChanged; + module->SetLoadAddress(process->GetTarget(), (addr_t)debug_event.u.LoadDll.lpBaseOfDll, false, loadAddrChanged); + } + break; + } + + // Process messages + while (process->CheckPendingMessages()) + { + } + + // Continue execution + ContinueDebugEvent(debug_event.dwProcessId, + debug_event.dwThreadId, + dwContinueStatus); + } + + return 0; +} + +void +ProcessWindows::StopAllThreads(lldb::tid_t stop_tid) +{ + // If a breakpoint occurs while we're stopping threads, we'll get back + // here, but we don't want to do it again. Only the MonitorChildProcess + // thread calls this function, so we don't need to protect this flag. + if (m_stopping_threads) + return; + m_stopping_threads = true; + + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("ProcessWindows::%s() stopping all threads", __FUNCTION__); + + // Walk the thread list and stop the other threads. The thread that caused + // the stop should already be marked as stopped before we get here. + Mutex::Locker thread_list_lock(m_thread_list.GetMutex()); + + uint32_t thread_count = m_thread_list.GetSize(false); + for (uint32_t i = 0; i < thread_count; ++i) + { + ThreadWindows *thread = static_cast( + m_thread_list.GetThreadAtIndex(i, false).get()); + assert(thread); + lldb::tid_t tid = thread->GetID(); + if (!StateIsStoppedState(thread->GetState(), false)) + SuspendThread(thread->GetHandle()); + } + + m_stopping_threads = false; + + if (log) + log->Printf ("ProcessLinux::%s() finished", __FUNCTION__); +} + +void +ProcessWindows::SendMessage(const ProcessMessage &message, bool wait_for_resume) +{ + Mutex::Locker lock(m_message_mutex); + + ThreadWindows *thread = static_cast( + m_thread_list.FindThreadByID(message.GetTID(), false).get()); + + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + if (log) + log->Printf("ThreadWindows::%s () tid = %" PRIi64 " Processing message %" PRIi32, __FUNCTION__, message.GetTID(), (uint32_t)message.GetKind()); + + // Process and dispatch messages + switch (message.GetKind()) + { + case ProcessMessage::eInvalidMessage: + return; + + case ProcessMessage::eExitMessage: + if (thread) + thread->SetState(eStateExited); + break; + case ProcessMessage::eTraceMessage: + case ProcessMessage::eBreakpointMessage: + case ProcessMessage::eNewThreadMessage: + case ProcessMessage::eCrashMessage: + if (message.GetKind() == ProcessMessage::eBreakpointMessage && thread != NULL) + { + // In case of known breakpoint, instruction has already been executed so we need to go back one byte, + // so that it can later be replaced with real code through ThreadPlanStepOverBreakpoint. + + // Get PC + lldb::addr_t pc = thread->GetRegisterContext()->GetPC(); + + if (log) + log->Printf("ThreadWindows::%s () tid = %" PRIi64 " Breakpoint at PC=0x%8.8" PRIx64, __FUNCTION__, thread->GetID(), pc); + + if (thread->GetResumeState() == eStateSuspended + || thread->GetTemporaryResumeState() == eStateSuspended) + { + // Thread is suspended so we delay this event propagation until thread is resumed. + // This happens when two threads A and B reach a breakpoint, thread A is processed first and asked to step over (with thread B suspended). + // Still thread B will throw the breakpoint event right after ContinueDebugEvent is called, event if it has been suspended. + thread->m_pending_message = message; + //ThreadPlanSP thread_plan_sp; + //thread_plan_sp.reset(new ThreadPlanPropagatePendingMessage(*thread, message)); + //thread->QueueThreadPlan(thread_plan_sp, false); + + if (log) + log->Printf("ThreadWindows::%s () Added pending breakpoint for tid = %" PRIi64 " at PC = 0x%8.8" PRIx64, __FUNCTION__, thread->GetID(), pc); + + return; + } + } + + // If currently in a DebugBreakProcess sequence, ignore NewThreadMessage otherwise Process::Halt will interpret the thread creation as the actual stop. + // Another way would be to wait in a loop until we reach an unexpected breakpoint. + if (m_expect_async_break && message.GetKind() == ProcessMessage::eNewThreadMessage) + { + m_message_queue.push(message); + ResumeThread((HANDLE)message.GetChildTID()); + m_expect_async_break = false; + return; + } + + if (thread) + thread->SetState(eStateStopped); + + // Stop all threads + StopAllThreads(message.GetTID()); + + { + // Send stopped message + lldb::StateType private_state = GetPrivateState(); + SetPrivateState(eStateStopped); + m_message_queue.push(message); + lock.Unlock(); + + + // Wait until we are told to resume + if (wait_for_resume) + WaitForSingleObject(m_resume_event, INFINITE); + } + return; + default: + return; + } + + m_message_queue.push(message); +} + +Error +ProcessWindows::DoAttachToProcessWithID(lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info) +{ + Error error; + + LaunchArgs* launchArgs = new LaunchArgs(this, pid); + + // Start debugger thread + Host::ThreadCreate("debugger", + DebuggerThreadFunction, + launchArgs, + NULL); + + // Initial state is stopped + SetPublicState(eStateStopped, false); + + return error; +} + +Error +ProcessWindows::DoLaunch (Module *module, + ProcessLaunchInfo &launch_info) +{ + Error error; + + SetPrivateState(eStateLaunching); + + const char* working_dir = launch_info.GetWorkingDirectory(); + + std::string command; + if (!launch_info.GetArguments().GetCommandString(command)) + return error; + + LaunchArgs* launchArgs = new LaunchArgs(this, launch_info.GetExecutableFile().GetPath(), working_dir, command, launch_info.GetFlags()); + + // Start debugger thread + Host::ThreadCreate("debugger", + DebuggerThreadFunction, + launchArgs, + NULL); + + // Initial state is stopped + SetPublicState(eStateStopped, false); + + return error; +} + +void +ProcessWindows::DidLaunch() +{ +} + +Error +ProcessWindows::DoResume() +{ + Error error; + + StateType state = GetPrivateState(); + + assert(state == eStateStopped || state == eStateCrashed); + + // Update private state + if (state == eStateStopped) + { + SetPrivateState(eStateRunning); + } + + // Resume threads + bool did_resume = false; + uint32_t thread_count = m_thread_list.GetSize(false); + for (uint32_t i = 0; i < thread_count; ++i) + { + ThreadWindows *thread = static_cast( + m_thread_list.GetThreadAtIndex(i, false).get()); + did_resume = thread->Resume() || did_resume; + } + assert(did_resume && "Process resume failed!"); + + // Notify debugger loop to continue + SetEvent(m_resume_event); + + return error; +} + +addr_t +ProcessWindows::GetImageInfoAddress() +{ + Target *target = &GetTarget(); + ObjectFile *obj_file = target->GetExecutableModule()->GetObjectFile(); + Address addr = obj_file->GetImageInfoAddress(target); + + if (addr.IsValid()) + return addr.GetLoadAddress(target); + else + return LLDB_INVALID_ADDRESS; +} + +Error +ProcessWindows::DoHalt(bool &caused_stop) +{ + Error error; + + StateType state = GetPrivateState(); + + if (state == eStateStopped) + { + caused_stop = false; + } + else + { + m_expect_async_break = true; + + caused_stop = DebugBreakProcess(m_hProcess); + if (!caused_stop) + return error; + + assert(caused_stop && "Process halt failed!"); + } + return error; +} + +Error +ProcessWindows::DoDetach(bool keep_stopped) +{ + Error error; + return error; +} + +Error +ProcessWindows::DoSignal(int signal) +{ + Error error; + return error; +} + +Error +ProcessWindows::DoDestroy() +{ + Error error; + + if (GetPrivateState() != eStateDetached && GetPrivateState() == eStateExited) + { + if (::TerminateProcess(m_hProcess, 0) != S_OK) + { + error.SetErrorToErrno(); + return error; + } + + SetPrivateState(eStateExited); + } + + return error; +} + +void +ProcessWindows::RefreshStateAfterStop() +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log && log->GetMask().Test(LIBLLDB_LOG_VERBOSE)) + log->Printf ("ProcessWindows::%s(), message_queue size = %d", __FUNCTION__, (int)m_message_queue.size()); + + Mutex::Locker lock(m_message_mutex); + + while (!m_message_queue.empty()) + { + ProcessMessage &message = m_message_queue.front(); + + lldb::tid_t tid = message.GetTID(); + + ThreadWindows *thread = static_cast( + GetThreadList().FindThreadByID(tid, false).get()); + + if (message.GetKind() == ProcessMessage::eNewThreadMessage) + { + if (log) + log->Printf ("ProcessWindows::%s() adding thread, tid = %" PRIi64, __FUNCTION__, message.GetChildTID()); + ThreadSP thread_sp; + HANDLE hThread = (HANDLE)message.GetChildTID(); + thread_sp.reset(thread = new ThreadWindows(*this, hThread)); + m_thread_list.AddThread(thread_sp); + } + + m_thread_list.RefreshStateAfterStop(); + + // Restore all threads to the real breakpoint location (if there is a breakpoint site) + uint32_t thread_count = m_thread_list.GetSize(false); + for (uint32_t i = 0; i < thread_count; ++i) + { + ThreadWindows *thread = static_cast( + m_thread_list.GetThreadAtIndex(i, false).get()); + + // Read context + lldb::addr_t pc = thread->GetRegisterContext()->GetPC(); + + // Adjust PC to where breakpoint was + pc--; + + BreakpointSiteSP bp_site_sp = GetBreakpointSiteList().FindByAddress(pc); + if (bp_site_sp) + { + if (log) + log->Printf("ThreadWindows::%s () Assigning breakpoint for tid = %" PRIi64 " to PC = 0x%8.8" PRIx64, __FUNCTION__, thread->GetID(), pc); + + thread->m_breakpoint = bp_site_sp; + } + } + + if (thread) + thread->Notify(message); + + if (message.GetKind() == ProcessMessage::eExitMessage) + { + // FIXME: We should tell the user about this, but the limbo message is probably better for that. + if (log) + log->Printf ("ProcessWindows::%s() removing thread, tid = %" PRIi64, __FUNCTION__, tid); + ThreadSP thread_sp = m_thread_list.RemoveThreadByID(tid, false); + thread_sp->SetState(eStateExited); + thread_sp.reset(); + } + + + m_message_queue.pop(); + } +} + +bool +ProcessWindows::IsAlive() +{ + StateType state = GetPrivateState(); + return state != eStateDetached + && state != eStateExited + && state != eStateInvalid + && state != eStateUnloaded; +} + +size_t +ProcessWindows::DoReadMemory(addr_t vm_addr, + void *buf, size_t size, Error &error) +{ + SIZE_T numberOfBytesRead; + ReadProcessMemory(m_hProcess, (void*) vm_addr, buf, size, &numberOfBytesRead); + return numberOfBytesRead; +} + +size_t +ProcessWindows::DoWriteMemory(addr_t vm_addr, const void *buf, size_t size, + Error &error) +{ + SIZE_T numberOfBytesWritten; + WriteProcessMemory(m_hProcess, (void*) vm_addr, buf, size, &numberOfBytesWritten); + FlushInstructionCache(m_hProcess, (void*) vm_addr, size); + return numberOfBytesWritten; +} + +addr_t +ProcessWindows::DoAllocateMemory(size_t size, uint32_t permissions, + Error &error) +{ + DWORD prot; + bool readable = (permissions & lldb::ePermissionsReadable); + bool writable = (permissions & lldb::ePermissionsWritable); + bool executable = (permissions & lldb::ePermissionsExecutable); + + // Compute memory protection flags + if (readable) + { + if (executable) + { + prot = writable ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ; + } + else + { + prot = writable ? PAGE_READWRITE : PAGE_READONLY; + } + } + else + { + assert(!writable); + prot = executable ? PAGE_EXECUTE : PAGE_NOACCESS; + } + + // Allocate + return (addr_t)VirtualAllocEx(m_hProcess, NULL, size, MEM_COMMIT | MEM_RESERVE, prot); +} + +Error +ProcessWindows::DoDeallocateMemory(lldb::addr_t addr) +{ + Error error; + + // Deallocate + VirtualFreeEx(m_hProcess, (LPVOID) addr, 0, MEM_RELEASE); + return error; +} + +addr_t +ProcessWindows::ResolveIndirectFunction(const Address *address, Error &error) +{ + addr_t function_addr = LLDB_INVALID_ADDRESS; + return function_addr; +} + +size_t +ProcessWindows::GetSoftwareBreakpointTrapOpcode(BreakpointSite* bp_site) +{ + static const uint8_t g_i386_opcode[] = { 0xCC }; + + ArchSpec arch = GetTarget().GetArchitecture(); + const uint8_t *opcode = NULL; + size_t opcode_size = 0; + + switch (arch.GetMachine()) + { + default: + assert(false && "CPU type not supported!"); + break; + + case llvm::Triple::x86: + case llvm::Triple::x86_64: + opcode = g_i386_opcode; + opcode_size = sizeof(g_i386_opcode); + break; + } + + bp_site->SetTrapOpcode(opcode, opcode_size); + return opcode_size; +} + +Error +ProcessWindows::EnableBreakpointSite(BreakpointSite *bp_site) +{ + return EnableSoftwareBreakpoint(bp_site); +} + +Error +ProcessWindows::DisableBreakpointSite(BreakpointSite *bp_site) +{ + return DisableSoftwareBreakpoint(bp_site); +} + +Error +ProcessWindows::EnableWatchpoint(Watchpoint *wp, bool notify) +{ + Error error; + return error; +} + +Error +ProcessWindows::DisableWatchpoint(Watchpoint *wp, bool notify) +{ + Error error; + return error; +} + +Error +ProcessWindows::GetWatchpointSupportInfo(uint32_t &num) +{ + Error error; + return error; +} + +Error +ProcessWindows::GetWatchpointSupportInfo(uint32_t &num, bool &after) +{ + Error error = GetWatchpointSupportInfo(num); + // Watchpoints trigger and halt the inferior after + // the corresponding instruction has been executed. + after = true; + return error; +} + +uint32_t +ProcessWindows::UpdateThreadListIfNeeded() +{ + // Do not allow recursive updates. + return m_thread_list.GetSize(false); +} + +bool +ProcessWindows::UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) +{ + new_thread_list = old_thread_list; + return new_thread_list.GetSize(false) > 0; +} + +ByteOrder +ProcessWindows::GetByteOrder() const +{ + // FIXME: We should be able to extract this value directly. See comment in + // ProcessWindows(). + return eByteOrderLittle; +} + +size_t +ProcessWindows::PutSTDIN(const char *buf, size_t len, Error &error) +{ + return 0; +} + +lldb_private::DynamicLoader * +ProcessWindows::GetDynamicLoader () +{ + if (m_dyld_ap.get() == NULL) + m_dyld_ap.reset (DynamicLoader::FindPlugin(this, NULL)); + return m_dyld_ap.get(); +} diff --git a/source/Plugins/Process/Windows/ProcessWindows.h b/source/Plugins/Process/Windows/ProcessWindows.h new file mode 100644 index 0000000000..e280b5280d --- /dev/null +++ b/source/Plugins/Process/Windows/ProcessWindows.h @@ -0,0 +1,202 @@ +//===-- ProcessWindows.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_ProcessWindows_H_ +#define liblldb_ProcessWindows_H_ + +// C Includes + +// C++ Includes +#include + +// Other libraries and framework includes +#include "lldb/Target/Process.h" +#include "lldb/Host/windows/windows.h" +#include "../POSIX/ProcessMessage.h" + +class ProcessWindows : + public lldb_private::Process +{ +public: + //------------------------------------------------------------------ + // Static functions. + //------------------------------------------------------------------ + static lldb::ProcessSP + CreateInstance(lldb_private::Target& target, + lldb_private::Listener &listener, + const lldb_private::FileSpec *); + + static void + Initialize(); + + static void + Terminate(); + + static lldb_private::ConstString + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + //------------------------------------------------------------------ + // Constructors and destructors + //------------------------------------------------------------------ + ProcessWindows(lldb_private::Target& target, + lldb_private::Listener &listener); + + virtual + ~ProcessWindows(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + + //------------------------------------------------------------------ + // Process protocol. + //------------------------------------------------------------------ + virtual bool + CanDebug(lldb_private::Target &target, bool plugin_specified_by_name); + + virtual lldb_private::Error + WillLaunch(lldb_private::Module *module); + + virtual bool + CheckPendingMessages(); + + virtual bool + DestroyRequiresHalt() { return false; } + + //virtual lldb_private::Error + //DoAttachToProcessWithID(lldb::pid_t pid); + + virtual lldb_private::Error + DoAttachToProcessWithID (lldb::pid_t pid, const lldb_private::ProcessAttachInfo &attach_info); + + virtual lldb_private::Error + DoLaunch (lldb_private::Module *exe_module, + lldb_private::ProcessLaunchInfo &launch_info); + + virtual void + DidLaunch(); + + virtual lldb_private::Error + DoResume(); + + virtual lldb_private::Error + DoHalt(bool &caused_stop); + + virtual lldb_private::Error + DoDetach(bool keep_stopped); + + virtual lldb_private::Error + DoSignal(int signal); + + virtual lldb_private::Error + DoDestroy(); + + virtual void + RefreshStateAfterStop(); + + virtual bool + IsAlive(); + + virtual size_t + DoReadMemory(lldb::addr_t vm_addr, + void *buf, + size_t size, + lldb_private::Error &error); + + virtual size_t + DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size, + lldb_private::Error &error); + + virtual lldb::addr_t + DoAllocateMemory(size_t size, uint32_t permissions, + lldb_private::Error &error); + + virtual lldb_private::Error + DoDeallocateMemory(lldb::addr_t ptr); + + virtual lldb::addr_t + ResolveIndirectFunction(const lldb_private::Address *address, lldb_private::Error &error); + + virtual size_t + GetSoftwareBreakpointTrapOpcode(lldb_private::BreakpointSite* bp_site); + + virtual lldb_private::Error + EnableBreakpointSite(lldb_private::BreakpointSite *bp_site); + + virtual lldb_private::Error + DisableBreakpointSite(lldb_private::BreakpointSite *bp_site); + + virtual lldb_private::Error + EnableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true); + + virtual lldb_private::Error + DisableWatchpoint(lldb_private::Watchpoint *wp, bool notify = true); + + virtual lldb_private::Error + GetWatchpointSupportInfo(uint32_t &num); + + virtual lldb_private::Error + GetWatchpointSupportInfo(uint32_t &num, bool &after); + + virtual uint32_t + UpdateThreadListIfNeeded(); + + virtual bool + UpdateThreadList(lldb_private::ThreadList &old_thread_list, + lldb_private::ThreadList &new_thread_list); + + virtual lldb::ByteOrder + GetByteOrder() const; + + virtual lldb::addr_t + GetImageInfoAddress(); + + virtual size_t + PutSTDIN(const char *buf, size_t len, lldb_private::Error &error); + + static lldb::thread_result_t + DebuggerThreadFunction (void *arg); + + void + SendMessage(const ProcessMessage &message, bool wait_for_resume = true); + + /// Stops all threads in the process. + /// The \p stop_tid parameter indicates the thread which initiated the stop. + virtual void + StopAllThreads(lldb::tid_t stop_tid); + + virtual lldb_private::DynamicLoader * + GetDynamicLoader (); + +protected: + /// The module we are executing. + lldb_private::Module *m_module; + + lldb_private::Mutex m_message_mutex; + std::queue m_message_queue; + + HANDLE m_hProcess; + + HANDLE m_resume_event; + HANDLE m_resumed_event; + + bool m_stopping_threads; + bool m_first_breakpoint_reached; + bool m_expect_async_break; +}; + +#endif // liblldb_MacOSXProcess_H_ diff --git a/source/Plugins/Process/Windows/RegisterContextWindowsDebug_i386.cpp b/source/Plugins/Process/Windows/RegisterContextWindowsDebug_i386.cpp new file mode 100644 index 0000000000..268a38f483 --- /dev/null +++ b/source/Plugins/Process/Windows/RegisterContextWindowsDebug_i386.cpp @@ -0,0 +1,356 @@ +//===-- RegisterContextWindowsDebug_i386.cpp --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Process.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/windows/windows.h" + +#include "RegisterContextPOSIX_x86.h" +#include "RegisterContextWindowsDebug_i386.h" + +#include "ThreadWindows.h" + +using namespace lldb_private; +using namespace lldb; + +static uint32_t +size_and_rw_bits(size_t size, bool read, bool write) +{ + uint32_t rw; + + if (read) + rw = 0x3; // READ or READ/WRITE + else if (write) + rw = 0x1; // WRITE + else + assert(0 && "read and write cannot both be false"); + + switch (size) + { + case 1: + return rw; + case 2: + return (0x1 << 2) | rw; + case 4: + return (0x3 << 2) | rw; + case 8: + return (0x2 << 2) | rw; + default: + assert(0 && "invalid size, must be one of 1, 2, 4, or 8"); + } +} + +RegisterContextWindowsDebug_i386::RegisterContextWindowsDebug_i386(Thread &thread, + uint32_t concrete_frame_idx, + RegisterInfoInterface *register_info) + : RegisterContextPOSIX_x86(thread, concrete_frame_idx, register_info) +{ + context.ContextFlags = CONTEXT_FULL; +} + +RegisterContextWindowsDebug_i386::~RegisterContextWindowsDebug_i386() +{ +} + +bool +RegisterContextWindowsDebug_i386::ReadRegister(const unsigned reg, RegisterValue &value) +{ + return false; +} + +bool +RegisterContextWindowsDebug_i386::ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) +{ + // For now it just fetch full context for every register + // TODO: Optimized version, only fetch and set what's necessary, when necessary + ThreadWindows& thread = static_cast(m_thread); + if (!GetThreadContext(thread.GetHandle(), &context)) + return false; + + switch (reg_info->byte_size) + { + case 4: + value = *(uint32_t *) ((uint8_t*)&context + reg_info->byte_offset); + return true; + case 8: + value = *(uint64_t *) ((uint8_t*)&context + reg_info->byte_offset); + return true; + } + return false; +} + +bool +RegisterContextWindowsDebug_i386::ReadAllRegisterValues(DataBufferSP &data_sp) +{ + return false; +} + +bool +RegisterContextWindowsDebug_i386::WriteRegister(const unsigned reg, const RegisterValue &value) +{ + return false; +} + +bool RegisterContextWindowsDebug_i386::WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) +{ + // For now it just fetch full context for every register + // TODO: Optimized version, only fetch and set what's necessary, when necessary + ThreadWindows& thread = static_cast(m_thread); + if (!GetThreadContext(thread.GetHandle(), &context)) + return false; + + switch (reg_info->byte_size) + { + case 4: + *(uint32_t *) ((uint8_t*)&context + reg_info->byte_offset) = value.GetAsUInt32(); + case 8: + *(uint64_t *) ((uint8_t*)&context + reg_info->byte_offset) = value.GetAsUInt64(); + } + + if (!SetThreadContext(thread.GetHandle(), &context)) + return false; + + return true; +} + +bool +RegisterContextWindowsDebug_i386::UpdateAfterBreakpoint() +{ + // PC points one byte past the int3 responsible for the breakpoint. + lldb::addr_t pc; + + if ((pc = GetPC()) == LLDB_INVALID_ADDRESS) + return false; + + // Only reset PC to int3 address if it's a known LLDB breakpoint site. + // User or system (DebugBreakProcess) int3 should be kept as is. + if (m_thread.GetProcess()->GetBreakpointSiteList().FindByAddress(pc - 1) + || (((ThreadWindows&)m_thread).m_breakpoint && ((ThreadWindows&)m_thread).m_breakpoint->GetLoadAddress() == pc - 1)) + SetPC(pc - 1); + return true; +} + +bool +RegisterContextWindowsDebug_i386::HardwareSingleStep(bool enable) +{ + enum { TRACE_BIT = 0x100 }; + uint64_t eflags; + + if ((eflags = ReadRegisterAsUnsigned(gpr_eflags_i386, -1UL)) == -1UL) + return false; + + if (enable) + { + if (eflags & TRACE_BIT) + return true; + + eflags |= TRACE_BIT; + } + else + { + if (!(eflags & TRACE_BIT)) + return false; + + eflags &= ~TRACE_BIT; + } + + return WriteRegisterFromUnsigned(gpr_eflags_i386, eflags); +} + +bool +RegisterContextWindowsDebug_i386::ReadGPR() +{ + GetThreadContext((HANDLE)m_thread.GetID(), &context); + + return true; +} + +bool +RegisterContextWindowsDebug_i386::ReadFPR() +{ + return false; +} + +bool +RegisterContextWindowsDebug_i386::WriteGPR() +{ + return false; +} + +bool +RegisterContextWindowsDebug_i386::WriteFPR() +{ + return false; +} + +unsigned +RegisterContextWindowsDebug_i386::GetRegisterIndexFromOffset(unsigned offset) +{ + unsigned reg; + for (reg = 0; reg < m_reg_info.num_registers; reg++) + { + if (GetRegisterInfo()[reg].byte_offset == offset) + break; + } + assert(reg < m_reg_info.num_registers && "Invalid register offset."); + return reg; +} + +bool +RegisterContextWindowsDebug_i386::IsWatchpointHit(uint32_t hw_index) +{ + bool is_hit = false; + + if (m_watchpoints_initialized == false) + { + // Reset the debug status and debug control registers + RegisterValue zero_bits = RegisterValue(uint64_t(0)); + if (!WriteRegister(m_reg_info.first_dr + 6, zero_bits) || !WriteRegister(m_reg_info.first_dr + 7, zero_bits)) + assert(false && "Could not initialize watchpoint registers"); + m_watchpoints_initialized = true; + } + + if (hw_index < NumSupportedHardwareWatchpoints()) + { + RegisterValue value; + + if (ReadRegister(m_reg_info.first_dr + 6, value)) + { + uint64_t val = value.GetAsUInt64(); + is_hit = val & (1 << hw_index); + } + } + + return is_hit; +} + +bool +RegisterContextWindowsDebug_i386::ClearWatchpointHits() +{ + return WriteRegister(m_reg_info.first_dr + 6, RegisterValue((uint64_t)0)); +} + +addr_t +RegisterContextWindowsDebug_i386::GetWatchpointAddress(uint32_t hw_index) +{ + addr_t wp_monitor_addr = LLDB_INVALID_ADDRESS; + + if (hw_index < NumSupportedHardwareWatchpoints()) + { + if (!IsWatchpointVacant(hw_index)) + { + RegisterValue value; + + if (ReadRegister(m_reg_info.first_dr + hw_index, value)) + wp_monitor_addr = value.GetAsUInt64(); + } + } + + return wp_monitor_addr; +} + +bool +RegisterContextWindowsDebug_i386::IsWatchpointVacant(uint32_t hw_index) +{ + bool is_vacant = false; + RegisterValue value; + + assert(hw_index < NumSupportedHardwareWatchpoints()); + + if (m_watchpoints_initialized == false) + { + // Reset the debug status and debug control registers + RegisterValue zero_bits = RegisterValue(uint64_t(0)); + if (!WriteRegister(m_reg_info.first_dr + 6, zero_bits) || !WriteRegister(m_reg_info.first_dr + 7, zero_bits)) + assert(false && "Could not initialize watchpoint registers"); + m_watchpoints_initialized = true; + } + + if (ReadRegister(m_reg_info.first_dr + 7, value)) + { + uint64_t val = value.GetAsUInt64(); + is_vacant = (val & (3 << 2*hw_index)) == 0; + } + + return is_vacant; +} + +bool +RegisterContextWindowsDebug_i386::SetHardwareWatchpointWithIndex(addr_t addr, size_t size, + bool read, bool write, + uint32_t hw_index) +{ + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + + if (num_hw_watchpoints == 0 || hw_index >= num_hw_watchpoints) + return false; + + if (!(size == 1 || size == 2 || size == 4 || size == 8)) + return false; + + if (read == false && write == false) + return false; + + if (!IsWatchpointVacant(hw_index)) + return false; + + // Set both dr7 (debug control register) and dri (debug address register). + + // dr7{7-0} encodes the local/global enable bits: + // global enable --. .-- local enable + // | | + // v v + // dr0 -> bits{1-0} + // dr1 -> bits{3-2} + // dr2 -> bits{5-4} + // dr3 -> bits{7-6} + // + // dr7{31-16} encodes the rw/len bits: + // b_x+3, b_x+2, b_x+1, b_x + // where bits{x+1, x} => rw + // 0b00: execute, 0b01: write, 0b11: read-or-write, + // 0b10: io read-or-write (unused) + // and bits{x+3, x+2} => len + // 0b00: 1-byte, 0b01: 2-byte, 0b11: 4-byte, 0b10: 8-byte + // + // dr0 -> bits{19-16} + // dr1 -> bits{23-20} + // dr2 -> bits{27-24} + // dr3 -> bits{31-28} + if (hw_index < num_hw_watchpoints) + { + RegisterValue current_dr7_bits; + + if (ReadRegister(m_reg_info.first_dr + 7, current_dr7_bits)) + { + uint64_t new_dr7_bits = current_dr7_bits.GetAsUInt64() | + (1 << (2*hw_index) | + size_and_rw_bits(size, read, write) << + (16+4*hw_index)); + + if (WriteRegister(m_reg_info.first_dr + hw_index, RegisterValue(addr)) && + WriteRegister(m_reg_info.first_dr + 7, RegisterValue(new_dr7_bits))) + return true; + } + } + + return false; +} + +uint32_t +RegisterContextWindowsDebug_i386::NumSupportedHardwareWatchpoints() +{ + // Available debug address registers: dr0, dr1, dr2, dr3 + return 4; +} diff --git a/source/Plugins/Process/Windows/RegisterContextWindowsDebug_i386.h b/source/Plugins/Process/Windows/RegisterContextWindowsDebug_i386.h new file mode 100644 index 0000000000..7b346def89 --- /dev/null +++ b/source/Plugins/Process/Windows/RegisterContextWindowsDebug_i386.h @@ -0,0 +1,90 @@ +//===-- RegisterContextWindowsDebug_i386.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_RegisterContextDebug_i386_h_ +#define liblldb_RegisterContextDebug_i386_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Log.h" +#include "RegisterContextPOSIX_x86.h" + +class RegisterContextWindowsDebug_i386 : public RegisterContextPOSIX_x86, public POSIXBreakpointProtocol +{ +public: + RegisterContextWindowsDebug_i386(lldb_private::Thread &thread, + uint32_t concreate_frame_idx, + RegisterInfoInterface *register_info); + + ~RegisterContextWindowsDebug_i386(); + + // lldb_private::RegisterContext + bool + ReadRegister(const unsigned reg, lldb_private::RegisterValue &value); + + bool + WriteRegister(const unsigned reg, const lldb_private::RegisterValue &value); + + bool + ReadRegister(const lldb_private::RegisterInfo *reg_info, lldb_private::RegisterValue &value); + + bool + WriteRegister(const lldb_private::RegisterInfo *reg_info, const lldb_private::RegisterValue &value); + + bool + ReadAllRegisterValues(lldb::DataBufferSP &data_sp); + + bool + HardwareSingleStep(bool enable); + +protected: + bool + ReadGPR(); + + bool + ReadFPR(); + + bool + WriteGPR(); + + bool + WriteFPR(); + + // POSIXBreakpointProtocol + bool + UpdateAfterBreakpoint(); + + unsigned + GetRegisterIndexFromOffset(unsigned offset); + + bool + IsWatchpointHit(uint32_t hw_index); + + bool + ClearWatchpointHits(); + + lldb::addr_t + GetWatchpointAddress(uint32_t hw_index); + + bool + IsWatchpointVacant(uint32_t hw_index); + + bool + SetHardwareWatchpointWithIndex(lldb::addr_t addr, size_t size, bool read, bool write, uint32_t hw_index); + + uint32_t + NumSupportedHardwareWatchpoints(); + +private: + CONTEXT context; +}; + +#endif // #ifndef liblldb_RegisterContext_i386_h_ diff --git a/source/Plugins/Process/Windows/RegisterContextWindows_i386.cpp b/source/Plugins/Process/Windows/RegisterContextWindows_i386.cpp new file mode 100644 index 0000000000..56f5646f46 --- /dev/null +++ b/source/Plugins/Process/Windows/RegisterContextWindows_i386.cpp @@ -0,0 +1,99 @@ +//===-- RegisterContextWindows_i386.cpp -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Process.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/windows/windows.h" + +#include "RegisterContextPOSIX_x86.h" +#include "RegisterContextWindows_i386.h" + +#include "ThreadWindows.h" + +using namespace lldb_private; +using namespace lldb; + +struct GPR +{ + DWORD ContextFlags; + + // CONTEXT_DEBUG_REGISTERS + // No dr4 and dr5, they map to dr6 and dr7 + DWORD dr[6]; + + // CONTEXT_FLOATING_POINT + FLOATING_SAVE_AREA FloatSave; + + // CONTEXT_SEGMENTS + DWORD gs; + DWORD fs; + DWORD es; + DWORD ds; + + // CONTEXT_INTEGER + DWORD edi; + DWORD esi; + DWORD ebx; + DWORD edx; + DWORD ecx; + DWORD eax; + + // CONTEXT_CONTROL + DWORD ebp; + DWORD eip; + DWORD cs; // MUST BE SANITIZED + DWORD eflags; // MUST BE SANITIZED + DWORD esp; + DWORD ss; + + // CONTEXT_EXTENDED_REGISTERS + BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; +}; + +#define DR_SIZE sizeof(DWORD) +#define DR_OFFSET(reg_index) \ + (LLVM_EXTENSION offsetof(GPR, dr[reg_index > 6 ? reg_index - 2 : reg_index])) + +//--------------------------------------------------------------------------- +// Include RegisterInfos_i386 to declare our g_register_infos_i386 structure. +//--------------------------------------------------------------------------- +#define DECLARE_REGISTER_INFOS_I386_STRUCT +#include "RegisterInfos_i386.h" +#undef DECLARE_REGISTER_INFOS_I386_STRUCT + +RegisterContextWindows_i386::RegisterContextWindows_i386(const ArchSpec &target_arch) : + RegisterInfoInterface(target_arch) +{ +} + +RegisterContextWindows_i386::~RegisterContextWindows_i386() +{ +} + +size_t +RegisterContextWindows_i386::GetGPRSize() +{ + return sizeof(GPR); +} + +const RegisterInfo * +RegisterContextWindows_i386::GetRegisterInfo() +{ + switch (m_target_arch.GetMachine()) + { + case llvm::Triple::x86: + return g_register_infos_i386; + default: + assert(false && "Unhandled target architecture."); + return NULL; + } +} diff --git a/source/Plugins/Process/Windows/RegisterContextWindows_i386.h b/source/Plugins/Process/Windows/RegisterContextWindows_i386.h new file mode 100644 index 0000000000..96b137f264 --- /dev/null +++ b/source/Plugins/Process/Windows/RegisterContextWindows_i386.h @@ -0,0 +1,34 @@ +//===-- RegisterContextWindows_i386.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_RegisterContext_i386_h_ +#define liblldb_RegisterContext_i386_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Log.h" +#include "RegisterContextPOSIX_x86.h" + +class RegisterContextWindows_i386 + : public RegisterInfoInterface +{ +public: + RegisterContextWindows_i386(const lldb_private::ArchSpec &target_arch); + virtual ~RegisterContextWindows_i386(); + + size_t + GetGPRSize(); + + const lldb_private::RegisterInfo * + GetRegisterInfo(); +}; + +#endif // #ifndef liblldb_RegisterContext_i386_h_ diff --git a/source/Plugins/Process/Windows/ThreadWindows.cpp b/source/Plugins/Process/Windows/ThreadWindows.cpp new file mode 100644 index 0000000000..879ef3c051 --- /dev/null +++ b/source/Plugins/Process/Windows/ThreadWindows.cpp @@ -0,0 +1,408 @@ +//===-- ThreadWindows.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +#include +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Breakpoint/BreakpointSite.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/State.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" +#include "ThreadWindows.h" +#include "ProcessWindows.h" +#include "RegisterContextWindows_i386.h" +#include "RegisterContextWindowsDebug_i386.h" + +#include "UnwindLLDB.h" + +using namespace lldb; +using namespace lldb_private; + +class WindowsStopInfo + : public lldb_private::StopInfo +{ +public: + WindowsStopInfo(lldb_private::Thread &thread, uint32_t status, lldb::StopReason stop_reason) + : StopInfo(thread, status), m_stop_reason(stop_reason) + { } + + lldb::StopReason + GetStopReason() const + { + return m_stop_reason; + } +private: + lldb::StopReason m_stop_reason; +}; + +ThreadWindows::ThreadWindows(Process &process, HANDLE handle) + : Thread(process, GetThreadId(handle)), + m_frame_ap(), + m_handle(handle), + m_posix_thread(NULL) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + if (log && log->GetMask().Test(LIBLLDB_LOG_VERBOSE)) + log->Printf ("ThreadWindows::%s (tid = %" PRIi64 ")", __FUNCTION__, GetID()); +} + +ThreadWindows::~ThreadWindows() +{ + DestroyThread(); +} + +void +ThreadWindows::RefreshStateAfterStop() +{ + // Invalidate all registers in our register context. We don't set "force" to + // true because the stop reply packet might have had some register values + // that were expedited and these will already be copied into the register + // context by the time this function gets called. The KDPRegisterContext + // class has been made smart enough to detect when it needs to invalidate + // which registers are valid by putting hooks in the register read and + // register supply functions where they check the process stop ID and do + // the right thing. + //if (StateIsStoppedState(GetState()) + { + const bool force = false; + GetRegisterContext()->InvalidateIfNeeded (force); + } + // FIXME: This should probably happen somewhere else. + SetResumeState(eStateRunning); + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("ThreadWindows::%s (tid = %" PRIi64 ") setting thread resume state to running", __FUNCTION__, GetID()); +} + +const char * +ThreadWindows::GetInfo() +{ + return NULL; +} + +lldb::RegisterContextSP +ThreadWindows::GetRegisterContext() +{ + if (!m_reg_context_sp) + { + m_posix_thread = NULL; + + ArchSpec arch = Host::GetArchitecture(); + + switch (arch.GetMachine()) + { + default: + assert(false && "CPU type not supported!"); + break; + + case llvm::Triple::x86: + { + RegisterInfoInterface *reg_interface = new RegisterContextWindows_i386(arch); + RegisterContextWindowsDebug_i386 *reg_ctx = new RegisterContextWindowsDebug_i386(*this, 0, reg_interface); + m_posix_thread = reg_ctx; + m_reg_context_sp.reset(reg_ctx); + break; + } + + case llvm::Triple::x86_64: +// TODO: Use target OS/architecture detection rather than ifdefs so that +// lldb built on FreeBSD can debug on Linux and vice-versa. +#ifdef __linux__ + m_reg_context_sp.reset(new RegisterContextLinux_x86_64(*this, 0)); +#endif +#ifdef __FreeBSD__ + m_reg_context_sp.reset(new RegisterContextFreeBSD_x86_64(*this, 0)); +#endif + break; + } + } + return m_reg_context_sp; +} + +lldb::RegisterContextSP +ThreadWindows::CreateRegisterContextForFrame(lldb_private::StackFrame *frame) +{ + lldb::RegisterContextSP reg_ctx_sp; + uint32_t concrete_frame_idx = 0; + + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + if (log && log->GetMask().Test(LIBLLDB_LOG_VERBOSE)) + log->Printf ("ThreadWindows::%s ()", __FUNCTION__); + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex(); + + if (concrete_frame_idx == 0) + reg_ctx_sp = GetRegisterContext(); + else + { + assert(GetUnwinder()); + reg_ctx_sp = GetUnwinder()->CreateRegisterContextForFrame(frame); + } + + return reg_ctx_sp; +} + +bool +ThreadWindows::CalculateStopInfo() +{ + SetStopInfo (m_stop_info_sp); + return true; +} + +Unwind * +ThreadWindows::GetUnwinder() +{ + if (m_unwinder_ap.get() == NULL) + m_unwinder_ap.reset(new UnwindLLDB(*this)); + + return m_unwinder_ap.get(); +} + +void +ThreadWindows::WillResume(lldb::StateType resume_state) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + if (log) + log->Printf ("ThreadWindows::%s (tid = %" PRIi64 ") setting thread resume state to %s", __FUNCTION__, GetID(), StateAsCString(resume_state)); + // TODO: the line below shouldn't really be done, but + // the ThreadWindows might rely on this so I will leave this in for now + SetResumeState(resume_state); +} + +void +ThreadWindows::DidStop() +{ + // Don't set the thread state to stopped unless we really stopped. +} + +bool +ThreadWindows::Resume() +{ + Mutex::Locker locker(m_state_mutex); + + lldb::StateType resume_state = GetResumeState(); + lldb::StateType current_state = GetState(); + bool status; + + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + if (log) + log->Printf("ThreadWindows::%s (), tid = %" PRIi64 " resume_state = %s", __FUNCTION__, GetID(), + StateAsCString(resume_state)); + + switch (resume_state) + { + default: + assert(false && "Unexpected state for resume!"); + status = false; + break; + + case lldb::eStateRunning: + { + SetState(resume_state); + int resumeCount; + do + { + resumeCount = ResumeThread(GetHandle()); + } while (resumeCount > 1); + status = resumeCount != -1; + } + break; + + case lldb::eStateStepping: + { + SuspendThread(GetHandle()); + + // Set stepping flag + SetState(resume_state); + CONTEXT context; + context.ContextFlags = CONTEXT_CONTROL; + GetThreadContext(GetHandle(), &context); + context.EFlags |= 0x100; + SetThreadContext(GetHandle(), &context); + + // Resume this thread + int resumeCount; + do + { + resumeCount = ResumeThread(GetHandle()); + } while (resumeCount > 1); + status = resumeCount != -1; + + //EventSP exit_event_sp; + //StateType state = m_process_wp.lock()->WaitForProcessToStop(NULL, &exit_event_sp); + } + break; + case lldb::eStateStopped: + case lldb::eStateSuspended: + if (current_state != lldb::eStateStopped && current_state != lldb::eStateSuspended) + SuspendThread(GetHandle()); + status = true; + break; + } + + return status; +} + +void +ThreadWindows::Notify(const ProcessMessage &message) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + //if (log) + // log->Printf ("ThreadWindows::%s () message kind = '%s'", __FUNCTION__, message.PrintKind()); + + switch (message.GetKind()) + { + default: + assert(false && "Unexpected message kind!"); + break; + + case ProcessMessage::eExitMessage: + // Nothing to be done. + break; + + case ProcessMessage::eBreakpointMessage: + { + bool status; + + assert(GetRegisterContext()); + status = GetRegisterContextWindows()->UpdateAfterBreakpoint(); + assert(status && "Breakpoint update failed!"); + + // With our register state restored, resolve the breakpoint object + // corresponding to our current PC. + lldb::addr_t pc = GetRegisterContext()->GetPC(); + lldb::BreakpointSiteSP bp_site(GetProcess()->GetBreakpointSiteList().FindByAddress(pc)); + + if (!bp_site && m_breakpoint && m_breakpoint->GetLoadAddress() == pc) + { + bp_site = m_breakpoint; + m_breakpoint.reset(); + } + + if (log) + log->Printf("ThreadWindows::%s (tid = %" PRIi64 ") PC=0x%8.8" PRIx64 " breakpoint site %" PRIi32, __FUNCTION__, GetID(), pc, bp_site ? bp_site->GetID() : 0); + + // If the breakpoint is for this thread, then we'll report the hit, but if it is for another thread, + // we create a stop reason with should_stop=false. If there is no breakpoint location, then report + // an invalid stop reason. We don't need to worry about stepping over the breakpoint here, that will + // be taken care of when the thread resumes and notices that there's a breakpoint under the pc. + if (bp_site) + { + lldb::break_id_t bp_id = bp_site->GetID(); + if (bp_site->ValidForThisThread(this)) + SetStopInfo(StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_id)); + else + { + const bool should_stop = false; + SetStopInfo(StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_id, should_stop)); + } + } + else + SetStopInfo(StopInfoSP()); + } + break; + + case ProcessMessage::eTraceMessage: + SetStopInfo(StopInfoSP(new WindowsStopInfo(*this, LLDB_INVALID_UID, eStopReasonTrace))); + break; + + case ProcessMessage::eNewThreadMessage: + SetStopInfo(StopInfoSP(new WindowsStopInfo(*this, 0, eStopReasonNone))); + break; + } +} + +bool +ThreadWindows::EnableHardwareWatchpoint(Watchpoint *wp) +{ + return false; +} + +bool +ThreadWindows::DisableHardwareWatchpoint(Watchpoint *wp) +{ + return false; +} + +uint32_t +ThreadWindows::NumSupportedHardwareWatchpoints() +{ + return 0; +} + +unsigned +ThreadWindows::GetRegisterIndexFromOffset(unsigned offset) +{ + unsigned reg; + ArchSpec arch = Host::GetArchitecture(); + + switch (arch.GetMachine()) + { + default: + assert(false && "CPU type not supported!"); + break; + + case llvm::Triple::x86: + case llvm::Triple::x86_64: + { + RegisterContextSP base = GetRegisterContext(); + if (base) { + POSIXBreakpointProtocol* reg_ctx = GetRegisterContextWindows(); + reg = reg_ctx->GetRegisterIndexFromOffset(offset); + } + } + break; + } + return reg; +} + +const char * +ThreadWindows::GetRegisterName(unsigned reg) +{ + const char * name = nullptr; + ArchSpec arch = Host::GetArchitecture(); + + switch (arch.GetMachine()) + { + default: + assert(false && "CPU type not supported!"); + break; + + case llvm::Triple::x86: + case llvm::Triple::x86_64: + name = GetRegisterContext()->GetRegisterName(reg); + break; + } + return name; +} + +const char * +ThreadWindows::GetRegisterNameFromOffset(unsigned offset) +{ + return GetRegisterName(GetRegisterIndexFromOffset(offset)); +} + +HANDLE +ThreadWindows::GetHandle() +{ + return m_handle; +} \ No newline at end of file diff --git a/source/Plugins/Process/Windows/ThreadWindows.h b/source/Plugins/Process/Windows/ThreadWindows.h new file mode 100644 index 0000000000..b8e94d64af --- /dev/null +++ b/source/Plugins/Process/Windows/ThreadWindows.h @@ -0,0 +1,117 @@ +//===-- ThreadWindows.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_ThreadWindows_H_ +#define liblldb_ThreadWindows_H_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +#include "lldb/Target/Thread.h" +#include "lldb/Host/windows/windows.h" +#include "RegisterContextPOSIX.h" +#include "../POSIX/ProcessMessage.h" + +class ProcessMessage; +class ProcessMonitor; +class RegisterContextWindowsDebug_i386; + +//------------------------------------------------------------------------------ +// @class POSIXThread +// @brief Abstraction of a linux process (thread). +class ThreadWindows + : public lldb_private::Thread +{ +public: + ThreadWindows(lldb_private::Process &process, HANDLE handle); + + virtual ~ThreadWindows(); + + void + RefreshStateAfterStop(); + + virtual void + WillResume(lldb::StateType resume_state); + + // This notifies the thread when a private stop occurs. + virtual void + DidStop (); + + const char * + GetInfo(); + + virtual lldb::RegisterContextSP + GetRegisterContext(); + + virtual lldb::RegisterContextSP + CreateRegisterContextForFrame (lldb_private::StackFrame *frame); + + //-------------------------------------------------------------------------- + // These functions provide a mapping from the register offset + // back to the register index or name for use in debugging or log + // output. + + unsigned + GetRegisterIndexFromOffset(unsigned offset); + + const char * + GetRegisterName(unsigned reg); + + const char * + GetRegisterNameFromOffset(unsigned offset); + + //-------------------------------------------------------------------------- + // These methods form a specialized interface to linux threads. + // + bool Resume(); + + void Notify(const ProcessMessage &message); + + //-------------------------------------------------------------------------- + // These methods provide an interface to watchpoints + // + bool EnableHardwareWatchpoint(lldb_private::Watchpoint *wp); + + bool DisableHardwareWatchpoint(lldb_private::Watchpoint *wp); + + uint32_t NumSupportedHardwareWatchpoints(); + + HANDLE GetHandle(); + +private: + POSIXBreakpointProtocol * + GetRegisterContextWindows () + { + if (!m_reg_context_sp) + m_reg_context_sp = GetRegisterContext(); + return m_posix_thread; + } + + std::unique_ptr m_frame_ap; + + lldb::BreakpointSiteSP m_breakpoint; + ProcessMessage m_pending_message; + + POSIXBreakpointProtocol *m_posix_thread; + + HANDLE m_handle; + + virtual bool + CalculateStopInfo(); + + lldb_private::Unwind * + GetUnwinder(); + + friend class ProcessWindows; + friend class RegisterContextWindowsDebug_i386; +}; + +#endif // #ifndef liblldb_POSIXThread_H_ diff --git a/source/lldb.cpp b/source/lldb.cpp index 23a70947e7..f93ccbf922 100644 --- a/source/lldb.cpp +++ b/source/lldb.cpp @@ -69,6 +69,10 @@ #include "Plugins/Platform/MacOSX/PlatformiOSSimulator.h" #include "Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.h" #endif +#ifdef _WIN32 +#include "Plugins/DynamicLoader/Windows/DynamicLoaderWindows.h" +#include "Plugins/Process/Windows/ProcessWindows.h" +#endif #include "Plugins/Process/mach-core/ProcessMachCore.h" @@ -132,6 +136,10 @@ lldb_private::Initialize () JITLoaderGDB::Initialize(); ProcessElfCore::Initialize(); +#ifdef _WIN32 + DynamicLoaderWindows::Initialize(); + ProcessWindows::Initialize(); +#endif #if defined (__APPLE__) //---------------------------------------------------------------------- // Apple/Darwin hosted plugins @@ -218,6 +226,10 @@ lldb_private::Terminate () JITLoaderGDB::Terminate(); ProcessElfCore::Terminate(); +#ifdef _WIN32 + DynamicLoaderWindows::Terminate(); + ProcessWindows::Terminate(); +#endif #if defined (__APPLE__) DynamicLoaderMacOSXDYLD::Terminate(); DynamicLoaderDarwinKernel::Terminate();