diff options
author | 2020-08-03 14:33:06 +0000 | |
---|---|---|
committer | 2020-08-03 14:33:06 +0000 | |
commit | 061da546b983eb767bad15e67af1174fb0bcf31c (patch) | |
tree | 83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp')
-rw-r--r-- | gnu/llvm/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp | 602 |
1 files changed, 602 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp b/gnu/llvm/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp new file mode 100644 index 00000000000..f888d152caa --- /dev/null +++ b/gnu/llvm/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp @@ -0,0 +1,602 @@ +//===-- NativeProcessWindows.cpp --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/windows/windows.h" +#include <psapi.h> + +#include "NativeProcessWindows.h" +#include "NativeThreadWindows.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostNativeProcessBase.h" +#include "lldb/Host/HostProcess.h" +#include "lldb/Host/ProcessLaunchInfo.h" +#include "lldb/Host/windows/AutoHandle.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/ProcessLauncherWindows.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/State.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Threading.h" +#include "llvm/Support/raw_ostream.h" + +#include "DebuggerThread.h" +#include "ExceptionRecord.h" +#include "ProcessWindowsLog.h" + +#include <tlhelp32.h> + +#pragma warning(disable : 4005) +#include "winternl.h" +#include <ntstatus.h> + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +namespace lldb_private { + +NativeProcessWindows::NativeProcessWindows(ProcessLaunchInfo &launch_info, + NativeDelegate &delegate, + llvm::Error &E) + : NativeProcessProtocol(LLDB_INVALID_PROCESS_ID, + launch_info.GetPTY().ReleaseMasterFileDescriptor(), + delegate), + ProcessDebugger(), m_arch(launch_info.GetArchitecture()) { + ErrorAsOutParameter EOut(&E); + DebugDelegateSP delegate_sp(new NativeDebugDelegate(*this)); + E = LaunchProcess(launch_info, delegate_sp).ToError(); + if (E) + return; + + SetID(GetDebuggedProcessId()); +} + +NativeProcessWindows::NativeProcessWindows(lldb::pid_t pid, int terminal_fd, + NativeDelegate &delegate, + llvm::Error &E) + : NativeProcessProtocol(pid, terminal_fd, delegate), ProcessDebugger() { + ErrorAsOutParameter EOut(&E); + DebugDelegateSP delegate_sp(new NativeDebugDelegate(*this)); + ProcessAttachInfo attach_info; + attach_info.SetProcessID(pid); + E = AttachProcess(pid, attach_info, delegate_sp).ToError(); + if (E) + return; + + SetID(GetDebuggedProcessId()); + + ProcessInstanceInfo info; + if (!Host::GetProcessInfo(pid, info)) { + E = createStringError(inconvertibleErrorCode(), + "Cannot get process information"); + return; + } + m_arch = info.GetArchitecture(); +} + +Status NativeProcessWindows::Resume(const ResumeActionList &resume_actions) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + Status error; + llvm::sys::ScopedLock lock(m_mutex); + + StateType state = GetState(); + if (state == eStateStopped || state == eStateCrashed) { + LLDB_LOG(log, "process {0} is in state {1}. Resuming...", + GetDebuggedProcessId(), state); + LLDB_LOG(log, "resuming {0} threads.", m_threads.size()); + + bool failed = false; + for (uint32_t i = 0; i < m_threads.size(); ++i) { + auto thread = static_cast<NativeThreadWindows *>(m_threads[i].get()); + const ResumeAction *const action = + resume_actions.GetActionForThread(thread->GetID(), true); + if (action == nullptr) + continue; + + switch (action->state) { + case eStateRunning: + case eStateStepping: { + Status result = thread->DoResume(action->state); + if (result.Fail()) { + failed = true; + LLDB_LOG(log, + "Trying to resume thread at index {0}, but failed with " + "error {1}.", + i, result); + } + break; + } + case eStateSuspended: + case eStateStopped: + llvm_unreachable("Unexpected state"); + + default: + return Status( + "NativeProcessWindows::%s (): unexpected state %s specified " + "for pid %" PRIu64 ", tid %" PRIu64, + __FUNCTION__, StateAsCString(action->state), GetID(), + thread->GetID()); + } + } + + if (failed) { + error.SetErrorString("NativeProcessWindows::DoResume failed"); + } else { + SetState(eStateRunning); + } + + // Resume the debug loop. + ExceptionRecordSP active_exception = + m_session_data->m_debugger->GetActiveException().lock(); + if (active_exception) { + // Resume the process and continue processing debug events. Mask the + // exception so that from the process's view, there is no indication that + // anything happened. + m_session_data->m_debugger->ContinueAsyncException( + ExceptionResult::MaskException); + } + } else { + LLDB_LOG(log, "error: process {0} is in state {1}. Returning...", + GetDebuggedProcessId(), GetState()); + } + + return error; +} + +NativeThreadWindows * +NativeProcessWindows::GetThreadByID(lldb::tid_t thread_id) { + return static_cast<NativeThreadWindows *>( + NativeProcessProtocol::GetThreadByID(thread_id)); +} + +Status NativeProcessWindows::Halt() { + bool caused_stop = false; + StateType state = GetState(); + if (state != eStateStopped) + return HaltProcess(caused_stop); + return Status(); +} + +Status NativeProcessWindows::Detach() { + Status error; + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + StateType state = GetState(); + if (state != eStateExited && state != eStateDetached) { + error = DetachProcess(); + if (error.Success()) + SetState(eStateDetached); + else + LLDB_LOG(log, "Detaching process error: {0}", error); + } else { + error.SetErrorStringWithFormatv("error: process {0} in state = {1}, but " + "cannot detach it in this state.", + GetID(), state); + LLDB_LOG(log, "error: {0}", error); + } + return error; +} + +Status NativeProcessWindows::Signal(int signo) { + Status error; + error.SetErrorString("Windows does not support sending signals to processes"); + return error; +} + +Status NativeProcessWindows::Interrupt() { return Halt(); } + +Status NativeProcessWindows::Kill() { + StateType state = GetState(); + return DestroyProcess(state); +} + +Status NativeProcessWindows::IgnoreSignals(llvm::ArrayRef<int> signals) { + return Status(); +} + +Status NativeProcessWindows::GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) { + return ProcessDebugger::GetMemoryRegionInfo(load_addr, range_info); +} + +Status NativeProcessWindows::ReadMemory(lldb::addr_t addr, void *buf, + size_t size, size_t &bytes_read) { + return ProcessDebugger::ReadMemory(addr, buf, size, bytes_read); +} + +Status NativeProcessWindows::WriteMemory(lldb::addr_t addr, const void *buf, + size_t size, size_t &bytes_written) { + return ProcessDebugger::WriteMemory(addr, buf, size, bytes_written); +} + +Status NativeProcessWindows::AllocateMemory(size_t size, uint32_t permissions, + lldb::addr_t &addr) { + return ProcessDebugger::AllocateMemory(size, permissions, addr); +} + +Status NativeProcessWindows::DeallocateMemory(lldb::addr_t addr) { + return ProcessDebugger::DeallocateMemory(addr); +} + +lldb::addr_t NativeProcessWindows::GetSharedLibraryInfoAddress() { return 0; } + +bool NativeProcessWindows::IsAlive() const { + StateType state = GetState(); + switch (state) { + case eStateCrashed: + case eStateDetached: + case eStateExited: + case eStateInvalid: + case eStateUnloaded: + return false; + default: + return true; + } +} + +void NativeProcessWindows::SetStopReasonForThread(NativeThreadWindows &thread, + lldb::StopReason reason, + std::string description) { + SetCurrentThreadID(thread.GetID()); + + ThreadStopInfo stop_info; + stop_info.reason = reason; + + // No signal support on Windows but required to provide a 'valid' signum. + if (reason == StopReason::eStopReasonException) { + stop_info.details.exception.type = 0; + stop_info.details.exception.data_count = 0; + } else { + stop_info.details.signal.signo = SIGTRAP; + } + + thread.SetStopReason(stop_info, description); +} + +void NativeProcessWindows::StopThread(lldb::tid_t thread_id, + lldb::StopReason reason, + std::string description) { + NativeThreadWindows *thread = GetThreadByID(thread_id); + if (!thread) + return; + + for (uint32_t i = 0; i < m_threads.size(); ++i) { + auto t = static_cast<NativeThreadWindows *>(m_threads[i].get()); + Status error = t->DoStop(); + if (error.Fail()) + exit(1); + } + SetStopReasonForThread(*thread, reason, description); +} + +size_t NativeProcessWindows::UpdateThreads() { return m_threads.size(); } + +llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> +NativeProcessWindows::GetAuxvData() const { + // Not available on this target. + return llvm::errc::not_supported; +} + +bool NativeProcessWindows::FindSoftwareBreakpoint(lldb::addr_t addr) { + auto it = m_software_breakpoints.find(addr); + if (it == m_software_breakpoints.end()) + return false; + return true; +} + +Status NativeProcessWindows::SetBreakpoint(lldb::addr_t addr, uint32_t size, + bool hardware) { + if (hardware) + return SetHardwareBreakpoint(addr, size); + return SetSoftwareBreakpoint(addr, size); +} + +Status NativeProcessWindows::RemoveBreakpoint(lldb::addr_t addr, + bool hardware) { + if (hardware) + return RemoveHardwareBreakpoint(addr); + return RemoveSoftwareBreakpoint(addr); +} + +Status NativeProcessWindows::CacheLoadedModules() { + Status error; + if (!m_loaded_modules.empty()) + return Status(); + + // Retrieve loaded modules by a Target/Module free implemenation. + AutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetID())); + if (snapshot.IsValid()) { + MODULEENTRY32W me; + me.dwSize = sizeof(MODULEENTRY32W); + if (Module32FirstW(snapshot.get(), &me)) { + do { + std::string path; + if (!llvm::convertWideToUTF8(me.szExePath, path)) + continue; + + FileSpec file_spec(path); + FileSystem::Instance().Resolve(file_spec); + m_loaded_modules[file_spec] = (addr_t)me.modBaseAddr; + } while (Module32Next(snapshot.get(), &me)); + } + + if (!m_loaded_modules.empty()) + return Status(); + } + + error.SetError(::GetLastError(), lldb::ErrorType::eErrorTypeWin32); + return error; +} + +Status NativeProcessWindows::GetLoadedModuleFileSpec(const char *module_path, + FileSpec &file_spec) { + Status error = CacheLoadedModules(); + if (error.Fail()) + return error; + + FileSpec module_file_spec(module_path); + FileSystem::Instance().Resolve(module_file_spec); + for (auto &it : m_loaded_modules) { + if (it.first == module_file_spec) { + file_spec = it.first; + return Status(); + } + } + return Status("Module (%s) not found in process %" PRIu64 "!", + module_file_spec.GetCString(), GetID()); +} + +Status +NativeProcessWindows::GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) { + Status error = CacheLoadedModules(); + if (error.Fail()) + return error; + + load_addr = LLDB_INVALID_ADDRESS; + FileSpec file_spec(file_name); + FileSystem::Instance().Resolve(file_spec); + for (auto &it : m_loaded_modules) { + if (it.first == file_spec) { + load_addr = it.second; + return Status(); + } + } + return Status("Can't get loaded address of file (%s) in process %" PRIu64 "!", + file_spec.GetCString(), GetID()); +} + +void NativeProcessWindows::OnExitProcess(uint32_t exit_code) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + LLDB_LOG(log, "Process {0} exited with code {1}", GetID(), exit_code); + + ProcessDebugger::OnExitProcess(exit_code); + + // No signal involved. It is just an exit event. + WaitStatus wait_status(WaitStatus::Exit, exit_code); + SetExitStatus(wait_status, true); + + // Notify the native delegate. + SetState(eStateExited, true); +} + +void NativeProcessWindows::OnDebuggerConnected(lldb::addr_t image_base) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_PROCESS); + LLDB_LOG(log, "Debugger connected to process {0}. Image base = {1:x}", + GetDebuggedProcessId(), image_base); + + // This is the earliest chance we can resolve the process ID and + // architecutre if we don't know them yet. + if (GetID() == LLDB_INVALID_PROCESS_ID) + SetID(GetDebuggedProcessId()); + + if (GetArchitecture().GetMachine() == llvm::Triple::UnknownArch) { + ProcessInstanceInfo process_info; + if (!Host::GetProcessInfo(GetDebuggedProcessId(), process_info)) { + LLDB_LOG(log, "Cannot get process information during debugger connecting " + "to process"); + return; + } + SetArchitecture(process_info.GetArchitecture()); + } + + // The very first one shall always be the main thread. + assert(m_threads.empty()); + m_threads.push_back(std::make_unique<NativeThreadWindows>( + *this, m_session_data->m_debugger->GetMainThread())); +} + +ExceptionResult +NativeProcessWindows::OnDebugException(bool first_chance, + const ExceptionRecord &record) { + Log *log = ProcessWindowsLog::GetLogIfAny(WINDOWS_LOG_EXCEPTION); + llvm::sys::ScopedLock lock(m_mutex); + + // Let the debugger establish the internal status. + ProcessDebugger::OnDebugException(first_chance, record); + + static bool initial_stop = false; + if (!first_chance) { + SetState(eStateStopped, false); + } + + ExceptionResult result = ExceptionResult::SendToApplication; + switch (record.GetExceptionCode()) { + case DWORD(STATUS_SINGLE_STEP): + case STATUS_WX86_SINGLE_STEP: { + uint32_t wp_id = LLDB_INVALID_INDEX32; + if (NativeThreadWindows *thread = GetThreadByID(record.GetThreadID())) { + NativeRegisterContextWindows ®_ctx = thread->GetRegisterContext(); + Status error = + reg_ctx.GetWatchpointHitIndex(wp_id, record.GetExceptionAddress()); + if (error.Fail()) + LLDB_LOG(log, + "received error while checking for watchpoint hits, pid = " + "{0}, error = {1}", + thread->GetID(), error); + if (wp_id != LLDB_INVALID_INDEX32) { + addr_t wp_addr = reg_ctx.GetWatchpointAddress(wp_id); + addr_t wp_hit_addr = reg_ctx.GetWatchpointHitAddress(wp_id); + std::string desc = + formatv("{0} {1} {2}", wp_addr, wp_id, wp_hit_addr).str(); + StopThread(record.GetThreadID(), StopReason::eStopReasonWatchpoint, + desc); + } + } + if (wp_id == LLDB_INVALID_INDEX32) + StopThread(record.GetThreadID(), StopReason::eStopReasonTrace); + + SetState(eStateStopped, true); + + // Continue the debugger. + return ExceptionResult::MaskException; + } + case DWORD(STATUS_BREAKPOINT): + case STATUS_WX86_BREAKPOINT: + if (FindSoftwareBreakpoint(record.GetExceptionAddress())) { + LLDB_LOG(log, "Hit non-loader breakpoint at address {0:x}.", + record.GetExceptionAddress()); + + StopThread(record.GetThreadID(), StopReason::eStopReasonBreakpoint); + + if (NativeThreadWindows *stop_thread = + GetThreadByID(record.GetThreadID())) { + auto ®ister_context = stop_thread->GetRegisterContext(); + // The current EIP is AFTER the BP opcode, which is one byte '0xCC' + uint64_t pc = register_context.GetPC() - 1; + register_context.SetPC(pc); + } + + SetState(eStateStopped, true); + return ExceptionResult::MaskException; + } + + if (!initial_stop) { + initial_stop = true; + LLDB_LOG(log, + "Hit loader breakpoint at address {0:x}, setting initial stop " + "event.", + record.GetExceptionAddress()); + + // We are required to report the reason for the first stop after + // launching or being attached. + if (NativeThreadWindows *thread = GetThreadByID(record.GetThreadID())) + SetStopReasonForThread(*thread, StopReason::eStopReasonBreakpoint); + + // Do not notify the native delegate (e.g. llgs) since at this moment + // the program hasn't returned from Factory::Launch() and the delegate + // might not have an valid native process to operate on. + SetState(eStateStopped, false); + + // Hit the initial stop. Continue the application. + return ExceptionResult::BreakInDebugger; + } + + LLVM_FALLTHROUGH; + default: + LLDB_LOG(log, + "Debugger thread reported exception {0:x} at address {1:x} " + "(first_chance={2})", + record.GetExceptionCode(), record.GetExceptionAddress(), + first_chance); + + { + std::string desc; + llvm::raw_string_ostream desc_stream(desc); + desc_stream << "Exception " + << llvm::format_hex(record.GetExceptionCode(), 8) + << " encountered at address " + << llvm::format_hex(record.GetExceptionAddress(), 8); + StopThread(record.GetThreadID(), StopReason::eStopReasonException, + desc_stream.str().c_str()); + + SetState(eStateStopped, true); + } + + // For non-breakpoints, give the application a chance to handle the + // exception first. + if (first_chance) + result = ExceptionResult::SendToApplication; + else + result = ExceptionResult::BreakInDebugger; + } + + return result; +} + +void NativeProcessWindows::OnCreateThread(const HostThread &new_thread) { + llvm::sys::ScopedLock lock(m_mutex); + + auto thread = std::make_unique<NativeThreadWindows>(*this, new_thread); + thread->GetRegisterContext().ClearAllHardwareWatchpoints(); + for (const auto &pair : GetWatchpointMap()) { + const NativeWatchpoint &wp = pair.second; + thread->SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags, + wp.m_hardware); + } + + m_threads.push_back(std::move(thread)); +} + +void NativeProcessWindows::OnExitThread(lldb::tid_t thread_id, + uint32_t exit_code) { + llvm::sys::ScopedLock lock(m_mutex); + NativeThreadWindows *thread = GetThreadByID(thread_id); + if (!thread) + return; + + for (auto t = m_threads.begin(); t != m_threads.end();) { + if ((*t)->GetID() == thread_id) { + t = m_threads.erase(t); + } else { + ++t; + } + } +} + +void NativeProcessWindows::OnLoadDll(const ModuleSpec &module_spec, + lldb::addr_t module_addr) { + // Simply invalidate the cached loaded modules. + if (!m_loaded_modules.empty()) + m_loaded_modules.clear(); +} + +void NativeProcessWindows::OnUnloadDll(lldb::addr_t module_addr) { + if (!m_loaded_modules.empty()) + m_loaded_modules.clear(); +} + +llvm::Expected<std::unique_ptr<NativeProcessProtocol>> +NativeProcessWindows::Factory::Launch( + ProcessLaunchInfo &launch_info, + NativeProcessProtocol::NativeDelegate &native_delegate, + MainLoop &mainloop) const { + Error E = Error::success(); + auto process_up = std::unique_ptr<NativeProcessWindows>( + new NativeProcessWindows(launch_info, native_delegate, E)); + if (E) + return std::move(E); + return std::move(process_up); +} + +llvm::Expected<std::unique_ptr<NativeProcessProtocol>> +NativeProcessWindows::Factory::Attach( + lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate, + MainLoop &mainloop) const { + Error E = Error::success(); + // Set pty master fd invalid since it is not available. + auto process_up = std::unique_ptr<NativeProcessWindows>( + new NativeProcessWindows(pid, -1, native_delegate, E)); + if (E) + return std::move(E); + return std::move(process_up); +} +} // namespace lldb_private |