relay.c 36.4 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1
/*
2
 * Win32 relay and snoop functions
Alexandre Julliard's avatar
Alexandre Julliard committed
3 4
 *
 * Copyright 1997 Alexandre Julliard
5
 * Copyright 1998 Marcus Meissner
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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
Alexandre Julliard's avatar
Alexandre Julliard committed
20 21
 */

22
#include "config.h"
23
#include "wine/port.h"
24

Alexandre Julliard's avatar
Alexandre Julliard committed
25
#include <assert.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
26
#include <string.h>
27
#include <stdarg.h>
28
#include <stdio.h>
29

30 31
#include "ntstatus.h"
#define WIN32_NO_STATUS
32
#include "windef.h"
33
#include "winternl.h"
34 35
#include "wine/exception.h"
#include "ntdll_misc.h"
36
#include "wine/unicode.h"
37
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
38

39
WINE_DEFAULT_DEBUG_CHANNEL(relay);
40

41
#if defined(__i386__) || defined(__x86_64__)
42

43 44 45
struct relay_descr  /* descriptor for a module */
{
    void               *magic;               /* signature */
46 47
    void               *relay_call;          /* functions to call from relay thunks */
    void               *relay_call_regs;
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
    void               *private;             /* reserved for the relay code private data */
    const char         *entry_point_base;    /* base address of entry point thunks */
    const unsigned int *entry_point_offsets; /* offsets of entry points thunks */
    const unsigned int *arg_types;           /* table of argument types for all entry points */
};

#define RELAY_DESCR_MAGIC  ((void *)0xdeb90001)

/* private data built at dll load time */

struct relay_entry_point
{
    void       *orig_func;    /* original entry point function */
    const char *name;         /* function name (if any) */
};

struct relay_private_data
{
    HMODULE                  module;            /* module handle of this dll */
    unsigned int             base;              /* ordinal base */
    char                     dllname[40];       /* dll name (without .dll extension) */
    struct relay_entry_point entry_points[1];   /* list of dll entry points */
};

72 73 74 75
static const WCHAR **debug_relay_excludelist;
static const WCHAR **debug_relay_includelist;
static const WCHAR **debug_snoop_excludelist;
static const WCHAR **debug_snoop_includelist;
76 77
static const WCHAR **debug_from_relay_excludelist;
static const WCHAR **debug_from_relay_includelist;
78 79
static const WCHAR **debug_from_snoop_excludelist;
static const WCHAR **debug_from_snoop_includelist;
80

81 82
static BOOL init_done;

83
/* compare an ASCII and a Unicode string without depending on the current codepage */
84
static inline int strcmpAW( const char *strA, const WCHAR *strW )
85 86 87 88 89 90
{
    while (*strA && ((unsigned char)*strA == *strW)) { strA++; strW++; }
    return (unsigned char)*strA - *strW;
}

/* compare an ASCII and a Unicode string without depending on the current codepage */
91
static inline int strncmpiAW( const char *strA, const WCHAR *strW, int n )
92 93 94 95 96 97
{
    int ret = 0;
    for ( ; n > 0; n--, strA++, strW++)
        if ((ret = toupperW((unsigned char)*strA) - toupperW(*strW)) || !*strA) break;
    return ret;
}
98

Alexandre Julliard's avatar
Alexandre Julliard committed
99
/***********************************************************************
100
 *           build_list
Alexandre Julliard's avatar
Alexandre Julliard committed
101
 *
102
 * Build a function list from a ';'-separated string.
Alexandre Julliard's avatar
Alexandre Julliard committed
103
 */
104
static const WCHAR **build_list( const WCHAR *buffer )
105 106
{
    int count = 1;
107 108
    const WCHAR *p = buffer;
    const WCHAR **ret;
109

110
    while ((p = strchrW( p, ';' )))
111
    {
112
        count++;
113
        p++;
Alexandre Julliard's avatar
Alexandre Julliard committed
114
    }
115
    /* allocate count+1 pointers, plus the space for a copy of the string */
116 117
    if ((ret = RtlAllocateHeap( GetProcessHeap(), 0,
                                (count+1) * sizeof(WCHAR*) + (strlenW(buffer)+1) * sizeof(WCHAR) )))
118
    {
119 120
        WCHAR *str = (WCHAR *)(ret + count + 1);
        WCHAR *p = str;
121

122
        strcpyW( str, buffer );
123 124 125 126
        count = 0;
        for (;;)
        {
            ret[count++] = p;
127
            if (!(p = strchrW( p, ';' ))) break;
128 129 130
            *p++ = 0;
        }
        ret[count++] = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
131
    }
132 133 134
    return ret;
}

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
/***********************************************************************
 *           load_list_value
 *
 * Load a function list from a registry value.
 */
static const WCHAR **load_list( HKEY hkey, const WCHAR *value )
{
    char initial_buffer[4096];
    char *buffer = initial_buffer;
    DWORD count;
    NTSTATUS status;
    UNICODE_STRING name;
    const WCHAR **list = NULL;

    RtlInitUnicodeString( &name, value );
    status = NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(buffer), &count );
    if (status == STATUS_BUFFER_OVERFLOW)
    {
        buffer = RtlAllocateHeap( GetProcessHeap(), 0, count );
        status = NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, count, &count );
    }
    if (status == STATUS_SUCCESS)
    {
        WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)buffer)->Data;
        list = build_list( str );
        if (list) TRACE( "%s = %s\n", debugstr_w(value), debugstr_w(str) );
    }

    if (buffer != initial_buffer) RtlFreeHeap( GetProcessHeap(), 0, buffer );
    return list;
}
166 167

/***********************************************************************
168
 *           init_debug_lists
169 170 171
 *
 * Build the relay include/exclude function lists.
 */
172
static void init_debug_lists(void)
173
{
174 175
    OBJECT_ATTRIBUTES attr;
    UNICODE_STRING name;
176
    HANDLE root, hkey;
177
    static const WCHAR configW[] = {'S','o','f','t','w','a','r','e','\\',
178 179 180 181 182 183
                                    'W','i','n','e','\\',
                                    'D','e','b','u','g',0};
    static const WCHAR RelayIncludeW[] = {'R','e','l','a','y','I','n','c','l','u','d','e',0};
    static const WCHAR RelayExcludeW[] = {'R','e','l','a','y','E','x','c','l','u','d','e',0};
    static const WCHAR SnoopIncludeW[] = {'S','n','o','o','p','I','n','c','l','u','d','e',0};
    static const WCHAR SnoopExcludeW[] = {'S','n','o','o','p','E','x','c','l','u','d','e',0};
184 185
    static const WCHAR RelayFromIncludeW[] = {'R','e','l','a','y','F','r','o','m','I','n','c','l','u','d','e',0};
    static const WCHAR RelayFromExcludeW[] = {'R','e','l','a','y','F','r','o','m','E','x','c','l','u','d','e',0};
186 187
    static const WCHAR SnoopFromIncludeW[] = {'S','n','o','o','p','F','r','o','m','I','n','c','l','u','d','e',0};
    static const WCHAR SnoopFromExcludeW[] = {'S','n','o','o','p','F','r','o','m','E','x','c','l','u','d','e',0};
188

189 190 191
    if (init_done) return;
    init_done = TRUE;

192
    RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
193
    attr.Length = sizeof(attr);
194
    attr.RootDirectory = root;
195 196 197 198 199 200
    attr.ObjectName = &name;
    attr.Attributes = 0;
    attr.SecurityDescriptor = NULL;
    attr.SecurityQualityOfService = NULL;
    RtlInitUnicodeString( &name, configW );

201 202 203 204
    /* @@ Wine registry key: HKCU\Software\Wine\Debug */
    if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) hkey = 0;
    NtClose( root );
    if (!hkey) return;
205

206 207 208 209 210 211 212 213
    debug_relay_includelist = load_list( hkey, RelayIncludeW );
    debug_relay_excludelist = load_list( hkey, RelayExcludeW );
    debug_snoop_includelist = load_list( hkey, SnoopIncludeW );
    debug_snoop_excludelist = load_list( hkey, SnoopExcludeW );
    debug_from_relay_includelist = load_list( hkey, RelayFromIncludeW );
    debug_from_relay_excludelist = load_list( hkey, RelayFromExcludeW );
    debug_from_snoop_includelist = load_list( hkey, SnoopFromIncludeW );
    debug_from_snoop_excludelist = load_list( hkey, SnoopFromExcludeW );
214

215
    NtClose( hkey );
Alexandre Julliard's avatar
Alexandre Julliard committed
216 217
}

218

219
/***********************************************************************
220
 *           check_list
221
 *
222
 * Check if a given module and function is in the list.
223
 */
224
static BOOL check_list( const char *module, int ordinal, const char *func, const WCHAR *const *list )
225
{
226
    char ord_str[10];
227

228 229
    sprintf( ord_str, "%d", ordinal );
    for(; *list; list++)
230
    {
231 232
        const WCHAR *p = strrchrW( *list, '.' );
        if (p && p > *list)  /* check module and function */
233
        {
234 235 236 237 238
            int len = p - *list;
            if (strncmpiAW( module, *list, len-1 ) || module[len]) continue;
            if (p[1] == '*' && !p[2]) return TRUE;
            if (!strcmpAW( ord_str, p + 1 )) return TRUE;
            if (func && !strcmpAW( func, p + 1 )) return TRUE;
239 240 241
        }
        else  /* function only */
        {
242
            if (func && !strcmpAW( func, *list )) return TRUE;
243 244
        }
    }
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
    return FALSE;
}


/***********************************************************************
 *           check_relay_include
 *
 * Check if a given function must be included in the relay output.
 */
static BOOL check_relay_include( const char *module, int ordinal, const char *func )
{
    if (debug_relay_excludelist && check_list( module, ordinal, func, debug_relay_excludelist ))
        return FALSE;
    if (debug_relay_includelist && !check_list( module, ordinal, func, debug_relay_includelist ))
        return FALSE;
    return TRUE;
261 262
}

263
/***********************************************************************
264
 *           check_from_module
265
 *
266 267
 * Check if calls from a given module must be included in the relay/snoop output,
 * given the exclusion and inclusion lists.
268
 */
269
static BOOL check_from_module( const WCHAR **includelist, const WCHAR **excludelist, const WCHAR *module )
270
{
271 272
    static const WCHAR dllW[] = {'.','d','l','l',0 };
    const WCHAR **listitem;
273 274
    BOOL show;

275
    if (!module) return TRUE;
276 277
    if (!includelist && !excludelist) return TRUE;
    if (excludelist)
278 279
    {
        show = TRUE;
280
        listitem = excludelist;
281 282 283 284
    }
    else
    {
        show = FALSE;
285
        listitem = includelist;
286 287 288 289 290
    }
    for(; *listitem; listitem++)
    {
        int len;

291 292 293
        if (!strcmpiW( *listitem, module )) return !show;
        len = strlenW( *listitem );
        if (!strncmpiW( *listitem, module, len ) && !strcmpiW( module + len, dllW ))
294 295 296 297 298
            return !show;
    }
    return show;
}

299 300 301
/***********************************************************************
 *           RELAY_PrintArgs
 */
302
static inline void RELAY_PrintArgs( const INT_PTR *args, int nb_args, unsigned int typemask )
303 304 305 306 307 308
{
    while (nb_args--)
    {
	if ((typemask & 3) && HIWORD(*args))
        {
	    if (typemask & 2)
309
                DPRINTF( "%08lx %s", *args, debugstr_w((LPCWSTR)*args) );
310
            else
311
                DPRINTF( "%08lx %s", *args, debugstr_a((LPCSTR)*args) );
312
	}
313
        else DPRINTF( "%08lx", *args );
314 315 316 317 318 319
        if (nb_args) DPRINTF( "," );
        args++;
        typemask >>= 2;
    }
}

320 321
extern LONGLONG CDECL call_entry_point( void *func, int nb_args, const INT_PTR *args );
#ifdef __i386__
322
__ASM_GLOBAL_FUNC( call_entry_point,
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
                   "pushl %ebp\n\t"
                   __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
                   __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
                   "movl %esp,%ebp\n\t"
                   __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
                   "pushl %esi\n\t"
                  __ASM_CFI(".cfi_rel_offset %esi,-4\n\t")
                   "pushl %edi\n\t"
                  __ASM_CFI(".cfi_rel_offset %edi,-8\n\t")
                   "movl 12(%ebp),%edx\n\t"
                   "shll $2,%edx\n\t"
                   "jz 1f\n\t"
                   "subl %edx,%esp\n\t"
                   "andl $~15,%esp\n\t"
                   "movl 12(%ebp),%ecx\n\t"
                   "movl 16(%ebp),%esi\n\t"
                   "movl %esp,%edi\n\t"
                   "cld\n\t"
                   "rep; movsl\n"
                   "1:\tcall *8(%ebp)\n\t"
                   "leal -8(%ebp),%esp\n\t"
                   "popl %edi\n\t"
                   __ASM_CFI(".cfi_same_value %edi\n\t")
                   "popl %esi\n\t"
                   __ASM_CFI(".cfi_same_value %esi\n\t")
                   "popl %ebp\n\t"
                   __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
                   __ASM_CFI(".cfi_same_value %ebp\n\t")
                   "ret" )
352 353
#else
__ASM_GLOBAL_FUNC( call_entry_point,
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
                   "pushq %rbp\n\t"
                   ".cfi_adjust_cfa_offset 8\n\t"
                   ".cfi_rel_offset %rbp,0\n\t"
                   "movq %rsp,%rbp\n\t"
                   ".cfi_def_cfa_register %rbp\n\t"
                   "pushq %rsi\n\t"
                   ".cfi_rel_offset %rsi,-8\n\t"
                   "pushq %rdi\n\t"
                   ".cfi_rel_offset %rdi,-16\n\t"
                   "movq %rcx,%rax\n\t"
                   "movq $4,%rcx\n\t"
                   "cmp %rcx,%rdx\n\t"
                   "cmovgq %rdx,%rcx\n\t"
                   "leaq 0(,%rcx,8),%rdx\n\t"
                   "subq %rdx,%rsp\n\t"
                   "andq $~15,%rsp\n\t"
                   "movq %rsp,%rdi\n\t"
                   "movq %r8,%rsi\n\t"
                   "rep; movsq\n\t"
                   "movq 0(%rsp),%rcx\n\t"
                   "movq 8(%rsp),%rdx\n\t"
                   "movq 16(%rsp),%r8\n\t"
                   "movq 24(%rsp),%r9\n\t"
                   "callq *%rax\n\t"
                   "leaq -16(%rbp),%rsp\n\t"
                   "popq %rdi\n\t"
                   ".cfi_same_value %rdi\n\t"
                   "popq %rsi\n\t"
                   ".cfi_same_value %rsi\n\t"
                   ".cfi_def_cfa_register %rsp\n\t"
                   "popq %rbp\n\t"
                   ".cfi_adjust_cfa_offset -8\n\t"
                   ".cfi_same_value %rbp\n\t"
                   "ret")
388
#endif
389 390


Alexandre Julliard's avatar
Alexandre Julliard committed
391
/***********************************************************************
392
 *           relay_call
Alexandre Julliard's avatar
Alexandre Julliard committed
393
 *
394
 * stack points to the return address, i.e. the first argument is stack[1].
Alexandre Julliard's avatar
Alexandre Julliard committed
395
 */
396
static LONGLONG WINAPI relay_call( struct relay_descr *descr, unsigned int idx, const INT_PTR *stack )
Alexandre Julliard's avatar
Alexandre Julliard committed
397
{
398
    LONGLONG ret;
399 400 401 402 403 404 405 406 407
    WORD ordinal = LOWORD(idx);
    BYTE nb_args = LOBYTE(HIWORD(idx));
    BYTE flags   = HIBYTE(HIWORD(idx));
    struct relay_private_data *data = descr->private;
    struct relay_entry_point *entry_point = data->entry_points + ordinal;

    if (!TRACE_ON(relay))
        ret = call_entry_point( entry_point->orig_func, nb_args, stack + 1 );
    else
408
    {
409
        if (entry_point->name)
410
            DPRINTF( "%04x:Call %s.%s(", GetCurrentThreadId(), data->dllname, entry_point->name );
411
        else
412
            DPRINTF( "%04x:Call %s.%u(", GetCurrentThreadId(), data->dllname, data->base + ordinal );
413
        RELAY_PrintArgs( stack + 1, nb_args, descr->arg_types[ordinal] );
414
        DPRINTF( ") ret=%08lx\n", stack[0] );
Alexandre Julliard's avatar
Alexandre Julliard committed
415

416
        ret = call_entry_point( entry_point->orig_func, nb_args, stack + 1 );
417

418
        if (entry_point->name)
419
            DPRINTF( "%04x:Ret  %s.%s()", GetCurrentThreadId(), data->dllname, entry_point->name );
420
        else
421
            DPRINTF( "%04x:Ret  %s.%u()", GetCurrentThreadId(), data->dllname, data->base + ordinal );
422

423
        if (flags & 1)  /* 64-bit return value */
424
            DPRINTF( " retval=%08x%08x ret=%08lx\n",
425
                     (UINT)(ret >> 32), (UINT)ret, stack[0] );
426
        else
427
            DPRINTF( " retval=%08lx ret=%08lx\n", (UINT_PTR)ret, stack[0] );
428
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
429 430 431 432 433
    return ret;
}


/***********************************************************************
434
 *           relay_call_regs
Alexandre Julliard's avatar
Alexandre Julliard committed
435
 */
436
#ifdef __i386__
437 438 439
void WINAPI __regs_relay_call_regs( struct relay_descr *descr, unsigned int idx,
                                    unsigned int orig_eax, unsigned int ret_addr,
                                    CONTEXT86 *context )
Alexandre Julliard's avatar
Alexandre Julliard committed
440
{
441 442 443 444 445
    WORD ordinal = LOWORD(idx);
    BYTE nb_args = LOBYTE(HIWORD(idx));
    struct relay_private_data *data = descr->private;
    struct relay_entry_point *entry_point = data->entry_points + ordinal;
    BYTE *orig_func = entry_point->orig_func;
446 447
    INT_PTR *args = (INT_PTR *)context->Esp;
    INT_PTR args_copy[32];
448 449 450 451

    /* restore the context to what it was before the relay thunk */
    context->Eax = orig_eax;
    context->Eip = ret_addr;
452
    context->Esp += nb_args * sizeof(int);
Alexandre Julliard's avatar
Alexandre Julliard committed
453

454 455
    if (TRACE_ON(relay))
    {
456
        if (entry_point->name)
457
            DPRINTF( "%04x:Call %s.%s(", GetCurrentThreadId(), data->dllname, entry_point->name );
458
        else
459
            DPRINTF( "%04x:Call %s.%u(", GetCurrentThreadId(), data->dllname, data->base + ordinal );
460 461
        RELAY_PrintArgs( args, nb_args, descr->arg_types[ordinal] );
        DPRINTF( ") ret=%08x\n", ret_addr );
462

463 464
        DPRINTF( "%04x:  eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x "
                 "ebp=%08x esp=%08x ds=%04x es=%04x fs=%04x gs=%04x flags=%08x\n",
465 466 467
                 GetCurrentThreadId(), context->Eax, context->Ebx, context->Ecx,
                 context->Edx, context->Esi, context->Edi, context->Ebp, context->Esp,
                 context->SegDs, context->SegEs, context->SegFs, context->SegGs, context->EFlags );
468

469 470 471
        assert( orig_func[0] == 0x68 /* pushl func */ );
        assert( orig_func[5] == 0x6a /* pushl args */ );
        assert( orig_func[7] == 0xe8 /* call */ );
472
    }
473

474
    /* now call the real function */
475

476
    memcpy( args_copy, args, nb_args * sizeof(args[0]) );
477
    args_copy[nb_args++] = (INT_PTR)context;  /* append context argument */
478

479
    call_entry_point( orig_func + 12 + *(int *)(orig_func + 1), nb_args, args_copy );
480 481


482
    if (TRACE_ON(relay))
483
    {
484
        if (entry_point->name)
485
            DPRINTF( "%04x:Ret  %s.%s() retval=%08x ret=%08x\n",
486 487 488
                     GetCurrentThreadId(), data->dllname, entry_point->name,
                     context->Eax, context->Eip );
        else
489
            DPRINTF( "%04x:Ret  %s.%u() retval=%08x ret=%08x\n",
490 491
                     GetCurrentThreadId(), data->dllname, data->base + ordinal,
                     context->Eax, context->Eip );
492 493
        DPRINTF( "%04x:  eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x "
                 "ebp=%08x esp=%08x ds=%04x es=%04x fs=%04x gs=%04x flags=%08x\n",
494 495 496
                 GetCurrentThreadId(), context->Eax, context->Ebx, context->Ecx,
                 context->Edx, context->Esi, context->Edi, context->Ebp, context->Esp,
                 context->SegDs, context->SegEs, context->SegFs, context->SegGs, context->EFlags );
497
    }
498
}
499
extern void WINAPI relay_call_regs(void);
500
DEFINE_REGS_ENTRYPOINT( relay_call_regs, 4 )
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570

#else  /* __i386__ */

void WINAPI __regs_relay_call_regs( struct relay_descr *descr, INT_PTR idx,
                                    INT_PTR *stack, CONTEXT *context )
{
    WORD ordinal = LOWORD(idx);
    BYTE nb_args = LOBYTE(HIWORD(idx));
    struct relay_private_data *data = descr->private;
    struct relay_entry_point *entry_point = data->entry_points + ordinal;
    BYTE *orig_func = entry_point->orig_func;
    INT_PTR *args = stack + 1;
    INT_PTR args_copy[32];

    /* restore the context to what it was before the relay thunk */
    context->Rip = stack[0];
    context->Rsp = (INT_PTR)args;

    if (TRACE_ON(relay))
    {
        if (entry_point->name)
            DPRINTF( "%04x:Call %s.%s(", GetCurrentThreadId(), data->dllname, entry_point->name );
        else
            DPRINTF( "%04x:Call %s.%u(", GetCurrentThreadId(), data->dllname, data->base + ordinal );
        RELAY_PrintArgs( args, nb_args, descr->arg_types[ordinal] );
        DPRINTF( ") ret=%08lx\n", context->Rip );

        DPRINTF( "%04x: rax=%016lx rbx=%016lx rcx=%016lx rdx=%016lx rsi=%016lx rdi=%016lx rbp=%016lx rsp=%016lx\n",
                 GetCurrentThreadId(), context->Rax, context->Rbx, context->Rcx, context->Rdx,
                 context->Rsi, context->Rdi, context->Rbp, context->Rsp );
        DPRINTF( "%04x:  r8=%016lx  r9=%016lx r10=%016lx r11=%016lx r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n",
                 GetCurrentThreadId(), context->R8, context->R9, context->R10, context->R11,
                 context->R12, context->R13, context->R14, context->R15 );

        assert( orig_func[17] == 0x48 /* leaq */ );
        assert( orig_func[18] == 0x8d );
        assert( orig_func[19] == 0x15 );
        assert( orig_func[24] == 0xe8 /* call */ );
    }

    /* now call the real function */

    memcpy( args_copy, args, nb_args * sizeof(args[0]) );
    args_copy[nb_args++] = (INT_PTR)context;  /* append context argument */

    call_entry_point( orig_func + 24 + *(int *)(orig_func + 20), nb_args, args_copy );


    if (TRACE_ON(relay))
    {
        if (entry_point->name)
            DPRINTF( "%04x:Ret  %s.%s() retval=%08lx ret=%08lx\n",
                     GetCurrentThreadId(), data->dllname, entry_point->name,
                     context->Rax, context->Rip );
        else
            DPRINTF( "%04x:Ret  %s.%u() retval=%08lx ret=%08lx\n",
                     GetCurrentThreadId(), data->dllname, data->base + ordinal,
                     context->Rax, context->Rip );
        DPRINTF( "%04x:  rax=%016lx rbx=%016lx rcx=%016lx rdx=%016lx rsi=%016lx rdi=%016lx rbp=%016lx rsp=%016lx\n",
                 GetCurrentThreadId(), context->Rax, context->Rbx, context->Rcx, context->Rdx,
                 context->Rsi, context->Rdi, context->Rbp, context->Rsp );
        DPRINTF( "%04x:  r8=%016lx  r9=%016lx r10=%016lx r11=%016lx r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n",
                 GetCurrentThreadId(), context->R8, context->R9, context->R10, context->R11,
                 context->R12, context->R13, context->R14, context->R15 );
    }
}
extern void WINAPI relay_call_regs(void);
DEFINE_REGS_ENTRYPOINT( relay_call_regs, 3 )

#endif  /* __i386__ */
571 572


573 574 575 576 577
/***********************************************************************
 *           RELAY_GetProcAddress
 *
 * Return the proc address to use for a given function.
 */
Eric Pouech's avatar
Eric Pouech committed
578
FARPROC RELAY_GetProcAddress( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports,
579
                              DWORD exp_size, FARPROC proc, DWORD ordinal, const WCHAR *user )
580
{
581 582
    struct relay_private_data *data;
    const struct relay_descr *descr = (const struct relay_descr *)((const char *)exports + exp_size);
583

584 585
    if (descr->magic != RELAY_DESCR_MAGIC || !(data = descr->private)) return proc;  /* no relay data */
    if (!data->entry_points[ordinal].orig_func) return proc;  /* not a relayed function */
586
    if (check_from_module( debug_from_relay_includelist, debug_from_relay_excludelist, user ))
587 588
        return proc;  /* we want to relay it */
    return data->entry_points[ordinal].orig_func;
589 590 591
}


592 593 594 595 596
/***********************************************************************
 *           RELAY_SetupDLL
 *
 * Setup relay debugging for a built-in dll.
 */
597
void RELAY_SetupDLL( HMODULE module )
598 599 600
{
    IMAGE_EXPORT_DIRECTORY *exports;
    DWORD *funcs;
601 602 603 604 605
    unsigned int i, len;
    DWORD size, entry_point_rva;
    struct relay_descr *descr;
    struct relay_private_data *data;
    const WORD *ordptr;
606

607 608
    if (!init_done) init_debug_lists();

609
    exports = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size );
610
    if (!exports) return;
611

612 613 614 615 616 617 618
    descr = (struct relay_descr *)((char *)exports + size);
    if (descr->magic != RELAY_DESCR_MAGIC) return;

    if (!(data = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data) +
                                  (exports->NumberOfFunctions-1) * sizeof(data->entry_points) )))
        return;

619 620
    descr->relay_call = relay_call;
    descr->relay_call_regs = relay_call_regs;
621 622 623 624 625 626 627 628 629 630 631 632 633 634
    descr->private = data;

    data->module = module;
    data->base   = exports->Base;
    len = strlen( (char *)module + exports->Name );
    if (len > 4 && !strcasecmp( (char *)module + exports->Name + len - 4, ".dll" )) len -= 4;
    len = min( len, sizeof(data->dllname) - 1 );
    memcpy( data->dllname, (char *)module + exports->Name, len );
    data->dllname[len] = 0;

    /* fetch name pointer for all entry points and store them in the private structure */

    ordptr = (const WORD *)((char *)module + exports->AddressOfNameOrdinals);
    for (i = 0; i < exports->NumberOfNames; i++, ordptr++)
635
    {
636 637 638
        DWORD name_rva = ((DWORD*)((char *)module + exports->AddressOfNames))[i];
        data->entry_points[*ordptr].name = (const char *)module + name_rva;
    }
639

640
    /* patch the functions in the export table to point to the relay thunks */
641

642
    funcs = (DWORD *)((char *)module + exports->AddressOfFunctions);
643
    entry_point_rva = descr->entry_point_base - (const char *)module;
644 645 646 647 648
    for (i = 0; i < exports->NumberOfFunctions; i++, funcs++)
    {
        if (!descr->entry_point_offsets[i]) continue;   /* not a normal function */
        if (!check_relay_include( data->dllname, i + exports->Base, data->entry_points[i].name ))
            continue;  /* don't include this entry point */
649

650 651
        data->entry_points[i].orig_func = (char *)module + *funcs;
        *funcs = entry_point_rva + descr->entry_point_offsets[i];
652 653 654
    }
}

655
#else  /* __i386__ || __x86_64__ */
656 657 658 659 660 661 662 663 664 665 666

FARPROC RELAY_GetProcAddress( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports,
                              DWORD exp_size, FARPROC proc, DWORD ordinal, const WCHAR *user )
{
    return proc;
}

void RELAY_SetupDLL( HMODULE module )
{
}

667
#endif  /* __i386__ || __x86_64__ */
668

669 670 671 672 673

/***********************************************************************/
/* snoop support */
/***********************************************************************/

674 675 676 677 678
#ifdef __i386__

WINE_DECLARE_DEBUG_CHANNEL(seh);
WINE_DECLARE_DEBUG_CHANNEL(snoop);

679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
#include "pshpack1.h"

typedef	struct
{
	/* code part */
	BYTE		lcall;		/* 0xe8 call snoopentry (relative) */
	/* NOTE: If you move snoopentry OR nrofargs fix the relative offset
	 * calculation!
	 */
	DWORD		snoopentry;	/* SNOOP_Entry relative */
	/* unreached */
	int		nrofargs;
	FARPROC	origfun;
	const char *name;
} SNOOP_FUN;

typedef struct tagSNOOP_DLL {
	HMODULE	hmod;
	SNOOP_FUN	*funs;
	DWORD		ordbase;
	DWORD		nrofordinals;
	struct tagSNOOP_DLL	*next;
	char name[1];
} SNOOP_DLL;

typedef struct
{
	/* code part */
	BYTE		lcall;		/* 0xe8 call snoopret relative*/
	/* NOTE: If you move snoopret OR origreturn fix the relative offset
	 * calculation!
	 */
	DWORD		snoopret;	/* SNOOP_Ret relative */
	/* unreached */
	FARPROC	origreturn;
	SNOOP_DLL	*dll;
	DWORD		ordinal;
	DWORD		origESP;
	DWORD		*args;		/* saved args across a stdcall */
} SNOOP_RETURNENTRY;

typedef struct tagSNOOP_RETURNENTRIES {
	SNOOP_RETURNENTRY entry[4092/sizeof(SNOOP_RETURNENTRY)];
	struct tagSNOOP_RETURNENTRIES	*next;
} SNOOP_RETURNENTRIES;

#include "poppack.h"

extern void WINAPI SNOOP_Entry(void);
extern void WINAPI SNOOP_Return(void);

static SNOOP_DLL *firstdll;
static SNOOP_RETURNENTRIES *firstrets;


734 735 736 737 738 739
/***********************************************************************
 *          SNOOP_ShowDebugmsgSnoop
 *
 * Simple function to decide if a particular debugging message is
 * wanted.
 */
740
static BOOL SNOOP_ShowDebugmsgSnoop(const char *module, int ordinal, const char *func)
741
{
742 743 744 745 746
    if (debug_snoop_excludelist && check_list( module, ordinal, func, debug_snoop_excludelist ))
        return FALSE;
    if (debug_snoop_includelist && !check_list( module, ordinal, func, debug_snoop_includelist ))
        return FALSE;
    return TRUE;
747 748 749 750 751 752 753 754 755 756 757 758 759 760
}


/***********************************************************************
 *           SNOOP_SetupDLL
 *
 * Setup snoop debugging for a native dll.
 */
void SNOOP_SetupDLL(HMODULE hmod)
{
    SNOOP_DLL **dll = &firstdll;
    char *p, *name;
    void *addr;
    SIZE_T size;
761
    ULONG size32;
762 763
    IMAGE_EXPORT_DIRECTORY *exports;

764 765
    if (!init_done) init_debug_lists();

766
    exports = RtlImageDirectoryEntryToData( hmod, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size32 );
767
    if (!exports || !exports->NumberOfFunctions) return;
768
    name = (char *)hmod + exports->Name;
769
    size = size32;
770

771
    TRACE_(snoop)("hmod=%p, name=%s\n", hmod, name);
772 773 774 775 776 777 778

    while (*dll) {
        if ((*dll)->hmod == hmod)
        {
            /* another dll, loaded at the same address */
            addr = (*dll)->funs;
            size = (*dll)->nrofordinals * sizeof(SNOOP_FUN);
779
            NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE);
780 781 782 783
            break;
        }
        dll = &((*dll)->next);
    }
784 785
    if (*dll)
        *dll = RtlReAllocateHeap(GetProcessHeap(),
786 787
                             HEAP_ZERO_MEMORY, *dll,
                             sizeof(SNOOP_DLL) + strlen(name));
788 789 790 791
    else
        *dll = RtlAllocateHeap(GetProcessHeap(),
                             HEAP_ZERO_MEMORY,
                             sizeof(SNOOP_DLL) + strlen(name));
792 793 794 795 796 797 798 799
    (*dll)->hmod	= hmod;
    (*dll)->ordbase = exports->Base;
    (*dll)->nrofordinals = exports->NumberOfFunctions;
    strcpy( (*dll)->name, name );
    p = (*dll)->name + strlen((*dll)->name) - 4;
    if (p > (*dll)->name && !strcasecmp( p, ".dll" )) *p = 0;

    size = exports->NumberOfFunctions * sizeof(SNOOP_FUN);
800
    addr = NULL;
801
    NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 0, &size,
802 803
                            MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!addr) {
804
        RtlFreeHeap(GetProcessHeap(),0,*dll);
805 806 807 808 809 810 811 812 813 814 815 816 817
        FIXME("out of memory\n");
        return;
    }
    (*dll)->funs = addr;
    memset((*dll)->funs,0,size);
}


/***********************************************************************
 *           SNOOP_GetProcAddress
 *
 * Return the proc address to use for a given function.
 */
Eric Pouech's avatar
Eric Pouech committed
818
FARPROC SNOOP_GetProcAddress( HMODULE hmod, const IMAGE_EXPORT_DIRECTORY *exports,
819 820
                              DWORD exp_size, FARPROC origfun, DWORD ordinal,
                              const WCHAR *user)
821
{
822
    unsigned int i;
Eric Pouech's avatar
Eric Pouech committed
823 824 825 826 827 828 829 830 831 832 833
    const char *ename;
    const WORD *ordinals;
    const DWORD *names;
    SNOOP_DLL *dll = firstdll;
    SNOOP_FUN *fun;
    const IMAGE_SECTION_HEADER *sec;

    if (!TRACE_ON(snoop)) return origfun;
    if (!check_from_module( debug_from_snoop_includelist, debug_from_snoop_excludelist, user ))
        return origfun; /* the calling module was explicitly excluded */

Austin English's avatar
Austin English committed
834
    if (!*(LPBYTE)origfun) /* 0x00 is an impossible opcode, possible dataref. */
Eric Pouech's avatar
Eric Pouech committed
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
        return origfun;

    sec = RtlImageRvaToSection( RtlImageNtHeader(hmod), hmod, (char *)origfun - (char *)hmod );

    if (!sec || !(sec->Characteristics & IMAGE_SCN_CNT_CODE))
        return origfun;  /* most likely a data reference */

    while (dll) {
        if (hmod == dll->hmod)
            break;
        dll = dll->next;
    }
    if (!dll)	/* probably internal */
        return origfun;

    /* try to find a name for it */
    ename = NULL;
    names = (const DWORD *)((const char *)hmod + exports->AddressOfNames);
    ordinals = (const WORD *)((const char *)hmod + exports->AddressOfNameOrdinals);
    if (names) for (i = 0; i < exports->NumberOfNames; i++)
    {
        if (ordinals[i] == ordinal)
857
        {
Eric Pouech's avatar
Eric Pouech committed
858 859
            ename = (const char *)hmod + names[i];
            break;
860
        }
Eric Pouech's avatar
Eric Pouech committed
861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
    }
    if (!SNOOP_ShowDebugmsgSnoop(dll->name,ordinal,ename))
        return origfun;
    assert(ordinal < dll->nrofordinals);
    fun = dll->funs + ordinal;
    if (!fun->name)
    {
        fun->name       = ename;
        fun->lcall	= 0xe8;
        /* NOTE: origreturn struct member MUST come directly after snoopentry */
        fun->snoopentry	= (char*)SNOOP_Entry-((char*)(&fun->nrofargs));
        fun->origfun	= origfun;
        fun->nrofargs	= -1;
    }
    return (FARPROC)&(fun->lcall);
876 877 878 879 880 881
}

static void SNOOP_PrintArg(DWORD x)
{
    int i,nostring;

882
    DPRINTF("%08x",x);
883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
    if (!HIWORD(x) || TRACE_ON(seh)) return; /* trivial reject to avoid faults */
    __TRY
    {
        LPBYTE s=(LPBYTE)x;
        i=0;nostring=0;
        while (i<80) {
            if (s[i]==0) break;
            if (s[i]<0x20) {nostring=1;break;}
            if (s[i]>=0x80) {nostring=1;break;}
            i++;
        }
        if (!nostring && i > 5)
            DPRINTF(" %s",debugstr_an((LPSTR)x,i));
        else  /* try unicode */
        {
            LPWSTR s=(LPWSTR)x;
            i=0;nostring=0;
            while (i<80) {
                if (s[i]==0) break;
                if (s[i]<0x20) {nostring=1;break;}
                if (s[i]>0x100) {nostring=1;break;}
                i++;
            }
            if (!nostring && i > 5) DPRINTF(" %s",debugstr_wn((LPWSTR)x,i));
        }
    }
909
    __EXCEPT_PAGE_FAULT
910
    {
911
    }
912
    __ENDTRY
913 914
}

915 916
#define CALLER1REF (*(DWORD*)context->Esp)

917
void WINAPI __regs_SNOOP_Entry( CONTEXT86 *context )
918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936
{
	DWORD		ordinal=0,entry = context->Eip - 5;
	SNOOP_DLL	*dll = firstdll;
	SNOOP_FUN	*fun = NULL;
	SNOOP_RETURNENTRIES	**rets = &firstrets;
	SNOOP_RETURNENTRY	*ret;
	int		i=0, max;

	while (dll) {
		if (	((char*)entry>=(char*)dll->funs)	&&
			((char*)entry<=(char*)(dll->funs+dll->nrofordinals))
		) {
			fun = (SNOOP_FUN*)entry;
			ordinal = fun-dll->funs;
			break;
		}
		dll=dll->next;
	}
	if (!dll) {
937
		FIXME("entrypoint 0x%08x not found\n",entry);
938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965
		return; /* oops */
	}
	/* guess cdecl ... */
	if (fun->nrofargs<0) {
		/* Typical cdecl return frame is:
		 *     add esp, xxxxxxxx
		 * which has (for xxxxxxxx up to 255 the opcode "83 C4 xx".
		 * (after that 81 C2 xx xx xx xx)
		 */
		LPBYTE	reteip = (LPBYTE)CALLER1REF;

		if (reteip) {
			if ((reteip[0]==0x83)&&(reteip[1]==0xc4))
				fun->nrofargs=reteip[2]/4;
		}
	}


	while (*rets) {
		for (i=0;i<sizeof((*rets)->entry)/sizeof((*rets)->entry[0]);i++)
			if (!(*rets)->entry[i].origreturn)
				break;
		if (i!=sizeof((*rets)->entry)/sizeof((*rets)->entry[0]))
			break;
		rets = &((*rets)->next);
	}
	if (!*rets) {
                SIZE_T size = 4096;
966
                VOID* addr = NULL;
967

968
                NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 0, &size, 
969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988
                                        MEM_COMMIT | MEM_RESERVE,
                                        PAGE_EXECUTE_READWRITE);
                if (!addr) return;
                *rets = addr;
		memset(*rets,0,4096);
		i = 0;	/* entry 0 is free */
	}
	ret = &((*rets)->entry[i]);
	ret->lcall	= 0xe8;
	/* NOTE: origreturn struct member MUST come directly after snoopret */
	ret->snoopret	= ((char*)SNOOP_Return)-(char*)(&ret->origreturn);
	ret->origreturn	= (FARPROC)CALLER1REF;
	CALLER1REF	= (DWORD)&ret->lcall;
	ret->dll	= dll;
	ret->args	= NULL;
	ret->ordinal	= ordinal;
	ret->origESP	= context->Esp;

	context->Eip = (DWORD)fun->origfun;

989 990
	if (fun->name) DPRINTF("%04x:CALL %s.%s(",GetCurrentThreadId(),dll->name,fun->name);
	else DPRINTF("%04x:CALL %s.%d(",GetCurrentThreadId(),dll->name,dll->ordbase+ordinal);
991 992 993 994 995 996 997 998 999 1000 1001
	if (fun->nrofargs>0) {
		max = fun->nrofargs; if (max>16) max=16;
		for (i=0;i<max;i++)
                {
                    SNOOP_PrintArg(*(DWORD*)(context->Esp + 4 + sizeof(DWORD)*i));
                    if (i<fun->nrofargs-1) DPRINTF(",");
                }
		if (max!=fun->nrofargs)
			DPRINTF(" ...");
	} else if (fun->nrofargs<0) {
		DPRINTF("<unknown, check return>");
1002
		ret->args = RtlAllocateHeap(GetProcessHeap(),
1003 1004 1005
                                            0,16*sizeof(DWORD));
		memcpy(ret->args,(LPBYTE)(context->Esp + 4),sizeof(DWORD)*16);
	}
1006
	DPRINTF(") ret=%08x\n",(DWORD)ret->origreturn);
1007 1008 1009
}


1010
void WINAPI __regs_SNOOP_Return( CONTEXT86 *context )
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026
{
	SNOOP_RETURNENTRY	*ret = (SNOOP_RETURNENTRY*)(context->Eip - 5);
        SNOOP_FUN *fun = &ret->dll->funs[ret->ordinal];

	/* We haven't found out the nrofargs yet. If we called a cdecl
	 * function it is too late anyway and we can just set '0' (which
	 * will be the difference between orig and current ESP
	 * If stdcall -> everything ok.
	 */
	if (ret->dll->funs[ret->ordinal].nrofargs<0)
		ret->dll->funs[ret->ordinal].nrofargs=(context->Esp - ret->origESP-4)/4;
	context->Eip = (DWORD)ret->origreturn;
	if (ret->args) {
		int	i,max;

                if (fun->name)
1027
                    DPRINTF("%04x:RET  %s.%s(", GetCurrentThreadId(), ret->dll->name, fun->name);
1028
                else
1029
                    DPRINTF("%04x:RET  %s.%d(", GetCurrentThreadId(),
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
                            ret->dll->name,ret->dll->ordbase+ret->ordinal);

		max = fun->nrofargs;
		if (max>16) max=16;

		for (i=0;i<max;i++)
                {
                    SNOOP_PrintArg(ret->args[i]);
                    if (i<max-1) DPRINTF(",");
                }
1040
		DPRINTF(") retval=%08x ret=%08x\n",
1041
			context->Eax,(DWORD)ret->origreturn );
1042
		RtlFreeHeap(GetProcessHeap(),0,ret->args);
1043 1044 1045 1046 1047
		ret->args = NULL;
	}
        else
        {
            if (fun->name)
1048
		DPRINTF("%04x:RET  %s.%s() retval=%08x ret=%08x\n",
1049 1050 1051
			GetCurrentThreadId(),
			ret->dll->name, fun->name, context->Eax, (DWORD)ret->origreturn);
            else
1052
		DPRINTF("%04x:RET  %s.%d() retval=%08x ret=%08x\n",
1053 1054 1055 1056 1057 1058 1059 1060
			GetCurrentThreadId(),
			ret->dll->name,ret->dll->ordbase+ret->ordinal,
			context->Eax, (DWORD)ret->origreturn);
        }
	ret->origreturn = NULL; /* mark as empty */
}

/* assembly wrappers that save the context */
1061 1062
DEFINE_REGS_ENTRYPOINT( SNOOP_Entry, 0 )
DEFINE_REGS_ENTRYPOINT( SNOOP_Return, 0 )
1063

1064 1065
#else  /* __i386__ */

1066
FARPROC SNOOP_GetProcAddress( HMODULE hmod, const IMAGE_EXPORT_DIRECTORY *exports, DWORD exp_size,
1067
                              FARPROC origfun, DWORD ordinal, const WCHAR *user )
1068 1069 1070 1071 1072 1073 1074 1075 1076
{
    return origfun;
}

void SNOOP_SetupDLL( HMODULE hmod )
{
    FIXME("snooping works only on i386 for now.\n");
}

1077
#endif /* __i386__ */