ErrorCode Thread::resume(int signal, Address const &address) { // TODO(sas): Not sure how to translate the signal concept to Windows yet. // We'll probably have to get rid of these at some point. DS2ASSERT(signal == 0); // TODO(sas): Continuing a thread from a given address is not implemented yet. DS2ASSERT(!address.valid()); ErrorCode error = kSuccess; if (_state == kStopped || _state == kStepped) { ProcessInfo info; error = process()->getInfo(info); if (error != kSuccess) return error; BOOL result = ContinueDebugEvent(_process->pid(), _tid, DBG_CONTINUE); if (!result) return Host::Platform::TranslateError(); _state = kRunning; } else if (_state == kTerminated) { error = kErrorProcessNotFound; } return error; }
ErrorCode SoftwareBreakpointManager::add(Address const &address, Type type, size_t size, Mode mode) { DS2ASSERT(size == 0 || size == 1); DS2ASSERT(mode == kModeExec); return super::add(address, type, size, mode); }
static bool is32BitProcess(Process *process) { DS2ASSERT(process != nullptr); DS2ASSERT(process->currentThread() != nullptr); Architecture::CPUState state; ErrorCode error = process->currentThread()->readCPUState(state); if (error != kSuccess) { return error; } return state.is32; }
ErrorCode PrepareSoftwareSingleStep(Process *process, BreakpointManager *manager, CPUState const &state, Address const &address) { ErrorCode error; bool link = false; bool isThumb = !!(state.gp.cpsr & (1 << 5)); uint32_t pc = address.valid() ? address.value() : state.pc(); uint32_t nextPC = static_cast<uint32_t>(-1); uint32_t nextPCSize = 0; uint32_t branchPC = static_cast<uint32_t>(-1); uint32_t branchPCSize = 0; if (isThumb) { error = PrepareThumbSoftwareSingleStep(process, pc, state, link, nextPC, nextPCSize, branchPC, branchPCSize); } else { error = PrepareARMSoftwareSingleStep(process, pc, state, link, nextPC, nextPCSize, branchPC, branchPCSize); } if (error != kSuccess) { return error; } DS2LOG(Debug, "PC=%#x, branchPC=%#x[size=%d, link=%s] nextPC=%#x[size=%d]", pc, branchPC, branchPCSize, link ? "true" : "false", nextPC, nextPCSize); if (branchPC != static_cast<uint32_t>(-1)) { DS2ASSERT(branchPCSize != 0); error = manager->add(branchPC, BreakpointManager::kTypeTemporaryOneShot, branchPCSize, BreakpointManager::kModeExec); if (error != kSuccess) { return error; } } if (nextPC != static_cast<uint32_t>(-1)) { DS2ASSERT(nextPCSize != 0); error = manager->add(nextPC, BreakpointManager::kTypeTemporaryOneShot, nextPCSize, BreakpointManager::kModeExec); if (error != kSuccess) { return error; } } return kSuccess; }
bool SoftwareBreakpointManager::hit(Target::Thread *thread) { ds2::Architecture::CPUState state; // // Ignore hardware signle-stepping. // if (thread->state() == Target::Thread::kStepped) return true; thread->readCPUState(state); state.setPC(state.pc() - 1); if (super::hit(state.pc())) { // // Move the PC back to the instruction, INT3 will move // the instruction pointer to the next byte. // if (thread->writeCPUState(state) != kSuccess) abort(); #if !defined(NDEBUG) uint64_t ex = state.pc(); #endif thread->readCPUState(state); DS2ASSERT(ex == state.pc()); return true; } return false; }
ErrorCode File::pwrite(ByteVector const &buf, uint64_t &count, uint64_t offset) { DS2ASSERT(count > 0); if (!valid()) { return _lastError = kErrorInvalidHandle; } if (offset > std::numeric_limits<off_t>::max()) { return _lastError = kErrorInvalidArgument; } auto offArg = static_cast<off_t>(offset); auto countArg = static_cast<size_t>(count); ssize_t nWritten = ::pwrite(_fd, buf.data(), countArg, offArg); if (nWritten < 0) { return _lastError = Platform::TranslateError(); } count = static_cast<uint64_t>(nWritten); return _lastError = kSuccess; }
void SoftwareBreakpointManager::getOpcode(uint32_t type, std::string &opcode) const { switch (type) { #if defined(ARCH_ARM) case 2: // udf #1 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ opcode += '\xde'; opcode += '\x01'; #else opcode += '\x01'; opcode += '\xde'; #endif break; case 3: // udf.w #0 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ opcode += '\xa0'; opcode += '\x00'; opcode += '\xf7'; opcode += '\xf0'; #else opcode += '\xf0'; opcode += '\xf7'; opcode += '\x00'; opcode += '\xa0'; #endif break; case 4: // udf #16 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ opcode += '\xe7'; opcode += '\xf0'; opcode += '\x01'; opcode += '\xf0'; #else opcode += '\xf0'; opcode += '\x01'; opcode += '\xf0'; opcode += '\xe7'; #endif break; #elif defined(ARCH_ARM64) case 4: #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ opcode += '\xd4'; opcode += '\x20'; opcode += '\x20'; opcode += '\x00'; #else opcode += '\x00'; opcode += '\x20'; opcode += '\x20'; opcode += '\xd4'; #endif break; #endif default: DS2LOG(Error, "invalid breakpoint type %d", type); DS2ASSERT(0 && "invalid breakpoint type"); break; } }
bool Platform::GetCurrentEnvironment(EnvironmentBlock &env) { WCHAR *envStrings = GetEnvironmentStringsW(); WCHAR *oldEnvStrings = envStrings; if (envStrings == nullptr) return false; while (*envStrings) { std::string envStr; envStr = WideToNarrowString(envStrings); size_t equal = envStr.find('='); DS2ASSERT(equal != std::string::npos); // Some environment values can start with '=' for MS-DOS compatibility. // Ignore these values. if (equal != 0) env[envStr.substr(0, equal)] = envStr.substr(equal + 1); envStrings += wcslen(envStrings) + 1; } FreeEnvironmentStringsW(oldEnvStrings); return true; }
void MessageQueue::clear(bool terminating) { std::lock_guard<std::mutex> guard(_lock); _messages.clear(); if (terminating) { DS2ASSERT(!_terminated); _terminated = true; _ready.notify_one(); } }
bool Platform::GetCurrentEnvironment(EnvironmentBlock &env) { for (int i = 0; environ[i] != nullptr; ++i) { char *equal = strchr(environ[i], '='); DS2ASSERT(equal != nullptr && equal != environ[i]); env[std::string(environ[i], equal)] = equal + 1; } return true; }
ErrorCode SoftwareBreakpointManager::isValid(Address const &address, size_t size, Mode mode) const { DS2ASSERT(mode == kModeExec); if (size < 2 || size > 4) { DS2LOG(Debug, "Received unsupported breakpoint size %zu", size); return kErrorInvalidArgument; } return super::isValid(address, size, mode); }
std::wstring Platform::NarrowToWideString(std::string const &s) { std::vector<wchar_t> res; int size; size = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, nullptr, 0); DS2ASSERT(size != 0); res.resize(size); MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, res.data(), size); return res.data(); }
ErrorCode BreakpointManager::remove(Address const &address) { if (!address.valid()) return kErrorInvalidArgument; auto it = _sites.find(address); if (it == _sites.end()) return kErrorNotFound; // If we have some sort of temporary breakpoint, just remove it. if ((it->second.lifetime & Lifetime::Permanent) == Lifetime::None) goto do_remove; // If we have a permanent breakpoint, refs should be non-null. If refs is // still non-null after the decrement, do not remove the breakpoint and // return. DS2ASSERT(it->second.refs > 0); if (--it->second.refs > 0) return kSuccess; // If we had a breakpoint that only had lifetime Lifetime::Permanent and refs // is now 0, we need to remove it. if (it->second.lifetime == Lifetime::Permanent) goto do_remove; // Otherwise, the breakpoint had multiple lifetimes; unset // Lifetime::Permanent and return. it->second.lifetime = it->second.lifetime & ~Lifetime::Permanent; return kSuccess; do_remove: DS2ASSERT(it->second.refs == 0); // // If the breakpoint manager is already in enabled state, disable // the newly removed breakpoint too. // ErrorCode error = kSuccess; if (enabled()) { error = disableLocation(it->second); } _sites.erase(it); return error; }
std::string Platform::WideToNarrowString(std::wstring const &s) { std::vector<char> res; int size; size = WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, nullptr, 0, nullptr, nullptr); DS2ASSERT(size != 0); res.resize(size); WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, res.data(), size, nullptr, nullptr); return res.data(); }
ErrorCode PTrace::readRegisterSet(ProcessThreadId const &ptid, int regSetCode, void *buffer, size_t length) { struct iovec iov = {buffer, length}; if (wrapPtrace(PTRACE_GETREGSET, ptid.validTid() ? ptid.tid : ptid.pid, regSetCode, &iov) < 0) return Platform::TranslateError(); // On return, the kernel modifies iov.len to return the number of bytes read. // This should be exactly equal to the number of bytes requested. DS2ASSERT(iov.iov_len == length); return kSuccess; }
ErrorCode Thread::writeCPUState(Architecture::CPUState const &state) { CONTEXT context; memset(&context, 0, sizeof(context)); // TODO(sas): Handle debug registers. context.ContextFlags = CONTEXT_INTEGER | // GP registers. CONTEXT_CONTROL | // Some more GP + CPSR. CONTEXT_FLOATING_POINT; // FP registers. // GP registers + CPSR. context.R0 = state.gp.r0; context.R1 = state.gp.r1; context.R2 = state.gp.r2; context.R3 = state.gp.r3; context.R4 = state.gp.r4; context.R5 = state.gp.r5; context.R6 = state.gp.r6; context.R7 = state.gp.r7; context.R8 = state.gp.r8; context.R9 = state.gp.r9; context.R10 = state.gp.r10; context.R11 = state.gp.r11; context.R12 = state.gp.ip; context.Sp = state.gp.sp; context.Lr = state.gp.lr; context.Pc = state.gp.pc; context.Cpsr = state.gp.cpsr; // Floating point registers. for (size_t i = 0; i < array_sizeof(context.D); ++i) { context.D[i] = state.vfp.dbl[i].value; } context.Fpscr = state.vfp.fpscr; if (state.isThumb()) { DS2ASSERT(!(state.gp.pc & 1ULL)); DS2LOG(Debug, "setting back thumb bit on pc and lr"); context.Pc |= 1ULL; } BOOL result = SetThreadContext(_handle, &context); if (!result) { return Platform::TranslateError(); } return kSuccess; }
ErrorCode PTrace::wait(ProcessThreadId const &ptid, int *status) { pid_t pid; CHK(ptidToPid(ptid, pid)); int stat; pid_t ret; ret = waitpid(pid, &stat, __WALL); if (ret < 0) return kErrorProcessNotFound; DS2ASSERT(ret == pid); if (status != nullptr) { *status = stat; } return kSuccess; }
ErrorCode Process::wait(int *status, bool hang) { DEBUG_EVENT de; BOOL result = WaitForDebugEvent(&de, hang ? INFINITE : 0); if (!result) return Platform::TranslateError(); switch (de.dwDebugEventCode) { case CREATE_PROCESS_DEBUG_EVENT: #define CHECK_AND_CLOSE(HAN) \ do { \ if ((de.u.CreateProcessInfo.HAN) != NULL) \ CloseHandle(de.u.CreateProcessInfo.HAN); \ } while (0) CHECK_AND_CLOSE(hFile); CHECK_AND_CLOSE(hProcess); CHECK_AND_CLOSE(hThread); #undef CHECK_AND_CLOSE return kSuccess; case EXIT_PROCESS_DEBUG_EVENT: _terminated = true; return kSuccess; case CREATE_THREAD_DEBUG_EVENT: DS2LOG(Fatal, "debug event CREATE_THREAD"); case EXIT_THREAD_DEBUG_EVENT: DS2LOG(Fatal, "debug event EXIT_THREAD"); case RIP_EVENT: DS2LOG(Fatal, "debug event RIP"); case EXCEPTION_DEBUG_EVENT: case LOAD_DLL_DEBUG_EVENT: case UNLOAD_DLL_DEBUG_EVENT: case OUTPUT_DEBUG_STRING_EVENT: { auto threadIt = _threads.find(de.dwThreadId); DS2ASSERT(threadIt != _threads.end()); threadIt->second->updateState(de); } break; default: DS2BUG("unknown debug event code: %d", de.dwDebugEventCode); } return kSuccess; }
ErrorCode Thread::resume(int signal, Address const &address) { // TODO(sas): Not sure how to translate the signal concept to Windows yet. // We'll probably have to get rid of these at some point. DS2ASSERT(signal == 0); if (address.valid()) { CHK(modifyRegisters( [&address](Architecture::CPUState &state) { state.setPC(address); })); } switch (_state) { case kInvalid: case kRunning: DS2BUG("trying to suspend tid %" PRI_PID " in state %s", tid(), Stringify::ThreadState(_state)); break; case kTerminated: return kErrorProcessNotFound; case kStopped: case kStepped: { if (_stopInfo.event == StopInfo::kEventStop && _stopInfo.reason == StopInfo::kReasonNone) { DWORD result = ::ResumeThread(_handle); if (result == (DWORD)-1) { return Platform::TranslateError(); } } else { BOOL result = ::ContinueDebugEvent(_process->pid(), _tid, DBG_CONTINUE); if (!result) { return Platform::TranslateError(); } } _state = kRunning; return kSuccess; } } // Silence warnings without using a `default:` case for the above switch. DS2_UNREACHABLE(); }
ErrorCode SoftwareBreakpointManager::add(Address const &address, Type type, size_t size, Mode mode) { DS2ASSERT(mode == kModeExec); if (size < 2 || size > 4) { bool isThumb = false; // // Unless the address specifies the thumb bit, // we look at the current CPSR. // if (address.value() & 1) { isThumb = true; } else { ds2::Architecture::CPUState state; ErrorCode error = _process->currentThread()->readCPUState(state); if (error != kSuccess) return error; isThumb = state.isThumb(); } if (isThumb) { // // The size for breakpoints is counted in bytes, but for the // special case of 3 where it indicates a 2x16bit word, this in // order to distinguish the 32bit ARM instructions. This semantic // is equivalent to the one used by GDB. // uint32_t insn; ErrorCode error = _process->readMemory(address.value() & ~1ULL, &insn, sizeof(insn)); if (error != kSuccess) { return error; } auto inst_size = GetThumbInstSize(insn); size = inst_size == ThumbInstSize::TwoByteInst ? 2 : 3; } else { size = 4; } } return super::add(address.value() & ~1ULL, type, size, mode); }
static ThreadId GetFirstThreadIdForProcess(ProcessId pid) { HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (snapshot == INVALID_HANDLE_VALUE) return kAnyThreadId; THREADENTRY32 threadEntry; threadEntry.dwSize = sizeof(THREADENTRY32); if (!Thread32First(snapshot, &threadEntry)) goto thread_fail; do { DS2ASSERT(threadEntry.dwSize >= sizeof(THREADENTRY32)); if (threadEntry.th32OwnerProcessID == pid) { CloseHandle(snapshot); return threadEntry.th32ThreadID; } threadEntry.dwSize = sizeof(THREADENTRY32); } while (Thread32Next(snapshot, &threadEntry)); thread_fail: CloseHandle(snapshot); return kAnyThreadId; }
void BreakpointManager::disable(Target::Thread *thread) { if (!enabled(thread)) { DS2LOG(Warning, "double-disabling breakpoints"); } enumerate( [this, thread](Site const &site) { disableLocation(site, thread); }); // // Remove temporary breakpoints. // auto it = _sites.begin(); while (it != _sites.end()) { it->second.lifetime = it->second.lifetime & ~Lifetime::TemporaryOneShot; if (it->second.lifetime == Lifetime::None) { // refs should always be 0 unless we have a Lifetime::Permanent // breakpoint. DS2ASSERT(it->second.refs == 0); _sites.erase(it++); } else { it++; } } }
ErrorCode Process::wait(int *rstatus) { int status, signal; ProcessInfo info; ErrorCode err; pid_t tid; // We have at least one thread when we start waiting on a process. DS2ASSERT(!_threads.empty()); continue_waiting: err = super::wait(&status); if (err != kSuccess) return err; DS2LOG(Debug, "stopped: status=%d", status); if (WIFEXITED(status)) { err = super::wait(&status); DS2LOG(Debug, "exited: status=%d", status); _currentThread->updateStopInfo(status); _terminated = true; if (rstatus != nullptr) { *rstatus = status; } return kSuccess; } DS2BUG("not implemented"); switch (_currentThread->_stopInfo.event) { case StopInfo::kEventNone: switch (_currentThread->_stopInfo.reason) { case StopInfo::kReasonNone: ptrace().resume(ProcessThreadId(_pid, tid), info); goto continue_waiting; default: DS2ASSERT(false); goto continue_waiting; } case StopInfo::kEventExit: case StopInfo::kEventKill: DS2LOG(Debug, "thread %d is exiting", tid); // // Killing the main thread? // // Note(sas): This might be buggy; the main thread exiting // doesn't mean that the process is dying. // if (tid == _pid && _threads.size() == 1) { DS2LOG(Debug, "last thread is exiting"); break; } // // Remove and release the thread associated with this pid. // removeThread(tid); goto continue_waiting; case StopInfo::kEventStop: if (getInfo(info) != kSuccess) { DS2LOG(Error, "couldn't get process info for pid %d", _pid); goto continue_waiting; } signal = _currentThread->_stopInfo.signal; if (signal == SIGSTOP || signal == SIGCHLD) { // // Silently ignore SIGSTOP, SIGCHLD and SIGRTMIN (this // last one used for thread cancellation) and continue. // // Note(oba): The SIGRTMIN defines expands to a glibc // call, this due to the fact the POSIX standard does // not mandate that SIGRT* defines to be user-land // constants. // // Note(sas): This is probably partially dead code as // ptrace().step() doesn't work on ARM. // // Note(sas): Single-step detection should be higher up, not // only for SIGSTOP, SIGCHLD and SIGRTMIN, but for every // signal that we choose to ignore. // bool stepping = (_currentThread->state() == Thread::kStepped); if (signal == SIGSTOP) { signal = 0; } else { DS2LOG(Debug, "%s due to special signal, tid=%d status=%#x signal=%s", stepping ? "stepping" : "resuming", tid, status, strsignal(signal)); } ErrorCode error; if (stepping) { error = ptrace().step(ProcessThreadId(_pid, tid), info, signal); } else { error = ptrace().resume(ProcessThreadId(_pid, tid), info, signal); } if (error != kSuccess) { DS2LOG(Warning, "cannot resume thread %d error=%d", tid, error); } goto continue_waiting; } else if (_passthruSignals.find(signal) != _passthruSignals.end()) { ptrace().resume(ProcessThreadId(_pid, tid), info, signal); goto continue_waiting; } else { // // This is a signal that we want to transmit back to the // debugger. // break; } } if (!(WIFEXITED(status) || WIFSIGNALED(status))) { // // Suspend the process, this must be done after updating // the thread trap info. // suspend(); } if ((WIFEXITED(status) || WIFSIGNALED(status))) { _terminated = true; } if (rstatus != nullptr) { *rstatus = status; } return kSuccess; }
size_t DummySessionDelegateImpl::getGPRSize() const { DS2ASSERT(!"this method shall be implemented by inheriting class"); return 0; }
void Thread::updateState(DEBUG_EVENT const &de) { DS2ASSERT(de.dwThreadId == _tid); switch (de.dwDebugEventCode) { case EXCEPTION_DEBUG_EVENT: _state = kStopped; _trap.event = StopInfo::kEventTrap; _trap.reason = StopInfo::kReasonNone; break; case LOAD_DLL_DEBUG_EVENT: { ErrorCode error; std::wstring name = L"<NONAME>"; ProcessInfo pi; error = process()->getInfo(pi); if (error != kSuccess) goto noname; if (de.u.LoadDll.lpImageName == nullptr) goto noname; uint64_t ptr = 0; error = process()->readMemory( reinterpret_cast<uint64_t>(de.u.LoadDll.lpImageName), &ptr, pi.pointerSize); if (error != kSuccess) goto noname; if (ptr == 0) goto noname; // It seems like all strings passed by the kernel here are guaranteed to be // unicode. DS2ASSERT(de.u.LoadDll.fUnicode); name.clear(); wchar_t c; do { error = process()->readMemory(ptr, &c, sizeof(c)); if (error != kSuccess) break; name.append(1, c); ptr += sizeof(c); } while (c != '\0'); noname: DS2LOG(Debug, "new DLL loaded: %s", Host::Platform::WideToNarrowString(name).c_str()); if (de.u.LoadDll.hFile != NULL) CloseHandle(de.u.LoadDll.hFile); } case UNLOAD_DLL_DEBUG_EVENT: case OUTPUT_DEBUG_STRING_EVENT: _state = kStopped; _trap.event = StopInfo::kEventNone; _trap.reason = StopInfo::kReasonNone; break; default: DS2BUG("unknown debug event code: %d", de.dwDebugEventCode); } }
// // Redirector Thread // void ProcessSpawner::redirectionThread() { for (;;) { struct pollfd pfds[3]; struct pollfd *ppfds = pfds; int nfds = 0; std::memset(pfds, 0, sizeof(pfds)); for (size_t n = 0; n < 3; n++) { switch (_descriptors[n].mode) { case kRedirectBuffer: case kRedirectDelegate: ppfds->fd = _descriptors[n].fd; ppfds->events = (n == 0) ? POLLOUT : POLLIN; ppfds++, nfds++; break; default: break; } } nfds = ::poll(pfds, nfds, 100); if (nfds < 0) break; bool done = false; bool hup = false; for (int n = 0; n < nfds; n++) { if (pfds[n].revents & POLLHUP) { hup = true; } size_t index; RedirectDescriptor *descriptor = nullptr; for (index = 0; index < 3; index++) { if (_descriptors[index].fd == pfds[n].fd) { descriptor = &_descriptors[index]; break; } } DS2ASSERT(descriptor != nullptr); if (pfds[n].events & POLLIN) { if (pfds[n].revents & POLLIN) { char buf[128]; ssize_t nread = ::read(pfds[n].fd, buf, sizeof(buf)); if (nread > 0) { if (descriptor->mode == kRedirectBuffer) { _outputBuffer.insert(_outputBuffer.end(), &buf[0], &buf[nread]); } else { descriptor->delegate(buf, nread); } done = true; } } } } if (!done && hup) break; } for (auto &_descriptor : _descriptors) { if (_descriptor.fd != -1) { ::close(_descriptor.fd); _descriptor.fd = -1; } } }
void Thread::updateState(DEBUG_EVENT const &de) { DS2ASSERT(de.dwThreadId == _tid); switch (de.dwDebugEventCode) { case EXCEPTION_DEBUG_EVENT: _state = kStopped; DS2LOG( Debug, "exception from inferior, tid=%lu, code=%s, address=%" PRI_PTR, tid(), Stringify::ExceptionCode(de.u.Exception.ExceptionRecord.ExceptionCode), PRI_PTR_CAST(de.u.Exception.ExceptionRecord.ExceptionAddress)); switch (de.u.Exception.ExceptionRecord.ExceptionCode) { case STATUS_BREAKPOINT: case STATUS_SINGLE_STEP: _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonBreakpoint; break; case STATUS_ACCESS_VIOLATION: case STATUS_ARRAY_BOUNDS_EXCEEDED: case STATUS_IN_PAGE_ERROR: case STATUS_STACK_OVERFLOW: case STATUS_STACK_BUFFER_OVERRUN: _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonMemoryError; break; case STATUS_DATATYPE_MISALIGNMENT: _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonMemoryAlignment; break; case STATUS_FLOAT_DENORMAL_OPERAND: case STATUS_FLOAT_DIVIDE_BY_ZERO: case STATUS_FLOAT_INEXACT_RESULT: case STATUS_FLOAT_INVALID_OPERATION: case STATUS_FLOAT_OVERFLOW: case STATUS_FLOAT_STACK_CHECK: case STATUS_FLOAT_UNDERFLOW: case STATUS_INTEGER_DIVIDE_BY_ZERO: case STATUS_INTEGER_OVERFLOW: _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonMathError; break; case STATUS_ILLEGAL_INSTRUCTION: case STATUS_PRIVILEGED_INSTRUCTION: _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonInstructionError; break; default: DS2LOG(Warning, "unsupported exception code: %lx", de.u.Exception.ExceptionRecord.ExceptionCode); case STATUS_INVALID_DISPOSITION: case STATUS_NONCONTINUABLE_EXCEPTION: case DS2_EXCEPTION_UNCAUGHT_COM: case DS2_EXCEPTION_UNCAUGHT_USER: case DS2_EXCEPTION_UNCAUGHT_WINRT: _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonInstructionError; break; } break; case LOAD_DLL_DEBUG_EVENT: { #define CHK_SKIP(C) CHK_ACTION(C, goto skip_name) std::wstring name = L"<unknown>"; ProcessInfo pi; CHK_SKIP(process()->getInfo(pi)); if (de.u.LoadDll.lpImageName == nullptr) { goto skip_name; } uint64_t ptr; // ptr needs to be set to 0 or `if (ptr != 0)` might be true even if the // pointer is null, on 32-bit targets. ptr = 0; CHK_SKIP(process()->readMemory( reinterpret_cast<uint64_t>(de.u.LoadDll.lpImageName), &ptr, pi.pointerSize)); if (ptr == 0) { goto skip_name; } // It seems like all strings passed by the kernel here are guaranteed to be // unicode. DS2ASSERT(de.u.LoadDll.fUnicode); name.clear(); wchar_t c; do { if (process()->readMemory(ptr, &c, sizeof(c)) != kSuccess) { break; } name.append(1, c); ptr += sizeof(c); } while (c != '\0'); skip_name: DS2LOG(Debug, "new DLL loaded: %s, base=%" PRI_PTR, Host::Platform::WideToNarrowString(name).c_str(), PRI_PTR_CAST(de.u.LoadDll.lpBaseOfDll)); if (de.u.LoadDll.hFile != NULL) { ::CloseHandle(de.u.LoadDll.hFile); } _state = kStopped; _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonLibraryEvent; } break; case UNLOAD_DLL_DEBUG_EVENT: DS2LOG(Debug, "DLL unloaded, base=%" PRI_PTR, PRI_PTR_CAST(de.u.UnloadDll.lpBaseOfDll)); _state = kStopped; _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonLibraryEvent; break; case EXIT_THREAD_DEBUG_EVENT: _state = kStopped; _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonThreadExit; break; case OUTPUT_DEBUG_STRING_EVENT: { auto const &dsInfo = de.u.DebugString; DS2LOG(Debug, "inferior output a debug string: %" PRI_PTR "[%d]", PRI_PTR_CAST(dsInfo.lpDebugStringData), dsInfo.nDebugStringLength); // The length includes terminating null character. DS2ASSERT(dsInfo.nDebugStringLength >= 1); std::string buffer((dsInfo.fUnicode ? sizeof(WCHAR) : 1) * (dsInfo.nDebugStringLength - 1), 0); CHKV(process()->readMemory( reinterpret_cast<uint64_t>(dsInfo.lpDebugStringData), const_cast<char *>(buffer.c_str()), buffer.size())); _stopInfo.debugString = dsInfo.fUnicode ? Platform::WideToNarrowString(std::wstring( reinterpret_cast<const wchar_t *>(buffer.c_str()), dsInfo.nDebugStringLength - 1)) : std::move(buffer); _state = kStopped; _stopInfo.event = StopInfo::kEventStop; _stopInfo.reason = StopInfo::kReasonDebugOutput; } break; default: DS2BUG("unknown debug event code: %lu", de.dwDebugEventCode); } }
ErrorCode SoftwareBreakpointManager::remove(Address const &address) { DS2ASSERT(!(address.value() & 1)); return super::remove(address); }
ErrorCode Process::wait(int *status, bool hang) { if (_terminated) return kSuccess; DEBUG_EVENT de; bool keepGoing = true; while (keepGoing) { BOOL result = WaitForDebugEvent(&de, hang ? INFINITE : 0); if (!result) return Platform::TranslateError(); keepGoing = false; switch (de.dwDebugEventCode) { case CREATE_PROCESS_DEBUG_EVENT: #define CHECK_AND_CLOSE(HAN) \ do { \ if ((de.u.CreateProcessInfo.HAN) != NULL) \ CloseHandle(de.u.CreateProcessInfo.HAN); \ } while (0) CHECK_AND_CLOSE(hFile); CHECK_AND_CLOSE(hProcess); CHECK_AND_CLOSE(hThread); #undef CHECK_AND_CLOSE return kSuccess; case EXIT_PROCESS_DEBUG_EVENT: _terminated = true; return kSuccess; case CREATE_THREAD_DEBUG_EVENT: { auto threadHandle = de.u.CreateThread.hThread; auto tid = GetThreadId(threadHandle); // No need to save the new thread pointer, as it gets added automatically // to the process. new Thread(this, tid, threadHandle); resume(); keepGoing = true; } break; case EXIT_THREAD_DEBUG_EVENT: { auto threadIt = _threads.find(de.dwThreadId); DS2ASSERT(threadIt != _threads.end()); auto tid = threadIt->second->tid(); ContinueDebugEvent(_pid, tid, DBG_CONTINUE); removeThread(threadIt->second->tid()); keepGoing = true; } break; case RIP_EVENT: DS2LOG(Fatal, "debug event RIP"); case EXCEPTION_DEBUG_EVENT: case LOAD_DLL_DEBUG_EVENT: case UNLOAD_DLL_DEBUG_EVENT: case OUTPUT_DEBUG_STRING_EVENT: { auto threadIt = _threads.find(de.dwThreadId); DS2ASSERT(threadIt != _threads.end()); threadIt->second->updateState(de); } break; default: DS2BUG("unknown debug event code: %lu", de.dwDebugEventCode); } } return kSuccess; }
bool SoftwareBreakpointManager::has(Address const &address) const { DS2ASSERT(!(address.value() & 1)); return super::has(address); }