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