From aef02eddce5753fb3c0904df8aefecec2ba01949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sun, 27 Dec 2009 03:51:54 +0000 Subject: [PATCH] use of memory barriers in the shm server and client code --- library/DFHackAPI.cpp | 4 + library/DFProcess-linux-SHM.cpp | 94 +++++++++++++++++-- library/DFProcessEnumerator-linux.cpp | 11 ++- library/DFProcessEnumerator-windows.cpp | 11 ++- library/DFProcessEnumerator.h | 1 + library/shmserver/df-hacked | 14 +++ library/shmserver/dfconnect.c | 116 +++++++++++++++++++++--- library/shmserver/dfconnect.h | 8 ++ library/shmserver/dfconnect.so | Bin 0 -> 12100 bytes library/shmserver/readme.txt | 3 + 10 files changed, 232 insertions(+), 30 deletions(-) create mode 100755 library/shmserver/df-hacked create mode 100755 library/shmserver/dfconnect.so diff --git a/library/DFHackAPI.cpp b/library/DFHackAPI.cpp index 264cf1a25..aa5537119 100644 --- a/library/DFHackAPI.cpp +++ b/library/DFHackAPI.cpp @@ -969,6 +969,10 @@ bool API::Attach() { d->pm = new ProcessEnumerator (d->xml); // FIXME: handle bad XML better } + else + { + d->pm->purge(); + } // find a process (ProcessManager can find multiple when used properly) if (!d->pm->findProcessess()) diff --git a/library/DFProcess-linux-SHM.cpp b/library/DFProcess-linux-SHM.cpp index 195801df5..01d8a3c59 100644 --- a/library/DFProcess-linux-SHM.cpp +++ b/library/DFProcess-linux-SHM.cpp @@ -28,8 +28,50 @@ distribution. #include #include #include "shmserver/dfconnect.h" +#include +#include +#include +#include using namespace DFHack; +// a full memory barrier! better be safe than sorry. +#define gcc_barrier asm volatile("" ::: "memory"); __sync_synchronize(); + +/* +* wait for futex +* futex has to be aligned to 4 bytes +* futex has to be equal to val (returns EWOULDBLOCK otherwise) +* wait can be broken by arriving signals (returns EINTR) +* returns 0 when broken by futex_wake +*/ +inline int futex_wait(int * futex, int val) +{ + return syscall(SYS_futex, futex, FUTEX_WAIT, val, 0, 0, 0); +} +/* +* wait for futex +* futex has to be aligned to 4 bytes +* futex has to be equal to val (returns EWOULDBLOCK otherwise) +* wait can be broken by arriving signals (returns EINTR) +* returns 0 when broken by futex_wake +* returns ETIMEDOUT on timeout +*/ +inline int futex_wait_timed(int * futex, int val, const struct timespec *timeout) +{ + return syscall(SYS_futex, futex, FUTEX_WAIT, val, timeout, 0, 0); +} +/* +* wake up futex. returns number of waked processes +*/ +inline int futex_wake(int * futex) +{ + return syscall(SYS_futex, futex, FUTEX_WAKE, 1, 0, 0, 0); +} +static timespec one_second = { 1,0 }; +static timespec five_second = { 5,0 }; + + + class SHMProcess::Private { public: @@ -38,6 +80,8 @@ class SHMProcess::Private my_datamodel = NULL; my_descriptor = NULL; my_pid = 0; + my_shm = 0; + my_shmid = -1; my_window = NULL; attached = false; suspended = false; @@ -54,6 +98,7 @@ class SHMProcess::Private bool attached; bool suspended; bool identified; + bool validate(char * exe_file, uint32_t pid, vector & known_versions); bool waitWhile (DF_PINGPONG state); bool DF_TestBridgeVersion(bool & ret); @@ -71,6 +116,7 @@ bool SHMProcess::Private::waitWhile (DF_PINGPONG state) shmctl(my_shmid, IPC_STAT, &descriptor); if(descriptor.shm_nattch == 1)// DF crashed? { + gcc_barrier ((shm_cmd *)my_shm)->pingpong = DFPP_RUNNING; attached = suspended = false; return false; @@ -96,8 +142,10 @@ bool SHMProcess::Private::waitWhile (DF_PINGPONG state) bool SHMProcess::Private::DF_TestBridgeVersion(bool & ret) { ((shm_cmd *)my_shm)->pingpong = DFPP_VERSION; + gcc_barrier if(!waitWhile(DFPP_VERSION)) return false; + gcc_barrier ((shm_cmd *)my_shm)->pingpong = DFPP_SUSPENDED; ret =( ((shm_retval *)my_shm)->value == PINGPONG_VERSION ); return true; @@ -106,8 +154,10 @@ bool SHMProcess::Private::DF_TestBridgeVersion(bool & ret) bool SHMProcess::Private::DF_GetPID(pid_t & ret) { ((shm_cmd *)my_shm)->pingpong = DFPP_PID; + gcc_barrier if(!waitWhile(DFPP_PID)) return false; + gcc_barrier ((shm_cmd *)my_shm)->pingpong = DFPP_SUSPENDED; ret = ((shm_retval *)my_shm)->value; return true; @@ -139,11 +189,12 @@ SHMProcess::SHMProcess(vector & known_versions) /* * Check if there are two processes connected to the segment */ - struct shmid_ds descriptor; + shmid_ds descriptor; shmctl(d->my_shmid, IPC_STAT, &descriptor); if(descriptor.shm_nattch != 2)// badness { - fprintf(stderr,"dfhack: no DF or different client already connected\n"); + fprintf(stderr,"dfhack: %d : invalid no. of processes connected\n", descriptor.shm_nattch); + fprintf(stderr,"detach: %d",shmdt(d->my_shm)); return; } @@ -182,8 +233,10 @@ SHMProcess::SHMProcess(vector & known_versions) d->validate(target_name, d->my_pid, known_versions); d->my_window = new DFWindow(this); } + gcc_barrier // at this point, DF is stopped and waiting for commands. make it run again ((shm_cmd *)d->my_shm)->pingpong = DFPP_RUNNING; + fprintf(stderr,"detach: %d",shmdt(d->my_shm)); } bool SHMProcess::isSuspended() @@ -239,6 +292,10 @@ SHMProcess::~SHMProcess() { delete d->my_window; } + if(d->my_shm) + { + fprintf(stderr,"detach: %d",shmdt(d->my_shm)); + } delete d; } @@ -341,15 +398,23 @@ bool SHMProcess::attach() cerr << "there's already a different process attached" << endl; return false; } - d->attached = true; - if(suspend()) + /* + * Attach the segment + */ + if ((d->my_shm = (char *) shmat(d->my_shmid, NULL, 0)) != (char *) -1) { - d->suspended = true; - g_pProcess = this; - return true; + d->attached = true; + if(suspend()) + { + d->suspended = true; + g_pProcess = this; + return true; + } + d->attached = false; + cerr << "unable to suspend" << endl; + return false; } - d->attached = false; - cerr << "unable to suspend" << endl; + cerr << "unable to attach" << endl; return false; } @@ -375,6 +440,7 @@ void SHMProcess::read (const uint32_t offset, const uint32_t size, uint8_t *targ assert (size < (SHM_SIZE - sizeof(shm_read))); ((shm_read *)d->my_shm)->address = offset; ((shm_read *)d->my_shm)->length = size; + gcc_barrier ((shm_read *)d->my_shm)->pingpong = DFPP_READ; d->waitWhile(DFPP_READ); memcpy (target, d->my_shm + sizeof(shm_ret_data),size); @@ -383,6 +449,7 @@ void SHMProcess::read (const uint32_t offset, const uint32_t size, uint8_t *targ uint8_t SHMProcess::readByte (const uint32_t offset) { ((shm_read_small *)d->my_shm)->address = offset; + gcc_barrier ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_BYTE; d->waitWhile(DFPP_READ_BYTE); return ((shm_retval *)d->my_shm)->value; @@ -391,6 +458,7 @@ uint8_t SHMProcess::readByte (const uint32_t offset) void SHMProcess::readByte (const uint32_t offset, uint8_t &val ) { ((shm_read_small *)d->my_shm)->address = offset; + gcc_barrier ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_BYTE; d->waitWhile(DFPP_READ_BYTE); val = ((shm_retval *)d->my_shm)->value; @@ -399,6 +467,7 @@ void SHMProcess::readByte (const uint32_t offset, uint8_t &val ) uint16_t SHMProcess::readWord (const uint32_t offset) { ((shm_read_small *)d->my_shm)->address = offset; + gcc_barrier ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_WORD; d->waitWhile(DFPP_READ_WORD); return ((shm_retval *)d->my_shm)->value; @@ -407,6 +476,7 @@ uint16_t SHMProcess::readWord (const uint32_t offset) void SHMProcess::readWord (const uint32_t offset, uint16_t &val) { ((shm_read_small *)d->my_shm)->address = offset; + gcc_barrier ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_WORD; d->waitWhile(DFPP_READ_WORD); val = ((shm_retval *)d->my_shm)->value; @@ -415,6 +485,7 @@ void SHMProcess::readWord (const uint32_t offset, uint16_t &val) uint32_t SHMProcess::readDWord (const uint32_t offset) { ((shm_read_small *)d->my_shm)->address = offset; + gcc_barrier ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_DWORD; d->waitWhile(DFPP_READ_DWORD); return ((shm_retval *)d->my_shm)->value; @@ -422,6 +493,7 @@ uint32_t SHMProcess::readDWord (const uint32_t offset) void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) { ((shm_read_small *)d->my_shm)->address = offset; + gcc_barrier ((shm_read_small *)d->my_shm)->pingpong = DFPP_READ_DWORD; d->waitWhile(DFPP_READ_DWORD); val = ((shm_retval *)d->my_shm)->value; @@ -435,6 +507,7 @@ void SHMProcess::writeDWord (uint32_t offset, uint32_t data) { ((shm_write_small *)d->my_shm)->address = offset; ((shm_write_small *)d->my_shm)->value = data; + gcc_barrier ((shm_write_small *)d->my_shm)->pingpong = DFPP_WRITE_DWORD; d->waitWhile(DFPP_WRITE_DWORD); } @@ -444,6 +517,7 @@ void SHMProcess::writeWord (uint32_t offset, uint16_t data) { ((shm_write_small *)d->my_shm)->address = offset; ((shm_write_small *)d->my_shm)->value = data; + gcc_barrier ((shm_write_small *)d->my_shm)->pingpong = DFPP_WRITE_WORD; d->waitWhile(DFPP_WRITE_WORD); } @@ -452,6 +526,7 @@ void SHMProcess::writeByte (uint32_t offset, uint8_t data) { ((shm_write_small *)d->my_shm)->address = offset; ((shm_write_small *)d->my_shm)->value = data; + gcc_barrier ((shm_write_small *)d->my_shm)->pingpong = DFPP_WRITE_BYTE; d->waitWhile(DFPP_WRITE_BYTE); } @@ -461,6 +536,7 @@ void SHMProcess::write (uint32_t offset, uint32_t size, const uint8_t *source) ((shm_write *)d->my_shm)->address = offset; ((shm_write *)d->my_shm)->length = size; memcpy(d->my_shm+sizeof(shm_write),source, size); + gcc_barrier ((shm_write *)d->my_shm)->pingpong = DFPP_WRITE; d->waitWhile(DFPP_WRITE); } diff --git a/library/DFProcessEnumerator-linux.cpp b/library/DFProcessEnumerator-linux.cpp index d477a1a7d..6755bf9e7 100644 --- a/library/DFProcessEnumerator-linux.cpp +++ b/library/DFProcessEnumerator-linux.cpp @@ -110,14 +110,19 @@ ProcessEnumerator::ProcessEnumerator( string path_to_xml ) d->meminfo = new MemInfoManager(path_to_xml); } - -ProcessEnumerator::~ProcessEnumerator() +void ProcessEnumerator::purge() { - // delete all processes for(uint32_t i = 0;i < d->processes.size();i++) { delete d->processes[i]; } + d->processes.clear(); +} + +ProcessEnumerator::~ProcessEnumerator() +{ + // delete all processes + purge(); delete d->meminfo; delete d; } diff --git a/library/DFProcessEnumerator-windows.cpp b/library/DFProcessEnumerator-windows.cpp index d6156f196..50532e42b 100644 --- a/library/DFProcessEnumerator-windows.cpp +++ b/library/DFProcessEnumerator-windows.cpp @@ -115,14 +115,19 @@ ProcessEnumerator::ProcessEnumerator( string path_to_xml ) d->meminfo = new MemInfoManager(path_to_xml); } - -ProcessEnumerator::~ProcessEnumerator() +ProcessEnumerator::purge() { - // delete all processes for(uint32_t i = 0;i < d->processes.size();i++) { delete d->processes[i]; } + d->processes.clear(); +} + +ProcessEnumerator::~ProcessEnumerator() +{ + // delete all processes + purge(); delete d->meminfo; delete d; } diff --git a/library/DFProcessEnumerator.h b/library/DFProcessEnumerator.h index f369330e7..601ac7f41 100644 --- a/library/DFProcessEnumerator.h +++ b/library/DFProcessEnumerator.h @@ -48,6 +48,7 @@ namespace DFHack bool findProcessess(); uint32_t size(); Process * operator[](uint32_t index); + void purge(void); }; } #endif // PROCESSMANAGER_H_INCLUDED diff --git a/library/shmserver/df-hacked b/library/shmserver/df-hacked new file mode 100755 index 000000000..bf56b33e2 --- /dev/null +++ b/library/shmserver/df-hacked @@ -0,0 +1,14 @@ +#!/bin/sh +DF_DIR=$(dirname "$0") +cd "${DF_DIR}" +export SDL_DISABLE_LOCK_KEYS=1 # Work around for bug in Debian/Ubuntu SDL patch. +#export SDL_VIDEO_CENTERED=1 # Centre the screen. Messes up resizing. +ldd dwarfort.exe | grep SDL_image | grep -qv "not found$" +if [ $? -eq 0 ]; then + mkdir unused_libs + mv libs/libSDL* unused_libs/ +fi +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"./libs" # Update library search path. +export LD_PRELOAD="./libs/dfconnect.so" # Hack DF! +./dwarfort.exe $* # Go, go, go! :) + diff --git a/library/shmserver/dfconnect.c b/library/shmserver/dfconnect.c index cfb93e0c7..655611250 100644 --- a/library/shmserver/dfconnect.c +++ b/library/shmserver/dfconnect.c @@ -25,8 +25,6 @@ distribution. /** * This is the source for the DF <-> dfhack shm bridge */ -#define _GNU_SOURCE - #include #include #include @@ -37,27 +35,77 @@ distribution. #include #include #include "dfconnect.h" +#include +#include +#include +#include + +// a full memory barrier! better be safe than sorry. +#define gcc_barrier asm volatile("" ::: "memory"); __sync_synchronize(); + +/* + * wait for futex + * futex has to be aligned to 4 bytes + * futex has to be equal to val (returns EWOULDBLOCK otherwise) + * wait can be broken by arriving signals (returns EINTR) + * returns 0 when broken by futex_wake + */ +inline int futex_wait(int * futex, int val) +{ + return syscall(SYS_futex, futex, FUTEX_WAIT, val, 0, 0, 0); +} +/* + * wait for futex + * futex has to be aligned to 4 bytes + * futex has to be equal to val (returns EWOULDBLOCK otherwise) + * wait can be broken by arriving signals (returns EINTR) + * returns 0 when broken by futex_wake + * returns ETIMEDOUT on timeout + */ +inline int futex_wait_timed(int * futex, int val, const struct timespec *timeout) +{ + return syscall(SYS_futex, futex, FUTEX_WAIT, val, timeout, 0, 0); +} +/* + * wake up futex. returns number of waked processes + */ +inline int futex_wake(int * futex) +{ + return syscall(SYS_futex, futex, FUTEX_WAKE, 1, 0, 0, 0); +} +static timespec one_second = { 1,0 }; +static timespec five_second = { 5,0 }; + // ptr to the real functions static void (*_SDL_GL_SwapBuffers)(void) = 0; static void (*_SDL_Quit)(void) = 0; static int (*_SDL_Init)(uint32_t flags) = 0; +static int (*_SDL_Flip)(void * some_ptr) = 0; // various crud int counter = 0; int errorstate = 0; char *shm; int shmid; -static unsigned char * BigFat; - void SHM_Init ( void ) { // name for the segment key_t key = 123466; - BigFat = (unsigned char *) malloc(SHM_SIZE); - // create the segment - if ((shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0600)) < 0) + // find previous segment, check if it's used by some processes. if it isn't, kill it with fire + if ((shmid = shmget(key, SHM_SIZE, 0600)) != -1) + { + shmid_ds descriptor; + shmctl(shmid, IPC_STAT, &descriptor); + if(descriptor.shm_nattch == 0) + { + shmctl(shmid,IPC_RMID,NULL); + fprintf(stderr,"dfhack: killed dangling resources from crashed DF.\n"); + } + } + // create the segment, make sure only ww are really creating it + if ((shmid = shmget(key, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0600)) < 0) { perror("shmget"); errorstate = 1; @@ -71,30 +119,40 @@ void SHM_Init ( void ) errorstate = 1; return; } + gcc_barrier ((shm_cmd *)shm)->pingpong = DFPP_RUNNING; // make sure we don't stall or do crazy stuff } void SHM_Destroy ( void ) { - // blah, I don't care + shmid_ds descriptor; + shmctl(shmid, IPC_STAT, &descriptor); + shmdt(shm); + while(descriptor.shm_nattch != 0) + { + shmctl(shmid, IPC_STAT, &descriptor); + } + shmctl(shmid,IPC_RMID,NULL); + fprintf(stderr,"dfhack: destroyed shared segment.\n"); } void SHM_Act (void) { - struct shmid_ds descriptor; - uint32_t numwaits = 0; if(errorstate) { return; } + shmid_ds descriptor; + uint32_t numwaits = 0; uint32_t length; uint32_t address; check_again: // goto target!!! if(numwaits == 10000) { - shmctl(shmid, IPC_STAT, &descriptor); + shmctl(shmid, IPC_STAT, &descriptor); if(descriptor.shm_nattch == 1)// other guy crashed { + gcc_barrier ((shm_cmd *)shm)->pingpong = DFPP_RUNNING; fprintf(stderr,"dfhack: Broke out of loop, other process disappeared.\n"); return; @@ -117,23 +175,26 @@ void SHM_Act (void) numwaits++; goto check_again; case DFPP_SUSPEND: + gcc_barrier ((shm_cmd *)shm)->pingpong = DFPP_SUSPENDED; goto check_again; - + /* case DFPP_BOUNCE: length = ((shm_bounce *)shm)->length; memcpy(BigFat,shm + sizeof(shm_bounce),length); memcpy(shm + sizeof(shm_ret_data),BigFat,length); ((shm_cmd *)shm)->pingpong = DFPP_RET_DATA; goto check_again; - + */ case DFPP_PID: ((shm_retval *)shm)->value = getpid(); + gcc_barrier ((shm_retval *)shm)->pingpong = DFPP_RET_PID; goto check_again; case DFPP_VERSION: ((shm_retval *)shm)->value = PINGPONG_VERSION; + gcc_barrier ((shm_retval *)shm)->pingpong = DFPP_RET_VERSION; goto check_again; @@ -141,24 +202,28 @@ void SHM_Act (void) length = ((shm_read *)shm)->length; address = ((shm_read *)shm)->address; memcpy(shm + sizeof(shm_ret_data), (void *) address,length); + gcc_barrier ((shm_cmd *)shm)->pingpong = DFPP_RET_DATA; goto check_again; case DFPP_READ_DWORD: address = ((shm_read_small *)shm)->address; ((shm_retval *)shm)->value = *((uint32_t*) address); + gcc_barrier ((shm_retval *)shm)->pingpong = DFPP_RET_DWORD; goto check_again; case DFPP_READ_WORD: address = ((shm_read_small *)shm)->address; ((shm_retval *)shm)->value = *((uint16_t*) address); + gcc_barrier ((shm_retval *)shm)->pingpong = DFPP_RET_WORD; goto check_again; case DFPP_READ_BYTE: address = ((shm_read_small *)shm)->address; ((shm_retval *)shm)->value = *((uint8_t*) address); + gcc_barrier ((shm_retval *)shm)->pingpong = DFPP_RET_BYTE; goto check_again; @@ -166,21 +231,25 @@ void SHM_Act (void) address = ((shm_write *)shm)->address; length = ((shm_write *)shm)->length; memcpy((void *)address, shm + sizeof(shm_write),length); + gcc_barrier ((shm_cmd *)shm)->pingpong = DFPP_SUSPENDED; goto check_again; case DFPP_WRITE_DWORD: (*(uint32_t*)((shm_write_small *)shm)->address) = ((shm_write_small *)shm)->value; + gcc_barrier ((shm_cmd *)shm)->pingpong = DFPP_SUSPENDED; goto check_again; case DFPP_WRITE_WORD: (*(uint16_t*)((shm_write_small *)shm)->address) = ((shm_write_small *)shm)->value; + gcc_barrier ((shm_cmd *)shm)->pingpong = DFPP_SUSPENDED; goto check_again; case DFPP_WRITE_BYTE: (*(uint8_t*)((shm_write_small *)shm)->address) = ((shm_write_small *)shm)->value; + gcc_barrier ((shm_cmd *)shm)->pingpong = DFPP_SUSPENDED; goto check_again; @@ -191,12 +260,13 @@ void SHM_Act (void) default: ((shm_retval *)shm)->value = DFEE_INVALID_COMMAND; + gcc_barrier ((shm_retval *)shm)->pingpong = DFPP_SV_ERROR; break; } } -// hook - called every tick +// hook - called every tick in OpenGL mode of DF extern "C" void SDL_GL_SwapBuffers(void) { if(_SDL_GL_SwapBuffers) @@ -210,6 +280,20 @@ extern "C" void SDL_GL_SwapBuffers(void) } } +// hook - called every tick in the 2D mode of DF +extern "C" int SDL_Flip(void * some_ptr) +{ + if(_SDL_Flip) + { + if(((shm_cmd *)shm)->pingpong != DFPP_RUNNING) + { + SHM_Act(); + } + counter ++; + return _SDL_Flip(some_ptr); + } +} + // hook - called at program exit extern "C" void SDL_Quit(void) { @@ -217,7 +301,8 @@ extern "C" void SDL_Quit(void) { _SDL_Quit(); } - fprintf(stderr,"dfhack: DF called SwapBuffers %d times, lol\n", counter); + fprintf(stderr,"dfhack: DF called SwapBuffers %d times\n", counter); + SHM_Destroy(); } // hook - called at program start, initialize some stuffs we'll use later @@ -226,6 +311,7 @@ extern "C" int SDL_Init(uint32_t flags) // find real functions _SDL_GL_SwapBuffers = (void (*)( void )) dlsym(RTLD_NEXT, "SDL_GL_SwapBuffers"); _SDL_Init = (int (*)( uint32_t )) dlsym(RTLD_NEXT, "SDL_Init"); + _SDL_Flip = (int (*)( void * )) dlsym(RTLD_NEXT, "SDL_Flip"); _SDL_Quit = (void (*)( void )) dlsym(RTLD_NEXT, "SDL_Quit"); // check if we got them diff --git a/library/shmserver/dfconnect.h b/library/shmserver/dfconnect.h index a75b3ea09..a86e7f4cf 100644 --- a/library/shmserver/dfconnect.h +++ b/library/shmserver/dfconnect.h @@ -12,6 +12,14 @@ * end - sent to DF for breaking out of the wait */ +enum DF_SHM_ERRORSTATE +{ + SHM_OK, // all OK + SHM_CANT_GET_SHM, // getting the SHM ID failed for some reason + SHM_CANT_ATTACH, // we can't attach the shm for some reason + SHM_SECOND_DF // we are a second DF process, can't use SHM at all +}; + enum DF_PINGPONG { DFPP_RUNNING = 0, // no command, normal server execution diff --git a/library/shmserver/dfconnect.so b/library/shmserver/dfconnect.so new file mode 100755 index 0000000000000000000000000000000000000000..17960daa83ea8f51cdd681ebf913f00b1dd88c87 GIT binary patch literal 12100 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 literal 0 HcmV?d00001 diff --git a/library/shmserver/readme.txt b/library/shmserver/readme.txt index 16a1c6c7d..54ab2d6d4 100644 --- a/library/shmserver/readme.txt +++ b/library/shmserver/readme.txt @@ -9,3 +9,6 @@ export LD_PRELOAD="./libs/dfconnect.so" # Hack DF! save and run the script! Has to be compiled for 32bit arch, otherwise the library isn't recognised. Client can be any arch. + +Precompiled dfconnect library is made available. dfconnect.so goes in DF/libs, df-hacked script goes in DF/ +Run ./df-hacked to use the shared memory API \ No newline at end of file