brintos

brintos / llvm-project-archived public Read only

0
0
Text · 21.3 KiB · a0f622f Raw
592 lines · cpp
1//===-- ProcessDebugger.cpp -----------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "ProcessDebugger.h"10 11// Windows includes12#include "lldb/Host/windows/windows.h"13#include <psapi.h>14 15#include "lldb/Host/FileSystem.h"16#include "lldb/Host/HostNativeProcessBase.h"17#include "lldb/Host/HostProcess.h"18#include "lldb/Host/HostThread.h"19#include "lldb/Host/ProcessLaunchInfo.h"20#include "lldb/Target/MemoryRegionInfo.h"21#include "lldb/Target/Process.h"22#include "llvm/Support/ConvertUTF.h"23#include "llvm/Support/Error.h"24 25#include "DebuggerThread.h"26#include "ExceptionRecord.h"27#include "ProcessWindowsLog.h"28 29using namespace lldb;30using namespace lldb_private;31 32static DWORD ConvertLldbToWinApiProtect(uint32_t protect) {33  // We also can process a read / write permissions here, but if the debugger34  // will make later a write into the allocated memory, it will fail. To get35  // around it is possible inside DoWriteMemory to remember memory permissions,36  // allow write, write and restore permissions, but for now we process only37  // the executable permission.38  //39  // TODO: Process permissions other than executable40  if (protect & ePermissionsExecutable)41    return PAGE_EXECUTE_READWRITE;42 43  return PAGE_READWRITE;44}45 46// The Windows page protection bits are NOT independent masks that can be47// bitwise-ORed together.  For example, PAGE_EXECUTE_READ is not (PAGE_EXECUTE48// | PAGE_READ).  To test for an access type, it's necessary to test for any of49// the bits that provide that access type.50static bool IsPageReadable(uint32_t protect) {51  return (protect & PAGE_NOACCESS) == 0;52}53 54static bool IsPageWritable(uint32_t protect) {55  return (protect & (PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY |56                     PAGE_READWRITE | PAGE_WRITECOPY)) != 0;57}58 59static bool IsPageExecutable(uint32_t protect) {60  return (protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE |61                     PAGE_EXECUTE_WRITECOPY)) != 0;62}63 64namespace lldb_private {65 66ProcessDebugger::~ProcessDebugger() {}67 68lldb::pid_t ProcessDebugger::GetDebuggedProcessId() const {69  if (m_session_data)70    return m_session_data->m_debugger->GetProcess().GetProcessId();71  return LLDB_INVALID_PROCESS_ID;72}73 74Status ProcessDebugger::DetachProcess() {75  Log *log = GetLog(WindowsLog::Process);76  DebuggerThreadSP debugger_thread;77  {78    // Acquire the lock only long enough to get the DebuggerThread.79    // StopDebugging() will trigger a call back into ProcessDebugger which will80    // also acquire the lock.  Thus we have to release the lock before calling81    // StopDebugging().82    llvm::sys::ScopedLock lock(m_mutex);83 84    if (!m_session_data) {85      LLDB_LOG(log, "there is no active session.");86      return Status();87    }88 89    debugger_thread = m_session_data->m_debugger;90  }91 92  Status error;93 94  LLDB_LOG(log, "detaching from process {0}.",95           debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle());96  error = debugger_thread->StopDebugging(false);97 98  // By the time StopDebugging returns, there is no more debugger thread, so99  // we can be assured that no other thread will race for the session data.100  m_session_data.reset();101 102  return error;103}104 105Status ProcessDebugger::LaunchProcess(ProcessLaunchInfo &launch_info,106                                      DebugDelegateSP delegate) {107  // Even though m_session_data is accessed here, it is before a debugger108  // thread has been kicked off.  So there's no race conditions, and it109  // shouldn't be necessary to acquire the mutex.110 111  Log *log = GetLog(WindowsLog::Process);112  Status result;113 114  FileSpec working_dir = launch_info.GetWorkingDirectory();115  namespace fs = llvm::sys::fs;116  if (working_dir) {117    FileSystem::Instance().Resolve(working_dir);118    if (!FileSystem::Instance().IsDirectory(working_dir)) {119      result = Status::FromErrorStringWithFormat(120          "No such file or directory: %s", working_dir.GetPath().c_str());121      return result;122    }123  }124 125  if (!launch_info.GetFlags().Test(eLaunchFlagDebug)) {126    StreamString stream;127    stream.Printf("ProcessDebugger unable to launch '%s'.  ProcessDebugger can "128                  "only be used for debug launches.",129                  launch_info.GetExecutableFile().GetPath().c_str());130    std::string message = stream.GetString().str();131    result = Status::FromErrorString(message.c_str());132 133    LLDB_LOG(log, "error: {0}", message);134    return result;135  }136 137  bool stop_at_entry = launch_info.GetFlags().Test(eLaunchFlagStopAtEntry);138  m_session_data.reset(new ProcessWindowsData(stop_at_entry));139  m_session_data->m_debugger.reset(new DebuggerThread(delegate));140  DebuggerThreadSP debugger = m_session_data->m_debugger;141 142  // Kick off the DebugLaunch asynchronously and wait for it to complete.143  result = debugger->DebugLaunch(launch_info);144  if (result.Fail()) {145    LLDB_LOG(log, "failed launching '{0}'. {1}",146             launch_info.GetExecutableFile().GetPath(), result);147    return result;148  }149 150  HostProcess process;151  Status error = WaitForDebuggerConnection(debugger, process);152  if (error.Fail()) {153    LLDB_LOG(log, "failed launching '{0}'. {1}",154             launch_info.GetExecutableFile().GetPath(), error);155    return error;156  }157 158  LLDB_LOG(log, "successfully launched '{0}'",159           launch_info.GetExecutableFile().GetPath());160 161  // We've hit the initial stop.  If eLaunchFlagsStopAtEntry was specified, the162  // private state should already be set to eStateStopped as a result of163  // hitting the initial breakpoint.  If it was not set, the breakpoint should164  // have already been resumed from and the private state should already be165  // eStateRunning.166  launch_info.SetProcessID(process.GetProcessId());167 168  return result;169}170 171Status ProcessDebugger::AttachProcess(lldb::pid_t pid,172                                      const ProcessAttachInfo &attach_info,173                                      DebugDelegateSP delegate) {174  Log *log = GetLog(WindowsLog::Process);175  m_session_data.reset(176      new ProcessWindowsData(!attach_info.GetContinueOnceAttached()));177  DebuggerThreadSP debugger(new DebuggerThread(delegate));178 179  m_session_data->m_debugger = debugger;180 181  DWORD process_id = static_cast<DWORD>(pid);182  Status error = debugger->DebugAttach(process_id, attach_info);183  if (error.Fail()) {184    LLDB_LOG(185        log,186        "encountered an error occurred initiating the asynchronous attach. {0}",187        error);188    return error;189  }190 191  HostProcess process;192  error = WaitForDebuggerConnection(debugger, process);193  if (error.Fail()) {194    LLDB_LOG(log,195             "encountered an error waiting for the debugger to connect. {0}",196             error);197    return error;198  }199 200  LLDB_LOG(log, "successfully attached to process with pid={0}", process_id);201 202  // We've hit the initial stop.  If eLaunchFlagsStopAtEntry was specified, the203  // private state should already be set to eStateStopped as a result of204  // hitting the initial breakpoint.  If it was not set, the breakpoint should205  // have already been resumed from and the private state should already be206  // eStateRunning.207 208  return error;209}210 211Status ProcessDebugger::DestroyProcess(const lldb::StateType state) {212  Log *log = GetLog(WindowsLog::Process);213  DebuggerThreadSP debugger_thread;214  {215    // Acquire this lock inside an inner scope, only long enough to get the216    // DebuggerThread. StopDebugging() will trigger a call back into217    // ProcessDebugger which will acquire the lock again, so we need to not218    // deadlock.219    llvm::sys::ScopedLock lock(m_mutex);220 221    if (!m_session_data) {222      LLDB_LOG(log, "warning: state = {0}, but there is no active session.",223               state);224      return Status();225    }226 227    debugger_thread = m_session_data->m_debugger;228  }229 230  if (state == eStateExited || state == eStateDetached) {231    LLDB_LOG(log, "warning: cannot destroy process {0} while state = {1}.",232             GetDebuggedProcessId(), state);233    return Status();234  }235 236  LLDB_LOG(log, "Shutting down process {0}.",237           debugger_thread->GetProcess().GetNativeProcess().GetSystemHandle());238  auto error = debugger_thread->StopDebugging(true);239 240  // By the time StopDebugging returns, there is no more debugger thread, so241  // we can be assured that no other thread will race for the session data.242  m_session_data.reset();243 244  return error;245}246 247Status ProcessDebugger::HaltProcess(bool &caused_stop) {248  Log *log = GetLog(WindowsLog::Process);249  Status error;250  llvm::sys::ScopedLock lock(m_mutex);251  caused_stop = ::DebugBreakProcess(m_session_data->m_debugger->GetProcess()252                                        .GetNativeProcess()253                                        .GetSystemHandle());254  if (!caused_stop) {255    error = Status(::GetLastError(), eErrorTypeWin32);256    LLDB_LOG(log, "DebugBreakProcess failed with error {0}", error);257  }258 259  return error;260}261 262Status ProcessDebugger::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,263                                   size_t &bytes_read) {264  Status error;265  bytes_read = 0;266  Log *log = GetLog(WindowsLog::Memory);267  llvm::sys::ScopedLock lock(m_mutex);268 269  if (!m_session_data) {270    error = Status::FromErrorString(271        "cannot read, there is no active debugger connection.");272    LLDB_LOG(log, "error: {0}", error);273    return error;274  }275 276  LLDB_LOG(log, "attempting to read {0} bytes from address {1:x}", size,277           vm_addr);278 279  lldb::process_t handle = m_session_data->m_debugger->GetProcess()280                               .GetNativeProcess()281                               .GetSystemHandle();282  void *addr = reinterpret_cast<void *>(vm_addr);283  SIZE_T num_of_bytes_read = 0;284  if (::ReadProcessMemory(handle, addr, buf, size, &num_of_bytes_read)) {285    bytes_read = num_of_bytes_read;286    return Status();287  }288  error = Status(GetLastError(), eErrorTypeWin32);289  MemoryRegionInfo info;290  if (GetMemoryRegionInfo(vm_addr, info).Fail() ||291      info.GetMapped() != MemoryRegionInfo::OptionalBool::eYes)292    return error;293  size = info.GetRange().GetRangeEnd() - vm_addr;294  LLDB_LOG(log, "retrying the read with size {0:x}", size);295  if (::ReadProcessMemory(handle, addr, buf, size, &num_of_bytes_read)) {296    LLDB_LOG(log, "success: read {0:x} bytes", num_of_bytes_read);297    bytes_read = num_of_bytes_read;298    return Status();299  }300  error = Status(GetLastError(), eErrorTypeWin32);301  LLDB_LOG(log, "error: {0}", error);302  return error;303}304 305Status ProcessDebugger::WriteMemory(lldb::addr_t vm_addr, const void *buf,306                                    size_t size, size_t &bytes_written) {307  Status error;308  bytes_written = 0;309  Log *log = GetLog(WindowsLog::Memory);310  llvm::sys::ScopedLock lock(m_mutex);311  LLDB_LOG(log, "attempting to write {0} bytes into address {1:x}", size,312           vm_addr);313 314  if (!m_session_data) {315    error = Status::FromErrorString(316        "cannot write, there is no active debugger connection.");317    LLDB_LOG(log, "error: {0}", error);318    return error;319  }320 321  HostProcess process = m_session_data->m_debugger->GetProcess();322  void *addr = reinterpret_cast<void *>(vm_addr);323  SIZE_T num_of_bytes_written = 0;324  lldb::process_t handle = process.GetNativeProcess().GetSystemHandle();325  if (::WriteProcessMemory(handle, addr, buf, size, &num_of_bytes_written)) {326    FlushInstructionCache(handle, addr, num_of_bytes_written);327    bytes_written = num_of_bytes_written;328  } else {329    error = Status(GetLastError(), eErrorTypeWin32);330    LLDB_LOG(log, "writing failed with error: {0}", error);331  }332  return error;333}334 335Status ProcessDebugger::AllocateMemory(size_t size, uint32_t permissions,336                                       lldb::addr_t &addr) {337  Status error;338  addr = LLDB_INVALID_ADDRESS;339  Log *log = GetLog(WindowsLog::Memory);340  llvm::sys::ScopedLock lock(m_mutex);341  LLDB_LOG(log, "attempting to allocate {0} bytes with permissions {1}", size,342           permissions);343 344  if (!m_session_data) {345    error = Status::FromErrorString(346        "cannot allocate, there is no active debugger connection");347    LLDB_LOG(log, "error: {0}", error);348    return error;349  }350 351  HostProcess process = m_session_data->m_debugger->GetProcess();352  lldb::process_t handle = process.GetNativeProcess().GetSystemHandle();353  auto protect = ConvertLldbToWinApiProtect(permissions);354  auto result = ::VirtualAllocEx(handle, nullptr, size, MEM_COMMIT, protect);355  if (!result) {356    error = Status(GetLastError(), eErrorTypeWin32);357    LLDB_LOG(log, "allocating failed with error: {0}", error);358  } else {359    addr = reinterpret_cast<addr_t>(result);360  }361  return error;362}363 364Status ProcessDebugger::DeallocateMemory(lldb::addr_t vm_addr) {365  Status result;366 367  Log *log = GetLog(WindowsLog::Memory);368  llvm::sys::ScopedLock lock(m_mutex);369  LLDB_LOG(log, "attempting to deallocate bytes at address {0}", vm_addr);370 371  if (!m_session_data) {372    result = Status::FromErrorString(373        "cannot deallocate, there is no active debugger connection");374    LLDB_LOG(log, "error: {0}", result);375    return result;376  }377 378  HostProcess process = m_session_data->m_debugger->GetProcess();379  lldb::process_t handle = process.GetNativeProcess().GetSystemHandle();380  if (!::VirtualFreeEx(handle, reinterpret_cast<LPVOID>(vm_addr), 0,381                       MEM_RELEASE)) {382    result = Status(GetLastError(), eErrorTypeWin32);383    LLDB_LOG(log, "deallocating failed with error: {0}", result);384  }385 386  return result;387}388 389Status ProcessDebugger::GetMemoryRegionInfo(lldb::addr_t vm_addr,390                                            MemoryRegionInfo &info) {391  Log *log = GetLog(WindowsLog::Memory);392  Status error;393  llvm::sys::ScopedLock lock(m_mutex);394  info.Clear();395 396  if (!m_session_data) {397    error = Status::FromErrorString(398        "GetMemoryRegionInfo called with no debugging session.");399    LLDB_LOG(log, "error: {0}", error);400    return error;401  }402  HostProcess process = m_session_data->m_debugger->GetProcess();403  lldb::process_t handle = process.GetNativeProcess().GetSystemHandle();404  if (handle == nullptr || handle == LLDB_INVALID_PROCESS) {405    error = Status::FromErrorString(406        "GetMemoryRegionInfo called with an invalid target process.");407    LLDB_LOG(log, "error: {0}", error);408    return error;409  }410 411  LLDB_LOG(log, "getting info for address {0:x}", vm_addr);412 413  void *addr = reinterpret_cast<void *>(vm_addr);414  MEMORY_BASIC_INFORMATION mem_info = {};415  SIZE_T result = ::VirtualQueryEx(handle, addr, &mem_info, sizeof(mem_info));416  if (result == 0) {417    DWORD last_error = ::GetLastError();418    if (last_error == ERROR_INVALID_PARAMETER) {419      // ERROR_INVALID_PARAMETER is returned if VirtualQueryEx is called with420      // an address past the highest accessible address. We should return a421      // range from the vm_addr to LLDB_INVALID_ADDRESS422      info.GetRange().SetRangeBase(vm_addr);423      info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS);424      info.SetReadable(MemoryRegionInfo::eNo);425      info.SetExecutable(MemoryRegionInfo::eNo);426      info.SetWritable(MemoryRegionInfo::eNo);427      info.SetMapped(MemoryRegionInfo::eNo);428      return error;429    } else {430      error = Status(last_error, eErrorTypeWin32);431      LLDB_LOG(log,432               "VirtualQueryEx returned error {0} while getting memory "433               "region info for address {1:x}",434               error, vm_addr);435      return error;436    }437  }438 439  // Protect bits are only valid for MEM_COMMIT regions.440  if (mem_info.State == MEM_COMMIT) {441    const bool readable = IsPageReadable(mem_info.Protect);442    const bool executable = IsPageExecutable(mem_info.Protect);443    const bool writable = IsPageWritable(mem_info.Protect);444    info.SetReadable(readable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo);445    info.SetExecutable(executable ? MemoryRegionInfo::eYes446                                  : MemoryRegionInfo::eNo);447    info.SetWritable(writable ? MemoryRegionInfo::eYes : MemoryRegionInfo::eNo);448  } else {449    info.SetReadable(MemoryRegionInfo::eNo);450    info.SetExecutable(MemoryRegionInfo::eNo);451    info.SetWritable(MemoryRegionInfo::eNo);452  }453 454  // AllocationBase is defined for MEM_COMMIT and MEM_RESERVE but not MEM_FREE.455  if (mem_info.State != MEM_FREE) {456    info.GetRange().SetRangeBase(457        reinterpret_cast<addr_t>(mem_info.BaseAddress));458    info.GetRange().SetRangeEnd(reinterpret_cast<addr_t>(mem_info.BaseAddress) +459                                mem_info.RegionSize);460    info.SetMapped(MemoryRegionInfo::eYes);461  } else {462    // In the unmapped case we need to return the distance to the next block of463    // memory. VirtualQueryEx nearly does that except that it gives the464    // distance from the start of the page containing vm_addr.465    SYSTEM_INFO data;466    ::GetSystemInfo(&data);467    DWORD page_offset = vm_addr % data.dwPageSize;468    info.GetRange().SetRangeBase(vm_addr);469    info.GetRange().SetByteSize(mem_info.RegionSize - page_offset);470    info.SetMapped(MemoryRegionInfo::eNo);471  }472 473  LLDB_LOGV(log,474            "Memory region info for address {0}: readable={1}, "475            "executable={2}, writable={3}",476            vm_addr, info.GetReadable(), info.GetExecutable(),477            info.GetWritable());478  return error;479}480 481void ProcessDebugger::OnExitProcess(uint32_t exit_code) {482  // If the process exits before any initial stop then notify the debugger483  // of the error otherwise WaitForDebuggerConnection() will be blocked.484  // An example of this issue is when a process fails to load a dependent DLL.485  if (m_session_data && !m_session_data->m_initial_stop_received) {486    Status error = Status::FromErrorStringWithFormatv(487        "Process prematurely exited with {0:x}", exit_code);488    OnDebuggerError(error, 0);489  }490}491 492void ProcessDebugger::OnDebuggerConnected(lldb::addr_t image_base) {}493 494ExceptionResult495ProcessDebugger::OnDebugException(bool first_chance,496                                  const ExceptionRecord &record) {497  Log *log = GetLog(WindowsLog::Exception);498  llvm::sys::ScopedLock lock(m_mutex);499  // FIXME: Without this check, occasionally when running the test suite500  // there is an issue where m_session_data can be null.  It's not clear how501  // this could happen but it only surfaces while running the test suite.  In502  // order to properly diagnose this, we probably need to first figure allow the503  // test suite to print out full lldb logs, and then add logging to the process504  // plugin.505  if (!m_session_data) {506    LLDB_LOG(log,507             "Debugger thread reported exception {0:x} at address {1:x}, but "508             "there is no session.",509             record.GetExceptionCode(), record.GetExceptionAddress());510    return ExceptionResult::SendToApplication;511  }512 513  ExceptionResult result = ExceptionResult::SendToApplication;514  if ((record.GetExceptionCode() == EXCEPTION_BREAKPOINT ||515       record.GetExceptionCode() ==516           0x4000001FL /*WOW64 STATUS_WX86_BREAKPOINT*/) &&517      !m_session_data->m_initial_stop_received) {518    // Handle breakpoints at the first chance.519    result = ExceptionResult::BreakInDebugger;520    LLDB_LOG(521        log,522        "Hit loader breakpoint at address {0:x}, setting initial stop event.",523        record.GetExceptionAddress());524    m_session_data->m_initial_stop_received = true;525    ::SetEvent(m_session_data->m_initial_stop_event);526  }527  return result;528}529 530void ProcessDebugger::OnCreateThread(const HostThread &thread) {531  // Do nothing by default532}533 534void ProcessDebugger::OnExitThread(lldb::tid_t thread_id, uint32_t exit_code) {535  // Do nothing by default536}537 538void ProcessDebugger::OnLoadDll(const ModuleSpec &module_spec,539                                lldb::addr_t module_addr) {540  // Do nothing by default541}542 543void ProcessDebugger::OnUnloadDll(lldb::addr_t module_addr) {544  // Do nothing by default545}546 547void ProcessDebugger::OnDebugString(const std::string &string) {}548 549void ProcessDebugger::OnDebuggerError(const Status &error, uint32_t type) {550  llvm::sys::ScopedLock lock(m_mutex);551  Log *log = GetLog(WindowsLog::Process);552 553  if (m_session_data->m_initial_stop_received) {554    // This happened while debugging.  Do we shutdown the debugging session,555    // try to continue, or do something else?556    LLDB_LOG(log,557             "Error {0} occurred during debugging.  Unexpected behavior "558             "may result.  {1}",559             error.GetError(), error);560  } else {561    // If we haven't actually launched the process yet, this was an error562    // launching the process.  Set the internal error and signal the initial563    // stop event so that the DoLaunch method wakes up and returns a failure.564    m_session_data->m_launch_error = error.Clone();565    ::SetEvent(m_session_data->m_initial_stop_event);566    LLDB_LOG(log,567             "Error {0} occurred launching the process before the initial "568             "stop. {1}",569             error.GetError(), error);570    return;571  }572}573 574Status ProcessDebugger::WaitForDebuggerConnection(DebuggerThreadSP debugger,575                                                  HostProcess &process) {576  Status result;577  Log *log = GetLog(WindowsLog::Process | WindowsLog::Breakpoints);578  LLDB_LOG(log, "Waiting for loader breakpoint.");579 580  // Block this function until we receive the initial stop from the process.581  if (::WaitForSingleObject(m_session_data->m_initial_stop_event, INFINITE) ==582      WAIT_OBJECT_0) {583    LLDB_LOG(log, "hit loader breakpoint, returning.");584 585    process = debugger->GetProcess();586    return m_session_data->m_launch_error.Clone();587  } else588    return Status(::GetLastError(), eErrorTypeWin32);589}590 591} // namespace lldb_private592