ntdll: Allocate 64-bit TEB and PEB in WoW64 mode.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Alexandre Julliard 2020-07-24 11:24:31 +02:00
parent e3b059b5ba
commit a865a4f61d
7 changed files with 131 additions and 46 deletions

View File

@ -3906,7 +3906,6 @@ void __wine_process_init(void)
UNICODE_STRING nt_name;
MEMORY_BASIC_INFORMATION meminfo;
INITIAL_TEB stack;
ULONG_PTR val;
TEB *teb = NtCurrentTeb();
PEB *peb = teb->Peb;
@ -3935,8 +3934,9 @@ void __wine_process_init(void)
InitializeListHead( &ldr.InMemoryOrderModuleList );
InitializeListHead( &ldr.InInitializationOrderModuleList );
NtQueryInformationProcess( GetCurrentProcess(), ProcessWow64Information, &val, sizeof(val), NULL );
is_wow64 = !!val;
#ifndef _WIN64
is_wow64 = !!NtCurrentTeb64();
#endif
init_unix_codepage();
init_directories();
@ -4008,6 +4008,19 @@ void __wine_process_init(void)
NtTerminateProcess( GetCurrentProcess(), status );
}
#ifndef _WIN64
if (NtCurrentTeb64())
{
PEB64 *peb64 = UlongToPtr( NtCurrentTeb64()->Peb );
peb64->ImageBaseAddress = PtrToUlong( peb->ImageBaseAddress );
peb64->OSMajorVersion = peb->OSMajorVersion;
peb64->OSMinorVersion = peb->OSMinorVersion;
peb64->OSBuildNumber = peb->OSBuildNumber;
peb64->OSPlatformId = peb->OSPlatformId;
peb64->SessionId = peb->SessionId;
}
#endif
/* the main exe needs to be the first in the load order list */
RemoveEntryList( &wm->ldr.InLoadOrderLinks );
InsertHeadList( &peb->LdrData->InLoadOrderModuleList, &wm->ldr.InLoadOrderLinks );

View File

@ -129,6 +129,10 @@ static inline struct ntdll_thread_data *ntdll_get_thread_data(void)
return (struct ntdll_thread_data *)&NtCurrentTeb()->GdiTebBatch;
}
#ifndef _WIN64
static inline TEB64 *NtCurrentTeb64(void) { return (TEB64 *)NtCurrentTeb()->GdiBatchCount; }
#endif
#define HASH_STRING_ALGORITHM_DEFAULT 0
#define HASH_STRING_ALGORITHM_X65599 1
#define HASH_STRING_ALGORITHM_INVALID 0xffffffff

View File

@ -2636,6 +2636,47 @@ static void test_thread_info(void)
ok( len == 0xdeadbeef, "wrong len %u\n", len );
}
static void test_wow64(void)
{
#ifndef _WIN64
if (is_wow64)
{
PEB64 *peb64;
TEB64 *teb64 = (TEB64 *)NtCurrentTeb()->GdiBatchCount;
ok( !!teb64, "GdiBatchCount not set\n" );
ok( (char *)NtCurrentTeb() + NtCurrentTeb()->WowTebOffset == (char *)teb64 ||
broken(!NtCurrentTeb()->WowTebOffset), /* pre-win10 */
"wrong WowTebOffset %x (%p/%p)\n", NtCurrentTeb()->WowTebOffset, teb64, NtCurrentTeb() );
ok( (char *)teb64 + 0x2000 == (char *)NtCurrentTeb(), "unexpected diff %p / %p\n",
teb64, NtCurrentTeb() );
ok( teb64->Tib.ExceptionList == PtrToUlong( NtCurrentTeb() ), "wrong Tib.ExceptionList %s / %p\n",
wine_dbgstr_longlong(teb64->Tib.ExceptionList), NtCurrentTeb() );
ok( teb64->Tib.Self == PtrToUlong( teb64 ), "wrong Tib.Self %s / %p\n",
wine_dbgstr_longlong(teb64->Tib.Self), teb64 );
ok( teb64->StaticUnicodeString.Buffer == PtrToUlong( teb64->StaticUnicodeBuffer ),
"wrong StaticUnicodeString %s / %p\n",
wine_dbgstr_longlong(teb64->StaticUnicodeString.Buffer), teb64->StaticUnicodeBuffer );
ok( teb64->ClientId.UniqueProcess == GetCurrentProcessId(), "wrong pid %s / %x\n",
wine_dbgstr_longlong(teb64->ClientId.UniqueProcess), GetCurrentProcessId() );
ok( teb64->ClientId.UniqueThread == GetCurrentThreadId(), "wrong tid %s / %x\n",
wine_dbgstr_longlong(teb64->ClientId.UniqueThread), GetCurrentThreadId() );
peb64 = ULongToPtr( teb64->Peb );
ok( peb64->ImageBaseAddress == PtrToUlong( NtCurrentTeb()->Peb->ImageBaseAddress ),
"wrong ImageBaseAddress %s / %p\n",
wine_dbgstr_longlong(peb64->ImageBaseAddress), NtCurrentTeb()->Peb->ImageBaseAddress);
ok( peb64->OSBuildNumber == NtCurrentTeb()->Peb->OSBuildNumber, "wrong OSBuildNumber %x / %x\n",
peb64->OSBuildNumber, NtCurrentTeb()->Peb->OSBuildNumber );
ok( peb64->OSPlatformId == NtCurrentTeb()->Peb->OSPlatformId, "wrong OSPlatformId %x / %x\n",
peb64->OSPlatformId, NtCurrentTeb()->Peb->OSPlatformId );
return;
}
#endif
ok( !NtCurrentTeb()->GdiBatchCount, "GdiBatchCount set to %x\n", NtCurrentTeb()->GdiBatchCount );
ok( !NtCurrentTeb()->WowTebOffset, "WowTebOffset set to %x\n", NtCurrentTeb()->WowTebOffset );
}
START_TEST(info)
{
char **argv;
@ -2693,6 +2734,7 @@ START_TEST(info)
test_thread_lookup();
test_affinity();
test_wow64();
/* belongs to its own file */
test_readvirtualmemory();

View File

@ -1551,7 +1551,19 @@ size_t server_init_thread( void *entry_point, BOOL *suspend )
}
SERVER_END_REQ;
is_wow64 = !is_win64 && (server_cpus & ((1 << CPU_x86_64) | (1 << CPU_ARM64))) != 0;
#ifndef _WIN64
is_wow64 = (server_cpus & ((1 << CPU_x86_64) | (1 << CPU_ARM64))) != 0;
if (is_wow64)
{
TEB64 *teb64 = (TEB64 *)((char *)NtCurrentTeb() - teb_offset);
NtCurrentTeb()->GdiBatchCount = PtrToUlong( teb64 );
NtCurrentTeb()->WowTebOffset = -teb_offset;
teb64->ClientId.UniqueProcess = PtrToUlong( NtCurrentTeb()->ClientId.UniqueProcess );
teb64->ClientId.UniqueThread = PtrToUlong( NtCurrentTeb()->ClientId.UniqueThread );
}
#endif
ntdll_get_thread_data()->wow64_redir = is_wow64;
switch (ret)

View File

@ -540,7 +540,7 @@ static inline TEB *get_current_teb(void)
{
unsigned long esp;
__asm__("movl %%esp,%0" : "=g" (esp) );
return (TEB *)(esp & ~signal_stack_mask);
return (TEB *)((esp & ~signal_stack_mask) + teb_offset);
}
@ -1937,7 +1937,7 @@ static void ldt_set_fs( WORD sel, TEB *teb )
struct modify_ldt_s ldt_info = { sel >> 3 };
ldt_info.base_addr = teb;
ldt_info.limit = teb_size - 1;
ldt_info.limit = page_size - 1;
ldt_info.seg_32bit = 1;
if (set_thread_area( &ldt_info ) < 0) perror( "set_thread_area" );
#elif defined(__FreeBSD__) || defined (__FreeBSD_kernel__) || defined(__DragonFly__)
@ -2052,7 +2052,7 @@ NTSTATUS signal_alloc_thread( TEB *teb )
static int first_thread = 1;
sigset_t sigset;
int idx;
LDT_ENTRY entry = ldt_make_entry( teb, teb_size - 1, LDT_FLAGS_DATA | LDT_FLAGS_32BIT );
LDT_ENTRY entry = ldt_make_entry( teb, page_size - 1, LDT_FLAGS_DATA | LDT_FLAGS_32BIT );
if (first_thread) /* no locking for first thread */
{

View File

@ -71,7 +71,17 @@ static inline struct ntdll_thread_data *ntdll_get_thread_data(void)
return (struct ntdll_thread_data *)&NtCurrentTeb()->GdiTebBatch;
}
static const UINT_PTR page_size = 0x1000;
static const SIZE_T page_size = 0x1000;
static const SIZE_T signal_stack_mask = 0xffff;
#ifdef _WIN64
static const SIZE_T teb_size = 0x2000;
static const SIZE_T teb_offset = 0;
static const SIZE_T signal_stack_size = 0x10000 - 0x2000;
#else
static const SIZE_T teb_size = 0x3000; /* TEB64 + TEB */
static const SIZE_T teb_offset = 0x2000;
static const SIZE_T signal_stack_size = 0x10000 - 0x3000;
#endif
/* callbacks to PE ntdll from the Unix side */
extern void (WINAPI *pDbgUiRemoteBreakin)( void *arg ) DECLSPEC_HIDDEN;
@ -144,9 +154,6 @@ extern BOOL is_wow64 DECLSPEC_HIDDEN;
extern HANDLE keyed_event DECLSPEC_HIDDEN;
extern timeout_t server_start_time DECLSPEC_HIDDEN;
extern sigset_t server_block_set DECLSPEC_HIDDEN;
extern SIZE_T signal_stack_size DECLSPEC_HIDDEN;
extern SIZE_T signal_stack_mask DECLSPEC_HIDDEN;
static const SIZE_T teb_size = 0x1000 * sizeof(void *) / 4;
extern struct _KUSER_SHARED_DATA *user_shared_data DECLSPEC_HIDDEN;
#ifdef __i386__
extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN;
@ -278,7 +285,7 @@ static inline IMAGE_NT_HEADERS *get_exe_nt_header(void)
static inline void *get_signal_stack(void)
{
return (char *)NtCurrentTeb() + teb_size;
return (char *)NtCurrentTeb() + teb_size - teb_offset;
}
static inline size_t ntdll_wcslen( const WCHAR *str )

View File

@ -154,12 +154,9 @@ static void *working_set_limit = (void *)0x7fff0000;
struct _KUSER_SHARED_DATA *user_shared_data = (void *)0x7ffe0000;
SIZE_T signal_stack_size = 0;
SIZE_T signal_stack_mask = 0;
/* TEB allocation blocks */
static TEB *teb_block;
static TEB *next_free_teb;
static void *teb_block;
static void **next_free_teb;
static int teb_block_pos;
static struct list teb_list = LIST_INIT( teb_list );
@ -2361,7 +2358,7 @@ void virtual_init(void)
const struct preload_info **preload_info = dlsym( RTLD_DEFAULT, "wine_main_preload_info" );
const char *preload = getenv( "WINEPRELOADRESERVE" );
struct alloc_virtual_heap alloc_views;
size_t size, align;
size_t size;
int i;
pthread_mutexattr_t attr;
@ -2389,13 +2386,6 @@ void virtual_init(void)
}
}
size = teb_size + max( MINSIGSTKSZ, 8192 );
/* find the first power of two not smaller than size */
align = page_shift;
while ((1u << align) < size) align++;
signal_stack_mask = (1 << align) - 1;
signal_stack_size = (1 << align) - teb_size;
/* try to find space in a reserved area for the views and pages protection table */
#ifdef _WIN64
pages_vprot_size = ((size_t)address_space_limit >> page_shift >> pages_vprot_shift) + 1;
@ -2566,6 +2556,19 @@ static void init_teb( TEB *teb, PEB *peb )
{
struct ntdll_thread_data *thread_data = (struct ntdll_thread_data *)&teb->GdiTebBatch;
#ifndef _WIN64
TEB64 *teb64 = (TEB64 *)((char *)teb - teb_offset);
teb64->Peb = PtrToUlong( (char *)peb + page_size );
teb64->Tib.Self = PtrToUlong( teb64 );
teb64->Tib.ExceptionList = PtrToUlong( teb );
teb64->ActivationContextStackPointer = PtrToUlong( &teb64->ActivationContextStack );
teb64->ActivationContextStack.FrameListCache.Flink =
teb64->ActivationContextStack.FrameListCache.Blink =
PtrToUlong( &teb64->ActivationContextStack.FrameListCache );
teb64->StaticUnicodeString.Buffer = PtrToUlong( teb64->StaticUnicodeBuffer );
teb64->StaticUnicodeString.MaximumLength = sizeof( teb64->StaticUnicodeBuffer );
#endif
teb->Peb = peb;
teb->Tib.Self = &teb->Tib;
teb->Tib.ExceptionList = (void *)~0ul;
@ -2589,10 +2592,11 @@ TEB *virtual_alloc_first_teb(void)
{
TEB *teb;
PEB *peb;
void *ptr;
NTSTATUS status;
SIZE_T data_size = page_size;
SIZE_T peb_size = page_size;
SIZE_T block_size = signal_stack_size + teb_size;
SIZE_T peb_size = page_size * (is_win64 ? 1 : 2);
SIZE_T block_size = signal_stack_mask + 1;
SIZE_T total = 32 * block_size;
/* reserve space for shared user data */
@ -2604,12 +2608,13 @@ TEB *virtual_alloc_first_teb(void)
exit(1);
}
NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&teb_block, 0, &total,
NtAllocateVirtualMemory( NtCurrentProcess(), &teb_block, 0, &total,
MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE );
teb_block_pos = 30;
teb = (TEB *)((char *)teb_block + 30 * block_size);
ptr = ((char *)teb_block + 30 * block_size);
teb = (TEB *)((char *)ptr + teb_offset);
peb = (PEB *)((char *)teb_block + 32 * block_size - peb_size);
NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&teb, 0, &block_size, MEM_COMMIT, PAGE_READWRITE );
NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&ptr, 0, &block_size, MEM_COMMIT, PAGE_READWRITE );
NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&peb, 0, &peb_size, MEM_COMMIT, PAGE_READWRITE );
init_teb( teb, peb );
*(ULONG_PTR *)peb->Reserved = get_image_address();
@ -2623,46 +2628,46 @@ TEB *virtual_alloc_first_teb(void)
NTSTATUS virtual_alloc_teb( TEB **ret_teb )
{
sigset_t sigset;
TEB *teb = NULL;
TEB *teb;
void *ptr = NULL;
NTSTATUS status = STATUS_SUCCESS;
SIZE_T teb_size = signal_stack_mask + 1;
SIZE_T block_size = signal_stack_mask + 1;
server_enter_uninterrupted_section( &virtual_mutex, &sigset );
if (next_free_teb)
{
teb = next_free_teb;
next_free_teb = *(TEB **)teb;
memset( teb, 0, sizeof(*teb) );
ptr = next_free_teb;
next_free_teb = *(void **)ptr;
memset( ptr, 0, teb_size );
}
else
{
if (!teb_block_pos)
{
void *addr = NULL;
SIZE_T total = 32 * teb_size;
SIZE_T total = 32 * block_size;
if ((status = NtAllocateVirtualMemory( NtCurrentProcess(), &addr, 0, &total,
if ((status = NtAllocateVirtualMemory( NtCurrentProcess(), &ptr, 0, &total,
MEM_RESERVE, PAGE_READWRITE )))
{
server_leave_uninterrupted_section( &virtual_mutex, &sigset );
return status;
}
teb_block = addr;
teb_block = ptr;
teb_block_pos = 32;
}
teb = (TEB *)((char *)teb_block + --teb_block_pos * teb_size);
NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&teb, 0, &teb_size,
ptr = ((char *)teb_block + --teb_block_pos * block_size);
NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&ptr, 0, &block_size,
MEM_COMMIT, PAGE_READWRITE );
}
*ret_teb = teb = (TEB *)((char *)ptr + teb_offset);
init_teb( teb, NtCurrentTeb()->Peb );
*ret_teb = teb;
server_leave_uninterrupted_section( &virtual_mutex, &sigset );
if ((status = signal_alloc_thread( teb )))
{
server_enter_uninterrupted_section( &virtual_mutex, &sigset );
*(TEB **)teb = next_free_teb;
next_free_teb = teb;
*(void **)ptr = next_free_teb;
next_free_teb = ptr;
server_leave_uninterrupted_section( &virtual_mutex, &sigset );
}
return status;
@ -2675,6 +2680,7 @@ NTSTATUS virtual_alloc_teb( TEB **ret_teb )
void virtual_free_teb( TEB *teb )
{
struct ntdll_thread_data *thread_data = (struct ntdll_thread_data *)&teb->GdiTebBatch;
void *ptr;
SIZE_T size;
sigset_t sigset;
@ -2692,8 +2698,9 @@ void virtual_free_teb( TEB *teb )
server_enter_uninterrupted_section( &virtual_mutex, &sigset );
list_remove( &thread_data->entry );
*(TEB **)teb = next_free_teb;
next_free_teb = teb;
ptr = (char *)teb - teb_offset;
*(void **)ptr = next_free_teb;
next_free_teb = ptr;
server_leave_uninterrupted_section( &virtual_mutex, &sigset );
}