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

599 lines
18 KiB
C++

$NetBSD$
--- plugins/DebuggerCore/unix/netbsd/DebuggerCore.cpp.orig 2017-02-19 02:09:05.311209498 +0000
+++ plugins/DebuggerCore/unix/netbsd/DebuggerCore.cpp
@@ -0,0 +1,593 @@
+/*
+Copyright (C) 2006 - 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/>.
+*/
+
+
+// TODO(eteran): research usage of process_vm_readv, process_vm_writev
+
+#include "DebuggerCore.h"
+#include "Configuration.h"
+#include "FeatureDetect.h"
+#include "MemoryRegions.h"
+#include "PlatformCommon.h"
+#include "PlatformEvent.h"
+#include "PlatformProcess.h"
+#include "PlatformRegion.h"
+#include "PlatformState.h"
+#include "DialogMemoryAccess.h"
+#include "State.h"
+#include "edb.h"
+#include "string_hash.h"
+
+#include <QDebug>
+#include <QDir>
+#include <QSettings>
+
+#include <cerrno>
+#include <cstring>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/ptrace.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <cpuid.h>
+
+#include <unistd.h>
+
+namespace DebuggerCorePlugin {
+
+namespace {
+
+const edb::address_t PageSize = getpagesize();
+
+//------------------------------------------------------------------------------
+// Name: is_numeric
+// Desc: returns true if the string only contains decimal digits
+//------------------------------------------------------------------------------
+bool is_numeric(const QString &s) {
+ for(QChar ch: s) {
+ if(!ch.isDigit()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool in64BitSegment() {
+ bool edbIsIn64BitSegment;
+ // Check that we're running in 64 bit segment: this can be in cases
+ // of LP64 and ILP32 programming models, so we can't rely on sizeof(void*)
+ asm(R"(
+ .byte 0x33,0xc0 # XOR EAX,EAX
+ .byte 0x48 # DEC EAX for 32 bit, REX prefix for 64 bit
+ .byte 0xff,0xc0 # INC EAX for 32 bit, INC RAX due to REX.W in 64 bit
+ )":"=a"(edbIsIn64BitSegment));
+ return edbIsIn64BitSegment;
+}
+
+bool os64Bit(bool edbIsIn64BitSegment) {
+ bool osIs64Bit;
+ if(edbIsIn64BitSegment)
+ osIs64Bit=true;
+ else {
+ // We want to be really sure the OS is 32 bit, so we can't rely on such easy
+ // to (even unintentionally) fake mechanisms as uname(2) (e.g. see setarch(8))
+ asm(R"(.intel_syntax noprefix
+ mov eax,cs
+ cmp ax,0x23 # this value is set for 32-bit processes on 64-bit kernel
+ mov ah,0 # not sure this is really needed: usually the compiler will do
+ # MOVZX EAX,AL, but we have to be certain the result is correct
+ sete al
+ .att_syntax # restore default syntax
+ )":"=a"(osIs64Bit));
+ }
+ return osIs64Bit;
+}
+
+
+
+
+}
+
+//------------------------------------------------------------------------------
+// Name: DebuggerCore
+// Desc: constructor
+//------------------------------------------------------------------------------
+DebuggerCore::DebuggerCore() :
+ binary_info_(nullptr),
+ process_(0),
+ pointer_size_(sizeof(void*)),
+ edbIsIn64BitSegment(in64BitSegment()),
+ osIs64Bit(os64Bit(edbIsIn64BitSegment)),
+ USER_CS_32(osIs64Bit?0x23:0x73),
+ USER_CS_64(osIs64Bit?0x33:0xfff8), // RPL 0 can't appear in user segment registers, so 0xfff8 is safe
+ USER_SS(osIs64Bit?0x2b:0x7b)
+{
+ qDebug() << "EDB is in" << (edbIsIn64BitSegment?"64":"32") << "bit segment";
+ qDebug() << "OS is" << (osIs64Bit?"64":"32") << "bit";
+}
+
+//------------------------------------------------------------------------------
+// Name: has_extension
+// Desc:
+//------------------------------------------------------------------------------
+bool DebuggerCore::has_extension(quint64 ext) const {
+
+ const auto mmxHash=edb::string_hash("MMX");
+ const auto xmmHash=edb::string_hash("XMM");
+ const auto ymmHash=edb::string_hash("YMM");
+ if(EDB_IS_64_BIT && (ext==xmmHash || ext==mmxHash))
+ return true;
+
+ quint32 eax;
+ quint32 ebx;
+ quint32 ecx;
+ quint32 edx;
+ __cpuid(1, eax, ebx, ecx, edx);
+
+ switch(ext) {
+ case mmxHash:
+ return (edx & bit_MMX);
+ case xmmHash:
+ return (edx & bit_SSE);
+ case ymmHash:
+ {
+ // Check OSXSAVE and AVX feature flags
+ if((ecx&0x18000000)!=0x18000000)
+ return false;
+ // Get XCR0, must be exactly after OSXSAVE feature check, otherwise #UD
+ asm volatile("xgetbv" : "=a"(eax),"=d"(edx) : "c"(0));
+ // Check that the OS has enabled XMM and YMM state support
+ if((eax&0x6)!=0x6)
+ return false;
+ return true;
+ }
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+//------------------------------------------------------------------------------
+// Name: page_size
+// Desc: returns the size of a page on this system
+//------------------------------------------------------------------------------
+edb::address_t DebuggerCore::page_size() const {
+ return PageSize;
+}
+
+std::size_t DebuggerCore::pointer_size() const {
+ return pointer_size_;
+}
+
+//------------------------------------------------------------------------------
+// Name: ~DebuggerCore
+// Desc: destructor
+//------------------------------------------------------------------------------
+DebuggerCore::~DebuggerCore() {
+ end_debug_session();
+}
+
+//------------------------------------------------------------------------------
+// Name: ptrace_getsiginfo
+// Desc:
+//------------------------------------------------------------------------------
+long DebuggerCore::ptrace_getsiginfo(edb::pid_t pid, ptrace_siginfo_t *siginfo) {
+ Q_ASSERT(siginfo != 0);
+ return ptrace(PT_GET_SIGINFO, pid, siginfo, sizeof(struct ptrace_siginfo));
+}
+
+//------------------------------------------------------------------------------
+// Name: ptrace_traceme
+// Desc:
+//------------------------------------------------------------------------------
+long DebuggerCore::ptrace_traceme() {
+ return ptrace(PT_TRACE_ME, 0, NULL, 0);
+}
+
+//------------------------------------------------------------------------------
+// Name: ptrace_continue
+// Desc:
+//------------------------------------------------------------------------------
+long DebuggerCore::ptrace_continue(edb::pid_t pid, long status) {
+ Q_ASSERT(pid != 0);
+ return ptrace(PT_CONTINUE, pid, (void *)1, status);
+}
+
+//------------------------------------------------------------------------------
+// Name: ptrace_step
+// Desc:
+//------------------------------------------------------------------------------
+long DebuggerCore::ptrace_step(edb::pid_t pid, long status) {
+ Q_ASSERT(pid != 0);
+ return ptrace(PT_STEP, pid, (void *)1, status);
+}
+
+//------------------------------------------------------------------------------
+// Name: handle_event
+// Desc:
+//------------------------------------------------------------------------------
+IDebugEvent::const_pointer DebuggerCore::handle_event(edb::pid_t pid, int status) {
+
+ // was it a thread exit event?
+ if(WIFEXITED(status)) {
+ threads_.clear();
+ return nullptr;
+ }
+
+ auto e = std::make_shared<PlatformEvent>();
+
+ e->pid_ = pid;
+ e->status_ = status;
+ ptrace_getsiginfo(pid, &e->siginfo_);
+
+ return e;
+}
+
+//------------------------------------------------------------------------------
+// Name: wait_debug_event
+// Desc: waits for a debug event, msecs is a timeout
+// it will return false if an error or timeout occurs
+//------------------------------------------------------------------------------
+IDebugEvent::const_pointer DebuggerCore::wait_debug_event(int msecs) {
+
+ if(process_) {
+ if(!native::wait_for_sigchld(msecs)) {
+ int status;
+ const edb::pid_t wpid = native::waitpid(pid(), &status, __WALL | WNOHANG);
+ if(wpid > 0) {
+ return handle_event(pid(), status);
+ }
+ }
+ }
+ return nullptr;
+}
+
+//------------------------------------------------------------------------------
+// Name: attach
+// Desc:
+//------------------------------------------------------------------------------
+QString DebuggerCore::attach(edb::pid_t pid) {
+
+ static const QString statusOK{};
+
+ end_debug_session();
+
+ lastMeansOfCapture=MeansOfCapture::Attach;
+
+ // create this, so the threads created can refer to it
+ process_ = new PlatformProcess(this, pid);
+
+ int lastErr=ptrace(PT_ATTACH, pid, NULL, 0); // Fail early if we are going to
+ if(lastErr) return std::strerror(lastErr);
+
+ pid_ = pid;
+ binary_info_ = edb::v1::get_binary_info(edb::v1::primary_code_region());
+ detectDebuggeeBitness();
+ return statusOK;
+}
+
+//------------------------------------------------------------------------------
+// Name: detach
+// Desc:
+//------------------------------------------------------------------------------
+void DebuggerCore::detach() {
+ if(process_) {
+
+ clear_breakpoints();
+
+ ptrace(PT_DETACH, pid(), NULL, 0);
+
+ delete process_;
+ process_ = nullptr;
+
+ reset();
+ }
+}
+
+//------------------------------------------------------------------------------
+// Name: kill
+// Desc:
+//------------------------------------------------------------------------------
+void DebuggerCore::kill() {
+ if(attached()) {
+ clear_breakpoints();
+
+ ptrace(PT_KILL, pid(), NULL, 0);
+
+ native::waitpid(pid(), 0, __WALL);
+
+ delete process_;
+ process_ = 0;
+
+ reset();
+ }
+}
+
+void DebuggerCore::detectDebuggeeBitness() {
+#if defined(__i386__)
+ qDebug() << "Debuggee is now 32 bit";
+ CapstoneEDB::init(CapstoneEDB::Architecture::ARCH_X86);
+#elif defined(__x86_64__)
+ qDebug() << "Debuggee is now 64 bit";
+ CapstoneEDB::init(CapstoneEDB::Architecture::ARCH_AMD64);
+#else
+#error portme
+#endif
+}
+
+//------------------------------------------------------------------------------
+// Name: open
+// Desc:
+//------------------------------------------------------------------------------
+QString DebuggerCore::open(const QString &path, const QString &cwd, const QList<QByteArray> &args, const QString &tty) {
+
+ static const QString statusOK{};
+
+ end_debug_session();
+
+ lastMeansOfCapture=MeansOfCapture::Launch;
+
+ static std::size_t sharedMemSize=getpagesize();
+ const auto sharedMem=static_cast<QChar*>(::mmap(nullptr,sharedMemSize,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0));
+ std::memset(sharedMem,0,sharedMemSize);
+
+ switch(pid_t pid = fork()) {
+ case 0:
+ {
+ // we are in the child now...
+
+ // set ourselves (the child proc) up to be traced
+ ptrace_traceme();
+
+ // redirect it's I/O
+ if(!tty.isEmpty()) {
+ FILE *const std_out = freopen(qPrintable(tty), "r+b", stdout);
+ FILE *const std_in = freopen(qPrintable(tty), "r+b", stdin);
+ FILE *const std_err = freopen(qPrintable(tty), "r+b", stderr);
+
+ Q_UNUSED(std_out);
+ Q_UNUSED(std_in);
+ Q_UNUSED(std_err);
+ }
+
+ // do the actual exec
+ const QString error=execute_process(path, cwd, args);
+ static_assert(std::is_trivially_copyable<QChar>::value,"Can't copy string of QChar to shared memory");
+ std::memcpy(sharedMem,error.constData(),std::min(sizeof(QChar)*error.size(),sharedMemSize-sizeof(QChar)/*prevent overwriting of last null*/));
+
+ // we should never get here!
+ abort();
+ break;
+ }
+ case -1:
+ // error! for some reason we couldn't fork
+ reset();
+ return QObject::tr("Failed to fork");
+ default:
+ // parent
+ do {
+ reset();
+
+ int status;
+ const auto wpidRet=native::waitpid(pid, &status, __WALL);
+ const QString childError(sharedMem);
+ ::munmap(sharedMem,sharedMemSize);
+
+ if(wpidRet == -1)
+ return QObject::tr("waitpid() failed: %1").arg(std::strerror(errno))+(childError.isEmpty()?"":QObject::tr(".\nError returned by child:\n%1.").arg(childError));
+
+ if(WIFEXITED(status))
+ return QObject::tr("The child unexpectedly exited with code %1. Error returned by child:\n%2").arg(WEXITSTATUS(status)).arg(childError);
+ if(WIFSIGNALED(status))
+ return QObject::tr("The child was unexpectedly killed by signal %1. Error returned by child:\n%2").arg(WTERMSIG(status)).arg(childError);
+
+ // This happens when exec failed, but just in case it's something another return some description.
+ if(WIFSTOPPED(status) && WSTOPSIG(status) == SIGABRT)
+ return childError.isEmpty() ? QObject::tr("The child unexpectedly aborted") : childError;
+
+ // the very first event should be a STOP of type SIGTRAP
+ if(!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP) {
+ end_debug_session();
+ return QObject::tr("First event after waitpid() should be a STOP of type SIGTRAP, but wasn't, instead status=0x%1")
+ .arg(status,0,16)+(childError.isEmpty()?"":QObject::tr(".\nError returned by child:\n%1.").arg(childError));
+ }
+
+ // create the process
+ process_ = new PlatformProcess(this, pid);
+
+ pid_ = pid;
+
+ detectDebuggeeBitness();
+
+ return statusOK;
+ } while(0);
+ break;
+ }
+}
+
+
+//------------------------------------------------------------------------------
+// Name: last_means_of_capture
+// Desc: Returns how the last process was captured to debug
+//------------------------------------------------------------------------------
+DebuggerCore::MeansOfCapture DebuggerCore::last_means_of_capture() {
+ return lastMeansOfCapture;
+}
+
+//------------------------------------------------------------------------------
+// Name: reset
+// Desc:
+//------------------------------------------------------------------------------
+void DebuggerCore::reset() {
+ threads_.clear();
+ pid_ = 0;
+}
+
+//------------------------------------------------------------------------------
+// Name: create_state
+// Desc:
+//------------------------------------------------------------------------------
+IState *DebuggerCore::create_state() const {
+ return new PlatformState;
+}
+
+//------------------------------------------------------------------------------
+// Name: enumerate_processes
+// Desc:
+//------------------------------------------------------------------------------
+QMap<edb::pid_t, IProcess::pointer> DebuggerCore::enumerate_processes() const {
+ QMap<edb::pid_t, IProcess::pointer> ret;
+
+ int err;
+ ::kinfo_proc2 *result;
+ int mib[6];
+ size_t length = 0;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC2;
+ mib[2] = KERN_PROC_ALL;
+ mib[3] = 0;
+ mib[4] = sizeof(struct kinfo_proc2);
+retry:
+ mib[5] = 0;
+
+ err = sysctl(mib, __arraycount(mib), NULL, &length, NULL, 0);
+ if (err == -1)
+ return ret;
+
+ result = (::kinfo_proc2 *)malloc(length);
+ if (result == NULL)
+ return ret;
+
+ mib[5] = length / sizeof(struct kinfo_proc2);
+ err = sysctl(mib, __arraycount(mib), result, &length, NULL, 0);
+ if (err == -1) {
+ if (errno == ENOMEM)
+ goto retry;
+
+ free(result);
+ return ret;
+ }
+
+ for (size_t i = 0; i < length / sizeof(kinfo_proc2); i++) {
+ const edb::pid_t pid = result[i].p_pid;
+ ret.insert(pid, std::make_shared<PlatformProcess>(const_cast<DebuggerCore*>(this), pid));
+ }
+
+ free(result);
+
+ return ret;
+}
+
+
+//------------------------------------------------------------------------------
+// Name:
+// Desc:
+//------------------------------------------------------------------------------
+edb::pid_t DebuggerCore::parent_pid(edb::pid_t pid) const {
+
+ struct user_stat user_stat;
+ int n = get_user_stat(pid, &user_stat);
+ if(n >= 4) {
+ return user_stat.ppid;
+ }
+
+ return 0;
+}
+
+//------------------------------------------------------------------------------
+// Name:
+// Desc: Returns EDB's native CPU type
+//------------------------------------------------------------------------------
+quint64 DebuggerCore::cpu_type() const {
+ if(EDB_IS_32_BIT)
+ return edb::string_hash("x86");
+ else
+ return edb::string_hash("x86-64");
+}
+
+
+//------------------------------------------------------------------------------
+// Name:
+// Desc:
+//------------------------------------------------------------------------------
+QString DebuggerCore::format_pointer(edb::address_t address) const {
+ return address.toPointerString();
+}
+
+//------------------------------------------------------------------------------
+// Name:
+// Desc:
+//------------------------------------------------------------------------------
+QString DebuggerCore::stack_pointer() const {
+ if(edb::v1::debuggeeIs32Bit())
+ return "esp";
+ else
+ return "rsp";
+}
+
+//------------------------------------------------------------------------------
+// Name:
+// Desc:
+//------------------------------------------------------------------------------
+QString DebuggerCore::frame_pointer() const {
+ if(edb::v1::debuggeeIs32Bit())
+ return "ebp";
+ else
+ return "rbp";
+}
+
+//------------------------------------------------------------------------------
+// Name:
+// Desc:
+//------------------------------------------------------------------------------
+QString DebuggerCore::instruction_pointer() const {
+ if(edb::v1::debuggeeIs32Bit())
+ return "eip";
+ else
+ return "rip";
+}
+
+//------------------------------------------------------------------------------
+// Name: flag_register
+// Desc: Returns the name of the flag register as a QString.
+//------------------------------------------------------------------------------
+QString DebuggerCore::flag_register() const {
+ if(edb::v1::debuggeeIs32Bit())
+ return "eflags";
+ else
+ return "rflags";
+}
+
+//------------------------------------------------------------------------------
+// Name: process
+// Desc:
+//------------------------------------------------------------------------------
+IProcess *DebuggerCore::process() const {
+ return process_;
+}
+
+#if QT_VERSION < 0x050000
+Q_EXPORT_PLUGIN2(DebuggerCore, DebuggerCore)
+#endif
+
+}