pkgsrc-wip/edb-debugger-git/patches/patch-plugins_DebuggerCore_...

450 lines
15 KiB
C++

$NetBSD$
--- plugins/DebuggerCore/unix/netbsd/PlatformThread.cpp.orig 2017-02-19 02:10:30.338910996 +0000
+++ plugins/DebuggerCore/unix/netbsd/PlatformThread.cpp
@@ -0,0 +1,444 @@
+/*
+Copyright (C) 2015 - 2015 Evan Teran
+ evan.teran@gmail.com
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "PlatformThread.h"
+#include "PlatformState.h"
+#include "PlatformCommon.h"
+#include "IProcess.h"
+#include "DebuggerCore.h"
+#include "State.h"
+#include <QtDebug>
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* or _BSD_SOURCE or _SVID_SOURCE */
+#endif
+
+#include <asm/ldt.h>
+#include <elf.h>
+#include <linux/uio.h>
+#include <sys/ptrace.h>
+#include <sys/user.h>
+
+// doesn't always seem to be defined in the headers
+#ifndef PTRACE_GET_THREAD_AREA
+#define PTRACE_GET_THREAD_AREA static_cast<__ptrace_request>(25)
+#endif
+
+#ifndef PTRACE_SET_THREAD_AREA
+#define PTRACE_SET_THREAD_AREA static_cast<__ptrace_request>(26)
+#endif
+
+#ifndef PTRACE_GETSIGINFO
+#define PTRACE_GETSIGINFO static_cast<__ptrace_request>(0x4202)
+#endif
+
+#ifndef PTRACE_GETREGSET
+#define PTRACE_GETREGSET static_cast<__ptrace_request>(0x4204)
+#endif
+
+#ifndef PTRACE_SETREGSET
+#define PTRACE_SETREGSET static_cast<__ptrace_request>(0x4205)
+#endif
+
+namespace DebuggerCorePlugin {
+
+//------------------------------------------------------------------------------
+// Name: fillSegmentBases
+// Desc:
+//------------------------------------------------------------------------------
+void PlatformThread::fillSegmentBases(PlatformState* state) {
+
+ struct user_desc desc;
+ std::memset(&desc, 0, sizeof(desc));
+
+ for(size_t sregIndex=0;sregIndex<state->seg_reg_count();++sregIndex) {
+ const edb::seg_reg_t reg=state->x86.segRegs[sregIndex];
+ if(!reg) continue;
+ bool fromGDT=!(reg&0x04); // otherwise the selector picks descriptor from LDT
+ if(!fromGDT) continue;
+ if(ptrace(PTRACE_GET_THREAD_AREA, tid_, reg / LDT_ENTRY_SIZE, &desc) != -1) {
+ state->x86.segRegBases[sregIndex] = desc.base_addr;
+ state->x86.segRegBasesFilled[sregIndex]=true;
+ }
+ }
+ for(size_t sregIndex=0;sregIndex<state->seg_reg_count();++sregIndex) {
+ const edb::seg_reg_t sreg=state->x86.segRegs[sregIndex];
+ if(sreg==core_->USER_CS_32||sreg==core_->USER_CS_64||sreg==core_->USER_SS ||
+ (state->is64Bit() && sregIndex<PlatformState::X86::FS)) {
+ state->x86.segRegBases[sregIndex] = 0;
+ state->x86.segRegBasesFilled[sregIndex]=true;
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Name: fillStateFromPrStatus
+// Desc:
+//------------------------------------------------------------------------------
+bool PlatformThread::fillStateFromPrStatus(PlatformState* state) {
+
+ static bool prStatusSupported=true;
+ if(!prStatusSupported)
+ return false;
+
+
+ PrStatus_X86_64 prstat64;
+
+ iovec prstat_iov = {&prstat64, sizeof(prstat64)};
+
+ if(ptrace(PTRACE_GETREGSET, tid_, NT_PRSTATUS, &prstat_iov) != -1) {
+ if(prstat_iov.iov_len==sizeof(prstat64)) {
+ state->fillFrom(prstat64);
+ } else if(prstat_iov.iov_len==sizeof(PrStatus_X86)) {
+ // In this case the actual structure returned is PrStatus_X86,
+ // so copy it to the correct container (reinterpret_cast would
+ // cause UB in any case). Good compiler should be able to optimize this out.
+ PrStatus_X86 prstat32;
+ std::memcpy(&prstat32,&prstat64,sizeof(prstat32));
+ state->fillFrom(prstat32);
+ } else {
+ prStatusSupported=false;
+ qWarning() << "PTRACE_GETREGSET(NT_PRSTATUS) returned unexpected length " << prstat_iov.iov_len;
+ return false;
+ }
+ }
+ else {
+ prStatusSupported=false;
+ perror("PTRACE_GETREGSET(NT_PRSTATUS) failed");
+ return false;
+ }
+ fillSegmentBases(state);
+ return true;
+}
+
+//------------------------------------------------------------------------------
+// Name: fillStateFromSimpleRegs
+// Desc:
+//------------------------------------------------------------------------------
+bool PlatformThread::fillStateFromSimpleRegs(PlatformState* state) {
+
+ user_regs_struct regs;
+ if(ptrace(PTRACE_GETREGS, tid_, 0, &regs) != -1) {
+
+ state->fillFrom(regs);
+ fillSegmentBases(state);
+ return true;
+ }
+ else {
+ perror("PTRACE_GETREGS failed");
+ return false;
+ }
+}
+
+
+//------------------------------------------------------------------------------
+// Name: PlatformThread
+// Desc:
+//------------------------------------------------------------------------------
+PlatformThread::PlatformThread(DebuggerCore *core, IProcess *process, edb::tid_t tid) : core_(core), process_(process), tid_(tid) {
+ assert(process);
+ assert(core);
+}
+
+//------------------------------------------------------------------------------
+// Name:
+// Desc:
+//------------------------------------------------------------------------------
+PlatformThread::~PlatformThread() {
+}
+
+//------------------------------------------------------------------------------
+// Name:
+// Desc:
+//------------------------------------------------------------------------------
+edb::tid_t PlatformThread::tid() const {
+ return tid_;
+}
+
+//------------------------------------------------------------------------------
+// Name:
+// Desc:
+//------------------------------------------------------------------------------
+QString PlatformThread::name() const {
+ struct user_stat thread_stat;
+ int n = get_user_stat(QString("/proc/%1/task/%2/stat").arg(process_->pid()).arg(tid_), &thread_stat);
+ if(n >= 2) {
+ return thread_stat.comm;
+ }
+
+ return QString();
+}
+
+//------------------------------------------------------------------------------
+// Name:
+// Desc:
+//------------------------------------------------------------------------------
+int PlatformThread::priority() const {
+ struct user_stat thread_stat;
+ int n = get_user_stat(QString("/proc/%1/task/%2/stat").arg(process_->pid()).arg(tid_), &thread_stat);
+ if(n >= 30) {
+ return thread_stat.priority;
+ }
+
+ return 0;
+}
+
+//------------------------------------------------------------------------------
+// Name:
+// Desc:
+//------------------------------------------------------------------------------
+edb::address_t PlatformThread::instruction_pointer() const {
+ struct user_stat thread_stat;
+ int n = get_user_stat(QString("/proc/%1/task/%2/stat").arg(process_->pid()).arg(tid_), &thread_stat);
+ if(n >= 18) {
+ return thread_stat.kstkeip;
+ }
+
+ return 0;
+}
+
+//------------------------------------------------------------------------------
+// Name:
+// Desc:
+//------------------------------------------------------------------------------
+QString PlatformThread::runState() const {
+ struct user_stat thread_stat;
+ int n = get_user_stat(QString("/proc/%1/task/%2/stat").arg(process_->pid()).arg(tid_), &thread_stat);
+ if(n >= 3) {
+ switch(thread_stat.state) { // 03
+ case 'R':
+ return tr("%1 (Running)").arg(thread_stat.state);
+ break;
+ case 'S':
+ return tr("%1 (Sleeping)").arg(thread_stat.state);
+ break;
+ case 'D':
+ return tr("%1 (Disk Sleep)").arg(thread_stat.state);
+ break;
+ case 'T':
+ return tr("%1 (Stopped)").arg(thread_stat.state);
+ break;
+ case 't':
+ return tr("%1 (Tracing Stop)").arg(thread_stat.state);
+ break;
+ case 'Z':
+ return tr("%1 (Zombie)").arg(thread_stat.state);
+ break;
+ case 'X':
+ case 'x':
+ return tr("%1 (Dead)").arg(thread_stat.state);
+ break;
+ case 'W':
+ return tr("%1 (Waking/Paging)").arg(thread_stat.state);
+ break;
+ case 'K':
+ return tr("%1 (Wakekill)").arg(thread_stat.state);
+ break;
+ case 'P':
+ return tr("%1 (Parked)").arg(thread_stat.state);
+ break;
+ default:
+ return tr("%1").arg(thread_stat.state);
+ break;
+ }
+ }
+
+ return tr("Unknown");
+}
+
+//------------------------------------------------------------------------------
+// Name: step
+// Desc: steps this thread one instruction, passing the signal that stopped it
+// (unless the signal was SIGSTOP)
+//------------------------------------------------------------------------------
+void PlatformThread::step() {
+ core_->ptrace_step(tid_, resume_code(status_));
+}
+
+//------------------------------------------------------------------------------
+// Name: step
+// Desc: steps this thread one instruction, passing the signal that stopped it
+// (unless the signal was SIGSTOP, or the passed status != DEBUG_EXCEPTION_NOT_HANDLED)
+//------------------------------------------------------------------------------
+void PlatformThread::step(edb::EVENT_STATUS status) {
+ const int code = (status == edb::DEBUG_EXCEPTION_NOT_HANDLED) ? resume_code(status_) : 0;
+ core_->ptrace_step(tid_, code);
+}
+
+//------------------------------------------------------------------------------
+// Name: resume
+// Desc: resumes this thread, passing the signal that stopped it
+// (unless the signal was SIGSTOP)
+//------------------------------------------------------------------------------
+void PlatformThread::resume() {
+ core_->ptrace_continue(tid_, resume_code(status_));
+}
+
+//------------------------------------------------------------------------------
+// Name: resume
+// Desc: resumes this thread , passing the signal that stopped it
+// (unless the signal was SIGSTOP, or the passed status != DEBUG_EXCEPTION_NOT_HANDLED)
+//------------------------------------------------------------------------------
+void PlatformThread::resume(edb::EVENT_STATUS status) {
+ const int code = (status == edb::DEBUG_EXCEPTION_NOT_HANDLED) ? resume_code(status_) : 0;
+ core_->ptrace_continue(tid_, code);
+}
+
+//------------------------------------------------------------------------------
+// Name: get_state
+// Desc:
+//------------------------------------------------------------------------------
+void PlatformThread::get_state(State *state) {
+ // TODO: assert that we are paused
+
+ core_->detectDebuggeeBitness();
+
+ if(auto state_impl = static_cast<PlatformState *>(state->impl_)) {
+ // State must be cleared before filling to zero all presence flags, otherwise something
+ // may remain not updated. Also, this way we'll mark all the unfilled values.
+ state_impl->clear();
+
+ if(EDB_IS_64_BIT)
+ fillStateFromSimpleRegs(state_impl); // 64-bit GETREGS call always returns 64-bit state, so use it
+ else if(!fillStateFromPrStatus(state_impl)) // if EDB is 32 bit, use GETREGSET so that we get 64-bit state for 64-bit debuggee
+ fillStateFromSimpleRegs(state_impl); // failing that, try to just get what we can
+
+ long ptraceStatus=0;
+
+ // First try to get full XSTATE
+ X86XState xstate;
+ iovec iov={&xstate,sizeof(xstate)};
+ ptraceStatus=ptrace(PTRACE_GETREGSET, tid_, NT_X86_XSTATE, &iov);
+ if(ptraceStatus==-1 || !state_impl->fillFrom(xstate,iov.iov_len)) {
+
+ // No XSTATE available, get just floating point and SSE registers
+ static bool getFPXRegsSupported=(EDB_IS_32_BIT ? true : false);
+ UserFPXRegsStructX86 fpxregs;
+ // This should be automatically optimized out on amd64. If not, not a big deal.
+ // Avoiding conditional compilation to facilitate syntax error checking
+ if(getFPXRegsSupported)
+ getFPXRegsSupported=(ptrace(PTRACE_GETFPXREGS, tid_, 0, &fpxregs)!=-1);
+
+ if(getFPXRegsSupported) {
+ state_impl->fillFrom(fpxregs);
+ } else {
+ // No GETFPXREGS: on x86 this means SSE is not supported
+ // on x86_64 FPREGS already contain SSE state
+ user_fpregs_struct fpregs;
+ if((ptraceStatus=ptrace(PTRACE_GETFPREGS, tid_, 0, &fpregs))!=-1)
+ state_impl->fillFrom(fpregs);
+ else
+ perror("PTRACE_GETFPREGS failed");
+ }
+ }
+
+ // debug registers
+ for(std::size_t i=0;i<8;++i)
+ state_impl->x86.dbgRegs[i] = get_debug_register(i);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Name: set_state
+// Desc:
+//------------------------------------------------------------------------------
+void PlatformThread::set_state(const State &state) {
+
+ // TODO: assert that we are paused
+
+ if(auto state_impl = static_cast<PlatformState *>(state.impl_)) {
+ bool setPrStatusDone=false;
+ if(EDB_IS_32_BIT && state_impl->is64Bit()) {
+ // Try to set 64-bit state
+ PrStatus_X86_64 prstat64;
+ state_impl->fillStruct(prstat64);
+ iovec prstat_iov = {&prstat64, sizeof(prstat64)};
+ if(ptrace(PTRACE_SETREGSET, tid_, NT_PRSTATUS, &prstat_iov) != -1)
+ setPrStatusDone=true;
+ else
+ perror("PTRACE_SETREGSET failed");
+ }
+ // Fallback to setting 32-bit set
+ if(!setPrStatusDone) {
+ user_regs_struct regs;
+ state_impl->fillStruct(regs);
+ ptrace(PTRACE_SETREGS, tid_, 0, &regs);
+ }
+
+ // debug registers
+ for(std::size_t i=0;i<8;++i)
+ set_debug_register(i,state_impl->x86.dbgRegs[i]);
+
+ static bool xsaveSupported=true; // hope for the best, adjust for reality
+ if(xsaveSupported) {
+ X86XState xstate;
+ const auto size=state_impl->fillStruct(xstate);
+ iovec iov={&xstate,size};
+ if(ptrace(PTRACE_SETREGSET, tid_, NT_X86_XSTATE, &iov)==-1)
+ xsaveSupported=false;
+ }
+ // If xsave/xrstor appears unsupported, fallback to fxrstor
+ // NOTE: it's not "else", it's an independent check for possibly modified flag
+ if(!xsaveSupported) {
+ static bool setFPXRegsSupported=EDB_IS_32_BIT;
+ if(setFPXRegsSupported) {
+ UserFPXRegsStructX86 fpxregs;
+ state_impl->fillStruct(fpxregs);
+ setFPXRegsSupported=(ptrace(PTRACE_SETFPXREGS, tid_, 0, &fpxregs)!=-1);
+ }
+ if(!setFPXRegsSupported) {
+ // No SETFPXREGS: on x86 this means SSE is not supported
+ // on x86_64 FPREGS already contain SSE state
+ // Just set fpregs then
+ user_fpregs_struct fpregs;
+ state_impl->fillStruct(fpregs);
+ if(ptrace(PTRACE_SETFPREGS, tid_, 0, &fpregs)==-1)
+ perror("PTRACE_SETFPREGS failed");
+ }
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// Name: get_debug_register
+// Desc:
+//------------------------------------------------------------------------------
+unsigned long PlatformThread::get_debug_register(std::size_t n) {
+ return ptrace(PTRACE_PEEKUSER, tid_, offsetof(struct user, u_debugreg[n]), 0);
+}
+
+//------------------------------------------------------------------------------
+// Name: set_debug_register
+// Desc:
+//------------------------------------------------------------------------------
+long PlatformThread::set_debug_register(std::size_t n, long value) {
+ return ptrace(PTRACE_POKEUSER, tid_, offsetof(struct user, u_debugreg[n]), value);
+}
+
+//------------------------------------------------------------------------------
+// Name: stop
+// Desc:
+//------------------------------------------------------------------------------
+void PlatformThread::stop() {
+ syscall(SYS_tgkill, process_->pid(), tid_, SIGSTOP);
+ // TODO(eteran): should this just be this?
+ //::kill(tid_, SIGSTOP);
+}
+
+
+}