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)