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