diff options
author | Miguel Freitas <miguelfreitas@users.sourceforge.net> | 2004-05-25 16:31:50 +0000 |
---|---|---|
committer | Miguel Freitas <miguelfreitas@users.sourceforge.net> | 2004-05-25 16:31:50 +0000 |
commit | 61ac53aea9a93f6fb9142db2750b38eb496071d1 (patch) | |
tree | 0387c034a17d216a0fce5c6fed48342a65f50c90 | |
parent | 7a20d5bd9e29787ab92148e75b0ca3877bc20e4d (diff) | |
download | xine-lib-61ac53aea9a93f6fb9142db2750b38eb496071d1.tar.gz xine-lib-61ac53aea9a93f6fb9142db2750b38eb496071d1.tar.bz2 |
the new, improved, the "definitive" ldt_keeper! ;-)
linux threads may share the same LDT, therefore we must check if
entry was already set before trying to create a new segment descriptor.
this patch must fix at least some segfaults when w32 decoders are disposed.
(did i said i hate this win32 crap?)
CVS patchset: 6590
CVS date: 2004/05/25 16:31:50
-rw-r--r-- | src/libw32dll/wine/ldt_keeper.c | 162 |
1 files changed, 120 insertions, 42 deletions
diff --git a/src/libw32dll/wine/ldt_keeper.c b/src/libw32dll/wine/ldt_keeper.c index 391c78327..19378b57e 100644 --- a/src/libw32dll/wine/ldt_keeper.c +++ b/src/libw32dll/wine/ldt_keeper.c @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: ldt_keeper.c,v 1.10 2004/04/22 20:28:05 miguelfreitas Exp $ + * $Id: ldt_keeper.c,v 1.11 2004/05/25 16:31:50 miguelfreitas Exp $ * * * contents: @@ -140,6 +140,9 @@ struct modify_ldt_ldt_s { static unsigned int teb_sel = LDT_SEL(TEB_SEL_IDX); +static ldt_fs_t global_ldt_fs; +static int global_usage_count = 0; + #ifdef __cplusplus extern "C" #endif @@ -160,7 +163,7 @@ void Check_FS_Segment(void) if( fs != teb_sel ) { printf("ldt_keeper: FS segment is not set or has being lost!\n"); - printf(" Please report this error to xine-devel@sourceforge.net\n"); + printf(" Please report this error to xine-devel@lists.sourceforge.net\n"); printf(" Aborting....\n"); abort(); } @@ -182,41 +185,11 @@ static void LDT_EntryToBytes( unsigned long *buffer, const struct modify_ldt_ldt } #endif -ldt_fs_t* Setup_LDT_Keeper(void) +static int _modify_ldt(struct modify_ldt_ldt_s array) { - struct modify_ldt_ldt_s array; int ret; - ldt_fs_t* ldt_fs = (ldt_fs_t*) malloc(sizeof(ldt_fs_t)); - - if (!ldt_fs) - return NULL; - - ldt_fs->fd = open("/dev/zero", O_RDWR); - if(ldt_fs->fd<0){ - perror( "Cannot open /dev/zero for READ+WRITE. Check permissions! error: "); - return NULL; - } - ldt_fs->fs_seg = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE, - ldt_fs->fd, 0); - if (ldt_fs->fs_seg == (void*)-1) - { - perror("ERROR: Couldn't allocate memory for fs segment"); - close(ldt_fs->fd); - free(ldt_fs); - return NULL; - } - *(void**)((char*)ldt_fs->fs_seg+0x18) = ldt_fs->fs_seg; - array.base_addr=(int)ldt_fs->fs_seg; - array.entry_number=TEB_SEL_IDX; - array.limit=array.base_addr+getpagesize()-1; - array.seg_32bit=1; - array.read_exec_only=0; - array.seg_not_present=0; - array.contents=MODIFY_LDT_CONTENTS_DATA; - array.limit_in_pages=0; #ifdef __linux__ - //ret=LDT_Modify(0x1, &array, sizeof(struct modify_ldt_ldt_s)); ret=modify_ldt(0x1, &array, sizeof(struct modify_ldt_ldt_s)); if(ret<0) { @@ -252,7 +225,7 @@ ldt_fs_t* Setup_LDT_Keeper(void) struct ssd ssd; ssd.sel = teb_sel; ssd.bo = array.base_addr; - ssd.ls = array.limit - array.base_addr; + ssd.ls = array.limit; ssd.acc1 = ((array.read_exec_only == 0) << 1) | (array.contents << 2) | 0xf0; /* P(resent) | DPL3 | S */ @@ -264,22 +237,127 @@ ldt_fs_t* Setup_LDT_Keeper(void) } #endif + return ret; +} + +ldt_fs_t* Setup_LDT_Keeper(void) +{ + struct modify_ldt_ldt_s array; + int ret; + int ldt_already_set = 0; + ldt_fs_t* ldt_fs = (ldt_fs_t*) malloc(sizeof(ldt_fs_t)); + + if (!ldt_fs) + return NULL; + +#ifdef __linux__ + /* + * LDT might be shared by different threads, so we must + * check it here to avoid filling the segment descriptor again. + */ + { + unsigned char *ldt = malloc((TEB_SEL_IDX+1)*8); + unsigned int limit; + + modify_ldt(0, ldt, (TEB_SEL_IDX+1)*8); +/* + printf("ldt_keeper: old LDT entry = [%x] [%x]\n", + *(unsigned int *) (&ldt[TEB_SEL_IDX*8]), + *(unsigned int *) (&ldt[TEB_SEL_IDX*8+4]) ); +*/ + limit = ((*(unsigned int *) (&ldt[TEB_SEL_IDX*8])) & 0xffff) | + ((*(unsigned int *) (&ldt[TEB_SEL_IDX*8+4])) & 0xf0000); + + if( limit ) { + if( limit == getpagesize()-1 ) { + ldt_already_set = 1; + } else { + printf("ldt_keeper: LDT entry seems to be used by someone else. [%x] [%x]\n", + *(unsigned int *) (&ldt[TEB_SEL_IDX*8]), + *(unsigned int *) (&ldt[TEB_SEL_IDX*8+4]) ); + printf(" Please report this error to xine-devel@lists.sourceforge.net\n"); + } + } + free(ldt); + } +#endif /*linux*/ + + if( !ldt_already_set ) + { +#ifdef LOG + printf("ldt_keeper: creating a new segment descriptor.\n"); +#endif + ldt_fs->fd = open("/dev/zero", O_RDWR); + if(ldt_fs->fd<0){ + perror( "Cannot open /dev/zero for READ+WRITE. Check permissions! error: "); + return NULL; + } + + ldt_fs->fs_seg = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE, + ldt_fs->fd, 0); + if (ldt_fs->fs_seg == (void*)-1) + { + perror("ERROR: Couldn't allocate memory for fs segment"); + close(ldt_fs->fd); + free(ldt_fs); + return NULL; + } + *(void**)((char*)ldt_fs->fs_seg+0x18) = ldt_fs->fs_seg; + array.base_addr=(int)ldt_fs->fs_seg; + array.entry_number=TEB_SEL_IDX; + array.limit=getpagesize()-1; + array.seg_32bit=1; + array.read_exec_only=0; + array.seg_not_present=0; + array.contents=MODIFY_LDT_CONTENTS_DATA; + array.limit_in_pages=0; + + ret = _modify_ldt(array); + + ldt_fs->prev_struct = (char*)malloc(sizeof(char) * 8); + *(void**)array.base_addr = ldt_fs->prev_struct; + + memcpy( &global_ldt_fs, ldt_fs, sizeof(ldt_fs_t) ); + } else { +#ifdef LOG + printf("ldt_keeper: LDT entry already set, reusing.\n"); +#endif + global_usage_count++; + memcpy( ldt_fs, &global_ldt_fs, sizeof(ldt_fs_t) ); + } + Setup_FS_Segment(); - ldt_fs->prev_struct = (char*)malloc(sizeof(char) * 8); - *(void**)array.base_addr = ldt_fs->prev_struct; - return ldt_fs; } void Restore_LDT_Keeper(ldt_fs_t* ldt_fs) { + struct modify_ldt_ldt_s array; + if (ldt_fs == NULL || ldt_fs->fs_seg == 0) return; - if (ldt_fs->prev_struct) - free(ldt_fs->prev_struct); - munmap((char*)ldt_fs->fs_seg, getpagesize()); - ldt_fs->fs_seg = 0; - close(ldt_fs->fd); + + if( global_usage_count ) { +#ifdef LOG + printf("ldt_keeper: shared LDT, restore does nothing.\n"); +#endif + /* shared LDT. only the last user can free. */ + global_usage_count--; + } else { +#ifdef LOG + printf("ldt_keeper: freeing LDT entry.\n"); +#endif + if (ldt_fs->prev_struct) + free(ldt_fs->prev_struct); + munmap((char*)ldt_fs->fs_seg, getpagesize()); + ldt_fs->fs_seg = 0; + close(ldt_fs->fd); + + /* mark LDT entry as free again */ + memset(&array, 0, sizeof(struct modify_ldt_ldt_s)); + array.entry_number=TEB_SEL_IDX; + _modify_ldt(array); + } free(ldt_fs); } |