From 5dc6d959526bc4854361c240408edc7a7075e81c Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 18 Dec 2005 10:45:01 +0100 Subject: [PATCH] ntdll: New implementation of relay thunks. New implementation of relay thunks that doesn't require modifying code on the fly, so that the thunks can be put in the text section. Some performance improvements. --- dlls/ntdll/loader.c | 4 +- dlls/ntdll/ntdll_misc.h | 2 +- dlls/ntdll/relay.c | 505 +++++++++++++++++---------------------- tools/winebuild/build.h | 2 + tools/winebuild/import.c | 3 +- tools/winebuild/spec32.c | 181 ++++++++++---- tools/winebuild/utils.c | 9 + 7 files changed, 367 insertions(+), 339 deletions(-) diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 99b4ccf0ea2..58da86fdddb 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -362,7 +362,7 @@ static FARPROC find_forwarded_export( HMODULE module, const char *forward ) * The loader_section must be locked while calling this function. */ static FARPROC find_ordinal_export( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports, - DWORD exp_size, int ordinal ) + DWORD exp_size, DWORD ordinal ) { FARPROC proc; const DWORD *functions = get_rva( module, exports->AddressOfFunctions ); @@ -389,7 +389,7 @@ static FARPROC find_ordinal_export( HMODULE module, const IMAGE_EXPORT_DIRECTORY if (TRACE_ON(relay)) { const WCHAR *user = current_modref ? current_modref->ldr.BaseDllName.Buffer : NULL; - proc = RELAY_GetProcAddress( module, exports, exp_size, proc, user ); + proc = RELAY_GetProcAddress( module, exports, exp_size, proc, ordinal, user ); } return proc; } diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index 47329171e5c..322a2799a80 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -60,7 +60,7 @@ extern void DECLSPEC_NORETURN server_abort_thread( int status ); /* module handling */ extern FARPROC RELAY_GetProcAddress( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports, - DWORD exp_size, FARPROC proc, const WCHAR *user ); + DWORD exp_size, FARPROC proc, DWORD ordinal, const WCHAR *user ); extern FARPROC SNOOP_GetProcAddress( HMODULE hmod, const IMAGE_EXPORT_DIRECTORY *exports, DWORD exp_size, FARPROC origfun, DWORD ordinal, const WCHAR *user ); extern void RELAY_SetupDLL( HMODULE hmod ); diff --git a/dlls/ntdll/relay.c b/dlls/ntdll/relay.c index 7401ab81855..454c0abd01e 100644 --- a/dlls/ntdll/relay.c +++ b/dlls/ntdll/relay.c @@ -41,6 +41,35 @@ WINE_DECLARE_DEBUG_CHANNEL(seh); #ifdef __i386__ +struct relay_descr /* descriptor for a module */ +{ + void *magic; /* signature */ + void *relay_from_32; /* functions to call from relay thunks */ + void *relay_from_32_regs; + void *private; /* reserved for the relay code private data */ + const char *entry_point_base; /* base address of entry point thunks */ + const unsigned int *entry_point_offsets; /* offsets of entry points thunks */ + const unsigned int *arg_types; /* table of argument types for all entry points */ +}; + +#define RELAY_DESCR_MAGIC ((void *)0xdeb90001) + +/* private data built at dll load time */ + +struct relay_entry_point +{ + void *orig_func; /* original entry point function */ + const char *name; /* function name (if any) */ +}; + +struct relay_private_data +{ + HMODULE module; /* module handle of this dll */ + unsigned int base; /* ordinal base */ + char dllname[40]; /* dll name (without .dll extension) */ + struct relay_entry_point entry_points[1]; /* list of dll entry points */ +}; + static const WCHAR **debug_relay_excludelist; static const WCHAR **debug_relay_includelist; static const WCHAR **debug_snoop_excludelist; @@ -208,70 +237,6 @@ static void init_debug_lists(void) } -#include "pshpack1.h" - -typedef struct -{ - BYTE call; /* 0xe8 call callfrom32 (relative) */ - DWORD callfrom32; /* RELAY_CallFrom32 relative addr */ - BYTE ret; /* 0xc2 ret $n or 0xc3 ret */ - WORD args; /* nb of args to remove from the stack */ - void *orig; /* original entry point */ - DWORD argtypes; /* argument types */ -} DEBUG_ENTRY_POINT; - -typedef struct -{ - /* code part */ - BYTE lcall; /* 0xe8 call snoopentry (relative) */ - /* NOTE: If you move snoopentry OR nrofargs fix the relative offset - * calculation! - */ - DWORD snoopentry; /* SNOOP_Entry relative */ - /* unreached */ - int nrofargs; - FARPROC origfun; - const char *name; -} SNOOP_FUN; - -typedef struct tagSNOOP_DLL { - HMODULE hmod; - SNOOP_FUN *funs; - DWORD ordbase; - DWORD nrofordinals; - struct tagSNOOP_DLL *next; - char name[1]; -} SNOOP_DLL; - -typedef struct -{ - /* code part */ - BYTE lcall; /* 0xe8 call snoopret relative*/ - /* NOTE: If you move snoopret OR origreturn fix the relative offset - * calculation! - */ - DWORD snoopret; /* SNOOP_Ret relative */ - /* unreached */ - FARPROC origreturn; - SNOOP_DLL *dll; - DWORD ordinal; - DWORD origESP; - DWORD *args; /* saved args across a stdcall */ -} SNOOP_RETURNENTRY; - -typedef struct tagSNOOP_RETURNENTRIES { - SNOOP_RETURNENTRY entry[4092/sizeof(SNOOP_RETURNENTRY)]; - struct tagSNOOP_RETURNENTRIES *next; -} SNOOP_RETURNENTRIES; - -#include "poppack.h" - -extern void WINAPI SNOOP_Entry(void); -extern void WINAPI SNOOP_Return(void); - -static SNOOP_DLL *firstdll; -static SNOOP_RETURNENTRIES *firstrets; - /*********************************************************************** * check_list * @@ -352,72 +317,6 @@ static BOOL check_from_module( const WCHAR **includelist, const WCHAR **excludel return show; } -/*********************************************************************** - * find_exported_name - * - * Find the name of an exported function. - */ -static const char *find_exported_name( HMODULE module, - IMAGE_EXPORT_DIRECTORY *exp, int ordinal ) -{ - unsigned int i; - const char *ret = NULL; - - WORD *ordptr = (WORD *)((char *)module + exp->AddressOfNameOrdinals); - for (i = 0; i < exp->NumberOfNames; i++, ordptr++) - if (*ordptr + exp->Base == ordinal) break; - if (i < exp->NumberOfNames) - ret = (char *)module + ((DWORD*)((char *)module + exp->AddressOfNames))[i]; - return ret; -} - - -/*********************************************************************** - * get_entry_point - * - * Get the name of the DLL entry point corresponding to a relay address. - */ -static void get_entry_point( char *buffer, DEBUG_ENTRY_POINT *relay ) -{ - IMAGE_EXPORT_DIRECTORY *exp = NULL; - DEBUG_ENTRY_POINT *debug; - char *p; - const char *name; - int ordinal = 0; - PLIST_ENTRY mark, entry; - PLDR_MODULE mod = NULL; - DWORD size; - - /* First find the module */ - - mark = &NtCurrentTeb()->Peb->LdrData->InLoadOrderModuleList; - for (entry = mark->Flink; entry != mark; entry = entry->Flink) - { - mod = CONTAINING_RECORD(entry, LDR_MODULE, InLoadOrderModuleList); - if (!(mod->Flags & LDR_WINE_INTERNAL)) continue; - exp = RtlImageDirectoryEntryToData( mod->BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size ); - if (!exp) continue; - debug = (DEBUG_ENTRY_POINT *)((char *)exp + size); - if (debug <= relay && relay < debug + exp->NumberOfFunctions) - { - ordinal = relay - debug; - break; - } - } - - /* Now find the function */ - - strcpy( buffer, (char *)mod->BaseAddress + exp->Name ); - p = buffer + strlen(buffer); - if (p > buffer + 4 && !strcasecmp( p - 4, ".dll" )) p -= 4; - - if ((name = find_exported_name( mod->BaseAddress, exp, ordinal + exp->Base ))) - sprintf( p, ".%s", name ); - else - sprintf( p, ".%ld", ordinal + exp->Base ); -} - - /*********************************************************************** * RELAY_PrintArgs */ @@ -464,158 +363,115 @@ __ASM_GLOBAL_FUNC( call_entry_point, /*********************************************************************** - * RELAY_CallFrom32 + * relay_call_from_32 * - * Stack layout on entry to this function: - * ... ... - * (esp+12) arg2 - * (esp+8) arg1 - * (esp+4) ret_addr - * (esp) return addr to relay code + * stack points to the return address, i.e. the first argument is stack[1]. */ -static LONGLONG RELAY_CallFrom32( int ret_addr, ... ) +static LONGLONG WINAPI relay_call_from_32( struct relay_descr *descr, unsigned int idx, int *stack ) { LONGLONG ret; - char buffer[80]; + WORD ordinal = LOWORD(idx); + BYTE nb_args = LOBYTE(HIWORD(idx)); + BYTE flags = HIBYTE(HIWORD(idx)); + struct relay_private_data *data = descr->private; + struct relay_entry_point *entry_point = data->entry_points + ordinal; - int *args = &ret_addr + 1; - /* Relay addr is the return address for this function */ - BYTE *relay_addr = (BYTE *)__builtin_return_address(0); - DEBUG_ENTRY_POINT *relay = (DEBUG_ENTRY_POINT *)(relay_addr - 5); - WORD nb_args = relay->args / sizeof(int); - - if (TRACE_ON(relay)) + if (!TRACE_ON(relay)) + ret = call_entry_point( entry_point->orig_func, nb_args, stack + 1 ); + else { - get_entry_point( buffer, relay ); - - DPRINTF( "%04lx:Call %s(", GetCurrentThreadId(), buffer ); - RELAY_PrintArgs( args, nb_args, relay->argtypes ); - DPRINTF( ") ret=%08x\n", ret_addr ); - } - - ret = call_entry_point( relay->orig, nb_args, args ); - - if (TRACE_ON(relay)) - { - BOOL ret64 = (relay->argtypes & 0x80000000) && (nb_args < 16); - if (ret64) - DPRINTF( "%04lx:Ret %s() retval=%08x%08x ret=%08x\n", - GetCurrentThreadId(), - buffer, (UINT)(ret >> 32), (UINT)ret, ret_addr ); + if (entry_point->name) + DPRINTF( "%04lx:Call %s.%s(", GetCurrentThreadId(), data->dllname, entry_point->name ); else - DPRINTF( "%04lx:Ret %s() retval=%08x ret=%08x\n", - GetCurrentThreadId(), - buffer, (UINT)ret, ret_addr ); + DPRINTF( "%04lx:Call %s.%u(", GetCurrentThreadId(), data->dllname, data->base + ordinal ); + RELAY_PrintArgs( stack + 1, nb_args, descr->arg_types[ordinal] ); + DPRINTF( ") ret=%08x\n", stack[0] ); + + ret = call_entry_point( entry_point->orig_func, nb_args, stack + 1 ); + + if (entry_point->name) + DPRINTF( "%04lx:Ret %s.%s()", GetCurrentThreadId(), data->dllname, entry_point->name ); + else + DPRINTF( "%04lx:Ret %s.%u()", GetCurrentThreadId(), data->dllname, data->base + ordinal ); + + if (flags & 1) /* 64-bit return value */ + DPRINTF( " retval=%08x%08x ret=%08x\n", + (UINT)(ret >> 32), (UINT)ret, stack[0] ); + else + DPRINTF( " retval=%08x ret=%08x\n", (UINT)ret, stack[0] ); } return ret; } /*********************************************************************** - * RELAY_CallFrom32Regs - * - * Stack layout (esp is context->Esp, not the current %esp): - * - * ... - * (esp+4) first arg - * (esp) return addr to caller - * (esp-4) return addr to DEBUG_ENTRY_POINT - * (esp-8) saved %eax - * (esp-12) ptr to relay entry code for RELAY_CallFrom32Regs - * ... >128 bytes space free to be modified (ensured by the assembly glue) + * relay_call_from_32_regs */ -void WINAPI __regs_RELAY_CallFrom32Regs( CONTEXT86 *context ) +void WINAPI __regs_relay_call_from_32_regs( struct relay_descr *descr, unsigned int idx, + unsigned int orig_eax, unsigned int ret_addr, + CONTEXT86 *context ) { - char buffer[80]; - int* args; - int args_copy[17]; - BYTE *entry_point; + WORD ordinal = LOWORD(idx); + BYTE nb_args = LOBYTE(HIWORD(idx)); + BYTE flags = HIBYTE(HIWORD(idx)); + struct relay_private_data *data = descr->private; + struct relay_entry_point *entry_point = data->entry_points + ordinal; + BYTE *orig_func = entry_point->orig_func; + int *args = (int *)context->Esp; + int args_copy[32]; - BYTE *relay_addr = *((BYTE **)context->Esp - 1); - DEBUG_ENTRY_POINT *relay = (DEBUG_ENTRY_POINT *)(relay_addr - 5); - WORD nb_args = relay->args / sizeof(int); - - /* remove extra stuff from the stack */ - context->Eip = *(DWORD *)context->Esp; - context->Esp += sizeof(DWORD); - args = (int *)context->Esp; - if (relay->ret == 0xc2) /* stdcall */ + /* restore the context to what it was before the relay thunk */ + context->Eax = orig_eax; + context->Eip = ret_addr; + if (flags & 2) /* stdcall */ context->Esp += nb_args * sizeof(int); - entry_point = (BYTE *)relay->orig; - assert( entry_point[0] == 0x50 /* pushl %eax */ ); - assert( entry_point[1] == 0xe8 /* call */ ); - if (TRACE_ON(relay)) { - get_entry_point( buffer, relay ); + if (entry_point->name) + DPRINTF( "%04lx:Call %s.%s(", GetCurrentThreadId(), data->dllname, entry_point->name ); + else + DPRINTF( "%04lx:Call %s.%u(", GetCurrentThreadId(), data->dllname, data->base + ordinal ); + RELAY_PrintArgs( args, nb_args, descr->arg_types[ordinal] ); + DPRINTF( ") ret=%08x\n", ret_addr ); - DPRINTF( "%04lx:Call %s(", GetCurrentThreadId(), buffer ); - RELAY_PrintArgs( args, nb_args, relay->argtypes ); - DPRINTF( ") ret=%08lx fs=%04lx\n", context->Eip, context->SegFs ); + DPRINTF( "%04lx: eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx " + "ebp=%08lx esp=%08lx ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\n", + GetCurrentThreadId(), context->Eax, context->Ebx, context->Ecx, + context->Edx, context->Esi, context->Edi, context->Ebp, context->Esp, + context->SegDs, context->SegEs, context->SegFs, context->SegGs, context->EFlags ); - DPRINTF(" eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx\n", - context->Eax, context->Ebx, context->Ecx, - context->Edx, context->Esi, context->Edi ); - DPRINTF(" ebp=%08lx esp=%08lx ds=%04lx es=%04lx gs=%04lx flags=%08lx\n", - context->Ebp, context->Esp, context->SegDs, - context->SegEs, context->SegGs, context->EFlags ); + assert( orig_func[0] == 0x50 /* pushl %eax */ ); + assert( orig_func[1] == 0xe8 /* call */ ); } - /* Now call the real function */ + /* now call the real function */ memcpy( args_copy, args, nb_args * sizeof(args[0]) ); - args_copy[nb_args] = (int)context; /* append context argument */ + args_copy[nb_args++] = (int)context; /* append context argument */ + + call_entry_point( orig_func + 6 + *(int *)(orig_func + 6), nb_args, args_copy ); - call_entry_point( (entry_point + 6 + *(DWORD *)(entry_point + 6)), nb_args+1, args_copy ); if (TRACE_ON(relay)) { - DPRINTF( "%04lx:Ret %s() retval=%08lx ret=%08lx fs=%04lx\n", - GetCurrentThreadId(), - buffer, context->Eax, context->Eip, context->SegFs ); - - DPRINTF(" eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx\n", - context->Eax, context->Ebx, context->Ecx, - context->Edx, context->Esi, context->Edi ); - DPRINTF(" ebp=%08lx esp=%08lx ds=%04lx es=%04lx gs=%04lx flags=%08lx\n", - context->Ebp, context->Esp, context->SegDs, - context->SegEs, context->SegGs, context->EFlags ); + if (entry_point->name) + DPRINTF( "%04lx:Ret %s.%s() retval=%08lx ret=%08lx\n", + GetCurrentThreadId(), data->dllname, entry_point->name, + context->Eax, context->Eip ); + else + DPRINTF( "%04lx:Ret %s.%u() retval=%08lx ret=%08lx\n", + GetCurrentThreadId(), data->dllname, data->base + ordinal, + context->Eax, context->Eip ); + DPRINTF( "%04lx: eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx " + "ebp=%08lx esp=%08lx ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\n", + GetCurrentThreadId(), context->Eax, context->Ebx, context->Ecx, + context->Edx, context->Esi, context->Edi, context->Ebp, context->Esp, + context->SegDs, context->SegEs, context->SegFs, context->SegGs, context->EFlags ); } } - -void WINAPI RELAY_CallFrom32Regs(void); -DEFINE_REGS_ENTRYPOINT( RELAY_CallFrom32Regs, 0, 0 ); - -/* check whether the function at addr starts with a call to __wine_call_from_32_regs */ -static BOOL is_register_entry_point( const BYTE *addr ) -{ - extern void __wine_call_from_32_regs(); - const int *offset; - const void *ptr; - - if (addr[0] != 0x50) return FALSE; /* pushl %eax */ - if (addr[1] != 0xe8) return FALSE; /* call */ - /* check if call target is __wine_call_from_32_regs */ - offset = (const int *)(addr + 2); - if (*offset == (const char *)__wine_call_from_32_regs - (const char *)(offset + 1)) return TRUE; - /* now check if call target is an import table jump to __wine_call_from_32_regs */ - addr = (const BYTE *)(offset + 1) + *offset; - - /* Note: the following checks depend on the asm code generated by winebuild */ - - if (addr[0] == 0xff && addr[1] == 0x25) /* indirect jmp */ - { - ptr = *(const void * const*)(addr + 2); /* get indirect jmp target address */ - } - else /* check for import thunk */ - { - if (addr[0] != 0xe8) return FALSE; /* call get_pc_thunk */ - if (addr[5] != 0xff || addr[6] != 0xa0) return FALSE; /* jmp *offset(%eax) */ - ptr = addr + 5 + *(const int *)(addr + 7); - } - return (*(const char * const*)ptr == (char *)__wine_call_from_32_regs); -} +extern void WINAPI relay_call_from_32_regs(void); +DEFINE_REGS_ENTRYPOINT( relay_call_from_32_regs, 16, 16 ); /*********************************************************************** @@ -624,18 +480,16 @@ static BOOL is_register_entry_point( const BYTE *addr ) * Return the proc address to use for a given function. */ FARPROC RELAY_GetProcAddress( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports, - DWORD exp_size, FARPROC proc, const WCHAR *user ) + DWORD exp_size, FARPROC proc, DWORD ordinal, const WCHAR *user ) { - const DEBUG_ENTRY_POINT *debug = (DEBUG_ENTRY_POINT *)proc; - const DEBUG_ENTRY_POINT *list = (const DEBUG_ENTRY_POINT *)((const char *)exports + exp_size); + struct relay_private_data *data; + const struct relay_descr *descr = (const struct relay_descr *)((const char *)exports + exp_size); - if (debug < list || debug >= list + exports->NumberOfFunctions) return proc; - if (list + (debug - list) != debug) return proc; /* not a valid address */ + if (descr->magic != RELAY_DESCR_MAGIC || !(data = descr->private)) return proc; /* no relay data */ + if (!data->entry_points[ordinal].orig_func) return proc; /* not a relayed function */ if (check_from_module( debug_from_relay_includelist, debug_from_relay_excludelist, user )) - return proc; /* we want to relay it */ - if (!debug->call) return proc; /* not a normal function */ - if (debug->call != 0xe8 && debug->call != 0xe9) return proc; /* not a debug thunk at all */ - return debug->orig; + return proc; /* we want to relay it */ + return data->entry_points[ordinal].orig_func; } @@ -647,51 +501,122 @@ FARPROC RELAY_GetProcAddress( HMODULE module, const IMAGE_EXPORT_DIRECTORY *expo void RELAY_SetupDLL( HMODULE module ) { IMAGE_EXPORT_DIRECTORY *exports; - DEBUG_ENTRY_POINT *debug; DWORD *funcs; - unsigned int i; - const char *name; - char *p, dllname[80]; - DWORD size; + unsigned int i, len; + DWORD size, entry_point_rva; + struct relay_descr *descr; + struct relay_private_data *data; + const WORD *ordptr; if (!init_done) init_debug_lists(); exports = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size ); if (!exports) return; - debug = (DEBUG_ENTRY_POINT *)((char *)exports + size); - funcs = (DWORD *)((char *)module + exports->AddressOfFunctions); - strcpy( dllname, (char *)module + exports->Name ); - p = dllname + strlen(dllname) - 4; - if (p > dllname && !strcasecmp( p, ".dll" )) *p = 0; - for (i = 0; i < exports->NumberOfFunctions; i++, funcs++, debug++) + descr = (struct relay_descr *)((char *)exports + size); + if (descr->magic != RELAY_DESCR_MAGIC) return; + + if (!(data = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data) + + (exports->NumberOfFunctions-1) * sizeof(data->entry_points) ))) + return; + + descr->relay_from_32 = relay_call_from_32; + descr->relay_from_32_regs = relay_call_from_32_regs; + descr->private = data; + + data->module = module; + data->base = exports->Base; + len = strlen( (char *)module + exports->Name ); + if (len > 4 && !strcasecmp( (char *)module + exports->Name + len - 4, ".dll" )) len -= 4; + len = min( len, sizeof(data->dllname) - 1 ); + memcpy( data->dllname, (char *)module + exports->Name, len ); + data->dllname[len] = 0; + + /* fetch name pointer for all entry points and store them in the private structure */ + + ordptr = (const WORD *)((char *)module + exports->AddressOfNameOrdinals); + for (i = 0; i < exports->NumberOfNames; i++, ordptr++) { - int on = 1; + DWORD name_rva = ((DWORD*)((char *)module + exports->AddressOfNames))[i]; + data->entry_points[*ordptr].name = (const char *)module + name_rva; + } - if (!debug->call) continue; /* not a normal function */ - if (debug->call != 0xe8 && debug->call != 0xe9) break; /* not a debug thunk at all */ + /* patch the functions in the export table to point to the relay thunks */ - name = find_exported_name( module, exports, i + exports->Base ); - on = check_relay_include( dllname, i + exports->Base, name ); + funcs = (DWORD *)((char *)module + exports->AddressOfFunctions); + entry_point_rva = (const char *)descr->entry_point_base - (const char *)module; + for (i = 0; i < exports->NumberOfFunctions; i++, funcs++) + { + if (!descr->entry_point_offsets[i]) continue; /* not a normal function */ + if (!check_relay_include( data->dllname, i + exports->Base, data->entry_points[i].name )) + continue; /* don't include this entry point */ - if (on) - { - debug->call = 0xe8; /* call relative */ - if (is_register_entry_point( debug->orig )) - debug->callfrom32 = (char *)RELAY_CallFrom32Regs - (char *)&debug->ret; - else - debug->callfrom32 = (char *)RELAY_CallFrom32 - (char *)&debug->ret; - } - else - { - debug->call = 0xe9; /* jmp relative */ - debug->callfrom32 = (char *)debug->orig - (char *)&debug->ret; - } - *funcs = (char *)debug - (char *)module; + data->entry_points[i].orig_func = (char *)module + *funcs; + *funcs = entry_point_rva + descr->entry_point_offsets[i]; } } + +/***********************************************************************/ +/* snoop support */ +/***********************************************************************/ + +#include "pshpack1.h" + +typedef struct +{ + /* code part */ + BYTE lcall; /* 0xe8 call snoopentry (relative) */ + /* NOTE: If you move snoopentry OR nrofargs fix the relative offset + * calculation! + */ + DWORD snoopentry; /* SNOOP_Entry relative */ + /* unreached */ + int nrofargs; + FARPROC origfun; + const char *name; +} SNOOP_FUN; + +typedef struct tagSNOOP_DLL { + HMODULE hmod; + SNOOP_FUN *funs; + DWORD ordbase; + DWORD nrofordinals; + struct tagSNOOP_DLL *next; + char name[1]; +} SNOOP_DLL; + +typedef struct +{ + /* code part */ + BYTE lcall; /* 0xe8 call snoopret relative*/ + /* NOTE: If you move snoopret OR origreturn fix the relative offset + * calculation! + */ + DWORD snoopret; /* SNOOP_Ret relative */ + /* unreached */ + FARPROC origreturn; + SNOOP_DLL *dll; + DWORD ordinal; + DWORD origESP; + DWORD *args; /* saved args across a stdcall */ +} SNOOP_RETURNENTRY; + +typedef struct tagSNOOP_RETURNENTRIES { + SNOOP_RETURNENTRY entry[4092/sizeof(SNOOP_RETURNENTRY)]; + struct tagSNOOP_RETURNENTRIES *next; +} SNOOP_RETURNENTRIES; + +#include "poppack.h" + +extern void WINAPI SNOOP_Entry(void); +extern void WINAPI SNOOP_Return(void); + +static SNOOP_DLL *firstdll; +static SNOOP_RETURNENTRIES *firstrets; + + /*********************************************************************** * SNOOP_ShowDebugmsgSnoop * @@ -1023,7 +948,7 @@ DEFINE_REGS_ENTRYPOINT( SNOOP_Return, 0, 0 ); #else /* __i386__ */ FARPROC RELAY_GetProcAddress( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports, - DWORD exp_size, FARPROC proc, const WCHAR *user ) + DWORD exp_size, FARPROC proc, DWORD ordinal, const WCHAR *user ) { return proc; } diff --git a/tools/winebuild/build.h b/tools/winebuild/build.h index 272f38efe15..ec4d9a98201 100644 --- a/tools/winebuild/build.h +++ b/tools/winebuild/build.h @@ -189,6 +189,7 @@ extern const char *asm_globl( const char *func ); extern const char *get_asm_ptr_keyword(void); extern const char *get_asm_string_keyword(void); extern const char *get_asm_short_keyword(void); +extern const char *get_asm_rodata_section(void); extern const char *get_asm_string_section(void); extern void output_function_size( FILE *outfile, const char *name ); @@ -199,6 +200,7 @@ extern void add_extra_ld_symbol( const char *name ); extern void read_undef_symbols( DLLSPEC *spec, char **argv ); extern int resolve_imports( DLLSPEC *spec ); extern int has_imports(void); +extern int has_relays( DLLSPEC *spec ); extern void output_get_pc_thunk( FILE *outfile ); extern void output_stubs( FILE *outfile, DLLSPEC *spec ); extern void output_imports( FILE *outfile, DLLSPEC *spec ); diff --git a/tools/winebuild/import.c b/tools/winebuild/import.c index c90bb7b1f4b..80c3d25033e 100644 --- a/tools/winebuild/import.c +++ b/tools/winebuild/import.c @@ -1241,5 +1241,6 @@ void output_imports( FILE *outfile, DLLSPEC *spec ) output_immediate_import_thunks( outfile ); output_delayed_import_thunks( outfile, spec ); output_external_link_imports( outfile, spec ); - if (nb_imports || ext_link_imports.count || has_stubs(spec)) output_get_pc_thunk( outfile ); + if (nb_imports || ext_link_imports.count || has_stubs(spec) || has_relays(spec)) + output_get_pc_thunk( outfile ); } diff --git a/tools/winebuild/spec32.c b/tools/winebuild/spec32.c index 041cd5748cf..963e916b51b 100644 --- a/tools/winebuild/spec32.c +++ b/tools/winebuild/spec32.c @@ -36,6 +36,128 @@ #include "build.h" +/* check if entry point needs a relay thunk */ +static inline int needs_relay( const ORDDEF *odp ) +{ + /* skip nonexistent entry points */ + if (!odp) return 0; + /* skip non-functions */ + if ((odp->type != TYPE_STDCALL) && (odp->type != TYPE_CDECL)) return 0; + /* skip norelay and forward entry points */ + if (odp->flags & (FLAG_NORELAY|FLAG_FORWARD)) return 0; + return 1; +} + +/* check if dll will output relay thunks */ +int has_relays( DLLSPEC *spec ) +{ + unsigned int i; + + if (target_cpu != CPU_x86) return 0; + + for (i = spec->base; i <= spec->limit; i++) + { + ORDDEF *odp = spec->ordinals[i]; + if (needs_relay( odp )) return 1; + } + return 0; +} + +/******************************************************************* + * output_relay_debug + * + * Output entry points for relay debugging + */ +static void output_relay_debug( FILE *outfile, DLLSPEC *spec ) +{ + unsigned int i, j, args, flags; + + /* first the table of entry point offsets */ + + fprintf( outfile, "\t%s\n", get_asm_rodata_section() ); + fprintf( outfile, "\t.align %d\n", get_alignment(4) ); + fprintf( outfile, ".L__wine_spec_relay_entry_point_offsets:\n" ); + + for (i = spec->base; i <= spec->limit; i++) + { + ORDDEF *odp = spec->ordinals[i]; + + if (needs_relay( odp )) + fprintf( outfile, "\t.long .L__wine_spec_relay_entry_point_%d-__wine_spec_relay_entry_points\n", i ); + else + fprintf( outfile, "\t.long 0\n" ); + } + + /* then the table of argument types */ + + fprintf( outfile, "\t.align %d\n", get_alignment(4) ); + fprintf( outfile, ".L__wine_spec_relay_arg_types:\n" ); + + for (i = spec->base; i <= spec->limit; i++) + { + ORDDEF *odp = spec->ordinals[i]; + unsigned int mask = 0; + + if (needs_relay( odp )) + { + for (j = 0; j < 16 && odp->u.func.arg_types[j]; j++) + { + if (odp->u.func.arg_types[j] == 't') mask |= 1<< (j*2); + if (odp->u.func.arg_types[j] == 'W') mask |= 2<< (j*2); + } + } + fprintf( outfile, "\t.long 0x%08x\n", mask ); + } + + /* then the relay thunks */ + + fprintf( outfile, "\t.text\n" ); + fprintf( outfile, "__wine_spec_relay_entry_points:\n" ); + fprintf( outfile, "\tnop\n" ); /* to avoid 0 offset */ + + for (i = spec->base; i <= spec->limit; i++) + { + ORDDEF *odp = spec->ordinals[i]; + + if (!needs_relay( odp )) continue; + + fprintf( outfile, "\t.align %d\n", get_alignment(4) ); + fprintf( outfile, ".L__wine_spec_relay_entry_point_%d:\n", i ); + + if (odp->flags & FLAG_REGISTER) + fprintf( outfile, "\tpushl %%eax\n" ); + else + fprintf( outfile, "\tpushl %%esp\n" ); + + args = strlen(odp->u.func.arg_types); + flags = 0; + if (odp->flags & FLAG_RET64) flags |= 1; + if (odp->type == TYPE_STDCALL) flags |= 2; + fprintf( outfile, "\tpushl $%u\n", (flags << 24) | (args << 16) | (i - spec->base) ); + + if (UsePIC) + { + fprintf( outfile, "\tcall %s\n", asm_name("__wine_spec_get_pc_thunk_eax") ); + fprintf( outfile, "1:\tleal .L__wine_spec_relay_descr-1b(%%eax),%%eax\n" ); + } + else fprintf( outfile, "\tmovl $.L__wine_spec_relay_descr,%%eax\n" ); + fprintf( outfile, "\tpushl %%eax\n" ); + + if (odp->flags & FLAG_REGISTER) + { + fprintf( outfile, "\tcall *8(%%eax)\n" ); + } + else + { + fprintf( outfile, "\tcall *4(%%eax)\n" ); + if (odp->type == TYPE_STDCALL) + fprintf( outfile, "\tret $%u\n", args * get_ptr_size() ); + else + fprintf( outfile, "\tret\n" ); + } + } +} + /******************************************************************* * output_exports * @@ -158,58 +280,27 @@ static void output_exports( FILE *outfile, DLLSPEC *spec ) fprintf( outfile, "\t%s \"%s\"\n", get_asm_string_keyword(), odp->link_name ); } } - fprintf( outfile, "\t.align %d\n", get_alignment(4) ); + fprintf( outfile, "\t.align %d\n", get_alignment(get_ptr_size()) ); fprintf( outfile, ".L__wine_spec_exports_end:\n" ); /* output relays */ /* we only support relay debugging on i386 */ - if (target_cpu == CPU_x86) + if (target_cpu != CPU_x86) { - for (i = spec->base; i <= spec->limit; i++) - { - ORDDEF *odp = spec->ordinals[i]; - unsigned int j, args, mask = 0; - - /* skip nonexistent entry points */ - if (!odp) goto ignore; - /* skip non-functions */ - if ((odp->type != TYPE_STDCALL) && (odp->type != TYPE_CDECL)) goto ignore; - /* skip norelay and forward entry points */ - if (odp->flags & (FLAG_NORELAY|FLAG_FORWARD)) goto ignore; - - for (j = 0; odp->u.func.arg_types[j]; j++) - { - if (odp->u.func.arg_types[j] == 't') mask |= 1<< (j*2); - if (odp->u.func.arg_types[j] == 'W') mask |= 2<< (j*2); - } - if ((odp->flags & FLAG_RET64) && (j < 16)) mask |= 0x80000000; - - args = strlen(odp->u.func.arg_types) * get_ptr_size(); - - switch(odp->type) - { - case TYPE_STDCALL: - fprintf( outfile, "\tjmp %s\n", asm_name(odp->link_name) ); - fprintf( outfile, "\tret $%d\n", args ); - fprintf( outfile, "\t.long %s,0x%08x\n", asm_name(odp->link_name), mask ); - break; - case TYPE_CDECL: - fprintf( outfile, "\tjmp %s\n", asm_name(odp->link_name) ); - fprintf( outfile, "\tret\n" ); - fprintf( outfile, "\t%s %d\n", get_asm_short_keyword(), args ); - fprintf( outfile, "\t.long %s,0x%08x\n", asm_name(odp->link_name), mask ); - break; - default: - assert(0); - } - continue; - - ignore: - fprintf( outfile, "\t.long 0,0,0,0\n" ); - } + fprintf( outfile, "\t%s 0\n", get_asm_ptr_keyword() ); + return; } - else fprintf( outfile, "\t.long 0\n" ); + + fprintf( outfile, ".L__wine_spec_relay_descr:\n" ); + fprintf( outfile, "\t%s 0xdeb90001\n", get_asm_ptr_keyword() ); /* magic */ + fprintf( outfile, "\t%s 0,0\n", get_asm_ptr_keyword() ); /* relay funcs */ + fprintf( outfile, "\t%s 0\n", get_asm_ptr_keyword() ); /* private data */ + fprintf( outfile, "\t%s __wine_spec_relay_entry_points\n", get_asm_ptr_keyword() ); + fprintf( outfile, "\t%s .L__wine_spec_relay_entry_point_offsets\n", get_asm_ptr_keyword() ); + fprintf( outfile, "\t%s .L__wine_spec_relay_arg_types\n", get_asm_ptr_keyword() ); + + output_relay_debug( outfile, spec ); } diff --git a/tools/winebuild/utils.c b/tools/winebuild/utils.c index a4cbb6fd0aa..8fb3acce4b5 100644 --- a/tools/winebuild/utils.c +++ b/tools/winebuild/utils.c @@ -582,6 +582,15 @@ const char *get_asm_short_keyword(void) } } +const char *get_asm_rodata_section(void) +{ + switch (target_platform) + { + case PLATFORM_APPLE: return ".const"; + default: return ".section .rodata"; + } +} + const char *get_asm_string_section(void) { switch (target_platform)