mf: Add MFGetSupportedMimeTypes stub.
[wine.git] / dlls / ntdll / relay.c
blobf16b4fc8164116516a5a03275e0688c8824420be
1 /*
2 * Win32 relay and snoop functions
4 * Copyright 1997 Alexandre Julliard
5 * Copyright 1998 Marcus Meissner
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.
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.
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
22 #include "config.h"
23 #include "wine/port.h"
25 #include <assert.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <stdio.h>
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"
39 WINE_DEFAULT_DEBUG_CHANNEL(relay);
41 #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
43 WINE_DECLARE_DEBUG_CHANNEL(timestamp);
44 WINE_DECLARE_DEBUG_CHANNEL(pid);
46 struct relay_descr /* descriptor for a module */
48 void *magic; /* signature */
49 void *relay_call; /* functions to call from relay thunks */
50 void *relay_call_regs; /* no longer used */
51 void *private; /* reserved for the relay code private data */
52 const char *entry_point_base; /* base address of entry point thunks */
53 const unsigned int *entry_point_offsets; /* offsets of entry points thunks */
54 const unsigned int *arg_types; /* table of argument types for all entry points */
57 #define RELAY_DESCR_MAGIC ((void *)0xdeb90001)
58 #define IS_INTARG(x) (((ULONG_PTR)(x) >> 16) == 0)
60 /* private data built at dll load time */
62 struct relay_entry_point
64 void *orig_func; /* original entry point function */
65 const char *name; /* function name (if any) */
68 struct relay_private_data
70 HMODULE module; /* module handle of this dll */
71 unsigned int base; /* ordinal base */
72 char dllname[40]; /* dll name (without .dll extension) */
73 struct relay_entry_point entry_points[1]; /* list of dll entry points */
76 static const WCHAR **debug_relay_excludelist;
77 static const WCHAR **debug_relay_includelist;
78 static const WCHAR **debug_snoop_excludelist;
79 static const WCHAR **debug_snoop_includelist;
80 static const WCHAR **debug_from_relay_excludelist;
81 static const WCHAR **debug_from_relay_includelist;
82 static const WCHAR **debug_from_snoop_excludelist;
83 static const WCHAR **debug_from_snoop_includelist;
85 static RTL_RUN_ONCE init_once = RTL_RUN_ONCE_INIT;
87 /* compare an ASCII and a Unicode string without depending on the current codepage */
88 static inline int strcmpAW( const char *strA, const WCHAR *strW )
90 while (*strA && ((unsigned char)*strA == *strW)) { strA++; strW++; }
91 return (unsigned char)*strA - *strW;
94 /* compare an ASCII and a Unicode string without depending on the current codepage */
95 static inline int strncmpiAW( const char *strA, const WCHAR *strW, int n )
97 int ret = 0;
98 for ( ; n > 0; n--, strA++, strW++)
99 if ((ret = toupperW((unsigned char)*strA) - toupperW(*strW)) || !*strA) break;
100 return ret;
103 /***********************************************************************
104 * build_list
106 * Build a function list from a ';'-separated string.
108 static const WCHAR **build_list( const WCHAR *buffer )
110 int count = 1;
111 const WCHAR *p = buffer;
112 const WCHAR **ret;
114 while ((p = strchrW( p, ';' )))
116 count++;
117 p++;
119 /* allocate count+1 pointers, plus the space for a copy of the string */
120 if ((ret = RtlAllocateHeap( GetProcessHeap(), 0,
121 (count+1) * sizeof(WCHAR*) + (strlenW(buffer)+1) * sizeof(WCHAR) )))
123 WCHAR *str = (WCHAR *)(ret + count + 1);
124 WCHAR *q = str;
126 strcpyW( str, buffer );
127 count = 0;
128 for (;;)
130 ret[count++] = q;
131 if (!(q = strchrW( q, ';' ))) break;
132 *q++ = 0;
134 ret[count++] = NULL;
136 return ret;
139 /***********************************************************************
140 * load_list_value
142 * Load a function list from a registry value.
144 static const WCHAR **load_list( HKEY hkey, const WCHAR *value )
146 char initial_buffer[4096];
147 char *buffer = initial_buffer;
148 DWORD count;
149 NTSTATUS status;
150 UNICODE_STRING name;
151 const WCHAR **list = NULL;
153 RtlInitUnicodeString( &name, value );
154 status = NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(initial_buffer), &count );
155 if (status == STATUS_BUFFER_OVERFLOW)
157 buffer = RtlAllocateHeap( GetProcessHeap(), 0, count );
158 status = NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, count, &count );
160 if (status == STATUS_SUCCESS)
162 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)buffer)->Data;
163 list = build_list( str );
164 if (list) TRACE( "%s = %s\n", debugstr_w(value), debugstr_w(str) );
167 if (buffer != initial_buffer) RtlFreeHeap( GetProcessHeap(), 0, buffer );
168 return list;
171 /***********************************************************************
172 * init_debug_lists
174 * Build the relay include/exclude function lists.
176 static DWORD WINAPI init_debug_lists( RTL_RUN_ONCE *once, void *param, void **context )
178 OBJECT_ATTRIBUTES attr;
179 UNICODE_STRING name;
180 HANDLE root, hkey;
181 static const WCHAR configW[] = {'S','o','f','t','w','a','r','e','\\',
182 'W','i','n','e','\\',
183 'D','e','b','u','g',0};
184 static const WCHAR RelayIncludeW[] = {'R','e','l','a','y','I','n','c','l','u','d','e',0};
185 static const WCHAR RelayExcludeW[] = {'R','e','l','a','y','E','x','c','l','u','d','e',0};
186 static const WCHAR SnoopIncludeW[] = {'S','n','o','o','p','I','n','c','l','u','d','e',0};
187 static const WCHAR SnoopExcludeW[] = {'S','n','o','o','p','E','x','c','l','u','d','e',0};
188 static const WCHAR RelayFromIncludeW[] = {'R','e','l','a','y','F','r','o','m','I','n','c','l','u','d','e',0};
189 static const WCHAR RelayFromExcludeW[] = {'R','e','l','a','y','F','r','o','m','E','x','c','l','u','d','e',0};
190 static const WCHAR SnoopFromIncludeW[] = {'S','n','o','o','p','F','r','o','m','I','n','c','l','u','d','e',0};
191 static const WCHAR SnoopFromExcludeW[] = {'S','n','o','o','p','F','r','o','m','E','x','c','l','u','d','e',0};
193 RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
194 attr.Length = sizeof(attr);
195 attr.RootDirectory = root;
196 attr.ObjectName = &name;
197 attr.Attributes = 0;
198 attr.SecurityDescriptor = NULL;
199 attr.SecurityQualityOfService = NULL;
200 RtlInitUnicodeString( &name, configW );
202 /* @@ Wine registry key: HKCU\Software\Wine\Debug */
203 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) hkey = 0;
204 NtClose( root );
205 if (!hkey) return TRUE;
207 debug_relay_includelist = load_list( hkey, RelayIncludeW );
208 debug_relay_excludelist = load_list( hkey, RelayExcludeW );
209 debug_snoop_includelist = load_list( hkey, SnoopIncludeW );
210 debug_snoop_excludelist = load_list( hkey, SnoopExcludeW );
211 debug_from_relay_includelist = load_list( hkey, RelayFromIncludeW );
212 debug_from_relay_excludelist = load_list( hkey, RelayFromExcludeW );
213 debug_from_snoop_includelist = load_list( hkey, SnoopFromIncludeW );
214 debug_from_snoop_excludelist = load_list( hkey, SnoopFromExcludeW );
216 NtClose( hkey );
217 return TRUE;
221 /***********************************************************************
222 * check_list
224 * Check if a given module and function is in the list.
226 static BOOL check_list( const char *module, int ordinal, const char *func, const WCHAR *const *list )
228 char ord_str[10];
230 sprintf( ord_str, "%d", ordinal );
231 for(; *list; list++)
233 const WCHAR *p = strrchrW( *list, '.' );
234 if (p && p > *list) /* check module and function */
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;
242 else /* function only */
244 if (func && !strcmpAW( func, *list )) return TRUE;
247 return FALSE;
251 /***********************************************************************
252 * check_relay_include
254 * Check if a given function must be included in the relay output.
256 static BOOL check_relay_include( const char *module, int ordinal, const char *func )
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;
265 /***********************************************************************
266 * check_from_module
268 * Check if calls from a given module must be included in the relay/snoop output,
269 * given the exclusion and inclusion lists.
271 static BOOL check_from_module( const WCHAR **includelist, const WCHAR **excludelist, const WCHAR *module )
273 static const WCHAR dllW[] = {'.','d','l','l',0 };
274 const WCHAR **listitem;
275 BOOL show;
277 if (!module) return TRUE;
278 if (!includelist && !excludelist) return TRUE;
279 if (excludelist)
281 show = TRUE;
282 listitem = excludelist;
284 else
286 show = FALSE;
287 listitem = includelist;
289 for(; *listitem; listitem++)
291 int len;
293 if (!strcmpiW( *listitem, module )) return !show;
294 len = strlenW( *listitem );
295 if (!strncmpiW( *listitem, module, len ) && !strcmpiW( module + len, dllW ))
296 return !show;
298 return show;
301 /***********************************************************************
302 * RELAY_PrintArgs
304 static inline void RELAY_PrintArgs( const INT_PTR *args, int nb_args, unsigned int typemask )
306 while (nb_args--)
308 if ((typemask & 3) && !IS_INTARG(*args))
310 if (typemask & 2)
311 DPRINTF( "%08lx %s", *args, debugstr_w((LPCWSTR)*args) );
312 else
313 DPRINTF( "%08lx %s", *args, debugstr_a((LPCSTR)*args) );
315 else DPRINTF( "%08lx", *args );
316 if (nb_args) DPRINTF( "," );
317 args++;
318 typemask >>= 2;
322 static void print_timestamp(void)
324 ULONG ticks = NtGetTickCount();
325 DPRINTF( "%3u.%03u:", ticks / 1000, ticks % 1000 );
328 /***********************************************************************
329 * relay_trace_entry
331 * stack points to the return address, i.e. the first argument is stack[1].
333 DECLSPEC_HIDDEN void * WINAPI relay_trace_entry( struct relay_descr *descr,
334 unsigned int idx, const INT_PTR *stack )
336 WORD ordinal = LOWORD(idx);
337 BYTE nb_args = LOBYTE(HIWORD(idx));
338 struct relay_private_data *data = descr->private;
339 struct relay_entry_point *entry_point = data->entry_points + ordinal;
341 if (TRACE_ON(relay))
343 if (TRACE_ON(timestamp)) print_timestamp();
345 if (TRACE_ON(pid))
346 DPRINTF( "%04x:", GetCurrentProcessId() );
348 if (entry_point->name)
349 DPRINTF( "%04x:Call %s.%s(", GetCurrentThreadId(), data->dllname, entry_point->name );
350 else
351 DPRINTF( "%04x:Call %s.%u(", GetCurrentThreadId(), data->dllname, data->base + ordinal );
352 RELAY_PrintArgs( stack + 1, nb_args, descr->arg_types[ordinal] );
353 DPRINTF( ") ret=%08lx\n", stack[0] );
355 return entry_point->orig_func;
358 /***********************************************************************
359 * relay_trace_exit
361 DECLSPEC_HIDDEN void WINAPI relay_trace_exit( struct relay_descr *descr, unsigned int idx,
362 const INT_PTR *stack, LONGLONG retval )
364 WORD ordinal = LOWORD(idx);
365 BYTE flags = HIBYTE(HIWORD(idx));
366 struct relay_private_data *data = descr->private;
367 struct relay_entry_point *entry_point = data->entry_points + ordinal;
369 if (!TRACE_ON(relay)) return;
371 if (TRACE_ON(timestamp)) print_timestamp();
373 if (TRACE_ON(pid))
374 DPRINTF( "%04x:", GetCurrentProcessId() );
376 if (entry_point->name)
377 DPRINTF( "%04x:Ret %s.%s()", GetCurrentThreadId(), data->dllname, entry_point->name );
378 else
379 DPRINTF( "%04x:Ret %s.%u()", GetCurrentThreadId(), data->dllname, data->base + ordinal );
381 if (flags & 1) /* 64-bit return value */
382 DPRINTF( " retval=%08x%08x ret=%08lx\n",
383 (UINT)(retval >> 32), (UINT)retval, stack[0] );
384 else
385 DPRINTF( " retval=%08lx ret=%08lx\n", (UINT_PTR)retval, stack[0] );
388 #ifdef __i386__
390 extern LONGLONG CDECL call_entry_point( void *func, int nb_args, const INT_PTR *args, int flags );
391 __ASM_GLOBAL_FUNC( call_entry_point,
392 "pushl %ebp\n\t"
393 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
394 __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
395 "movl %esp,%ebp\n\t"
396 __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
397 "pushl %esi\n\t"
398 __ASM_CFI(".cfi_rel_offset %esi,-4\n\t")
399 "pushl %edi\n\t"
400 __ASM_CFI(".cfi_rel_offset %edi,-8\n\t")
401 "movl 12(%ebp),%edx\n\t"
402 "shll $2,%edx\n\t"
403 "jz 1f\n\t"
404 "subl %edx,%esp\n\t"
405 "andl $~15,%esp\n\t"
406 "movl 12(%ebp),%ecx\n\t"
407 "movl 16(%ebp),%esi\n\t"
408 "movl %esp,%edi\n\t"
409 "cld\n\t"
410 "rep; movsl\n"
411 "testl $2,20(%ebp)\n\t" /* (flags & 2) -> thiscall */
412 "jz 1f\n\t"
413 "popl %ecx\n\t"
414 "1:\tcall *8(%ebp)\n\t"
415 "leal -8(%ebp),%esp\n\t"
416 "popl %edi\n\t"
417 __ASM_CFI(".cfi_same_value %edi\n\t")
418 "popl %esi\n\t"
419 __ASM_CFI(".cfi_same_value %esi\n\t")
420 "popl %ebp\n\t"
421 __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
422 __ASM_CFI(".cfi_same_value %ebp\n\t")
423 "ret" )
425 extern LONGLONG WINAPI relay_call( struct relay_descr *descr, unsigned int idx, const INT_PTR *stack );
426 __ASM_GLOBAL_FUNC( relay_call,
427 "pushl %ebp\n\t"
428 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
429 __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
430 "movl %esp,%ebp\n\t"
431 __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
432 "pushl %esi\n\t"
433 __ASM_CFI(".cfi_rel_offset %esi,-4\n\t")
434 "pushl %edi\n\t"
435 __ASM_CFI(".cfi_rel_offset %edi,-8\n\t")
436 "pushl %ecx\n\t"
437 __ASM_CFI(".cfi_rel_offset %ecx,-12\n\t")
438 /* trace the parameters */
439 "pushl 16(%ebp)\n\t"
440 "pushl 12(%ebp)\n\t"
441 "pushl 8(%ebp)\n\t"
442 "call " __ASM_NAME("relay_trace_entry") "\n\t"
443 /* copy the arguments*/
444 "movzbl 14(%ebp),%ecx\n\t" /* number of args */
445 "jecxz 1f\n\t"
446 "leal 0(,%ecx,4),%edx\n\t"
447 "subl %edx,%esp\n\t"
448 "andl $~15,%esp\n\t"
449 "movl 16(%ebp),%esi\n\t"
450 "addl $4,%esi\n\t"
451 "movl %esp,%edi\n\t"
452 "cld\n\t"
453 "rep; movsl\n\t"
454 "testb $2,15(%ebp)\n\t" /* (flags & 2) -> thiscall */
455 "jz 1f\n\t"
456 "popl %ecx\n"
457 /* call the entry point */
458 "1:\tcall *%eax\n\t"
459 "movl %eax,%esi\n\t"
460 "movl %edx,%edi\n\t"
461 /* trace the return value */
462 "leal -20(%ebp),%esp\n\t"
463 "pushl %edx\n\t"
464 "pushl %eax\n\t"
465 "pushl 16(%ebp)\n\t"
466 "pushl 12(%ebp)\n\t"
467 "pushl 8(%ebp)\n\t"
468 "call " __ASM_NAME("relay_trace_exit") "\n\t"
469 /* restore return value and return */
470 "leal -12(%ebp),%esp\n\t"
471 "movl %esi,%eax\n\t"
472 "movl %edi,%edx\n\t"
473 "popl %ecx\n\t"
474 __ASM_CFI(".cfi_same_value %ecx\n\t")
475 "popl %edi\n\t"
476 __ASM_CFI(".cfi_same_value %edi\n\t")
477 "popl %esi\n\t"
478 __ASM_CFI(".cfi_same_value %esi\n\t")
479 "popl %ebp\n\t"
480 __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
481 __ASM_CFI(".cfi_same_value %ebp\n\t")
482 "ret $12" )
484 #elif defined(__arm__)
486 extern LONGLONG CDECL call_entry_point( void *func, int nb_args, const INT_PTR *args, int flags );
487 __ASM_GLOBAL_FUNC( call_entry_point,
488 ".arm\n\t"
489 "push {r4, r5, LR}\n\t"
490 "mov r4, r0\n\t"
491 "mov r5, SP\n\t"
492 "lsl r3, r1, #2\n\t"
493 "cmp r3, #0\n\t"
494 "beq 5f\n\t"
495 "sub SP, SP, r3\n\t"
496 "tst r1, #1\n\t"
497 "subeq SP, SP, #4\n\t"
498 "1:\tsub r3, r3, #4\n\t"
499 "ldr r0, [r2, r3]\n\t"
500 "str r0, [SP, r3]\n\t"
501 "cmp r3, #0\n\t"
502 "bgt 1b\n\t"
503 "cmp r1, #1\n\t"
504 "bgt 2f\n\t"
505 "pop {r0}\n\t"
506 "b 5f\n\t"
507 "2:\tcmp r1, #2\n\t"
508 "bgt 3f\n\t"
509 "pop {r0-r1}\n\t"
510 "b 5f\n\t"
511 "3:\tcmp r1, #3\n\t"
512 "bgt 4f\n\t"
513 "pop {r0-r2}\n\t"
514 "b 5f\n\t"
515 "4:\tpop {r0-r3}\n\t"
516 "5:\tblx r4\n\t"
517 "mov SP, r5\n\t"
518 "pop {r4, r5, PC}" )
520 static LONGLONG WINAPI relay_call( struct relay_descr *descr, unsigned int idx, const INT_PTR *stack )
522 BYTE nb_args = LOBYTE(HIWORD(idx));
523 BYTE flags = HIBYTE(HIWORD(idx));
524 void *func = relay_trace_entry( descr, idx, stack );
525 LONGLONG ret = call_entry_point( func, nb_args, stack + 1, flags );
526 relay_trace_exit( descr, idx, stack, ret );
527 return ret;
530 #elif defined(__aarch64__)
532 extern LONGLONG CDECL call_entry_point( void *func, int nb_args, const INT_PTR *args, int flags );
533 __ASM_GLOBAL_FUNC( call_entry_point,
534 "stp x29, x30, [SP,#-16]!\n\t"
535 "stp x19, x20, [SP,#-16]!\n\t"
536 "mov x29, SP\n\t"
537 "mov x19, x2\n\t"
538 "ldp x8, x9, [x19, #-32]\n\t"
539 "mov x9, x0\n\t"
540 "cbz w1, 2f\n\t"
541 "mov w10, w1\n\t"
542 "mov x11, x2\n\t"
543 "mov x12, #0\n\t"
544 "ldp x0, x1, [x11], #16\n\t"
545 "add w12, w12, #2\n\t"
546 "cmp w12, w10\n\t"
547 "b.hs 2f\n\t"
548 "ldp x2, x3, [x11], #16\n\t"
549 "add w12, w12, #2\n\t"
550 "cmp w12, w10\n\t"
551 "b.hs 2f\n\t"
552 "ldp x4, x5, [x11], #16\n\t"
553 "add w12, w12, #2\n\t"
554 "cmp w12, w10\n\t"
555 "b.hs 2f\n\t"
556 "ldp x6, x7, [x11], #16\n\t"
557 "add w12, w12, #2\n\t"
558 "cmp w12, w10\n\t"
559 "b.hs 2f\n\t"
560 "sub w12, w10, #8\n\t"
561 "lsl w12, w12, #3\n\t"
562 "sub SP, SP, w12, uxtw\n\t"
563 "tbz w12, #3, 1f\n\t"
564 "sub SP, SP, #8\n\t"
565 "1: sub w12, w12, #8\n\t"
566 "ldr x13, [x11, x12]\n\t"
567 "str x13, [SP, x12]\n\t"
568 "cbnz w12, 1b\n\t"
569 "2: blr x9\n\t"
570 "mov SP, x29\n\t"
571 "ldp x19, x20, [SP], #16\n\t"
572 "ldp x29, x30, [SP], #16\n\t"
573 "ret\n" )
575 static LONGLONG WINAPI relay_call( struct relay_descr *descr, unsigned int idx, const INT_PTR *stack )
577 BYTE nb_args = LOBYTE(HIWORD(idx));
578 BYTE flags = HIBYTE(HIWORD(idx));
579 void *func = relay_trace_entry( descr, idx, stack + 3 );
580 LONGLONG ret = call_entry_point( func, nb_args, stack + 4, flags );
581 relay_trace_exit( descr, idx, stack + 3, ret );
582 return ret;
585 #elif defined(__x86_64__)
587 extern void * WINAPI relay_call( struct relay_descr *descr, unsigned int idx, const INT_PTR *stack );
588 __ASM_GLOBAL_FUNC( relay_call,
589 "pushq %rbp\n\t"
590 __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t")
591 __ASM_CFI(".cfi_rel_offset %rbp,0\n\t")
592 "movq %rsp,%rbp\n\t"
593 __ASM_CFI(".cfi_def_cfa_register %rbp\n\t")
594 "subq $0x30,%rsp\n\t"
595 "movq %rsi,0x20(%rsp)\n\t"
596 __ASM_CFI(".cfi_rel_offset %rsi,-16\n\t")
597 "movq %rdi,0x28(%rsp)\n\t"
598 __ASM_CFI(".cfi_rel_offset %rdi,-8\n\t")
599 /* trace the parameters */
600 "movq %rcx,0x10(%rbp)\n\t"
601 "movq %rdx,0x18(%rbp)\n\t"
602 "movq %r8,0x20(%rbp)\n\t"
603 "call " __ASM_NAME("relay_trace_entry") "\n\t"
604 /* copy the arguments */
605 "movzbq 0x1a(%rbp),%rdx\n\t" /* number of args */
606 "movq $4,%rcx\n\t"
607 "cmp %rcx,%rdx\n\t"
608 "cmovgq %rdx,%rcx\n\t"
609 "leaq -16(,%rcx,8),%rdx\n\t"
610 "andq $~15,%rdx\n\t"
611 "subq %rdx,%rsp\n\t"
612 "movq 0x20(%rbp),%r8\n\t" /* original stack */
613 "leaq 8(%r8),%rsi\n\t"
614 "movq %rsp,%rdi\n\t"
615 "rep; movsq\n\t"
616 /* call the entry point */
617 "movq 0(%rsp),%rcx\n\t"
618 "movq 8(%rsp),%rdx\n\t"
619 "movq 16(%rsp),%r8\n\t"
620 "movq 24(%rsp),%r9\n\t"
621 "movq 0(%rsp),%xmm0\n\t"
622 "movq 8(%rsp),%xmm1\n\t"
623 "movq 16(%rsp),%xmm2\n\t"
624 "movq 24(%rsp),%xmm3\n\t"
625 "callq *%rax\n\t"
626 /* trace the return value */
627 "leaq -0x30(%rbp),%rsp\n\t"
628 "movq 0x10(%rbp),%rcx\n\t"
629 "movq 0x18(%rbp),%rdx\n\t"
630 "movq 0x20(%rbp),%r8\n\t"
631 "movq %rax,%rsi\n\t"
632 "movaps %xmm0,0x10(%rbp)\n\t"
633 "movq %rax,%r9\n\t"
634 "call " __ASM_NAME("relay_trace_exit") "\n\t"
635 /* restore return value and return */
636 "movq %rsi,%rax\n\t"
637 "movaps 0x10(%rbp),%xmm0\n\t"
638 "movq 0x20(%rsp),%rsi\n\t"
639 __ASM_CFI(".cfi_same_value %rsi\n\t")
640 "movq 0x28(%rsp),%rdi\n\t"
641 __ASM_CFI(".cfi_same_value %rdi\n\t")
642 "movq %rbp,%rsp\n\t"
643 __ASM_CFI(".cfi_def_cfa_register %rsp\n\t")
644 "popq %rbp\n\t"
645 __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t")
646 __ASM_CFI(".cfi_same_value %rbp\n\t")
647 "ret")
649 #else
650 #error Not supported on this CPU
651 #endif
654 /***********************************************************************
655 * RELAY_GetProcAddress
657 * Return the proc address to use for a given function.
659 FARPROC RELAY_GetProcAddress( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports,
660 DWORD exp_size, FARPROC proc, DWORD ordinal, const WCHAR *user )
662 struct relay_private_data *data;
663 const struct relay_descr *descr = (const struct relay_descr *)((const char *)exports + exp_size);
665 if (descr->magic != RELAY_DESCR_MAGIC || !(data = descr->private)) return proc; /* no relay data */
666 if (!data->entry_points[ordinal].orig_func) return proc; /* not a relayed function */
667 if (check_from_module( debug_from_relay_includelist, debug_from_relay_excludelist, user ))
668 return proc; /* we want to relay it */
669 return data->entry_points[ordinal].orig_func;
673 /***********************************************************************
674 * RELAY_SetupDLL
676 * Setup relay debugging for a built-in dll.
678 void RELAY_SetupDLL( HMODULE module )
680 IMAGE_EXPORT_DIRECTORY *exports;
681 DWORD *funcs;
682 unsigned int i, len;
683 DWORD size, entry_point_rva;
684 struct relay_descr *descr;
685 struct relay_private_data *data;
686 const WORD *ordptr;
688 RtlRunOnceExecuteOnce( &init_once, init_debug_lists, NULL, NULL );
690 exports = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size );
691 if (!exports) return;
693 descr = (struct relay_descr *)((char *)exports + size);
694 if (descr->magic != RELAY_DESCR_MAGIC) return;
696 if (!(data = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data) +
697 (exports->NumberOfFunctions-1) * sizeof(data->entry_points) )))
698 return;
700 descr->relay_call = relay_call;
701 descr->private = data;
703 data->module = module;
704 data->base = exports->Base;
705 len = strlen( (char *)module + exports->Name );
706 if (len > 4 && !strcasecmp( (char *)module + exports->Name + len - 4, ".dll" )) len -= 4;
707 len = min( len, sizeof(data->dllname) - 1 );
708 memcpy( data->dllname, (char *)module + exports->Name, len );
709 data->dllname[len] = 0;
711 /* fetch name pointer for all entry points and store them in the private structure */
713 ordptr = (const WORD *)((char *)module + exports->AddressOfNameOrdinals);
714 for (i = 0; i < exports->NumberOfNames; i++, ordptr++)
716 DWORD name_rva = ((DWORD*)((char *)module + exports->AddressOfNames))[i];
717 data->entry_points[*ordptr].name = (const char *)module + name_rva;
720 /* patch the functions in the export table to point to the relay thunks */
722 funcs = (DWORD *)((char *)module + exports->AddressOfFunctions);
723 entry_point_rva = descr->entry_point_base - (const char *)module;
724 for (i = 0; i < exports->NumberOfFunctions; i++, funcs++)
726 if (!descr->entry_point_offsets[i]) continue; /* not a normal function */
727 if (!check_relay_include( data->dllname, i + exports->Base, data->entry_points[i].name ))
728 continue; /* don't include this entry point */
730 data->entry_points[i].orig_func = (char *)module + *funcs;
731 *funcs = entry_point_rva + descr->entry_point_offsets[i];
735 #else /* __i386__ || __x86_64__ || __arm__ || __aarch64__ */
737 FARPROC RELAY_GetProcAddress( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports,
738 DWORD exp_size, FARPROC proc, DWORD ordinal, const WCHAR *user )
740 return proc;
743 void RELAY_SetupDLL( HMODULE module )
747 #endif /* __i386__ || __x86_64__ || __arm__ || __aarch64__ */
750 /***********************************************************************/
751 /* snoop support */
752 /***********************************************************************/
754 #ifdef __i386__
756 WINE_DECLARE_DEBUG_CHANNEL(seh);
757 WINE_DECLARE_DEBUG_CHANNEL(snoop);
759 #include "pshpack1.h"
761 typedef struct
763 /* code part */
764 BYTE lcall; /* 0xe8 call snoopentry (relative) */
765 DWORD snoopentry; /* SNOOP_Entry relative */
766 /* unreached */
767 int nrofargs;
768 FARPROC origfun;
769 const char *name;
770 } SNOOP_FUN;
772 typedef struct tagSNOOP_DLL {
773 HMODULE hmod;
774 SNOOP_FUN *funs;
775 DWORD ordbase;
776 DWORD nrofordinals;
777 struct tagSNOOP_DLL *next;
778 char name[1];
779 } SNOOP_DLL;
781 typedef struct
783 /* code part */
784 BYTE lcall; /* 0xe8 call snoopret relative*/
785 DWORD snoopret; /* SNOOP_Ret relative */
786 /* unreached */
787 FARPROC origreturn;
788 SNOOP_DLL *dll;
789 DWORD ordinal;
790 void **origESP;
791 DWORD *args; /* saved args across a stdcall */
792 } SNOOP_RETURNENTRY;
794 typedef struct tagSNOOP_RETURNENTRIES {
795 SNOOP_RETURNENTRY entry[4092/sizeof(SNOOP_RETURNENTRY)];
796 struct tagSNOOP_RETURNENTRIES *next;
797 } SNOOP_RETURNENTRIES;
799 #include "poppack.h"
801 extern void WINAPI SNOOP_Entry(void);
802 extern void WINAPI SNOOP_Return(void);
804 static SNOOP_DLL *firstdll;
805 static SNOOP_RETURNENTRIES *firstrets;
808 /***********************************************************************
809 * SNOOP_ShowDebugmsgSnoop
811 * Simple function to decide if a particular debugging message is
812 * wanted.
814 static BOOL SNOOP_ShowDebugmsgSnoop(const char *module, int ordinal, const char *func)
816 if (debug_snoop_excludelist && check_list( module, ordinal, func, debug_snoop_excludelist ))
817 return FALSE;
818 if (debug_snoop_includelist && !check_list( module, ordinal, func, debug_snoop_includelist ))
819 return FALSE;
820 return TRUE;
824 /***********************************************************************
825 * SNOOP_SetupDLL
827 * Setup snoop debugging for a native dll.
829 void SNOOP_SetupDLL(HMODULE hmod)
831 SNOOP_DLL **dll = &firstdll;
832 char *p, *name;
833 void *addr;
834 SIZE_T size;
835 ULONG size32;
836 IMAGE_EXPORT_DIRECTORY *exports;
838 RtlRunOnceExecuteOnce( &init_once, init_debug_lists, NULL, NULL );
840 exports = RtlImageDirectoryEntryToData( hmod, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size32 );
841 if (!exports || !exports->NumberOfFunctions) return;
842 name = (char *)hmod + exports->Name;
843 size = size32;
845 TRACE_(snoop)("hmod=%p, name=%s\n", hmod, name);
847 while (*dll) {
848 if ((*dll)->hmod == hmod)
850 /* another dll, loaded at the same address */
851 addr = (*dll)->funs;
852 size = (*dll)->nrofordinals * sizeof(SNOOP_FUN);
853 NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE);
854 break;
856 dll = &((*dll)->next);
858 if (*dll)
859 *dll = RtlReAllocateHeap(GetProcessHeap(),
860 HEAP_ZERO_MEMORY, *dll,
861 sizeof(SNOOP_DLL) + strlen(name));
862 else
863 *dll = RtlAllocateHeap(GetProcessHeap(),
864 HEAP_ZERO_MEMORY,
865 sizeof(SNOOP_DLL) + strlen(name));
866 (*dll)->hmod = hmod;
867 (*dll)->ordbase = exports->Base;
868 (*dll)->nrofordinals = exports->NumberOfFunctions;
869 strcpy( (*dll)->name, name );
870 p = (*dll)->name + strlen((*dll)->name) - 4;
871 if (p > (*dll)->name && !strcasecmp( p, ".dll" )) *p = 0;
873 size = exports->NumberOfFunctions * sizeof(SNOOP_FUN);
874 addr = NULL;
875 NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 0, &size,
876 MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
877 if (!addr) {
878 RtlFreeHeap(GetProcessHeap(),0,*dll);
879 FIXME("out of memory\n");
880 return;
882 (*dll)->funs = addr;
883 memset((*dll)->funs,0,size);
887 /***********************************************************************
888 * SNOOP_GetProcAddress
890 * Return the proc address to use for a given function.
892 FARPROC SNOOP_GetProcAddress( HMODULE hmod, const IMAGE_EXPORT_DIRECTORY *exports,
893 DWORD exp_size, FARPROC origfun, DWORD ordinal,
894 const WCHAR *user)
896 unsigned int i;
897 const char *ename;
898 const WORD *ordinals;
899 const DWORD *names;
900 SNOOP_DLL *dll = firstdll;
901 SNOOP_FUN *fun;
902 const IMAGE_SECTION_HEADER *sec;
904 if (!TRACE_ON(snoop)) return origfun;
905 if (!check_from_module( debug_from_snoop_includelist, debug_from_snoop_excludelist, user ))
906 return origfun; /* the calling module was explicitly excluded */
908 if (!*(LPBYTE)origfun) /* 0x00 is an impossible opcode, possible dataref. */
909 return origfun;
911 sec = RtlImageRvaToSection( RtlImageNtHeader(hmod), hmod, (char *)origfun - (char *)hmod );
913 if (!sec || !(sec->Characteristics & IMAGE_SCN_CNT_CODE))
914 return origfun; /* most likely a data reference */
916 while (dll) {
917 if (hmod == dll->hmod)
918 break;
919 dll = dll->next;
921 if (!dll) /* probably internal */
922 return origfun;
924 /* try to find a name for it */
925 ename = NULL;
926 names = (const DWORD *)((const char *)hmod + exports->AddressOfNames);
927 ordinals = (const WORD *)((const char *)hmod + exports->AddressOfNameOrdinals);
928 if (names) for (i = 0; i < exports->NumberOfNames; i++)
930 if (ordinals[i] == ordinal)
932 ename = (const char *)hmod + names[i];
933 break;
936 if (!SNOOP_ShowDebugmsgSnoop(dll->name,ordinal,ename))
937 return origfun;
938 assert(ordinal < dll->nrofordinals);
939 fun = dll->funs + ordinal;
940 if (!fun->name)
942 fun->name = ename;
943 fun->lcall = 0xe8;
944 fun->snoopentry = (char *)SNOOP_Entry - (char *)(&fun->snoopentry + 1);
945 fun->origfun = origfun;
946 fun->nrofargs = -1;
948 return (FARPROC)&(fun->lcall);
951 static void SNOOP_PrintArg(DWORD x)
953 int i,nostring;
955 DPRINTF("%08x",x);
956 if (IS_INTARG(x) || TRACE_ON(seh)) return; /* trivial reject to avoid faults */
957 __TRY
959 LPBYTE s=(LPBYTE)x;
960 i=0;nostring=0;
961 while (i<80) {
962 if (s[i]==0) break;
963 if (s[i]<0x20) {nostring=1;break;}
964 if (s[i]>=0x80) {nostring=1;break;}
965 i++;
967 if (!nostring && i > 5)
968 DPRINTF(" %s",debugstr_an((LPSTR)x,i));
969 else /* try unicode */
971 LPWSTR s=(LPWSTR)x;
972 i=0;nostring=0;
973 while (i<80) {
974 if (s[i]==0) break;
975 if (s[i]<0x20) {nostring=1;break;}
976 if (s[i]>0x100) {nostring=1;break;}
977 i++;
979 if (!nostring && i > 5) DPRINTF(" %s",debugstr_wn((LPWSTR)x,i));
982 __EXCEPT_PAGE_FAULT
985 __ENDTRY
988 void WINAPI DECLSPEC_HIDDEN __regs_SNOOP_Entry( void **stack )
990 SNOOP_DLL *dll;
991 SNOOP_FUN *fun = (SNOOP_FUN *)((char *)stack[0] - 5);
992 SNOOP_RETURNENTRIES **rets = &firstrets;
993 SNOOP_RETURNENTRY *ret;
994 int i=0, max;
996 for (dll = firstdll; dll; dll = dll->next )
997 if (fun >= dll->funs && fun < dll->funs + dll->nrofordinals) break;
999 if (!dll) {
1000 FIXME("entrypoint %p not found\n", fun);
1001 return; /* oops */
1003 /* guess cdecl ... */
1004 if (fun->nrofargs<0) {
1005 /* Typical cdecl return frame is:
1006 * add esp, xxxxxxxx
1007 * which has (for xxxxxxxx up to 255 the opcode "83 C4 xx".
1008 * (after that 81 C2 xx xx xx xx)
1010 LPBYTE reteip = stack[1];
1012 if (reteip) {
1013 if ((reteip[0]==0x83)&&(reteip[1]==0xc4))
1014 fun->nrofargs=reteip[2]/4;
1019 while (*rets) {
1020 for (i=0;i<sizeof((*rets)->entry)/sizeof((*rets)->entry[0]);i++)
1021 if (!(*rets)->entry[i].origreturn)
1022 break;
1023 if (i!=sizeof((*rets)->entry)/sizeof((*rets)->entry[0]))
1024 break;
1025 rets = &((*rets)->next);
1027 if (!*rets) {
1028 SIZE_T size = 4096;
1029 VOID* addr = NULL;
1031 NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 0, &size,
1032 MEM_COMMIT | MEM_RESERVE,
1033 PAGE_EXECUTE_READWRITE);
1034 if (!addr) return;
1035 *rets = addr;
1036 i = 0; /* entry 0 is free */
1038 ret = &((*rets)->entry[i]);
1039 ret->lcall = 0xe8;
1040 ret->snoopret = (char *)SNOOP_Return - (char *)(&ret->snoopret + 1);
1041 ret->origreturn = stack[1];
1042 stack[1] = &ret->lcall;
1043 ret->dll = dll;
1044 ret->args = NULL;
1045 ret->ordinal = fun - dll->funs;
1046 ret->origESP = stack;
1048 stack[0] = fun->origfun;
1050 if (!TRACE_ON(snoop)) return;
1052 if (TRACE_ON(timestamp))
1053 print_timestamp();
1054 if (fun->name) DPRINTF("%04x:CALL %s.%s(",GetCurrentThreadId(),dll->name,fun->name);
1055 else DPRINTF("%04x:CALL %s.%d(",GetCurrentThreadId(),dll->name,dll->ordbase+ret->ordinal);
1056 if (fun->nrofargs>0) {
1057 max = fun->nrofargs; if (max>16) max=16;
1058 for (i=0;i<max;i++)
1060 SNOOP_PrintArg( (DWORD)stack[i + 2] );
1061 if (i<fun->nrofargs-1) DPRINTF(",");
1063 if (max!=fun->nrofargs)
1064 DPRINTF(" ...");
1065 } else if (fun->nrofargs<0) {
1066 DPRINTF("<unknown, check return>");
1067 ret->args = RtlAllocateHeap(GetProcessHeap(),
1068 0,16*sizeof(DWORD));
1069 memcpy(ret->args, stack + 2, sizeof(DWORD)*16);
1071 DPRINTF(") ret=%08x\n",(DWORD)ret->origreturn);
1074 void WINAPI DECLSPEC_HIDDEN __regs_SNOOP_Return( void **stack )
1076 SNOOP_RETURNENTRY *ret = (SNOOP_RETURNENTRY*)((char *)stack[0] - 5);
1077 SNOOP_FUN *fun = &ret->dll->funs[ret->ordinal];
1078 DWORD retval = (DWORD)stack[-1]; /* get saved %eax from the stack */
1080 /* We haven't found out the nrofargs yet. If we called a cdecl
1081 * function it is too late anyway and we can just set '0' (which
1082 * will be the difference between orig and current ESP
1083 * If stdcall -> everything ok.
1085 if (ret->dll->funs[ret->ordinal].nrofargs<0)
1086 ret->dll->funs[ret->ordinal].nrofargs = stack - ret->origESP - 1;
1087 stack[0] = ret->origreturn;
1089 if (!TRACE_ON(snoop)) {
1090 ret->origreturn = NULL; /* mark as empty */
1091 return;
1094 if (TRACE_ON(timestamp))
1095 print_timestamp();
1096 if (ret->args) {
1097 int i,max;
1099 if (fun->name)
1100 DPRINTF("%04x:RET %s.%s(", GetCurrentThreadId(), ret->dll->name, fun->name);
1101 else
1102 DPRINTF("%04x:RET %s.%d(", GetCurrentThreadId(),
1103 ret->dll->name,ret->dll->ordbase+ret->ordinal);
1105 max = fun->nrofargs;
1106 if (max>16) max=16;
1108 for (i=0;i<max;i++)
1110 SNOOP_PrintArg(ret->args[i]);
1111 if (i<max-1) DPRINTF(",");
1113 DPRINTF(") retval=%08x ret=%08x\n", retval, (DWORD)ret->origreturn );
1114 RtlFreeHeap(GetProcessHeap(),0,ret->args);
1115 ret->args = NULL;
1117 else
1119 if (fun->name)
1120 DPRINTF("%04x:RET %s.%s() retval=%08x ret=%08x\n",
1121 GetCurrentThreadId(),
1122 ret->dll->name, fun->name, retval, (DWORD)ret->origreturn);
1123 else
1124 DPRINTF("%04x:RET %s.%d() retval=%08x ret=%08x\n",
1125 GetCurrentThreadId(),
1126 ret->dll->name,ret->dll->ordbase+ret->ordinal,
1127 retval, (DWORD)ret->origreturn);
1129 ret->origreturn = NULL; /* mark as empty */
1132 /* small wrappers that save registers and get the stack pointer */
1133 #define SNOOP_WRAPPER(name) \
1134 __ASM_STDCALL_FUNC( name, 0, \
1135 "pushl %eax\n\t" \
1136 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") \
1137 "pushl %ecx\n\t" \
1138 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") \
1139 "pushl %edx\n\t" \
1140 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") \
1141 "leal 12(%esp),%eax\n\t" \
1142 "pushl %eax\n\t" \
1143 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") \
1144 "call " __ASM_NAME("__regs_" #name) __ASM_STDCALL(4) "\n\t" \
1145 __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t") \
1146 "popl %edx\n\t" \
1147 __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t") \
1148 "popl %ecx\n\t" \
1149 __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t") \
1150 "popl %eax\n\t" \
1151 __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t") \
1152 "ret" )
1154 SNOOP_WRAPPER( SNOOP_Entry )
1155 SNOOP_WRAPPER( SNOOP_Return )
1157 #else /* __i386__ */
1159 FARPROC SNOOP_GetProcAddress( HMODULE hmod, const IMAGE_EXPORT_DIRECTORY *exports, DWORD exp_size,
1160 FARPROC origfun, DWORD ordinal, const WCHAR *user )
1162 return origfun;
1165 void SNOOP_SetupDLL( HMODULE hmod )
1167 FIXME("snooping works only on i386 for now.\n");
1170 #endif /* __i386__ */