773 lines
23 KiB
C++
773 lines
23 KiB
C++
$NetBSD$
|
|
|
|
--- plugins/DebuggerCore/unix/netbsd/PlatformProcess.cpp.orig 2017-02-19 02:09:05.379589420 +0000
|
|
+++ plugins/DebuggerCore/unix/netbsd/PlatformProcess.cpp
|
|
@@ -0,0 +1,767 @@
|
|
+/*
|
|
+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/>.
|
|
+*/
|
|
+
|
|
+#define _LARGEFILE64_SOURCE
|
|
+#include "PlatformProcess.h"
|
|
+#include "DebuggerCore.h"
|
|
+#include "PlatformCommon.h"
|
|
+#include "PlatformRegion.h"
|
|
+#include "MemoryRegions.h"
|
|
+#include "edb.h"
|
|
+#include "linker.h"
|
|
+
|
|
+#include <QByteArray>
|
|
+#include <QFile>
|
|
+#include <QFileInfo>
|
|
+#include <QTextStream>
|
|
+#include <QDateTime>
|
|
+
|
|
+#include <boost/functional/hash.hpp>
|
|
+#include <fstream>
|
|
+
|
|
+#include <sys/mman.h>
|
|
+#include <sys/ptrace.h>
|
|
+#include <sys/types.h>
|
|
+#include <unistd.h>
|
|
+#include <pwd.h>
|
|
+#include <elf.h>
|
|
+
|
|
+namespace DebuggerCorePlugin {
|
|
+namespace {
|
|
+
|
|
+// Used as size of ptrace word
|
|
+#define EDB_WORDSIZE sizeof(long)
|
|
+
|
|
+void set_ok(bool &ok, long value) {
|
|
+ ok = (value != -1) || (errno == 0);
|
|
+}
|
|
+
|
|
+QStringList split_max(const QString &str, const int &maxparts) {
|
|
+ int prev_idx = 0, idx = 0;
|
|
+ QStringList items;
|
|
+ for (const QChar &c : str) {
|
|
+ if (c == ' ') {
|
|
+ if (prev_idx < idx) {
|
|
+ if (items.size() < maxparts - 1)
|
|
+ items << str.mid(prev_idx, idx - prev_idx);
|
|
+ else {
|
|
+ items << str.right(str.size() - prev_idx);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ prev_idx = idx + 1;
|
|
+ }
|
|
+ ++idx;
|
|
+ }
|
|
+ if (prev_idx < str.size() && items.size() < maxparts) {
|
|
+ items << str.right(str.size() - prev_idx);
|
|
+ }
|
|
+ return items;
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name: process_map_line
|
|
+// Desc: parses the data from a line of a memory map file
|
|
+//------------------------------------------------------------------------------
|
|
+IRegion::pointer process_map_line(const QString &line) {
|
|
+
|
|
+ edb::address_t start;
|
|
+ edb::address_t end;
|
|
+ edb::address_t base;
|
|
+ IRegion::permissions_t permissions;
|
|
+ QString name;
|
|
+
|
|
+ const QStringList items = split_max(line, 6);
|
|
+ if(items.size() >= 3) {
|
|
+ bool ok;
|
|
+ const QStringList bounds = items[0].split("-");
|
|
+ if(bounds.size() == 2) {
|
|
+ start = edb::address_t::fromHexString(bounds[0],&ok);
|
|
+ if(ok) {
|
|
+ end = edb::address_t::fromHexString(bounds[1],&ok);
|
|
+ if(ok) {
|
|
+ base = edb::address_t::fromHexString(items[2],&ok);
|
|
+ if(ok) {
|
|
+ const QString perms = items[1];
|
|
+ permissions = 0;
|
|
+ if(perms[0] == 'r') permissions |= PROT_READ;
|
|
+ if(perms[1] == 'w') permissions |= PROT_WRITE;
|
|
+ if(perms[2] == 'x') permissions |= PROT_EXEC;
|
|
+
|
|
+ if(items.size() >= 6) {
|
|
+ name = items[5];
|
|
+ }
|
|
+
|
|
+ return std::make_shared<PlatformRegion>(start, end, base, name, permissions);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return nullptr;;
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name:
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+template<class Addr>
|
|
+QList<Module> loaded_modules_(const IProcess* process, const std::unique_ptr<IBinary> &binary_info_) {
|
|
+ QList<Module> ret;
|
|
+
|
|
+ if(binary_info_) {
|
|
+ edb::linux::r_debug<Addr> dynamic_info;
|
|
+ if(const edb::address_t debug_pointer = binary_info_->debug_pointer()) {
|
|
+ if(process) {
|
|
+ if(process->read_bytes(debug_pointer, &dynamic_info, sizeof(dynamic_info))) {
|
|
+ if(dynamic_info.r_map) {
|
|
+
|
|
+ auto link_address = edb::address_t::fromZeroExtended(dynamic_info.r_map);
|
|
+
|
|
+ while(link_address) {
|
|
+
|
|
+ edb::linux::link_map<Addr> map;
|
|
+ if(process->read_bytes(link_address, &map, sizeof(map))) {
|
|
+ char path[PATH_MAX];
|
|
+ if(!process->read_bytes(edb::address_t::fromZeroExtended(map.l_name), &path, sizeof(path))) {
|
|
+ path[0] = '\0';
|
|
+ }
|
|
+
|
|
+ if(map.l_addr) {
|
|
+ Module module;
|
|
+ module.name = path;
|
|
+ module.base_address = map.l_addr;
|
|
+ ret.push_back(module);
|
|
+ }
|
|
+
|
|
+ link_address = edb::address_t::fromZeroExtended(map.l_next);
|
|
+ } else {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // fallback
|
|
+ if(ret.isEmpty()) {
|
|
+ const QList<IRegion::pointer> r = edb::v1::memory_regions().regions();
|
|
+ QSet<QString> found_modules;
|
|
+
|
|
+ for(const IRegion::pointer ®ion: r) {
|
|
+
|
|
+ // we assume that modules will be listed by absolute path
|
|
+ if(region->name().startsWith("/")) {
|
|
+ if(!found_modules.contains(region->name())) {
|
|
+ Module module;
|
|
+ module.name = region->name();
|
|
+ module.base_address = region->start();
|
|
+ found_modules.insert(region->name());
|
|
+ ret.push_back(module);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name: PlatformProcess
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+PlatformProcess::PlatformProcess(DebuggerCore *core, edb::pid_t pid) : core_(core), pid_(pid), ro_mem_file_(0), rw_mem_file_(0) {
|
|
+ if (!core_->proc_mem_read_broken_) {
|
|
+ QFile* memory_file = new QFile(QString("/proc/%1/mem").arg(pid_));
|
|
+ auto flags = QIODevice::ReadOnly | QIODevice::Unbuffered;
|
|
+ if (!core_->proc_mem_write_broken_) {
|
|
+ flags |= QIODevice::WriteOnly;
|
|
+ }
|
|
+ if (memory_file->open(flags)) {
|
|
+ ro_mem_file_ = memory_file;
|
|
+ if (!core_->proc_mem_write_broken_) {
|
|
+ rw_mem_file_ = memory_file;
|
|
+ }
|
|
+ } else {
|
|
+ delete memory_file;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name: ~PlatformProcess
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+PlatformProcess::~PlatformProcess() {
|
|
+ if (ro_mem_file_) {
|
|
+ delete ro_mem_file_;
|
|
+ }
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name: seek_addr
|
|
+// Desc: seeks memory file to given address, taking possible negativity of the
|
|
+// address into account
|
|
+//------------------------------------------------------------------------------
|
|
+void seek_addr(QFile& file, edb::address_t address) {
|
|
+ if(address <= UINT64_MAX/2) {
|
|
+ file.seek(address);
|
|
+ } else {
|
|
+ const int fd=file.handle();
|
|
+ // Seek in two parts to avoid specifying negative offset: off64_t is a signed type
|
|
+ const off64_t halfAddressTruncated=address>>1;
|
|
+ lseek64(fd,halfAddressTruncated,SEEK_SET);
|
|
+ const off64_t secondHalfAddress=address-halfAddressTruncated;
|
|
+ lseek64(fd,secondHalfAddress,SEEK_CUR);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name: read_bytes
|
|
+// Desc: reads <len> bytes into <buf> starting at <address>
|
|
+// Note: returns the number of bytes read <N>
|
|
+// Note: if the read is short, only the first <N> bytes are defined
|
|
+//------------------------------------------------------------------------------
|
|
+std::size_t PlatformProcess::read_bytes(edb::address_t address, void* buf, std::size_t len) const {
|
|
+ quint64 read = 0;
|
|
+
|
|
+ Q_ASSERT(buf);
|
|
+ Q_ASSERT(core_->process_ == this);
|
|
+
|
|
+ auto ptr = reinterpret_cast<char *>(buf);
|
|
+
|
|
+ if(len != 0) {
|
|
+
|
|
+ // small reads take the fast path
|
|
+ if(len == 1) {
|
|
+
|
|
+ auto it = core_->breakpoints_.find(address);
|
|
+ if(it != core_->breakpoints_.end()) {
|
|
+ *ptr = (*it)->original_byte();
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ if(ro_mem_file_) {
|
|
+ seek_addr(*ro_mem_file_, address);
|
|
+ read = ro_mem_file_->read(ptr, 1);
|
|
+ if (read == 1) {
|
|
+ return 1;
|
|
+ }
|
|
+ return 0;
|
|
+ } else {
|
|
+ bool ok;
|
|
+ quint8 x = read_byte_via_ptrace(address, &ok);
|
|
+ if(ok) {
|
|
+ *ptr = x;
|
|
+ return 1;
|
|
+ }
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if(ro_mem_file_) {
|
|
+ seek_addr(*ro_mem_file_, address);
|
|
+ read = ro_mem_file_->read(ptr, len);
|
|
+ if(read == 0 || read == quint64(-1)) {
|
|
+ return 0;
|
|
+ }
|
|
+ } else {
|
|
+ for(std::size_t index = 0; index < len; ++index) {
|
|
+
|
|
+ // read a byte, if we failed, we are done
|
|
+ bool ok;
|
|
+ const quint8 x = read_byte_via_ptrace(address + index, &ok);
|
|
+ if(!ok) {
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ // store it
|
|
+ reinterpret_cast<char*>(buf)[index] = x;
|
|
+
|
|
+ ++read;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // replace any breakpoints
|
|
+ for(const IBreakpoint::pointer &bp: core_->breakpoints_) {
|
|
+ if(bp->address() >= address && bp->address() < (address + read)) {
|
|
+ // show the original bytes in the buffer..
|
|
+ ptr[bp->address() - address] = bp->original_byte();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return read;
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name: write_bytes
|
|
+// Desc: writes <len> bytes from <buf> starting at <address>
|
|
+//------------------------------------------------------------------------------
|
|
+std::size_t PlatformProcess::write_bytes(edb::address_t address, const void *buf, std::size_t len) {
|
|
+ quint64 written = 0;
|
|
+
|
|
+ Q_ASSERT(buf);
|
|
+ Q_ASSERT(core_->process_ == this);
|
|
+
|
|
+ if(len != 0) {
|
|
+ if(rw_mem_file_) {
|
|
+ seek_addr(*rw_mem_file_,address);
|
|
+ written = rw_mem_file_->write(reinterpret_cast<const char *>(buf), len);
|
|
+ if(written == 0 || written == quint64(-1)) {
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+ else {
|
|
+ // TODO write whole words at a time using ptrace_poke.
|
|
+ for(std::size_t byteIndex=0;byteIndex<len;++byteIndex) {
|
|
+ bool ok=false;
|
|
+ write_byte_via_ptrace(address+byteIndex, *(reinterpret_cast<const char*>(buf)+byteIndex), &ok);
|
|
+ if(!ok) return written;
|
|
+ ++written;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return written;
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name: read_pages
|
|
+// Desc: reads <count> pages from the process starting at <address>
|
|
+// Note: buf's size must be >= count * core_->page_size()
|
|
+// Note: address should be page aligned.
|
|
+//------------------------------------------------------------------------------
|
|
+std::size_t PlatformProcess::read_pages(edb::address_t address, void *buf, std::size_t count) const {
|
|
+ Q_ASSERT(buf);
|
|
+ Q_ASSERT(core_->process_ == this);
|
|
+ return read_bytes(address, buf, count * core_->page_size()) / core_->page_size();
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name:
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+QDateTime PlatformProcess::start_time() const {
|
|
+ QFileInfo info(QString("/proc/%1/stat").arg(pid_));
|
|
+ return info.created();
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name:
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+QList<QByteArray> PlatformProcess::arguments() const {
|
|
+ QList<QByteArray> ret;
|
|
+
|
|
+ if(pid_ != 0) {
|
|
+ const QString command_line_file(QString("/proc/%1/cmdline").arg(pid_));
|
|
+ QFile file(command_line_file);
|
|
+
|
|
+ if(file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
+ QTextStream in(&file);
|
|
+
|
|
+ QByteArray s;
|
|
+ QChar ch;
|
|
+
|
|
+ while(in.status() == QTextStream::Ok) {
|
|
+ in >> ch;
|
|
+ if(ch == '\0') {
|
|
+ if(!s.isEmpty()) {
|
|
+ ret << s;
|
|
+ }
|
|
+ s.clear();
|
|
+ } else {
|
|
+ s += ch;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if(!s.isEmpty()) {
|
|
+ ret << s;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name:
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+QString PlatformProcess::current_working_directory() const {
|
|
+ return edb::v1::symlink_target(QString("/proc/%1/cwd").arg(pid_));
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name:
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+QString PlatformProcess::executable() const {
|
|
+ return edb::v1::symlink_target(QString("/proc/%1/exe").arg(pid_));
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name:
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+edb::pid_t PlatformProcess::pid() const {
|
|
+ return pid_;
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name:
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+IProcess::pointer PlatformProcess::parent() const {
|
|
+
|
|
+ struct user_stat user_stat;
|
|
+ int n = get_user_stat(pid_, &user_stat);
|
|
+ if(n >= 4) {
|
|
+ return std::make_shared<PlatformProcess>(core_, user_stat.ppid);
|
|
+ }
|
|
+
|
|
+ return nullptr;
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name:
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+edb::address_t PlatformProcess::code_address() const {
|
|
+ struct user_stat user_stat;
|
|
+ int n = get_user_stat(pid_, &user_stat);
|
|
+ if(n >= 26) {
|
|
+ return user_stat.startcode;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name:
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+edb::address_t PlatformProcess::data_address() const {
|
|
+ struct user_stat user_stat;
|
|
+ int n = get_user_stat(pid_, &user_stat);
|
|
+ if(n >= 27) {
|
|
+ return user_stat.endcode + 1; // endcode == startdata ?
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name:
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+QList<IRegion::pointer> PlatformProcess::regions() const {
|
|
+ static QList<IRegion::pointer> regions;
|
|
+ static size_t totalHash = 0;
|
|
+
|
|
+ const QString map_file(QString("/proc/%1/maps").arg(pid_));
|
|
+
|
|
+ // hash the region file to see if it changed or not
|
|
+ {
|
|
+ std::ifstream mf(map_file.toStdString());
|
|
+ size_t newHash = 0;
|
|
+ std::string line;
|
|
+
|
|
+ while(std::getline(mf,line)) {
|
|
+ boost::hash_combine(newHash, line);
|
|
+ }
|
|
+
|
|
+ if(totalHash == newHash) {
|
|
+ return regions;
|
|
+ }
|
|
+
|
|
+ totalHash = newHash;
|
|
+ regions.clear();
|
|
+ }
|
|
+
|
|
+ // it changed, so let's process it
|
|
+ QFile file(map_file);
|
|
+ if(file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
+
|
|
+ QTextStream in(&file);
|
|
+ QString line = in.readLine();
|
|
+
|
|
+ while(!line.isNull()) {
|
|
+ if(IRegion::pointer region = process_map_line(line)) {
|
|
+ regions.push_back(region);
|
|
+ }
|
|
+ line = in.readLine();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return regions;
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name: read_byte
|
|
+// Desc: the base implementation of reading a byte
|
|
+//------------------------------------------------------------------------------
|
|
+quint8 PlatformProcess::read_byte_via_ptrace(edb::address_t address, bool *ok) const {
|
|
+ // TODO(eteran): assert that we are paused
|
|
+
|
|
+ Q_ASSERT(ok);
|
|
+ Q_ASSERT(core_->process_ == this);
|
|
+
|
|
+ *ok = false;
|
|
+
|
|
+ // if this spot is unreadable, then just return 0xff, otherwise
|
|
+ // continue as normal.
|
|
+
|
|
+ // core_->page_size() - 1 will always be 0xf* because pagesizes
|
|
+ // are always 0x10*, so the masking works
|
|
+ // range of nBytesToNextPage is [1..n] where n=pagesize, and we have to adjust
|
|
+ // if nByteToNextPage < wordsize
|
|
+ const edb::address_t nBytesToNextPage = core_->page_size() - (address & (core_->page_size() - 1));
|
|
+
|
|
+ // Avoid crossing page boundary, since next page may be unreadable
|
|
+ const edb::address_t addressShift = nBytesToNextPage < EDB_WORDSIZE ? EDB_WORDSIZE - nBytesToNextPage : 0;
|
|
+ address -= addressShift;
|
|
+
|
|
+ const long value = ptrace_peek(address, ok);
|
|
+
|
|
+ if(*ok) {
|
|
+ quint8 result;
|
|
+ // We aren't interested in `value` as in number, it's just a buffer, so no endianness magic.
|
|
+ // Just have to compensate for `addressShift` when reading it.
|
|
+ std::memcpy(&result,reinterpret_cast<const char*>(&value)+addressShift,sizeof result);
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+ return 0xff;
|
|
+}
|
|
+
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name: write_byte
|
|
+// Desc: writes a single byte at a given address via ptrace API.
|
|
+// Note: assumes the this will not trample any breakpoints, must be handled
|
|
+// in calling code!
|
|
+//------------------------------------------------------------------------------
|
|
+void PlatformProcess::write_byte_via_ptrace(edb::address_t address, quint8 value, bool *ok) {
|
|
+ // TODO(eteran): assert that we are paused
|
|
+
|
|
+ Q_ASSERT(ok);
|
|
+ Q_ASSERT(core_->process_ == this);
|
|
+
|
|
+ *ok = false;
|
|
+
|
|
+ // core_->page_size() - 1 will always be 0xf* because pagesizes
|
|
+ // are always 0x10*, so the masking works
|
|
+ // range of nBytesToNextPage is [1..n] where n=pagesize, and we have to adjust
|
|
+ // if nBytesToNextPage < wordsize
|
|
+ const edb::address_t nBytesToNextPage = core_->page_size() - (address & (core_->page_size() - 1));
|
|
+
|
|
+ // Avoid crossing page boundary, since next page may be inaccessible
|
|
+ const edb::address_t addressShift = nBytesToNextPage < EDB_WORDSIZE ? EDB_WORDSIZE - nBytesToNextPage : 0;
|
|
+ address -= addressShift;
|
|
+
|
|
+ long word = ptrace_peek(address, ok);
|
|
+ if(!*ok) return;
|
|
+
|
|
+ // We aren't interested in `value` as in number, it's just a buffer, so no endianness magic.
|
|
+ // Just have to compensate for `addressShift` when writing it.
|
|
+ std::memcpy(reinterpret_cast<char*>(&word)+addressShift,&value,sizeof value);
|
|
+
|
|
+ *ok = ptrace_poke(address, word);
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name: ptrace_peek
|
|
+// Desc:
|
|
+// Note: this will fail on newer versions of linux if called from a
|
|
+// different thread than the one which attached to process
|
|
+//------------------------------------------------------------------------------
|
|
+long PlatformProcess::ptrace_peek(edb::address_t address, bool *ok) const {
|
|
+ Q_ASSERT(ok);
|
|
+ Q_ASSERT(core_->process_ == this);
|
|
+
|
|
+ if (EDB_IS_32_BIT && address > 0xffffffffULL) {
|
|
+ // 32 bit ptrace can't handle such long addresses
|
|
+ *ok = false;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ errno = 0;
|
|
+ // NOTE: on some Linux systems ptrace prototype has ellipsis instead of third and fourth arguments
|
|
+ // Thus we can't just pass address as is on IA32 systems: it'd put 64 bit integer on stack and cause UB
|
|
+ auto nativeAddress=reinterpret_cast<const void* const>(address.toUint());
|
|
+ const long v = ptrace(PTRACE_PEEKTEXT, pid_, nativeAddress, 0);
|
|
+ set_ok(*ok, v);
|
|
+ return v;
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name: ptrace_poke
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+bool PlatformProcess::ptrace_poke(edb::address_t address, long value) {
|
|
+
|
|
+ Q_ASSERT(core_->process_ == this);
|
|
+
|
|
+ if (EDB_IS_32_BIT && address > 0xffffffffULL) {
|
|
+ // 32 bit ptrace can't handle such long addresses
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ // NOTE: on some Linux systems ptrace prototype has ellipsis instead of third and fourth arguments
|
|
+ // Thus we can't just pass address as is on IA32 systems: it'd put 64 bit integer on stack and cause UB
|
|
+ auto nativeAddress=reinterpret_cast<const void* const>(address.toUint());
|
|
+ return ptrace(PTRACE_POKETEXT, pid_, nativeAddress, value) != -1;
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name: threads
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+QList<IThread::pointer> PlatformProcess::threads() const {
|
|
+
|
|
+ Q_ASSERT(core_->process_ == this);
|
|
+
|
|
+ QList<IThread::pointer> threadList;
|
|
+
|
|
+ for(auto &thread : core_->threads_) {
|
|
+ threadList.push_back(thread);
|
|
+ }
|
|
+
|
|
+ return threadList;
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name: current_thread
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+IThread::pointer PlatformProcess::current_thread() const {
|
|
+
|
|
+ Q_ASSERT(core_->process_ == this);
|
|
+
|
|
+ auto it = core_->threads_.find(core_->active_thread_);
|
|
+ if(it != core_->threads_.end()) {
|
|
+ return it.value();
|
|
+ }
|
|
+ return IThread::pointer();
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name:
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+edb::uid_t PlatformProcess::uid() const {
|
|
+
|
|
+ const QFileInfo info(QString("/proc/%1").arg(pid_));
|
|
+ return info.ownerId();
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name:
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+QString PlatformProcess::user() const {
|
|
+ if(const struct passwd *const pwd = ::getpwuid(uid())) {
|
|
+ return pwd->pw_name;
|
|
+ }
|
|
+
|
|
+ return QString();
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name:
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+QString PlatformProcess::name() const {
|
|
+ struct user_stat user_stat;
|
|
+ const int n = get_user_stat(pid_, &user_stat);
|
|
+ if(n >= 2) {
|
|
+ return user_stat.comm;
|
|
+ }
|
|
+
|
|
+ return QString();
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name:
|
|
+// Desc:
|
|
+//------------------------------------------------------------------------------
|
|
+QList<Module> PlatformProcess::loaded_modules() const {
|
|
+ if(edb::v1::debuggeeIs64Bit()) {
|
|
+ return loaded_modules_<Elf64_Addr>(this, core_->binary_info_);
|
|
+ } else if(edb::v1::debuggeeIs32Bit()) {
|
|
+ return loaded_modules_<Elf32_Addr>(this, core_->binary_info_);
|
|
+ } else {
|
|
+ return QList<Module>();
|
|
+ }
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name: pause
|
|
+// Desc: stops *all* threads of a process
|
|
+//------------------------------------------------------------------------------
|
|
+void PlatformProcess::pause() {
|
|
+ // belive it or not, I belive that this is sufficient for all threads
|
|
+ // this is because in the debug event handler above, a SIGSTOP is sent
|
|
+ // to all threads when any event arrives, so no need to explicitly do
|
|
+ // it here. We just need any thread to stop. So we'll just target the
|
|
+ // pid_ which will send it to any one of the threads in the process.
|
|
+ ::kill(pid_, SIGSTOP);
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name: resume
|
|
+// Desc: resumes ALL threads
|
|
+//------------------------------------------------------------------------------
|
|
+void PlatformProcess::resume(edb::EVENT_STATUS status) {
|
|
+ // TODO: assert that we are paused
|
|
+ Q_ASSERT(core_->process_ == this);
|
|
+
|
|
+ if(status != edb::DEBUG_STOP) {
|
|
+ if(IThread::pointer thread = current_thread()) {
|
|
+ thread->resume(status);
|
|
+
|
|
+ // resume the other threads passing the signal they originally reported had
|
|
+ for(auto &other_thread : threads()) {
|
|
+ if(core_->waited_threads_.contains(other_thread->tid())) {
|
|
+ other_thread->resume();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+//------------------------------------------------------------------------------
|
|
+// Name: step
|
|
+// Desc: steps the currently active thread
|
|
+//------------------------------------------------------------------------------
|
|
+void PlatformProcess::step(edb::EVENT_STATUS status) {
|
|
+ // TODO: assert that we are paused
|
|
+ Q_ASSERT(core_->process_ == this);
|
|
+
|
|
+ if(status != edb::DEBUG_STOP) {
|
|
+ if(IThread::pointer thread = current_thread()) {
|
|
+ thread->step(status);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+}
|