2 * Win32 relay and snoop functions
4 * Copyright 1997 Alexandre Julliard
5 * Copyright 1998 Marcus Meissner
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "wine/port.h"
35 #include "wine/exception.h"
36 #include "ntdll_misc.h"
37 #include "wine/unicode.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(relay);
41 WINE_DECLARE_DEBUG_CHANNEL(snoop);
42 WINE_DECLARE_DEBUG_CHANNEL(seh);
44 const WCHAR **debug_relay_excludelist = NULL;
45 const WCHAR **debug_relay_includelist = NULL;
46 const WCHAR **debug_snoop_excludelist = NULL;
47 const WCHAR **debug_snoop_includelist = NULL;
49 static const WCHAR **debug_from_relay_excludelist;
50 static const WCHAR **debug_from_relay_includelist;
52 /* compare an ASCII and a Unicode string without depending on the current codepage */
53 inline static int strcmpAW( const char *strA, const WCHAR *strW )
55 while (*strA && ((unsigned char)*strA == *strW)) { strA++; strW++; }
56 return (unsigned char)*strA - *strW;
59 /* compare an ASCII and a Unicode string without depending on the current codepage */
60 inline static int strncmpiAW( const char *strA, const WCHAR *strW, int n )
63 for ( ; n > 0; n--, strA++, strW++)
64 if ((ret = toupperW((unsigned char)*strA) - toupperW(*strW)) || !*strA) break;
68 /***********************************************************************
71 * Build a function list from a ';'-separated string.
73 static const WCHAR **build_list( const WCHAR *buffer )
76 const WCHAR *p = buffer;
79 while ((p = strchrW( p, ';' )))
84 /* allocate count+1 pointers, plus the space for a copy of the string */
85 if ((ret = RtlAllocateHeap( GetProcessHeap(), 0,
86 (count+1) * sizeof(WCHAR*) + (strlenW(buffer)+1) * sizeof(WCHAR) )))
88 WCHAR *str = (WCHAR *)(ret + count + 1);
91 strcpyW( str, buffer );
96 if (!(p = strchrW( p, ';' ))) break;
105 /***********************************************************************
106 * RELAY_InitDebugLists
108 * Build the relay include/exclude function lists.
110 void RELAY_InitDebugLists(void)
112 OBJECT_ATTRIBUTES attr;
118 static const WCHAR configW[] = {'M','a','c','h','i','n','e','\\',
119 'S','o','f','t','w','a','r','e','\\',
120 'W','i','n','e','\\',
121 'W','i','n','e','\\',
122 'C','o','n','f','i','g','\\',
123 'D','e','b','u','g',0};
124 static const WCHAR RelayIncludeW[] = {'R','e','l','a','y','I','n','c','l','u','d','e',0};
125 static const WCHAR RelayExcludeW[] = {'R','e','l','a','y','E','x','c','l','u','d','e',0};
126 static const WCHAR SnoopIncludeW[] = {'S','n','o','o','p','I','n','c','l','u','d','e',0};
127 static const WCHAR SnoopExcludeW[] = {'S','n','o','o','p','E','x','c','l','u','d','e',0};
128 static const WCHAR RelayFromIncludeW[] = {'R','e','l','a','y','F','r','o','m','I','n','c','l','u','d','e',0};
129 static const WCHAR RelayFromExcludeW[] = {'R','e','l','a','y','F','r','o','m','E','x','c','l','u','d','e',0};
131 attr.Length = sizeof(attr);
132 attr.RootDirectory = 0;
133 attr.ObjectName = &name;
135 attr.SecurityDescriptor = NULL;
136 attr.SecurityQualityOfService = NULL;
137 RtlInitUnicodeString( &name, configW );
139 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return;
141 str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)buffer)->Data;
142 RtlInitUnicodeString( &name, RelayIncludeW );
143 if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
145 TRACE("RelayInclude = %s\n", debugstr_w(str) );
146 debug_relay_includelist = build_list( str );
149 RtlInitUnicodeString( &name, RelayExcludeW );
150 if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
152 TRACE( "RelayExclude = %s\n", debugstr_w(str) );
153 debug_relay_excludelist = build_list( str );
156 RtlInitUnicodeString( &name, SnoopIncludeW );
157 if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
159 TRACE_(snoop)( "SnoopInclude = %s\n", debugstr_w(str) );
160 debug_snoop_includelist = build_list( str );
163 RtlInitUnicodeString( &name, SnoopExcludeW );
164 if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
166 TRACE_(snoop)( "SnoopExclude = %s\n", debugstr_w(str) );
167 debug_snoop_excludelist = build_list( str );
170 RtlInitUnicodeString( &name, RelayFromIncludeW );
171 if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
173 TRACE("RelayFromInclude = %s\n", debugstr_w(str) );
174 debug_from_relay_includelist = build_list( str );
177 RtlInitUnicodeString( &name, RelayFromExcludeW );
178 if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
180 TRACE( "RelayFromExclude = %s\n", debugstr_w(str) );
181 debug_from_relay_excludelist = build_list( str );
190 #include "pshpack1.h"
194 BYTE call; /* 0xe8 call callfrom32 (relative) */
195 DWORD callfrom32; /* RELAY_CallFrom32 relative addr */
196 BYTE ret; /* 0xc2 ret $n or 0xc3 ret */
197 WORD args; /* nb of args to remove from the stack */
198 void *orig; /* original entry point */
199 DWORD argtypes; /* argument types */
205 BYTE lcall; /* 0xe8 call snoopentry (relative) */
206 /* NOTE: If you move snoopentry OR nrofargs fix the relative offset
209 DWORD snoopentry; /* SNOOP_Entry relative */
216 typedef struct tagSNOOP_DLL {
221 struct tagSNOOP_DLL *next;
228 BYTE lcall; /* 0xe8 call snoopret relative*/
229 /* NOTE: If you move snoopret OR origreturn fix the relative offset
232 DWORD snoopret; /* SNOOP_Ret relative */
238 DWORD *args; /* saved args across a stdcall */
241 typedef struct tagSNOOP_RETURNENTRIES {
242 SNOOP_RETURNENTRY entry[4092/sizeof(SNOOP_RETURNENTRY)];
243 struct tagSNOOP_RETURNENTRIES *next;
244 } SNOOP_RETURNENTRIES;
248 extern void WINAPI SNOOP_Entry();
249 extern void WINAPI SNOOP_Return();
251 static SNOOP_DLL *firstdll;
252 static SNOOP_RETURNENTRIES *firstrets;
254 static WINE_EXCEPTION_FILTER(page_fault)
256 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ||
257 GetExceptionCode() == EXCEPTION_PRIV_INSTRUCTION)
258 return EXCEPTION_EXECUTE_HANDLER;
259 return EXCEPTION_CONTINUE_SEARCH;
262 /***********************************************************************
263 * check_relay_include
265 * Check if a given function must be included in the relay output.
267 static BOOL check_relay_include( const char *module, const char *func )
269 const WCHAR **listitem;
272 if (!debug_relay_excludelist && !debug_relay_includelist) return TRUE;
273 if (debug_relay_excludelist)
276 listitem = debug_relay_excludelist;
281 listitem = debug_relay_includelist;
283 for(; *listitem; listitem++)
285 WCHAR *p = strrchrW( *listitem, '.' );
286 if (p && p > *listitem) /* check module and function */
288 int len = p - *listitem;
289 if (strncmpiAW( module, *listitem, len-1 ) || module[len]) continue;
290 if (p[1] == '*' && !p[2]) return !show;
291 if (!strcmpAW( func, p + 1 )) return !show;
293 else /* function only */
295 if (!strcmpAW( func, *listitem )) return !show;
302 /***********************************************************************
303 * check_relay_from_module
305 * Check if calls from a given module must be included in the relay output.
307 static BOOL check_relay_from_module( const WCHAR *module )
309 static const WCHAR dllW[] = {'.','d','l','l',0 };
310 const WCHAR **listitem;
313 if (!debug_from_relay_excludelist && !debug_from_relay_includelist) return TRUE;
314 if (debug_from_relay_excludelist)
317 listitem = debug_from_relay_excludelist;
322 listitem = debug_from_relay_includelist;
324 for(; *listitem; listitem++)
328 if (!strcmpiW( *listitem, module )) return !show;
329 len = strlenW( *listitem );
330 if (!strncmpiW( *listitem, module, len ) && !strcmpiW( module + len, dllW ))
337 /***********************************************************************
340 * Find the name of an exported function.
342 static const char *find_exported_name( HMODULE module,
343 IMAGE_EXPORT_DIRECTORY *exp, int ordinal )
346 const char *ret = NULL;
348 WORD *ordptr = (WORD *)((char *)module + exp->AddressOfNameOrdinals);
349 for (i = 0; i < exp->NumberOfNames; i++, ordptr++)
350 if (*ordptr + exp->Base == ordinal) break;
351 if (i < exp->NumberOfNames)
352 ret = (char *)module + ((DWORD*)((char *)module + exp->AddressOfNames))[i];
357 /***********************************************************************
360 * Get the name of the DLL entry point corresponding to a relay address.
362 static void get_entry_point( char *buffer, DEBUG_ENTRY_POINT *relay )
364 IMAGE_EXPORT_DIRECTORY *exp = NULL;
365 DEBUG_ENTRY_POINT *debug;
369 PLIST_ENTRY mark, entry;
370 PLDR_MODULE mod = NULL;
373 /* First find the module */
375 mark = &NtCurrentTeb()->Peb->LdrData->InLoadOrderModuleList;
376 for (entry = mark->Flink; entry != mark; entry = entry->Flink)
378 mod = CONTAINING_RECORD(entry, LDR_MODULE, InLoadOrderModuleList);
379 if (!(mod->Flags & LDR_WINE_INTERNAL)) continue;
380 exp = RtlImageDirectoryEntryToData( mod->BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size );
382 debug = (DEBUG_ENTRY_POINT *)((char *)exp + size);
383 if (debug <= relay && relay < debug + exp->NumberOfFunctions)
385 ordinal = relay - debug;
390 /* Now find the function */
392 strcpy( buffer, (char *)mod->BaseAddress + exp->Name );
393 p = buffer + strlen(buffer);
394 if (p > buffer + 4 && !strcasecmp( p - 4, ".dll" )) p -= 4;
396 if ((name = find_exported_name( mod->BaseAddress, exp, ordinal + exp->Base )))
397 sprintf( p, ".%s", name );
399 sprintf( p, ".%ld", ordinal + exp->Base );
403 /***********************************************************************
406 static inline void RELAY_PrintArgs( int *args, int nb_args, unsigned int typemask )
410 if ((typemask & 3) && HIWORD(*args))
413 DPRINTF( "%08x %s", *args, debugstr_w((LPWSTR)*args) );
415 DPRINTF( "%08x %s", *args, debugstr_a((LPCSTR)*args) );
417 else DPRINTF( "%08x", *args );
418 if (nb_args) DPRINTF( "," );
425 typedef LONGLONG (*LONGLONG_CPROC)();
426 typedef LONGLONG (WINAPI *LONGLONG_FARPROC)();
429 /***********************************************************************
430 * call_cdecl_function
432 static LONGLONG call_cdecl_function( LONGLONG_CPROC func, int nb_args, const int *args )
437 case 0: ret = func(); break;
438 case 1: ret = func(args[0]); break;
439 case 2: ret = func(args[0],args[1]); break;
440 case 3: ret = func(args[0],args[1],args[2]); break;
441 case 4: ret = func(args[0],args[1],args[2],args[3]); break;
442 case 5: ret = func(args[0],args[1],args[2],args[3],args[4]); break;
443 case 6: ret = func(args[0],args[1],args[2],args[3],args[4],
445 case 7: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
447 case 8: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
448 args[6],args[7]); break;
449 case 9: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
450 args[6],args[7],args[8]); break;
451 case 10: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
452 args[6],args[7],args[8],args[9]); break;
453 case 11: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
454 args[6],args[7],args[8],args[9],args[10]); break;
455 case 12: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
456 args[6],args[7],args[8],args[9],args[10],
458 case 13: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
459 args[6],args[7],args[8],args[9],args[10],args[11],
461 case 14: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
462 args[6],args[7],args[8],args[9],args[10],args[11],
463 args[12],args[13]); break;
464 case 15: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
465 args[6],args[7],args[8],args[9],args[10],args[11],
466 args[12],args[13],args[14]); break;
467 case 16: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
468 args[6],args[7],args[8],args[9],args[10],args[11],
469 args[12],args[13],args[14],args[15]); break;
471 ERR( "Unsupported nb of args %d\n", nb_args );
480 /***********************************************************************
481 * call_stdcall_function
483 static LONGLONG call_stdcall_function( LONGLONG_FARPROC func, int nb_args, const int *args )
488 case 0: ret = func(); break;
489 case 1: ret = func(args[0]); break;
490 case 2: ret = func(args[0],args[1]); break;
491 case 3: ret = func(args[0],args[1],args[2]); break;
492 case 4: ret = func(args[0],args[1],args[2],args[3]); break;
493 case 5: ret = func(args[0],args[1],args[2],args[3],args[4]); break;
494 case 6: ret = func(args[0],args[1],args[2],args[3],args[4],
496 case 7: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
498 case 8: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
499 args[6],args[7]); break;
500 case 9: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
501 args[6],args[7],args[8]); break;
502 case 10: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
503 args[6],args[7],args[8],args[9]); break;
504 case 11: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
505 args[6],args[7],args[8],args[9],args[10]); break;
506 case 12: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
507 args[6],args[7],args[8],args[9],args[10],
509 case 13: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
510 args[6],args[7],args[8],args[9],args[10],args[11],
512 case 14: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
513 args[6],args[7],args[8],args[9],args[10],args[11],
514 args[12],args[13]); break;
515 case 15: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
516 args[6],args[7],args[8],args[9],args[10],args[11],
517 args[12],args[13],args[14]); break;
518 case 16: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
519 args[6],args[7],args[8],args[9],args[10],args[11],
520 args[12],args[13],args[14],args[15]); break;
522 ERR( "Unsupported nb of args %d\n", nb_args );
531 /***********************************************************************
534 * Stack layout on entry to this function:
539 * (esp) return addr to relay code
541 static LONGLONG RELAY_CallFrom32( int ret_addr, ... )
546 int *args = &ret_addr + 1;
547 /* Relay addr is the return address for this function */
548 BYTE *relay_addr = (BYTE *)__builtin_return_address(0);
549 DEBUG_ENTRY_POINT *relay = (DEBUG_ENTRY_POINT *)(relay_addr - 5);
550 WORD nb_args = relay->args / sizeof(int);
554 get_entry_point( buffer, relay );
556 DPRINTF( "%04lx:Call %s(", GetCurrentThreadId(), buffer );
557 RELAY_PrintArgs( args, nb_args, relay->argtypes );
558 DPRINTF( ") ret=%08x\n", ret_addr );
561 if (relay->ret == 0xc3) /* cdecl */
563 ret = call_cdecl_function( (LONGLONG_CPROC)relay->orig, nb_args, args );
567 ret = call_stdcall_function( (LONGLONG_FARPROC)relay->orig, nb_args, args );
572 BOOL ret64 = (relay->argtypes & 0x80000000) && (nb_args < 16);
574 DPRINTF( "%04lx:Ret %s() retval=%08x%08x ret=%08x\n",
575 GetCurrentThreadId(),
576 buffer, (UINT)(ret >> 32), (UINT)ret, ret_addr );
578 DPRINTF( "%04lx:Ret %s() retval=%08x ret=%08x\n",
579 GetCurrentThreadId(),
580 buffer, (UINT)ret, ret_addr );
586 /***********************************************************************
587 * RELAY_CallFrom32Regs
589 * Stack layout (esp is context->Esp, not the current %esp):
593 * (esp) return addr to caller
594 * (esp-4) return addr to DEBUG_ENTRY_POINT
595 * (esp-8) ptr to relay entry code for RELAY_CallFrom32Regs
596 * ... >128 bytes space free to be modified (ensured by the assembly glue)
598 void WINAPI RELAY_DoCallFrom32Regs( CONTEXT86 *context )
605 BYTE *relay_addr = *((BYTE **)context->Esp - 1);
606 DEBUG_ENTRY_POINT *relay = (DEBUG_ENTRY_POINT *)(relay_addr - 5);
607 WORD nb_args = relay->args / sizeof(int);
609 /* remove extra stuff from the stack */
610 context->Eip = *(DWORD *)context->Esp;
611 context->Esp += sizeof(DWORD);
612 args = (int *)context->Esp;
613 if (relay->ret == 0xc2) /* stdcall */
614 context->Esp += nb_args * sizeof(int);
616 entry_point = (BYTE *)relay->orig;
617 assert( *entry_point == 0xe8 /* lcall */ );
621 get_entry_point( buffer, relay );
623 DPRINTF( "%04lx:Call %s(", GetCurrentThreadId(), buffer );
624 RELAY_PrintArgs( args, nb_args, relay->argtypes );
625 DPRINTF( ") ret=%08lx fs=%04lx\n", context->Eip, context->SegFs );
627 DPRINTF(" eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx\n",
628 context->Eax, context->Ebx, context->Ecx,
629 context->Edx, context->Esi, context->Edi );
630 DPRINTF(" ebp=%08lx esp=%08lx ds=%04lx es=%04lx gs=%04lx flags=%08lx\n",
631 context->Ebp, context->Esp, context->SegDs,
632 context->SegEs, context->SegGs, context->EFlags );
635 /* Now call the real function */
637 memcpy( args_copy, args, nb_args * sizeof(args[0]) );
638 args_copy[nb_args] = (int)context; /* append context argument */
639 if (relay->ret == 0xc3) /* cdecl */
641 call_cdecl_function( *(LONGLONG_CPROC *)(entry_point + 5), nb_args+1, args_copy );
645 call_stdcall_function( *(LONGLONG_FARPROC *)(entry_point + 5), nb_args+1, args_copy );
650 DPRINTF( "%04lx:Ret %s() retval=%08lx ret=%08lx fs=%04lx\n",
651 GetCurrentThreadId(),
652 buffer, context->Eax, context->Eip, context->SegFs );
654 DPRINTF(" eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx\n",
655 context->Eax, context->Ebx, context->Ecx,
656 context->Edx, context->Esi, context->Edi );
657 DPRINTF(" ebp=%08lx esp=%08lx ds=%04lx es=%04lx gs=%04lx flags=%08lx\n",
658 context->Ebp, context->Esp, context->SegDs,
659 context->SegEs, context->SegGs, context->EFlags );
663 void WINAPI RELAY_CallFrom32Regs(void);
664 __ASM_GLOBAL_FUNC( RELAY_CallFrom32Regs,
665 "call " __ASM_NAME("__wine_call_from_32_regs") "\n\t"
666 ".long " __ASM_NAME("RELAY_DoCallFrom32Regs") ",0" );
669 /* check whether the function at addr starts with a call to __wine_call_from_32_regs */
670 static BOOL is_register_entry_point( const BYTE *addr )
672 extern void __wine_call_from_32_regs();
676 if (*addr != 0xe8) return FALSE; /* not a call */
677 /* check if call target is __wine_call_from_32_regs */
678 offset = (int *)(addr + 1);
679 if (*offset == (char *)__wine_call_from_32_regs - (char *)(offset + 1)) return TRUE;
680 /* now check if call target is an import table jump to __wine_call_from_32_regs */
681 addr = (BYTE *)(offset + 1) + *offset;
682 if (addr[0] != 0xff || addr[1] != 0x25) return FALSE; /* not an indirect jmp */
683 ptr = *(void **)(addr + 2); /* get indirect jmp target address */
684 return (*(char **)ptr == (char *)__wine_call_from_32_regs);
688 /***********************************************************************
689 * RELAY_GetProcAddress
691 * Return the proc address to use for a given function.
693 FARPROC RELAY_GetProcAddress( HMODULE module, IMAGE_EXPORT_DIRECTORY *exports,
694 DWORD exp_size, FARPROC proc, const WCHAR *user )
696 DEBUG_ENTRY_POINT *debug = (DEBUG_ENTRY_POINT *)proc;
697 DEBUG_ENTRY_POINT *list = (DEBUG_ENTRY_POINT *)((char *)exports + exp_size);
699 if (debug < list || debug >= list + exports->NumberOfFunctions) return proc;
700 if (list + (debug - list) != debug) return proc; /* not a valid address */
701 if (check_relay_from_module( user )) return proc; /* we want to relay it */
702 if (!debug->call) return proc; /* not a normal function */
703 if (debug->call != 0xe8 && debug->call != 0xe9) return proc; /* not a debug thunk at all */
708 /***********************************************************************
711 * Setup relay debugging for a built-in dll.
713 void RELAY_SetupDLL( HMODULE module )
715 IMAGE_EXPORT_DIRECTORY *exports;
716 DEBUG_ENTRY_POINT *debug;
720 char *p, dllname[80];
723 exports = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size );
724 if (!exports) return;
725 debug = (DEBUG_ENTRY_POINT *)((char *)exports + size);
726 funcs = (DWORD *)((char *)module + exports->AddressOfFunctions);
727 strcpy( dllname, (char *)module + exports->Name );
728 p = dllname + strlen(dllname) - 4;
729 if (p > dllname && !strcasecmp( p, ".dll" )) *p = 0;
731 for (i = 0; i < exports->NumberOfFunctions; i++, funcs++, debug++)
735 if (!debug->call) continue; /* not a normal function */
736 if (debug->call != 0xe8 && debug->call != 0xe9) break; /* not a debug thunk at all */
738 if ((name = find_exported_name( module, exports, i + exports->Base )))
739 on = check_relay_include( dllname, name );
743 debug->call = 0xe8; /* call relative */
744 if (is_register_entry_point( debug->orig ))
745 debug->callfrom32 = (char *)RELAY_CallFrom32Regs - (char *)&debug->ret;
747 debug->callfrom32 = (char *)RELAY_CallFrom32 - (char *)&debug->ret;
751 debug->call = 0xe9; /* jmp relative */
752 debug->callfrom32 = (char *)debug->orig - (char *)&debug->ret;
755 debug->orig = (FARPROC)((char *)module + (DWORD)*funcs);
756 *funcs = (char *)debug - (char *)module;
761 /***********************************************************************
762 * SNOOP_ShowDebugmsgSnoop
764 * Simple function to decide if a particular debugging message is
767 int SNOOP_ShowDebugmsgSnoop(const char *dll, int ord, const char *fname)
769 if(debug_snoop_excludelist || debug_snoop_includelist) {
770 const WCHAR **listitem;
772 int len, len2, itemlen, show;
774 if(debug_snoop_excludelist) {
776 listitem = debug_snoop_excludelist;
779 listitem = debug_snoop_includelist;
783 sprintf(buf, "%s.%d", dll, ord);
785 for(; *listitem; listitem++)
787 itemlen = strlenW(*listitem);
788 if((itemlen == len && !strncmpiAW( buf, *listitem, len)) ||
789 (itemlen == len2 && !strncmpiAW(buf, *listitem, len2)) ||
790 (fname && !strcmpAW(fname, *listitem)))
799 /***********************************************************************
802 * Setup snoop debugging for a native dll.
804 void SNOOP_SetupDLL(HMODULE hmod)
806 SNOOP_DLL **dll = &firstdll;
810 IMAGE_EXPORT_DIRECTORY *exports;
812 exports = RtlImageDirectoryEntryToData( hmod, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size );
813 if (!exports) return;
814 name = (char *)hmod + exports->Name;
816 TRACE("hmod=%p, name=%s\n", hmod, name);
819 if ((*dll)->hmod == hmod)
821 /* another dll, loaded at the same address */
823 size = (*dll)->nrofordinals * sizeof(SNOOP_FUN);
824 NtFreeVirtualMemory(GetCurrentProcess(), &addr, &size, MEM_RELEASE);
827 dll = &((*dll)->next);
829 *dll = RtlReAllocateHeap(ntdll_get_process_heap(),
830 HEAP_ZERO_MEMORY, *dll,
831 sizeof(SNOOP_DLL) + strlen(name));
833 (*dll)->ordbase = exports->Base;
834 (*dll)->nrofordinals = exports->NumberOfFunctions;
835 strcpy( (*dll)->name, name );
836 p = (*dll)->name + strlen((*dll)->name) - 4;
837 if (p > (*dll)->name && !strcasecmp( p, ".dll" )) *p = 0;
839 size = exports->NumberOfFunctions * sizeof(SNOOP_FUN);
840 NtAllocateVirtualMemory(GetCurrentProcess(), &addr, NULL, &size,
841 MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
843 RtlFreeHeap(ntdll_get_process_heap(),0,*dll);
844 FIXME("out of memory\n");
848 memset((*dll)->funs,0,size);
852 /***********************************************************************
853 * SNOOP_GetProcAddress
855 * Return the proc address to use for a given function.
857 FARPROC SNOOP_GetProcAddress( HMODULE hmod, IMAGE_EXPORT_DIRECTORY *exports,
858 DWORD exp_size, FARPROC origfun, DWORD ordinal )
864 SNOOP_DLL *dll = firstdll;
866 IMAGE_SECTION_HEADER *sec;
868 if (!TRACE_ON(snoop)) return origfun;
869 if (!*(LPBYTE)origfun) /* 0x00 is an imposs. opcode, poss. dataref. */
872 sec = RtlImageRvaToSection( RtlImageNtHeader(hmod), hmod, (char *)origfun - (char *)hmod );
874 if (!sec || !(sec->Characteristics & IMAGE_SCN_CNT_CODE))
875 return origfun; /* most likely a data reference */
878 if (hmod == dll->hmod)
882 if (!dll) /* probably internal */
885 /* try to find a name for it */
887 names = (DWORD *)((char *)hmod + exports->AddressOfNames);
888 ordinals = (WORD *)((char *)hmod + exports->AddressOfNameOrdinals);
889 if (names) for (i = 0; i < exports->NumberOfNames; i++)
891 if (ordinals[i] == ordinal)
893 ename = (char *)hmod + names[i];
897 if (!SNOOP_ShowDebugmsgSnoop(dll->name,ordinal,ename))
899 assert(ordinal < dll->nrofordinals);
900 fun = dll->funs+ordinal;
905 /* NOTE: origreturn struct member MUST come directly after snoopentry */
906 fun->snoopentry = (char*)SNOOP_Entry-((char*)(&fun->nrofargs));
907 fun->origfun = origfun;
910 return (FARPROC)&(fun->lcall);
913 static void SNOOP_PrintArg(DWORD x)
918 if (!HIWORD(x) || TRACE_ON(seh)) return; /* trivial reject to avoid faults */
925 if (s[i]<0x20) {nostring=1;break;}
926 if (s[i]>=0x80) {nostring=1;break;}
929 if (!nostring && i > 5)
930 DPRINTF(" %s",debugstr_an((LPSTR)x,i));
931 else /* try unicode */
937 if (s[i]<0x20) {nostring=1;break;}
938 if (s[i]>0x100) {nostring=1;break;}
941 if (!nostring && i > 5) DPRINTF(" %s",debugstr_wn((LPWSTR)x,i));
950 #define CALLER1REF (*(DWORD*)context->Esp)
952 void WINAPI SNOOP_DoEntry( CONTEXT86 *context )
954 DWORD ordinal=0,entry = context->Eip - 5;
955 SNOOP_DLL *dll = firstdll;
956 SNOOP_FUN *fun = NULL;
957 SNOOP_RETURNENTRIES **rets = &firstrets;
958 SNOOP_RETURNENTRY *ret;
962 if ( ((char*)entry>=(char*)dll->funs) &&
963 ((char*)entry<=(char*)(dll->funs+dll->nrofordinals))
965 fun = (SNOOP_FUN*)entry;
966 ordinal = fun-dll->funs;
972 FIXME("entrypoint 0x%08lx not found\n",entry);
975 /* guess cdecl ... */
976 if (fun->nrofargs<0) {
977 /* Typical cdecl return frame is:
979 * which has (for xxxxxxxx up to 255 the opcode "83 C4 xx".
980 * (after that 81 C2 xx xx xx xx)
982 LPBYTE reteip = (LPBYTE)CALLER1REF;
985 if ((reteip[0]==0x83)&&(reteip[1]==0xc4))
986 fun->nrofargs=reteip[2]/4;
992 for (i=0;i<sizeof((*rets)->entry)/sizeof((*rets)->entry[0]);i++)
993 if (!(*rets)->entry[i].origreturn)
995 if (i!=sizeof((*rets)->entry)/sizeof((*rets)->entry[0]))
997 rets = &((*rets)->next);
1003 NtAllocateVirtualMemory(GetCurrentProcess(), &addr, NULL, &size,
1004 MEM_COMMIT | MEM_RESERVE,
1005 PAGE_EXECUTE_READWRITE);
1008 memset(*rets,0,4096);
1009 i = 0; /* entry 0 is free */
1011 ret = &((*rets)->entry[i]);
1013 /* NOTE: origreturn struct member MUST come directly after snoopret */
1014 ret->snoopret = ((char*)SNOOP_Return)-(char*)(&ret->origreturn);
1015 ret->origreturn = (FARPROC)CALLER1REF;
1016 CALLER1REF = (DWORD)&ret->lcall;
1019 ret->ordinal = ordinal;
1020 ret->origESP = context->Esp;
1022 context->Eip = (DWORD)fun->origfun;
1024 if (fun->name) DPRINTF("%04lx:CALL %s.%s(",GetCurrentThreadId(),dll->name,fun->name);
1025 else DPRINTF("%04lx:CALL %s.%ld(",GetCurrentThreadId(),dll->name,dll->ordbase+ordinal);
1026 if (fun->nrofargs>0) {
1027 max = fun->nrofargs; if (max>16) max=16;
1030 SNOOP_PrintArg(*(DWORD*)(context->Esp + 4 + sizeof(DWORD)*i));
1031 if (i<fun->nrofargs-1) DPRINTF(",");
1033 if (max!=fun->nrofargs)
1035 } else if (fun->nrofargs<0) {
1036 DPRINTF("<unknown, check return>");
1037 ret->args = RtlAllocateHeap(ntdll_get_process_heap(),
1038 0,16*sizeof(DWORD));
1039 memcpy(ret->args,(LPBYTE)(context->Esp + 4),sizeof(DWORD)*16);
1041 DPRINTF(") ret=%08lx\n",(DWORD)ret->origreturn);
1045 void WINAPI SNOOP_DoReturn( CONTEXT86 *context )
1047 SNOOP_RETURNENTRY *ret = (SNOOP_RETURNENTRY*)(context->Eip - 5);
1048 SNOOP_FUN *fun = &ret->dll->funs[ret->ordinal];
1050 /* We haven't found out the nrofargs yet. If we called a cdecl
1051 * function it is too late anyway and we can just set '0' (which
1052 * will be the difference between orig and current ESP
1053 * If stdcall -> everything ok.
1055 if (ret->dll->funs[ret->ordinal].nrofargs<0)
1056 ret->dll->funs[ret->ordinal].nrofargs=(context->Esp - ret->origESP-4)/4;
1057 context->Eip = (DWORD)ret->origreturn;
1062 DPRINTF("%04lx:RET %s.%s(", GetCurrentThreadId(), ret->dll->name, fun->name);
1064 DPRINTF("%04lx:RET %s.%ld(", GetCurrentThreadId(),
1065 ret->dll->name,ret->dll->ordbase+ret->ordinal);
1067 max = fun->nrofargs;
1072 SNOOP_PrintArg(ret->args[i]);
1073 if (i<max-1) DPRINTF(",");
1075 DPRINTF(") retval=%08lx ret=%08lx\n",
1076 context->Eax,(DWORD)ret->origreturn );
1077 RtlFreeHeap(ntdll_get_process_heap(),0,ret->args);
1083 DPRINTF("%04lx:RET %s.%s() retval=%08lx ret=%08lx\n",
1084 GetCurrentThreadId(),
1085 ret->dll->name, fun->name, context->Eax, (DWORD)ret->origreturn);
1087 DPRINTF("%04lx:RET %s.%ld() retval=%08lx ret=%08lx\n",
1088 GetCurrentThreadId(),
1089 ret->dll->name,ret->dll->ordbase+ret->ordinal,
1090 context->Eax, (DWORD)ret->origreturn);
1092 ret->origreturn = NULL; /* mark as empty */
1095 /* assembly wrappers that save the context */
1096 __ASM_GLOBAL_FUNC( SNOOP_Entry,
1097 "call " __ASM_NAME("__wine_call_from_32_regs") "\n\t"
1098 ".long " __ASM_NAME("SNOOP_DoEntry") ",0" );
1099 __ASM_GLOBAL_FUNC( SNOOP_Return,
1100 "call " __ASM_NAME("__wine_call_from_32_regs") "\n\t"
1101 ".long " __ASM_NAME("SNOOP_DoReturn") ",0" );
1103 #else /* __i386__ */
1105 FARPROC RELAY_GetProcAddress( HMODULE module, IMAGE_EXPORT_DIRECTORY *exports,
1106 DWORD exp_size, FARPROC proc, const WCHAR *user )
1111 FARPROC SNOOP_GetProcAddress( HMODULE hmod, IMAGE_EXPORT_DIRECTORY *exports,
1112 DWORD exp_size, FARPROC origfun, DWORD ordinal )
1117 void RELAY_SetupDLL( HMODULE module )
1121 void SNOOP_SetupDLL( HMODULE hmod )
1123 FIXME("snooping works only on i386 for now.\n");
1126 #endif /* __i386__ */