Commit ce131538 authored by Alexandre Julliard's avatar Alexandre Julliard

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.
parent 28d65b1a
......@@ -49,6 +49,21 @@ extern int __wine_set_signal_handler(unsigned, int (*)(unsigned));
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
*/
......@@ -114,6 +129,9 @@ static BOOL process_attach(void)
/* Create the shared heap for broken win95 native dlls */
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 */
__wine_set_signal_handler(SIGINT, CONSOLE_HandleCtrlC);
......
......@@ -876,15 +876,17 @@ void WINAPI DOSVM_Int31Handler( CONTEXT86 *context )
case 0x0006: /* Get selector base address */
TRACE( "get selector base address (0x%04x)\n", BX_reg(context) );
{
LDT_ENTRY entry;
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 */
SET_CFLAG(context);
}
else
{
DWORD base = GetSelectorBase( sel );
void *base = wine_ldt_get_base(&entry);
SET_CX( context, HIWORD(base) );
SET_DX( context, LOWORD(base) );
}
......
......@@ -145,11 +145,7 @@ extern char IO_pp_init(void);
* selector which is neither system selector nor zero.
*/
#define CTX_SEG_OFF_TO_LIN(context,seg,off) \
(ISV86(context) ? PTR_REAL_TO_LIN((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)))))
(ISV86(context) ? PTR_REAL_TO_LIN((seg),(off)) : wine_ldt_get_ptr((seg),(off)))
#define INT_BARF(context,num) \
ERR( "int%x: unknown/not implemented parameters:\n" \
......
......@@ -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 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) */
#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) \
(IS_SELECTOR_SYSTEM(sel) || (wine_ldt_copy.flags[LOWORD(sel) >> 3] & WINE_LDT_FLAGS_32BIT))
......
......@@ -68,8 +68,23 @@ extern int* (*wine_h_errno_location)(void);
/* 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 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 */
#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_ALLOCATED 0x80 /* Segment is allocated (no longer free) */
#define WINE_LDT_FIRST_ENTRY 17
/* helper functions to manipulate the LDT_ENTRY structure */
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;
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 */
......
......@@ -790,17 +790,6 @@ FARPROC16 WINAPI MakeProcInstance16( FARPROC16 func, HANDLE16 hInstance )
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)
&& (hInstance != 0)
&& (hInstance != 0xffff) )
......
......@@ -55,13 +55,8 @@ inline static void *make_ptr( CONTEXT86 *context, DWORD seg, DWORD off, int long
inline static void *get_stack( CONTEXT86 *context )
{
if (ISV86(context))
return PTR_REAL_TO_LIN( 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) ) );
if (ISV86(context)) return PTR_REAL_TO_LIN( context->SegSs, context->Esp );
return wine_ldt_get_ptr( context->SegSs, context->Esp );
}
/***********************************************************************
......@@ -97,8 +92,6 @@ static BOOL INSTR_ReplaceSelector( CONTEXT86 *context, WORD *sel )
*sel = DOSMEM_BiosDataSeg;
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 */
}
......@@ -112,6 +105,7 @@ static BYTE *INSTR_GetOperandAddr( CONTEXT86 *context, BYTE *instr,
int long_addr, int segprefix, int *len )
{
int mod, rm, base, index = 0, ss = 0, seg = 0, off;
LDT_ENTRY entry;
#define GET_VAL(val,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 */
if (IS_SELECTOR_SYSTEM(seg)) return (BYTE *)(base + (index << ss));
if (((seg & 7) != 7) || IS_SELECTOR_FREE(seg)) return NULL;
if (wine_ldt_copy.limit[seg >> 3] < (base + (index << ss))) return NULL;
return (char *) MapSL( MAKESEGPTR( seg, 0 ) ) + base + (index << ss);
if ((seg & 7) != 7) return NULL;
wine_ldt_get_entry( seg, &entry );
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
}
......
......@@ -42,39 +42,13 @@ inline static WORD get_sel_count( WORD sel )
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)
*/
WORD WINAPI AllocSelectorArray16( WORD count )
{
WORD i, sel = SELECTOR_AllocArray( count );
WORD i, sel = wine_ldt_alloc_entries( count );
if (sel)
{
......@@ -96,7 +70,7 @@ WORD WINAPI AllocSelector16( WORD sel )
WORD newsel, count, i;
count = sel ? get_sel_count(sel) : 1;
newsel = SELECTOR_AllocArray( count );
newsel = wine_ldt_alloc_entries( count );
TRACE("(%04x): returning %04x\n", sel, newsel );
if (!newsel) return 0;
if (!sel) return newsel; /* nothing to copy */
......@@ -115,42 +89,21 @@ WORD WINAPI AllocSelector16( 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__
/* Check if we are freeing current %fs or %gs selector */
/* Check if we are freeing current %fs selector */
if (!((wine_get_fs() ^ sel) & ~3))
{
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__ */
wine_ldt_set_entry( sel, &null_entry );
wine_ldt_copy.flags[sel >> __AHSHIFT] &= ~WINE_LDT_FLAGS_ALLOCATED;
wine_ldt_free_entries( sel, 1 );
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
*
* Set the LDT entries for an array of selectors.
......@@ -185,7 +138,7 @@ WORD SELECTOR_AllocBlock( const void *base, DWORD size, unsigned char flags )
if (!size) return 0;
count = (size + 0xffff) / 0x10000;
sel = SELECTOR_AllocArray( count );
sel = wine_ldt_alloc_entries( count );
if (sel) SELECTOR_SetEntries( sel, base, size, flags );
return sel;
}
......@@ -213,37 +166,14 @@ void SELECTOR_FreeBlock( WORD sel )
WORD SELECTOR_ReallocBlock( WORD sel, const void *base, DWORD size )
{
LDT_ENTRY entry;
WORD i, oldcount, newcount;
int oldcount, newcount;
if (!size) size = 1;
oldcount = get_sel_count( sel );
newcount = (size + 0xffff) >> 16;
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 */
{
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) );
}
sel = wine_ldt_realloc_entries( sel, oldcount, newcount );
if (sel) SELECTOR_SetEntries( sel, base, size, wine_ldt_get_flags(&entry) );
return sel;
}
......@@ -272,7 +202,7 @@ WORD WINAPI AllocCStoDSAlias16( WORD sel )
WORD newsel;
LDT_ENTRY entry;
newsel = SELECTOR_AllocArray( 1 );
newsel = wine_ldt_alloc_entries( 1 );
TRACE("(%04x): returning %04x\n",
sel, newsel );
if (!newsel) return 0;
......@@ -291,7 +221,7 @@ WORD WINAPI AllocDStoCSAlias16( WORD sel )
WORD newsel;
LDT_ENTRY entry;
newsel = SELECTOR_AllocArray( 1 );
newsel = wine_ldt_alloc_entries( 1 );
TRACE("(%04x): returning %04x\n",
sel, newsel );
if (!newsel) return 0;
......@@ -395,8 +325,8 @@ BOOL16 WINAPI IsBadCodePtr16( SEGPTR lpfn )
sel = SELECTOROF(lpfn);
if (!sel) return TRUE;
if (IS_SELECTOR_FREE(sel)) return TRUE;
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 */
if ((entry.HighWord.Bits.Type ^ WINE_LDT_FLAGS_CODE) & 0x18) 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);
if (!sel) return TRUE;
if (IS_SELECTOR_FREE(sel)) return TRUE;
wine_ldt_get_entry( sel, &entry );
if (wine_ldt_is_empty( &entry )) return TRUE;
/* check for data or readable code segment */
if (!(entry.HighWord.Bits.Type & 0x10)) return TRUE; /* system descriptor */
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);
if (!sel) return TRUE;
if (IS_SELECTOR_FREE(sel)) return TRUE;
wine_ldt_get_entry( sel, &entry );
if (wine_ldt_is_empty( &entry )) return TRUE;
/* check for data or readable code segment */
if (!(entry.HighWord.Bits.Type & 0x10)) return TRUE; /* system descriptor */
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);
if (!sel) return TRUE;
if (IS_SELECTOR_FREE(sel)) return TRUE;
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 */
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;
......@@ -496,13 +426,15 @@ BOOL16 WINAPI IsBadFlatReadWritePtr16( SEGPTR ptr, DWORD size, BOOL16 bWrite )
*/
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;
if (offset > wine_ldt_copy.limit[index]) return 0;
if (offset + count > wine_ldt_copy.limit[index] + 1)
count = wine_ldt_copy.limit[index] + 1 - offset;
memcpy( buffer, (char *)wine_ldt_copy.base[index] + offset, count );
wine_ldt_get_entry( sel, &entry );
if (wine_ldt_is_empty( &entry )) return 0;
limit = wine_ldt_get_limit( &entry );
if (offset > limit) return 0;
if (offset + count > limit + 1) count = limit + 1 - offset;
memcpy( buffer, (char *)wine_ldt_get_base(&entry) + offset, 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 )
{
WORD index = sel >> __AHSHIFT;
LDT_ENTRY entry;
DWORD limit;
if (!(wine_ldt_copy.flags[index] & WINE_LDT_FLAGS_ALLOCATED)) return 0;
if (offset > wine_ldt_copy.limit[index]) return 0;
if (offset + count > wine_ldt_copy.limit[index] + 1)
count = wine_ldt_copy.limit[index] + 1 - offset;
memcpy( (char *)wine_ldt_copy.base[index] + offset, buffer, count );
wine_ldt_get_entry( sel, &entry );
if (wine_ldt_is_empty( &entry )) return 0;
limit = wine_ldt_get_limit( &entry );
if (offset > limit) return 0;
if (offset + count > limit) count = limit + 1 - offset;
memcpy( (char *)wine_ldt_get_base(&entry) + offset, buffer, count );
return count;
}
......
......@@ -64,8 +64,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(thread);
# endif /* CLONE_VM */
#endif /* linux || HAVE_CLONE */
extern void SELECTOR_FreeFs(void);
struct thread_cleanup_info
{
void *stack_base;
......@@ -88,7 +86,12 @@ void SYSDEPS_SetCurThread( TEB *teb )
{
#if defined(__i386__)
/* 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__)
/* On PowerPC, the current TEB is in the gpr13 register */
__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 */
struct thread_cleanup_info info = *(struct thread_cleanup_info *)ptr;
munmap( info.stack_base, info.stack_size );
SELECTOR_FreeFs();
wine_ldt_free_fs( wine_get_fs() );
#ifdef HAVE__LWP_CREATE
_lwp_exit();
#endif
......@@ -294,7 +297,7 @@ void SYSDEPS_ExitThread( int status )
struct thread_cleanup_info info;
MEMORY_BASIC_INFORMATION meminfo;
FreeSelector16( teb->stack_sel );
wine_ldt_free_entries( teb->stack_sel, 1 );
VirtualQuery( teb->stack_top, &meminfo, sizeof(meminfo) );
info.stack_base = meminfo.AllocationBase;
info.stack_size = meminfo.RegionSize + ((char *)teb->stack_top - (char *)meminfo.AllocationBase);
......
......@@ -105,7 +105,7 @@ static BOOL THREAD_InitTEB( TEB *teb )
teb->stack_top = (void *)~0UL;
teb->StaticUnicodeString.MaximumLength = sizeof(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);
}
......@@ -120,8 +120,8 @@ static void THREAD_FreeTEB( TEB *teb )
{
TRACE("(%p) called\n", teb );
/* Free the associated memory */
FreeSelector16( teb->stack_sel );
FreeSelector16( teb->teb_sel );
wine_ldt_free_entries( teb->stack_sel, 1 );
wine_ldt_free_fs( teb->teb_sel );
VirtualFree( teb->stack_base, 0, MEM_RELEASE );
}
......@@ -204,7 +204,7 @@ TEB *THREAD_InitStack( TEB *teb, DWORD stack_size )
return teb;
error:
FreeSelector16( teb->teb_sel );
wine_ldt_free_fs( teb->teb_sel );
VirtualFree( base, 0, MEM_RELEASE );
return NULL;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment