Added support for implementing VxDs as separate dlls and loading them
[wine.git] / dlls / ntdll / relay.c
blob4853317857213f895a6ba26bf971edfff9165a62
1 /*
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
22 #include "config.h"
23 #include "wine/port.h"
25 #include <assert.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <stdio.h>
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winreg.h"
33 #include "winternl.h"
34 #include "excpt.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 static const WCHAR **debug_relay_excludelist;
45 static const WCHAR **debug_relay_includelist;
46 static const WCHAR **debug_snoop_excludelist;
47 static const WCHAR **debug_snoop_includelist;
48 static const WCHAR **debug_from_relay_excludelist;
49 static const WCHAR **debug_from_relay_includelist;
51 /* compare an ASCII and a Unicode string without depending on the current codepage */
52 inline static int strcmpAW( const char *strA, const WCHAR *strW )
54 while (*strA && ((unsigned char)*strA == *strW)) { strA++; strW++; }
55 return (unsigned char)*strA - *strW;
58 /* compare an ASCII and a Unicode string without depending on the current codepage */
59 inline static int strncmpiAW( const char *strA, const WCHAR *strW, int n )
61 int ret = 0;
62 for ( ; n > 0; n--, strA++, strW++)
63 if ((ret = toupperW((unsigned char)*strA) - toupperW(*strW)) || !*strA) break;
64 return ret;
67 /***********************************************************************
68 * build_list
70 * Build a function list from a ';'-separated string.
72 static const WCHAR **build_list( const WCHAR *buffer )
74 int count = 1;
75 const WCHAR *p = buffer;
76 const WCHAR **ret;
78 while ((p = strchrW( p, ';' )))
80 count++;
81 p++;
83 /* allocate count+1 pointers, plus the space for a copy of the string */
84 if ((ret = RtlAllocateHeap( GetProcessHeap(), 0,
85 (count+1) * sizeof(WCHAR*) + (strlenW(buffer)+1) * sizeof(WCHAR) )))
87 WCHAR *str = (WCHAR *)(ret + count + 1);
88 WCHAR *p = str;
90 strcpyW( str, buffer );
91 count = 0;
92 for (;;)
94 ret[count++] = p;
95 if (!(p = strchrW( p, ';' ))) break;
96 *p++ = 0;
98 ret[count++] = NULL;
100 return ret;
104 /***********************************************************************
105 * RELAY_InitDebugLists
107 * Build the relay include/exclude function lists.
109 void RELAY_InitDebugLists(void)
111 OBJECT_ATTRIBUTES attr;
112 UNICODE_STRING name;
113 char buffer[1024];
114 HKEY hkey;
115 DWORD count;
116 WCHAR *str;
117 static const WCHAR configW[] = {'M','a','c','h','i','n','e','\\',
118 'S','o','f','t','w','a','r','e','\\',
119 'W','i','n','e','\\',
120 'W','i','n','e','\\',
121 'C','o','n','f','i','g','\\',
122 'D','e','b','u','g',0};
123 static const WCHAR RelayIncludeW[] = {'R','e','l','a','y','I','n','c','l','u','d','e',0};
124 static const WCHAR RelayExcludeW[] = {'R','e','l','a','y','E','x','c','l','u','d','e',0};
125 static const WCHAR SnoopIncludeW[] = {'S','n','o','o','p','I','n','c','l','u','d','e',0};
126 static const WCHAR SnoopExcludeW[] = {'S','n','o','o','p','E','x','c','l','u','d','e',0};
127 static const WCHAR RelayFromIncludeW[] = {'R','e','l','a','y','F','r','o','m','I','n','c','l','u','d','e',0};
128 static const WCHAR RelayFromExcludeW[] = {'R','e','l','a','y','F','r','o','m','E','x','c','l','u','d','e',0};
130 attr.Length = sizeof(attr);
131 attr.RootDirectory = 0;
132 attr.ObjectName = &name;
133 attr.Attributes = 0;
134 attr.SecurityDescriptor = NULL;
135 attr.SecurityQualityOfService = NULL;
136 RtlInitUnicodeString( &name, configW );
138 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) return;
140 str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)buffer)->Data;
141 RtlInitUnicodeString( &name, RelayIncludeW );
142 if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
144 TRACE("RelayInclude = %s\n", debugstr_w(str) );
145 debug_relay_includelist = build_list( str );
148 RtlInitUnicodeString( &name, RelayExcludeW );
149 if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
151 TRACE( "RelayExclude = %s\n", debugstr_w(str) );
152 debug_relay_excludelist = build_list( str );
155 RtlInitUnicodeString( &name, SnoopIncludeW );
156 if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
158 TRACE_(snoop)( "SnoopInclude = %s\n", debugstr_w(str) );
159 debug_snoop_includelist = build_list( str );
162 RtlInitUnicodeString( &name, SnoopExcludeW );
163 if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
165 TRACE_(snoop)( "SnoopExclude = %s\n", debugstr_w(str) );
166 debug_snoop_excludelist = build_list( str );
169 RtlInitUnicodeString( &name, RelayFromIncludeW );
170 if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
172 TRACE("RelayFromInclude = %s\n", debugstr_w(str) );
173 debug_from_relay_includelist = build_list( str );
176 RtlInitUnicodeString( &name, RelayFromExcludeW );
177 if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
179 TRACE( "RelayFromExclude = %s\n", debugstr_w(str) );
180 debug_from_relay_excludelist = build_list( str );
183 NtClose( hkey );
187 #ifdef __i386__
189 #include "pshpack1.h"
191 typedef struct
193 BYTE call; /* 0xe8 call callfrom32 (relative) */
194 DWORD callfrom32; /* RELAY_CallFrom32 relative addr */
195 BYTE ret; /* 0xc2 ret $n or 0xc3 ret */
196 WORD args; /* nb of args to remove from the stack */
197 void *orig; /* original entry point */
198 DWORD argtypes; /* argument types */
199 } DEBUG_ENTRY_POINT;
201 typedef struct
203 /* code part */
204 BYTE lcall; /* 0xe8 call snoopentry (relative) */
205 /* NOTE: If you move snoopentry OR nrofargs fix the relative offset
206 * calculation!
208 DWORD snoopentry; /* SNOOP_Entry relative */
209 /* unreached */
210 int nrofargs;
211 FARPROC origfun;
212 const char *name;
213 } SNOOP_FUN;
215 typedef struct tagSNOOP_DLL {
216 HMODULE hmod;
217 SNOOP_FUN *funs;
218 DWORD ordbase;
219 DWORD nrofordinals;
220 struct tagSNOOP_DLL *next;
221 char name[1];
222 } SNOOP_DLL;
224 typedef struct
226 /* code part */
227 BYTE lcall; /* 0xe8 call snoopret relative*/
228 /* NOTE: If you move snoopret OR origreturn fix the relative offset
229 * calculation!
231 DWORD snoopret; /* SNOOP_Ret relative */
232 /* unreached */
233 FARPROC origreturn;
234 SNOOP_DLL *dll;
235 DWORD ordinal;
236 DWORD origESP;
237 DWORD *args; /* saved args across a stdcall */
238 } SNOOP_RETURNENTRY;
240 typedef struct tagSNOOP_RETURNENTRIES {
241 SNOOP_RETURNENTRY entry[4092/sizeof(SNOOP_RETURNENTRY)];
242 struct tagSNOOP_RETURNENTRIES *next;
243 } SNOOP_RETURNENTRIES;
245 #include "poppack.h"
247 extern void WINAPI SNOOP_Entry();
248 extern void WINAPI SNOOP_Return();
250 static SNOOP_DLL *firstdll;
251 static SNOOP_RETURNENTRIES *firstrets;
253 static WINE_EXCEPTION_FILTER(page_fault)
255 if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ||
256 GetExceptionCode() == EXCEPTION_PRIV_INSTRUCTION)
257 return EXCEPTION_EXECUTE_HANDLER;
258 return EXCEPTION_CONTINUE_SEARCH;
261 /***********************************************************************
262 * check_list
264 * Check if a given module and function is in the list.
266 static BOOL check_list( const char *module, int ordinal, const char *func, const WCHAR **list )
268 char ord_str[10];
270 sprintf( ord_str, "%d", ordinal );
271 for(; *list; list++)
273 const WCHAR *p = strrchrW( *list, '.' );
274 if (p && p > *list) /* check module and function */
276 int len = p - *list;
277 if (strncmpiAW( module, *list, len-1 ) || module[len]) continue;
278 if (p[1] == '*' && !p[2]) return TRUE;
279 if (!strcmpAW( ord_str, p + 1 )) return TRUE;
280 if (func && !strcmpAW( func, p + 1 )) return TRUE;
282 else /* function only */
284 if (func && !strcmpAW( func, *list )) return TRUE;
287 return FALSE;
291 /***********************************************************************
292 * check_relay_include
294 * Check if a given function must be included in the relay output.
296 static BOOL check_relay_include( const char *module, int ordinal, const char *func )
298 if (debug_relay_excludelist && check_list( module, ordinal, func, debug_relay_excludelist ))
299 return FALSE;
300 if (debug_relay_includelist && !check_list( module, ordinal, func, debug_relay_includelist ))
301 return FALSE;
302 return TRUE;
306 /***********************************************************************
307 * check_relay_from_module
309 * Check if calls from a given module must be included in the relay output.
311 static BOOL check_relay_from_module( const WCHAR *module )
313 static const WCHAR dllW[] = {'.','d','l','l',0 };
314 const WCHAR **listitem;
315 BOOL show;
317 if (!debug_from_relay_excludelist && !debug_from_relay_includelist) return TRUE;
318 if (debug_from_relay_excludelist)
320 show = TRUE;
321 listitem = debug_from_relay_excludelist;
323 else
325 show = FALSE;
326 listitem = debug_from_relay_includelist;
328 for(; *listitem; listitem++)
330 int len;
332 if (!strcmpiW( *listitem, module )) return !show;
333 len = strlenW( *listitem );
334 if (!strncmpiW( *listitem, module, len ) && !strcmpiW( module + len, dllW ))
335 return !show;
337 return show;
341 /***********************************************************************
342 * find_exported_name
344 * Find the name of an exported function.
346 static const char *find_exported_name( HMODULE module,
347 IMAGE_EXPORT_DIRECTORY *exp, int ordinal )
349 int i;
350 const char *ret = NULL;
352 WORD *ordptr = (WORD *)((char *)module + exp->AddressOfNameOrdinals);
353 for (i = 0; i < exp->NumberOfNames; i++, ordptr++)
354 if (*ordptr + exp->Base == ordinal) break;
355 if (i < exp->NumberOfNames)
356 ret = (char *)module + ((DWORD*)((char *)module + exp->AddressOfNames))[i];
357 return ret;
361 /***********************************************************************
362 * get_entry_point
364 * Get the name of the DLL entry point corresponding to a relay address.
366 static void get_entry_point( char *buffer, DEBUG_ENTRY_POINT *relay )
368 IMAGE_EXPORT_DIRECTORY *exp = NULL;
369 DEBUG_ENTRY_POINT *debug;
370 char *p;
371 const char *name;
372 int ordinal = 0;
373 PLIST_ENTRY mark, entry;
374 PLDR_MODULE mod = NULL;
375 DWORD size;
377 /* First find the module */
379 mark = &NtCurrentTeb()->Peb->LdrData->InLoadOrderModuleList;
380 for (entry = mark->Flink; entry != mark; entry = entry->Flink)
382 mod = CONTAINING_RECORD(entry, LDR_MODULE, InLoadOrderModuleList);
383 if (!(mod->Flags & LDR_WINE_INTERNAL)) continue;
384 exp = RtlImageDirectoryEntryToData( mod->BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size );
385 if (!exp) continue;
386 debug = (DEBUG_ENTRY_POINT *)((char *)exp + size);
387 if (debug <= relay && relay < debug + exp->NumberOfFunctions)
389 ordinal = relay - debug;
390 break;
394 /* Now find the function */
396 strcpy( buffer, (char *)mod->BaseAddress + exp->Name );
397 p = buffer + strlen(buffer);
398 if (p > buffer + 4 && !strcasecmp( p - 4, ".dll" )) p -= 4;
400 if ((name = find_exported_name( mod->BaseAddress, exp, ordinal + exp->Base )))
401 sprintf( p, ".%s", name );
402 else
403 sprintf( p, ".%ld", ordinal + exp->Base );
407 /***********************************************************************
408 * RELAY_PrintArgs
410 static inline void RELAY_PrintArgs( int *args, int nb_args, unsigned int typemask )
412 while (nb_args--)
414 if ((typemask & 3) && HIWORD(*args))
416 if (typemask & 2)
417 DPRINTF( "%08x %s", *args, debugstr_w((LPWSTR)*args) );
418 else
419 DPRINTF( "%08x %s", *args, debugstr_a((LPCSTR)*args) );
421 else DPRINTF( "%08x", *args );
422 if (nb_args) DPRINTF( "," );
423 args++;
424 typemask >>= 2;
429 typedef LONGLONG (*LONGLONG_CPROC)();
430 typedef LONGLONG (WINAPI *LONGLONG_FARPROC)();
433 /***********************************************************************
434 * call_cdecl_function
436 static LONGLONG call_cdecl_function( LONGLONG_CPROC func, int nb_args, const int *args )
438 LONGLONG ret;
439 switch(nb_args)
441 case 0: ret = func(); break;
442 case 1: ret = func(args[0]); break;
443 case 2: ret = func(args[0],args[1]); break;
444 case 3: ret = func(args[0],args[1],args[2]); break;
445 case 4: ret = func(args[0],args[1],args[2],args[3]); break;
446 case 5: ret = func(args[0],args[1],args[2],args[3],args[4]); break;
447 case 6: ret = func(args[0],args[1],args[2],args[3],args[4],
448 args[5]); break;
449 case 7: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
450 args[6]); break;
451 case 8: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
452 args[6],args[7]); break;
453 case 9: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
454 args[6],args[7],args[8]); break;
455 case 10: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
456 args[6],args[7],args[8],args[9]); break;
457 case 11: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
458 args[6],args[7],args[8],args[9],args[10]); break;
459 case 12: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
460 args[6],args[7],args[8],args[9],args[10],
461 args[11]); break;
462 case 13: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
463 args[6],args[7],args[8],args[9],args[10],args[11],
464 args[12]); break;
465 case 14: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
466 args[6],args[7],args[8],args[9],args[10],args[11],
467 args[12],args[13]); break;
468 case 15: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
469 args[6],args[7],args[8],args[9],args[10],args[11],
470 args[12],args[13],args[14]); break;
471 case 16: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
472 args[6],args[7],args[8],args[9],args[10],args[11],
473 args[12],args[13],args[14],args[15]); break;
474 default:
475 ERR( "Unsupported nb of args %d\n", nb_args );
476 assert(FALSE);
477 ret = 0;
478 break;
480 return ret;
484 /***********************************************************************
485 * call_stdcall_function
487 static LONGLONG call_stdcall_function( LONGLONG_FARPROC func, int nb_args, const int *args )
489 LONGLONG ret;
490 switch(nb_args)
492 case 0: ret = func(); break;
493 case 1: ret = func(args[0]); break;
494 case 2: ret = func(args[0],args[1]); break;
495 case 3: ret = func(args[0],args[1],args[2]); break;
496 case 4: ret = func(args[0],args[1],args[2],args[3]); break;
497 case 5: ret = func(args[0],args[1],args[2],args[3],args[4]); break;
498 case 6: ret = func(args[0],args[1],args[2],args[3],args[4],
499 args[5]); break;
500 case 7: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
501 args[6]); break;
502 case 8: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
503 args[6],args[7]); break;
504 case 9: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
505 args[6],args[7],args[8]); break;
506 case 10: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
507 args[6],args[7],args[8],args[9]); break;
508 case 11: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
509 args[6],args[7],args[8],args[9],args[10]); break;
510 case 12: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
511 args[6],args[7],args[8],args[9],args[10],
512 args[11]); break;
513 case 13: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
514 args[6],args[7],args[8],args[9],args[10],args[11],
515 args[12]); break;
516 case 14: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
517 args[6],args[7],args[8],args[9],args[10],args[11],
518 args[12],args[13]); break;
519 case 15: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
520 args[6],args[7],args[8],args[9],args[10],args[11],
521 args[12],args[13],args[14]); break;
522 case 16: ret = func(args[0],args[1],args[2],args[3],args[4],args[5],
523 args[6],args[7],args[8],args[9],args[10],args[11],
524 args[12],args[13],args[14],args[15]); break;
525 default:
526 ERR( "Unsupported nb of args %d\n", nb_args );
527 assert(FALSE);
528 ret = 0;
529 break;
531 return ret;
535 /***********************************************************************
536 * RELAY_CallFrom32
538 * Stack layout on entry to this function:
539 * ... ...
540 * (esp+12) arg2
541 * (esp+8) arg1
542 * (esp+4) ret_addr
543 * (esp) return addr to relay code
545 static LONGLONG RELAY_CallFrom32( int ret_addr, ... )
547 LONGLONG ret;
548 char buffer[80];
550 int *args = &ret_addr + 1;
551 /* Relay addr is the return address for this function */
552 BYTE *relay_addr = (BYTE *)__builtin_return_address(0);
553 DEBUG_ENTRY_POINT *relay = (DEBUG_ENTRY_POINT *)(relay_addr - 5);
554 WORD nb_args = relay->args / sizeof(int);
556 if (TRACE_ON(relay))
558 get_entry_point( buffer, relay );
560 DPRINTF( "%04lx:Call %s(", GetCurrentThreadId(), buffer );
561 RELAY_PrintArgs( args, nb_args, relay->argtypes );
562 DPRINTF( ") ret=%08x\n", ret_addr );
565 if (relay->ret == 0xc3) /* cdecl */
567 ret = call_cdecl_function( (LONGLONG_CPROC)relay->orig, nb_args, args );
569 else /* stdcall */
571 ret = call_stdcall_function( (LONGLONG_FARPROC)relay->orig, nb_args, args );
574 if (TRACE_ON(relay))
576 BOOL ret64 = (relay->argtypes & 0x80000000) && (nb_args < 16);
577 if (ret64)
578 DPRINTF( "%04lx:Ret %s() retval=%08x%08x ret=%08x\n",
579 GetCurrentThreadId(),
580 buffer, (UINT)(ret >> 32), (UINT)ret, ret_addr );
581 else
582 DPRINTF( "%04lx:Ret %s() retval=%08x ret=%08x\n",
583 GetCurrentThreadId(),
584 buffer, (UINT)ret, ret_addr );
586 return ret;
590 /***********************************************************************
591 * RELAY_CallFrom32Regs
593 * Stack layout (esp is context->Esp, not the current %esp):
595 * ...
596 * (esp+4) first arg
597 * (esp) return addr to caller
598 * (esp-4) return addr to DEBUG_ENTRY_POINT
599 * (esp-8) ptr to relay entry code for RELAY_CallFrom32Regs
600 * ... >128 bytes space free to be modified (ensured by the assembly glue)
602 void WINAPI RELAY_DoCallFrom32Regs( CONTEXT86 *context )
604 char buffer[80];
605 int* args;
606 int args_copy[17];
607 BYTE *entry_point;
609 BYTE *relay_addr = *((BYTE **)context->Esp - 1);
610 DEBUG_ENTRY_POINT *relay = (DEBUG_ENTRY_POINT *)(relay_addr - 5);
611 WORD nb_args = relay->args / sizeof(int);
613 /* remove extra stuff from the stack */
614 context->Eip = *(DWORD *)context->Esp;
615 context->Esp += sizeof(DWORD);
616 args = (int *)context->Esp;
617 if (relay->ret == 0xc2) /* stdcall */
618 context->Esp += nb_args * sizeof(int);
620 entry_point = (BYTE *)relay->orig;
621 assert( *entry_point == 0xe8 /* lcall */ );
623 if (TRACE_ON(relay))
625 get_entry_point( buffer, relay );
627 DPRINTF( "%04lx:Call %s(", GetCurrentThreadId(), buffer );
628 RELAY_PrintArgs( args, nb_args, relay->argtypes );
629 DPRINTF( ") ret=%08lx fs=%04lx\n", context->Eip, context->SegFs );
631 DPRINTF(" eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx\n",
632 context->Eax, context->Ebx, context->Ecx,
633 context->Edx, context->Esi, context->Edi );
634 DPRINTF(" ebp=%08lx esp=%08lx ds=%04lx es=%04lx gs=%04lx flags=%08lx\n",
635 context->Ebp, context->Esp, context->SegDs,
636 context->SegEs, context->SegGs, context->EFlags );
639 /* Now call the real function */
641 memcpy( args_copy, args, nb_args * sizeof(args[0]) );
642 args_copy[nb_args] = (int)context; /* append context argument */
643 if (relay->ret == 0xc3) /* cdecl */
645 call_cdecl_function( *(LONGLONG_CPROC *)(entry_point + 5), nb_args+1, args_copy );
647 else /* stdcall */
649 call_stdcall_function( *(LONGLONG_FARPROC *)(entry_point + 5), nb_args+1, args_copy );
652 if (TRACE_ON(relay))
654 DPRINTF( "%04lx:Ret %s() retval=%08lx ret=%08lx fs=%04lx\n",
655 GetCurrentThreadId(),
656 buffer, context->Eax, context->Eip, context->SegFs );
658 DPRINTF(" eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx\n",
659 context->Eax, context->Ebx, context->Ecx,
660 context->Edx, context->Esi, context->Edi );
661 DPRINTF(" ebp=%08lx esp=%08lx ds=%04lx es=%04lx gs=%04lx flags=%08lx\n",
662 context->Ebp, context->Esp, context->SegDs,
663 context->SegEs, context->SegGs, context->EFlags );
667 void WINAPI RELAY_CallFrom32Regs(void);
668 __ASM_GLOBAL_FUNC( RELAY_CallFrom32Regs,
669 "call " __ASM_NAME("__wine_call_from_32_regs") "\n\t"
670 ".long " __ASM_NAME("RELAY_DoCallFrom32Regs") ",0" );
673 /* check whether the function at addr starts with a call to __wine_call_from_32_regs */
674 static BOOL is_register_entry_point( const BYTE *addr )
676 extern void __wine_call_from_32_regs();
677 int *offset;
678 void *ptr;
680 if (*addr != 0xe8) return FALSE; /* not a call */
681 /* check if call target is __wine_call_from_32_regs */
682 offset = (int *)(addr + 1);
683 if (*offset == (char *)__wine_call_from_32_regs - (char *)(offset + 1)) return TRUE;
684 /* now check if call target is an import table jump to __wine_call_from_32_regs */
685 addr = (BYTE *)(offset + 1) + *offset;
686 if (addr[0] != 0xff || addr[1] != 0x25) return FALSE; /* not an indirect jmp */
687 ptr = *(void **)(addr + 2); /* get indirect jmp target address */
688 return (*(char **)ptr == (char *)__wine_call_from_32_regs);
692 /***********************************************************************
693 * RELAY_GetProcAddress
695 * Return the proc address to use for a given function.
697 FARPROC RELAY_GetProcAddress( HMODULE module, IMAGE_EXPORT_DIRECTORY *exports,
698 DWORD exp_size, FARPROC proc, const WCHAR *user )
700 DEBUG_ENTRY_POINT *debug = (DEBUG_ENTRY_POINT *)proc;
701 DEBUG_ENTRY_POINT *list = (DEBUG_ENTRY_POINT *)((char *)exports + exp_size);
703 if (debug < list || debug >= list + exports->NumberOfFunctions) return proc;
704 if (list + (debug - list) != debug) return proc; /* not a valid address */
705 if (check_relay_from_module( user )) return proc; /* we want to relay it */
706 if (!debug->call) return proc; /* not a normal function */
707 if (debug->call != 0xe8 && debug->call != 0xe9) return proc; /* not a debug thunk at all */
708 return debug->orig;
712 /***********************************************************************
713 * RELAY_SetupDLL
715 * Setup relay debugging for a built-in dll.
717 void RELAY_SetupDLL( HMODULE module )
719 IMAGE_EXPORT_DIRECTORY *exports;
720 DEBUG_ENTRY_POINT *debug;
721 DWORD *funcs;
722 int i;
723 const char *name;
724 char *p, dllname[80];
725 DWORD size;
727 exports = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size );
728 if (!exports) return;
729 debug = (DEBUG_ENTRY_POINT *)((char *)exports + size);
730 funcs = (DWORD *)((char *)module + exports->AddressOfFunctions);
731 strcpy( dllname, (char *)module + exports->Name );
732 p = dllname + strlen(dllname) - 4;
733 if (p > dllname && !strcasecmp( p, ".dll" )) *p = 0;
735 for (i = 0; i < exports->NumberOfFunctions; i++, funcs++, debug++)
737 int on = 1;
739 if (!debug->call) continue; /* not a normal function */
740 if (debug->call != 0xe8 && debug->call != 0xe9) break; /* not a debug thunk at all */
742 name = find_exported_name( module, exports, i + exports->Base );
743 on = check_relay_include( dllname, i + exports->Base, name );
745 if (on)
747 debug->call = 0xe8; /* call relative */
748 if (is_register_entry_point( debug->orig ))
749 debug->callfrom32 = (char *)RELAY_CallFrom32Regs - (char *)&debug->ret;
750 else
751 debug->callfrom32 = (char *)RELAY_CallFrom32 - (char *)&debug->ret;
753 else
755 debug->call = 0xe9; /* jmp relative */
756 debug->callfrom32 = (char *)debug->orig - (char *)&debug->ret;
758 *funcs = (char *)debug - (char *)module;
763 /***********************************************************************
764 * SNOOP_ShowDebugmsgSnoop
766 * Simple function to decide if a particular debugging message is
767 * wanted.
769 static BOOL SNOOP_ShowDebugmsgSnoop(const char *module, int ordinal, const char *func)
771 if (debug_snoop_excludelist && check_list( module, ordinal, func, debug_snoop_excludelist ))
772 return FALSE;
773 if (debug_snoop_includelist && !check_list( module, ordinal, func, debug_snoop_includelist ))
774 return FALSE;
775 return TRUE;
779 /***********************************************************************
780 * SNOOP_SetupDLL
782 * Setup snoop debugging for a native dll.
784 void SNOOP_SetupDLL(HMODULE hmod)
786 SNOOP_DLL **dll = &firstdll;
787 char *p, *name;
788 void *addr;
789 SIZE_T size;
790 IMAGE_EXPORT_DIRECTORY *exports;
792 exports = RtlImageDirectoryEntryToData( hmod, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size );
793 if (!exports) return;
794 name = (char *)hmod + exports->Name;
796 TRACE("hmod=%p, name=%s\n", hmod, name);
798 while (*dll) {
799 if ((*dll)->hmod == hmod)
801 /* another dll, loaded at the same address */
802 addr = (*dll)->funs;
803 size = (*dll)->nrofordinals * sizeof(SNOOP_FUN);
804 NtFreeVirtualMemory(GetCurrentProcess(), &addr, &size, MEM_RELEASE);
805 break;
807 dll = &((*dll)->next);
809 if (*dll)
810 *dll = RtlReAllocateHeap(GetProcessHeap(),
811 HEAP_ZERO_MEMORY, *dll,
812 sizeof(SNOOP_DLL) + strlen(name));
813 else
814 *dll = RtlAllocateHeap(GetProcessHeap(),
815 HEAP_ZERO_MEMORY,
816 sizeof(SNOOP_DLL) + strlen(name));
817 (*dll)->hmod = hmod;
818 (*dll)->ordbase = exports->Base;
819 (*dll)->nrofordinals = exports->NumberOfFunctions;
820 strcpy( (*dll)->name, name );
821 p = (*dll)->name + strlen((*dll)->name) - 4;
822 if (p > (*dll)->name && !strcasecmp( p, ".dll" )) *p = 0;
824 size = exports->NumberOfFunctions * sizeof(SNOOP_FUN);
825 NtAllocateVirtualMemory(GetCurrentProcess(), &addr, NULL, &size,
826 MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
827 if (!addr) {
828 RtlFreeHeap(ntdll_get_process_heap(),0,*dll);
829 FIXME("out of memory\n");
830 return;
832 (*dll)->funs = addr;
833 memset((*dll)->funs,0,size);
837 /***********************************************************************
838 * SNOOP_GetProcAddress
840 * Return the proc address to use for a given function.
842 FARPROC SNOOP_GetProcAddress( HMODULE hmod, IMAGE_EXPORT_DIRECTORY *exports,
843 DWORD exp_size, FARPROC origfun, DWORD ordinal )
845 int i;
846 const char *ename;
847 WORD *ordinals;
848 DWORD *names;
849 SNOOP_DLL *dll = firstdll;
850 SNOOP_FUN *fun;
851 IMAGE_SECTION_HEADER *sec;
853 if (!TRACE_ON(snoop)) return origfun;
854 if (!*(LPBYTE)origfun) /* 0x00 is an imposs. opcode, poss. dataref. */
855 return origfun;
857 sec = RtlImageRvaToSection( RtlImageNtHeader(hmod), hmod, (char *)origfun - (char *)hmod );
859 if (!sec || !(sec->Characteristics & IMAGE_SCN_CNT_CODE))
860 return origfun; /* most likely a data reference */
862 while (dll) {
863 if (hmod == dll->hmod)
864 break;
865 dll=dll->next;
867 if (!dll) /* probably internal */
868 return origfun;
870 /* try to find a name for it */
871 ename = NULL;
872 names = (DWORD *)((char *)hmod + exports->AddressOfNames);
873 ordinals = (WORD *)((char *)hmod + exports->AddressOfNameOrdinals);
874 if (names) for (i = 0; i < exports->NumberOfNames; i++)
876 if (ordinals[i] == ordinal)
878 ename = (char *)hmod + names[i];
879 break;
882 if (!SNOOP_ShowDebugmsgSnoop(dll->name,ordinal,ename))
883 return origfun;
884 assert(ordinal < dll->nrofordinals);
885 fun = dll->funs+ordinal;
886 if (!fun->name)
888 fun->name = ename;
889 fun->lcall = 0xe8;
890 /* NOTE: origreturn struct member MUST come directly after snoopentry */
891 fun->snoopentry = (char*)SNOOP_Entry-((char*)(&fun->nrofargs));
892 fun->origfun = origfun;
893 fun->nrofargs = -1;
895 return (FARPROC)&(fun->lcall);
898 static void SNOOP_PrintArg(DWORD x)
900 int i,nostring;
902 DPRINTF("%08lx",x);
903 if (!HIWORD(x) || TRACE_ON(seh)) return; /* trivial reject to avoid faults */
904 __TRY
906 LPBYTE s=(LPBYTE)x;
907 i=0;nostring=0;
908 while (i<80) {
909 if (s[i]==0) break;
910 if (s[i]<0x20) {nostring=1;break;}
911 if (s[i]>=0x80) {nostring=1;break;}
912 i++;
914 if (!nostring && i > 5)
915 DPRINTF(" %s",debugstr_an((LPSTR)x,i));
916 else /* try unicode */
918 LPWSTR s=(LPWSTR)x;
919 i=0;nostring=0;
920 while (i<80) {
921 if (s[i]==0) break;
922 if (s[i]<0x20) {nostring=1;break;}
923 if (s[i]>0x100) {nostring=1;break;}
924 i++;
926 if (!nostring && i > 5) DPRINTF(" %s",debugstr_wn((LPWSTR)x,i));
929 __EXCEPT(page_fault)
932 __ENDTRY
935 #define CALLER1REF (*(DWORD*)context->Esp)
937 void WINAPI SNOOP_DoEntry( CONTEXT86 *context )
939 DWORD ordinal=0,entry = context->Eip - 5;
940 SNOOP_DLL *dll = firstdll;
941 SNOOP_FUN *fun = NULL;
942 SNOOP_RETURNENTRIES **rets = &firstrets;
943 SNOOP_RETURNENTRY *ret;
944 int i=0, max;
946 while (dll) {
947 if ( ((char*)entry>=(char*)dll->funs) &&
948 ((char*)entry<=(char*)(dll->funs+dll->nrofordinals))
950 fun = (SNOOP_FUN*)entry;
951 ordinal = fun-dll->funs;
952 break;
954 dll=dll->next;
956 if (!dll) {
957 FIXME("entrypoint 0x%08lx not found\n",entry);
958 return; /* oops */
960 /* guess cdecl ... */
961 if (fun->nrofargs<0) {
962 /* Typical cdecl return frame is:
963 * add esp, xxxxxxxx
964 * which has (for xxxxxxxx up to 255 the opcode "83 C4 xx".
965 * (after that 81 C2 xx xx xx xx)
967 LPBYTE reteip = (LPBYTE)CALLER1REF;
969 if (reteip) {
970 if ((reteip[0]==0x83)&&(reteip[1]==0xc4))
971 fun->nrofargs=reteip[2]/4;
976 while (*rets) {
977 for (i=0;i<sizeof((*rets)->entry)/sizeof((*rets)->entry[0]);i++)
978 if (!(*rets)->entry[i].origreturn)
979 break;
980 if (i!=sizeof((*rets)->entry)/sizeof((*rets)->entry[0]))
981 break;
982 rets = &((*rets)->next);
984 if (!*rets) {
985 SIZE_T size = 4096;
986 VOID* addr;
988 NtAllocateVirtualMemory(GetCurrentProcess(), &addr, NULL, &size,
989 MEM_COMMIT | MEM_RESERVE,
990 PAGE_EXECUTE_READWRITE);
991 if (!addr) return;
992 *rets = addr;
993 memset(*rets,0,4096);
994 i = 0; /* entry 0 is free */
996 ret = &((*rets)->entry[i]);
997 ret->lcall = 0xe8;
998 /* NOTE: origreturn struct member MUST come directly after snoopret */
999 ret->snoopret = ((char*)SNOOP_Return)-(char*)(&ret->origreturn);
1000 ret->origreturn = (FARPROC)CALLER1REF;
1001 CALLER1REF = (DWORD)&ret->lcall;
1002 ret->dll = dll;
1003 ret->args = NULL;
1004 ret->ordinal = ordinal;
1005 ret->origESP = context->Esp;
1007 context->Eip = (DWORD)fun->origfun;
1009 if (fun->name) DPRINTF("%04lx:CALL %s.%s(",GetCurrentThreadId(),dll->name,fun->name);
1010 else DPRINTF("%04lx:CALL %s.%ld(",GetCurrentThreadId(),dll->name,dll->ordbase+ordinal);
1011 if (fun->nrofargs>0) {
1012 max = fun->nrofargs; if (max>16) max=16;
1013 for (i=0;i<max;i++)
1015 SNOOP_PrintArg(*(DWORD*)(context->Esp + 4 + sizeof(DWORD)*i));
1016 if (i<fun->nrofargs-1) DPRINTF(",");
1018 if (max!=fun->nrofargs)
1019 DPRINTF(" ...");
1020 } else if (fun->nrofargs<0) {
1021 DPRINTF("<unknown, check return>");
1022 ret->args = RtlAllocateHeap(ntdll_get_process_heap(),
1023 0,16*sizeof(DWORD));
1024 memcpy(ret->args,(LPBYTE)(context->Esp + 4),sizeof(DWORD)*16);
1026 DPRINTF(") ret=%08lx\n",(DWORD)ret->origreturn);
1030 void WINAPI SNOOP_DoReturn( CONTEXT86 *context )
1032 SNOOP_RETURNENTRY *ret = (SNOOP_RETURNENTRY*)(context->Eip - 5);
1033 SNOOP_FUN *fun = &ret->dll->funs[ret->ordinal];
1035 /* We haven't found out the nrofargs yet. If we called a cdecl
1036 * function it is too late anyway and we can just set '0' (which
1037 * will be the difference between orig and current ESP
1038 * If stdcall -> everything ok.
1040 if (ret->dll->funs[ret->ordinal].nrofargs<0)
1041 ret->dll->funs[ret->ordinal].nrofargs=(context->Esp - ret->origESP-4)/4;
1042 context->Eip = (DWORD)ret->origreturn;
1043 if (ret->args) {
1044 int i,max;
1046 if (fun->name)
1047 DPRINTF("%04lx:RET %s.%s(", GetCurrentThreadId(), ret->dll->name, fun->name);
1048 else
1049 DPRINTF("%04lx:RET %s.%ld(", GetCurrentThreadId(),
1050 ret->dll->name,ret->dll->ordbase+ret->ordinal);
1052 max = fun->nrofargs;
1053 if (max>16) max=16;
1055 for (i=0;i<max;i++)
1057 SNOOP_PrintArg(ret->args[i]);
1058 if (i<max-1) DPRINTF(",");
1060 DPRINTF(") retval=%08lx ret=%08lx\n",
1061 context->Eax,(DWORD)ret->origreturn );
1062 RtlFreeHeap(ntdll_get_process_heap(),0,ret->args);
1063 ret->args = NULL;
1065 else
1067 if (fun->name)
1068 DPRINTF("%04lx:RET %s.%s() retval=%08lx ret=%08lx\n",
1069 GetCurrentThreadId(),
1070 ret->dll->name, fun->name, context->Eax, (DWORD)ret->origreturn);
1071 else
1072 DPRINTF("%04lx:RET %s.%ld() retval=%08lx ret=%08lx\n",
1073 GetCurrentThreadId(),
1074 ret->dll->name,ret->dll->ordbase+ret->ordinal,
1075 context->Eax, (DWORD)ret->origreturn);
1077 ret->origreturn = NULL; /* mark as empty */
1080 /* assembly wrappers that save the context */
1081 __ASM_GLOBAL_FUNC( SNOOP_Entry,
1082 "call " __ASM_NAME("__wine_call_from_32_regs") "\n\t"
1083 ".long " __ASM_NAME("SNOOP_DoEntry") ",0" );
1084 __ASM_GLOBAL_FUNC( SNOOP_Return,
1085 "call " __ASM_NAME("__wine_call_from_32_regs") "\n\t"
1086 ".long " __ASM_NAME("SNOOP_DoReturn") ",0" );
1088 #else /* __i386__ */
1090 FARPROC RELAY_GetProcAddress( HMODULE module, IMAGE_EXPORT_DIRECTORY *exports,
1091 DWORD exp_size, FARPROC proc, const WCHAR *user )
1093 return proc;
1096 FARPROC SNOOP_GetProcAddress( HMODULE hmod, IMAGE_EXPORT_DIRECTORY *exports,
1097 DWORD exp_size, FARPROC origfun, DWORD ordinal )
1099 return origfun;
1102 void RELAY_SetupDLL( HMODULE module )
1106 void SNOOP_SetupDLL( HMODULE hmod )
1108 FIXME("snooping works only on i386 for now.\n");
1111 #endif /* __i386__ */