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