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