void ProtocolInterpreter::onCommand(std::string const &command, std::string const &arguments) { size_t commandLength; Handler const *handler = findHandler(command, commandLength); if (handler == nullptr) { DS2LOG(Packet, "handler for command '%s' unknown", command.c_str()); // // The handler couldn't be found, we don't support this packet. // _session->sendError(kErrorUnsupported); return; } std::string extra; if (commandLength != command.length()) { // // Command has part of the argument, LLDB doesn't use separators :( // extra = command.substr(commandLength); } extra += arguments; if (extra.find_first_of("*}") != std::string::npos) { extra = Unescape(extra); DS2LOG(Packet, "args='%.*s'", static_cast<int>(extra.length()), &extra[0]); } (handler->handler->*handler->callback)(*handler, extra); }
void SoftwareBreakpointManager::enableLocation(Site const &site) { std::string opcode; std::string old; ErrorCode error; getOpcode(site.size, opcode); old.resize(opcode.size()); error = _process->readMemory(site.address, &old[0], old.size()); if (error != kSuccess) { DS2LOG(Error, "cannot enable breakpoint at %#lx", (unsigned long)site.address.value()); return; } error = _process->writeMemory(site.address, &opcode[0], opcode.size()); if (error != kSuccess) { DS2LOG(Error, "cannot enable breakpoint at %#lx", (unsigned long)site.address.value()); return; } DS2LOG(Info, "set breakpoint instruction %#lx at %#lx (saved insn %#lx)", (unsigned long)(site.size == 2 ? *(uint16_t *)&opcode[0] : *(uint32_t *)&opcode[0]), (unsigned long)site.address.value(), (unsigned long)(site.size == 2 ? *(uint16_t *)&old[0] : *(uint32_t *)&old[0])); _insns[site.address] = old; }
void SoftwareBreakpointManager::enableLocation(Site const &site) { uint8_t const opcode = 0xcc; // int 3 uint8_t old; ErrorCode error; error = _process->readMemory(site.address, &old, sizeof(old)); if (error != kSuccess) { DS2LOG(BPManager, Error, "cannot enable breakpoint at %#lx", (unsigned long)site.address.value()); return; } error = _process->writeMemory(site.address, &opcode, sizeof(opcode)); if (error != kSuccess) { DS2LOG(BPManager, Error, "cannot enable breakpoint at %#lx", (unsigned long)site.address.value()); return; } DS2LOG(BPManager, Info, "set breakpoint instruction %#x at %#lx (saved insn %#x)", opcode, (unsigned long)site.address.value(), old); _insns[site.address] = old; }
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 PTrace::attach(ProcessId pid) { if (pid <= kAnyProcessId) return kErrorProcessNotFound; DS2LOG(Debug, "attaching to pid %" PRIu64, (uint64_t)pid); if (wrapPtrace(PT_ATTACH, pid, nullptr, nullptr) < 0) { ErrorCode error = Platform::TranslateError(); DS2LOG(Error, "Unable to attach: %d", error); return error; } return kSuccess; }
void SoftwareBreakpointManager::disableLocation(Site const &site) { ErrorCode error; uint8_t old = _insns[site.address]; error = _process->writeMemory(site.address, &old, sizeof(old)); if (error != kSuccess) { DS2LOG(BPManager, Error, "cannot restore instruction at %#lx", (unsigned long)site.address.value()); return; } DS2LOG(BPManager, Info, "reset instruction %#x at %#lx", old, (unsigned long)site.address.value()); _insns.erase(site.address); }
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; } }
ds2::Target::Process *Process::Create(ProcessSpawner &spawner) { ErrorCode error; // // Create the process. // auto process = new Target::Process; error = spawner.run(); if (error != kSuccess) goto fail; DS2LOG(Debug, "created process %I64u", (uint64_t)spawner.pid()); // // Wait the process. // error = process->initialize(spawner.pid(), spawner.handle(), spawner.tid(), spawner.threadHandle(), 0); if (error != kSuccess) goto fail; return process; fail: delete process; return nullptr; }
ds2::Target::Process *Process::Attach(ProcessId pid) { if (pid <= 0) return nullptr; // // Create the process. // Target::Process *process = new Target::Process; // // And try to attach. // ErrorCode error = process->ptrace().attach(pid); if (error != kSuccess) { DS2LOG(Error, "ptrace attach failed: %s", strerror(errno)); goto fail; } error = process->initialize(pid, kFlagAttachedProcess); if (error != kSuccess) { process->ptrace().detach(pid); goto fail; } return process; fail: delete process; return nullptr; }
FILE *ProcFS::OpenFILE(char const *what, char const *mode) { char path[PATH_MAX + 1]; ds2_snprintf(path, PATH_MAX, "/proc/%s", what); FILE *res = fopen(path, mode); if (res == nullptr) DS2LOG(Target, Error, "can't open %s: %s", path, strerror(errno)); return res; }
ErrorCode ProcessBase::suspend() { std::set<Thread *> threads; enumerateThreads([&](Thread *thread) { threads.insert(thread); }); for (auto thread : threads) { switch (thread->state()) { case Thread::kInvalid: DS2BUG("trying to suspend tid %" PRI_PID " in state %s", thread->tid(), Stringify::ThreadState(thread->state())); break; case Thread::kStepped: case Thread::kStopped: case Thread::kTerminated: DS2LOG(Debug, "not suspending tid %" PRI_PID ", already in state %s", thread->tid(), Stringify::ThreadState(thread->state())); if (thread->state() == Thread::kTerminated) { removeThread(thread->tid()); } break; case Thread::kRunning: { DS2LOG(Debug, "suspending tid %" PRI_PID, thread->tid()); ErrorCode error = thread->suspend(); switch (error) { case kSuccess: break; case kErrorProcessNotFound: DS2LOG(Debug, "tried to suspended tid %" PRI_PID " which is already dead", thread->tid()); removeThread(thread->tid()); return error; default: DS2LOG(Warning, "failed suspending tid %" PRI_PID ", error=%s", thread->tid(), Stringify::Error(error)); return error; } } break; } } 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; }
void ProtocolInterpreter::onInvalidData(std::string const &data) { DS2LOG(Warning, "received invalid data: '%s'", data.c_str()); if (_session == nullptr) return; _session->onInvalidData(data); }
void SoftwareBreakpointManager::disableLocation(Site const &site) { ErrorCode error; std::string old = _insns[site.address]; error = _process->writeMemory(site.address, &old[0], old.size()); if (error != kSuccess) { DS2LOG(Error, "cannot restore instruction at %#lx", (unsigned long)site.address.value()); return; } DS2LOG(Info, "reset instruction %#lx at %#lx", (unsigned long)(site.size == 2 ? *(uint16_t *)&old[0] : *(uint32_t *)&old[0]), (unsigned long)site.address.value()); _insns.erase(site.address); }
void ProcessBase::insert(ThreadBase *thread) { if (!_threads .insert(std::make_pair(thread->tid(), static_cast<Thread *>(thread))) .second) return; DS2LOG(Debug, "[new Thread %" PRI_PTR " (LWP %" PRIu64 ")]", PRI_PTR_CAST(thread), (uint64_t)thread->tid()); }
FILE *ProcFS::OpenFILE(pid_t pid, pid_t tid, char const *what, char const *mode) { char path[PATH_MAX + 1]; MakePath(path, PATH_MAX, pid, tid, what); FILE *res = fopen(path, mode); if (res == nullptr) DS2LOG(Target, Error, "can't open %s: %s", path, strerror(errno)); return res; }
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); }
ErrorCode PTrace::traceMe(bool disableASLR) { if (disableASLR) { int persona = ::personality(std::numeric_limits<uint32_t>::max()); if (::personality(persona | ADDR_NO_RANDOMIZE) == -1) { DS2LOG(Warning, "unable to disable ASLR, error=%s", strerror(errno)); } } return super::traceMe(false); }
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; }
void BreakpointManager::enable(Target::Thread *thread) { // // Both callbacks should be installed by child class before // enabling the breakpoint manager. // if (enabled(thread)) { DS2LOG(Warning, "double-enabling breakpoints"); } enumerate([this, thread](Site const &site) { enableLocation(site, thread); }); }
ErrorCode PTrace::detach(ProcessId pid) { if (pid <= kAnyProcessId) return kErrorProcessNotFound; DS2LOG(Debug, "detaching from pid %" PRIu64, (uint64_t)pid); if (wrapPtrace(PT_DETACH, pid, nullptr, nullptr) < 0) return Platform::TranslateError(); 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 Process::checkMemoryErrorCode(uint64_t address) { // mmap() returns MAP_FAILED thanks to the libc wrapper. Here we don't have // any, so we get the raw kernel return value, which is the address of the // newly allocated pages, if the call succeeds, or -errno if the call fails. if (address & (Platform::GetPageSize() - 1)) { int error = -address; DS2LOG(Debug, "mmap failed with errno=%s", Stringify::Errno(error)); return Platform::TranslateError(error); } return kSuccess; }
void ProcessBase::removeThread(ThreadId tid) { auto it = _threads.find(tid); if (it == _threads.end()) return; Thread *thread = it->second; _threads.erase(it); DS2LOG(Debug, "[delete Thread %" PRI_PTR " (LWP %" PRIu64 ") exited]", PRI_PTR_CAST(thread), (uint64_t)thread->tid()); delete thread; }
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; }
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::traceThat(ProcessId pid) { if (pid <= 0) return kErrorInvalidArgument; unsigned long traceFlags = PTRACE_O_TRACECLONE; // // Trace clone and exit events to track threads. // if (wrapPtrace(PTRACE_SETOPTIONS, pid, nullptr, traceFlags) < 0) { DS2LOG(Warning, "unable to set PTRACE_O_TRACECLONE on pid %d, error=%s", pid, strerror(errno)); return Platform::TranslateError(); } return kSuccess; }
ds2::Target::Process *Process::Attach(ProcessId pid) { if (pid <= 0) return nullptr; auto process = ds2::make_unique<Target::Process>(); if (process->ptrace().attach(pid) != kSuccess) { DS2LOG(Error, "ptrace attach failed: %s", strerror(errno)); return nullptr; } if (process->initialize(pid, kFlagAttachedProcess) != kSuccess) { process->ptrace().detach(pid); return nullptr; } return process.release(); }
ErrorCode Thread::step(int signal, Address const &address) { if (_state == kInvalid || _state == kRunning) { return kErrorInvalidArgument; } else if (_state == kTerminated) { return kErrorProcessNotFound; } DS2LOG(Debug, "stepping tid %d", tid()); // Prepare a software (arch-dependent) single step and resume execution. Architecture::CPUState state; CHK(readCPUState(state)); CHK(PrepareSoftwareSingleStep( process(), process()->softwareBreakpointManager(), state, address)); CHK(resume(signal, address)); return kSuccess; }