Example #1
0
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);
}
Example #3
0
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;
}
Example #4
0
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;
}
Example #6
0
File: File.cpp Project: fjricci/ds2
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;
  }
}
Example #8
0
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;
}
Example #9
0
void MessageQueue::clear(bool terminating) {
  std::lock_guard<std::mutex> guard(_lock);
  _messages.clear();
  if (terminating) {
    DS2ASSERT(!_terminated);
    _terminated = true;
    _ready.notify_one();
  }
}
Example #10
0
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;
}
Example #11
0
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);
}
Example #12
0
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();
}
Example #13
0
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;
}
Example #14
0
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();
}
Example #15
0
File: PTrace.cpp Project: sas/ds2
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;
}
Example #16
0
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;
}
Example #17
0
File: PTrace.cpp Project: sas/ds2
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;
}
Example #18
0
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;
}
Example #19
0
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);
}
Example #21
0
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;
}
Example #22
0
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++;
    }
  }
}
Example #23
0
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;
}
Example #25
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);
  }
}
Example #26
0
//
// 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;
    }
  }
}
Example #27
0
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);
  }
}
Example #28
0
ErrorCode SoftwareBreakpointManager::remove(Address const &address) {
  DS2ASSERT(!(address.value() & 1));
  return super::remove(address);
}
Example #29
0
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;
}
Example #30
0
bool SoftwareBreakpointManager::has(Address const &address) const {
  DS2ASSERT(!(address.value() & 1));
  return super::has(address);
}