ntdll/tests: Try to fail more gracefully on win9x.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 "excpt.h"
33 #include "wine/exception.h"
34 #include "ntdll_misc.h"
35 #include "wine/unicode.h"
36 #include "wine/debug.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(relay);
39 WINE_DECLARE_DEBUG_CHANNEL(snoop);
40 WINE_DECLARE_DEBUG_CHANNEL(seh);
41
42 #ifdef __i386__
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 inline static 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 inline static 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 **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( 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((LPWSTR)*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, 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( "%04lx:Call %s.%s(", GetCurrentThreadId(), data->dllname, entry_point->name );
385         else
386             DPRINTF( "%04lx: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( "%04lx:Ret  %s.%s()", GetCurrentThreadId(), data->dllname, entry_point->name );
394         else
395             DPRINTF( "%04lx: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( "%04lx:Call %s.%s(", GetCurrentThreadId(), data->dllname, entry_point->name );
433         else
434             DPRINTF( "%04lx: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( "%04lx:  eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx "
439                  "ebp=%08lx esp=%08lx ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\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( "%04lx:Ret  %s.%s() retval=%08lx ret=%08lx\n",
460                      GetCurrentThreadId(), data->dllname, entry_point->name,
461                      context->Eax, context->Eip );
462         else
463             DPRINTF( "%04lx:Ret  %s.%u() retval=%08lx ret=%08lx\n",
464                      GetCurrentThreadId(), data->dllname, data->base + ordinal,
465                      context->Eax, context->Eip );
466         DPRINTF( "%04lx:  eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx "
467                  "ebp=%08lx esp=%08lx ds=%04lx es=%04lx fs=%04lx gs=%04lx flags=%08lx\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     IMAGE_EXPORT_DIRECTORY *exports;
648
649     if (!init_done) init_debug_lists();
650
651     exports = RtlImageDirectoryEntryToData( hmod, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size );
652     if (!exports) return;
653     name = (char *)hmod + exports->Name;
654
655     TRACE_(snoop)("hmod=%p, name=%s\n", hmod, name);
656
657     while (*dll) {
658         if ((*dll)->hmod == hmod)
659         {
660             /* another dll, loaded at the same address */
661             addr = (*dll)->funs;
662             size = (*dll)->nrofordinals * sizeof(SNOOP_FUN);
663             NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE);
664             break;
665         }
666         dll = &((*dll)->next);
667     }
668     if (*dll)
669         *dll = RtlReAllocateHeap(GetProcessHeap(),
670                              HEAP_ZERO_MEMORY, *dll,
671                              sizeof(SNOOP_DLL) + strlen(name));
672     else
673         *dll = RtlAllocateHeap(GetProcessHeap(),
674                              HEAP_ZERO_MEMORY,
675                              sizeof(SNOOP_DLL) + strlen(name));
676     (*dll)->hmod        = hmod;
677     (*dll)->ordbase = exports->Base;
678     (*dll)->nrofordinals = exports->NumberOfFunctions;
679     strcpy( (*dll)->name, name );
680     p = (*dll)->name + strlen((*dll)->name) - 4;
681     if (p > (*dll)->name && !strcasecmp( p, ".dll" )) *p = 0;
682
683     size = exports->NumberOfFunctions * sizeof(SNOOP_FUN);
684     addr = NULL;
685     NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 0, &size,
686                             MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
687     if (!addr) {
688         RtlFreeHeap(GetProcessHeap(),0,*dll);
689         FIXME("out of memory\n");
690         return;
691     }
692     (*dll)->funs = addr;
693     memset((*dll)->funs,0,size);
694 }
695
696
697 /***********************************************************************
698  *           SNOOP_GetProcAddress
699  *
700  * Return the proc address to use for a given function.
701  */
702 FARPROC SNOOP_GetProcAddress( HMODULE hmod, const IMAGE_EXPORT_DIRECTORY *exports,
703                               DWORD exp_size, FARPROC origfun, DWORD ordinal,
704                               const WCHAR *user)
705 {
706     unsigned int i;
707     const char *ename;
708     const WORD *ordinals;
709     const DWORD *names;
710     SNOOP_DLL *dll = firstdll;
711     SNOOP_FUN *fun;
712     const IMAGE_SECTION_HEADER *sec;
713
714     if (!TRACE_ON(snoop)) return origfun;
715     if (!check_from_module( debug_from_snoop_includelist, debug_from_snoop_excludelist, user ))
716         return origfun; /* the calling module was explicitly excluded */
717
718     if (!*(LPBYTE)origfun) /* 0x00 is an imposs. opcode, poss. dataref. */
719         return origfun;
720
721     sec = RtlImageRvaToSection( RtlImageNtHeader(hmod), hmod, (char *)origfun - (char *)hmod );
722
723     if (!sec || !(sec->Characteristics & IMAGE_SCN_CNT_CODE))
724         return origfun;  /* most likely a data reference */
725
726     while (dll) {
727         if (hmod == dll->hmod)
728             break;
729         dll = dll->next;
730     }
731     if (!dll)   /* probably internal */
732         return origfun;
733
734     /* try to find a name for it */
735     ename = NULL;
736     names = (const DWORD *)((const char *)hmod + exports->AddressOfNames);
737     ordinals = (const WORD *)((const char *)hmod + exports->AddressOfNameOrdinals);
738     if (names) for (i = 0; i < exports->NumberOfNames; i++)
739     {
740         if (ordinals[i] == ordinal)
741         {
742             ename = (const char *)hmod + names[i];
743             break;
744         }
745     }
746     if (!SNOOP_ShowDebugmsgSnoop(dll->name,ordinal,ename))
747         return origfun;
748     assert(ordinal < dll->nrofordinals);
749     fun = dll->funs + ordinal;
750     if (!fun->name)
751     {
752         fun->name       = ename;
753         fun->lcall      = 0xe8;
754         /* NOTE: origreturn struct member MUST come directly after snoopentry */
755         fun->snoopentry = (char*)SNOOP_Entry-((char*)(&fun->nrofargs));
756         fun->origfun    = origfun;
757         fun->nrofargs   = -1;
758     }
759     return (FARPROC)&(fun->lcall);
760 }
761
762 static void SNOOP_PrintArg(DWORD x)
763 {
764     int i,nostring;
765
766     DPRINTF("%08lx",x);
767     if (!HIWORD(x) || TRACE_ON(seh)) return; /* trivial reject to avoid faults */
768     __TRY
769     {
770         LPBYTE s=(LPBYTE)x;
771         i=0;nostring=0;
772         while (i<80) {
773             if (s[i]==0) break;
774             if (s[i]<0x20) {nostring=1;break;}
775             if (s[i]>=0x80) {nostring=1;break;}
776             i++;
777         }
778         if (!nostring && i > 5)
779             DPRINTF(" %s",debugstr_an((LPSTR)x,i));
780         else  /* try unicode */
781         {
782             LPWSTR s=(LPWSTR)x;
783             i=0;nostring=0;
784             while (i<80) {
785                 if (s[i]==0) break;
786                 if (s[i]<0x20) {nostring=1;break;}
787                 if (s[i]>0x100) {nostring=1;break;}
788                 i++;
789             }
790             if (!nostring && i > 5) DPRINTF(" %s",debugstr_wn((LPWSTR)x,i));
791         }
792     }
793     __EXCEPT_PAGE_FAULT
794     {
795     }
796     __ENDTRY
797 }
798
799 #define CALLER1REF (*(DWORD*)context->Esp)
800
801 void WINAPI __regs_SNOOP_Entry( CONTEXT86 *context )
802 {
803         DWORD           ordinal=0,entry = context->Eip - 5;
804         SNOOP_DLL       *dll = firstdll;
805         SNOOP_FUN       *fun = NULL;
806         SNOOP_RETURNENTRIES     **rets = &firstrets;
807         SNOOP_RETURNENTRY       *ret;
808         int             i=0, max;
809
810         while (dll) {
811                 if (    ((char*)entry>=(char*)dll->funs)        &&
812                         ((char*)entry<=(char*)(dll->funs+dll->nrofordinals))
813                 ) {
814                         fun = (SNOOP_FUN*)entry;
815                         ordinal = fun-dll->funs;
816                         break;
817                 }
818                 dll=dll->next;
819         }
820         if (!dll) {
821                 FIXME("entrypoint 0x%08lx not found\n",entry);
822                 return; /* oops */
823         }
824         /* guess cdecl ... */
825         if (fun->nrofargs<0) {
826                 /* Typical cdecl return frame is:
827                  *     add esp, xxxxxxxx
828                  * which has (for xxxxxxxx up to 255 the opcode "83 C4 xx".
829                  * (after that 81 C2 xx xx xx xx)
830                  */
831                 LPBYTE  reteip = (LPBYTE)CALLER1REF;
832
833                 if (reteip) {
834                         if ((reteip[0]==0x83)&&(reteip[1]==0xc4))
835                                 fun->nrofargs=reteip[2]/4;
836                 }
837         }
838
839
840         while (*rets) {
841                 for (i=0;i<sizeof((*rets)->entry)/sizeof((*rets)->entry[0]);i++)
842                         if (!(*rets)->entry[i].origreturn)
843                                 break;
844                 if (i!=sizeof((*rets)->entry)/sizeof((*rets)->entry[0]))
845                         break;
846                 rets = &((*rets)->next);
847         }
848         if (!*rets) {
849                 SIZE_T size = 4096;
850                 VOID* addr = NULL;
851
852                 NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 0, &size, 
853                                         MEM_COMMIT | MEM_RESERVE,
854                                         PAGE_EXECUTE_READWRITE);
855                 if (!addr) return;
856                 *rets = addr;
857                 memset(*rets,0,4096);
858                 i = 0;  /* entry 0 is free */
859         }
860         ret = &((*rets)->entry[i]);
861         ret->lcall      = 0xe8;
862         /* NOTE: origreturn struct member MUST come directly after snoopret */
863         ret->snoopret   = ((char*)SNOOP_Return)-(char*)(&ret->origreturn);
864         ret->origreturn = (FARPROC)CALLER1REF;
865         CALLER1REF      = (DWORD)&ret->lcall;
866         ret->dll        = dll;
867         ret->args       = NULL;
868         ret->ordinal    = ordinal;
869         ret->origESP    = context->Esp;
870
871         context->Eip = (DWORD)fun->origfun;
872
873         if (fun->name) DPRINTF("%04lx:CALL %s.%s(",GetCurrentThreadId(),dll->name,fun->name);
874         else DPRINTF("%04lx:CALL %s.%ld(",GetCurrentThreadId(),dll->name,dll->ordbase+ordinal);
875         if (fun->nrofargs>0) {
876                 max = fun->nrofargs; if (max>16) max=16;
877                 for (i=0;i<max;i++)
878                 {
879                     SNOOP_PrintArg(*(DWORD*)(context->Esp + 4 + sizeof(DWORD)*i));
880                     if (i<fun->nrofargs-1) DPRINTF(",");
881                 }
882                 if (max!=fun->nrofargs)
883                         DPRINTF(" ...");
884         } else if (fun->nrofargs<0) {
885                 DPRINTF("<unknown, check return>");
886                 ret->args = RtlAllocateHeap(GetProcessHeap(),
887                                             0,16*sizeof(DWORD));
888                 memcpy(ret->args,(LPBYTE)(context->Esp + 4),sizeof(DWORD)*16);
889         }
890         DPRINTF(") ret=%08lx\n",(DWORD)ret->origreturn);
891 }
892
893
894 void WINAPI __regs_SNOOP_Return( CONTEXT86 *context )
895 {
896         SNOOP_RETURNENTRY       *ret = (SNOOP_RETURNENTRY*)(context->Eip - 5);
897         SNOOP_FUN *fun = &ret->dll->funs[ret->ordinal];
898
899         /* We haven't found out the nrofargs yet. If we called a cdecl
900          * function it is too late anyway and we can just set '0' (which
901          * will be the difference between orig and current ESP
902          * If stdcall -> everything ok.
903          */
904         if (ret->dll->funs[ret->ordinal].nrofargs<0)
905                 ret->dll->funs[ret->ordinal].nrofargs=(context->Esp - ret->origESP-4)/4;
906         context->Eip = (DWORD)ret->origreturn;
907         if (ret->args) {
908                 int     i,max;
909
910                 if (fun->name)
911                     DPRINTF("%04lx:RET  %s.%s(", GetCurrentThreadId(), ret->dll->name, fun->name);
912                 else
913                     DPRINTF("%04lx:RET  %s.%ld(", GetCurrentThreadId(),
914                             ret->dll->name,ret->dll->ordbase+ret->ordinal);
915
916                 max = fun->nrofargs;
917                 if (max>16) max=16;
918
919                 for (i=0;i<max;i++)
920                 {
921                     SNOOP_PrintArg(ret->args[i]);
922                     if (i<max-1) DPRINTF(",");
923                 }
924                 DPRINTF(") retval=%08lx ret=%08lx\n",
925                         context->Eax,(DWORD)ret->origreturn );
926                 RtlFreeHeap(GetProcessHeap(),0,ret->args);
927                 ret->args = NULL;
928         }
929         else
930         {
931             if (fun->name)
932                 DPRINTF("%04lx:RET  %s.%s() retval=%08lx ret=%08lx\n",
933                         GetCurrentThreadId(),
934                         ret->dll->name, fun->name, context->Eax, (DWORD)ret->origreturn);
935             else
936                 DPRINTF("%04lx:RET  %s.%ld() retval=%08lx ret=%08lx\n",
937                         GetCurrentThreadId(),
938                         ret->dll->name,ret->dll->ordbase+ret->ordinal,
939                         context->Eax, (DWORD)ret->origreturn);
940         }
941         ret->origreturn = NULL; /* mark as empty */
942 }
943
944 /* assembly wrappers that save the context */
945 DEFINE_REGS_ENTRYPOINT( SNOOP_Entry, 0, 0 );
946 DEFINE_REGS_ENTRYPOINT( SNOOP_Return, 0, 0 );
947
948 #else  /* __i386__ */
949
950 FARPROC RELAY_GetProcAddress( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports,
951                               DWORD exp_size, FARPROC proc, DWORD ordinal, const WCHAR *user )
952 {
953     return proc;
954 }
955
956 FARPROC SNOOP_GetProcAddress( HMODULE hmod, const IMAGE_EXPORT_DIRECTORY *exports, DWORD exp_size,
957                               FARPROC origfun, DWORD ordinal, const WCHAR *user )
958 {
959     return origfun;
960 }
961
962 void RELAY_SetupDLL( HMODULE module )
963 {
964 }
965
966 void SNOOP_SetupDLL( HMODULE hmod )
967 {
968     FIXME("snooping works only on i386 for now.\n");
969 }
970
971 #endif /* __i386__ */