diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index bb7a9c199..d54fc79d5 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -29,6 +29,10 @@ include_directories (${CMAKE_SOURCE_DIR}/library/private/) SET(PROJECT_HDRS_INTERNAL private/ContextShared.h private/Internal.h + private/SHMProcess.h + private/LinuxProcess.h + private/WindowsProcess.h + private/ProcessFactory.h ) SET(PROJECT_HDRS @@ -89,6 +93,7 @@ DFContext.cpp DFTileTypes.cpp DFProcessEnumerator.cpp ContextShared.cpp +DFProcess-SHM.cpp depends/md5/md5.cpp depends/md5/md5wrapper.cpp @@ -138,6 +143,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-SHM.cpp b/library/DFProcess-SHM.cpp new file mode 100644 index 000000000..3246e567e --- /dev/null +++ b/library/DFProcess-SHM.cpp @@ -0,0 +1,485 @@ +/* +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 "ProcessFactory.h" +#include "dfhack/VersionInfo.h" +#include "dfhack/DFError.h" +#include "shms.h" +#include "mod-core.h" + +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)) +{ + 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 9fe3684a5..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,76 +39,37 @@ distribution. using namespace DFHack; -// a full memory barrier! better be safe than sorry. -#define gcc_barrier asm volatile("" ::: "memory"); __sync_synchronize(); - -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 waitWhile (uint32_t state); - bool SetAndWait (uint32_t state); - bool GetLocks(); - bool AreLocksOk(); - void FreeLocks(); -}; - -#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) { 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 +83,6 @@ bool SHMProcess::Private::SetAndWait (uint32_t state) cnt = 0; } } - if(useYield) - { - SCHED_YIELD - } cnt++; } // server returned a generic error @@ -155,21 +112,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,50 +200,21 @@ bool SHMProcess::Private::GetLocks() return false; } -SHMProcess::SHMProcess(uint32_t PID, vector< VersionInfo* >& known_versions) -: d(new Private(this)) +// test if we have client and server locks and the server is present +bool SHMProcess::Private::AreLocksOk() { - 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) + // both locks are inited (we hold our lock) + if(client_lock != -1 && server_lock != -1) { - detach(); - throw Error::SHMVersionMismatch(); + if(lockf(server_lock,F_TEST,0) == -1) // and server holds its lock + { + return true; // OK, locks are good + } } - - // try to identify the DF version (md5 the binary, compare with known versions) - d->validate(known_versions); - // detach - detach(); + // locks are bad + return false; } -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) { @@ -333,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; @@ -347,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 ) { @@ -408,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; + return (lockf(d->suspend_lock,F_LOCK,0) == 0); } -// FIXME: needs a good think-through -bool SHMProcess::asyncSuspend() +bool SHMProcess::releaseSuspendLock() { - 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; - } -} - -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; - // 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); } @@ -532,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; @@ -590,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; - gcc_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; - gcc_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; - gcc_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; - gcc_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; - gcc_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; - gcc_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; - gcc_barrier - d->SetAndWait(CORE_READ_DWORD); - val = reinterpret_cast (D_SHMHDR->value); -} - -/* - * WRITING - */ - -void SHMProcess::writeQuad (const uint32_t offset, const uint64_t data) -{ - if(!d->locked) throw Error::MemoryAccessDenied(); - - D_SHMHDR->address = offset; - D_SHMHDR->Qvalue = data; - gcc_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; - gcc_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; - gcc_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; - gcc_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); - gcc_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); - gcc_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(); @@ -825,55 +390,6 @@ string SHMProcess::readClassName (uint32_t vptr) return raw.substr(start,end-start); } -// 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; -} string SHMProcess::getPath() { char cwd_name[256]; @@ -886,3 +402,10 @@ string SHMProcess::getPath() target_name[target_result] = '\0'; return(string(target_name)); } + +char * SHMProcess::getSHMStart (void) +{ + if(!d->locked) return 0; //THROW HERE! + + return d->shm_addr; +} diff --git a/library/DFProcess-linux-base.cpp b/library/DFProcess-linux-base.cpp new file mode 100644 index 000000000..7549c1387 --- /dev/null +++ b/library/DFProcess-linux-base.cpp @@ -0,0 +1,432 @@ +/* +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::LinuxProcessBase(uint32_t pid) +: my_pid(pid) +{ + my_descriptor = NULL; + attached = false; + suspended = false; + memFileHandle = 0; +} + +bool LinuxProcessBase::isSuspended() +{ + return suspended; +} +bool LinuxProcessBase::isAttached() +{ + return attached; +} + +bool LinuxProcessBase::isIdentified() +{ + return identified; +} + +LinuxProcessBase::~LinuxProcessBase() +{ + if(attached) + { + detach(); + } + // destroy our copy of the memory descriptor + if(my_descriptor) + delete my_descriptor; +} + +VersionInfo * LinuxProcessBase::getDescriptor() +{ + return my_descriptor; +} + +int LinuxProcessBase::getPID() +{ + return 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", 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(!attached) + return false; + if(suspended) + return true; + if (kill(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(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; + } + } + suspended = true; + return true; +} + +bool LinuxProcessBase::forceresume() +{ + return resume(); +} + +bool LinuxProcessBase::resume() +{ + if(!attached) + return false; + if(!suspended) + return true; + if (ptrace(PTRACE_CONT, my_pid, NULL, NULL) == -1) + { + // no, we got an error + perror("ptrace resume error"); + return false; + } + suspended = false; + return true; +} + + +bool LinuxProcessBase::attach() +{ + int status; + if(attached) + { + if(!suspended) + return suspend(); + return true; + } + // can we attach? + if (ptrace(PTRACE_ATTACH , my_pid, NULL, NULL) == -1) + { + // no, we got an error + perror("ptrace attach error"); + cerr << "attach failed on pid " << my_pid << endl; + return false; + } + while(true) + { + // we wait on the pid + pid_t w = waitpid(my_pid, &status, 0); + if (w == -1) + { + // child died + perror("wait inside attach()"); + return false; + } + // stopped -> let's continue + if (WIFSTOPPED(status)) + { + break; + } + } + suspended = true; + + int proc_pid_mem = open(memFile.c_str(),O_RDONLY); + if(proc_pid_mem == -1) + { + 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 + { + attached = true; + + memFileHandle = proc_pid_mem; + return true; // we are attached + } +} + +bool LinuxProcessBase::detach() +{ + if(!attached) return true; + if(!suspended) suspend(); + int result = 0; + // close /proc/PID/mem + result = close(memFileHandle); + if(result == -1) + { + cerr << "couldn't close /proc/"<< my_pid <<"/mem" << endl; + perror("mem file close"); + return false; + } + else + { + // detach + result = ptrace(PTRACE_DETACH, my_pid, NULL, NULL); + if(result == -1) + { + cerr << "couldn't detach from process pid" << my_pid << endl; + perror("ptrace detach"); + return false; + } + else + { + 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(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,my_pid, offset, data); + #else + ptrace(PTRACE_POKEDATA,my_pid, offset, (uint32_t) data); + ptrace(PTRACE_POKEDATA,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,my_pid, offset, orig); + #else + ptrace(PTRACE_POKEDATA,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,my_pid, offset, orig); + #else + uint32_t orig = readDWord(offset); + orig &= 0xFFFF0000; + orig |= data; + ptrace(PTRACE_POKEDATA,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,my_pid, offset, orig); + #else + uint32_t orig = readDWord(offset); + orig &= 0xFFFFFF00; + orig |= data; + ptrace(PTRACE_POKEDATA,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..a75b8077d 100644 --- a/library/DFProcess-linux-wine.cpp +++ b/library/DFProcess-linux-wine.cpp @@ -22,7 +22,8 @@ must not be misrepresented as being the original software. distribution. */ #include "Internal.h" -#include "ProcessInternal.h" +#include "LinuxProcess.h" +#include "ProcessFactory.h" #include "dfhack/VersionInfo.h" #include "dfhack/DFError.h" #include @@ -30,37 +31,32 @@ 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_; +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); }; - ~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); -}; +} + +Process* DFHack::createWineProcess(uint32_t pid, vector & known_versions) +{ + return new WineProcess(pid, 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]; @@ -70,8 +66,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); @@ -107,27 +103,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); + 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 +120,41 @@ 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()) + { + // keep track of created memory_info object so we can destroy it later + my_descriptor = new VersionInfo(**it); + my_descriptor->setParentProcess(this); + // tell Process about the /proc/PID/mem file + memFile = memFile; + identified = true; + + 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"); + 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 +175,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 +206,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 867492181..dda919663 100644 --- a/library/DFProcess-linux.cpp +++ b/library/DFProcess-linux.cpp @@ -22,42 +22,36 @@ must not be misrepresented as being the original software. distribution. */ #include "Internal.h" -#include "ProcessInternal.h" +#include "LinuxProcess.h" +#include "ProcessFactory.h" #include "dfhack/VersionInfo.h" #include "dfhack/DFError.h" #include #include using namespace DFHack; -class NormalProcess::Private -{ - public: - Private(Process * self_) +namespace { + class NormalProcess : public LinuxProcessBase { - my_descriptor = NULL; - my_handle = NULL; - my_pid = 0; - attached = false; - suspended = false; - memFileHandle = 0; - self = self_; + 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); }; - ~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); -}; +} + +Process* DFHack::createNormalProcess(uint32_t pid, vector & known_versions) +{ + return new NormalProcess(pid, 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]; @@ -67,8 +61,8 @@ NormalProcess::NormalProcess(uint32_t pid, vector< VersionInfo* >& known_version 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); @@ -89,26 +83,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 ); + 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 +100,18 @@ 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; + // keep track of created memory_info object so we can destroy it later + my_descriptor = new VersionInfo(**it); + my_descriptor->setParentProcess(this); + // tell Process about the /proc/PID/mem file + memFile = memFile; + 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,369 +122,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 = Process::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 = Process::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 { uint32_t _M_length; @@ -555,15 +164,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-SHM.cpp b/library/DFProcess-windows-SHM.cpp index 4d23d8837..7966cf58c 100644 --- a/library/DFProcess-windows-SHM.cpp +++ b/library/DFProcess-windows-SHM.cpp @@ -22,68 +22,25 @@ 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; -// a full memory barrier! better be safe than sorry. -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)) - -bool SHMProcess::SetAndWait (uint32_t state) -{ - return d->SetAndWait(state); + 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) @@ -116,6 +73,7 @@ bool SHMProcess::Private::SetAndWait (uint32_t state) } cnt++; } + // server returned a generic error if(SHMCMD == CORE_ERROR) { return false; @@ -123,6 +81,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,67 +216,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()) -{ - 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 @@ -371,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 ) { @@ -445,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; + return ( WaitForSingleObject(d->DFCLSuspendMutex,INFINITE) == 0 ); } -// FIXME: needs a good think-through -bool SHMProcess::asyncSuspend() +bool SHMProcess::releaseSuspendLock() { - 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; - } -} - -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( 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); } @@ -577,21 +349,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 +391,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 @@ -651,230 +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::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); -} - -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; -} - -/* - * 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); @@ -895,51 +426,10 @@ 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) -{ - 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/DFProcess-windows.cpp b/library/DFProcess-windows.cpp index 0d7d32f8c..5a9967ffb 100644 --- a/library/DFProcess-windows.cpp +++ b/library/DFProcess-windows.cpp @@ -22,12 +22,18 @@ must not be misrepresented as being the original software. distribution. */ #include "Internal.h" -#include "ProcessInternal.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 fdd96ec27..be33d41fd 100644 --- a/library/DFProcessEnumerator.cpp +++ b/library/DFProcessEnumerator.cpp @@ -23,7 +23,7 @@ distribution. */ #include "Internal.h" -#include "ProcessInternal.h" +#include "ProcessFactory.h" #include "dfhack/VersionInfoFactory.h" #include "dfhack/DFProcessEnumerator.h" @@ -120,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 new file mode 100644 index 000000000..cc9df1a05 --- /dev/null +++ b/library/private/LinuxProcess.h @@ -0,0 +1,96 @@ +/* +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 LINUX_PROCESS_H_INCLUDED +#define LINUX_PROCESS_H_INCLUDED + +#ifdef LINUX_BUILD + +#include "dfhack/DFProcess.h" + +namespace DFHack +{ + class LinuxProcessBase : public Process + { + protected: + VersionInfo * my_descriptor; + pid_t my_pid; + string memFile; + int memFileHandle; + bool attached; + bool suspended; + bool identified; + public: + LinuxProcessBase(uint32_t pid); + ~LinuxProcessBase(); + + 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 readCString (uint32_t offset); + + bool isSuspended(); + bool isAttached(); + bool isIdentified(); + + 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 + char * getSHMStart (void){return 0;}; + // set a SHM command and wait for a response + bool SetAndWait (uint32_t state){return false;}; + }; + +} + +#endif +#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/ProcessInternal.h b/library/private/ProcessInternal.h deleted file mode 100644 index 0bca44d54..000000000 --- a/library/private/ProcessInternal.h +++ /dev/null @@ -1,216 +0,0 @@ -/* -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_INTERNAL_H_INCLUDED -#define PROCESS_INTERNAL_H_INCLUDED - -#include "dfhack/DFProcess.h" - -namespace DFHack -{ - //////////////////////////////////////////////////////////////////////////// - // Compiler appeasement area. Not worth a look really... // - //////////////////////////////////////////////////////////////////////////// - - 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;}; - }; - - 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 - { - 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); - - 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(); - // 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(); - }; -#endif -} -#endif diff --git a/library/private/SHMProcess.h b/library/private/SHMProcess.h new file mode 100644 index 000000000..5fb704b07 --- /dev/null +++ b/library/private/SHMProcess.h @@ -0,0 +1,143 @@ +/* +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 + { + private: + class 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 diff --git a/library/private/WindowsProcess.h b/library/private/WindowsProcess.h new file mode 100644 index 000000000..97565f47c --- /dev/null +++ b/library/private/WindowsProcess.h @@ -0,0 +1,93 @@ +/* +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 NormalProcess : public Process + { + 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