Refactor DFProcess-linux-{,wine}.cpp into DFProcess-linux-base.cpp.

develop
Tom Prince 2011-02-23 05:26:33 -05:00
parent c287e54793
commit b3c490857c
8 changed files with 634 additions and 954 deletions

@ -29,8 +29,9 @@ include_directories (${CMAKE_SOURCE_DIR}/library/private/)
SET(PROJECT_HDRS_INTERNAL SET(PROJECT_HDRS_INTERNAL
private/ContextShared.h private/ContextShared.h
private/Internal.h private/Internal.h
private/ProcessInternal.h
private/SHMProcess.h private/SHMProcess.h
private/LinuxProcess.h
private/WindowsProcess.h
) )
SET(PROJECT_HDRS SET(PROJECT_HDRS
@ -141,6 +142,7 @@ include/dfhack/DFstdint_win.h
SET(PROJECT_SRCS_LINUX SET(PROJECT_SRCS_LINUX
DFProcess-linux.cpp DFProcess-linux.cpp
DFProcess-linux-base.cpp
DFProcess-linux-SHM.cpp DFProcess-linux-SHM.cpp
DFProcess-linux-wine.cpp DFProcess-linux-wine.cpp
modules/WindowIO-linux.cpp modules/WindowIO-linux.cpp

@ -0,0 +1,439 @@
/*
www.sourceforge.net/projects/dfhack
Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include "Internal.h"
#include "LinuxProcess.h"
#include "dfhack/VersionInfo.h"
#include "dfhack/DFError.h"
#include <errno.h>
#include <sys/ptrace.h>
using namespace DFHack;
LinuxProcessBase::Private::Private(LinuxProcessBase * self_, pid_t pid)
{
my_descriptor = NULL;
my_pid = pid;
attached = false;
suspended = false;
memFileHandle = 0;
self = self_;
}
LinuxProcessBase::LinuxProcessBase(uint32_t pid)
: d(new Private(this, pid))
{
}
bool LinuxProcessBase::isSuspended()
{
return d->suspended;
}
bool LinuxProcessBase::isAttached()
{
return d->attached;
}
bool LinuxProcessBase::isIdentified()
{
return d->identified;
}
LinuxProcessBase::~LinuxProcessBase()
{
if(d->attached)
{
detach();
}
// destroy our copy of the memory descriptor
if(d->my_descriptor)
delete d->my_descriptor;
delete d;
}
VersionInfo * LinuxProcessBase::getDescriptor()
{
return d->my_descriptor;
}
int LinuxProcessBase::getPID()
{
return d->my_pid;
}
//FIXME: implement
bool LinuxProcessBase::getThreadIDs(vector<uint32_t> & threads )
{
return false;
}
//FIXME: cross-reference with ELF segment entries?
void LinuxProcessBase::getMemRanges( vector<t_memrange> & ranges )
{
char buffer[1024];
char permissions[5]; // r/-, w/-, x/-, p/s, 0
sprintf(buffer, "/proc/%lu/maps", d->my_pid);
FILE *mapFile = ::fopen(buffer, "r");
uint64_t offset, device1, device2, node;
while (fgets(buffer, 1024, mapFile))
{
t_memrange temp;
temp.name[0] = 0;
sscanf(buffer, "%llx-%llx %s %llx %2llu:%2llu %llu %s",
&temp.start,
&temp.end,
(char*)&permissions,
&offset, &device1, &device2, &node,
(char*)&temp.name);
temp.read = permissions[0] == 'r';
temp.write = permissions[1] == 'w';
temp.execute = permissions[2] == 'x';
temp.valid = true;
ranges.push_back(temp);
}
}
bool LinuxProcessBase::asyncSuspend()
{
return suspend();
}
bool LinuxProcessBase::suspend()
{
int status;
if(!d->attached)
return false;
if(d->suspended)
return true;
if (kill(d->my_pid, SIGSTOP) == -1)
{
// no, we got an error
perror("kill SIGSTOP error");
return false;
}
while(true)
{
// we wait on the pid
pid_t w = waitpid(d->my_pid, &status, 0);
if (w == -1)
{
// child died
perror("DF exited during suspend call");
return false;
}
// stopped -> let's continue
if (WIFSTOPPED(status))
{
break;
}
}
d->suspended = true;
return true;
}
bool LinuxProcessBase::forceresume()
{
return resume();
}
bool LinuxProcessBase::resume()
{
if(!d->attached)
return false;
if(!d->suspended)
return true;
if (ptrace(PTRACE_CONT, d->my_pid, NULL, NULL) == -1)
{
// no, we got an error
perror("ptrace resume error");
return false;
}
d->suspended = false;
return true;
}
bool LinuxProcessBase::attach()
{
int status;
if(d->attached)
{
if(!d->suspended)
return suspend();
return true;
}
// can we attach?
if (ptrace(PTRACE_ATTACH , d->my_pid, NULL, NULL) == -1)
{
// no, we got an error
perror("ptrace attach error");
cerr << "attach failed on pid " << d->my_pid << endl;
return false;
}
while(true)
{
// we wait on the pid
pid_t w = waitpid(d->my_pid, &status, 0);
if (w == -1)
{
// child died
perror("wait inside attach()");
return false;
}
// stopped -> let's continue
if (WIFSTOPPED(status))
{
break;
}
}
d->suspended = true;
int proc_pid_mem = open(d->memFile.c_str(),O_RDONLY);
if(proc_pid_mem == -1)
{
ptrace(PTRACE_DETACH, d->my_pid, NULL, NULL);
cerr << d->memFile << endl;
cerr << "couldn't open /proc/" << d->my_pid << "/mem" << endl;
perror("open(memFile.c_str(),O_RDONLY)");
return false;
}
else
{
d->attached = true;
d->memFileHandle = proc_pid_mem;
return true; // we are attached
}
}
bool LinuxProcessBase::detach()
{
if(!d->attached) return true;
if(!d->suspended) suspend();
int result = 0;
// close /proc/PID/mem
result = close(d->memFileHandle);
if(result == -1)
{
cerr << "couldn't close /proc/"<< d->my_pid <<"/mem" << endl;
perror("mem file close");
return false;
}
else
{
// detach
result = ptrace(PTRACE_DETACH, d->my_pid, NULL, NULL);
if(result == -1)
{
cerr << "couldn't detach from process pid" << d->my_pid << endl;
perror("ptrace detach");
return false;
}
else
{
d->attached = false;
return true;
}
}
}
void LinuxProcessBase::read (const uint32_t offset, const uint32_t size, uint8_t *target)
{
if(size == 0) return;
ssize_t result;
ssize_t total = 0;
ssize_t remaining = size;
while (total != size)
{
result = pread(d->memFileHandle, target + total ,remaining,offset + total);
if(result == -1)
{
cerr << "pread failed: can't read " << size << " bytes at addres " << offset << endl;
cerr << "errno: " << errno << endl;
errno = 0;
throw Error::MemoryAccessDenied();
}
else
{
total += result;
remaining -= result;
}
}
}
void LinuxProcessBase::readByte (const uint32_t offset, uint8_t &val )
{
read(offset, 1, &val);
}
void LinuxProcessBase::readWord (const uint32_t offset, uint16_t &val)
{
read(offset, 2, (uint8_t *) &val);
}
void LinuxProcessBase::readDWord (const uint32_t offset, uint32_t &val)
{
read(offset, 4, (uint8_t *) &val);
}
void LinuxProcessBase::readFloat (const uint32_t offset, float &val)
{
read(offset, 4, (uint8_t *) &val);
}
void LinuxProcessBase::readQuad (const uint32_t offset, uint64_t &val)
{
read(offset, 8, (uint8_t *) &val);
}
/*
* WRITING
*/
void LinuxProcessBase::writeQuad (uint32_t offset, const uint64_t data)
{
#ifdef HAVE_64_BIT
ptrace(PTRACE_POKEDATA,d->my_pid, offset, data);
#else
ptrace(PTRACE_POKEDATA,d->my_pid, offset, (uint32_t) data);
ptrace(PTRACE_POKEDATA,d->my_pid, offset+4, (uint32_t) (data >> 32));
#endif
}
void LinuxProcessBase::writeDWord (uint32_t offset, uint32_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = Process::readQuad(offset);
orig &= 0xFFFFFFFF00000000;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_pid, offset, orig);
#else
ptrace(PTRACE_POKEDATA,d->my_pid, offset, data);
#endif
}
// using these is expensive.
void LinuxProcessBase::writeWord (uint32_t offset, uint16_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = Process::readQuad(offset);
orig &= 0xFFFFFFFFFFFF0000;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_pid, offset, orig);
#else
uint32_t orig = readDWord(offset);
orig &= 0xFFFF0000;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_pid, offset, orig);
#endif
}
void LinuxProcessBase::writeByte (uint32_t offset, uint8_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = Process::readQuad(offset);
orig &= 0xFFFFFFFFFFFFFF00;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_pid, offset, orig);
#else
uint32_t orig = readDWord(offset);
orig &= 0xFFFFFF00;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_pid, offset, orig);
#endif
}
// blah. I hate the kernel devs for crippling /proc/PID/mem. THIS IS RIDICULOUS
void LinuxProcessBase::write (uint32_t offset, uint32_t size, uint8_t *source)
{
uint32_t indexptr = 0;
while (size > 0)
{
#ifdef HAVE_64_BIT
// quad!
if(size >= 8)
{
writeQuad(offset, *(uint64_t *) (source + indexptr));
offset +=8;
indexptr +=8;
size -=8;
}
else
#endif
// default: we push 4 bytes
if(size >= 4)
{
writeDWord(offset, *(uint32_t *) (source + indexptr));
offset +=4;
indexptr +=4;
size -=4;
}
// last is either three or 2 bytes
else if(size >= 2)
{
writeWord(offset, *(uint16_t *) (source + indexptr));
offset +=2;
indexptr +=2;
size -=2;
}
// finishing move
else if(size == 1)
{
writeByte(offset, *(uint8_t *) (source + indexptr));
return;
}
}
}
const std::string LinuxProcessBase::readCString (uint32_t offset)
{
std::string temp;
char temp_c[256];
int counter = 0;
char r;
do
{
r = Process::readByte(offset+counter);
temp_c[counter] = r;
counter++;
} while (r && counter < 255);
temp_c[counter] = 0;
temp = temp_c;
return temp;
}
string LinuxProcessBase::getPath()
{
char cwd_name[256];
char target_name[1024];
int target_result;
sprintf(cwd_name,"/proc/%d/cwd", getPID());
// resolve /proc/PID/exe link
target_result = readlink(cwd_name, target_name, sizeof(target_name));
target_name[target_result] = '\0';
return(string(target_name));
}

@ -22,7 +22,7 @@ must not be misrepresented as being the original software.
distribution. distribution.
*/ */
#include "Internal.h" #include "Internal.h"
#include "ProcessInternal.h" #include "LinuxProcess.h"
#include "dfhack/VersionInfo.h" #include "dfhack/VersionInfo.h"
#include "dfhack/DFError.h" #include "dfhack/DFError.h"
#include <errno.h> #include <errno.h>
@ -30,37 +30,8 @@ distribution.
#include <stdio.h> #include <stdio.h>
using namespace DFHack; using namespace DFHack;
class WineProcess::Private
{
public:
Private(Process * self_)
{
my_descriptor = NULL;
my_handle = NULL;
my_pid = 0;
attached = false;
suspended = false;
memFileHandle = 0;
self = self_;
};
~Private(){};
VersionInfo * my_descriptor;
Process * self;
pid_t my_handle;
uint32_t my_pid;
string memFile;
int memFileHandle;
bool attached;
bool suspended;
bool identified;
uint32_t STLSTR_buf_off;
uint32_t STLSTR_size_off;
uint32_t STLSTR_cap_off;
bool validate(char * exe_file, uint32_t pid, char * mem_file, vector <VersionInfo *> & known_versions);
};
WineProcess::WineProcess(uint32_t pid, vector <VersionInfo *> & known_versions) WineProcess::WineProcess(uint32_t pid, vector <VersionInfo *> & known_versions) : LinuxProcessBase(pid)
: d(new Private(this))
{ {
char dir_name [256]; char dir_name [256];
char exe_link_name [256]; char exe_link_name [256];
@ -107,27 +78,14 @@ WineProcess::WineProcess(uint32_t pid, vector <VersionInfo *> & known_versions)
sprintf(exe_link,"%s/%s",target_name,cmdline.c_str()); sprintf(exe_link,"%s/%s",target_name,cmdline.c_str());
// create wine process, add it to the vector // create wine process, add it to the vector
d->identified = d->validate(exe_link,pid,mem_name,known_versions); d->identified = validate(exe_link,pid,mem_name,known_versions);
return; return;
} }
} }
} }
bool WineProcess::isSuspended()
{
return d->suspended;
}
bool WineProcess::isAttached()
{
return d->attached;
}
bool WineProcess::isIdentified() bool WineProcess::validate(char * exe_file,uint32_t pid, char * memFile, vector <VersionInfo *> & known_versions)
{
return d->identified;
}
bool WineProcess::Private::validate(char* exe_file, uint32_t pid, char* mem_file, std::vector< VersionInfo* >& known_versions)
{ {
md5wrapper md5; md5wrapper md5;
// get hash of the running DF process // get hash of the running DF process
@ -137,27 +95,20 @@ bool WineProcess::Private::validate(char* exe_file, uint32_t pid, char* mem_file
// iterate over the list of memory locations // iterate over the list of memory locations
for ( it=known_versions.begin() ; it < known_versions.end(); it++ ) for ( it=known_versions.begin() ; it < known_versions.end(); it++ )
{ {
string thishash;
try try
{ {
thishash = (*it)->getMD5(); if (hash == (*it)->getMD5()) // are the md5 hashes the same?
}
catch (Error::AllMemdef& e)
{ {
continue; if (OS_WINDOWS == (*it)->getOS())
}
// are the md5 hashes the same?
if(OS_WINDOWS == (*it)->getOS() && hash == thishash)
{ {
// keep track of created memory_info object so we can destroy it later
VersionInfo *m = new VersionInfo(**it); VersionInfo *m = new VersionInfo(**it);
my_descriptor = m; // keep track of created memory_info object so we can destroy it later
m->setParentProcess(dynamic_cast<Process *>( self )); d->my_descriptor = m;
my_handle = my_pid = pid; m->setParentProcess(this);
// tell WineProcess about the /proc/PID/mem file // tell Process about the /proc/PID/mem file
memFile = mem_file; d->memFile = memFile;
identified = true; d->identified = true;
OffsetGroup * strGrp = m->getGroup("string")->getGroup("MSVC"); OffsetGroup * strGrp = m->getGroup("string")->getGroup("MSVC");
STLSTR_buf_off = strGrp->getOffset("buffer"); STLSTR_buf_off = strGrp->getOffset("buffer");
STLSTR_size_off = strGrp->getOffset("size"); STLSTR_size_off = strGrp->getOffset("size");
@ -165,382 +116,21 @@ bool WineProcess::Private::validate(char* exe_file, uint32_t pid, char* mem_file
return true; return true;
} }
} }
return false;
}
WineProcess::~WineProcess()
{
if(d->attached)
{
detach();
}
// destroy our copy of the memory descriptor
if(d->my_descriptor)
delete d->my_descriptor;
delete d;
}
VersionInfo * WineProcess::getDescriptor()
{
return d->my_descriptor;
}
int WineProcess::getPID()
{
return d->my_pid;
}
//FIXME: implement
bool WineProcess::getThreadIDs(vector<uint32_t> & threads )
{
return false;
}
//FIXME: cross-reference with ELF segment entries?
void WineProcess::getMemRanges( vector<t_memrange> & ranges )
{
char buffer[1024];
char permissions[5]; // r/-, w/-, x/-, p/s, 0
sprintf(buffer, "/proc/%lu/maps", d->my_pid);
FILE *mapFile = ::fopen(buffer, "r");
uint64_t offset, device1, device2, node;
while (fgets(buffer, 1024, mapFile))
{
t_memrange temp;
temp.name[0] = 0;
sscanf(buffer, "%llx-%llx %s %llx %2llu:%2llu %llu %s",
&temp.start,
&temp.end,
(char*)&permissions,
&offset, &device1, &device2, &node,
(char*)&temp.name);
temp.read = permissions[0] == 'r';
temp.write = permissions[1] == 'w';
temp.execute = permissions[2] == 'x';
temp.valid = true;
ranges.push_back(temp);
}
}
bool WineProcess::asyncSuspend()
{
return suspend();
}
bool WineProcess::suspend()
{
int status;
if(!d->attached)
return false;
if(d->suspended)
return true;
if (kill(d->my_handle, SIGSTOP) == -1)
{
// no, we got an error
perror("kill SIGSTOP error");
return false;
}
while(true)
{
// we wait on the pid
pid_t w = waitpid(d->my_handle, &status, 0);
if (w == -1)
{
// child died
perror("DF exited during suspend call");
return false;
}
// stopped -> let's continue
if (WIFSTOPPED(status))
{
break;
}
}
d->suspended = true;
return true;
}
bool WineProcess::forceresume()
{
return resume();
}
bool WineProcess::resume()
{
if(!d->attached)
return false;
if(!d->suspended)
return true;
if (ptrace(PTRACE_CONT, d->my_handle, NULL, NULL) == -1)
{
// no, we got an error
perror("ptrace resume error");
return false;
}
d->suspended = false;
return true;
}
bool WineProcess::attach()
{
int status;
if(d->attached)
{
if(!d->suspended)
return suspend();
return true;
}
// can we attach?
if (ptrace(PTRACE_ATTACH , d->my_handle, NULL, NULL) == -1)
{
// no, we got an error
perror("ptrace attach error");
cerr << "attach failed on pid " << d->my_handle << endl;
return false;
}
while(true)
{
// we wait on the pid
pid_t w = waitpid(d->my_handle, &status, 0);
if (w == -1)
{
// child died
perror("wait inside attach()");
return false;
} }
// stopped -> let's continue catch (Error::AllMemdef&)
if (WIFSTOPPED(status))
{ {
break; continue;
}
} }
d->suspended = true;
int proc_pid_mem = open(d->memFile.c_str(),O_RDONLY);
if(proc_pid_mem == -1)
{
ptrace(PTRACE_DETACH, d->my_handle, NULL, NULL);
cerr << d->memFile << endl;
cerr << "couldn't open /proc/" << d->my_handle << "/mem" << endl;
perror("open(memFile.c_str(),O_RDONLY)");
return false;
} }
else
{
d->attached = true;
d->memFileHandle = proc_pid_mem;
return true; // we are attached
}
}
bool WineProcess::detach()
{
if(!d->attached) return true;
if(!d->suspended) suspend();
int result = 0;
// close /proc/PID/mem
result = close(d->memFileHandle);
if(result == -1)
{
cerr << "couldn't close /proc/"<< d->my_handle <<"/mem" << endl;
perror("mem file close");
return false; return false;
}
else
{
// detach
result = ptrace(PTRACE_DETACH, d->my_handle, NULL, NULL);
if(result == -1)
{
cerr << "couldn't detach from process pid" << d->my_handle << endl;
perror("ptrace detach");
return false;
}
else
{
d->attached = false;
return true;
}
}
}
// danger: uses recursion!
void WineProcess::read (const uint32_t offset, const uint32_t size, uint8_t *target)
{
if(size == 0) return;
ssize_t result;
ssize_t total = 0;
ssize_t remaining = size;
while (total != size)
{
result = pread(d->memFileHandle, target + total ,remaining,offset + total);
if(result == -1)
{
cerr << "pread failed: can't read " << size << " bytes at addres " << offset << endl;
cerr << "errno: " << errno << endl;
errno = 0;
throw Error::MemoryAccessDenied();
}
else
{
total += result;
remaining -= result;
}
}
} }
void WineProcess::readByte (const uint32_t offset, uint8_t &val )
{
read(offset, 1, &val);
}
void WineProcess::readWord (const uint32_t offset, uint16_t &val)
{
read(offset, 2, (uint8_t *) &val);
}
void WineProcess::readDWord (const uint32_t offset, uint32_t &val)
{
read(offset, 4, (uint8_t *) &val);
}
void WineProcess::readFloat (const uint32_t offset, float &val)
{
read(offset, 4, (uint8_t *) &val);
}
void WineProcess::readQuad (const uint32_t offset, uint64_t &val)
{
read(offset, 8, (uint8_t *) &val);
}
/*
* WRITING
*/
void WineProcess::writeQuad (uint32_t offset, const uint64_t data)
{
#ifdef HAVE_64_BIT
ptrace(PTRACE_POKEDATA,d->my_handle, offset, data);
#else
ptrace(PTRACE_POKEDATA,d->my_handle, offset, (uint32_t) data);
ptrace(PTRACE_POKEDATA,d->my_handle, offset+4, (uint32_t) (data >> 32));
#endif
}
void WineProcess::writeDWord (uint32_t offset, uint32_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = Process::readQuad(offset);
orig &= 0xFFFFFFFF00000000;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#else
ptrace(PTRACE_POKEDATA,d->my_handle, offset, data);
#endif
}
// using these is expensive.
void WineProcess::writeWord (uint32_t offset, uint16_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = Process::readQuad(offset);
orig &= 0xFFFFFFFFFFFF0000;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#else
uint32_t orig = readDWord(offset);
orig &= 0xFFFF0000;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#endif
}
void WineProcess::writeByte (uint32_t offset, uint8_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = Process::readQuad(offset);
orig &= 0xFFFFFFFFFFFFFF00;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#else
uint32_t orig = readDWord(offset);
orig &= 0xFFFFFF00;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#endif
}
// blah. I hate the kernel devs for crippling /proc/PID/mem. THIS IS RIDICULOUS
void WineProcess::write (uint32_t offset, uint32_t size, uint8_t *source)
{
uint32_t indexptr = 0;
while (size > 0)
{
#ifdef HAVE_64_BIT
// quad!
if(size >= 8)
{
writeQuad(offset, *(uint64_t *) (source + indexptr));
offset +=8;
indexptr +=8;
size -=8;
}
else
#endif
// default: we push 4 bytes
if(size >= 4)
{
writeDWord(offset, *(uint32_t *) (source + indexptr));
offset +=4;
indexptr +=4;
size -=4;
}
// last is either three or 2 bytes
else if(size >= 2)
{
writeWord(offset, *(uint16_t *) (source + indexptr));
offset +=2;
indexptr +=2;
size -=2;
}
// finishing move
else if(size == 1)
{
writeByte(offset, *(uint8_t *) (source + indexptr));
return;
}
}
}
const std::string WineProcess::readCString (uint32_t offset)
{
std::string temp;
char temp_c[256];
int counter = 0;
char r;
do
{
r = Process::readByte(offset+counter);
temp_c[counter] = r;
counter++;
} while (r && counter < 255);
temp_c[counter] = 0;
temp = temp_c;
return temp;
}
size_t WineProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) size_t WineProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity)
{ {
uint32_t start_offset = offset + d->STLSTR_buf_off; uint32_t start_offset = offset + STLSTR_buf_off;
size_t length = Process::readDWord(offset + d->STLSTR_size_off); size_t length = Process::readDWord(offset + STLSTR_size_off);
size_t capacity = Process::readDWord(offset + d->STLSTR_cap_off); size_t capacity = Process::readDWord(offset + STLSTR_cap_off);
size_t read_real = min(length, bufcapacity-1);// keep space for null termination size_t read_real = min(length, bufcapacity-1);// keep space for null termination
@ -561,9 +151,9 @@ size_t WineProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcap
const string WineProcess::readSTLString (uint32_t offset) const string WineProcess::readSTLString (uint32_t offset)
{ {
uint32_t start_offset = offset + d->STLSTR_buf_off; uint32_t start_offset = offset + STLSTR_buf_off;
size_t length = Process::readDWord(offset + d->STLSTR_size_off); size_t length = Process::readDWord(offset + STLSTR_size_off);
size_t capacity = Process::readDWord(offset + d->STLSTR_cap_off); size_t capacity = Process::readDWord(offset + STLSTR_cap_off);
char * temp = new char[capacity+1]; char * temp = new char[capacity+1];
@ -592,15 +182,3 @@ string WineProcess::readClassName (uint32_t vptr)
raw.resize(raw.length() - 2);// trim @@ from end raw.resize(raw.length() - 2);// trim @@ from end
return raw; return raw;
} }
string WineProcess::getPath()
{
char cwd_name[256];
char target_name[1024];
int target_result;
sprintf(cwd_name,"/proc/%d/cwd", getPID());
// resolve /proc/PID/exe link
target_result = readlink(cwd_name, target_name, sizeof(target_name));
target_name[target_result] = '\0';
return(string(target_name));
}

@ -22,42 +22,14 @@ must not be misrepresented as being the original software.
distribution. distribution.
*/ */
#include "Internal.h" #include "Internal.h"
#include "ProcessInternal.h" #include "LinuxProcess.h"
#include "dfhack/VersionInfo.h" #include "dfhack/VersionInfo.h"
#include "dfhack/DFError.h" #include "dfhack/DFError.h"
#include <errno.h> #include <errno.h>
#include <sys/ptrace.h> #include <sys/ptrace.h>
using namespace DFHack; using namespace DFHack;
class NormalProcess::Private NormalProcess::NormalProcess(uint32_t pid, vector <VersionInfo *> & known_versions) : LinuxProcessBase(pid)
{
public:
Private(Process * self_)
{
my_descriptor = NULL;
my_handle = NULL;
my_pid = 0;
attached = false;
suspended = false;
memFileHandle = 0;
self = self_;
};
~Private(){};
Window* my_window;
VersionInfo * my_descriptor;
pid_t my_handle;
uint32_t my_pid;
string memFile;
int memFileHandle;
bool attached;
bool suspended;
bool identified;
Process * self;
bool validate(char * exe_file, uint32_t pid, char * mem_file, vector <VersionInfo *> & known_versions);
};
NormalProcess::NormalProcess(uint32_t pid, vector< VersionInfo* >& known_versions)
: d(new Private(this))
{ {
char dir_name [256]; char dir_name [256];
char exe_link_name [256]; char exe_link_name [256];
@ -89,26 +61,12 @@ NormalProcess::NormalProcess(uint32_t pid, vector< VersionInfo* >& known_version
if (strstr(target_name, "dwarfort.exe") != 0 || strstr(target_name,"Dwarf_Fortress") != 0) if (strstr(target_name, "dwarfort.exe") != 0 || strstr(target_name,"Dwarf_Fortress") != 0)
{ {
// create linux process, add it to the vector // create linux process, add it to the vector
d->identified = d->validate(target_name,pid,mem_name,known_versions ); d->identified = validate(target_name,pid,mem_name,known_versions);
return; return;
} }
} }
bool NormalProcess::isSuspended() bool NormalProcess::validate(char * exe_file,uint32_t pid, char * memFile, vector <VersionInfo *> & known_versions)
{
return d->suspended;
}
bool NormalProcess::isAttached()
{
return d->attached;
}
bool NormalProcess::isIdentified()
{
return d->identified;
}
bool NormalProcess::Private::validate(char * exe_file,uint32_t pid, char * memFile, vector <VersionInfo *> & known_versions)
{ {
md5wrapper md5; md5wrapper md5;
// get hash of the running DF process // get hash of the running DF process
@ -120,28 +78,21 @@ bool NormalProcess::Private::validate(char * exe_file,uint32_t pid, char * memFi
{ {
try try
{ {
//cout << hash << " ?= " << (*it)->getMD5() << endl; if (hash == (*it)->getMD5()) // are the md5 hashes the same?
if(hash == (*it)->getMD5()) // are the md5 hashes the same?
{
VersionInfo * m = *it;
if (OS_LINUX == m->getOS())
{ {
VersionInfo *m2 = new VersionInfo(*m); if (OS_LINUX == (*it)->getOS())
my_descriptor = m2;
m2->setParentProcess(dynamic_cast<Process *>( self ));
my_handle = my_pid = pid;
}
else
{ {
// some error happened, continue with next process VersionInfo *m = new VersionInfo(**it);
continue; // keep track of created memory_info object so we can destroy it later
} d->my_descriptor = m;
// tell NormalProcess about the /proc/PID/mem file m->setParentProcess(this);
this->memFile = memFile; // tell Process about the /proc/PID/mem file
identified = true; d->memFile = memFile;
d->identified = true;
return true; return true;
} }
} }
}
catch (Error::AllMemdef&) catch (Error::AllMemdef&)
{ {
continue; continue;
@ -150,368 +101,6 @@ bool NormalProcess::Private::validate(char * exe_file,uint32_t pid, char * memFi
return false; return false;
} }
NormalProcess::~NormalProcess()
{
if(d->attached)
{
detach();
}
// destroy our copy of the memory descriptor
if(d->my_descriptor)
delete d->my_descriptor;
delete d;
}
VersionInfo * NormalProcess::getDescriptor()
{
return d->my_descriptor;
}
int NormalProcess::getPID()
{
return d->my_pid;
}
//FIXME: implement
bool NormalProcess::getThreadIDs(vector<uint32_t> & threads )
{
return false;
}
//FIXME: cross-reference with ELF segment entries?
void NormalProcess::getMemRanges( vector<t_memrange> & ranges )
{
char buffer[1024];
char permissions[5]; // r/-, w/-, x/-, p/s, 0
sprintf(buffer, "/proc/%lu/maps", d->my_pid);
FILE *mapFile = ::fopen(buffer, "r");
uint64_t offset, device1, device2, node;
while (fgets(buffer, 1024, mapFile))
{
t_memrange temp;
temp.name[0] = 0;
sscanf(buffer, "%llx-%llx %s %llx %2llu:%2llu %llu %s",
&temp.start,
&temp.end,
(char*)&permissions,
&offset, &device1, &device2, &node,
(char*)&temp.name);
temp.read = permissions[0] == 'r';
temp.write = permissions[1] == 'w';
temp.execute = permissions[2] == 'x';
temp.valid = true;
ranges.push_back(temp);
}
}
bool NormalProcess::asyncSuspend()
{
return suspend();
}
bool NormalProcess::suspend()
{
int status;
if(!d->attached)
return false;
if(d->suspended)
return true;
if (kill(d->my_handle, SIGSTOP) == -1)
{
// no, we got an error
perror("kill SIGSTOP error");
return false;
}
while(true)
{
// we wait on the pid
pid_t w = waitpid(d->my_handle, &status, 0);
if (w == -1)
{
// child died
perror("DF exited during suspend call");
return false;
}
// stopped -> let's continue
if (WIFSTOPPED(status))
{
break;
}
}
d->suspended = true;
return true;
}
bool NormalProcess::forceresume()
{
return resume();
}
bool NormalProcess::resume()
{
if(!d->attached)
return false;
if(!d->suspended)
return true;
if (ptrace(PTRACE_CONT, d->my_handle, NULL, NULL) == -1)
{
// no, we got an error
perror("ptrace resume error");
return false;
}
d->suspended = false;
return true;
}
bool NormalProcess::attach()
{
int status;
if(d->attached)
{
if(!d->suspended)
return suspend();
return true;
}
// can we attach?
if (ptrace(PTRACE_ATTACH , d->my_handle, NULL, NULL) == -1)
{
// no, we got an error
perror("ptrace attach error");
cerr << "attach failed on pid " << d->my_handle << endl;
return false;
}
while(true)
{
// we wait on the pid
pid_t w = waitpid(d->my_handle, &status, 0);
if (w == -1)
{
// child died
perror("wait inside attach()");
return false;
}
// stopped -> let's continue
if (WIFSTOPPED(status))
{
break;
}
}
d->suspended = true;
int proc_pid_mem = open(d->memFile.c_str(),O_RDONLY);
if(proc_pid_mem == -1)
{
ptrace(PTRACE_DETACH, d->my_handle, NULL, NULL);
cerr << "couldn't open /proc/" << d->my_handle << "/mem" << endl;
perror("open(memFile.c_str(),O_RDONLY)");
return false;
}
else
{
d->attached = true;
d->memFileHandle = proc_pid_mem;
return true; // we are attached
}
}
bool NormalProcess::detach()
{
if(!d->attached) return true;
if(!d->suspended) suspend();
int result = 0;
// close /proc/PID/mem
result = close(d->memFileHandle);
if(result == -1)
{
cerr << "couldn't close /proc/"<< d->my_handle <<"/mem" << endl;
perror("mem file close");
return false;
}
else
{
// detach
result = ptrace(PTRACE_DETACH, d->my_handle, NULL, NULL);
if(result == -1)
{
cerr << "couldn't detach from process pid" << d->my_handle << endl;
perror("ptrace detach");
return false;
}
else
{
d->attached = false;
return true;
}
}
}
// danger: uses recursion!
void NormalProcess::read (const uint32_t offset, const uint32_t size, uint8_t *target)
{
if(size == 0) return;
ssize_t result;
result = pread(d->memFileHandle, target,size,offset);
if(result != size)
{
if(result == -1)
{
cerr << "pread failed: can't read 0x" << hex << size << " bytes at address 0x" << offset << endl;
cerr << "errno: " << errno << endl;
errno = 0;
throw Error::MemoryAccessDenied();
}
else
{
this->read(offset + result, size - result, target + result);
}
}
}
void NormalProcess::readByte (const uint32_t offset, uint8_t &val )
{
read(offset, 1, &val);
}
void NormalProcess::readWord (const uint32_t offset, uint16_t &val)
{
read(offset, 2, (uint8_t *) &val);
}
void NormalProcess::readDWord (const uint32_t offset, uint32_t &val)
{
read(offset, 4, (uint8_t *) &val);
}
void NormalProcess::readFloat (const uint32_t offset, float &val)
{
read(offset, 4, (uint8_t *) &val);
}
void NormalProcess::readQuad (const uint32_t offset, uint64_t &val)
{
read(offset, 8, (uint8_t *) &val);
}
/*
* WRITING
*/
void NormalProcess::writeQuad (uint32_t offset, const uint64_t data)
{
#ifdef HAVE_64_BIT
ptrace(PTRACE_POKEDATA,d->my_handle, offset, data);
#else
ptrace(PTRACE_POKEDATA,d->my_handle, offset, (uint32_t) data);
ptrace(PTRACE_POKEDATA,d->my_handle, offset+4, (uint32_t) (data >> 32));
#endif
}
void NormalProcess::writeDWord (uint32_t offset, uint32_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = Process::readQuad(offset);
orig &= 0xFFFFFFFF00000000;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#else
ptrace(PTRACE_POKEDATA,d->my_handle, offset, data);
#endif
}
// using these is expensive.
void NormalProcess::writeWord (uint32_t offset, uint16_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = Process::readQuad(offset);
orig &= 0xFFFFFFFFFFFF0000;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#else
uint32_t orig = readDWord(offset);
orig &= 0xFFFF0000;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#endif
}
void NormalProcess::writeByte (uint32_t offset, uint8_t data)
{
#ifdef HAVE_64_BIT
uint64_t orig = Process::readQuad(offset);
orig &= 0xFFFFFFFFFFFFFF00;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#else
uint32_t orig = readDWord(offset);
orig &= 0xFFFFFF00;
orig |= data;
ptrace(PTRACE_POKEDATA,d->my_handle, offset, orig);
#endif
}
// blah. I hate the kernel devs for crippling /proc/PID/mem. THIS IS RIDICULOUS
void NormalProcess::write (uint32_t offset, uint32_t size, uint8_t *source)
{
uint32_t indexptr = 0;
while (size > 0)
{
#ifdef HAVE_64_BIT
// quad!
if(size >= 8)
{
writeQuad(offset, *(uint64_t *) (source + indexptr));
offset +=8;
indexptr +=8;
size -=8;
}
else
#endif
// default: we push 4 bytes
if(size >= 4)
{
writeDWord(offset, *(uint32_t *) (source + indexptr));
offset +=4;
indexptr +=4;
size -=4;
}
// last is either three or 2 bytes
else if(size >= 2)
{
writeWord(offset, *(uint16_t *) (source + indexptr));
offset +=2;
indexptr +=2;
size -=2;
}
// finishing move
else if(size == 1)
{
writeByte(offset, *(uint8_t *) (source + indexptr));
return;
}
}
}
const std::string NormalProcess::readCString (uint32_t offset)
{
std::string temp;
char temp_c[256];
int counter = 0;
char r;
do
{
r = Process::readByte(offset+counter);
temp_c[counter] = r;
counter++;
} while (r && counter < 255);
temp_c[counter] = 0;
temp = temp_c;
return temp;
}
struct _Rep_base struct _Rep_base
{ {
@ -555,15 +144,3 @@ string NormalProcess::readClassName (uint32_t vptr)
size_t end = raw.length(); size_t end = raw.length();
return raw.substr(start,end-start); return raw.substr(start,end-start);
} }
string NormalProcess::getPath()
{
char cwd_name[256];
char target_name[1024];
int target_result;
sprintf(cwd_name,"/proc/%d/cwd", getPID());
// resolve /proc/PID/exe link
target_result = readlink(cwd_name, target_name, sizeof(target_name));
target_name[target_result] = '\0';
return(string(target_name));
}

@ -22,7 +22,7 @@ must not be misrepresented as being the original software.
distribution. distribution.
*/ */
#include "Internal.h" #include "Internal.h"
#include "ProcessInternal.h" #include "WindowsProcess.h"
#include "dfhack/VersionInfo.h" #include "dfhack/VersionInfo.h"
#include "dfhack/DFError.h" #include "dfhack/DFError.h"
#include <string.h> #include <string.h>

@ -23,8 +23,9 @@ distribution.
*/ */
#include "Internal.h" #include "Internal.h"
#include "ProcessInternal.h"
#include "SHMProcess.h" #include "SHMProcess.h"
#include "LinuxProcess.h"
#include "WindowsProcess.h"
#include "dfhack/VersionInfoFactory.h" #include "dfhack/VersionInfoFactory.h"
#include "dfhack/DFProcessEnumerator.h" #include "dfhack/DFProcessEnumerator.h"

@ -22,22 +22,26 @@ must not be misrepresented as being the original software.
distribution. distribution.
*/ */
#ifndef PROCESS_INTERNAL_H_INCLUDED #ifndef LINUX_PROCESS_H_INCLUDED
#define PROCESS_INTERNAL_H_INCLUDED #define LINUX_PROCESS_H_INCLUDED
#ifdef LINUX_BUILD
#include "dfhack/DFProcess.h" #include "dfhack/DFProcess.h"
namespace DFHack namespace DFHack
{ {
class DFHACK_EXPORT NormalProcess : public Process class LinuxProcessBase : public Process
{ {
friend class ProcessEnumerator; public:
class Private; class Private;
private: protected:
Private * const d; Private * const d;
bool readProgramName(char *target_name, char *mem_name, char *cmdline_name);
public: public:
NormalProcess(uint32_t pid, std::vector <VersionInfo *> & known_versions); LinuxProcessBase(uint32_t pid);
~NormalProcess(); ~LinuxProcessBase();
bool attach(); bool attach();
bool detach(); bool detach();
@ -63,23 +67,18 @@ namespace DFHack
void read( uint32_t address, uint32_t length, uint8_t* buffer); void read( uint32_t address, uint32_t length, uint8_t* buffer);
void write(uint32_t address, uint32_t length, uint8_t* buffer); void write(uint32_t address, uint32_t length, uint8_t* buffer);
const std::string readSTLString (uint32_t offset);
size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity);
void writeSTLString(const uint32_t address, const std::string writeString){};
// get class name of an object with rtti/type info
std::string readClassName(uint32_t vptr);
const std::string readCString (uint32_t offset); const std::string readCString (uint32_t offset);
bool isSuspended(); bool isSuspended();
bool isAttached(); bool isAttached();
bool isIdentified(); bool isIdentified();
bool getThreadIDs(std::vector<uint32_t> & threads );
void getMemRanges(std::vector<t_memrange> & ranges );
VersionInfo *getDescriptor(); VersionInfo *getDescriptor();
int getPID(); int getPID();
std::string getPath(); std::string getPath();
bool getThreadIDs(std::vector<uint32_t> & threads );
void getMemRanges(std::vector<t_memrange> & ranges );
// get module index by name and version. bool 1 = error // get module index by name and version. bool 1 = error
bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) { OUTPUT=0; return false;}; bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) { OUTPUT=0; return false;};
// get the SHM start if available // get the SHM start if available
@ -88,65 +87,55 @@ namespace DFHack
bool SetAndWait (uint32_t state){return false;}; bool SetAndWait (uint32_t state){return false;};
}; };
#ifdef LINUX_BUILD class DFHACK_EXPORT NormalProcess : public LinuxProcessBase
class DFHACK_EXPORT WineProcess : public Process
{ {
friend class ProcessEnumerator; friend class ProcessEnumerator;
class Private; public:
NormalProcess(uint32_t pid, std::vector <VersionInfo *> & known_versions);
const std::string readSTLString (uint32_t offset);
size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity);
void writeSTLString(const uint32_t address, const std::string writeString){};
// get class name of an object with rtti/type info
std::string readClassName(uint32_t vptr);
private: private:
Private * const d; bool validate(char * exe_file,uint32_t pid, char * memFile, vector <VersionInfo *> & known_versions);
};
class DFHACK_EXPORT WineProcess : public LinuxProcessBase
{
friend class ProcessEnumerator;
private:
uint32_t STLSTR_buf_off;
uint32_t STLSTR_size_off;
uint32_t STLSTR_cap_off;
public: public:
WineProcess(uint32_t pid, std::vector <VersionInfo *> & known_versions); WineProcess(uint32_t pid, std::vector <VersionInfo *> & known_versions);
~WineProcess();
bool attach();
bool detach();
bool suspend();
bool asyncSuspend();
bool resume();
bool forceresume();
void readQuad(const uint32_t address, uint64_t & value);
void writeQuad(const uint32_t address, const uint64_t value);
void readDWord(const uint32_t address, uint32_t & value);
void writeDWord(const uint32_t address, const uint32_t value);
void readFloat(const uint32_t address, float & value);
void readWord(const uint32_t address, uint16_t & value);
void writeWord(const uint32_t address, const uint16_t value);
void readByte(const uint32_t address, uint8_t & value);
void writeByte(const uint32_t address, const uint8_t value);
void read( uint32_t address, uint32_t length, uint8_t* buffer);
void write(uint32_t address, uint32_t length, uint8_t* buffer);
const std::string readSTLString (uint32_t offset); const std::string readSTLString (uint32_t offset);
size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity); size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity);
void writeSTLString(const uint32_t address, const std::string writeString){}; void writeSTLString(const uint32_t address, const std::string writeString){};
// get class name of an object with rtti/type info // get class name of an object with rtti/type info
std::string readClassName(uint32_t vptr); std::string readClassName(uint32_t vptr);
private:
bool validate(char * exe_file,uint32_t pid, char * memFile, vector <VersionInfo *> & known_versions);
};
const std::string readCString (uint32_t offset); class LinuxProcessBase::Private
{
bool isSuspended(); public:
bool isAttached(); Private(LinuxProcessBase * self_, pid_t);
bool isIdentified(); ~Private(){};
VersionInfo * my_descriptor;
bool getThreadIDs(std::vector<uint32_t> & threads ); LinuxProcessBase * self;
void getMemRanges(std::vector<t_memrange> & ranges ); pid_t my_pid;
VersionInfo *getDescriptor(); string memFile;
int getPID(); int memFileHandle;
// get module index by name and version. bool 1 = error bool attached;
bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) {OUTPUT=0; return false;}; bool suspended;
// get the SHM start if available bool identified;
char * getSHMStart (void){return 0;};
bool SetAndWait (uint32_t state){return false;};
std::string getPath();
}; };
#endif
} }
#endif
#endif #endif

@ -0,0 +1,94 @@
/*
www.sourceforge.net/projects/dfhack
Copyright (c) 2009 Petr Mrázek (peterix), Kenneth Ferland (Impaler[WrG]), dorf
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#ifndef WINDOWS_PROCESS_H_INCLUDED
#define WINDOWS_PROCESS_H_INCLUDED
#ifndef LINUX_BUILD
#include "dfhack/DFProcess.h"
namespace DFHack
{
class DFHACK_EXPORT NormalProcess : public Process
{
friend class ProcessEnumerator;
class Private;
private:
Private * const d;
public:
NormalProcess(uint32_t pid, std::vector <VersionInfo *> & known_versions);
~NormalProcess();
bool attach();
bool detach();
bool suspend();
bool asyncSuspend();
bool resume();
bool forceresume();
void readQuad(const uint32_t address, uint64_t & value);
void writeQuad(const uint32_t address, const uint64_t value);
void readDWord(const uint32_t address, uint32_t & value);
void writeDWord(const uint32_t address, const uint32_t value);
void readFloat(const uint32_t address, float & value);
void readWord(const uint32_t address, uint16_t & value);
void writeWord(const uint32_t address, const uint16_t value);
void readByte(const uint32_t address, uint8_t & value);
void writeByte(const uint32_t address, const uint8_t value);
void read( uint32_t address, uint32_t length, uint8_t* buffer);
void write(uint32_t address, uint32_t length, uint8_t* buffer);
const std::string readSTLString (uint32_t offset);
size_t readSTLString (uint32_t offset, char * buffer, size_t bufcapacity);
void writeSTLString(const uint32_t address, const std::string writeString){};
// get class name of an object with rtti/type info
std::string readClassName(uint32_t vptr);
const std::string readCString (uint32_t offset);
bool isSuspended();
bool isAttached();
bool isIdentified();
bool getThreadIDs(std::vector<uint32_t> & threads );
void getMemRanges(std::vector<t_memrange> & ranges );
VersionInfo *getDescriptor();
int getPID();
std::string getPath();
// get module index by name and version. bool 1 = error
bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) { OUTPUT=0; return false;};
// get the SHM start if available
char * getSHMStart (void){return 0;};
// set a SHM command and wait for a response
bool SetAndWait (uint32_t state){return false;};
};
}
#endif
#endif