From 9ba80d517f730cfe17546d5dc040d00a4f93fc05 Mon Sep 17 00:00:00 2001 From: Tom Prince Date: Wed, 23 Feb 2011 02:26:55 -0500 Subject: [PATCH 1/5] Remove some gratuitous differences in the SHM implementations. --- library/DFProcess-linux-SHM.cpp | 105 +++++++++++++++--------------- library/DFProcess-windows-SHM.cpp | 66 ++++--------------- 2 files changed, 66 insertions(+), 105 deletions(-) diff --git a/library/DFProcess-linux-SHM.cpp b/library/DFProcess-linux-SHM.cpp index 9fe3684a5..700dec976 100644 --- a/library/DFProcess-linux-SHM.cpp +++ b/library/DFProcess-linux-SHM.cpp @@ -39,9 +39,6 @@ distribution. using namespace DFHack; -// a full memory barrier! better be safe than sorry. -#define gcc_barrier asm volatile("" ::: "memory"); __sync_synchronize(); - class SHMProcess::Private { public: @@ -81,14 +78,14 @@ class SHMProcess::Private bool validate(std::vector< VersionInfo* >& known_versions); - bool Aux_Core_Attach(bool & versionOK, pid_t & PID); - //bool waitWhile (uint32_t state); + bool Aux_Core_Attach(bool & versionOK, pid_t& PID); bool SetAndWait (uint32_t state); bool GetLocks(); bool AreLocksOk(); void FreeLocks(); }; +// some helpful macros to keep the code bloat in check #define SHMCMD ( (uint32_t *) shm_addr)[attachmentIdx] #define D_SHMCMD ( (uint32_t *) (d->shm_addr))[d->attachmentIdx] @@ -103,12 +100,17 @@ bool SHMProcess::Private::SetAndWait (uint32_t state) uint32_t cnt = 0; if(!attached) return false; SHMCMD = state; + while (SHMCMD == state) { - // check if the other process is still there + // yield the CPU, only on single-core CPUs + if(useYield) + { + SCHED_YIELD + } if(cnt == 10000) { - if(!AreLocksOk()) + if(!AreLocksOk())// DF not there anymore? { //detach the shared memory shmdt(shm_addr); @@ -122,10 +124,6 @@ bool SHMProcess::Private::SetAndWait (uint32_t state) cnt = 0; } } - if(useYield) - { - SCHED_YIELD - } cnt++; } // server returned a generic error @@ -155,21 +153,6 @@ uint32_t OS_getAffinity() return affinity; } -// test if we have client and server locks and the server is present -bool SHMProcess::Private::AreLocksOk() -{ - // both locks are inited (we hold our lock) - if(client_lock != -1 && server_lock != -1) - { - if(lockf(server_lock,F_TEST,0) == -1) // and server holds its lock - { - return true; // OK, locks are good - } - } - // locks are bad - return false; -} - void SHMProcess::Private::FreeLocks() { attachmentIdx = -1; @@ -258,6 +241,21 @@ bool SHMProcess::Private::GetLocks() return false; } +// test if we have client and server locks and the server is present +bool SHMProcess::Private::AreLocksOk() +{ + // both locks are inited (we hold our lock) + if(client_lock != -1 && server_lock != -1) + { + if(lockf(server_lock,F_TEST,0) == -1) // and server holds its lock + { + return true; // OK, locks are good + } + } + // locks are bad + return false; +} + SHMProcess::SHMProcess(uint32_t PID, vector< VersionInfo* >& known_versions) : d(new Private(this)) { @@ -425,8 +423,8 @@ bool SHMProcess::suspend() if(D_SHMCMD == CORE_RUN) { //fprintf(stderr,"%d invokes step\n",d->attachmentIdx); - /* // wait for the next window + /* if(!d->SetAndWait(CORE_STEP)) { throw Error::SHMLockingError("if(!d->SetAndWait(CORE_STEP))"); @@ -599,7 +597,7 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff { D_SHMHDR->address = src_address; D_SHMHDR->length = size; - gcc_barrier + full_barrier d->SetAndWait(CORE_READ); memcpy (target_buffer, D_SHMDATA(void),size); } @@ -613,7 +611,7 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff // read to_read bytes from src_cursor D_SHMHDR->address = src_address; D_SHMHDR->length = to_read; - gcc_barrier + full_barrier d->SetAndWait(CORE_READ); memcpy (target_buffer, D_SHMDATA(void) ,to_read); // decrease size by bytes read @@ -632,7 +630,7 @@ void SHMProcess::readByte (const uint32_t offset, uint8_t &val ) if(!d->locked) throw Error::MemoryAccessDenied(); D_SHMHDR->address = offset; - gcc_barrier + full_barrier d->SetAndWait(CORE_READ_BYTE); val = D_SHMHDR->value; } @@ -642,7 +640,7 @@ void SHMProcess::readWord (const uint32_t offset, uint16_t &val) if(!d->locked) throw Error::MemoryAccessDenied(); D_SHMHDR->address = offset; - gcc_barrier + full_barrier d->SetAndWait(CORE_READ_WORD); val = D_SHMHDR->value; } @@ -652,7 +650,7 @@ void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) if(!d->locked) throw Error::MemoryAccessDenied(); D_SHMHDR->address = offset; - gcc_barrier + full_barrier d->SetAndWait(CORE_READ_DWORD); val = D_SHMHDR->value; } @@ -662,7 +660,7 @@ void SHMProcess::readQuad (const uint32_t offset, uint64_t &val) if(!d->locked) throw Error::MemoryAccessDenied(); D_SHMHDR->address = offset; - gcc_barrier + full_barrier d->SetAndWait(CORE_READ_QUAD); val = D_SHMHDR->Qvalue; } @@ -672,7 +670,7 @@ void SHMProcess::readFloat (const uint32_t offset, float &val) if(!d->locked) throw Error::MemoryAccessDenied(); D_SHMHDR->address = offset; - gcc_barrier + full_barrier d->SetAndWait(CORE_READ_DWORD); val = reinterpret_cast (D_SHMHDR->value); } @@ -681,13 +679,13 @@ void SHMProcess::readFloat (const uint32_t offset, float &val) * WRITING */ -void SHMProcess::writeQuad (const uint32_t offset, const uint64_t data) +void SHMProcess::writeQuad (uint32_t offset, uint64_t data) { if(!d->locked) throw Error::MemoryAccessDenied(); D_SHMHDR->address = offset; D_SHMHDR->Qvalue = data; - gcc_barrier + full_barrier d->SetAndWait(CORE_WRITE_QUAD); } @@ -697,7 +695,7 @@ void SHMProcess::writeDWord (uint32_t offset, uint32_t data) D_SHMHDR->address = offset; D_SHMHDR->value = data; - gcc_barrier + full_barrier d->SetAndWait(CORE_WRITE_DWORD); } @@ -708,7 +706,7 @@ void SHMProcess::writeWord (uint32_t offset, uint16_t data) D_SHMHDR->address = offset; D_SHMHDR->value = data; - gcc_barrier + full_barrier d->SetAndWait(CORE_WRITE_WORD); } @@ -718,7 +716,7 @@ void SHMProcess::writeByte (uint32_t offset, uint8_t data) D_SHMHDR->address = offset; D_SHMHDR->value = data; - gcc_barrier + full_barrier d->SetAndWait(CORE_WRITE_BYTE); } @@ -732,7 +730,7 @@ void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buf D_SHMHDR->address = dst_address; D_SHMHDR->length = size; memcpy(D_SHMDATA(void),source_buffer, size); - gcc_barrier + full_barrier d->SetAndWait(CORE_WRITE); } // a big write, we push this over the shm in iterations @@ -746,7 +744,7 @@ void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buf D_SHMHDR->address = dst_address; D_SHMHDR->length = to_write; memcpy(D_SHMDATA(void),source_buffer, to_write); - gcc_barrier + full_barrier d->SetAndWait(CORE_WRITE); // decrease size by bytes written size -= to_write; @@ -825,6 +823,19 @@ string SHMProcess::readClassName (uint32_t vptr) return raw.substr(start,end-start); } +string SHMProcess::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)); +} + // get module index by name and version. bool 0 = error bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) { @@ -874,15 +885,3 @@ bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, pid_t & PID) #endif return true; } -string SHMProcess::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)); -} diff --git a/library/DFProcess-windows-SHM.cpp b/library/DFProcess-windows-SHM.cpp index 4d23d8837..97711a979 100644 --- a/library/DFProcess-windows-SHM.cpp +++ b/library/DFProcess-windows-SHM.cpp @@ -29,7 +29,6 @@ distribution. #include "mod-core.h" using namespace DFHack; -// a full memory barrier! better be safe than sorry. class SHMProcess::Private { public: @@ -81,11 +80,6 @@ class SHMProcess::Private #define SHMDATA(type) ((type *)(shm_addr + SHM_HEADER)) #define D_SHMDATA(type) ((type *)(d->shm_addr + SHM_HEADER)) -bool SHMProcess::SetAndWait (uint32_t state) -{ - return d->SetAndWait(state); -} - bool SHMProcess::Private::SetAndWait (uint32_t state) { uint32_t cnt = 0; @@ -116,6 +110,7 @@ bool SHMProcess::Private::SetAndWait (uint32_t state) } cnt++; } + // server returned a generic error if(SHMCMD == CORE_ERROR) { return false; @@ -123,6 +118,11 @@ bool SHMProcess::Private::SetAndWait (uint32_t state) return true; } +bool SHMProcess::SetAndWait (uint32_t state) +{ + return d->SetAndWait(state); +} + uint32_t OS_getAffinity() { HANDLE hProcess = GetCurrentProcess(); @@ -253,27 +253,6 @@ bool SHMProcess::Private::AreLocksOk() return false; } - - - /* - char svmutexname [256]; - - char clmutexname [256]; - sprintf(clmutexname,"DFCLMutex-%d",PID); - - // get server and client mutex - d->DFSVMutex = OpenMutex(SYNCHRONIZE,false, svmutexname); - if(d->DFSVMutex == 0) - { - return; - } - d->DFCLMutex = OpenMutex(SYNCHRONIZE,false, clmutexname); - if(d->DFCLMutex == 0) - { - return; - } - */ - SHMProcess::SHMProcess(uint32_t PID, vector & known_versions) : d(new Private()) { @@ -463,7 +442,7 @@ bool SHMProcess::suspend() { //fprintf(stderr,"%d invokes step\n",d->attachmentIdx); // wait for the next window - /* + /* if(!d->SetAndWait(CORE_STEP)) { throw Error::SHMLockingError("if(!d->SetAndWait(CORE_STEP))"); @@ -577,21 +556,6 @@ bool SHMProcess::attach() //cerr << "server is full or not really there!" << endl; return false; } - /* - // check if DF is there - if(!d->isValidSV()) - { - return false; // NOT - } - */ - /* - // try locking client mutex - uint32_t result = WaitForSingleObject(d->DFCLMutex,0); - if( result != WAIT_OBJECT_0 && result != WAIT_ABANDONED) - { - return false; // we couldn't lock it - } - */ /* * Locate the segment. @@ -634,12 +598,10 @@ bool SHMProcess::attach() bool SHMProcess::detach() { if(!d->attached) return true; - //cerr << "detach" << endl;// FIXME: throw if(d->locked) { resume(); } - //cerr << "detach after resume" << endl;// FIXME: throw // detach segment UnmapViewOfFile(d->shm_addr); // release it for some other client @@ -718,24 +680,24 @@ void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) val = D_SHMHDR->value; } -void SHMProcess::readFloat (const uint32_t offset, float &val) +void SHMProcess::readQuad (const uint32_t offset, uint64_t &val) { if(!d->locked) throw Error::MemoryAccessDenied(); D_SHMHDR->address = offset; full_barrier - d->SetAndWait(CORE_READ_DWORD); - val = reinterpret_cast (D_SHMHDR->value); + d->SetAndWait(CORE_READ_QUAD); + val = D_SHMHDR->Qvalue; } -void SHMProcess::readQuad (const uint32_t offset, uint64_t &val) +void SHMProcess::readFloat (const uint32_t offset, float &val) { if(!d->locked) throw Error::MemoryAccessDenied(); D_SHMHDR->address = offset; full_barrier - d->SetAndWait(CORE_READ_QUAD); - val = D_SHMHDR->Qvalue; + d->SetAndWait(CORE_READ_DWORD); + val = reinterpret_cast (D_SHMHDR->value); } /* @@ -752,7 +714,6 @@ void SHMProcess::writeQuad (uint32_t offset, uint64_t data) d->SetAndWait(CORE_WRITE_QUAD); } - void SHMProcess::writeDWord (uint32_t offset, uint32_t data) { if(!d->locked) throw Error::MemoryAccessDenied(); @@ -895,6 +856,7 @@ string SHMProcess::getPath() string out(String); return(out.substr(0,out.find_last_of("\\"))); } + // get module index by name and version. bool 0 = error bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) { From c287e547938f65313360a7cafc4bccf18d341485 Mon Sep 17 00:00:00 2001 From: Tom Prince Date: Wed, 23 Feb 2011 04:02:14 -0500 Subject: [PATCH 2/5] Merge much of DFProcess-*-SHM.cpp into DFProcess-SHM.cpp. --- library/CMakeLists.txt | 3 + library/DFProcess-SHM.cpp | 479 +++++++++++++++++++++++++++ library/DFProcess-linux-SHM.cpp | 518 ++---------------------------- library/DFProcess-windows-SHM.cpp | 508 ++--------------------------- library/DFProcessEnumerator.cpp | 1 + library/private/ProcessInternal.h | 64 ---- library/private/SHMProcess.h | 144 +++++++++ 7 files changed, 666 insertions(+), 1051 deletions(-) create mode 100644 library/DFProcess-SHM.cpp create mode 100644 library/private/SHMProcess.h diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index bb7a9c199..e3ba69df4 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -29,6 +29,8 @@ include_directories (${CMAKE_SOURCE_DIR}/library/private/) SET(PROJECT_HDRS_INTERNAL private/ContextShared.h private/Internal.h + private/ProcessInternal.h + private/SHMProcess.h ) SET(PROJECT_HDRS @@ -89,6 +91,7 @@ DFContext.cpp DFTileTypes.cpp DFProcessEnumerator.cpp ContextShared.cpp +DFProcess-SHM.cpp depends/md5/md5.cpp depends/md5/md5wrapper.cpp diff --git a/library/DFProcess-SHM.cpp b/library/DFProcess-SHM.cpp new file mode 100644 index 000000000..ad7ebc8a5 --- /dev/null +++ b/library/DFProcess-SHM.cpp @@ -0,0 +1,479 @@ +/* +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 "SHMProcess.h" +#include "dfhack/VersionInfo.h" +#include "dfhack/DFError.h" +#include "shms.h" +#include "mod-core.h" + +using namespace DFHack; + +SHMProcess::SHMProcess(uint32_t PID, vector & known_versions) +: d(new Private(this)) +{ + d->process_ID = PID; + // attach the SHM + if(!attach()) + { + return; + } + // Test bridge version, get PID, sync Yield + bool bridgeOK; + if(!d->Aux_Core_Attach(bridgeOK,d->process_ID)) + { + detach(); + throw Error::SHMAttachFailure(); + } + else if(!bridgeOK) + { + detach(); + throw Error::SHMVersionMismatch(); + } + + // try to identify the DF version (md5 the binary, compare with known versions) + d->validate(known_versions); + // at this point, DF is attached and suspended, make it run + detach(); +} + +SHMProcess::~SHMProcess() +{ + if(d->attached) + { + detach(); + } + // destroy data model. this is assigned by processmanager + if(d->memdescriptor) + delete d->memdescriptor; + delete d; +} + +VersionInfo * SHMProcess::getDescriptor() +{ + return d->memdescriptor; +} + +int SHMProcess::getPID() +{ + return d->process_ID; +} + + +bool SHMProcess::isSuspended() +{ + return d->locked; +} +bool SHMProcess::isAttached() +{ + return d->attached; +} + +bool SHMProcess::isIdentified() +{ + return d->identified; +} + +bool SHMProcess::suspend() +{ + if(!d->attached) + { + return false; + } + if(d->locked) + { + return true; + } + //cerr << "suspend" << endl;// FIXME: throw + // FIXME: this should be controlled on the server side + // FIXME: IF server got CORE_RUN in this frame, interpret CORE_SUSPEND as CORE_STEP + // did we just resume a moment ago? + if(D_SHMCMD == CORE_RUN) + { + //fprintf(stderr,"%d invokes step\n",attachmentIdx); + // wait for the next window + /* + if(!d->SetAndWait(CORE_STEP)) + { + throw Error::SHMLockingError("if(!d->SetAndWait(CORE_STEP))"); + } + */ + D_SHMCMD = CORE_STEP; + } + else + { + //fprintf(stderr,"%d invokes suspend\n",attachmentIdx); + // lock now + /* + if(!d->SetAndWait(CORE_SUSPEND)) + { + throw Error::SHMLockingError("if(!d->SetAndWait(CORE_SUSPEND))"); + } + */ + D_SHMCMD = CORE_SUSPEND; + } + //fprintf(stderr,"waiting for lock\n"); + // we wait for the server to give up our suspend lock (held by default) + if(acquireSuspendLock()) + { + d->locked = true; + return true; + } + return false; +} + +// FIXME: needs a good think-through +bool SHMProcess::asyncSuspend() +{ + if(!d->attached) + { + return false; + } + if(d->locked) + { + return true; + } + //cerr << "async suspend" << endl;// FIXME: throw + uint32_t cmd = D_SHMCMD; + if(cmd == CORE_SUSPENDED) + { + // we have to hold the lock to be really suspended + if(acquireSuspendLock()) + { + d->locked = true; + return true; + } + return false; + } + else + { + // did we just resume a moment ago? + if(cmd == CORE_STEP) + { + return false; + } + else if(cmd == CORE_RUN) + { + D_SHMCMD = CORE_STEP; + } + else + { + D_SHMCMD = CORE_SUSPEND; + } + return false; + } +} + +bool SHMProcess::forceresume() +{ + return resume(); +} + +// FIXME: wait for the server to advance a step! +bool SHMProcess::resume() +{ + if(!d->attached) + return false; + if(!d->locked) + return true; + //cerr << "resume" << endl;// FIXME: throw + // unlock the suspend lock + if(releaseSuspendLock()) + { + d->locked = false; + if(d->SetAndWait(CORE_RUN)) // we have to make sure the server responds! + { + return true; + } + throw Error::SHMLockingError("if(d->SetAndWait(CORE_RUN))"); + } + throw Error::SHMLockingError("if(releaseSuspendLock())"); + return false; +} + +// get module index by name and version. bool 0 = error +bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) +{ + if(!d->locked) throw Error::MemoryAccessDenied(); + + modulelookup * payload = D_SHMDATA(modulelookup); + payload->version = version; + + strncpy(payload->name,name,255); + payload->name[255] = 0; + + if(!SetAndWait(CORE_ACQUIRE_MODULE)) + { + return false; // FIXME: throw a fatal exception instead + } + if(D_SHMHDR->error) + { + return false; + } + //fprintf(stderr,"%s v%d : %d\n", name, version, D_SHMHDR->value); + OUTPUT = D_SHMHDR->value; + return true; +} + +bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, pid_t & PID) +{ + if(!locked) throw Error::MemoryAccessDenied(); + + SHMDATA(coreattach)->cl_affinity = OS_getAffinity(); + if(!SetAndWait(CORE_ATTACH)) return false; + /* + cerr <<"CORE_VERSION" << CORE_VERSION << endl; + cerr <<"server CORE_VERSION" << SHMDATA(coreattach)->sv_version << endl; + */ + versionOK =( SHMDATA(coreattach)->sv_version == CORE_VERSION ); + PID = SHMDATA(coreattach)->sv_PID; + useYield = SHMDATA(coreattach)->sv_useYield; + #ifdef DEBUG + if(useYield) cerr << "Using Yield!" << endl; + #endif + return true; +} + +void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer) +{ + if(!d->locked) throw Error::MemoryAccessDenied(); + + // normal read under 1MB + if(size <= SHM_BODY) + { + D_SHMHDR->address = src_address; + D_SHMHDR->length = size; + full_barrier + d->SetAndWait(CORE_READ); + memcpy (target_buffer, D_SHMDATA(void),size); + } + // a big read, we pull data over the shm in iterations + else + { + // first read equals the size of the SHM window + uint32_t to_read = SHM_BODY; + while (size) + { + // read to_read bytes from src_cursor + D_SHMHDR->address = src_address; + D_SHMHDR->length = to_read; + full_barrier + d->SetAndWait(CORE_READ); + memcpy (target_buffer, D_SHMDATA(void) ,to_read); + // decrease size by bytes read + size -= to_read; + // move the cursors + src_address += to_read; + target_buffer += to_read; + // check how much to write in the next iteration + to_read = min(size, (uint32_t) SHM_BODY); + } + } +} + +void SHMProcess::readByte (const uint32_t offset, uint8_t &val ) +{ + if(!d->locked) throw Error::MemoryAccessDenied(); + + D_SHMHDR->address = offset; + full_barrier + d->SetAndWait(CORE_READ_BYTE); + val = D_SHMHDR->value; +} + +void SHMProcess::readWord (const uint32_t offset, uint16_t &val) +{ + if(!d->locked) throw Error::MemoryAccessDenied(); + + D_SHMHDR->address = offset; + full_barrier + d->SetAndWait(CORE_READ_WORD); + val = D_SHMHDR->value; +} + +void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) +{ + if(!d->locked) throw Error::MemoryAccessDenied(); + + D_SHMHDR->address = offset; + full_barrier + d->SetAndWait(CORE_READ_DWORD); + val = D_SHMHDR->value; +} + +void SHMProcess::readQuad (const uint32_t offset, uint64_t &val) +{ + if(!d->locked) throw Error::MemoryAccessDenied(); + + D_SHMHDR->address = offset; + full_barrier + d->SetAndWait(CORE_READ_QUAD); + val = D_SHMHDR->Qvalue; +} + +void SHMProcess::readFloat (const uint32_t offset, float &val) +{ + if(!d->locked) throw Error::MemoryAccessDenied(); + + D_SHMHDR->address = offset; + full_barrier + d->SetAndWait(CORE_READ_DWORD); + val = reinterpret_cast (D_SHMHDR->value); +} + +/* + * WRITING + */ + +void SHMProcess::writeQuad (uint32_t offset, uint64_t data) +{ + if(!d->locked) throw Error::MemoryAccessDenied(); + + D_SHMHDR->address = offset; + D_SHMHDR->Qvalue = data; + full_barrier + d->SetAndWait(CORE_WRITE_QUAD); +} + +void SHMProcess::writeDWord (uint32_t offset, uint32_t data) +{ + if(!d->locked) throw Error::MemoryAccessDenied(); + + D_SHMHDR->address = offset; + D_SHMHDR->value = data; + full_barrier + d->SetAndWait(CORE_WRITE_DWORD); +} + +// using these is expensive. +void SHMProcess::writeWord (uint32_t offset, uint16_t data) +{ + if(!d->locked) throw Error::MemoryAccessDenied(); + + D_SHMHDR->address = offset; + D_SHMHDR->value = data; + full_barrier + d->SetAndWait(CORE_WRITE_WORD); +} + +void SHMProcess::writeByte (uint32_t offset, uint8_t data) +{ + if(!d->locked) throw Error::MemoryAccessDenied(); + + D_SHMHDR->address = offset; + D_SHMHDR->value = data; + full_barrier + d->SetAndWait(CORE_WRITE_BYTE); +} + +void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer) +{ + if(!d->locked) throw Error::MemoryAccessDenied(); + + // normal write under 1MB + if(size <= SHM_BODY) + { + D_SHMHDR->address = dst_address; + D_SHMHDR->length = size; + memcpy(D_SHMDATA(void),source_buffer, size); + full_barrier + d->SetAndWait(CORE_WRITE); + } + // a big write, we push this over the shm in iterations + else + { + // first write equals the size of the SHM window + uint32_t to_write = SHM_BODY; + while (size) + { + // write to_write bytes to dst_cursor + D_SHMHDR->address = dst_address; + D_SHMHDR->length = to_write; + memcpy(D_SHMDATA(void),source_buffer, to_write); + full_barrier + d->SetAndWait(CORE_WRITE); + // decrease size by bytes written + size -= to_write; + // move the cursors + source_buffer += to_write; + dst_address += to_write; + // check how much to write in the next iteration + to_write = min(size, (uint32_t) SHM_BODY); + } + } +} + +// FIXME: butt-fugly +const std::string SHMProcess::readCString (uint32_t offset) +{ + if(!d->locked) throw Error::MemoryAccessDenied(); + + 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; +} + +const std::string SHMProcess::readSTLString(uint32_t offset) +{ + if(!d->locked) throw Error::MemoryAccessDenied(); + + D_SHMHDR->address = offset; + full_barrier + d->SetAndWait(CORE_READ_STL_STRING); + return(string( D_SHMDATA(char) )); +} + +size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) +{ + if(!d->locked) throw Error::MemoryAccessDenied(); + + D_SHMHDR->address = offset; + full_barrier + d->SetAndWait(CORE_READ_STL_STRING); + size_t length = D_SHMHDR->value; + size_t fit = min(bufcapacity - 1, length); + strncpy(buffer,D_SHMDATA(char),fit); + buffer[fit] = 0; + return fit; +} + +void SHMProcess::writeSTLString(const uint32_t address, const std::string writeString) +{ + if(!d->locked) throw Error::MemoryAccessDenied(); + + D_SHMHDR->address = address; + strncpy(D_SHMDATA(char),writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator + full_barrier + d->SetAndWait(CORE_WRITE_STL_STRING); +} diff --git a/library/DFProcess-linux-SHM.cpp b/library/DFProcess-linux-SHM.cpp index 700dec976..8229b6adb 100644 --- a/library/DFProcess-linux-SHM.cpp +++ b/library/DFProcess-linux-SHM.cpp @@ -22,7 +22,7 @@ must not be misrepresented as being the original software. distribution. */ #include "Internal.h" -#include "ProcessInternal.h" +#include "SHMProcess.h" #include "dfhack/VersionInfo.h" #include "dfhack/DFError.h" @@ -39,61 +39,20 @@ distribution. using namespace DFHack; -class SHMProcess::Private +SHMProcess::Private::Private(SHMProcess * self_) { - public: - Private(Process * self_) - { - memdescriptor = NULL; - process_ID = 0; - shm_addr = 0; - //shm_addr_with_cl_idx = 0; - shm_ID = -1; - attached = false; - identified = false; - useYield = false; - server_lock = -1; - client_lock = -1; - suspend_lock = -1; - attachmentIdx = 0; - locked = false; - self = self_; - }; - ~Private(){}; - VersionInfo * memdescriptor; - Process * self; - pid_t process_ID; - char *shm_addr; - int shm_ID; - Process* q; - int server_lock; - int client_lock; - int suspend_lock; - int attachmentIdx; - - bool attached; - bool locked; - bool identified; - bool useYield; - - bool validate(std::vector< VersionInfo* >& known_versions); - - bool Aux_Core_Attach(bool & versionOK, pid_t& PID); - bool SetAndWait (uint32_t state); - bool GetLocks(); - bool AreLocksOk(); - void FreeLocks(); -}; - -// some helpful macros to keep the code bloat in check -#define SHMCMD ( (uint32_t *) shm_addr)[attachmentIdx] -#define D_SHMCMD ( (uint32_t *) (d->shm_addr))[d->attachmentIdx] - -#define SHMHDR ((shm_core_hdr *)shm_addr) -#define D_SHMHDR ((shm_core_hdr *)(d->shm_addr)) - -#define SHMDATA(type) ((type *)(shm_addr + SHM_HEADER)) -#define D_SHMDATA(type) ((type *)(d->shm_addr + SHM_HEADER)) + memdescriptor = NULL; + process_ID = 0; + shm_ID = -1; + attached = false; + identified = false; + useYield = false; + server_lock = -1; + client_lock = -1; + suspend_lock = -1; + locked = false; + self = self_; +} bool SHMProcess::Private::SetAndWait (uint32_t state) { @@ -256,50 +215,6 @@ bool SHMProcess::Private::AreLocksOk() return false; } -SHMProcess::SHMProcess(uint32_t PID, vector< VersionInfo* >& known_versions) -: d(new Private(this)) -{ - d->process_ID = PID; - d->memdescriptor = 0; - if(!attach()) - { - // couldn't attach to process - return; - } - /* - * Test bridge version, get PID, sync Yield - */ - bool bridgeOK; - if(!d->Aux_Core_Attach(bridgeOK,d->process_ID)) - { - detach(); - throw Error::SHMAttachFailure(); - } - if(!bridgeOK) - { - detach(); - throw Error::SHMVersionMismatch(); - } - - // try to identify the DF version (md5 the binary, compare with known versions) - d->validate(known_versions); - // detach - detach(); -} - -bool SHMProcess::isSuspended() -{ - return d->locked; -} -bool SHMProcess::isAttached() -{ - return d->attached; -} - -bool SHMProcess::isIdentified() -{ - return d->identified; -} bool SHMProcess::Private::validate(vector & known_versions) { @@ -331,7 +246,7 @@ bool SHMProcess::Private::validate(vector & known_versions) { VersionInfo *m = new VersionInfo(**it); memdescriptor = m; - m->setParentProcess(dynamic_cast( self )); + m->setParentProcess(self); identified = true; // cerr << "identified " << m->getVersion() << endl; return true; @@ -345,27 +260,6 @@ bool SHMProcess::Private::validate(vector & known_versions) return false; } -SHMProcess::~SHMProcess() -{ - if(d->attached) - { - detach(); - } - if(d->memdescriptor) - delete d->memdescriptor; - delete d; -} - -VersionInfo * SHMProcess::getDescriptor() -{ - return d->memdescriptor; -} - -int SHMProcess::getPID() -{ - return d->process_ID; -} - // there is only one we care about. bool SHMProcess::getThreadIDs(vector & threads ) { @@ -406,119 +300,14 @@ void SHMProcess::getMemRanges( vector & ranges ) } } -bool SHMProcess::suspend() +bool SHMProcess::acquireSuspendLock() { - if(!d->attached) - { - return false; - } - if(d->locked) - { - return true; - } - - // FIXME: this should be controlled on the server side - // FIXME: IF server got CORE_RUN in this frame, interpret CORE_SUSPEND as CORE_STEP - // did we just resume a moment ago? - if(D_SHMCMD == CORE_RUN) - { - //fprintf(stderr,"%d invokes step\n",d->attachmentIdx); - // wait for the next window - /* - if(!d->SetAndWait(CORE_STEP)) - { - throw Error::SHMLockingError("if(!d->SetAndWait(CORE_STEP))"); - } - */ - D_SHMCMD = CORE_STEP; - } - else - { - //fprintf(stderr,"%d invokes suspend\n",d->attachmentIdx); - // lock now - /* - if(!d->SetAndWait(CORE_SUSPEND)) - { - throw Error::SHMLockingError("if(!d->SetAndWait(CORE_SUSPEND))"); - } - */ - D_SHMCMD = CORE_SUSPEND; - } - //fprintf(stderr,"waiting for lock\n"); - // we wait for the server to give up our suspend lock (held by default) - if(lockf(d->suspend_lock,F_LOCK,0) == 0) - { - d->locked = true; - return true; - } - return false; -} - -// FIXME: needs a good think-through -bool SHMProcess::asyncSuspend() -{ - if(!d->attached) - { - return false; - } - if(d->locked) - { - return true; - } - uint32_t cmd = D_SHMCMD; - if(cmd == CORE_SUSPENDED) - { - // we have to hold the lock to be really suspended - if(lockf(d->suspend_lock,F_LOCK,0) == 0) - { - d->locked = true; - return true; - } - return false; - } - else - { - // did we just resume a moment ago? - if(cmd == CORE_STEP) - { - return false; - } - else if(cmd == CORE_RUN) - { - D_SHMCMD = CORE_STEP; - } - else - { - D_SHMCMD = CORE_SUSPEND; - } - return false; - } + return (lockf(d->suspend_lock,F_LOCK,0) == 0); } -bool SHMProcess::forceresume() +bool SHMProcess::releaseSuspendLock() { - return resume(); -} - -// FIXME: wait for the server to advance a step! -bool SHMProcess::resume() -{ - if(!d->attached) - return false; - if(!d->locked) - return true; - // unlock the suspend lock - if(lockf(d->suspend_lock,F_ULOCK,0) == 0) - { - d->locked = false; - if(d->SetAndWait(CORE_RUN)) // we have to make sure the server responds! - { - return true; - } - throw Error::SHMLockingError("if(d->SetAndWait(CORE_RUN))"); - } - throw Error::SHMLockingError("if(lockf(d->suspend_lock,F_ULOCK,0) == 0)"); - return false; + return (lockf(d->suspend_lock,F_ULOCK,0) == 0); } @@ -530,6 +319,7 @@ bool SHMProcess::attach() return suspend(); return true; } + //cerr << "attach" << endl;// FIXME: throw if(!d->GetLocks()) { //cerr << "server is full or not really there!" << endl; @@ -588,229 +378,6 @@ bool SHMProcess::detach() return false; } -void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - // normal read under 1MB - if(size <= SHM_BODY) - { - D_SHMHDR->address = src_address; - D_SHMHDR->length = size; - full_barrier - d->SetAndWait(CORE_READ); - memcpy (target_buffer, D_SHMDATA(void),size); - } - // a big read, we pull data over the shm in iterations - else - { - // first read equals the size of the SHM window - uint32_t to_read = SHM_BODY; - while (size) - { - // read to_read bytes from src_cursor - D_SHMHDR->address = src_address; - D_SHMHDR->length = to_read; - full_barrier - d->SetAndWait(CORE_READ); - memcpy (target_buffer, D_SHMDATA(void) ,to_read); - // decrease size by bytes read - size -= to_read; - // move the cursors - src_address += to_read; - target_buffer += to_read; - // check how much to write in the next iteration - to_read = min(size, (uint32_t) SHM_BODY); - } - } -} - -void SHMProcess::readByte (const uint32_t offset, uint8_t &val ) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - full_barrier - d->SetAndWait(CORE_READ_BYTE); - val = D_SHMHDR->value; -} - -void SHMProcess::readWord (const uint32_t offset, uint16_t &val) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - full_barrier - d->SetAndWait(CORE_READ_WORD); - val = D_SHMHDR->value; -} - -void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - full_barrier - d->SetAndWait(CORE_READ_DWORD); - val = D_SHMHDR->value; -} - -void SHMProcess::readQuad (const uint32_t offset, uint64_t &val) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - full_barrier - d->SetAndWait(CORE_READ_QUAD); - val = D_SHMHDR->Qvalue; -} - -void SHMProcess::readFloat (const uint32_t offset, float &val) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - full_barrier - d->SetAndWait(CORE_READ_DWORD); - val = reinterpret_cast (D_SHMHDR->value); -} - -/* - * WRITING - */ - -void SHMProcess::writeQuad (uint32_t offset, uint64_t data) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - D_SHMHDR->Qvalue = data; - full_barrier - d->SetAndWait(CORE_WRITE_QUAD); -} - -void SHMProcess::writeDWord (uint32_t offset, uint32_t data) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - D_SHMHDR->value = data; - full_barrier - d->SetAndWait(CORE_WRITE_DWORD); -} - -// using these is expensive. -void SHMProcess::writeWord (uint32_t offset, uint16_t data) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - D_SHMHDR->value = data; - full_barrier - d->SetAndWait(CORE_WRITE_WORD); -} - -void SHMProcess::writeByte (uint32_t offset, uint8_t data) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - D_SHMHDR->value = data; - full_barrier - d->SetAndWait(CORE_WRITE_BYTE); -} - -void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - // normal write under 1MB - if(size <= SHM_BODY) - { - D_SHMHDR->address = dst_address; - D_SHMHDR->length = size; - memcpy(D_SHMDATA(void),source_buffer, size); - full_barrier - d->SetAndWait(CORE_WRITE); - } - // a big write, we push this over the shm in iterations - else - { - // first write equals the size of the SHM window - uint32_t to_write = SHM_BODY; - while (size) - { - // write to_write bytes to dst_cursor - D_SHMHDR->address = dst_address; - D_SHMHDR->length = to_write; - memcpy(D_SHMDATA(void),source_buffer, to_write); - full_barrier - d->SetAndWait(CORE_WRITE); - // decrease size by bytes written - size -= to_write; - // move the cursors - source_buffer += to_write; - dst_address += to_write; - // check how much to write in the next iteration - to_write = min(size, (uint32_t) SHM_BODY); - } - } -} - -// FIXME: butt-fugly -const std::string SHMProcess::readCString (uint32_t offset) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - 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; -} - -const std::string SHMProcess::readSTLString(uint32_t offset) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - full_barrier - d->SetAndWait(CORE_READ_STL_STRING); - return(string( D_SHMDATA(char) )); -} - -size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - full_barrier - d->SetAndWait(CORE_READ_STL_STRING); - size_t length = D_SHMHDR->value; - size_t fit = min(bufcapacity - 1, length); - strncpy(buffer,D_SHMDATA(char),fit); - buffer[fit] = 0; - return fit; -} - -void SHMProcess::writeSTLString(const uint32_t address, const std::string writeString) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = address; - strncpy(D_SHMDATA(char),writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator - full_barrier - d->SetAndWait(CORE_WRITE_STL_STRING); -} - string SHMProcess::readClassName (uint32_t vptr) { if(!d->locked) throw Error::MemoryAccessDenied(); @@ -836,52 +403,9 @@ string SHMProcess::getPath() return(string(target_name)); } -// get module index by name and version. bool 0 = error -bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - modulelookup * payload = D_SHMDATA(modulelookup); - payload->version = version; - - strncpy(payload->name,name,255); - payload->name[255] = 0; - - if(!SetAndWait(CORE_ACQUIRE_MODULE)) - { - return false; // FIXME: throw a fatal exception instead - } - if(D_SHMHDR->error) - { - return false; - } - //fprintf(stderr,"%s v%d : %d\n", name, version, D_SHMHDR->value); - OUTPUT = D_SHMHDR->value; - return true; -} - char * SHMProcess::getSHMStart (void) { if(!d->locked) return 0; //THROW HERE! - return /*d->shm_addr_with_cl_idx*/ d->shm_addr; -} - -bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, pid_t & PID) -{ - if(!locked) throw Error::MemoryAccessDenied(); - - SHMDATA(coreattach)->cl_affinity = OS_getAffinity(); - if(!SetAndWait(CORE_ATTACH)) return false; - /* - cerr <<"CORE_VERSION" << CORE_VERSION << endl; - cerr <<"server CORE_VERSION" << SHMDATA(coreattach)->sv_version << endl; - */ - versionOK =( SHMDATA(coreattach)->sv_version == CORE_VERSION ); - PID = SHMDATA(coreattach)->sv_PID; - useYield = SHMDATA(coreattach)->sv_useYield; - #ifdef DEBUG - if(useYield) cerr << "Using Yield!" << endl; - #endif - return true; + return d->shm_addr; } diff --git a/library/DFProcess-windows-SHM.cpp b/library/DFProcess-windows-SHM.cpp index 97711a979..7966cf58c 100644 --- a/library/DFProcess-windows-SHM.cpp +++ b/library/DFProcess-windows-SHM.cpp @@ -22,63 +22,26 @@ must not be misrepresented as being the original software. distribution. */ #include "Internal.h" -#include "ProcessInternal.h" +#include "SHMProcess.h" #include "dfhack/VersionInfo.h" #include "dfhack/DFError.h" #include "shms.h" #include "mod-core.h" using namespace DFHack; -class SHMProcess::Private +SHMProcess::Private::Private(SHMProcess * self_) { - public: - Private() - { - memdescriptor = NULL; - process_ID = 0; - shm_addr = 0; - attached = false; - locked = false; - identified = false; - useYield = 0; - DFSVMutex = 0; - DFCLMutex = 0; - DFCLSuspendMutex = 0; - attachmentIdx = -1; - }; - ~Private(){}; - VersionInfo * memdescriptor; - SHMProcess * self; - uint32_t process_ID; - char *shm_addr; - HANDLE DFSVMutex; - HANDLE DFCLMutex; - HANDLE DFCLSuspendMutex; - int attachmentIdx; - - bool attached; - bool locked; - bool identified; - bool useYield; - - bool validate(std::vector< VersionInfo* >& known_versions); - - bool Aux_Core_Attach(bool & versionOK, uint32_t & PID); - bool SetAndWait (uint32_t state); - bool GetLocks(); - bool AreLocksOk(); - void FreeLocks(); -}; - -// some helpful macros to keep the code bloat in check -#define SHMCMD ( (uint32_t *) shm_addr)[attachmentIdx] -#define D_SHMCMD ( (uint32_t *) (d->shm_addr))[d->attachmentIdx] - -#define SHMHDR ((shm_core_hdr *)shm_addr) -#define D_SHMHDR ((shm_core_hdr *)(d->shm_addr)) - -#define SHMDATA(type) ((type *)(shm_addr + SHM_HEADER)) -#define D_SHMDATA(type) ((type *)(d->shm_addr + SHM_HEADER)) + memdescriptor = NULL; + process_ID = 0; + attached = false; + locked = false; + identified = false; + useYield = 0; + DFSVMutex = 0; + DFCLMutex = 0; + DFCLSuspendMutex = 0; + self = self_; +} bool SHMProcess::Private::SetAndWait (uint32_t state) { @@ -253,46 +216,6 @@ bool SHMProcess::Private::AreLocksOk() return false; } -SHMProcess::SHMProcess(uint32_t PID, vector & known_versions) -: d(new Private()) -{ - d->process_ID = PID; - d->self = this; - // attach the SHM - if(!attach()) - { - return; - } - // Test bridge version, get PID, sync Yield - bool bridgeOK; - if(!d->Aux_Core_Attach(bridgeOK,d->process_ID)) - { - detach(); - throw Error::SHMAttachFailure(); - } - else if(!bridgeOK) - { - detach(); - throw Error::SHMVersionMismatch(); - } - d->validate(known_versions); - // at this point, DF is attached and suspended, make it run - detach(); -} - -bool SHMProcess::isSuspended() -{ - return d->locked; -} -bool SHMProcess::isAttached() -{ - return d->attached; -} - -bool SHMProcess::isIdentified() -{ - return d->identified; -} bool SHMProcess::Private::validate(vector & known_versions) { // try to identify the DF version @@ -350,29 +273,6 @@ bool SHMProcess::Private::validate(vector & known_versions) } return false; } -SHMProcess::~SHMProcess() -{ - if(d->attached) - { - detach(); - } - // destroy data model. this is assigned by processmanager - if(d->memdescriptor) - { - delete d->memdescriptor; - } - delete d; -} - -VersionInfo * SHMProcess::getDescriptor() -{ - return d->memdescriptor; -} - -int SHMProcess::getPID() -{ - return d->process_ID; -} bool SHMProcess::getThreadIDs(vector & threads ) { @@ -424,121 +324,14 @@ void SHMProcess::getMemRanges( vector & ranges ) ranges.push_back(temp); } -bool SHMProcess::suspend() +bool SHMProcess::acquireSuspendLock() { - if(!d->attached) - { - return false; - } - if(d->locked) - { - return true; - } - //cerr << "suspend" << endl;// FIXME: throw - // FIXME: this should be controlled on the server side - // FIXME: IF server got CORE_RUN in this frame, interpret CORE_SUSPEND as CORE_STEP - // did we just resume a moment ago? - if(D_SHMCMD == CORE_RUN) - { - //fprintf(stderr,"%d invokes step\n",d->attachmentIdx); - // wait for the next window - /* - if(!d->SetAndWait(CORE_STEP)) - { - throw Error::SHMLockingError("if(!d->SetAndWait(CORE_STEP))"); - } - */ - D_SHMCMD = CORE_STEP; - } - else - { - //fprintf(stderr,"%d invokes suspend\n",d->attachmentIdx); - // lock now - /* - if(!d->SetAndWait(CORE_SUSPEND)) - { - throw Error::SHMLockingError("if(!d->SetAndWait(CORE_SUSPEND))"); - } - */ - D_SHMCMD = CORE_SUSPEND; - } - //fprintf(stderr,"waiting for lock\n"); - // we wait for the server to give up our suspend lock (held by default) - if( WaitForSingleObject(d->DFCLSuspendMutex,INFINITE) == 0 ) - { - d->locked = true; - return true; - } - return false; -} - -// FIXME: needs a good think-through -bool SHMProcess::asyncSuspend() -{ - if(!d->attached) - { - return false; - } - if(d->locked) - { - return true; - } - //cerr << "async suspend" << endl;// FIXME: throw - uint32_t cmd = D_SHMCMD; - if(cmd == CORE_SUSPENDED) - { - // we have to hold the lock to be really suspended - if( WaitForSingleObject(d->DFCLSuspendMutex,INFINITE) == 0 ) - { - d->locked = true; - return true; - } - return false; - } - else - { - // did we just resume a moment ago? - if(cmd == CORE_STEP) - { - return false; - } - else if(cmd == CORE_RUN) - { - D_SHMCMD = CORE_STEP; - } - else - { - D_SHMCMD = CORE_SUSPEND; - } - return false; - } + return ( WaitForSingleObject(d->DFCLSuspendMutex,INFINITE) == 0 ); } -bool SHMProcess::forceresume() +bool SHMProcess::releaseSuspendLock() { - return resume(); -} - -// FIXME: wait for the server to advance a step! -bool SHMProcess::resume() -{ - if(!d->attached) - return false; - if(!d->locked) - return true; - //cerr << "resume" << endl;// FIXME: throw - // unlock the suspend lock - if( ReleaseMutex(d->DFCLSuspendMutex) != 0) - { - d->locked = false; - if(d->SetAndWait(CORE_RUN)) // we have to make sure the server responds! - { - return true; - } - throw Error::SHMLockingError("if(d->SetAndWait(CORE_RUN))"); - } - throw Error::SHMLockingError("if( ReleaseMutex(d->DFCLSuspendMutex) != 0)"); - return false; + return ( ReleaseMutex(d->DFCLSuspendMutex) != 0); } @@ -613,229 +406,6 @@ bool SHMProcess::detach() return true; } -void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - // normal read under 1MB - if(size <= SHM_BODY) - { - D_SHMHDR->address = src_address; - D_SHMHDR->length = size; - full_barrier - d->SetAndWait(CORE_READ); - memcpy (target_buffer, D_SHMDATA(void),size); - } - // a big read, we pull data over the shm in iterations - else - { - // first read equals the size of the SHM window - uint32_t to_read = SHM_BODY; - while (size) - { - // read to_read bytes from src_cursor - D_SHMHDR->address = src_address; - D_SHMHDR->length = to_read; - full_barrier - d->SetAndWait(CORE_READ); - memcpy (target_buffer, D_SHMDATA(void) ,to_read); - // decrease size by bytes read - size -= to_read; - // move the cursors - src_address += to_read; - target_buffer += to_read; - // check how much to write in the next iteration - to_read = min(size, (uint32_t) SHM_BODY); - } - } -} - -void SHMProcess::readByte (const uint32_t offset, uint8_t &val ) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - full_barrier - d->SetAndWait(CORE_READ_BYTE); - val = D_SHMHDR->value; -} - -void SHMProcess::readWord (const uint32_t offset, uint16_t &val) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - full_barrier - d->SetAndWait(CORE_READ_WORD); - val = D_SHMHDR->value; -} - -void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - full_barrier - d->SetAndWait(CORE_READ_DWORD); - val = D_SHMHDR->value; -} - -void SHMProcess::readQuad (const uint32_t offset, uint64_t &val) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - full_barrier - d->SetAndWait(CORE_READ_QUAD); - val = D_SHMHDR->Qvalue; -} - -void SHMProcess::readFloat (const uint32_t offset, float &val) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - full_barrier - d->SetAndWait(CORE_READ_DWORD); - val = reinterpret_cast (D_SHMHDR->value); -} - -/* - * WRITING - */ - -void SHMProcess::writeQuad (uint32_t offset, uint64_t data) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - D_SHMHDR->Qvalue = data; - full_barrier - d->SetAndWait(CORE_WRITE_QUAD); -} - -void SHMProcess::writeDWord (uint32_t offset, uint32_t data) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - D_SHMHDR->value = data; - full_barrier - d->SetAndWait(CORE_WRITE_DWORD); -} - -// using these is expensive. -void SHMProcess::writeWord (uint32_t offset, uint16_t data) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - D_SHMHDR->value = data; - full_barrier - d->SetAndWait(CORE_WRITE_WORD); -} - -void SHMProcess::writeByte (uint32_t offset, uint8_t data) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - D_SHMHDR->value = data; - full_barrier - d->SetAndWait(CORE_WRITE_BYTE); -} - -void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - // normal write under 1MB - if(size <= SHM_BODY) - { - D_SHMHDR->address = dst_address; - D_SHMHDR->length = size; - memcpy(D_SHMDATA(void),source_buffer, size); - full_barrier - d->SetAndWait(CORE_WRITE); - } - // a big write, we push this over the shm in iterations - else - { - // first write equals the size of the SHM window - uint32_t to_write = SHM_BODY; - while (size) - { - // write to_write bytes to dst_cursor - D_SHMHDR->address = dst_address; - D_SHMHDR->length = to_write; - memcpy(D_SHMDATA(void),source_buffer, to_write); - full_barrier - d->SetAndWait(CORE_WRITE); - // decrease size by bytes written - size -= to_write; - // move the cursors - source_buffer += to_write; - dst_address += to_write; - // check how much to write in the next iteration - to_write = min(size, (uint32_t) SHM_BODY); - } - } -} - -// FIXME: butt-fugly -const std::string SHMProcess::readCString (uint32_t offset) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - 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; -} - -const std::string SHMProcess::readSTLString(uint32_t offset) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - full_barrier - d->SetAndWait(CORE_READ_STL_STRING); - return(string( D_SHMDATA(char) )); -} - -size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - full_barrier - d->SetAndWait(CORE_READ_STL_STRING); - size_t length = D_SHMHDR->value; - size_t fit = min(bufcapacity - 1, length); - strncpy(buffer,D_SHMDATA(char),fit); - buffer[fit] = 0; - return fit; -} - -void SHMProcess::writeSTLString(const uint32_t address, const std::string writeString) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = address; - strncpy(D_SHMDATA(char),writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator - full_barrier - d->SetAndWait(CORE_WRITE_STL_STRING); -} - string SHMProcess::readClassName (uint32_t vptr) { int rtti = Process::readDWord(vptr - 0x4); @@ -857,51 +427,9 @@ string SHMProcess::getPath() return(out.substr(0,out.find_last_of("\\"))); } -// get module index by name and version. bool 0 = error -bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - modulelookup * payload = D_SHMDATA(modulelookup); - payload->version = version; - - strncpy(payload->name,name,255); - payload->name[255] = 0; - - if(!SetAndWait(CORE_ACQUIRE_MODULE)) - { - return false; // FIXME: throw a fatal exception instead - } - if(D_SHMHDR->error) - { - return false; - } - //fprintf(stderr,"%s v%d : %d\n", name, version, D_SHMHDR->value); - OUTPUT = D_SHMHDR->value; - return true; -} - char * SHMProcess::getSHMStart (void) { if(!d->locked) throw Error::MemoryAccessDenied(); - return /*d->shm_addr_with_cl_idx*/ d->shm_addr; -} - -bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, uint32_t & PID) -{ - if(!locked) throw Error::MemoryAccessDenied(); - SHMDATA(coreattach)->cl_affinity = OS_getAffinity(); - if(!SetAndWait(CORE_ATTACH)) return false; - /* - cerr <<"CORE_VERSION" << CORE_VERSION << endl; - cerr <<"server CORE_VERSION" << SHMDATA(coreattach)->sv_version << endl; - */ - versionOK =( SHMDATA(coreattach)->sv_version == CORE_VERSION ); - PID = SHMDATA(coreattach)->sv_PID; - useYield = SHMDATA(coreattach)->sv_useYield; - #ifdef DEBUG - if(useYield) cerr << "Using Yield!" << endl; - #endif - return true; + return d->shm_addr; } diff --git a/library/DFProcessEnumerator.cpp b/library/DFProcessEnumerator.cpp index fdd96ec27..e9d785fdb 100644 --- a/library/DFProcessEnumerator.cpp +++ b/library/DFProcessEnumerator.cpp @@ -24,6 +24,7 @@ distribution. #include "Internal.h" #include "ProcessInternal.h" +#include "SHMProcess.h" #include "dfhack/VersionInfoFactory.h" #include "dfhack/DFProcessEnumerator.h" diff --git a/library/private/ProcessInternal.h b/library/private/ProcessInternal.h index 0bca44d54..8bab5b2c2 100644 --- a/library/private/ProcessInternal.h +++ b/library/private/ProcessInternal.h @@ -29,10 +29,6 @@ distribution. namespace DFHack { - //////////////////////////////////////////////////////////////////////////// - // Compiler appeasement area. Not worth a look really... // - //////////////////////////////////////////////////////////////////////////// - class DFHACK_EXPORT NormalProcess : public Process { friend class ProcessEnumerator; @@ -92,66 +88,6 @@ namespace DFHack bool SetAndWait (uint32_t state){return false;}; }; - class DFHACK_EXPORT SHMProcess : public Process - { - friend class ProcessEnumerator; - class Private; - private: - Private * const d; - - public: - SHMProcess(uint32_t PID, std::vector & known_versions); - ~SHMProcess(); - // Set up stuff so we can read memory - 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 & threads ); - void getMemRanges(std::vector & 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); - // get the SHM start if available - char * getSHMStart (void); - bool SetAndWait (uint32_t state); - }; - #ifdef LINUX_BUILD class DFHACK_EXPORT WineProcess : public Process { diff --git a/library/private/SHMProcess.h b/library/private/SHMProcess.h new file mode 100644 index 000000000..a4fe6e55f --- /dev/null +++ b/library/private/SHMProcess.h @@ -0,0 +1,144 @@ +/* +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 SHM_PROCESS_H_INCLUDED +#define SHM_PROCESS_H_INCLUDED + +#include "dfhack/DFProcess.h" + +namespace DFHack +{ + class DFHACK_EXPORT SHMProcess : public Process + { + friend class ProcessEnumerator; + class Private; + private: + Private * const d; + + public: + SHMProcess(uint32_t PID, std::vector & known_versions); + ~SHMProcess(); + // Set up stuff so we can read memory + 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 & threads ); + void getMemRanges(std::vector & 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); + // get the SHM start if available + char * getSHMStart (void); + bool SetAndWait (uint32_t state); + private: + bool acquireSuspendLock(); + bool releaseSuspendLock(); + }; + + class SHMProcess::Private + { + public: + Private(SHMProcess * self_); + ~Private(){} + VersionInfo * memdescriptor; + SHMProcess * self; + char *shm_addr; + int attachmentIdx; + + bool attached; + bool locked; + bool identified; + bool useYield; + +#ifdef LINUX_BUILD + pid_t process_ID; + int shm_ID; + int server_lock; + int client_lock; + int suspend_lock; +#else + typedef unit32_t pid_t; + uint32_t process_ID; + HANDLE DFSVMutex; + HANDLE DFCLMutex; + HANDLE DFCLSuspendMutex; +#endif + + bool validate(std::vector< VersionInfo* >& known_versions); + + bool Aux_Core_Attach(bool & versionOK, pid_t& PID); + bool SetAndWait (uint32_t state); + bool GetLocks(); + bool AreLocksOk(); + void FreeLocks(); + }; +} + +// some helpful macros to keep the code bloat in check +#define SHMCMD ( (uint32_t *) shm_addr)[attachmentIdx] +#define D_SHMCMD ( (uint32_t *) (d->shm_addr))[d->attachmentIdx] + +#define SHMHDR ((shm_core_hdr *)shm_addr) +#define D_SHMHDR ((shm_core_hdr *)(d->shm_addr)) + +#define SHMDATA(type) ((type *)(shm_addr + SHM_HEADER)) +#define D_SHMDATA(type) ((type *)(d->shm_addr + SHM_HEADER)) + +#endif From b3c490857cc15428a7b8c2af522c953642228126 Mon Sep 17 00:00:00 2001 From: Tom Prince Date: Wed, 23 Feb 2011 05:26:33 -0500 Subject: [PATCH 3/5] Refactor DFProcess-linux-{,wine}.cpp into DFProcess-linux-base.cpp. --- library/CMakeLists.txt | 4 +- library/DFProcess-linux-base.cpp | 439 ++++++++++++++++ library/DFProcess-linux-wine.cpp | 482 ++---------------- library/DFProcess-linux.cpp | 451 +--------------- library/DFProcess-windows.cpp | 2 +- library/DFProcessEnumerator.cpp | 3 +- .../{ProcessInternal.h => LinuxProcess.h} | 113 ++-- library/private/WindowsProcess.h | 94 ++++ 8 files changed, 634 insertions(+), 954 deletions(-) create mode 100644 library/DFProcess-linux-base.cpp rename library/private/{ProcessInternal.h => LinuxProcess.h} (65%) create mode 100644 library/private/WindowsProcess.h diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index e3ba69df4..f22c53fd4 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -29,8 +29,9 @@ include_directories (${CMAKE_SOURCE_DIR}/library/private/) SET(PROJECT_HDRS_INTERNAL private/ContextShared.h private/Internal.h - private/ProcessInternal.h private/SHMProcess.h + private/LinuxProcess.h + private/WindowsProcess.h ) SET(PROJECT_HDRS @@ -141,6 +142,7 @@ include/dfhack/DFstdint_win.h SET(PROJECT_SRCS_LINUX DFProcess-linux.cpp +DFProcess-linux-base.cpp DFProcess-linux-SHM.cpp DFProcess-linux-wine.cpp modules/WindowIO-linux.cpp diff --git a/library/DFProcess-linux-base.cpp b/library/DFProcess-linux-base.cpp new file mode 100644 index 000000000..7683cb121 --- /dev/null +++ b/library/DFProcess-linux-base.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 +#include +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 & threads ) +{ + return false; +} + +//FIXME: cross-reference with ELF segment entries? +void LinuxProcessBase::getMemRanges( vector & 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)); +} diff --git a/library/DFProcess-linux-wine.cpp b/library/DFProcess-linux-wine.cpp index 7f880afbb..7e794f497 100644 --- a/library/DFProcess-linux-wine.cpp +++ b/library/DFProcess-linux-wine.cpp @@ -22,7 +22,7 @@ must not be misrepresented as being the original software. distribution. */ #include "Internal.h" -#include "ProcessInternal.h" +#include "LinuxProcess.h" #include "dfhack/VersionInfo.h" #include "dfhack/DFError.h" #include @@ -30,37 +30,8 @@ distribution. #include 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 & known_versions); -}; -WineProcess::WineProcess(uint32_t pid, vector & known_versions) -: d(new Private(this)) +WineProcess::WineProcess(uint32_t pid, vector & known_versions) : LinuxProcessBase(pid) { char dir_name [256]; char exe_link_name [256]; @@ -107,27 +78,14 @@ WineProcess::WineProcess(uint32_t pid, vector & known_versions) sprintf(exe_link,"%s/%s",target_name,cmdline.c_str()); // 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; } } } -bool WineProcess::isSuspended() -{ - return d->suspended; -} -bool WineProcess::isAttached() -{ - return d->attached; -} - -bool WineProcess::isIdentified() -{ - return d->identified; -} -bool WineProcess::Private::validate(char* exe_file, uint32_t pid, char* mem_file, std::vector< VersionInfo* >& known_versions) +bool WineProcess::validate(char * exe_file,uint32_t pid, char * memFile, vector & known_versions) { md5wrapper md5; // get hash of the running DF process @@ -137,410 +95,42 @@ bool WineProcess::Private::validate(char* exe_file, uint32_t pid, char* mem_file // iterate over the list of memory locations for ( it=known_versions.begin() ; it < known_versions.end(); it++ ) { - string thishash; try { - thishash = (*it)->getMD5(); - } - catch (Error::AllMemdef& e) - { - continue; - } - // 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); - my_descriptor = m; - m->setParentProcess(dynamic_cast( self )); - my_handle = my_pid = pid; - // tell WineProcess about the /proc/PID/mem file - memFile = mem_file; - identified = true; - OffsetGroup * strGrp = m->getGroup("string")->getGroup("MSVC"); - STLSTR_buf_off = strGrp->getOffset("buffer"); - STLSTR_size_off = strGrp->getOffset("size"); - STLSTR_cap_off = strGrp->getOffset("capacity"); - 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 & threads ) -{ - return false; -} - -//FIXME: cross-reference with ELF segment entries? -void WineProcess::getMemRanges( vector & 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 - 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 << 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; - } - 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) + if (hash == (*it)->getMD5()) // are the md5 hashes the same? { - writeQuad(offset, *(uint64_t *) (source + indexptr)); - offset +=8; - indexptr +=8; - size -=8; + if (OS_WINDOWS == (*it)->getOS()) + { + VersionInfo *m = new VersionInfo(**it); + // keep track of created memory_info object so we can destroy it later + d->my_descriptor = m; + m->setParentProcess(this); + // tell Process about the /proc/PID/mem file + d->memFile = memFile; + d->identified = true; + + OffsetGroup * strGrp = m->getGroup("string")->getGroup("MSVC"); + STLSTR_buf_off = strGrp->getOffset("buffer"); + STLSTR_size_off = strGrp->getOffset("size"); + STLSTR_cap_off = strGrp->getOffset("capacity"); + return true; + } } - 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) + catch (Error::AllMemdef&) { - 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; + continue; } } + return false; } -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) { - uint32_t start_offset = offset + d->STLSTR_buf_off; - size_t length = Process::readDWord(offset + d->STLSTR_size_off); - size_t capacity = Process::readDWord(offset + d->STLSTR_cap_off); + uint32_t start_offset = offset + STLSTR_buf_off; + size_t length = Process::readDWord(offset + STLSTR_size_off); + size_t capacity = Process::readDWord(offset + STLSTR_cap_off); 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) { - uint32_t start_offset = offset + d->STLSTR_buf_off; - size_t length = Process::readDWord(offset + d->STLSTR_size_off); - size_t capacity = Process::readDWord(offset + d->STLSTR_cap_off); + uint32_t start_offset = offset + STLSTR_buf_off; + size_t length = Process::readDWord(offset + STLSTR_size_off); + size_t capacity = Process::readDWord(offset + STLSTR_cap_off); char * temp = new char[capacity+1]; @@ -592,15 +182,3 @@ string WineProcess::readClassName (uint32_t vptr) raw.resize(raw.length() - 2);// trim @@ from end 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)); -} diff --git a/library/DFProcess-linux.cpp b/library/DFProcess-linux.cpp index 284bb2dc2..bb2509c5f 100644 --- a/library/DFProcess-linux.cpp +++ b/library/DFProcess-linux.cpp @@ -22,42 +22,14 @@ must not be misrepresented as being the original software. distribution. */ #include "Internal.h" -#include "ProcessInternal.h" +#include "LinuxProcess.h" #include "dfhack/VersionInfo.h" #include "dfhack/DFError.h" #include #include using namespace DFHack; -class NormalProcess::Private -{ - 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 & known_versions); -}; - -NormalProcess::NormalProcess(uint32_t pid, vector< VersionInfo* >& known_versions) -: d(new Private(this)) +NormalProcess::NormalProcess(uint32_t pid, vector & known_versions) : LinuxProcessBase(pid) { char dir_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) { // 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; } } -bool NormalProcess::isSuspended() -{ - 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 & known_versions) +bool NormalProcess::validate(char * exe_file,uint32_t pid, char * memFile, vector & known_versions) { md5wrapper md5; // get hash of the running DF process @@ -120,26 +78,19 @@ bool NormalProcess::Private::validate(char * exe_file,uint32_t pid, char * memFi { 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()) + if (OS_LINUX == (*it)->getOS()) { - VersionInfo *m2 = new VersionInfo(*m); - my_descriptor = m2; - m2->setParentProcess(dynamic_cast( self )); - my_handle = my_pid = pid; + VersionInfo *m = new VersionInfo(**it); + // keep track of created memory_info object so we can destroy it later + d->my_descriptor = m; + m->setParentProcess(this); + // tell Process about the /proc/PID/mem file + d->memFile = memFile; + d->identified = true; + return true; } - else - { - // some error happened, continue with next process - continue; - } - // tell NormalProcess about the /proc/PID/mem file - this->memFile = memFile; - identified = true; - return true; } } catch (Error::AllMemdef&) @@ -150,368 +101,6 @@ bool NormalProcess::Private::validate(char * exe_file,uint32_t pid, char * memFi 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 & threads ) -{ - return false; -} - -//FIXME: cross-reference with ELF segment entries? -void NormalProcess::getMemRanges( vector & 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 { @@ -555,15 +144,3 @@ string NormalProcess::readClassName (uint32_t vptr) size_t end = raw.length(); 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)); -} diff --git a/library/DFProcess-windows.cpp b/library/DFProcess-windows.cpp index 0d7d32f8c..4c4fae337 100644 --- a/library/DFProcess-windows.cpp +++ b/library/DFProcess-windows.cpp @@ -22,7 +22,7 @@ must not be misrepresented as being the original software. distribution. */ #include "Internal.h" -#include "ProcessInternal.h" +#include "WindowsProcess.h" #include "dfhack/VersionInfo.h" #include "dfhack/DFError.h" #include diff --git a/library/DFProcessEnumerator.cpp b/library/DFProcessEnumerator.cpp index e9d785fdb..2dbfd806b 100644 --- a/library/DFProcessEnumerator.cpp +++ b/library/DFProcessEnumerator.cpp @@ -23,8 +23,9 @@ distribution. */ #include "Internal.h" -#include "ProcessInternal.h" #include "SHMProcess.h" +#include "LinuxProcess.h" +#include "WindowsProcess.h" #include "dfhack/VersionInfoFactory.h" #include "dfhack/DFProcessEnumerator.h" diff --git a/library/private/ProcessInternal.h b/library/private/LinuxProcess.h similarity index 65% rename from library/private/ProcessInternal.h rename to library/private/LinuxProcess.h index 8bab5b2c2..17d30b85a 100644 --- a/library/private/ProcessInternal.h +++ b/library/private/LinuxProcess.h @@ -22,22 +22,26 @@ must not be misrepresented as being the original software. distribution. */ -#ifndef PROCESS_INTERNAL_H_INCLUDED -#define PROCESS_INTERNAL_H_INCLUDED +#ifndef LINUX_PROCESS_H_INCLUDED +#define LINUX_PROCESS_H_INCLUDED + +#ifdef LINUX_BUILD #include "dfhack/DFProcess.h" namespace DFHack { - class DFHACK_EXPORT NormalProcess : public Process + class LinuxProcessBase : public Process { - friend class ProcessEnumerator; + public: class Private; - private: + protected: Private * const d; + bool readProgramName(char *target_name, char *mem_name, char *cmdline_name); public: - NormalProcess(uint32_t pid, std::vector & known_versions); - ~NormalProcess(); + LinuxProcessBase(uint32_t pid); + ~LinuxProcessBase(); + bool attach(); bool detach(); @@ -63,23 +67,18 @@ namespace DFHack 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 & threads ); - void getMemRanges(std::vector & ranges ); VersionInfo *getDescriptor(); int getPID(); std::string getPath(); + + bool getThreadIDs(std::vector & threads ); + void getMemRanges(std::vector & ranges ); // 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 @@ -88,65 +87,55 @@ namespace DFHack bool SetAndWait (uint32_t state){return false;}; }; -#ifdef LINUX_BUILD - class DFHACK_EXPORT WineProcess : public Process + class DFHACK_EXPORT NormalProcess : public LinuxProcessBase { friend class ProcessEnumerator; - class Private; - private: - Private * const d; - public: - WineProcess(uint32_t pid, std::vector & 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); + NormalProcess(uint32_t pid, std::vector & 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: + bool validate(char * exe_file,uint32_t pid, char * memFile, vector & known_versions); + }; - const std::string readCString (uint32_t offset); + 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: + WineProcess(uint32_t pid, std::vector & known_versions); - bool isSuspended(); - bool isAttached(); - bool isIdentified(); + 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: + bool validate(char * exe_file,uint32_t pid, char * memFile, vector & known_versions); + }; - bool getThreadIDs(std::vector & threads ); - void getMemRanges(std::vector & ranges ); - VersionInfo *getDescriptor(); - int getPID(); - // 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;}; - bool SetAndWait (uint32_t state){return false;}; - std::string getPath(); + class LinuxProcessBase::Private + { + public: + Private(LinuxProcessBase * self_, pid_t); + ~Private(){}; + VersionInfo * my_descriptor; + LinuxProcessBase * self; + pid_t my_pid; + string memFile; + int memFileHandle; + bool attached; + bool suspended; + bool identified; }; -#endif } + +#endif #endif diff --git a/library/private/WindowsProcess.h b/library/private/WindowsProcess.h new file mode 100644 index 000000000..96ed504a1 --- /dev/null +++ b/library/private/WindowsProcess.h @@ -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 & 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 & threads ); + void getMemRanges(std::vector & 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 From 2927b08f936773016d19097b53dfd79faf25f841 Mon Sep 17 00:00:00 2001 From: Tom Prince Date: Wed, 23 Feb 2011 05:49:33 -0500 Subject: [PATCH 4/5] Remove handle-body idiom of DFProcess on linux. Since the classes aren't exposed to the client, there is no reason to split the class. --- library/DFProcess-linux-base.cpp | 105 +++++++++++++++---------------- library/DFProcess-linux-wine.cpp | 18 +++--- library/DFProcess-linux.cpp | 15 +++-- library/private/LinuxProcess.h | 34 ++++------ 4 files changed, 75 insertions(+), 97 deletions(-) diff --git a/library/DFProcess-linux-base.cpp b/library/DFProcess-linux-base.cpp index 7683cb121..7549c1387 100644 --- a/library/DFProcess-linux-base.cpp +++ b/library/DFProcess-linux-base.cpp @@ -29,55 +29,48 @@ distribution. #include using namespace DFHack; -LinuxProcessBase::Private::Private(LinuxProcessBase * self_, pid_t pid) +LinuxProcessBase::LinuxProcessBase(uint32_t pid) +: my_pid(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; + return suspended; } bool LinuxProcessBase::isAttached() { - return d->attached; + return attached; } bool LinuxProcessBase::isIdentified() { - return d->identified; + return identified; } LinuxProcessBase::~LinuxProcessBase() { - if(d->attached) + if(attached) { detach(); } // destroy our copy of the memory descriptor - if(d->my_descriptor) - delete d->my_descriptor; - delete d; + if(my_descriptor) + delete my_descriptor; } VersionInfo * LinuxProcessBase::getDescriptor() { - return d->my_descriptor; + return my_descriptor; } int LinuxProcessBase::getPID() { - return d->my_pid; + return my_pid; } //FIXME: implement @@ -92,7 +85,7 @@ void LinuxProcessBase::getMemRanges( vector & ranges ) char buffer[1024]; char permissions[5]; // r/-, w/-, x/-, p/s, 0 - sprintf(buffer, "/proc/%lu/maps", d->my_pid); + sprintf(buffer, "/proc/%lu/maps", my_pid); FILE *mapFile = ::fopen(buffer, "r"); uint64_t offset, device1, device2, node; @@ -122,11 +115,11 @@ bool LinuxProcessBase::asyncSuspend() bool LinuxProcessBase::suspend() { int status; - if(!d->attached) + if(!attached) return false; - if(d->suspended) + if(suspended) return true; - if (kill(d->my_pid, SIGSTOP) == -1) + if (kill(my_pid, SIGSTOP) == -1) { // no, we got an error perror("kill SIGSTOP error"); @@ -135,7 +128,7 @@ bool LinuxProcessBase::suspend() while(true) { // we wait on the pid - pid_t w = waitpid(d->my_pid, &status, 0); + pid_t w = waitpid(my_pid, &status, 0); if (w == -1) { // child died @@ -148,7 +141,7 @@ bool LinuxProcessBase::suspend() break; } } - d->suspended = true; + suspended = true; return true; } @@ -159,17 +152,17 @@ bool LinuxProcessBase::forceresume() bool LinuxProcessBase::resume() { - if(!d->attached) + if(!attached) return false; - if(!d->suspended) + if(!suspended) return true; - if (ptrace(PTRACE_CONT, d->my_pid, NULL, NULL) == -1) + if (ptrace(PTRACE_CONT, my_pid, NULL, NULL) == -1) { // no, we got an error perror("ptrace resume error"); return false; } - d->suspended = false; + suspended = false; return true; } @@ -177,24 +170,24 @@ bool LinuxProcessBase::resume() bool LinuxProcessBase::attach() { int status; - if(d->attached) + if(attached) { - if(!d->suspended) + if(!suspended) return suspend(); return true; } // can we attach? - if (ptrace(PTRACE_ATTACH , d->my_pid, NULL, NULL) == -1) + if (ptrace(PTRACE_ATTACH , my_pid, NULL, NULL) == -1) { // no, we got an error perror("ptrace attach error"); - cerr << "attach failed on pid " << d->my_pid << endl; + cerr << "attach failed on pid " << my_pid << endl; return false; } while(true) { // we wait on the pid - pid_t w = waitpid(d->my_pid, &status, 0); + pid_t w = waitpid(my_pid, &status, 0); if (w == -1) { // child died @@ -207,52 +200,52 @@ bool LinuxProcessBase::attach() break; } } - d->suspended = true; + suspended = true; - int proc_pid_mem = open(d->memFile.c_str(),O_RDONLY); + int proc_pid_mem = open(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; + ptrace(PTRACE_DETACH, my_pid, NULL, NULL); + cerr << memFile << endl; + cerr << "couldn't open /proc/" << my_pid << "/mem" << endl; perror("open(memFile.c_str(),O_RDONLY)"); return false; } else { - d->attached = true; + attached = true; - d->memFileHandle = proc_pid_mem; + memFileHandle = proc_pid_mem; return true; // we are attached } } bool LinuxProcessBase::detach() { - if(!d->attached) return true; - if(!d->suspended) suspend(); + if(!attached) return true; + if(!suspended) suspend(); int result = 0; // close /proc/PID/mem - result = close(d->memFileHandle); + result = close(memFileHandle); if(result == -1) { - cerr << "couldn't close /proc/"<< d->my_pid <<"/mem" << endl; + cerr << "couldn't close /proc/"<< my_pid <<"/mem" << endl; perror("mem file close"); return false; } else { // detach - result = ptrace(PTRACE_DETACH, d->my_pid, NULL, NULL); + result = ptrace(PTRACE_DETACH, my_pid, NULL, NULL); if(result == -1) { - cerr << "couldn't detach from process pid" << d->my_pid << endl; + cerr << "couldn't detach from process pid" << my_pid << endl; perror("ptrace detach"); return false; } else { - d->attached = false; + attached = false; return true; } } @@ -268,7 +261,7 @@ void LinuxProcessBase::read (const uint32_t offset, const uint32_t size, uint8_t ssize_t remaining = size; while (total != size) { - result = pread(d->memFileHandle, target + total ,remaining,offset + total); + result = pread(memFileHandle, target + total ,remaining,offset + total); if(result == -1) { cerr << "pread failed: can't read " << size << " bytes at addres " << offset << endl; @@ -316,10 +309,10 @@ void LinuxProcessBase::readQuad (const uint32_t offset, uint64_t &val) void LinuxProcessBase::writeQuad (uint32_t offset, const uint64_t data) { #ifdef HAVE_64_BIT - ptrace(PTRACE_POKEDATA,d->my_pid, offset, data); + ptrace(PTRACE_POKEDATA,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)); + ptrace(PTRACE_POKEDATA,my_pid, offset, (uint32_t) data); + ptrace(PTRACE_POKEDATA,my_pid, offset+4, (uint32_t) (data >> 32)); #endif } @@ -329,9 +322,9 @@ void LinuxProcessBase::writeDWord (uint32_t offset, uint32_t data) uint64_t orig = Process::readQuad(offset); orig &= 0xFFFFFFFF00000000; orig |= data; - ptrace(PTRACE_POKEDATA,d->my_pid, offset, orig); + ptrace(PTRACE_POKEDATA,my_pid, offset, orig); #else - ptrace(PTRACE_POKEDATA,d->my_pid, offset, data); + ptrace(PTRACE_POKEDATA,my_pid, offset, data); #endif } @@ -342,12 +335,12 @@ void LinuxProcessBase::writeWord (uint32_t offset, uint16_t data) uint64_t orig = Process::readQuad(offset); orig &= 0xFFFFFFFFFFFF0000; orig |= data; - ptrace(PTRACE_POKEDATA,d->my_pid, offset, orig); + ptrace(PTRACE_POKEDATA,my_pid, offset, orig); #else uint32_t orig = readDWord(offset); orig &= 0xFFFF0000; orig |= data; - ptrace(PTRACE_POKEDATA,d->my_pid, offset, orig); + ptrace(PTRACE_POKEDATA,my_pid, offset, orig); #endif } @@ -357,12 +350,12 @@ void LinuxProcessBase::writeByte (uint32_t offset, uint8_t data) uint64_t orig = Process::readQuad(offset); orig &= 0xFFFFFFFFFFFFFF00; orig |= data; - ptrace(PTRACE_POKEDATA,d->my_pid, offset, orig); + ptrace(PTRACE_POKEDATA,my_pid, offset, orig); #else uint32_t orig = readDWord(offset); orig &= 0xFFFFFF00; orig |= data; - ptrace(PTRACE_POKEDATA,d->my_pid, offset, orig); + ptrace(PTRACE_POKEDATA,my_pid, offset, orig); #endif } diff --git a/library/DFProcess-linux-wine.cpp b/library/DFProcess-linux-wine.cpp index 7e794f497..419f633b9 100644 --- a/library/DFProcess-linux-wine.cpp +++ b/library/DFProcess-linux-wine.cpp @@ -30,7 +30,6 @@ distribution. #include using namespace DFHack; - WineProcess::WineProcess(uint32_t pid, vector & known_versions) : LinuxProcessBase(pid) { char dir_name [256]; @@ -41,8 +40,8 @@ WineProcess::WineProcess(uint32_t pid, vector & known_versions) char target_name[1024]; int target_result; - d->identified = false; - d->my_descriptor = 0; + identified = false; + my_descriptor = 0; sprintf(dir_name,"/proc/%d/", pid); sprintf(exe_link_name,"/proc/%d/exe", pid); @@ -78,7 +77,7 @@ WineProcess::WineProcess(uint32_t pid, vector & known_versions) sprintf(exe_link,"%s/%s",target_name,cmdline.c_str()); // create wine process, add it to the vector - d->identified = validate(exe_link,pid,mem_name,known_versions); + identified = validate(exe_link,pid,mem_name,known_versions); return; } } @@ -101,15 +100,14 @@ bool WineProcess::validate(char * exe_file,uint32_t pid, char * memFile, vector { if (OS_WINDOWS == (*it)->getOS()) { - VersionInfo *m = new VersionInfo(**it); // keep track of created memory_info object so we can destroy it later - d->my_descriptor = m; - m->setParentProcess(this); + my_descriptor = new VersionInfo(**it); + my_descriptor->setParentProcess(this); // tell Process about the /proc/PID/mem file - d->memFile = memFile; - d->identified = true; + memFile = memFile; + identified = true; - OffsetGroup * strGrp = m->getGroup("string")->getGroup("MSVC"); + OffsetGroup * strGrp = my_descriptor->getGroup("string")->getGroup("MSVC"); STLSTR_buf_off = strGrp->getOffset("buffer"); STLSTR_size_off = strGrp->getOffset("size"); STLSTR_cap_off = strGrp->getOffset("capacity"); diff --git a/library/DFProcess-linux.cpp b/library/DFProcess-linux.cpp index bb2509c5f..7ce1d2152 100644 --- a/library/DFProcess-linux.cpp +++ b/library/DFProcess-linux.cpp @@ -39,8 +39,8 @@ NormalProcess::NormalProcess(uint32_t pid, vector & known_versio char target_name[1024]; int target_result; - d->identified = false; - d->my_descriptor = 0; + identified = false; + my_descriptor = 0; sprintf(dir_name,"/proc/%d/", pid); sprintf(exe_link_name,"/proc/%d/exe", pid); @@ -61,7 +61,7 @@ NormalProcess::NormalProcess(uint32_t pid, vector & known_versio if (strstr(target_name, "dwarfort.exe") != 0 || strstr(target_name,"Dwarf_Fortress") != 0) { // create linux process, add it to the vector - d->identified = validate(target_name,pid,mem_name,known_versions); + identified = validate(target_name,pid,mem_name,known_versions); return; } } @@ -82,13 +82,12 @@ bool NormalProcess::validate(char * exe_file,uint32_t pid, char * memFile, vecto { if (OS_LINUX == (*it)->getOS()) { - VersionInfo *m = new VersionInfo(**it); // keep track of created memory_info object so we can destroy it later - d->my_descriptor = m; - m->setParentProcess(this); + my_descriptor = new VersionInfo(**it); + my_descriptor->setParentProcess(this); // tell Process about the /proc/PID/mem file - d->memFile = memFile; - d->identified = true; + memFile = memFile; + identified = true; return true; } } diff --git a/library/private/LinuxProcess.h b/library/private/LinuxProcess.h index 17d30b85a..a9e3c094e 100644 --- a/library/private/LinuxProcess.h +++ b/library/private/LinuxProcess.h @@ -33,11 +33,14 @@ namespace DFHack { class LinuxProcessBase : public Process { - public: - class Private; protected: - Private * const d; - bool readProgramName(char *target_name, char *mem_name, char *cmdline_name); + VersionInfo * my_descriptor; + pid_t my_pid; + string memFile; + int memFileHandle; + bool attached; + bool suspended; + bool identified; public: LinuxProcessBase(uint32_t pid); ~LinuxProcessBase(); @@ -98,8 +101,8 @@ namespace DFHack 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: - bool validate(char * exe_file,uint32_t pid, char * memFile, vector & known_versions); + private: + bool validate(char * exe_file,uint32_t pid, char * memFile, vector & known_versions); }; class DFHACK_EXPORT WineProcess : public LinuxProcessBase @@ -117,23 +120,8 @@ namespace DFHack 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: - bool validate(char * exe_file,uint32_t pid, char * memFile, vector & known_versions); - }; - - class LinuxProcessBase::Private - { - public: - Private(LinuxProcessBase * self_, pid_t); - ~Private(){}; - VersionInfo * my_descriptor; - LinuxProcessBase * self; - pid_t my_pid; - string memFile; - int memFileHandle; - bool attached; - bool suspended; - bool identified; + private: + bool validate(char * exe_file,uint32_t pid, char * memFile, vector & known_versions); }; } From 63ab8672d3da6e8ab92ee8266847caaec2c09d07 Mon Sep 17 00:00:00 2001 From: Tom Prince Date: Wed, 23 Feb 2011 06:08:30 -0500 Subject: [PATCH 5/5] Add create*Process functions to create process instances. This allows us to remove friendship of DFProcessEnumerator for Process subclasses. --- library/CMakeLists.txt | 1 + library/DFProcess-SHM.cpp | 6 +++++ library/DFProcess-linux-wine.cpp | 26 ++++++++++++++++++++++ library/DFProcess-linux.cpp | 22 ++++++++++++++++++ library/DFProcess-windows.cpp | 6 +++++ library/DFProcessEnumerator.cpp | 10 ++++----- library/private/LinuxProcess.h | 33 --------------------------- library/private/ProcessFactory.h | 38 ++++++++++++++++++++++++++++++++ library/private/SHMProcess.h | 3 +-- library/private/WindowsProcess.h | 3 +-- 10 files changed, 105 insertions(+), 43 deletions(-) create mode 100644 library/private/ProcessFactory.h diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index f22c53fd4..d54fc79d5 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -32,6 +32,7 @@ SET(PROJECT_HDRS_INTERNAL private/SHMProcess.h private/LinuxProcess.h private/WindowsProcess.h + private/ProcessFactory.h ) SET(PROJECT_HDRS diff --git a/library/DFProcess-SHM.cpp b/library/DFProcess-SHM.cpp index ad7ebc8a5..3246e567e 100644 --- a/library/DFProcess-SHM.cpp +++ b/library/DFProcess-SHM.cpp @@ -23,6 +23,7 @@ distribution. */ #include "Internal.h" #include "SHMProcess.h" +#include "ProcessFactory.h" #include "dfhack/VersionInfo.h" #include "dfhack/DFError.h" #include "shms.h" @@ -30,6 +31,11 @@ distribution. using namespace DFHack; +Process* DFHack::createSHMProcess(uint32_t pid, vector & known_versions) +{ + return new SHMProcess(pid, known_versions); +} + SHMProcess::SHMProcess(uint32_t PID, vector & known_versions) : d(new Private(this)) { diff --git a/library/DFProcess-linux-wine.cpp b/library/DFProcess-linux-wine.cpp index 419f633b9..a75b8077d 100644 --- a/library/DFProcess-linux-wine.cpp +++ b/library/DFProcess-linux-wine.cpp @@ -23,6 +23,7 @@ distribution. */ #include "Internal.h" #include "LinuxProcess.h" +#include "ProcessFactory.h" #include "dfhack/VersionInfo.h" #include "dfhack/DFError.h" #include @@ -30,6 +31,31 @@ distribution. #include using namespace DFHack; +namespace { + class WineProcess : public LinuxProcessBase + { + private: + uint32_t STLSTR_buf_off; + uint32_t STLSTR_size_off; + uint32_t STLSTR_cap_off; + public: + WineProcess(uint32_t pid, std::vector & 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: + bool validate(char * exe_file,uint32_t pid, char * memFile, vector & known_versions); + }; +} + +Process* DFHack::createWineProcess(uint32_t pid, vector & known_versions) +{ + return new WineProcess(pid, known_versions); +} + WineProcess::WineProcess(uint32_t pid, vector & known_versions) : LinuxProcessBase(pid) { char dir_name [256]; diff --git a/library/DFProcess-linux.cpp b/library/DFProcess-linux.cpp index 7ce1d2152..44ba5afea 100644 --- a/library/DFProcess-linux.cpp +++ b/library/DFProcess-linux.cpp @@ -23,12 +23,34 @@ distribution. */ #include "Internal.h" #include "LinuxProcess.h" +#include "ProcessFactory.h" #include "dfhack/VersionInfo.h" #include "dfhack/DFError.h" #include #include using namespace DFHack; +namespace { + class NormalProcess : public LinuxProcessBase + { + public: + NormalProcess(uint32_t pid, std::vector & 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: + bool validate(char * exe_file,uint32_t pid, char * memFile, vector & known_versions); + }; +} + +Process* DFHack::createNormalProcess(uint32_t pid, vector & known_versions) +{ + return new NormalProcess(pid, known_versions); +} + NormalProcess::NormalProcess(uint32_t pid, vector & known_versions) : LinuxProcessBase(pid) { char dir_name [256]; diff --git a/library/DFProcess-windows.cpp b/library/DFProcess-windows.cpp index 4c4fae337..5a9967ffb 100644 --- a/library/DFProcess-windows.cpp +++ b/library/DFProcess-windows.cpp @@ -23,11 +23,17 @@ distribution. */ #include "Internal.h" #include "WindowsProcess.h" +#include "ProcessFactory.h" #include "dfhack/VersionInfo.h" #include "dfhack/DFError.h" #include using namespace DFHack; +Process* DFHack::createNormalProcess(uint32_t pid, vector & known_versions) +{ + return new NormalProcess(pid, known_versions); +} + class NormalProcess::Private { public: diff --git a/library/DFProcessEnumerator.cpp b/library/DFProcessEnumerator.cpp index 2dbfd806b..be33d41fd 100644 --- a/library/DFProcessEnumerator.cpp +++ b/library/DFProcessEnumerator.cpp @@ -23,9 +23,7 @@ distribution. */ #include "Internal.h" -#include "SHMProcess.h" -#include "LinuxProcess.h" -#include "WindowsProcess.h" +#include "ProcessFactory.h" #include "dfhack/VersionInfoFactory.h" #include "dfhack/DFProcessEnumerator.h" @@ -122,19 +120,19 @@ Process * BadProcesses::operator[](uint32_t index) Process *ProcessEnumerator::Private::GetProcessObject(ProcessID ID) { - Process *p1 = new SHMProcess(ID.pid,meminfo->versions); + Process *p1 = createSHMProcess(ID.pid,meminfo->versions); if(p1->isIdentified()) return p1; else delete p1; - Process *p2 = new NormalProcess(ID.pid,meminfo->versions); + Process *p2 = createNormalProcess(ID.pid,meminfo->versions); if(p2->isIdentified()) return p2; else delete p2; #ifdef LINUX_BUILD - Process *p3 = new WineProcess(ID.pid,meminfo->versions); + Process *p3 = createWineProcess(ID.pid,meminfo->versions); if(p3->isIdentified()) return p3; else diff --git a/library/private/LinuxProcess.h b/library/private/LinuxProcess.h index a9e3c094e..cc9df1a05 100644 --- a/library/private/LinuxProcess.h +++ b/library/private/LinuxProcess.h @@ -90,39 +90,6 @@ namespace DFHack bool SetAndWait (uint32_t state){return false;}; }; - class DFHACK_EXPORT NormalProcess : public LinuxProcessBase - { - friend class ProcessEnumerator; - public: - NormalProcess(uint32_t pid, std::vector & 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: - bool validate(char * exe_file,uint32_t pid, char * memFile, vector & 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: - WineProcess(uint32_t pid, std::vector & 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: - bool validate(char * exe_file,uint32_t pid, char * memFile, vector & known_versions); - }; } #endif diff --git a/library/private/ProcessFactory.h b/library/private/ProcessFactory.h new file mode 100644 index 000000000..217dbe839 --- /dev/null +++ b/library/private/ProcessFactory.h @@ -0,0 +1,38 @@ +/* +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 PROCESS_FACTORY_H_INCLUDED +#define PROCESS_FACTORY_H_INCLUDED + +#include "dfhack/DFProcess.h" + +namespace DFHack +{ + Process* createNormalProcess(uint32_t pid, std::vector & known_versions); + Process* createSHMProcess(uint32_t pid, std::vector & known_versions); +#ifdef LINUX_BUILD + Process* createWineProcess(uint32_t pid, std::vector & known_versions); +#endif +} +#endif diff --git a/library/private/SHMProcess.h b/library/private/SHMProcess.h index a4fe6e55f..5fb704b07 100644 --- a/library/private/SHMProcess.h +++ b/library/private/SHMProcess.h @@ -31,9 +31,8 @@ namespace DFHack { class DFHACK_EXPORT SHMProcess : public Process { - friend class ProcessEnumerator; - class Private; private: + class Private; Private * const d; public: diff --git a/library/private/WindowsProcess.h b/library/private/WindowsProcess.h index 96ed504a1..97565f47c 100644 --- a/library/private/WindowsProcess.h +++ b/library/private/WindowsProcess.h @@ -30,9 +30,8 @@ distribution. namespace DFHack { - class DFHACK_EXPORT NormalProcess : public Process + class NormalProcess : public Process { - friend class ProcessEnumerator; class Private; private: Private * const d;