user32/test: Destroy no longer needed test dialog windows.
[wine] / dlls / ntdll / relay.c
1 /*
2  * Win32 relay and snoop functions
3  *
4  * Copyright 1997 Alexandre Julliard
5  * Copyright 1998 Marcus Meissner
6  *
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.
11  *
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.
16  *
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <assert.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29
30 #include "windef.h"
31 #include "winternl.h"
32 #include "wine/exception.h"
33 #include "ntdll_misc.h"
34 #include "wine/unicode.h"
35 #include "wine/debug.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(relay);
38
39 #ifdef __i386__
40
41 WINE_DECLARE_DEBUG_CHANNEL(snoop);
42 WINE_DECLARE_DEBUG_CHANNEL(seh);
43
44 struct relay_descr  /* descriptor for a module */
45 {
46     void               *magic;               /* signature */
47     void               *relay_from_32;       /* functions to call from relay thunks */
48     void               *relay_from_32_regs;
49     void               *private;             /* reserved for the relay code private data */
50     const char         *entry_point_base;    /* base address of entry point thunks */
51     const unsigned int *entry_point_offsets; /* offsets of entry points thunks */
52     const unsigned int *arg_types;           /* table of argument types for all entry points */
53 };
54
55 #define RELAY_DESCR_MAGIC  ((void *)0xdeb90001)
56
57 /* private data built at dll load time */
58
59 struct relay_entry_point
60 {
61     void       *orig_func;    /* original entry point function */
62     const char *name;         /* function name (if any) */
63 };
64
65 struct relay_private_data
66 {
67     HMODULE                  module;            /* module handle of this dll */
68     unsigned int             base;              /* ordinal base */
69     char                     dllname[40];       /* dll name (without .dll extension) */
70     struct relay_entry_point entry_points[1];   /* list of dll entry points */
71 };
72
73 static const WCHAR **debug_relay_excludelist;
74 static const WCHAR **debug_relay_includelist;
75 static const WCHAR **debug_snoop_excludelist;
76 static const WCHAR **debug_snoop_includelist;
77 static const WCHAR **debug_from_relay_excludelist;
78 static const WCHAR **debug_from_relay_includelist;
79 static const WCHAR **debug_from_snoop_excludelist;
80 static const WCHAR **debug_from_snoop_includelist;
81
82 static BOOL init_done;
83
84 /* compare an ASCII and a Unicode string without depending on the current codepage */
85 static inline int strcmpAW( const char *strA, const WCHAR *strW )
86 {
87     while (*strA && ((unsigned char)*strA == *strW)) { strA++; strW++; }
88     return (unsigned char)*strA - *strW;
89 }
90
91 /* compare an ASCII and a Unicode string without depending on the current codepage */
92 static inline int strncmpiAW( const char *strA, const WCHAR *strW, int n )
93 {
94     int ret = 0;
95     for ( ; n > 0; n--, strA++, strW++)
96         if ((ret = toupperW((unsigned char)*strA) - toupperW(*strW)) || !*strA) break;
97     return ret;
98 }
99
100 /***********************************************************************
101  *           build_list
102  *
103  * Build a function list from a ';'-separated string.
104  */
105 static const WCHAR **build_list( const WCHAR *buffer )
106 {
107     int count = 1;
108     const WCHAR *p = buffer;
109     const WCHAR **ret;
110
111     while ((p = strchrW( p, ';' )))
112     {
113         count++;
114         p++;
115     }
116     /* allocate count+1 pointers, plus the space for a copy of the string */
117     if ((ret = RtlAllocateHeap( GetProcessHeap(), 0,
118                                 (count+1) * sizeof(WCHAR*) + (strlenW(buffer)+1) * sizeof(WCHAR) )))
119     {
120         WCHAR *str = (WCHAR *)(ret + count + 1);
121         WCHAR *p = str;
122
123         strcpyW( str, buffer );
124         count = 0;
125         for (;;)
126         {
127             ret[count++] = p;
128             if (!(p = strchrW( p, ';' ))) break;
129             *p++ = 0;
130         }
131         ret[count++] = NULL;
132     }
133     return ret;
134 }
135
136
137 /***********************************************************************
138  *           init_debug_lists
139  *
140  * Build the relay include/exclude function lists.
141  */
142 static void init_debug_lists(void)
143 {
144     OBJECT_ATTRIBUTES attr;
145     UNICODE_STRING name;
146     char buffer[1024];
147     HANDLE root, hkey;
148     DWORD count;
149     WCHAR *str;
150     static const WCHAR configW[] = {'S','o','f','t','w','a','r','e','\\',
151                                     'W','i','n','e','\\',
152                                     'D','e','b','u','g',0};
153     static const WCHAR RelayIncludeW[] = {'R','e','l','a','y','I','n','c','l','u','d','e',0};
154     static const WCHAR RelayExcludeW[] = {'R','e','l','a','y','E','x','c','l','u','d','e',0};
155     static const WCHAR SnoopIncludeW[] = {'S','n','o','o','p','I','n','c','l','u','d','e',0};
156     static const WCHAR SnoopExcludeW[] = {'S','n','o','o','p','E','x','c','l','u','d','e',0};
157     static const WCHAR RelayFromIncludeW[] = {'R','e','l','a','y','F','r','o','m','I','n','c','l','u','d','e',0};
158     static const WCHAR RelayFromExcludeW[] = {'R','e','l','a','y','F','r','o','m','E','x','c','l','u','d','e',0};
159     static const WCHAR SnoopFromIncludeW[] = {'S','n','o','o','p','F','r','o','m','I','n','c','l','u','d','e',0};
160     static const WCHAR SnoopFromExcludeW[] = {'S','n','o','o','p','F','r','o','m','E','x','c','l','u','d','e',0};
161
162     if (init_done) return;
163     init_done = TRUE;
164
165     RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
166     attr.Length = sizeof(attr);
167     attr.RootDirectory = root;
168     attr.ObjectName = &name;
169     attr.Attributes = 0;
170     attr.SecurityDescriptor = NULL;
171     attr.SecurityQualityOfService = NULL;
172     RtlInitUnicodeString( &name, configW );
173
174     /* @@ Wine registry key: HKCU\Software\Wine\Debug */
175     if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) hkey = 0;
176     NtClose( root );
177     if (!hkey) return;
178
179     str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)buffer)->Data;
180     RtlInitUnicodeString( &name, RelayIncludeW );
181     if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
182     {
183         TRACE("RelayInclude = %s\n", debugstr_w(str) );
184         debug_relay_includelist = build_list( str );
185     }
186
187     RtlInitUnicodeString( &name, RelayExcludeW );
188     if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
189     {
190         TRACE( "RelayExclude = %s\n", debugstr_w(str) );
191         debug_relay_excludelist = build_list( str );
192     }
193
194     RtlInitUnicodeString( &name, SnoopIncludeW );
195     if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
196     {
197         TRACE_(snoop)( "SnoopInclude = %s\n", debugstr_w(str) );
198         debug_snoop_includelist = build_list( str );
199     }
200
201     RtlInitUnicodeString( &name, SnoopExcludeW );
202     if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
203     {
204         TRACE_(snoop)( "SnoopExclude = %s\n", debugstr_w(str) );
205         debug_snoop_excludelist = build_list( str );
206     }
207
208     RtlInitUnicodeString( &name, RelayFromIncludeW );
209     if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
210     {
211         TRACE("RelayFromInclude = %s\n", debugstr_w(str) );
212         debug_from_relay_includelist = build_list( str );
213     }
214
215     RtlInitUnicodeString( &name, RelayFromExcludeW );
216     if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
217     {
218         TRACE( "RelayFromExclude = %s\n", debugstr_w(str) );
219         debug_from_relay_excludelist = build_list( str );
220     }
221
222     RtlInitUnicodeString( &name, SnoopFromIncludeW );
223     if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
224     {
225         TRACE_(snoop)("SnoopFromInclude = %s\n", debugstr_w(str) );
226         debug_from_snoop_includelist = build_list( str );
227     }
228
229     RtlInitUnicodeString( &name, SnoopFromExcludeW );
230     if (!NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count ))
231     {
232         TRACE_(snoop)( "SnoopFromExclude = %s\n", debugstr_w(str) );
233         debug_from_snoop_excludelist = build_list( str );
234     }
235
236     NtClose( hkey );
237 }
238
239
240 /***********************************************************************
241  *           check_list
242  *
243  * Check if a given module and function is in the list.
244  */
245 static BOOL check_list( const char *module, int ordinal, const char *func, const WCHAR *const *list )
246 {
247     char ord_str[10];
248
249     sprintf( ord_str, "%d", ordinal );
250     for(; *list; list++)
251     {
252         const WCHAR *p = strrchrW( *list, '.' );
253         if (p && p > *list)  /* check module and function */
254         {
255             int len = p - *list;
256             if (strncmpiAW( module, *list, len-1 ) || module[len]) continue;
257             if (p[1] == '*' && !p[2]) return TRUE;
258             if (!strcmpAW( ord_str, p + 1 )) return TRUE;
259             if (func && !strcmpAW( func, p + 1 )) return TRUE;
260         }
261         else  /* function only */
262         {
263             if (func && !strcmpAW( func, *list )) return TRUE;
264         }
265     }
266     return FALSE;
267 }
268
269
270 /***********************************************************************
271  *           check_relay_include
272  *
273  * Check if a given function must be included in the relay output.
274  */
275 static BOOL check_relay_include( const char *module, int ordinal, const char *func )
276 {
277     if (debug_relay_excludelist && check_list( module, ordinal, func, debug_relay_excludelist ))
278         return FALSE;
279     if (debug_relay_includelist && !check_list( module, ordinal, func, debug_relay_includelist ))
280         return FALSE;
281     return TRUE;
282 }
283
284 /***********************************************************************
285  *           check_from_module
286  *
287  * Check if calls from a given module must be included in the relay/snoop output,
288  * given the exclusion and inclusion lists.
289  */
290 static BOOL check_from_module( const WCHAR **includelist, const WCHAR **excludelist, const WCHAR *module )
291 {
292     static const WCHAR dllW[] = {'.','d','l','l',0 };
293     const WCHAR **listitem;
294     BOOL show;
295
296     if (!module) return TRUE;
297     if (!includelist && !excludelist) return TRUE;
298     if (excludelist)
299     {
300         show = TRUE;
301         listitem = excludelist;
302     }
303     else
304     {
305         show = FALSE;
306         listitem = includelist;
307     }
308     for(; *listitem; listitem++)
309     {
310         int len;
311
312         if (!strcmpiW( *listitem, module )) return !show;
313         len = strlenW( *listitem );
314         if (!strncmpiW( *listitem, module, len ) && !strcmpiW( module + len, dllW ))
315             return !show;
316     }
317     return show;
318 }
319
320 /***********************************************************************
321  *           RELAY_PrintArgs
322  */
323 static inline void RELAY_PrintArgs( const int *args, int nb_args, unsigned int typemask )
324 {
325     while (nb_args--)
326     {
327         if ((typemask & 3) && HIWORD(*args))
328         {
329             if (typemask & 2)
330                 DPRINTF( "%08x %s", *args, debugstr_w((LPCWSTR)*args) );
331             else
332                 DPRINTF( "%08x %s", *args, debugstr_a((LPCSTR)*args) );
333         }
334         else DPRINTF( "%08x", *args );
335         if (nb_args) DPRINTF( "," );
336         args++;
337         typemask >>= 2;
338     }
339 }
340
341 extern LONGLONG call_entry_point( void *func, int nb_args, const int *args );
342 __ASM_GLOBAL_FUNC( call_entry_point,
343                    "\tpushl %ebp\n"
344                    "\tmovl %esp,%ebp\n"
345                    "\tpushl %esi\n"
346                    "\tpushl %edi\n"
347                    "\tmovl 12(%ebp),%edx\n"
348                    "\tshll $2,%edx\n"
349                    "\tjz 1f\n"
350                    "\tsubl %edx,%esp\n"
351                    "\tandl $~15,%esp\n"
352                    "\tmovl 12(%ebp),%ecx\n"
353                    "\tmovl 16(%ebp),%esi\n"
354                    "\tmovl %esp,%edi\n"
355                    "\tcld\n"
356                    "\trep; movsl\n"
357                    "1:\tcall *8(%ebp)\n"
358                    "\tleal -8(%ebp),%esp\n"
359                    "\tpopl %edi\n"
360                    "\tpopl %esi\n"
361                    "\tpopl %ebp\n"
362                    "\tret" )
363
364
365 /***********************************************************************
366  *           relay_call_from_32
367  *
368  * stack points to the return address, i.e. the first argument is stack[1].
369  */
370 static LONGLONG WINAPI relay_call_from_32( struct relay_descr *descr, unsigned int idx, const int *stack )
371 {
372     LONGLONG ret;
373     WORD ordinal = LOWORD(idx);
374     BYTE nb_args = LOBYTE(HIWORD(idx));
375     BYTE flags   = HIBYTE(HIWORD(idx));
376     struct relay_private_data *data = descr->private;
377     struct relay_entry_point *entry_point = data->entry_points + ordinal;
378
379     if (!TRACE_ON(relay))
380         ret = call_entry_point( entry_point->orig_func, nb_args, stack + 1 );
381     else
382     {
383         if (entry_point->name)
384             DPRINTF( "%04x:Call %s.%s(", GetCurrentThreadId(), data->dllname, entry_point->name );
385         else
386             DPRINTF( "%04x:Call %s.%u(", GetCurrentThreadId(), data->dllname, data->base + ordinal );
387         RELAY_PrintArgs( stack + 1, nb_args, descr->arg_types[ordinal] );
388         DPRINTF( ") ret=%08x\n", stack[0] );
389
390         ret = call_entry_point( entry_point->orig_func, nb_args, stack + 1 );
391
392         if (entry_point->name)
393             DPRINTF( "%04x:Ret  %s.%s()", GetCurrentThreadId(), data->dllname, entry_point->name );
394         else
395             DPRINTF( "%04x:Ret  %s.%u()", GetCurrentThreadId(), data->dllname, data->base + ordinal );
396
397         if (flags & 1)  /* 64-bit return value */
398             DPRINTF( " retval=%08x%08x ret=%08x\n",
399                      (UINT)(ret >> 32), (UINT)ret, stack[0] );
400         else
401             DPRINTF( " retval=%08x ret=%08x\n", (UINT)ret, stack[0] );
402     }
403     return ret;
404 }
405
406
407 /***********************************************************************
408  *           relay_call_from_32_regs
409  */
410 void WINAPI __regs_relay_call_from_32_regs( struct relay_descr *descr, unsigned int idx,
411                                             unsigned int orig_eax, unsigned int ret_addr,
412                                             CONTEXT86 *context )
413 {
414     WORD ordinal = LOWORD(idx);
415     BYTE nb_args = LOBYTE(HIWORD(idx));
416     BYTE flags   = HIBYTE(HIWORD(idx));
417     struct relay_private_data *data = descr->private;
418     struct relay_entry_point *entry_point = data->entry_points + ordinal;
419     BYTE *orig_func = entry_point->orig_func;
420     int *args = (int *)context->Esp;
421     int args_copy[32];
422
423     /* restore the context to what it was before the relay thunk */
424     context->Eax = orig_eax;
425     context->Eip = ret_addr;
426     if (flags & 2)  /* stdcall */
427         context->Esp += nb_args * sizeof(int);
428
429     if (TRACE_ON(relay))
430     {
431         if (entry_point->name)
432             DPRINTF( "%04x:Call %s.%s(", GetCurrentThreadId(), data->dllname, entry_point->name );
433         else
434             DPRINTF( "%04x:Call %s.%u(", GetCurrentThreadId(), data->dllname, data->base + ordinal );
435         RELAY_PrintArgs( args, nb_args, descr->arg_types[ordinal] );
436         DPRINTF( ") ret=%08x\n", ret_addr );
437
438         DPRINTF( "%04x:  eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x "
439                  "ebp=%08x esp=%08x ds=%04x es=%04x fs=%04x gs=%04x flags=%08x\n",
440                  GetCurrentThreadId(), context->Eax, context->Ebx, context->Ecx,
441                  context->Edx, context->Esi, context->Edi, context->Ebp, context->Esp,
442                  context->SegDs, context->SegEs, context->SegFs, context->SegGs, context->EFlags );
443
444         assert( orig_func[0] == 0x50 /* pushl %eax */ );
445         assert( orig_func[1] == 0xe8 /* call */ );
446     }
447
448     /* now call the real function */
449
450     memcpy( args_copy, args, nb_args * sizeof(args[0]) );
451     args_copy[nb_args++] = (int)context;  /* append context argument */
452
453     call_entry_point( orig_func + 6 + *(int *)(orig_func + 6), nb_args, args_copy );
454
455
456     if (TRACE_ON(relay))
457     {
458         if (entry_point->name)
459             DPRINTF( "%04x:Ret  %s.%s() retval=%08x ret=%08x\n",
460                      GetCurrentThreadId(), data->dllname, entry_point->name,
461                      context->Eax, context->Eip );
462         else
463             DPRINTF( "%04x:Ret  %s.%u() retval=%08x ret=%08x\n",
464                      GetCurrentThreadId(), data->dllname, data->base + ordinal,
465                      context->Eax, context->Eip );
466         DPRINTF( "%04x:  eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x "
467                  "ebp=%08x esp=%08x ds=%04x es=%04x fs=%04x gs=%04x flags=%08x\n",
468                  GetCurrentThreadId(), context->Eax, context->Ebx, context->Ecx,
469                  context->Edx, context->Esi, context->Edi, context->Ebp, context->Esp,
470                  context->SegDs, context->SegEs, context->SegFs, context->SegGs, context->EFlags );
471     }
472 }
473 extern void WINAPI relay_call_from_32_regs(void);
474 DEFINE_REGS_ENTRYPOINT( relay_call_from_32_regs, 16, 16 )
475
476
477 /***********************************************************************
478  *           RELAY_GetProcAddress
479  *
480  * Return the proc address to use for a given function.
481  */
482 FARPROC RELAY_GetProcAddress( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports,
483                               DWORD exp_size, FARPROC proc, DWORD ordinal, const WCHAR *user )
484 {
485     struct relay_private_data *data;
486     const struct relay_descr *descr = (const struct relay_descr *)((const char *)exports + exp_size);
487
488     if (descr->magic != RELAY_DESCR_MAGIC || !(data = descr->private)) return proc;  /* no relay data */
489     if (!data->entry_points[ordinal].orig_func) return proc;  /* not a relayed function */
490     if (check_from_module( debug_from_relay_includelist, debug_from_relay_excludelist, user ))
491         return proc;  /* we want to relay it */
492     return data->entry_points[ordinal].orig_func;
493 }
494
495
496 /***********************************************************************
497  *           RELAY_SetupDLL
498  *
499  * Setup relay debugging for a built-in dll.
500  */
501 void RELAY_SetupDLL( HMODULE module )
502 {
503     IMAGE_EXPORT_DIRECTORY *exports;
504     DWORD *funcs;
505     unsigned int i, len;
506     DWORD size, entry_point_rva;
507     struct relay_descr *descr;
508     struct relay_private_data *data;
509     const WORD *ordptr;
510
511     if (!init_done) init_debug_lists();
512
513     exports = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size );
514     if (!exports) return;
515
516     descr = (struct relay_descr *)((char *)exports + size);
517     if (descr->magic != RELAY_DESCR_MAGIC) return;
518
519     if (!(data = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data) +
520                                   (exports->NumberOfFunctions-1) * sizeof(data->entry_points) )))
521         return;
522
523     descr->relay_from_32 = relay_call_from_32;
524     descr->relay_from_32_regs = relay_call_from_32_regs;
525     descr->private = data;
526
527     data->module = module;
528     data->base   = exports->Base;
529     len = strlen( (char *)module + exports->Name );
530     if (len > 4 && !strcasecmp( (char *)module + exports->Name + len - 4, ".dll" )) len -= 4;
531     len = min( len, sizeof(data->dllname) - 1 );
532     memcpy( data->dllname, (char *)module + exports->Name, len );
533     data->dllname[len] = 0;
534
535     /* fetch name pointer for all entry points and store them in the private structure */
536
537     ordptr = (const WORD *)((char *)module + exports->AddressOfNameOrdinals);
538     for (i = 0; i < exports->NumberOfNames; i++, ordptr++)
539     {
540         DWORD name_rva = ((DWORD*)((char *)module + exports->AddressOfNames))[i];
541         data->entry_points[*ordptr].name = (const char *)module + name_rva;
542     }
543
544     /* patch the functions in the export table to point to the relay thunks */
545
546     funcs = (DWORD *)((char *)module + exports->AddressOfFunctions);
547     entry_point_rva = (const char *)descr->entry_point_base - (const char *)module;
548     for (i = 0; i < exports->NumberOfFunctions; i++, funcs++)
549     {
550         if (!descr->entry_point_offsets[i]) continue;   /* not a normal function */
551         if (!check_relay_include( data->dllname, i + exports->Base, data->entry_points[i].name ))
552             continue;  /* don't include this entry point */
553
554         data->entry_points[i].orig_func = (char *)module + *funcs;
555         *funcs = entry_point_rva + descr->entry_point_offsets[i];
556     }
557 }
558
559
560
561 /***********************************************************************/
562 /* snoop support */
563 /***********************************************************************/
564
565 #include "pshpack1.h"
566
567 typedef struct
568 {
569         /* code part */
570         BYTE            lcall;          /* 0xe8 call snoopentry (relative) */
571         /* NOTE: If you move snoopentry OR nrofargs fix the relative offset
572          * calculation!
573          */
574         DWORD           snoopentry;     /* SNOOP_Entry relative */
575         /* unreached */
576         int             nrofargs;
577         FARPROC origfun;
578         const char *name;
579 } SNOOP_FUN;
580
581 typedef struct tagSNOOP_DLL {
582         HMODULE hmod;
583         SNOOP_FUN       *funs;
584         DWORD           ordbase;
585         DWORD           nrofordinals;
586         struct tagSNOOP_DLL     *next;
587         char name[1];
588 } SNOOP_DLL;
589
590 typedef struct
591 {
592         /* code part */
593         BYTE            lcall;          /* 0xe8 call snoopret relative*/
594         /* NOTE: If you move snoopret OR origreturn fix the relative offset
595          * calculation!
596          */
597         DWORD           snoopret;       /* SNOOP_Ret relative */
598         /* unreached */
599         FARPROC origreturn;
600         SNOOP_DLL       *dll;
601         DWORD           ordinal;
602         DWORD           origESP;
603         DWORD           *args;          /* saved args across a stdcall */
604 } SNOOP_RETURNENTRY;
605
606 typedef struct tagSNOOP_RETURNENTRIES {
607         SNOOP_RETURNENTRY entry[4092/sizeof(SNOOP_RETURNENTRY)];
608         struct tagSNOOP_RETURNENTRIES   *next;
609 } SNOOP_RETURNENTRIES;
610
611 #include "poppack.h"
612
613 extern void WINAPI SNOOP_Entry(void);
614 extern void WINAPI SNOOP_Return(void);
615
616 static SNOOP_DLL *firstdll;
617 static SNOOP_RETURNENTRIES *firstrets;
618
619
620 /***********************************************************************
621  *          SNOOP_ShowDebugmsgSnoop
622  *
623  * Simple function to decide if a particular debugging message is
624  * wanted.
625  */
626 static BOOL SNOOP_ShowDebugmsgSnoop(const char *module, int ordinal, const char *func)
627 {
628     if (debug_snoop_excludelist && check_list( module, ordinal, func, debug_snoop_excludelist ))
629         return FALSE;
630     if (debug_snoop_includelist && !check_list( module, ordinal, func, debug_snoop_includelist ))
631         return FALSE;
632     return TRUE;
633 }
634
635
636 /***********************************************************************
637  *           SNOOP_SetupDLL
638  *
639  * Setup snoop debugging for a native dll.
640  */
641 void SNOOP_SetupDLL(HMODULE hmod)
642 {
643     SNOOP_DLL **dll = &firstdll;
644     char *p, *name;
645     void *addr;
646     SIZE_T size;
647     ULONG size32;
648     IMAGE_EXPORT_DIRECTORY *exports;
649
650     if (!init_done) init_debug_lists();
651
652     exports = RtlImageDirectoryEntryToData( hmod, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size32 );
653     if (!exports || !exports->NumberOfFunctions) return;
654     name = (char *)hmod + exports->Name;
655     size = size32;
656
657     TRACE_(snoop)("hmod=%p, name=%s\n", hmod, name);
658
659     while (*dll) {
660         if ((*dll)->hmod == hmod)
661         {
662             /* another dll, loaded at the same address */
663             addr = (*dll)->funs;
664             size = (*dll)->nrofordinals * sizeof(SNOOP_FUN);
665             NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE);
666             break;
667         }
668         dll = &((*dll)->next);
669     }
670     if (*dll)
671         *dll = RtlReAllocateHeap(GetProcessHeap(),
672                              HEAP_ZERO_MEMORY, *dll,
673                              sizeof(SNOOP_DLL) + strlen(name));
674     else
675         *dll = RtlAllocateHeap(GetProcessHeap(),
676                              HEAP_ZERO_MEMORY,
677                              sizeof(SNOOP_DLL) + strlen(name));
678     (*dll)->hmod        = hmod;
679     (*dll)->ordbase = exports->Base;
680     (*dll)->nrofordinals = exports->NumberOfFunctions;
681     strcpy( (*dll)->name, name );
682     p = (*dll)->name + strlen((*dll)->name) - 4;
683     if (p > (*dll)->name && !strcasecmp( p, ".dll" )) *p = 0;
684
685     size = exports->NumberOfFunctions * sizeof(SNOOP_FUN);
686     addr = NULL;
687     NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 0, &size,
688                             MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
689     if (!addr) {
690         RtlFreeHeap(GetProcessHeap(),0,*dll);
691         FIXME("out of memory\n");
692         return;
693     }
694     (*dll)->funs = addr;
695     memset((*dll)->funs,0,size);
696 }
697
698
699 /***********************************************************************
700  *           SNOOP_GetProcAddress
701  *
702  * Return the proc address to use for a given function.
703  */
704 FARPROC SNOOP_GetProcAddress( HMODULE hmod, const IMAGE_EXPORT_DIRECTORY *exports,
705                               DWORD exp_size, FARPROC origfun, DWORD ordinal,
706                               const WCHAR *user)
707 {
708     unsigned int i;
709     const char *ename;
710     const WORD *ordinals;
711     const DWORD *names;
712     SNOOP_DLL *dll = firstdll;
713     SNOOP_FUN *fun;
714     const IMAGE_SECTION_HEADER *sec;
715
716     if (!TRACE_ON(snoop)) return origfun;
717     if (!check_from_module( debug_from_snoop_includelist, debug_from_snoop_excludelist, user ))
718         return origfun; /* the calling module was explicitly excluded */
719
720     if (!*(LPBYTE)origfun) /* 0x00 is an imposs. opcode, poss. dataref. */
721         return origfun;
722
723     sec = RtlImageRvaToSection( RtlImageNtHeader(hmod), hmod, (char *)origfun - (char *)hmod );
724
725     if (!sec || !(sec->Characteristics & IMAGE_SCN_CNT_CODE))
726         return origfun;  /* most likely a data reference */
727
728     while (dll) {
729         if (hmod == dll->hmod)
730             break;
731         dll = dll->next;
732     }
733     if (!dll)   /* probably internal */
734         return origfun;
735
736     /* try to find a name for it */
737     ename = NULL;
738     names = (const DWORD *)((const char *)hmod + exports->AddressOfNames);
739     ordinals = (const WORD *)((const char *)hmod + exports->AddressOfNameOrdinals);
740     if (names) for (i = 0; i < exports->NumberOfNames; i++)
741     {
742         if (ordinals[i] == ordinal)
743         {
744             ename = (const char *)hmod + names[i];
745             break;
746         }
747     }
748     if (!SNOOP_ShowDebugmsgSnoop(dll->name,ordinal,ename))
749         return origfun;
750     assert(ordinal < dll->nrofordinals);
751     fun = dll->funs + ordinal;
752     if (!fun->name)
753     {
754         fun->name       = ename;
755         fun->lcall      = 0xe8;
756         /* NOTE: origreturn struct member MUST come directly after snoopentry */
757         fun->snoopentry = (char*)SNOOP_Entry-((char*)(&fun->nrofargs));
758         fun->origfun    = origfun;
759         fun->nrofargs   = -1;
760     }
761     return (FARPROC)&(fun->lcall);
762 }
763
764 static void SNOOP_PrintArg(DWORD x)
765 {
766     int i,nostring;
767
768     DPRINTF("%08x",x);
769     if (!HIWORD(x) || TRACE_ON(seh)) return; /* trivial reject to avoid faults */
770     __TRY
771     {
772         LPBYTE s=(LPBYTE)x;
773         i=0;nostring=0;
774         while (i<80) {
775             if (s[i]==0) break;
776             if (s[i]<0x20) {nostring=1;break;}
777             if (s[i]>=0x80) {nostring=1;break;}
778             i++;
779         }
780         if (!nostring && i > 5)
781             DPRINTF(" %s",debugstr_an((LPSTR)x,i));
782         else  /* try unicode */
783         {
784             LPWSTR s=(LPWSTR)x;
785             i=0;nostring=0;
786             while (i<80) {
787                 if (s[i]==0) break;
788                 if (s[i]<0x20) {nostring=1;break;}
789                 if (s[i]>0x100) {nostring=1;break;}
790                 i++;
791             }
792             if (!nostring && i > 5) DPRINTF(" %s",debugstr_wn((LPWSTR)x,i));
793         }
794     }
795     __EXCEPT_PAGE_FAULT
796     {
797     }
798     __ENDTRY
799 }
800
801 #define CALLER1REF (*(DWORD*)context->Esp)
802
803 void WINAPI __regs_SNOOP_Entry( CONTEXT86 *context )
804 {
805         DWORD           ordinal=0,entry = context->Eip - 5;
806         SNOOP_DLL       *dll = firstdll;
807         SNOOP_FUN       *fun = NULL;
808         SNOOP_RETURNENTRIES     **rets = &firstrets;
809         SNOOP_RETURNENTRY       *ret;
810         int             i=0, max;
811
812         while (dll) {
813                 if (    ((char*)entry>=(char*)dll->funs)        &&
814                         ((char*)entry<=(char*)(dll->funs+dll->nrofordinals))
815                 ) {
816                         fun = (SNOOP_FUN*)entry;
817                         ordinal = fun-dll->funs;
818                         break;
819                 }
820                 dll=dll->next;
821         }
822         if (!dll) {
823                 FIXME("entrypoint 0x%08x not found\n",entry);
824                 return; /* oops */
825         }
826         /* guess cdecl ... */
827         if (fun->nrofargs<0) {
828                 /* Typical cdecl return frame is:
829                  *     add esp, xxxxxxxx
830                  * which has (for xxxxxxxx up to 255 the opcode "83 C4 xx".
831                  * (after that 81 C2 xx xx xx xx)
832                  */
833                 LPBYTE  reteip = (LPBYTE)CALLER1REF;
834
835                 if (reteip) {
836                         if ((reteip[0]==0x83)&&(reteip[1]==0xc4))
837                                 fun->nrofargs=reteip[2]/4;
838                 }
839         }
840
841
842         while (*rets) {
843                 for (i=0;i<sizeof((*rets)->entry)/sizeof((*rets)->entry[0]);i++)
844                         if (!(*rets)->entry[i].origreturn)
845                                 break;
846                 if (i!=sizeof((*rets)->entry)/sizeof((*rets)->entry[0]))
847                         break;
848                 rets = &((*rets)->next);
849         }
850         if (!*rets) {
851                 SIZE_T size = 4096;
852                 VOID* addr = NULL;
853
854                 NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 0, &size, 
855                                         MEM_COMMIT | MEM_RESERVE,
856                                         PAGE_EXECUTE_READWRITE);
857                 if (!addr) return;
858                 *rets = addr;
859                 memset(*rets,0,4096);
860                 i = 0;  /* entry 0 is free */
861         }
862         ret = &((*rets)->entry[i]);
863         ret->lcall      = 0xe8;
864         /* NOTE: origreturn struct member MUST come directly after snoopret */
865         ret->snoopret   = ((char*)SNOOP_Return)-(char*)(&ret->origreturn);
866         ret->origreturn = (FARPROC)CALLER1REF;
867         CALLER1REF      = (DWORD)&ret->lcall;
868         ret->dll        = dll;
869         ret->args       = NULL;
870         ret->ordinal    = ordinal;
871         ret->origESP    = context->Esp;
872
873         context->Eip = (DWORD)fun->origfun;
874
875         if (fun->name) DPRINTF("%04x:CALL %s.%s(",GetCurrentThreadId(),dll->name,fun->name);
876         else DPRINTF("%04x:CALL %s.%d(",GetCurrentThreadId(),dll->name,dll->ordbase+ordinal);
877         if (fun->nrofargs>0) {
878                 max = fun->nrofargs; if (max>16) max=16;
879                 for (i=0;i<max;i++)
880                 {
881                     SNOOP_PrintArg(*(DWORD*)(context->Esp + 4 + sizeof(DWORD)*i));
882                     if (i<fun->nrofargs-1) DPRINTF(",");
883                 }
884                 if (max!=fun->nrofargs)
885                         DPRINTF(" ...");
886         } else if (fun->nrofargs<0) {
887                 DPRINTF("<unknown, check return>");
888                 ret->args = RtlAllocateHeap(GetProcessHeap(),
889                                             0,16*sizeof(DWORD));
890                 memcpy(ret->args,(LPBYTE)(context->Esp + 4),sizeof(DWORD)*16);
891         }
892         DPRINTF(") ret=%08x\n",(DWORD)ret->origreturn);
893 }
894
895
896 void WINAPI __regs_SNOOP_Return( CONTEXT86 *context )
897 {
898         SNOOP_RETURNENTRY       *ret = (SNOOP_RETURNENTRY*)(context->Eip - 5);
899         SNOOP_FUN *fun = &ret->dll->funs[ret->ordinal];
900
901         /* We haven't found out the nrofargs yet. If we called a cdecl
902          * function it is too late anyway and we can just set '0' (which
903          * will be the difference between orig and current ESP
904          * If stdcall -> everything ok.
905          */
906         if (ret->dll->funs[ret->ordinal].nrofargs<0)
907                 ret->dll->funs[ret->ordinal].nrofargs=(context->Esp - ret->origESP-4)/4;
908         context->Eip = (DWORD)ret->origreturn;
909         if (ret->args) {
910                 int     i,max;
911
912                 if (fun->name)
913                     DPRINTF("%04x:RET  %s.%s(", GetCurrentThreadId(), ret->dll->name, fun->name);
914                 else
915                     DPRINTF("%04x:RET  %s.%d(", GetCurrentThreadId(),
916                             ret->dll->name,ret->dll->ordbase+ret->ordinal);
917
918                 max = fun->nrofargs;
919                 if (max>16) max=16;
920
921                 for (i=0;i<max;i++)
922                 {
923                     SNOOP_PrintArg(ret->args[i]);
924                     if (i<max-1) DPRINTF(",");
925                 }
926                 DPRINTF(") retval=%08x ret=%08x\n",
927                         context->Eax,(DWORD)ret->origreturn );
928                 RtlFreeHeap(GetProcessHeap(),0,ret->args);
929                 ret->args = NULL;
930         }
931         else
932         {
933             if (fun->name)
934                 DPRINTF("%04x:RET  %s.%s() retval=%08x ret=%08x\n",
935                         GetCurrentThreadId(),
936                         ret->dll->name, fun->name, context->Eax, (DWORD)ret->origreturn);
937             else
938                 DPRINTF("%04x:RET  %s.%d() retval=%08x ret=%08x\n",
939                         GetCurrentThreadId(),
940                         ret->dll->name,ret->dll->ordbase+ret->ordinal,
941                         context->Eax, (DWORD)ret->origreturn);
942         }
943         ret->origreturn = NULL; /* mark as empty */
944 }
945
946 /* assembly wrappers that save the context */
947 DEFINE_REGS_ENTRYPOINT( SNOOP_Entry, 0, 0 )
948 DEFINE_REGS_ENTRYPOINT( SNOOP_Return, 0, 0 )
949
950 #else  /* __i386__ */
951
952 FARPROC RELAY_GetProcAddress( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports,
953                               DWORD exp_size, FARPROC proc, DWORD ordinal, const WCHAR *user )
954 {
955     return proc;
956 }
957
958 FARPROC SNOOP_GetProcAddress( HMODULE hmod, const IMAGE_EXPORT_DIRECTORY *exports, DWORD exp_size,
959                               FARPROC origfun, DWORD ordinal, const WCHAR *user )
960 {
961     return origfun;
962 }
963
964 void RELAY_SetupDLL( HMODULE module )
965 {
966 }
967
968 void SNOOP_SetupDLL( HMODULE hmod )
969 {
970     FIXME("snooping works only on i386 for now.\n");
971 }
972
973 #endif /* __i386__ */