Moved LDT selector allocation routines to libwine.

Added support for correct locking of all LDT operations.
Added separate functions to manipulate the %fs selector, which allows
using a global GDT selector on recent Linux kernels.
This commit is contained in:
Alexandre Julliard 2003-02-26 20:34:45 +00:00
parent 28d65b1ac9
commit ce13153821
11 changed files with 390 additions and 179 deletions

View File

@ -49,6 +49,21 @@ extern int __wine_set_signal_handler(unsigned, int (*)(unsigned));
extern int main_create_flags; extern int main_create_flags;
static CRITICAL_SECTION ldt_section = CRITICAL_SECTION_INIT("ldt_section");
/***********************************************************************
* locking for LDT routines
*/
static void ldt_lock(void)
{
EnterCriticalSection( &ldt_section );
}
static void ldt_unlock(void)
{
LeaveCriticalSection( &ldt_section );
}
/*********************************************************************** /***********************************************************************
* KERNEL process initialisation routine * KERNEL process initialisation routine
*/ */
@ -114,6 +129,9 @@ static BOOL process_attach(void)
/* Create the shared heap for broken win95 native dlls */ /* Create the shared heap for broken win95 native dlls */
HeapCreate( HEAP_SHARED, 0, 0 ); HeapCreate( HEAP_SHARED, 0, 0 );
/* initialize LDT locking */
wine_ldt_init_locking( ldt_lock, ldt_unlock );
/* finish the process initialisation for console bits, if needed */ /* finish the process initialisation for console bits, if needed */
__wine_set_signal_handler(SIGINT, CONSOLE_HandleCtrlC); __wine_set_signal_handler(SIGINT, CONSOLE_HandleCtrlC);

View File

@ -876,15 +876,17 @@ void WINAPI DOSVM_Int31Handler( CONTEXT86 *context )
case 0x0006: /* Get selector base address */ case 0x0006: /* Get selector base address */
TRACE( "get selector base address (0x%04x)\n", BX_reg(context) ); TRACE( "get selector base address (0x%04x)\n", BX_reg(context) );
{ {
LDT_ENTRY entry;
WORD sel = BX_reg(context); WORD sel = BX_reg(context);
if (IS_SELECTOR_SYSTEM(sel) || IS_SELECTOR_FREE(sel)) wine_ldt_get_entry( sel, &entry );
if (wine_ldt_is_empty(&entry))
{ {
context->Eax = 0x8022; /* invalid selector */ context->Eax = 0x8022; /* invalid selector */
SET_CFLAG(context); SET_CFLAG(context);
} }
else else
{ {
DWORD base = GetSelectorBase( sel ); void *base = wine_ldt_get_base(&entry);
SET_CX( context, HIWORD(base) ); SET_CX( context, HIWORD(base) );
SET_DX( context, LOWORD(base) ); SET_DX( context, LOWORD(base) );
} }

View File

@ -145,11 +145,7 @@ extern char IO_pp_init(void);
* selector which is neither system selector nor zero. * selector which is neither system selector nor zero.
*/ */
#define CTX_SEG_OFF_TO_LIN(context,seg,off) \ #define CTX_SEG_OFF_TO_LIN(context,seg,off) \
(ISV86(context) ? PTR_REAL_TO_LIN((seg),(off)) : \ (ISV86(context) ? PTR_REAL_TO_LIN((seg),(off)) : wine_ldt_get_ptr((seg),(off)))
(!seg || IS_SELECTOR_SYSTEM(seg))? (void *)(ULONG_PTR)(off) : \
(IS_SELECTOR_32BIT(seg) ? \
(void *)((off) + (char*)MapSL(MAKESEGPTR((seg),0))) : \
MapSL(MAKESEGPTR((seg),(off)))))
#define INT_BARF(context,num) \ #define INT_BARF(context,num) \
ERR( "int%x: unknown/not implemented parameters:\n" \ ERR( "int%x: unknown/not implemented parameters:\n" \

View File

@ -28,13 +28,9 @@ extern WORD SELECTOR_AllocBlock( const void *base, DWORD size, unsigned char fla
extern WORD SELECTOR_ReallocBlock( WORD sel, const void *base, DWORD size ); extern WORD SELECTOR_ReallocBlock( WORD sel, const void *base, DWORD size );
extern void SELECTOR_FreeBlock( WORD sel ); extern void SELECTOR_FreeBlock( WORD sel );
#define FIRST_LDT_ENTRY_TO_ALLOC 17
#define IS_SELECTOR_FREE(sel) (!(wine_ldt_copy.flags[LOWORD(sel) >> 3] & WINE_LDT_FLAGS_ALLOCATED))
/* Determine if sel is a system selector (i.e. not managed by Wine) */ /* Determine if sel is a system selector (i.e. not managed by Wine) */
#define IS_SELECTOR_SYSTEM(sel) \ #define IS_SELECTOR_SYSTEM(sel) \
(!((sel) & 4) || ((LOWORD(sel) >> 3) < FIRST_LDT_ENTRY_TO_ALLOC)) (!((sel) & 4) || ((LOWORD(sel) >> 3) < WINE_LDT_FIRST_ENTRY))
#define IS_SELECTOR_32BIT(sel) \ #define IS_SELECTOR_32BIT(sel) \
(IS_SELECTOR_SYSTEM(sel) || (wine_ldt_copy.flags[LOWORD(sel) >> 3] & WINE_LDT_FLAGS_32BIT)) (IS_SELECTOR_SYSTEM(sel) || (wine_ldt_copy.flags[LOWORD(sel) >> 3] & WINE_LDT_FLAGS_32BIT))

View File

@ -68,8 +68,23 @@ extern int* (*wine_h_errno_location)(void);
/* LDT management */ /* LDT management */
extern void wine_ldt_init_locking( void (*lock_func)(void), void (*unlock_func)(void) );
extern void wine_ldt_get_entry( unsigned short sel, LDT_ENTRY *entry ); extern void wine_ldt_get_entry( unsigned short sel, LDT_ENTRY *entry );
extern int wine_ldt_set_entry( unsigned short sel, const LDT_ENTRY *entry ); extern int wine_ldt_set_entry( unsigned short sel, const LDT_ENTRY *entry );
extern void *wine_ldt_get_ptr( unsigned short sel, unsigned int offset );
extern unsigned short wine_ldt_alloc_entries( int count );
extern unsigned short wine_ldt_realloc_entries( unsigned short sel, int oldcount, int newcount );
extern void wine_ldt_free_entries( unsigned short sel, int count );
#ifdef __i386__
extern unsigned short wine_ldt_alloc_fs(void);
extern void wine_ldt_init_fs( unsigned short sel, const LDT_ENTRY *entry );
extern void wine_ldt_free_fs( unsigned short sel );
#else /* __i386__ */
static inline unsigned short wine_ldt_alloc_fs(void) { return 0x0b; /* pseudo GDT selector */ }
static inline void wine_ldt_init_fs( unsigned short sel, const LDT_ENTRY *entry ) { }
static inline void wine_ldt_free_fs( unsigned short sel ) { }
#endif /* __i386__ */
/* the local copy of the LDT */ /* the local copy of the LDT */
#ifdef __CYGWIN__ #ifdef __CYGWIN__
@ -96,6 +111,8 @@ WINE_LDT_EXTERN struct __wine_ldt_copy
#define WINE_LDT_FLAGS_32BIT 0x40 /* Segment is 32-bit (code or stack) */ #define WINE_LDT_FLAGS_32BIT 0x40 /* Segment is 32-bit (code or stack) */
#define WINE_LDT_FLAGS_ALLOCATED 0x80 /* Segment is allocated (no longer free) */ #define WINE_LDT_FLAGS_ALLOCATED 0x80 /* Segment is allocated (no longer free) */
#define WINE_LDT_FIRST_ENTRY 17
/* helper functions to manipulate the LDT_ENTRY structure */ /* helper functions to manipulate the LDT_ENTRY structure */
inline static void wine_ldt_set_base( LDT_ENTRY *ent, const void *base ) inline static void wine_ldt_set_base( LDT_ENTRY *ent, const void *base )
{ {
@ -136,6 +153,11 @@ inline static unsigned char wine_ldt_get_flags( const LDT_ENTRY *ent )
if (ent->HighWord.Bits.Default_Big) ret |= WINE_LDT_FLAGS_32BIT; if (ent->HighWord.Bits.Default_Big) ret |= WINE_LDT_FLAGS_32BIT;
return ret; return ret;
} }
inline static int wine_ldt_is_empty( const LDT_ENTRY *ent )
{
const DWORD *dw = (const DWORD *)ent;
return (dw[0] | dw[1]) == 0;
}
/* segment register access */ /* segment register access */

View File

@ -22,6 +22,7 @@
#include "config.h" #include "config.h"
#include "wine/port.h" #include "wine/port.h"
#include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -52,11 +53,25 @@ struct modify_ldt_s
unsigned int useable:1; unsigned int useable:1;
}; };
static inline int modify_ldt( int func, struct modify_ldt_s *ptr, #ifndef SYS_set_thread_area
unsigned long count ) #define SYS_set_thread_area 243
#endif
static inline void fill_modify_ldt_struct( struct modify_ldt_s *ptr, const LDT_ENTRY *entry )
{
ptr->base_addr = (unsigned long)wine_ldt_get_base(entry);
ptr->limit = entry->LimitLow | (entry->HighWord.Bits.LimitHi << 16);
ptr->seg_32bit = entry->HighWord.Bits.Default_Big;
ptr->contents = (entry->HighWord.Bits.Type >> 2) & 3;
ptr->read_exec_only = !(entry->HighWord.Bits.Type & 2);
ptr->limit_in_pages = entry->HighWord.Bits.Granularity;
ptr->seg_not_present = !entry->HighWord.Bits.Pres;
ptr->useable = entry->HighWord.Bits.Sys;
}
static inline int modify_ldt( int func, struct modify_ldt_s *ptr, unsigned long count )
{ {
int res; int res;
#ifdef __PIC__
__asm__ __volatile__( "pushl %%ebx\n\t" __asm__ __volatile__( "pushl %%ebx\n\t"
"movl %2,%%ebx\n\t" "movl %2,%%ebx\n\t"
"int $0x80\n\t" "int $0x80\n\t"
@ -66,14 +81,20 @@ static inline int modify_ldt( int func, struct modify_ldt_s *ptr,
"r" (func), "r" (func),
"c" (ptr), "c" (ptr),
"d" (count) ); "d" (count) );
#else if (res >= 0) return res;
__asm__ __volatile__("int $0x80" errno = -res;
: "=a" (res) return -1;
: "0" (SYS_modify_ldt), }
"b" (func),
"c" (ptr), static inline int set_thread_area( struct modify_ldt_s *ptr )
"d" (count) ); {
#endif /* __PIC__ */ int res;
__asm__ __volatile__( "pushl %%ebx\n\t"
"movl %2,%%ebx\n\t"
"int $0x80\n\t"
"popl %%ebx"
: "=a" (res)
: "0" (SYS_set_thread_area), "r" (ptr) );
if (res >= 0) return res; if (res >= 0) return res;
errno = -res; errno = -res;
return -1; return -1;
@ -101,32 +122,67 @@ extern int i386_set_ldt(int, union descriptor *, int);
/* local copy of the LDT */ /* local copy of the LDT */
struct __wine_ldt_copy wine_ldt_copy; struct __wine_ldt_copy wine_ldt_copy;
static const LDT_ENTRY null_entry; /* all-zeros, used to clear LDT entries */
#define LDT_SIZE 8192
/* empty function for default locks */
static void nop(void) { }
static void (*lock_ldt)(void) = nop;
static void (*unlock_ldt)(void) = nop;
static inline int is_gdt_sel( unsigned short sel ) { return !(sel & 4); }
/*********************************************************************** /***********************************************************************
* ldt_get_entry * wine_ldt_init_locking
* *
* Retrieve an LDT entry. * Set the LDT locking/unlocking functions.
*/ */
void wine_ldt_get_entry( unsigned short sel, LDT_ENTRY *entry ) void wine_ldt_init_locking( void (*lock_func)(void), void (*unlock_func)(void) )
{ {
int index = sel >> 3; lock_ldt = lock_func;
wine_ldt_set_base( entry, wine_ldt_copy.base[index] ); unlock_ldt = unlock_func;
wine_ldt_set_limit( entry, wine_ldt_copy.limit[index] );
wine_ldt_set_flags( entry, wine_ldt_copy.flags[index] );
} }
/*********************************************************************** /***********************************************************************
* ldt_set_entry * wine_ldt_get_entry
* *
* Set an LDT entry. * Retrieve an LDT entry. Return a null entry if selector is not allocated.
*/ */
int wine_ldt_set_entry( unsigned short sel, const LDT_ENTRY *entry ) void wine_ldt_get_entry( unsigned short sel, LDT_ENTRY *entry )
{
int index = sel >> 3;
if (is_gdt_sel(sel))
{
*entry = null_entry;
return;
}
lock_ldt();
if (wine_ldt_copy.flags[index] & WINE_LDT_FLAGS_ALLOCATED)
{
wine_ldt_set_base( entry, wine_ldt_copy.base[index] );
wine_ldt_set_limit( entry, wine_ldt_copy.limit[index] );
wine_ldt_set_flags( entry, wine_ldt_copy.flags[index] );
}
else *entry = null_entry;
unlock_ldt();
}
/***********************************************************************
* internal_set_entry
*
* Set an LDT entry, without locking. For internal use only.
*/
static int internal_set_entry( unsigned short sel, const LDT_ENTRY *entry )
{ {
int ret = 0, index = sel >> 3; int ret = 0, index = sel >> 3;
/* Entry 0 must not be modified; its base and limit are always 0 */ if (index < WINE_LDT_FIRST_ENTRY) return 0; /* cannot modify reserved entries */
if (!index) return 0;
#ifdef __i386__ #ifdef __i386__
@ -134,16 +190,8 @@ int wine_ldt_set_entry( unsigned short sel, const LDT_ENTRY *entry )
{ {
struct modify_ldt_s ldt_info; struct modify_ldt_s ldt_info;
ldt_info.entry_number = index; ldt_info.entry_number = index;
ldt_info.base_addr = (unsigned long)wine_ldt_get_base(entry); fill_modify_ldt_struct( &ldt_info, entry );
ldt_info.limit = entry->LimitLow | (entry->HighWord.Bits.LimitHi << 16);
ldt_info.seg_32bit = entry->HighWord.Bits.Default_Big;
ldt_info.contents = (entry->HighWord.Bits.Type >> 2) & 3;
ldt_info.read_exec_only = !(entry->HighWord.Bits.Type & 2);
ldt_info.limit_in_pages = entry->HighWord.Bits.Granularity;
ldt_info.seg_not_present = !entry->HighWord.Bits.Pres;
ldt_info.useable = entry->HighWord.Bits.Sys;
if ((ret = modify_ldt(0x11, &ldt_info, sizeof(ldt_info))) < 0) if ((ret = modify_ldt(0x11, &ldt_info, sizeof(ldt_info))) < 0)
perror( "modify_ldt" ); perror( "modify_ldt" );
} }
@ -192,10 +240,216 @@ int wine_ldt_set_entry( unsigned short sel, const LDT_ENTRY *entry )
} }
/***********************************************************************
* wine_ldt_set_entry
*
* Set an LDT entry.
*/
int wine_ldt_set_entry( unsigned short sel, const LDT_ENTRY *entry )
{
int ret;
lock_ldt();
ret = internal_set_entry( sel, entry );
unlock_ldt();
return ret;
}
/***********************************************************************
* wine_ldt_get_ptr
*
* Convert a segment:offset pair to a linear pointer.
* Note: we don't lock the LDT since this has to be fast.
*/
void *wine_ldt_get_ptr( unsigned short sel, unsigned int offset )
{
int index;
if (is_gdt_sel(sel)) /* GDT selector */
return (void *)offset;
if ((index = (sel >> 3)) < WINE_LDT_FIRST_ENTRY) /* system selector */
return (void *)offset;
if (!(wine_ldt_copy.flags[index] & WINE_LDT_FLAGS_32BIT)) offset &= 0xffff;
return (char *)wine_ldt_copy.base[index] + offset;
}
/***********************************************************************
* wine_ldt_alloc_entries
*
* Allocate a number of consecutive ldt entries, without setting the LDT contents.
* Return a selector for the first entry.
*/
unsigned short wine_ldt_alloc_entries( int count )
{
int i, index, size = 0;
if (count <= 0) return 0;
lock_ldt();
for (i = WINE_LDT_FIRST_ENTRY; i < LDT_SIZE; i++)
{
if (wine_ldt_copy.flags[i] & WINE_LDT_FLAGS_ALLOCATED) size = 0;
else if (++size >= count) /* found a large enough block */
{
index = i - size + 1;
/* mark selectors as allocated */
for (i = 0; i < count; i++) wine_ldt_copy.flags[index + i] |= WINE_LDT_FLAGS_ALLOCATED;
unlock_ldt();
return (index << 3) | 7;
}
}
unlock_ldt();
return 0;
}
/***********************************************************************
* wine_ldt_realloc_entries
*
* Reallocate a number of consecutive ldt entries, without changing the LDT contents.
* Return a selector for the first entry.
*/
unsigned short wine_ldt_realloc_entries( unsigned short sel, int oldcount, int newcount )
{
int i;
if (oldcount < newcount) /* we need to add selectors */
{
int index = sel >> 3;
lock_ldt();
/* check if the next selectors are free */
if (index + newcount > LDT_SIZE) i = oldcount;
else
for (i = oldcount; i < newcount; i++)
if (wine_ldt_copy.flags[index+i] & WINE_LDT_FLAGS_ALLOCATED) break;
if (i < newcount) /* they are not free */
{
wine_ldt_free_entries( sel, oldcount );
sel = wine_ldt_alloc_entries( newcount );
}
else /* mark the selectors as allocated */
{
for (i = oldcount; i < newcount; i++)
wine_ldt_copy.flags[index+i] |= WINE_LDT_FLAGS_ALLOCATED;
}
unlock_ldt();
}
else if (oldcount > newcount) /* we need to remove selectors */
{
wine_ldt_free_entries( sel + (newcount << 3), newcount - oldcount );
}
return sel;
}
/***********************************************************************
* wine_ldt_free_entries
*
* Free a number of consecutive ldt entries and clear their contents.
*/
void wine_ldt_free_entries( unsigned short sel, int count )
{
int index;
lock_ldt();
for (index = sel >> 3; count > 0; count--, index++)
{
internal_set_entry( sel, &null_entry );
wine_ldt_copy.flags[index] = 0;
}
unlock_ldt();
}
#ifdef __i386__
static int fs_gdt_index = -1; /* GDT index for %fs, or 0 if GDT not supported on this kernel */
/***********************************************************************
* wine_ldt_alloc_fs
*
* Allocate an LDT entry for a %fs selector, reusing a global
* GDT selector if possible. Return the selector value.
*/
unsigned short wine_ldt_alloc_fs(void)
{
if (fs_gdt_index == -1)
{
#ifdef __linux__
struct modify_ldt_s ldt_info;
int ret;
ldt_info.entry_number = -1;
fill_modify_ldt_struct( &ldt_info, &null_entry );
if ((ret = set_thread_area( &ldt_info ) < 0))
{
fs_gdt_index = 0; /* don't try it again */
if (errno != ENOSYS) perror( "set_thread_area" );
}
else fs_gdt_index = ldt_info.entry_number;
#endif /* __linux__ */
}
if (fs_gdt_index > 0) return (fs_gdt_index << 3) | 3;
return wine_ldt_alloc_entries( 1 );
}
/***********************************************************************
* wine_ldt_init_fs
*
* Initialize the entry for the %fs selector of the current thread, and
* set the thread %fs register.
*
* Note: this runs in the context of the new thread, so cannot acquire locks.
*/
void wine_ldt_init_fs( unsigned short sel, const LDT_ENTRY *entry )
{
if (is_gdt_sel(sel))
{
#ifdef __linux__
struct modify_ldt_s ldt_info;
int ret;
ldt_info.entry_number = sel >> 3;
assert( ldt_info.entry_number == fs_gdt_index );
fill_modify_ldt_struct( &ldt_info, entry );
if ((ret = set_thread_area( &ldt_info ) < 0)) perror( "set_thread_area" );
#endif /* __linux__ */
}
else /* LDT selector */
{
internal_set_entry( sel, entry );
}
wine_set_fs( sel );
}
/***********************************************************************
* wine_ldt_free_fs
*
* Free a %fs selector returned by wine_ldt_alloc_fs.
*/
void wine_ldt_free_fs( unsigned short sel )
{
if (is_gdt_sel(sel)) return; /* nothing to do */
if (!((wine_get_fs() ^ sel) & ~3))
{
/* FIXME: if freeing current %fs we cannot acquire locks */
wine_set_fs( 0 );
internal_set_entry( sel, &null_entry );
wine_ldt_copy.flags[sel >> 3] = 0;
}
else wine_ldt_free_entries( sel, 1 );
}
/*********************************************************************** /***********************************************************************
* selector access functions * selector access functions
*/ */
#ifdef __i386__
# ifndef _MSC_VER # ifndef _MSC_VER
/* Nothing needs to be done for MS C, it will do with inline versions from the winnt.h */ /* Nothing needs to be done for MS C, it will do with inline versions from the winnt.h */
__ASM_GLOBAL_FUNC( wine_get_cs, "movw %cs,%ax\n\tret" ) __ASM_GLOBAL_FUNC( wine_get_cs, "movw %cs,%ax\n\tret" )
@ -207,4 +461,5 @@ __ASM_GLOBAL_FUNC( wine_get_ss, "movw %ss,%ax\n\tret" )
__ASM_GLOBAL_FUNC( wine_set_fs, "movl 4(%esp),%eax\n\tmovw %ax,%fs\n\tret" ) __ASM_GLOBAL_FUNC( wine_set_fs, "movl 4(%esp),%eax\n\tmovw %ax,%fs\n\tret" )
__ASM_GLOBAL_FUNC( wine_set_gs, "movl 4(%esp),%eax\n\tmovw %ax,%gs\n\tret" ) __ASM_GLOBAL_FUNC( wine_set_gs, "movl 4(%esp),%eax\n\tmovw %ax,%gs\n\tret" )
# endif /* defined(_MSC_VER) */ # endif /* defined(_MSC_VER) */
#endif /* defined(__i386__) */
#endif /* __i386__ */

View File

@ -790,17 +790,6 @@ FARPROC16 WINAPI MakeProcInstance16( FARPROC16 func, HANDLE16 hInstance )
return (FARPROC16)0; return (FARPROC16)0;
} }
if (hInstance)
{
if ( (!(hInstance & 4)) ||
((hInstance != 0xffff) && IS_SELECTOR_FREE(hInstance|7)) )
{
WARN("Invalid hInstance (%04x) passed to MakeProcInstance !\n",
hInstance);
return 0;
}
}
if ( (GlobalHandleToSel16(CURRENT_DS) != hInstanceSelector) if ( (GlobalHandleToSel16(CURRENT_DS) != hInstanceSelector)
&& (hInstance != 0) && (hInstance != 0)
&& (hInstance != 0xffff) ) && (hInstance != 0xffff) )

View File

@ -55,13 +55,8 @@ inline static void *make_ptr( CONTEXT86 *context, DWORD seg, DWORD off, int long
inline static void *get_stack( CONTEXT86 *context ) inline static void *get_stack( CONTEXT86 *context )
{ {
if (ISV86(context)) if (ISV86(context)) return PTR_REAL_TO_LIN( context->SegSs, context->Esp );
return PTR_REAL_TO_LIN( context->SegSs, context->Esp ); return wine_ldt_get_ptr( context->SegSs, context->Esp );
if (IS_SELECTOR_SYSTEM(context->SegSs))
return (void *)context->Esp;
if (IS_SELECTOR_32BIT(context->SegSs))
return (char *) MapSL( MAKESEGPTR( context->SegSs, 0 ) ) + context->Esp;
return MapSL( MAKESEGPTR( context->SegSs, LOWORD(context->Esp) ) );
} }
/*********************************************************************** /***********************************************************************
@ -97,8 +92,6 @@ static BOOL INSTR_ReplaceSelector( CONTEXT86 *context, WORD *sel )
*sel = DOSMEM_BiosDataSeg; *sel = DOSMEM_BiosDataSeg;
return TRUE; return TRUE;
} }
if (!IS_SELECTOR_SYSTEM(*sel) && !IS_SELECTOR_FREE(*sel))
ERR("Got protection fault on valid selector, maybe your kernel is too old?\n" );
return FALSE; /* Can't replace selector, crashdump */ return FALSE; /* Can't replace selector, crashdump */
} }
@ -112,6 +105,7 @@ static BYTE *INSTR_GetOperandAddr( CONTEXT86 *context, BYTE *instr,
int long_addr, int segprefix, int *len ) int long_addr, int segprefix, int *len )
{ {
int mod, rm, base, index = 0, ss = 0, seg = 0, off; int mod, rm, base, index = 0, ss = 0, seg = 0, off;
LDT_ENTRY entry;
#define GET_VAL(val,type) \ #define GET_VAL(val,type) \
{ *val = *(type *)instr; instr += sizeof(type); *len += sizeof(type); } { *val = *(type *)instr; instr += sizeof(type); *len += sizeof(type); }
@ -253,9 +247,11 @@ static BYTE *INSTR_GetOperandAddr( CONTEXT86 *context, BYTE *instr,
/* Make sure the segment and offset are valid */ /* Make sure the segment and offset are valid */
if (IS_SELECTOR_SYSTEM(seg)) return (BYTE *)(base + (index << ss)); if (IS_SELECTOR_SYSTEM(seg)) return (BYTE *)(base + (index << ss));
if (((seg & 7) != 7) || IS_SELECTOR_FREE(seg)) return NULL; if ((seg & 7) != 7) return NULL;
if (wine_ldt_copy.limit[seg >> 3] < (base + (index << ss))) return NULL; wine_ldt_get_entry( seg, &entry );
return (char *) MapSL( MAKESEGPTR( seg, 0 ) ) + base + (index << ss); if (wine_ldt_is_empty( &entry )) return NULL;
if (wine_ldt_get_limit(&entry) < (base + (index << ss))) return NULL;
return (char *)wine_ldt_get_base(&entry) + base + (index << ss);
#undef GET_VAL #undef GET_VAL
} }

View File

@ -42,39 +42,13 @@ inline static WORD get_sel_count( WORD sel )
return (wine_ldt_copy.limit[sel >> __AHSHIFT] >> 16) + 1; return (wine_ldt_copy.limit[sel >> __AHSHIFT] >> 16) + 1;
} }
static const LDT_ENTRY null_entry; /* all-zeros, used to clear LDT entries */
/***********************************************************************
* SELECTOR_AllocArray
*
* Allocate a selector array without setting the LDT entries
*/
static WORD SELECTOR_AllocArray( WORD count )
{
WORD i, sel, size = 0;
if (!count) return 0;
for (i = FIRST_LDT_ENTRY_TO_ALLOC; i < LDT_SIZE; i++)
{
if (wine_ldt_copy.flags[i] & WINE_LDT_FLAGS_ALLOCATED) size = 0;
else if (++size >= count) break;
}
if (i == LDT_SIZE) return 0;
sel = i - size + 1;
/* mark selectors as allocated */
for (i = 0; i < count; i++) wine_ldt_copy.flags[sel + i] |= WINE_LDT_FLAGS_ALLOCATED;
return (sel << __AHSHIFT) | 7;
}
/*********************************************************************** /***********************************************************************
* AllocSelectorArray (KERNEL.206) * AllocSelectorArray (KERNEL.206)
*/ */
WORD WINAPI AllocSelectorArray16( WORD count ) WORD WINAPI AllocSelectorArray16( WORD count )
{ {
WORD i, sel = SELECTOR_AllocArray( count ); WORD i, sel = wine_ldt_alloc_entries( count );
if (sel) if (sel)
{ {
@ -96,7 +70,7 @@ WORD WINAPI AllocSelector16( WORD sel )
WORD newsel, count, i; WORD newsel, count, i;
count = sel ? get_sel_count(sel) : 1; count = sel ? get_sel_count(sel) : 1;
newsel = SELECTOR_AllocArray( count ); newsel = wine_ldt_alloc_entries( count );
TRACE("(%04x): returning %04x\n", sel, newsel ); TRACE("(%04x): returning %04x\n", sel, newsel );
if (!newsel) return 0; if (!newsel) return 0;
if (!sel) return newsel; /* nothing to copy */ if (!sel) return newsel; /* nothing to copy */
@ -115,41 +89,20 @@ WORD WINAPI AllocSelector16( WORD sel )
*/ */
WORD WINAPI FreeSelector16( WORD sel ) WORD WINAPI FreeSelector16( WORD sel )
{ {
if (IS_SELECTOR_FREE(sel)) return sel; /* error */ LDT_ENTRY entry;
wine_ldt_get_entry( sel, &entry );
if (wine_ldt_is_empty( &entry )) return sel; /* error */
#ifdef __i386__ #ifdef __i386__
/* Check if we are freeing current %fs or %gs selector */ /* Check if we are freeing current %fs selector */
if (!((wine_get_fs() ^ sel) & ~3)) if (!((wine_get_fs() ^ sel) & ~3))
{
WARN("Freeing %%fs selector (%04x), not good.\n", wine_get_fs() ); WARN("Freeing %%fs selector (%04x), not good.\n", wine_get_fs() );
wine_set_fs( 0 );
}
if (!((wine_get_gs() ^ sel) & ~3)) wine_set_gs( 0 );
#endif /* __i386__ */ #endif /* __i386__ */
wine_ldt_free_entries( sel, 1 );
wine_ldt_set_entry( sel, &null_entry );
wine_ldt_copy.flags[sel >> __AHSHIFT] &= ~WINE_LDT_FLAGS_ALLOCATED;
return 0; return 0;
} }
/***********************************************************************
* SELECTOR_FreeFs
*
* Free the current %fs selector.
*/
void SELECTOR_FreeFs(void)
{
WORD fs = wine_get_fs();
if (fs)
{
wine_ldt_copy.flags[fs >> __AHSHIFT] &= ~WINE_LDT_FLAGS_ALLOCATED;
wine_set_fs(0);
wine_ldt_set_entry( fs, &null_entry );
}
}
/*********************************************************************** /***********************************************************************
* SELECTOR_SetEntries * SELECTOR_SetEntries
* *
@ -185,7 +138,7 @@ WORD SELECTOR_AllocBlock( const void *base, DWORD size, unsigned char flags )
if (!size) return 0; if (!size) return 0;
count = (size + 0xffff) / 0x10000; count = (size + 0xffff) / 0x10000;
sel = SELECTOR_AllocArray( count ); sel = wine_ldt_alloc_entries( count );
if (sel) SELECTOR_SetEntries( sel, base, size, flags ); if (sel) SELECTOR_SetEntries( sel, base, size, flags );
return sel; return sel;
} }
@ -213,37 +166,14 @@ void SELECTOR_FreeBlock( WORD sel )
WORD SELECTOR_ReallocBlock( WORD sel, const void *base, DWORD size ) WORD SELECTOR_ReallocBlock( WORD sel, const void *base, DWORD size )
{ {
LDT_ENTRY entry; LDT_ENTRY entry;
WORD i, oldcount, newcount; int oldcount, newcount;
if (!size) size = 1; if (!size) size = 1;
oldcount = get_sel_count( sel );
newcount = (size + 0xffff) >> 16;
wine_ldt_get_entry( sel, &entry ); wine_ldt_get_entry( sel, &entry );
oldcount = (wine_ldt_get_limit(&entry) >> 16) + 1;
newcount = (size + 0xffff) >> 16;
if (oldcount < newcount) /* We need to add selectors */ sel = wine_ldt_realloc_entries( sel, oldcount, newcount );
{
WORD index = sel >> __AHSHIFT;
/* Check if the next selectors are free */
if (index + newcount > LDT_SIZE) i = oldcount;
else
for (i = oldcount; i < newcount; i++)
if (wine_ldt_copy.flags[index+i] & WINE_LDT_FLAGS_ALLOCATED) break;
if (i < newcount) /* they are not free */
{
SELECTOR_FreeBlock( sel );
sel = SELECTOR_AllocArray( newcount );
}
else /* mark the selectors as allocated */
{
for (i = oldcount; i < newcount; i++)
wine_ldt_copy.flags[index+i] |= WINE_LDT_FLAGS_ALLOCATED;
}
}
else if (oldcount > newcount) /* We need to remove selectors */
{
SELECTOR_FreeBlock( sel + (newcount << __AHSHIFT) );
}
if (sel) SELECTOR_SetEntries( sel, base, size, wine_ldt_get_flags(&entry) ); if (sel) SELECTOR_SetEntries( sel, base, size, wine_ldt_get_flags(&entry) );
return sel; return sel;
} }
@ -272,7 +202,7 @@ WORD WINAPI AllocCStoDSAlias16( WORD sel )
WORD newsel; WORD newsel;
LDT_ENTRY entry; LDT_ENTRY entry;
newsel = SELECTOR_AllocArray( 1 ); newsel = wine_ldt_alloc_entries( 1 );
TRACE("(%04x): returning %04x\n", TRACE("(%04x): returning %04x\n",
sel, newsel ); sel, newsel );
if (!newsel) return 0; if (!newsel) return 0;
@ -291,7 +221,7 @@ WORD WINAPI AllocDStoCSAlias16( WORD sel )
WORD newsel; WORD newsel;
LDT_ENTRY entry; LDT_ENTRY entry;
newsel = SELECTOR_AllocArray( 1 ); newsel = wine_ldt_alloc_entries( 1 );
TRACE("(%04x): returning %04x\n", TRACE("(%04x): returning %04x\n",
sel, newsel ); sel, newsel );
if (!newsel) return 0; if (!newsel) return 0;
@ -395,8 +325,8 @@ BOOL16 WINAPI IsBadCodePtr16( SEGPTR lpfn )
sel = SELECTOROF(lpfn); sel = SELECTOROF(lpfn);
if (!sel) return TRUE; if (!sel) return TRUE;
if (IS_SELECTOR_FREE(sel)) return TRUE;
wine_ldt_get_entry( sel, &entry ); wine_ldt_get_entry( sel, &entry );
if (wine_ldt_is_empty( &entry )) return TRUE;
/* check for code segment, ignoring conforming, read-only and accessed bits */ /* check for code segment, ignoring conforming, read-only and accessed bits */
if ((entry.HighWord.Bits.Type ^ WINE_LDT_FLAGS_CODE) & 0x18) return TRUE; if ((entry.HighWord.Bits.Type ^ WINE_LDT_FLAGS_CODE) & 0x18) return TRUE;
if (OFFSETOF(lpfn) > wine_ldt_get_limit(&entry)) return TRUE; if (OFFSETOF(lpfn) > wine_ldt_get_limit(&entry)) return TRUE;
@ -414,8 +344,8 @@ BOOL16 WINAPI IsBadStringPtr16( SEGPTR ptr, UINT16 size )
sel = SELECTOROF(ptr); sel = SELECTOROF(ptr);
if (!sel) return TRUE; if (!sel) return TRUE;
if (IS_SELECTOR_FREE(sel)) return TRUE;
wine_ldt_get_entry( sel, &entry ); wine_ldt_get_entry( sel, &entry );
if (wine_ldt_is_empty( &entry )) return TRUE;
/* check for data or readable code segment */ /* check for data or readable code segment */
if (!(entry.HighWord.Bits.Type & 0x10)) return TRUE; /* system descriptor */ if (!(entry.HighWord.Bits.Type & 0x10)) return TRUE; /* system descriptor */
if ((entry.HighWord.Bits.Type & 0x0a) == 0x08) return TRUE; /* non-readable code segment */ if ((entry.HighWord.Bits.Type & 0x0a) == 0x08) return TRUE; /* non-readable code segment */
@ -435,8 +365,8 @@ BOOL16 WINAPI IsBadHugeReadPtr16( SEGPTR ptr, DWORD size )
sel = SELECTOROF(ptr); sel = SELECTOROF(ptr);
if (!sel) return TRUE; if (!sel) return TRUE;
if (IS_SELECTOR_FREE(sel)) return TRUE;
wine_ldt_get_entry( sel, &entry ); wine_ldt_get_entry( sel, &entry );
if (wine_ldt_is_empty( &entry )) return TRUE;
/* check for data or readable code segment */ /* check for data or readable code segment */
if (!(entry.HighWord.Bits.Type & 0x10)) return TRUE; /* system descriptor */ if (!(entry.HighWord.Bits.Type & 0x10)) return TRUE; /* system descriptor */
if ((entry.HighWord.Bits.Type & 0x0a) == 0x08) return TRUE; /* non-readable code segment */ if ((entry.HighWord.Bits.Type & 0x0a) == 0x08) return TRUE; /* non-readable code segment */
@ -455,8 +385,8 @@ BOOL16 WINAPI IsBadHugeWritePtr16( SEGPTR ptr, DWORD size )
sel = SELECTOROF(ptr); sel = SELECTOROF(ptr);
if (!sel) return TRUE; if (!sel) return TRUE;
if (IS_SELECTOR_FREE(sel)) return TRUE;
wine_ldt_get_entry( sel, &entry ); wine_ldt_get_entry( sel, &entry );
if (wine_ldt_is_empty( &entry )) return TRUE;
/* check for writeable data segment, ignoring expand-down and accessed flags */ /* check for writeable data segment, ignoring expand-down and accessed flags */
if ((entry.HighWord.Bits.Type ^ WINE_LDT_FLAGS_DATA) & ~5) return TRUE; if ((entry.HighWord.Bits.Type ^ WINE_LDT_FLAGS_DATA) & ~5) return TRUE;
if (size && (OFFSETOF(ptr) + size - 1 > wine_ldt_get_limit( &entry ))) return TRUE; if (size && (OFFSETOF(ptr) + size - 1 > wine_ldt_get_limit( &entry ))) return TRUE;
@ -496,13 +426,15 @@ BOOL16 WINAPI IsBadFlatReadWritePtr16( SEGPTR ptr, DWORD size, BOOL16 bWrite )
*/ */
DWORD WINAPI MemoryRead16( WORD sel, DWORD offset, void *buffer, DWORD count ) DWORD WINAPI MemoryRead16( WORD sel, DWORD offset, void *buffer, DWORD count )
{ {
WORD index = sel >> __AHSHIFT; LDT_ENTRY entry;
DWORD limit;
if (!(wine_ldt_copy.flags[index] & WINE_LDT_FLAGS_ALLOCATED)) return 0; wine_ldt_get_entry( sel, &entry );
if (offset > wine_ldt_copy.limit[index]) return 0; if (wine_ldt_is_empty( &entry )) return 0;
if (offset + count > wine_ldt_copy.limit[index] + 1) limit = wine_ldt_get_limit( &entry );
count = wine_ldt_copy.limit[index] + 1 - offset; if (offset > limit) return 0;
memcpy( buffer, (char *)wine_ldt_copy.base[index] + offset, count ); if (offset + count > limit + 1) count = limit + 1 - offset;
memcpy( buffer, (char *)wine_ldt_get_base(&entry) + offset, count );
return count; return count;
} }
@ -512,13 +444,15 @@ DWORD WINAPI MemoryRead16( WORD sel, DWORD offset, void *buffer, DWORD count )
*/ */
DWORD WINAPI MemoryWrite16( WORD sel, DWORD offset, void *buffer, DWORD count ) DWORD WINAPI MemoryWrite16( WORD sel, DWORD offset, void *buffer, DWORD count )
{ {
WORD index = sel >> __AHSHIFT; LDT_ENTRY entry;
DWORD limit;
if (!(wine_ldt_copy.flags[index] & WINE_LDT_FLAGS_ALLOCATED)) return 0; wine_ldt_get_entry( sel, &entry );
if (offset > wine_ldt_copy.limit[index]) return 0; if (wine_ldt_is_empty( &entry )) return 0;
if (offset + count > wine_ldt_copy.limit[index] + 1) limit = wine_ldt_get_limit( &entry );
count = wine_ldt_copy.limit[index] + 1 - offset; if (offset > limit) return 0;
memcpy( (char *)wine_ldt_copy.base[index] + offset, buffer, count ); if (offset + count > limit) count = limit + 1 - offset;
memcpy( (char *)wine_ldt_get_base(&entry) + offset, buffer, count );
return count; return count;
} }

View File

@ -64,8 +64,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(thread);
# endif /* CLONE_VM */ # endif /* CLONE_VM */
#endif /* linux || HAVE_CLONE */ #endif /* linux || HAVE_CLONE */
extern void SELECTOR_FreeFs(void);
struct thread_cleanup_info struct thread_cleanup_info
{ {
void *stack_base; void *stack_base;
@ -88,7 +86,12 @@ void SYSDEPS_SetCurThread( TEB *teb )
{ {
#if defined(__i386__) #if defined(__i386__)
/* On the i386, the current thread is in the %fs register */ /* On the i386, the current thread is in the %fs register */
wine_set_fs( teb->teb_sel ); LDT_ENTRY fs_entry;
wine_ldt_set_base( &fs_entry, teb );
wine_ldt_set_limit( &fs_entry, 0xfff );
wine_ldt_set_flags( &fs_entry, WINE_LDT_FLAGS_DATA|WINE_LDT_FLAGS_32BIT );
wine_ldt_init_fs( teb->teb_sel, &fs_entry );
#elif defined(__powerpc__) #elif defined(__powerpc__)
/* On PowerPC, the current TEB is in the gpr13 register */ /* On PowerPC, the current TEB is in the gpr13 register */
__asm__ __volatile__("mr 2, %0" : : "r" (teb)); __asm__ __volatile__("mr 2, %0" : : "r" (teb));
@ -142,7 +145,7 @@ static void cleanup_thread( void *ptr )
/* copy the info structure since it is on the stack we will free */ /* copy the info structure since it is on the stack we will free */
struct thread_cleanup_info info = *(struct thread_cleanup_info *)ptr; struct thread_cleanup_info info = *(struct thread_cleanup_info *)ptr;
munmap( info.stack_base, info.stack_size ); munmap( info.stack_base, info.stack_size );
SELECTOR_FreeFs(); wine_ldt_free_fs( wine_get_fs() );
#ifdef HAVE__LWP_CREATE #ifdef HAVE__LWP_CREATE
_lwp_exit(); _lwp_exit();
#endif #endif
@ -294,7 +297,7 @@ void SYSDEPS_ExitThread( int status )
struct thread_cleanup_info info; struct thread_cleanup_info info;
MEMORY_BASIC_INFORMATION meminfo; MEMORY_BASIC_INFORMATION meminfo;
FreeSelector16( teb->stack_sel ); wine_ldt_free_entries( teb->stack_sel, 1 );
VirtualQuery( teb->stack_top, &meminfo, sizeof(meminfo) ); VirtualQuery( teb->stack_top, &meminfo, sizeof(meminfo) );
info.stack_base = meminfo.AllocationBase; info.stack_base = meminfo.AllocationBase;
info.stack_size = meminfo.RegionSize + ((char *)teb->stack_top - (char *)meminfo.AllocationBase); info.stack_size = meminfo.RegionSize + ((char *)teb->stack_top - (char *)meminfo.AllocationBase);

View File

@ -105,7 +105,7 @@ static BOOL THREAD_InitTEB( TEB *teb )
teb->stack_top = (void *)~0UL; teb->stack_top = (void *)~0UL;
teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer); teb->StaticUnicodeString.MaximumLength = sizeof(teb->StaticUnicodeBuffer);
teb->StaticUnicodeString.Buffer = (PWSTR)teb->StaticUnicodeBuffer; teb->StaticUnicodeString.Buffer = (PWSTR)teb->StaticUnicodeBuffer;
teb->teb_sel = SELECTOR_AllocBlock( teb, 0x1000, WINE_LDT_FLAGS_DATA|WINE_LDT_FLAGS_32BIT ); teb->teb_sel = wine_ldt_alloc_fs();
return (teb->teb_sel != 0); return (teb->teb_sel != 0);
} }
@ -120,8 +120,8 @@ static void THREAD_FreeTEB( TEB *teb )
{ {
TRACE("(%p) called\n", teb ); TRACE("(%p) called\n", teb );
/* Free the associated memory */ /* Free the associated memory */
FreeSelector16( teb->stack_sel ); wine_ldt_free_entries( teb->stack_sel, 1 );
FreeSelector16( teb->teb_sel ); wine_ldt_free_fs( teb->teb_sel );
VirtualFree( teb->stack_base, 0, MEM_RELEASE ); VirtualFree( teb->stack_base, 0, MEM_RELEASE );
} }
@ -204,7 +204,7 @@ TEB *THREAD_InitStack( TEB *teb, DWORD stack_size )
return teb; return teb;
error: error:
FreeSelector16( teb->teb_sel ); wine_ldt_free_fs( teb->teb_sel );
VirtualFree( base, 0, MEM_RELEASE ); VirtualFree( base, 0, MEM_RELEASE );
return NULL; return NULL;
} }