ErrorCode PTrace::step(ProcessThreadId const &ptid, ProcessInfo const &pinfo, int signal, Address const &address) { pid_t pid; if (!ptid.valid()) return kErrorInvalidArgument; if (!(ptid.tid <= kAnyThreadId)) { pid = ptid.tid; } else { pid = ptid.pid; } // // Continuation from address? // if (address.valid()) { Architecture::CPUState state; ErrorCode error = readCPUState(ptid, pinfo, state); if (error != kSuccess) return error; state.setPC(address); error = writeCPUState(ptid, pinfo, state); if (error != kSuccess) return error; } // (caddr_t)1 indicate that execution is to pick up where it left off. if (wrapPtrace(PT_STEP, pid, (caddr_t)1, signal) < 0) return Platform::TranslateError(); return kSuccess; }
ErrorCode ProcessBase::resume(int signal, std::set<Thread *> const &excluded) { enumerateThreads([&](Thread *thread) { if (excluded.find(thread) != excluded.end()) return; switch (thread->state()) { case Thread::kInvalid: case Thread::kTerminated: DS2BUG("trying to resume tid %" PRI_PID " in state %s", thread->tid(), Stringify::ThreadState(thread->state())); break; case Thread::kRunning: DS2LOG(Debug, "not resuming tid %" PRI_PID ", already in state %s", thread->tid(), Stringify::ThreadState(thread->state())); break; case Thread::kStopped: case Thread::kStepped: { Architecture::CPUState state; thread->readCPUState(state); DS2LOG(Debug, "resuming tid %" PRI_PID " from pc %" PRI_PTR " with signal %d", thread->tid(), PRI_PTR_CAST(state.pc()), signal); ErrorCode error = thread->resume(signal); if (error != kSuccess) { DS2LOG(Warning, "failed resuming tid %" PRI_PID ", error=%s", thread->tid(), Stringify::Error(error)); } } break; } }); return kSuccess; }
ErrorCode Thread::readCPUState(Architecture::CPUState &state) { CONTEXT context; memset(&context, 0, sizeof(context)); context.ContextFlags = CONTEXT_INTEGER | // GP registers. CONTEXT_CONTROL | // Some more GP + CPSR. CONTEXT_FLOATING_POINT | // FP registers. CONTEXT_DEBUG_REGISTERS; // Debug registers. BOOL result = GetThreadContext(_handle, &context); if (!result) { return Platform::TranslateError(); } // GP registers + CPSR. state.gp.r0 = context.R0; state.gp.r1 = context.R1; state.gp.r2 = context.R2; state.gp.r3 = context.R3; state.gp.r4 = context.R4; state.gp.r5 = context.R5; state.gp.r6 = context.R6; state.gp.r7 = context.R7; state.gp.r8 = context.R8; state.gp.r9 = context.R9; state.gp.r10 = context.R10; state.gp.r11 = context.R11; state.gp.ip = context.R12; state.gp.sp = context.Sp; state.gp.lr = context.Lr; state.gp.pc = context.Pc; state.gp.cpsr = context.Cpsr; // Floating point registers. static_assert(sizeof(context.D) == sizeof(state.vfp.dbl), "floating point register count mismatch"); for (size_t i = 0; i < array_sizeof(context.D); ++i) { state.vfp.dbl[i].value = context.D[i]; } state.vfp.fpscr = context.Fpscr; if (state.isThumb()) { if (state.gp.pc & 1ULL) { DS2LOG(Debug, "removing thumb bit from pc and lr"); state.gp.pc &= ~1ULL; } else { DS2LOG(Warning, "CPU is in thumb mode but doesn't have thumb bit set in pc"); } } // TODO(sas): Handle debug registers. return kSuccess; }
ErrorCode Process::suspend() { std::set<Thread *> threads; enumerateThreads([&](Thread *thread) { threads.insert(thread); }); for (auto thread : threads) { Architecture::CPUState state; if (thread->state() != Thread::kRunning) { thread->readCPUState(state); } DS2LOG(Debug, "tid %d state %d at pc %#" PRIx64, thread->tid(), thread->state(), thread->state() == Thread::kStopped ? (uint64_t)state.pc() : 0); if (thread->state() == Thread::kRunning) { ErrorCode error; DS2LOG(Debug, "suspending tid %d", thread->tid()); error = thread->suspend(); if (error == kSuccess) { DS2LOG(Debug, "suspended tid %d at pc %#" PRIx64, thread->tid(), (uint64_t)state.pc()); thread->readCPUState(state); } else if (error == kErrorProcessNotFound) { // // Thread is dead. // removeThread(thread->tid()); DS2LOG(Debug, "tried to suspended tid %d which is already dead", thread->tid()); } else { return error; } } else if (thread->state() == Thread::kTerminated) { // // Thread is dead. // removeThread(thread->tid()); } } return kSuccess; }
ErrorCode PTrace::prepareAddressForResume(ProcessThreadId const &ptid, ProcessInfo const &pinfo, Address const &address) { if (address.valid()) { Architecture::CPUState state; ErrorCode error = readCPUState(ptid, pinfo, state); if (error != kSuccess) { return error; } state.setPC(address); error = writeCPUState(ptid, pinfo, state); if (error != kSuccess) { return error; } } return kSuccess; }
ErrorCode Process::resume(int signal, std::set<Thread *> const &excluded) { enumerateThreads([&](Thread *thread) { if (excluded.find(thread) != excluded.end()) return; if (thread->state() == Thread::kStopped || thread->state() == Thread::kStepped) { Architecture::CPUState state; thread->readCPUState(state); DS2LOG(Debug, "resuming tid %I64u from pc %#I64x", (uint64_t)thread->tid(), (uint64_t)state.pc()); ErrorCode error = thread->resume(signal); if (error != kSuccess) { DS2LOG(Warning, "failed resuming tid %I64u, error=%d", (uint64_t)thread->tid(), error); } } }); 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 Thread::prepareSoftwareSingleStep(Address const &address) { Architecture::CPUState state; ErrorCode error = readCPUState(state); if (error != kSuccess) return error; bool link = false; bool isThumb = (state.gp.cpsr & (1 << 5)); uint32_t pc = address.valid() ? address.value() : state.pc(); uint32_t nextPC = pc; uint32_t branchPC = static_cast<uint32_t>(-1); uint32_t size = 0; // // We need to read 8 bytes, because of IT block // if (isThumb) { error = PrepareThumbSoftwareSingleStep(process(), pc, state, size, link, branchPC, nextPC); } else { error = PrepareARMSoftwareSingleStep(process(), pc, state, size, link, branchPC, nextPC); } DS2LOG(Architecture, Debug, "branchPC=%#lx[link=%s] nextPC=%#lx", (unsigned long)branchPC, link ? "true" : "false", (unsigned long)nextPC); if (branchPC != static_cast<uint32_t>(-1)) { error = process()->breakpointManager()->add( branchPC, BreakpointManager::kTypeTemporaryOneShot, size); if (error != kSuccess) return error; } if (nextPC != static_cast<uint32_t>(-1)) { if ((nextPC & ~1) == pc) { if (!isThumb) { nextPC += 4; size = 4; } else { uint32_t insn; error = process()->readMemory(pc & ~1, &insn, sizeof(insn)); if (error != kSuccess) return error; auto inst_size = Architecture::ARM::GetThumbInstSize(insn); nextPC += static_cast<std::uint8_t>(inst_size); size = 2; } } error = process()->breakpointManager()->add( nextPC, BreakpointManager::kTypeTemporaryOneShot, size); if (error != kSuccess) return error; } return kSuccess; }