relay.c 46.3 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 <assert.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
23
#include <string.h>
24
#include <stdarg.h>
25
#include <stdio.h>
26

27 28
#include "ntstatus.h"
#define WIN32_NO_STATUS
29
#include "windef.h"
30
#include "winternl.h"
31 32
#include "wine/exception.h"
#include "ntdll_misc.h"
33
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
34

35
WINE_DEFAULT_DEBUG_CHANNEL(relay);
36

37
#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
38

39 40
struct relay_descr  /* descriptor for a module */
{
41
    ULONG_PTR           magic;               /* signature */
42
    void               *relay_call;          /* functions to call from relay thunks */
43 44 45
    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 */
46
    const char         *args_string;         /* string describing the arguments */
47 48
};

49 50 51 52 53 54 55
struct relay_descr_rva  /* RVA to the descriptor for PE dlls */
{
    DWORD magic;
    DWORD descr;
};

#define RELAY_DESCR_MAGIC  0xdeb90002
56
#define IS_INTARG(x)       (((ULONG_PTR)(x) >> 16) == 0)
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73

/* 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 */
};

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

83
static RTL_RUN_ONCE init_once = RTL_RUN_ONCE_INIT;
84

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

Alexandre Julliard's avatar
Alexandre Julliard committed
92
/***********************************************************************
93
 *           build_list
Alexandre Julliard's avatar
Alexandre Julliard committed
94
 *
95
 * Build a function list from a ';'-separated string.
Alexandre Julliard's avatar
Alexandre Julliard committed
96
 */
97
static const WCHAR **build_list( const WCHAR *buffer )
98 99
{
    int count = 1;
100 101
    const WCHAR *p = buffer;
    const WCHAR **ret;
102

103
    while ((p = wcschr( p, ';' )))
104
    {
105
        count++;
106
        p++;
Alexandre Julliard's avatar
Alexandre Julliard committed
107
    }
108
    /* allocate count+1 pointers, plus the space for a copy of the string */
109
    if ((ret = RtlAllocateHeap( GetProcessHeap(), 0,
110
                                (count+1) * sizeof(WCHAR*) + (wcslen(buffer)+1) * sizeof(WCHAR) )))
111
    {
112
        WCHAR *str = (WCHAR *)(ret + count + 1);
113
        WCHAR *q = str;
114

115
        wcscpy( str, buffer );
116 117 118
        count = 0;
        for (;;)
        {
119
            ret[count++] = q;
120
            if (!(q = wcschr( q, ';' ))) break;
121
            *q++ = 0;
122 123
        }
        ret[count++] = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
124
    }
125 126 127
    return ret;
}

128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
/***********************************************************************
 *           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 );
143
    status = NtQueryValueKey( hkey, &name, KeyValuePartialInformation, buffer, sizeof(initial_buffer), &count );
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
    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;
}
159 160

/***********************************************************************
161
 *           init_debug_lists
162 163 164
 *
 * Build the relay include/exclude function lists.
 */
165
static DWORD WINAPI init_debug_lists( RTL_RUN_ONCE *once, void *param, void **context )
166
{
167 168
    OBJECT_ATTRIBUTES attr;
    UNICODE_STRING name;
169
    HANDLE root, hkey;
170

171
    RtlOpenCurrentUser( KEY_ALL_ACCESS, &root );
172
    attr.Length = sizeof(attr);
173
    attr.RootDirectory = root;
174 175 176 177
    attr.ObjectName = &name;
    attr.Attributes = 0;
    attr.SecurityDescriptor = NULL;
    attr.SecurityQualityOfService = NULL;
178
    RtlInitUnicodeString( &name, L"Software\\Wine\\Debug" );
179

180 181 182
    /* @@ Wine registry key: HKCU\Software\Wine\Debug */
    if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr )) hkey = 0;
    NtClose( root );
183
    if (!hkey) return TRUE;
184

185 186 187 188 189 190 191 192
    debug_relay_includelist = load_list( hkey, L"RelayInclude" );
    debug_relay_excludelist = load_list( hkey, L"RelayExclude" );
    debug_snoop_includelist = load_list( hkey, L"SnoopInclude" );
    debug_snoop_excludelist = load_list( hkey, L"SnoopExclude" );
    debug_from_relay_includelist = load_list( hkey, L"RelayFromInclude" );
    debug_from_relay_excludelist = load_list( hkey, L"RelayFromExclude" );
    debug_from_snoop_includelist = load_list( hkey, L"SnoopFromInclude" );
    debug_from_snoop_excludelist = load_list( hkey, L"SnoopFromExclude" );
193

194
    NtClose( hkey );
195
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
196 197
}

198

199
/***********************************************************************
200
 *           check_list
201
 *
202
 * Check if a given module and function is in the list.
203
 */
204
static BOOL check_list( const WCHAR *module, int ordinal, const char *func, const WCHAR *const *list )
205
{
206
    char ord_str[10];
207

208 209
    sprintf( ord_str, "%d", ordinal );
    for(; *list; list++)
210
    {
211
        const WCHAR *p = wcsrchr( *list, '.' );
212
        if (p && p > *list)  /* check module and function */
213
        {
214
            int len = p - *list;
215
            if (wcsnicmp( module, *list, len - 1 ) || module[len]) continue;
216 217 218
            if (p[1] == '*' && !p[2]) return TRUE;
            if (!strcmpAW( ord_str, p + 1 )) return TRUE;
            if (func && !strcmpAW( func, p + 1 )) return TRUE;
219 220 221
        }
        else  /* function only */
        {
222
            if (func && !strcmpAW( func, *list )) return TRUE;
223 224
        }
    }
225 226 227 228 229 230 231 232 233
    return FALSE;
}


/***********************************************************************
 *           check_relay_include
 *
 * Check if a given function must be included in the relay output.
 */
234
static BOOL check_relay_include( const WCHAR *module, int ordinal, const char *func )
235 236 237 238 239 240
{
    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;
241 242
}

243
/***********************************************************************
244
 *           check_from_module
245
 *
246 247
 * Check if calls from a given module must be included in the relay/snoop output,
 * given the exclusion and inclusion lists.
248
 */
249
static BOOL check_from_module( const WCHAR **includelist, const WCHAR **excludelist, const WCHAR *module )
250
{
251
    const WCHAR **listitem;
252 253
    BOOL show;

254
    if (!module) return TRUE;
255 256
    if (!includelist && !excludelist) return TRUE;
    if (excludelist)
257 258
    {
        show = TRUE;
259
        listitem = excludelist;
260 261 262 263
    }
    else
    {
        show = FALSE;
264
        listitem = includelist;
265 266 267 268 269
    }
    for(; *listitem; listitem++)
    {
        int len;

270
        if (!wcsicmp( *listitem, module )) return !show;
271
        len = wcslen( *listitem );
272
        if (!wcsnicmp( *listitem, module, len ) && !wcsicmp( module + len, L".dll" ))
273 274 275 276 277
            return !show;
    }
    return show;
}

278 279

static BOOL is_ret_val( char type )
280
{
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
    return type >= 'A' && type <= 'Z';
}

static const char *func_name( struct relay_private_data *data, unsigned int ordinal )
{
    struct relay_entry_point *entry_point = data->entry_points + ordinal;

    if (entry_point->name)
        return wine_dbg_sprintf( "%s.%s", data->dllname, entry_point->name );
    else
        return wine_dbg_sprintf( "%s.%u", data->dllname, data->base + ordinal );
}

static void trace_string_a( INT_PTR ptr )
{
296 297
    if (!IS_INTARG( ptr )) TRACE( "%08Ix %s", ptr, debugstr_a( (char *)ptr ));
    else TRACE( "%08Ix", ptr );
298 299 300 301
}

static void trace_string_w( INT_PTR ptr )
{
302 303
    if (!IS_INTARG( ptr )) TRACE( "%08Ix %s", ptr, debugstr_w( (WCHAR *)ptr ));
    else TRACE( "%08Ix", ptr );
304 305
}

306 307
#ifdef __i386__

308 309 310
/***********************************************************************
 *           relay_trace_entry
 */
311 312
DECLSPEC_HIDDEN void * WINAPI relay_trace_entry( struct relay_descr *descr, unsigned int idx,
                                                 const DWORD *stack, unsigned int *nb_args )
313 314
{
    WORD ordinal = LOWORD(idx);
315
    const char *arg_types = descr->args_string + HIWORD(idx);
316 317
    struct relay_private_data *data = descr->private;
    struct relay_entry_point *entry_point = data->entry_points + ordinal;
318
    unsigned int i, pos;
319

320 321 322
    TRACE( "\1Call %s(", func_name( data, ordinal ));

    for (i = pos = 0; !is_ret_val( arg_types[i] ); i++)
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 352
        switch (arg_types[i])
        {
        case 'j': /* int64 */
            TRACE( "%x%08x", stack[pos+1], stack[pos] );
            pos += 2;
            break;
        case 'k': /* int128 */
            TRACE( "{%08x,%08x,%08x,%08x}", stack[pos], stack[pos+1], stack[pos+2], stack[pos+3] );
            pos += 4;
            break;
        case 's': /* str */
            trace_string_a( stack[pos++] );
            break;
        case 'w': /* wstr */
            trace_string_w( stack[pos++] );
            break;
        case 'f': /* float */
            TRACE( "%g", *(const float *)&stack[pos++] );
            break;
        case 'd': /* double */
            TRACE( "%g", *(const double *)&stack[pos] );
            pos += 2;
            break;
        case 'i': /* long */
        default:
            TRACE( "%08x", stack[pos++] );
            break;
        }
        if (!is_ret_val( arg_types[i+1] )) TRACE( "," );
353
    }
354
    *nb_args = pos;
355 356 357 358 359
    if (arg_types[0] == 't')
    {
        *nb_args |= 0x80000000;  /* thiscall/fastcall */
        if (arg_types[1] == 't') *nb_args |= 0x40000000;  /* fastcall */
    }
360
    TRACE( ") ret=%08x\n", stack[-1] );
361 362 363 364 365 366
    return entry_point->orig_func;
}

/***********************************************************************
 *           relay_trace_exit
 */
367
DECLSPEC_HIDDEN void WINAPI relay_trace_exit( struct relay_descr *descr, unsigned int idx,
368
                                              void *retaddr, LONGLONG retval )
369
{
370
    const char *arg_types = descr->args_string + HIWORD(idx);
371

372
    TRACE( "\1Ret  %s()", func_name( descr->private, LOWORD(idx) ));
373

374 375 376 377
    while (!is_ret_val( *arg_types )) arg_types++;
    if (*arg_types == 'J')  /* int64 return value */
        TRACE( " retval=%08x%08x ret=%08x\n",
               (UINT)(retval >> 32), (UINT)retval, (UINT)retaddr );
378
    else
379
        TRACE( " retval=%08x ret=%08x\n", (UINT)retval, (UINT)retaddr );
380 381
}

382
extern LONGLONG WINAPI relay_call( struct relay_descr *descr, unsigned int idx );
383
__ASM_STDCALL_FUNC( relay_call, 8,
384 385 386 387 388 389 390 391 392 393 394 395
                   "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")
                   "pushl %ecx\n\t"
                  __ASM_CFI(".cfi_rel_offset %ecx,-12\n\t")
                   /* trace the parameters */
396 397 398 399
                   "pushl %eax\n\t"
                   "pushl %esp\n\t"  /* number of args return ptr */
                   "leal 20(%ebp),%esi\n\t"  /* stack */
                   "pushl %esi\n\t"
400 401
                   "pushl 12(%ebp)\n\t"
                   "pushl 8(%ebp)\n\t"
402
                   "call " __ASM_STDCALL("relay_trace_entry",16) "\n\t"
403
                   /* copy the arguments*/
404
                   "movzwl -16(%ebp),%ecx\n\t"  /* number of args */
405 406 407 408 409 410 411
                   "jecxz 1f\n\t"
                   "leal 0(,%ecx,4),%edx\n\t"
                   "subl %edx,%esp\n\t"
                   "andl $~15,%esp\n\t"
                   "movl %esp,%edi\n\t"
                   "cld\n\t"
                   "rep; movsl\n\t"
412
                   "testl $0x80000000,-16(%ebp)\n\t"  /* thiscall */
413
                   "jz 1f\n\t"
414 415 416 417
                   "popl %ecx\n\t"
                   "testl $0x40000000,-16(%ebp)\n\t"  /* fastcall */
                   "jz 1f\n\t"
                   "popl %edx\n"
418 419 420 421 422 423 424 425 426 427 428
                   /* call the entry point */
                   "1:\tcall *%eax\n\t"
                   "movl %eax,%esi\n\t"
                   "movl %edx,%edi\n\t"
                   /* trace the return value */
                   "leal -20(%ebp),%esp\n\t"
                   "pushl %edx\n\t"
                   "pushl %eax\n\t"
                   "pushl 16(%ebp)\n\t"
                   "pushl 12(%ebp)\n\t"
                   "pushl 8(%ebp)\n\t"
429
                   "call " __ASM_STDCALL("relay_trace_exit",20) "\n\t"
430 431 432 433 434 435 436 437 438 439 440 441 442
                   /* restore return value and return */
                   "leal -12(%ebp),%esp\n\t"
                   "movl %esi,%eax\n\t"
                   "movl %edi,%edx\n\t"
                   "popl %ecx\n\t"
                   __ASM_CFI(".cfi_same_value %ecx\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")
443
                   "ret $8" )
444

445
#elif defined(__arm__)
446

447 448 449 450 451 452 453 454 455 456 457
/***********************************************************************
 *           relay_trace_entry
 */
DECLSPEC_HIDDEN void * WINAPI relay_trace_entry( struct relay_descr *descr, unsigned int idx,
                                                 const DWORD *stack, unsigned int *nb_args )
{
    WORD ordinal = LOWORD(idx);
    const char *arg_types = descr->args_string + HIWORD(idx);
    struct relay_private_data *data = descr->private;
    struct relay_entry_point *entry_point = data->entry_points + ordinal;
    unsigned int i, pos;
458 459 460 461
#ifndef __SOFTFP__
    unsigned int float_pos = 0, double_pos = 0;
    const union fpregs { float s[16]; double d[8]; } *fpstack = (const union fpregs *)stack - 1;
#endif
462 463 464 465 466 467 468 469

    TRACE( "\1Call %s(", func_name( data, ordinal ));

    for (i = pos = 0; !is_ret_val( arg_types[i] ); i++)
    {
        switch (arg_types[i])
        {
        case 'j': /* int64 */
470
            pos = (pos + 1) & ~1;
471 472 473 474 475 476 477 478 479 480 481 482 483
            TRACE( "%x%08x", stack[pos+1], stack[pos] );
            pos += 2;
            break;
        case 'k': /* int128 */
            TRACE( "{%08x,%08x,%08x,%08x}", stack[pos], stack[pos+1], stack[pos+2], stack[pos+3] );
            pos += 4;
            break;
        case 's': /* str */
            trace_string_a( stack[pos++] );
            break;
        case 'w': /* wstr */
            trace_string_w( stack[pos++] );
            break;
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
        case 'f': /* float */
#ifndef __SOFTFP__
            if (!(float_pos % 2)) float_pos = max( float_pos, double_pos * 2 );
            if (float_pos < 16)
            {
                TRACE( "%g", fpstack->s[float_pos++] );
                break;
            }
#endif
            TRACE( "%g", *(const float *)&stack[pos++] );
            break;
        case 'd': /* double */
#ifndef __SOFTFP__
            double_pos = max( (float_pos + 1) / 2, double_pos );
            if (double_pos < 8)
            {
                TRACE( "%g", fpstack->d[double_pos++] );
                break;
            }
#endif
            pos = (pos + 1) & ~1;
            TRACE( "%g", *(const double *)&stack[pos] );
            pos += 2;
            break;
508 509 510 511 512 513 514
        case 'i': /* long */
        default:
            TRACE( "%08x", stack[pos++] );
            break;
        }
        if (!is_ret_val( arg_types[i+1] )) TRACE( "," );
    }
515 516 517 518 519 520 521 522

#ifndef __SOFTFP__
    if (float_pos || double_pos)
    {
        pos |= 0x80000000;
        stack = (const DWORD *)fpstack;  /* retaddr is below the fp regs */
    }
#endif
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
    *nb_args = pos;
    TRACE( ") ret=%08x\n", stack[-1] );
    return entry_point->orig_func;
}

/***********************************************************************
 *           relay_trace_exit
 */
DECLSPEC_HIDDEN void WINAPI relay_trace_exit( struct relay_descr *descr, unsigned int idx,
                                              DWORD retaddr, LONGLONG retval )
{
    const char *arg_types = descr->args_string + HIWORD(idx);

    TRACE( "\1Ret  %s()", func_name( descr->private, LOWORD(idx) ));

    while (!is_ret_val( *arg_types )) arg_types++;
    if (*arg_types == 'J')  /* int64 return value */
        TRACE( " retval=%08x%08x ret=%08x\n",
               (UINT)(retval >> 32), (UINT)retval, retaddr );
    else
        TRACE( " retval=%08x ret=%08x\n", (UINT)retval, retaddr );
}

546 547 548 549 550 551 552 553 554 555 556 557
extern LONGLONG WINAPI relay_call( struct relay_descr *descr, unsigned int idx, const DWORD *stack );
__ASM_GLOBAL_FUNC( relay_call,
                   "push {r4-r8,lr}\n\t"
                   "sub sp, #16\n\t"
                   "mov r6, r2\n\t"
                   "add r3, sp, #12\n\t"
                   "mov r7, r0\n\t"
                   "mov r8, r1\n\t"
                   "bl " __ASM_NAME("relay_trace_entry") "\n\t"
                   "mov ip, r0\n\t"  /* entry point */
                   "mov r5, sp\n\t"
                   "ldr r1, [sp, #12]\n\t"  /* number of args */
558
                   "lsl r3, r1, #2\n\t"
559 560
                   "subs r3, #16\n\t"   /* first 4 args are in registers */
                   "ble 2f\n\t"
561 562
                   "add r3, #7\n\t"
                   "and r3, #~7\n"
563 564 565
                   "sub sp, r3\n\t"
                   "add r2, r6, #16\n\t"   /* skip r0-r3 */
                   "1:\tsubs r3, r3, #4\n\t"
566
                   "ldr r0, [r2, r3]\n\t"
567 568 569 570 571 572
                   "str r0, [sp, r3]\n\t"
                   "bgt 1b\n"
                   "2:\t"
#ifndef __SOFTFP__
                   "tst r1, #0x80000000\n\t"
                   "ldm r6, {r0-r3}\n\t"
573
                   "it ne\n\t"
574 575 576 577 578 579 580 581 582 583 584
                   "vldmdbne r6!, {s0-s15}\n\t"
#else
                   "ldm r6, {r0-r3}\n\t"
#endif
                   "blx ip\n\t"
                   "mov sp, r5\n\t"
                   "ldr r2, [r6, #-4]\n\t"  /* retaddr */
                   "mov r4, r0\n\t"
                   "mov r5, r1\n\t"
                   "mov r0, r7\n\t"
                   "mov r1, r8\n\t"
585 586
                   "str r4, [sp]\n\t"
                   "str r5, [sp, #4]\n\t"
587 588 589 590 591 592 593 594 595 596 597
#ifndef __SOFTFP__
                   "vstr d0, [sp, #8]\n\t"  /* preserve floating point retval */
                   "bl " __ASM_NAME("relay_trace_exit") "\n\t"
                   "vldr d0, [sp, #8]\n\t"
#else
                   "bl " __ASM_NAME("relay_trace_exit") "\n\t"
#endif
                   "mov r0, r4\n\t"
                   "mov r1, r5\n\t"
                   "add sp, #16\n\t"
                   "pop {r4-r8,pc}" )
598

599 600
#elif defined(__aarch64__)

601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
/***********************************************************************
 *           relay_trace_entry
 */
DECLSPEC_HIDDEN void * WINAPI relay_trace_entry( struct relay_descr *descr, unsigned int idx,
                                                 const INT_PTR *stack, unsigned int *nb_args )
{
    WORD ordinal = LOWORD(idx);
    const char *arg_types = descr->args_string + HIWORD(idx);
    struct relay_private_data *data = descr->private;
    struct relay_entry_point *entry_point = data->entry_points + ordinal;
    unsigned int i;

    TRACE( "\1Call %s(", func_name( data, ordinal ));

    for (i = 0; !is_ret_val( arg_types[i] ); i++)
    {
        switch (arg_types[i])
        {
        case 's': /* str */
            trace_string_a( stack[i] );
            break;
        case 'w': /* wstr */
            trace_string_w( stack[i] );
            break;
        case 'i': /* long */
        default:
627
            TRACE( "%08zx", stack[i] );
628 629 630 631 632
            break;
        }
        if (!is_ret_val( arg_types[i + 1] )) TRACE( "," );
    }
    *nb_args = i;
633
    TRACE( ") ret=%08zx\n", stack[-1] );
634 635 636 637 638 639 640 641 642
    return entry_point->orig_func;
}

/***********************************************************************
 *           relay_trace_exit
 */
DECLSPEC_HIDDEN void WINAPI relay_trace_exit( struct relay_descr *descr, unsigned int idx,
                                              INT_PTR retaddr, INT_PTR retval )
{
643
    TRACE( "\1Ret  %s() retval=%08zx ret=%08zx\n",
644 645 646 647
           func_name( descr->private, LOWORD(idx) ), retval, retaddr );
}

extern LONGLONG CDECL call_entry_point( void *func, int nb_args, const INT_PTR *args );
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
__ASM_GLOBAL_FUNC( call_entry_point,
                   "stp x29, x30, [SP,#-16]!\n\t"
                   "stp x19, x20, [SP,#-16]!\n\t"
                   "mov x29, SP\n\t"
                   "mov x19, x2\n\t"
                   "ldp x8, x9, [x19, #-32]\n\t"
                   "mov x9, x0\n\t"
                   "cbz w1, 2f\n\t"
                   "mov w10, w1\n\t"
                   "mov x11, x2\n\t"
                   "mov x12, #0\n\t"
                   "ldp x0, x1, [x11], #16\n\t"
                   "add w12, w12, #2\n\t"
                   "cmp w12, w10\n\t"
                   "b.hs 2f\n\t"
                   "ldp x2, x3, [x11], #16\n\t"
                   "add w12, w12, #2\n\t"
                   "cmp w12, w10\n\t"
                   "b.hs 2f\n\t"
                   "ldp x4, x5, [x11], #16\n\t"
                   "add w12, w12, #2\n\t"
                   "cmp w12, w10\n\t"
                   "b.hs 2f\n\t"
                   "ldp x6, x7, [x11], #16\n\t"
                   "add w12, w12, #2\n\t"
                   "cmp w12, w10\n\t"
                   "b.hs 2f\n\t"
                   "sub w12, w10, #8\n\t"
                   "lsl w12, w12, #3\n\t"
                   "sub SP, SP, w12, uxtw\n\t"
                   "tbz w12, #3, 1f\n\t"
                   "sub SP, SP, #8\n\t"
                   "1: sub w12, w12, #8\n\t"
                   "ldr x13, [x11, x12]\n\t"
                   "str x13, [SP, x12]\n\t"
                   "cbnz w12, 1b\n\t"
                   "2: blr x9\n\t"
                   "mov SP, x29\n\t"
                   "ldp x19, x20, [SP], #16\n\t"
                   "ldp x29, x30, [SP], #16\n\t"
                   "ret\n" )

static LONGLONG WINAPI relay_call( struct relay_descr *descr, unsigned int idx, const INT_PTR *stack )
{
692 693 694 695
    unsigned int nb_args;
    void *func = relay_trace_entry( descr, idx, stack, &nb_args );
    LONGLONG ret = call_entry_point( func, nb_args, stack );
    relay_trace_exit( descr, idx, stack[-1], ret );
696 697 698
    return ret;
}

699 700
#elif defined(__x86_64__)

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
/***********************************************************************
 *           relay_trace_entry
 */
DECLSPEC_HIDDEN void * WINAPI relay_trace_entry( struct relay_descr *descr, unsigned int idx,
                                                 const INT_PTR *stack, unsigned int *nb_args )
{
    WORD ordinal = LOWORD(idx);
    const char *arg_types = descr->args_string + HIWORD(idx);
    struct relay_private_data *data = descr->private;
    struct relay_entry_point *entry_point = data->entry_points + ordinal;
    unsigned int i;

    TRACE( "\1Call %s(", func_name( data, ordinal ));

    for (i = 0; !is_ret_val( arg_types[i] ); i++)
    {
        switch (arg_types[i])
        {
        case 's': /* str */
            trace_string_a( stack[i] );
            break;
        case 'w': /* wstr */
            trace_string_w( stack[i] );
            break;
        case 'f': /* float */
            TRACE( "%g", *(const float *)&stack[i] );
            break;
        case 'd': /* double */
            TRACE( "%g", *(const double *)&stack[i] );
            break;
        case 'i': /* long */
        default:
733
            TRACE( "%08zx", stack[i] );
734 735
            break;
        }
736
        if (!is_ret_val( arg_types[i+1] )) TRACE( "," );
737 738
    }
    *nb_args = i;
739
    TRACE( ") ret=%08zx\n", stack[-1] );
740 741 742 743 744 745 746 747 748
    return entry_point->orig_func;
}

/***********************************************************************
 *           relay_trace_exit
 */
DECLSPEC_HIDDEN void WINAPI relay_trace_exit( struct relay_descr *descr, unsigned int idx,
                                              INT_PTR retaddr, INT_PTR retval )
{
749
    TRACE( "\1Ret  %s() retval=%08zx ret=%08zx\n",
750 751 752 753
           func_name( descr->private, LOWORD(idx) ), retval, retaddr );
}

extern INT_PTR WINAPI relay_call( struct relay_descr *descr, unsigned int idx, const INT_PTR *stack );
754
__ASM_GLOBAL_FUNC( relay_call,
755
                   "pushq %rbp\n\t"
756
                   __ASM_SEH(".seh_pushreg %rbp\n\t")
757 758
                   __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t")
                   __ASM_CFI(".cfi_rel_offset %rbp,0\n\t")
759
                   "movq %rsp,%rbp\n\t"
760 761
                   __ASM_SEH(".seh_setframe %rbp,0\n\t")
                   __ASM_SEH(".seh_endprologue\n\t")
762
                   __ASM_CFI(".cfi_def_cfa_register %rbp\n\t")
763 764 765 766 767 768 769
                   "leaq -0x48(%rbp),%rsp\n\t"
                   "andq $~15,%rsp\n\t"
                   "movq %rcx,-32(%rbp)\n\t"
                   __ASM_CFI(".cfi_rel_offset %rcx,-32\n\t")
                   "movq %rdx,-24(%rbp)\n\t"
                   __ASM_CFI(".cfi_rel_offset %rdx,-24\n\t")
                   "movq %rsi,-16(%rbp)\n\t"
770
                   __ASM_CFI(".cfi_rel_offset %rsi,-16\n\t")
771
                   "movq %rdi,-8(%rbp)\n\t"
772 773
                   __ASM_CFI(".cfi_rel_offset %rdi,-8\n\t")
                   /* trace the parameters */
774 775
                   "leaq 24(%rbp),%r8\n\t"   /* stack */
                   "leaq -40(%rbp),%r9\n\t"
776 777
                   "call " __ASM_NAME("relay_trace_entry") "\n\t"
                   /* copy the arguments */
778
                   "movl -40(%rbp),%edx\n\t"  /* number of args */
779 780 781
                   "movq $4,%rcx\n\t"
                   "cmp %rcx,%rdx\n\t"
                   "cmovgq %rdx,%rcx\n\t"
782 783
                   "leaq -16(,%rcx,8),%rdx\n\t"
                   "andq $~15,%rdx\n\t"
784
                   "subq %rdx,%rsp\n\t"
785
                   "leaq 24(%rbp),%rsi\n\t"  /* original stack */
786 787
                   "movq %rsp,%rdi\n\t"
                   "rep; movsq\n\t"
788
                   /* call the entry point */
789 790 791 792
                   "movq 0(%rsp),%rcx\n\t"
                   "movq 8(%rsp),%rdx\n\t"
                   "movq 16(%rsp),%r8\n\t"
                   "movq 24(%rsp),%r9\n\t"
793 794 795 796
                   "movq 0(%rsp),%xmm0\n\t"
                   "movq 8(%rsp),%xmm1\n\t"
                   "movq 16(%rsp),%xmm2\n\t"
                   "movq 24(%rsp),%xmm3\n\t"
797
                   "callq *%rax\n\t"
798
                   /* trace the return value */
799 800 801
                   "movq -32(%rbp),%rcx\n\t"
                   "movq -24(%rbp),%rdx\n\t"
                   "movq 16(%rbp),%r8\n\t"   /* retaddr */
802
                   "movq %rax,%rsi\n\t"
803
                   "movaps %xmm0,32(%rsp)\n\t"
804 805 806 807
                   "movq %rax,%r9\n\t"
                   "call " __ASM_NAME("relay_trace_exit") "\n\t"
                   /* restore return value and return */
                   "movq %rsi,%rax\n\t"
808 809
                   "movaps 32(%rsp),%xmm0\n\t"
                   "movq -16(%rbp),%rsi\n\t"
810
                   __ASM_CFI(".cfi_same_value %rsi\n\t")
811
                   "movq -8(%rbp),%rdi\n\t"
812
                   __ASM_CFI(".cfi_same_value %rdi\n\t")
813
                   "leaq 0(%rbp),%rsp\n\t"
814
                   __ASM_CFI(".cfi_def_cfa_register %rsp\n\t")
815
                   "popq %rbp\n\t"
816 817
                   __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t")
                   __ASM_CFI(".cfi_same_value %rbp\n\t")
818
                   "ret")
819

820 821 822
#else
#error Not supported on this CPU
#endif
823 824


825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
static struct relay_descr *get_relay_descr( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports,
                                            DWORD exp_size )
{
    struct relay_descr *descr;
    struct relay_descr_rva *rva;
    ULONG_PTR ptr = (ULONG_PTR)module + exports->Name;

    /* sanity checks */
    if (ptr <= (ULONG_PTR)(exports + 1)) return NULL;
    if (ptr > (ULONG_PTR)exports + exp_size) return NULL;
    if (ptr % sizeof(DWORD)) return NULL;

    rva = (struct relay_descr_rva *)ptr - 1;
    if (rva->magic != RELAY_DESCR_MAGIC) return NULL;
    if (rva->descr) descr = (struct relay_descr *)((char *)module + rva->descr);
    else descr = (struct relay_descr *)((const char *)exports + exp_size);
    if (descr->magic != RELAY_DESCR_MAGIC) return NULL;
    return descr;
}

845 846 847 848 849
/***********************************************************************
 *           RELAY_GetProcAddress
 *
 * Return the proc address to use for a given function.
 */
Eric Pouech's avatar
Eric Pouech committed
850
FARPROC RELAY_GetProcAddress( HMODULE module, const IMAGE_EXPORT_DIRECTORY *exports,
851
                              DWORD exp_size, FARPROC proc, DWORD ordinal, const WCHAR *user )
852
{
853
    struct relay_private_data *data;
854
    const struct relay_descr *descr = get_relay_descr( module, exports, exp_size );
855

856
    if (!descr || !(data = descr->private)) return proc;  /* no relay data */
857
    if (!data->entry_points[ordinal].orig_func) return proc;  /* not a relayed function */
858
    if (check_from_module( debug_from_relay_includelist, debug_from_relay_excludelist, user ))
859 860
        return proc;  /* we want to relay it */
    return data->entry_points[ordinal].orig_func;
861 862 863
}


864 865 866 867 868
/***********************************************************************
 *           RELAY_SetupDLL
 *
 * Setup relay debugging for a built-in dll.
 */
869
void RELAY_SetupDLL( HMODULE module )
870 871 872
{
    IMAGE_EXPORT_DIRECTORY *exports;
    DWORD *funcs;
873
    unsigned int i, len;
874
    DWORD size, entry_point_rva, old_prot;
875 876
    struct relay_descr *descr;
    struct relay_private_data *data;
877
    WCHAR dllnameW[sizeof(data->dllname)];
878
    const WORD *ordptr;
879 880
    void *func_base;
    SIZE_T func_size;
881

882
    RtlRunOnceExecuteOnce( &init_once, init_debug_lists, NULL, NULL );
883

884
    exports = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size );
885
    if (!exports) return;
886

887
    if (!(descr = get_relay_descr( module, exports, size ))) return;
888 889 890 891 892

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

893
    descr->relay_call = relay_call;
894 895 896 897 898
    descr->private = data;

    data->module = module;
    data->base   = exports->Base;
    len = strlen( (char *)module + exports->Name );
899
    if (len > 4 && !_stricmp( (char *)module + exports->Name + len - 4, ".dll" )) len -= 4;
900 901 902
    len = min( len, sizeof(data->dllname) - 1 );
    memcpy( data->dllname, (char *)module + exports->Name, len );
    data->dllname[len] = 0;
903
    ascii_to_unicode( dllnameW, data->dllname, len + 1 );
904 905 906 907 908

    /* 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++)
909
    {
910 911 912
        DWORD name_rva = ((DWORD*)((char *)module + exports->AddressOfNames))[i];
        data->entry_points[*ordptr].name = (const char *)module + name_rva;
    }
913

914
    /* patch the functions in the export table to point to the relay thunks */
915

916
    funcs = (DWORD *)((char *)module + exports->AddressOfFunctions);
917
    entry_point_rva = descr->entry_point_base - (const char *)module;
918 919 920 921

    func_base = funcs;
    func_size = exports->NumberOfFunctions * sizeof(*funcs);
    NtProtectVirtualMemory( NtCurrentProcess(), &func_base, &func_size, PAGE_READWRITE, &old_prot );
922 923 924
    for (i = 0; i < exports->NumberOfFunctions; i++, funcs++)
    {
        if (!descr->entry_point_offsets[i]) continue;   /* not a normal function */
925
        if (!check_relay_include( dllnameW, i + exports->Base, data->entry_points[i].name ))
926
            continue;  /* don't include this entry point */
927

928 929
        data->entry_points[i].orig_func = (char *)module + *funcs;
        *funcs = entry_point_rva + descr->entry_point_offsets[i];
930
    }
931 932
    if (old_prot != PAGE_READWRITE)
        NtProtectVirtualMemory( NtCurrentProcess(), &func_base, &func_size, old_prot, &old_prot );
933 934
}

935
#else  /* __i386__ || __x86_64__ || __arm__ || __aarch64__ */
936 937 938 939 940 941 942 943 944 945 946

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 )
{
}

947
#endif  /* __i386__ || __x86_64__ || __arm__ || __aarch64__ */
948

949 950 951 952 953

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

954 955 956 957 958
#ifdef __i386__

WINE_DECLARE_DEBUG_CHANNEL(seh);
WINE_DECLARE_DEBUG_CHANNEL(snoop);

959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989
#include "pshpack1.h"

typedef	struct
{
	/* code part */
	BYTE		lcall;		/* 0xe8 call snoopentry (relative) */
	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*/
	DWORD		snoopret;	/* SNOOP_Ret relative */
	/* unreached */
	FARPROC	origreturn;
	SNOOP_DLL	*dll;
	DWORD		ordinal;
990
	void		**origESP;
991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
	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;


1008 1009 1010 1011 1012 1013
/***********************************************************************
 *          SNOOP_ShowDebugmsgSnoop
 *
 * Simple function to decide if a particular debugging message is
 * wanted.
 */
1014
static BOOL SNOOP_ShowDebugmsgSnoop(const char *module, int ordinal, const char *func)
1015
{
1016 1017 1018 1019 1020 1021
    WCHAR moduleW[40];
    int len = strlen(module);

    if (len >= ARRAY_SIZE( moduleW )) return FALSE;
    ascii_to_unicode( moduleW, module, len + 1 );
    if (debug_snoop_excludelist && check_list( moduleW, ordinal, func, debug_snoop_excludelist ))
1022
        return FALSE;
1023
    if (debug_snoop_includelist && !check_list( moduleW, ordinal, func, debug_snoop_includelist ))
1024 1025
        return FALSE;
    return TRUE;
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
}


/***********************************************************************
 *           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;
1040
    ULONG size32;
1041 1042
    IMAGE_EXPORT_DIRECTORY *exports;

1043
    RtlRunOnceExecuteOnce( &init_once, init_debug_lists, NULL, NULL );
1044

1045
    exports = RtlImageDirectoryEntryToData( hmod, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &size32 );
1046
    if (!exports || !exports->NumberOfFunctions) return;
1047
    name = (char *)hmod + exports->Name;
1048
    size = size32;
1049

1050
    TRACE_(snoop)("hmod=%p, name=%s\n", hmod, name);
1051 1052 1053 1054 1055 1056 1057

    while (*dll) {
        if ((*dll)->hmod == hmod)
        {
            /* another dll, loaded at the same address */
            addr = (*dll)->funs;
            size = (*dll)->nrofordinals * sizeof(SNOOP_FUN);
1058
            NtFreeVirtualMemory(NtCurrentProcess(), &addr, &size, MEM_RELEASE);
1059 1060 1061 1062
            break;
        }
        dll = &((*dll)->next);
    }
1063 1064
    if (*dll)
        *dll = RtlReAllocateHeap(GetProcessHeap(),
1065 1066
                             HEAP_ZERO_MEMORY, *dll,
                             sizeof(SNOOP_DLL) + strlen(name));
1067 1068 1069 1070
    else
        *dll = RtlAllocateHeap(GetProcessHeap(),
                             HEAP_ZERO_MEMORY,
                             sizeof(SNOOP_DLL) + strlen(name));
1071 1072 1073 1074 1075
    (*dll)->hmod	= hmod;
    (*dll)->ordbase = exports->Base;
    (*dll)->nrofordinals = exports->NumberOfFunctions;
    strcpy( (*dll)->name, name );
    p = (*dll)->name + strlen((*dll)->name) - 4;
1076
    if (p > (*dll)->name && !_stricmp( p, ".dll" )) *p = 0;
1077 1078

    size = exports->NumberOfFunctions * sizeof(SNOOP_FUN);
1079
    addr = NULL;
1080
    NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 0, &size,
1081 1082
                            MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!addr) {
1083
        RtlFreeHeap(GetProcessHeap(),0,*dll);
1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
        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
1097
FARPROC SNOOP_GetProcAddress( HMODULE hmod, const IMAGE_EXPORT_DIRECTORY *exports,
1098 1099
                              DWORD exp_size, FARPROC origfun, DWORD ordinal,
                              const WCHAR *user)
1100
{
1101
    unsigned int i;
Eric Pouech's avatar
Eric Pouech committed
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112
    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
1113
    if (!*(LPBYTE)origfun) /* 0x00 is an impossible opcode, possible dataref. */
Eric Pouech's avatar
Eric Pouech committed
1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135
        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)
1136
        {
Eric Pouech's avatar
Eric Pouech committed
1137 1138
            ename = (const char *)hmod + names[i];
            break;
1139
        }
Eric Pouech's avatar
Eric Pouech committed
1140 1141 1142 1143 1144 1145 1146 1147 1148
    }
    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;
1149
        fun->snoopentry	= (char *)SNOOP_Entry - (char *)(&fun->snoopentry + 1);
Eric Pouech's avatar
Eric Pouech committed
1150 1151 1152 1153
        fun->origfun	= origfun;
        fun->nrofargs	= -1;
    }
    return (FARPROC)&(fun->lcall);
1154 1155 1156 1157 1158 1159
}

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

1160
    TRACE_(snoop)("%08x",x);
1161
    if (IS_INTARG(x) || TRACE_ON(seh)) return; /* trivial reject to avoid faults */
1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172
    __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)
1173
            TRACE_(snoop)(" %s",debugstr_an((LPSTR)x,i));
1174 1175 1176 1177 1178 1179 1180 1181 1182 1183
        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++;
            }
1184
            if (!nostring && i > 5) TRACE_(snoop)(" %s",debugstr_wn((LPWSTR)x,i));
1185 1186
        }
    }
1187
    __EXCEPT_PAGE_FAULT
1188
    {
1189
    }
1190
    __ENDTRY
1191 1192
}

1193
void WINAPI DECLSPEC_HIDDEN __regs_SNOOP_Entry( void **stack )
1194
{
1195 1196
	SNOOP_DLL *dll;
	SNOOP_FUN *fun = (SNOOP_FUN *)((char *)stack[0] - 5);
1197 1198 1199 1200
	SNOOP_RETURNENTRIES	**rets = &firstrets;
	SNOOP_RETURNENTRY	*ret;
	int		i=0, max;

1201 1202 1203
	for (dll = firstdll; dll; dll = dll->next )
            if (fun >= dll->funs && fun < dll->funs + dll->nrofordinals) break;

1204
	if (!dll) {
1205
		FIXME("entrypoint %p not found\n", fun);
1206 1207 1208 1209 1210 1211 1212 1213 1214
		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)
		 */
1215
		LPBYTE	reteip = stack[1];
1216 1217 1218 1219 1220 1221 1222 1223 1224

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


	while (*rets) {
1225
		for (i=0;i<ARRAY_SIZE( (*rets)->entry );i++)
1226 1227
			if (!(*rets)->entry[i].origreturn)
				break;
1228
		if (i!=ARRAY_SIZE( (*rets)->entry ))
1229 1230 1231 1232 1233
			break;
		rets = &((*rets)->next);
	}
	if (!*rets) {
                SIZE_T size = 4096;
1234
                VOID* addr = NULL;
1235

1236
                NtAllocateVirtualMemory(NtCurrentProcess(), &addr, 0, &size, 
1237 1238 1239 1240 1241 1242 1243 1244
                                        MEM_COMMIT | MEM_RESERVE,
                                        PAGE_EXECUTE_READWRITE);
                if (!addr) return;
                *rets = addr;
		i = 0;	/* entry 0 is free */
	}
	ret = &((*rets)->entry[i]);
	ret->lcall	= 0xe8;
1245 1246 1247
	ret->snoopret	= (char *)SNOOP_Return - (char *)(&ret->snoopret + 1);
	ret->origreturn	= stack[1];
	stack[1]	= &ret->lcall;
1248 1249
	ret->dll	= dll;
	ret->args	= NULL;
1250 1251
	ret->ordinal	= fun - dll->funs;
	ret->origESP	= stack;
1252

1253
	stack[0] = fun->origfun;
1254

1255 1256
        if (!TRACE_ON(snoop)) return;

1257 1258
	if (fun->name) TRACE_(snoop)("\1CALL %s.%s(", dll->name, fun->name);
	else TRACE_(snoop)("\1CALL %s.%d(", dll->name, dll->ordbase+ret->ordinal);
1259 1260 1261 1262
	if (fun->nrofargs>0) {
		max = fun->nrofargs; if (max>16) max=16;
		for (i=0;i<max;i++)
                {
1263
                    SNOOP_PrintArg( (DWORD)stack[i + 2] );
1264
                    if (i<fun->nrofargs-1) TRACE_(snoop)(",");
1265 1266
                }
		if (max!=fun->nrofargs)
1267
			TRACE_(snoop)(" ...");
1268
	} else if (fun->nrofargs<0) {
1269
		TRACE_(snoop)("<unknown, check return>");
1270
		ret->args = RtlAllocateHeap(GetProcessHeap(),
1271
                                            0,16*sizeof(DWORD));
1272
		memcpy(ret->args, stack + 2, sizeof(DWORD)*16);
1273
	}
1274
	TRACE_(snoop)(") ret=%08x\n",(DWORD)ret->origreturn);
1275 1276
}

1277
void WINAPI DECLSPEC_HIDDEN __regs_SNOOP_Return( void **stack )
1278
{
1279
	SNOOP_RETURNENTRY *ret = (SNOOP_RETURNENTRY*)((char *)stack[0] - 5);
1280
        SNOOP_FUN *fun = &ret->dll->funs[ret->ordinal];
1281
        DWORD retval = (DWORD)stack[-1];  /* get saved %eax from the stack */
1282 1283 1284 1285 1286 1287 1288

	/* 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)
1289 1290
		ret->dll->funs[ret->ordinal].nrofargs = stack - ret->origESP - 1;
	stack[0] = ret->origreturn;
1291 1292 1293 1294 1295 1296

        if (!TRACE_ON(snoop)) {
            ret->origreturn = NULL; /* mark as empty */
            return;
        }

1297 1298 1299 1300
	if (ret->args) {
		int	i,max;

                if (fun->name)
1301
                    TRACE_(snoop)("\1RET  %s.%s(", ret->dll->name, fun->name);
1302
                else
1303
                    TRACE_(snoop)("\1RET  %s.%d(", ret->dll->name, ret->dll->ordbase+ret->ordinal);
1304 1305 1306 1307 1308 1309 1310

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

		for (i=0;i<max;i++)
                {
                    SNOOP_PrintArg(ret->args[i]);
1311
                    if (i<max-1) TRACE_(snoop)(",");
1312
                }
1313
		TRACE_(snoop)(") retval=%08x ret=%08x\n", retval, (DWORD)ret->origreturn );
1314
		RtlFreeHeap(GetProcessHeap(),0,ret->args);
1315 1316 1317 1318 1319
		ret->args = NULL;
	}
        else
        {
            if (fun->name)
1320 1321
		TRACE_(snoop)("\1RET  %s.%s() retval=%08x ret=%08x\n",
                        ret->dll->name, fun->name, retval, (DWORD)ret->origreturn);
1322
            else
1323
		TRACE_(snoop)("\1RET  %s.%d() retval=%08x ret=%08x\n",
1324
			ret->dll->name,ret->dll->ordbase+ret->ordinal,
1325
			retval, (DWORD)ret->origreturn);
1326 1327 1328 1329
        }
	ret->origreturn = NULL; /* mark as empty */
}

1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341
/* small wrappers that save registers and get the stack pointer */
#define SNOOP_WRAPPER(name) \
    __ASM_STDCALL_FUNC( name, 0,                                        \
                        "pushl %eax\n\t"                                \
                        __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")       \
                        "pushl %ecx\n\t"                                \
                        __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")       \
                        "pushl %edx\n\t"                                \
                        __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")       \
                        "leal 12(%esp),%eax\n\t"                        \
                        "pushl %eax\n\t"                                \
                        __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")       \
1342
                        "call " __ASM_STDCALL("__regs_" #name,4) "\n\t" \
1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353
                        __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t")      \
                        "popl %edx\n\t"                                 \
                        __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t")      \
                        "popl %ecx\n\t"                                 \
                        __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t")      \
                        "popl %eax\n\t"                                 \
                        __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t")      \
                        "ret" )

SNOOP_WRAPPER( SNOOP_Entry )
SNOOP_WRAPPER( SNOOP_Return )
1354

1355 1356
#else  /* __i386__ */

1357
FARPROC SNOOP_GetProcAddress( HMODULE hmod, const IMAGE_EXPORT_DIRECTORY *exports, DWORD exp_size,
1358
                              FARPROC origfun, DWORD ordinal, const WCHAR *user )
1359 1360 1361 1362 1363 1364 1365 1366 1367
{
    return origfun;
}

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

1368
#endif /* __i386__ */