summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libw32dll/wine/ldt_keeper.c162
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);
}