wowthunk.c 29.1 KB
Newer Older
1 2 3
/*
 * Win32 WOW Generic Thunk API
 *
4
 * Copyright 1999 Ulrich Weigand
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * 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
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 20
 */

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

24
#include <assert.h>
25
#include <stdarg.h>
26
#include <errno.h>
27

28
#include "wine/winbase16.h"
29
#include "windef.h"
30
#include "winbase.h"
31
#include "winerror.h"
32
#include "wownt32.h"
33
#include "excpt.h"
34
#include "winternl.h"
35
#include "kernel_private.h"
36
#include "kernel16_private.h"
37
#include "wine/exception.h"
38
#include "wine/debug.h"
39

40
WINE_DEFAULT_DEBUG_CHANNEL(thunk);
41
WINE_DECLARE_DEBUG_CHANNEL(relay);
42
WINE_DECLARE_DEBUG_CHANNEL(snoop);
43

44
/*
45 46
 * These are the 16-bit side WOW routines.  They reside in wownt16.h
 * in the SDK; since we don't support Win16 source code anyway, I've
47 48 49 50 51 52 53 54 55 56 57 58
 * placed them here for compilation with Wine ...
 */

DWORD WINAPI GetVDMPointer32W16(SEGPTR,UINT16);

DWORD WINAPI LoadLibraryEx32W16(LPCSTR,DWORD,DWORD);
DWORD WINAPI GetProcAddress32W16(DWORD,LPCSTR);
DWORD WINAPI FreeLibrary32W16(DWORD);

#define CPEX_DEST_STDCALL   0x00000000L
#define CPEX_DEST_CDECL     0x80000000L

59 60

#ifdef __i386__
61

62 63 64
/* symbols exported from relay16.s */
extern DWORD WINAPI wine_call_to_16( FARPROC16 target, DWORD cbArgs, PEXCEPTION_HANDLER handler );
extern void WINAPI wine_call_to_16_regs( CONTEXT86 *context, DWORD cbArgs, PEXCEPTION_HANDLER handler );
65
extern void __wine_call_to_16_ret(void);
66 67
extern void CALL32_CBClient_Ret();
extern void CALL32_CBClientEx_Ret();
68 69 70
extern void DPMI_PendingEventCheck();
extern void DPMI_PendingEventCheck_Cleanup();
extern void DPMI_PendingEventCheck_Return();
71 72
extern BYTE __wine_call16_start[];
extern BYTE __wine_call16_end[];
73

74 75
extern void RELAY16_InitDebugLists(void);

76
static SEGPTR call16_ret_addr;  /* segptr to __wine_call_to_16_ret routine */
77

78 79 80 81 82
static WORD  dpmi_checker_selector;
static DWORD dpmi_checker_offset_call;
static DWORD dpmi_checker_offset_cleanup;
static DWORD dpmi_checker_offset_return;

83 84 85 86 87 88
/***********************************************************************
 *           WOWTHUNK_Init
 */
BOOL WOWTHUNK_Init(void)
{
    /* allocate the code selector for CallTo16 routines */
89 90 91
    LDT_ENTRY entry;
    WORD codesel = wine_ldt_alloc_entries(1);

92
    if (!codesel) return FALSE;
93 94 95 96
    wine_ldt_set_base( &entry, __wine_call16_start );
    wine_ldt_set_limit( &entry, (BYTE *)(&CallTo16_TebSelector + 1) - __wine_call16_start - 1 );
    wine_ldt_set_flags( &entry, WINE_LDT_FLAGS_CODE | WINE_LDT_FLAGS_32BIT );
    wine_ldt_set_entry( codesel, &entry );
97 98 99 100

      /* Patch the return addresses for CallTo16 routines */

    CallTo16_DataSelector = wine_get_ds();
101
    call16_ret_addr = MAKESEGPTR( codesel, (BYTE *)__wine_call_to_16_ret - __wine_call16_start );
102
    CALL32_CBClient_RetAddr =
103
        MAKESEGPTR( codesel, (BYTE *)CALL32_CBClient_Ret - __wine_call16_start );
104
    CALL32_CBClientEx_RetAddr =
105
        MAKESEGPTR( codesel, (BYTE *)CALL32_CBClientEx_Ret - __wine_call16_start );
106

107 108
    /* Prepare selector and offsets for DPMI event checking. */
    dpmi_checker_selector = codesel;
109 110 111
    dpmi_checker_offset_call = (BYTE *)DPMI_PendingEventCheck - __wine_call16_start;
    dpmi_checker_offset_cleanup = (BYTE *)DPMI_PendingEventCheck_Cleanup - __wine_call16_start;
    dpmi_checker_offset_return = (BYTE *)DPMI_PendingEventCheck_Return - __wine_call16_start;
112

113
    if (TRACE_ON(relay) || TRACE_ON(snoop)) RELAY16_InitDebugLists();
114

115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
    return TRUE;
}


/*************************************************************
 *            fix_selector
 *
 * Fix a selector load that caused an exception if it's in the
 * 16-bit relay code.
 */
static BOOL fix_selector( CONTEXT *context )
{
    WORD *stack;
    BYTE *instr = (BYTE *)context->Eip;

130
    if (instr < __wine_call16_start || instr >= __wine_call16_end) return FALSE;
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156

    /* skip prefixes */
    while (*instr == 0x66 || *instr == 0x67) instr++;

    switch(instr[0])
    {
    case 0x07: /* pop es */
    case 0x17: /* pop ss */
    case 0x1f: /* pop ds */
        break;
    case 0x0f: /* extended instruction */
        switch(instr[1])
        {
        case 0xa1: /* pop fs */
        case 0xa9: /* pop gs */
            break;
        default:
            return FALSE;
        }
        break;
    default:
        return FALSE;
    }
    stack = wine_ldt_get_ptr( context->SegSs, context->Esp );
    TRACE( "fixing up selector %x for pop instruction\n", *stack );
    *stack = 0;
157 158 159 160
    return TRUE;
}


161 162 163 164 165 166 167 168 169 170 171 172 173
/*************************************************************
 *            insert_event_check
 *
 * Make resuming the context check for pending DPMI events
 * before the original context is restored. This is required
 * because DPMI events are asynchronous, they are blocked while 
 * Wine 32-bit code is being executed and we want to prevent 
 * a race when returning back to 16-bit or 32-bit DPMI context.
 */
static void insert_event_check( CONTEXT *context )
{
    char *stack = wine_ldt_get_ptr( context->SegSs, context->Esp );

174 175 176 177
    /* don't do event check while in system code */
    if (wine_ldt_is_system(context->SegCs))
        return;

178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
    if(context->SegCs == dpmi_checker_selector &&
       context->Eip   >= dpmi_checker_offset_call && 
       context->Eip   <= dpmi_checker_offset_cleanup)
    {
        /*
         * Nested call. Stack will be preserved. 
         */
    }
    else if(context->SegCs == dpmi_checker_selector &&
            context->Eip   == dpmi_checker_offset_return)
    {
        /*
         * Nested call. We have just finished popping the fs
         * register, lets put it back into stack.
         */

        stack -= sizeof(WORD);
        *(WORD*)stack = context->SegFs;

        context->Esp -= 2;
    }
    else
    {
        /*
         * Call is not nested.
         * Push modified registers into stack.
         * These will be popped by the assembler stub.
         */

        stack -= sizeof(DWORD);
        *(DWORD*)stack = context->EFlags;
   
        stack -= sizeof(DWORD);
        *(DWORD*)stack = context->SegCs;

        stack -= sizeof(DWORD);
        *(DWORD*)stack = context->Eip;

        stack -= sizeof(WORD);
        *(WORD*)stack = context->SegFs;

        context->Esp  -= 14;
    }

    /*
     * Modify the context so that we jump into assembler stub.
     * TEB access is made easier by providing the stub
     * with the correct fs register value.
     */

    context->SegCs = dpmi_checker_selector;
    context->Eip   = dpmi_checker_offset_call;
    context->SegFs = wine_get_fs();
}


234 235 236 237 238
/*************************************************************
 *            call16_handler
 *
 * Handler for exceptions occurring in 16-bit code.
 */
239 240
static DWORD call16_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
                             CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **pdispatcher )
241 242 243 244 245
{
    if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
    {
        /* unwinding: restore the stack pointer in the TEB, and leave the Win16 mutex */
        STACK32FRAME *frame32 = (STACK32FRAME *)((char *)frame - offsetof(STACK32FRAME,frame));
246
        NtCurrentTeb()->WOW32Reserved = (void *)frame32->frame16;
247 248
        _LeaveWin16Lock();
    }
249 250
    else if (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
             record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION)
251
    {
252
        if (wine_ldt_is_system(context->SegCs))
253
        {
254 255
            if (fix_selector( context )) return ExceptionContinueExecution;
        }
256
        else
257
        {
258
            SEGPTR gpHandler;
259
            DWORD ret = __wine_emulate_instruction( record, context );
260

261 262 263 264 265 266
            /*
             * Insert check for pending DPMI events. Note that this 
             * check must be inserted after instructions have been 
             * emulated because the instruction emulation requires
             * original CS:IP and the emulation may change TEB.dpmi_vif.
             */
267
            if(get_vm86_teb_info()->dpmi_vif)
268 269
                insert_event_check( context );

270
            if (ret != ExceptionContinueSearch) return ret;
271 272 273

            /* check for Win16 __GP handler */
            if ((gpHandler = HasGPHandler16( MAKESEGPTR( context->SegCs, context->Eip ) )))
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
            {
                WORD *stack = wine_ldt_get_ptr( context->SegSs, context->Esp );
                *--stack = context->SegCs;
                *--stack = context->Eip;

                if (!IS_SELECTOR_32BIT(context->SegSs))
                    context->Esp = MAKELONG( LOWORD(context->Esp - 2*sizeof(WORD)),
                                             HIWORD(context->Esp) );
                else
                    context->Esp -= 2*sizeof(WORD);

                context->SegCs = SELECTOROF( gpHandler );
                context->Eip   = OFFSETOF( gpHandler );
                return ExceptionContinueExecution;
            }
289 290
        }
    }
291 292 293 294
    else if (record->ExceptionCode == EXCEPTION_VM86_STI)
    {
        insert_event_check( context );
    }
295 296 297
    return ExceptionContinueSearch;
}

298 299 300 301 302 303 304 305 306 307 308 309 310 311 312

/*************************************************************
 *            vm86_handler
 *
 * Handler for exceptions occurring in vm86 code.
 */
static DWORD vm86_handler( EXCEPTION_RECORD *record, EXCEPTION_REGISTRATION_RECORD *frame,
                           CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **pdispatcher )
{
    if (record->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
        return ExceptionContinueSearch;

    if (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
        record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION)
    {
313
        return __wine_emulate_instruction( record, context );
314 315 316 317 318 319
    }

    return ExceptionContinueSearch;
}


320 321 322 323 324 325 326 327 328
#else  /* __i386__ */

BOOL WOWTHUNK_Init(void)
{
    return TRUE;
}

#endif  /* __i386__ */

329

330 331 332 333 334
/*
 *  32-bit WOW routines (in WOW32, but actually forwarded to KERNEL32)
 */

/**********************************************************************
335
 *           K32WOWGetDescriptor        (KERNEL32.70)
336
 */
337
BOOL WINAPI K32WOWGetDescriptor( SEGPTR segptr, LPLDT_ENTRY ldtent )
338
{
339
    return GetThreadSelectorEntry( GetCurrentThread(),
340 341 342 343
                                   segptr >> 16, ldtent );
}

/**********************************************************************
344
 *           K32WOWGetVDMPointer        (KERNEL32.56)
345
 */
346
LPVOID WINAPI K32WOWGetVDMPointer( DWORD vp, DWORD dwBytes, BOOL fProtectedMode )
347 348 349 350
{
    /* FIXME: add size check too */

    if ( fProtectedMode )
351
        return MapSL( vp );
352 353 354 355 356
    else
        return DOSMEM_MapRealToLinear( vp );
}

/**********************************************************************
357
 *           K32WOWGetVDMPointerFix     (KERNEL32.68)
358
 */
359
LPVOID WINAPI K32WOWGetVDMPointerFix( DWORD vp, DWORD dwBytes, BOOL fProtectedMode )
360
{
361
    /*
362 363 364 365 366
     * Hmmm. According to the docu, we should call:
     *
     *          GlobalFix16( SELECTOROF(vp) );
     *
     * But this is unnecessary under Wine, as we never move global
367
     * memory segments in linear memory anyway.
368
     *
369
     * (I'm not so sure what we are *supposed* to do if
370 371 372
     *  fProtectedMode is TRUE, anyway ...)
     */

373
    return K32WOWGetVDMPointer( vp, dwBytes, fProtectedMode );
374 375 376
}

/**********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
377
 *           K32WOWGetVDMPointerUnfix   (KERNEL32.69)
378
 */
379
VOID WINAPI K32WOWGetVDMPointerUnfix( DWORD vp )
380 381 382 383 384 385 386 387 388 389
{
    /*
     * See above why we don't call:
     *
     * GlobalUnfix16( SELECTOROF(vp) );
     *
     */
}

/**********************************************************************
390
 *           K32WOWGlobalAlloc16        (KERNEL32.59)
391
 */
392
WORD WINAPI K32WOWGlobalAlloc16( WORD wFlags, DWORD cb )
393 394 395 396 397
{
    return (WORD)GlobalAlloc16( wFlags, cb );
}

/**********************************************************************
398
 *           K32WOWGlobalFree16         (KERNEL32.62)
399
 */
400
WORD WINAPI K32WOWGlobalFree16( WORD hMem )
401 402 403 404 405
{
    return (WORD)GlobalFree16( (HGLOBAL16)hMem );
}

/**********************************************************************
406
 *           K32WOWGlobalUnlock16       (KERNEL32.61)
407
 */
408
BOOL WINAPI K32WOWGlobalUnlock16( WORD hMem )
409 410 411 412 413
{
    return (BOOL)GlobalUnlock16( (HGLOBAL16)hMem );
}

/**********************************************************************
414
 *           K32WOWGlobalAllocLock16    (KERNEL32.63)
415
 */
416
DWORD WINAPI K32WOWGlobalAllocLock16( WORD wFlags, DWORD cb, WORD *phMem )
417
{
418
    WORD hMem = K32WOWGlobalAlloc16( wFlags, cb );
419 420
    if (phMem) *phMem = hMem;

421
    return K32WOWGlobalLock16( hMem );
422 423 424
}

/**********************************************************************
425
 *           K32WOWGlobalLockSize16     (KERNEL32.65)
426
 */
427
DWORD WINAPI K32WOWGlobalLockSize16( WORD hMem, PDWORD pcb )
428
{
429
    if ( pcb )
430 431
        *pcb = GlobalSize16( (HGLOBAL16)hMem );

432
    return K32WOWGlobalLock16( hMem );
433 434 435
}

/**********************************************************************
436
 *           K32WOWGlobalUnlockFree16   (KERNEL32.64)
437
 */
438
WORD WINAPI K32WOWGlobalUnlockFree16( DWORD vpMem )
439
{
440
    if ( !K32WOWGlobalUnlock16( HIWORD(vpMem) ) )
441 442
        return FALSE;

443
    return K32WOWGlobalFree16( HIWORD(vpMem) );
444 445 446 447
}


/**********************************************************************
448
 *           K32WOWYield16              (KERNEL32.66)
449
 */
450
VOID WINAPI K32WOWYield16( void )
451 452
{
    /*
453
     * This does the right thing for both Win16 and Win32 tasks.
454 455 456 457 458 459
     * More or less, at least :-/
     */
    Yield16();
}

/**********************************************************************
460
 *           K32WOWDirectedYield16       (KERNEL32.67)
461
 */
462
VOID WINAPI K32WOWDirectedYield16( WORD htask16 )
463 464 465
{
    /*
     * Argh.  Our scheduler doesn't like DirectedYield by Win32
466
     * tasks at all.  So we do hope that this routine is indeed
467 468 469 470 471 472 473
     * only ever called by Win16 tasks that have thunked up ...
     */
    DirectedYield16( (HTASK16)htask16 );
}


/***********************************************************************
474
 *           K32WOWHandle32              (KERNEL32.57)
475
 */
476
HANDLE WINAPI K32WOWHandle32( WORD handle, WOW_HANDLE_TYPE type )
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
{
    switch ( type )
    {
    case WOW_TYPE_HWND:
    case WOW_TYPE_HMENU:
    case WOW_TYPE_HDWP:
    case WOW_TYPE_HDROP:
    case WOW_TYPE_HDC:
    case WOW_TYPE_HFONT:
    case WOW_TYPE_HRGN:
    case WOW_TYPE_HBITMAP:
    case WOW_TYPE_HBRUSH:
    case WOW_TYPE_HPALETTE:
    case WOW_TYPE_HPEN:
    case WOW_TYPE_HACCEL:
492 493 494 495
        return (HANDLE)(ULONG_PTR)handle;

    case WOW_TYPE_HMETAFILE:
        FIXME( "conversion of metafile handles not supported yet\n" );
496
        return (HANDLE)(ULONG_PTR)handle;
497

498
    case WOW_TYPE_HTASK:
499
        return ((TDB *)GlobalLock16(handle))->teb->ClientId.UniqueThread;
500

501 502 503 504
    case WOW_TYPE_FULLHWND:
        FIXME( "conversion of full window handles not supported yet\n" );
        return (HANDLE)(ULONG_PTR)handle;

505 506
    default:
        ERR( "handle 0x%04x of unknown type %d\n", handle, type );
507
        return (HANDLE)(ULONG_PTR)handle;
508 509 510 511
    }
}

/***********************************************************************
512
 *           K32WOWHandle16              (KERNEL32.58)
513
 */
514
WORD WINAPI K32WOWHandle16( HANDLE handle, WOW_HANDLE_TYPE type )
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
{
    switch ( type )
    {
    case WOW_TYPE_HWND:
    case WOW_TYPE_HMENU:
    case WOW_TYPE_HDWP:
    case WOW_TYPE_HDROP:
    case WOW_TYPE_HDC:
    case WOW_TYPE_HFONT:
    case WOW_TYPE_HRGN:
    case WOW_TYPE_HBITMAP:
    case WOW_TYPE_HBRUSH:
    case WOW_TYPE_HPALETTE:
    case WOW_TYPE_HPEN:
    case WOW_TYPE_HACCEL:
    case WOW_TYPE_FULLHWND:
531 532
    	if ( HIWORD(handle ) )
        	ERR( "handle %p of type %d has non-zero HIWORD\n", handle, type );
533 534
        return LOWORD(handle);

535 536 537 538
    case WOW_TYPE_HMETAFILE:
        FIXME( "conversion of metafile handles not supported yet\n" );
        return LOWORD(handle);

539
    case WOW_TYPE_HTASK:
540
        return TASK_GetTaskFromThread( (DWORD)handle );
541

542
    default:
543
        ERR( "handle %p of unknown type %d\n", handle, type );
544 545 546 547 548
        return LOWORD(handle);
    }
}

/**********************************************************************
549
 *           K32WOWCallback16Ex         (KERNEL32.55)
550
 */
551 552
BOOL WINAPI K32WOWCallback16Ex( DWORD vpfn16, DWORD dwFlags,
                                DWORD cbArgs, LPVOID pArgs, LPDWORD pdwRetCode )
553
{
554
#ifdef __i386__
555 556 557
    /*
     * Arguments must be prepared in the correct order by the caller
     * (both for PASCAL and CDECL calling convention), so we simply
558
     * copy them to the 16-bit stack ...
559
     */
560
    char *stack = (char *)CURRENT_STACK16 - cbArgs;
561 562

    memcpy( stack, pArgs, cbArgs );
563

564 565 566 567 568 569 570
    if (dwFlags & (WCB16_REGS|WCB16_REGS_LONG))
    {
        CONTEXT *context = (CONTEXT *)pdwRetCode;

        if (TRACE_ON(relay))
        {
            DWORD count = cbArgs / sizeof(WORD);
571
            WORD * wstack = (WORD *)stack;
572

573
            DPRINTF("%04x:CallTo16(func=%04x:%04x,ds=%04x",
574 575
                    GetCurrentThreadId(),
                    context->SegCs, LOWORD(context->Eip), context->SegDs );
576
            while (count) DPRINTF( ",%04x", wstack[--count] );
577
            DPRINTF(") ss:sp=%04x:%04x",
578
                    SELECTOROF(NtCurrentTeb()->WOW32Reserved), OFFSETOF(NtCurrentTeb()->WOW32Reserved) );
579 580 581 582 583 584 585
            DPRINTF(" ax=%04x bx=%04x cx=%04x dx=%04x si=%04x di=%04x bp=%04x es=%04x fs=%04x\n",
                    (WORD)context->Eax, (WORD)context->Ebx, (WORD)context->Ecx,
                    (WORD)context->Edx, (WORD)context->Esi, (WORD)context->Edi,
                    (WORD)context->Ebp, (WORD)context->SegEs, (WORD)context->SegFs );
            SYSLEVEL_CheckNotLevel( 2 );
        }

586
        if (context->EFlags & 0x00020000)  /* v86 mode */
587
        {
588 589
            EXCEPTION_REGISTRATION_RECORD frame;
            frame.Handler = vm86_handler;
590
            errno = 0;
591 592 593
            __wine_push_frame( &frame );
            __wine_enter_vm86( context );
            __wine_pop_frame( &frame );
594 595
            if (errno != 0)  /* enter_vm86 will fall with ENOSYS on x64 kernels */
            {
596
                WARN("__wine_enter_vm86 failed (errno=%d)\n", errno);
597 598 599 600 601 602
                if (errno == ENOSYS)
                    SetLastError(ERROR_NOT_SUPPORTED);
                else
                    SetLastError(ERROR_GEN_FAILURE);
                return FALSE;
            }
603
        }
604
        else
605
        {
606 607 608
            /* push return address */
            if (dwFlags & WCB16_REGS_LONG)
            {
609 610 611 612
                stack -= sizeof(DWORD);
                *((DWORD *)stack) = HIWORD(call16_ret_addr);
                stack -= sizeof(DWORD);
                *((DWORD *)stack) = LOWORD(call16_ret_addr);
613 614 615 616
                cbArgs += 2 * sizeof(DWORD);
            }
            else
            {
617 618
                stack -= sizeof(SEGPTR);
                *((SEGPTR *)stack) = call16_ret_addr;
619 620
                cbArgs += sizeof(SEGPTR);
            }
621

622 623 624 625 626
            /*
             * Start call by checking for pending events.
             * Note that wine_call_to_16_regs overwrites context stack
             * pointer so we may modify it here without a problem.
             */
627
            if (get_vm86_teb_info()->dpmi_vif)
628 629 630 631 632 633 634
            {
                context->SegSs = wine_get_ds();
                context->Esp   = (DWORD)stack;
                insert_event_check( context );
                cbArgs += (DWORD)stack - context->Esp;
            }

635 636 637 638
            _EnterWin16Lock();
            wine_call_to_16_regs( context, cbArgs, call16_handler );
            _LeaveWin16Lock();
        }
639 640 641

        if (TRACE_ON(relay))
        {
642
            DPRINTF("%04x:RetFrom16() ss:sp=%04x:%04x ",
643 644
                    GetCurrentThreadId(), SELECTOROF(NtCurrentTeb()->WOW32Reserved),
                    OFFSETOF(NtCurrentTeb()->WOW32Reserved));
645 646 647 648 649 650 651 652 653 654 655 656 657
            DPRINTF(" ax=%04x bx=%04x cx=%04x dx=%04x bp=%04x sp=%04x\n",
                    (WORD)context->Eax, (WORD)context->Ebx, (WORD)context->Ecx,
                    (WORD)context->Edx, (WORD)context->Ebp, (WORD)context->Esp );
            SYSLEVEL_CheckNotLevel( 2 );
        }
    }
    else
    {
        DWORD ret;

        if (TRACE_ON(relay))
        {
            DWORD count = cbArgs / sizeof(WORD);
658
            WORD * wstack = (WORD *)stack;
659

660
            DPRINTF("%04x:CallTo16(func=%04x:%04x,ds=%04x",
661
                    GetCurrentThreadId(), HIWORD(vpfn16), LOWORD(vpfn16),
662
                    SELECTOROF(NtCurrentTeb()->WOW32Reserved) );
663
            while (count) DPRINTF( ",%04x", wstack[--count] );
664
            DPRINTF(") ss:sp=%04x:%04x\n",
665
                    SELECTOROF(NtCurrentTeb()->WOW32Reserved), OFFSETOF(NtCurrentTeb()->WOW32Reserved) );
666 667 668
            SYSLEVEL_CheckNotLevel( 2 );
        }

669
        /* push return address */
670 671
        stack -= sizeof(SEGPTR);
        *((SEGPTR *)stack) = call16_ret_addr;
672 673
        cbArgs += sizeof(SEGPTR);

674 675 676 677 678 679 680
        /*
         * Actually, we should take care whether the called routine cleans up
         * its stack or not.  Fortunately, our wine_call_to_16 core doesn't rely on
         * the callee to do so; after the routine has returned, the 16-bit
         * stack pointer is always reset to the position it had before.
         */
        _EnterWin16Lock();
681
        ret = wine_call_to_16( (FARPROC16)vpfn16, cbArgs, call16_handler );
682 683 684 685 686
        if (pdwRetCode) *pdwRetCode = ret;
        _LeaveWin16Lock();

        if (TRACE_ON(relay))
        {
687
            DPRINTF("%04x:RetFrom16() ss:sp=%04x:%04x retval=%08x\n",
688 689
                    GetCurrentThreadId(), SELECTOROF(NtCurrentTeb()->WOW32Reserved),
                    OFFSETOF(NtCurrentTeb()->WOW32Reserved), ret);
690 691 692 693 694 695
            SYSLEVEL_CheckNotLevel( 2 );
        }
    }
#else
    assert(0);  /* cannot call to 16-bit on non-Intel architectures */
#endif  /* __i386__ */
696 697 698 699

    return TRUE;  /* success */
}

700
/**********************************************************************
701
 *           K32WOWCallback16            (KERNEL32.54)
702 703 704 705 706
 */
DWORD WINAPI K32WOWCallback16( DWORD vpfn16, DWORD dwParam )
{
    DWORD ret;

707
    if ( !K32WOWCallback16Ex( vpfn16, WCB16_PASCAL,
708 709 710 711 712
                           sizeof(DWORD), &dwParam, &ret ) )
        ret = 0L;

    return ret;
}
713 714 715 716 717 718 719


/*
 *  16-bit WOW routines (in KERNEL)
 */

/**********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
720
 *           GetVDMPointer32W      (KERNEL.516)
721 722 723
 */
DWORD WINAPI GetVDMPointer32W16( SEGPTR vp, UINT16 fMode )
{
724
    GlobalPageLock16(GlobalHandle16(SELECTOROF(vp)));
725
    return (DWORD)K32WOWGetVDMPointer( vp, 0, (DWORD)fMode );
726 727 728
}

/***********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
729
 *           LoadLibraryEx32W      (KERNEL.513)
730 731 732 733
 */
DWORD WINAPI LoadLibraryEx32W16( LPCSTR lpszLibFile, DWORD hFile, DWORD dwFlags )
{
    HMODULE hModule;
734
    DWORD mutex_count;
735 736
    OFSTRUCT ofs;
    const char *p;
737 738 739 740 741 742 743

    if (!lpszLibFile)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return 0;
    }

744
    /* if the file cannot be found, call LoadLibraryExA anyway, since it might be
745
       a builtin module. This case is handled in MODULE_LoadLibraryExA */
746

747 748 749 750 751 752 753 754 755 756 757 758 759
    if ((p = strrchr( lpszLibFile, '.' )) && !strchr( p, '\\' ))  /* got an extension */
    {
        if (OpenFile16( lpszLibFile, &ofs, OF_EXIST ) != HFILE_ERROR16)
            lpszLibFile = ofs.szPathName;
    }
    else
    {
        char buffer[MAX_PATH+4];
        strcpy( buffer, lpszLibFile );
        strcat( buffer, ".dll" );
        if (OpenFile16( buffer, &ofs, OF_EXIST ) != HFILE_ERROR16)
            lpszLibFile = ofs.szPathName;
    }
760

761
    ReleaseThunkLock( &mutex_count );
762
    hModule = LoadLibraryExA( lpszLibFile, (HANDLE)hFile, dwFlags );
763
    RestoreThunkLock( mutex_count );
764

765 766 767 768
    return (DWORD)hModule;
}

/***********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
769
 *           GetProcAddress32W     (KERNEL.515)
770 771 772 773 774 775 776
 */
DWORD WINAPI GetProcAddress32W16( DWORD hModule, LPCSTR lpszProc )
{
    return (DWORD)GetProcAddress( (HMODULE)hModule, lpszProc );
}

/***********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
777
 *           FreeLibrary32W        (KERNEL.514)
778 779 780 781
 */
DWORD WINAPI FreeLibrary32W16( DWORD hLibModule )
{
    BOOL retv;
782
    DWORD mutex_count;
783

784
    ReleaseThunkLock( &mutex_count );
785
    retv = FreeLibrary( (HMODULE)hLibModule );
786
    RestoreThunkLock( mutex_count );
787 788 789 790 791 792 793
    return (DWORD)retv;
}


/**********************************************************************
 *           WOW_CallProc32W
 */
794
static DWORD WOW_CallProc32W16( FARPROC proc32, DWORD nrofargs, DWORD *args )
795
{
796
    DWORD ret;
797
    DWORD mutex_count;
798

799
    ReleaseThunkLock( &mutex_count );
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832

    /*
     * FIXME:  If ( nrofargs & CPEX_DEST_CDECL ) != 0, we should call a
     *         32-bit CDECL routine ...
     */

    if (!proc32) ret = 0;
    else switch (nrofargs)
    {
    case 0: ret = proc32();
            break;
    case 1: ret = proc32(args[0]);
            break;
    case 2: ret = proc32(args[0],args[1]);
            break;
    case 3: ret = proc32(args[0],args[1],args[2]);
            break;
    case 4: ret = proc32(args[0],args[1],args[2],args[3]);
            break;
    case 5: ret = proc32(args[0],args[1],args[2],args[3],args[4]);
            break;
    case 6: ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5]);
            break;
    case 7: ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6]);
            break;
    case 8: ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7]);
            break;
    case 9: ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8]);
            break;
    case 10:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9]);
            break;
    case 11:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10]);
            break;
833 834 835 836
    case 12:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11]);
            break;
    case 13:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11],args[12]);
            break;
837 838 839 840
    case 14:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11],args[12],args[13]);
            break;
    case 15:ret = proc32(args[0],args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],args[10],args[11],args[12],args[13],args[14]);
            break;
841 842
    default:
            /* FIXME: should go up to 32  arguments */
843
            ERR("Unsupported number of arguments %d, please report.\n",nrofargs);
844 845 846
            ret = 0;
            break;
    }
847

848
    RestoreThunkLock( mutex_count );
849

850
    TRACE("returns %08x\n",ret);
851 852 853 854
    return ret;
}

/**********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
855
 *           CallProc32W           (KERNEL.517)
856
 */
857
DWORD WINAPIV CallProc32W16( DWORD nrofargs, DWORD argconvmask, FARPROC proc32, VA_LIST16 valist )
858
{
859 860 861
    DWORD args[32];
    unsigned int i;

862
    TRACE("(%d,%d,%p args[",nrofargs,argconvmask,proc32);
863 864 865 866 867 868 869 870

    for (i=0;i<nrofargs;i++)
    {
        if (argconvmask & (1<<i))
        {
            SEGPTR ptr = VA_ARG16( valist, SEGPTR );
            /* pascal convention, have to reverse the arguments order */
            args[nrofargs - i - 1] = (DWORD)MapSL(ptr);
871
            TRACE("%08x(%p),",ptr,MapSL(ptr));
872 873 874 875 876 877
        }
        else
        {
            DWORD arg = VA_ARG16( valist, DWORD );
            /* pascal convention, have to reverse the arguments order */
            args[nrofargs - i - 1] = arg;
878
            TRACE("%d,", arg);
879 880 881 882 883 884 885 886
        }
    }
    TRACE("])\n");

    /* POP nrofargs DWORD arguments and 3 DWORD parameters */
    stack16_pop( (3 + nrofargs) * sizeof(DWORD) );

    return WOW_CallProc32W16( proc32, nrofargs, args );
887 888 889
}

/**********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
890
 *           _CallProcEx32W         (KERNEL.518)
891
 */
892
DWORD WINAPIV CallProcEx32W16( DWORD nrofargs, DWORD argconvmask, FARPROC proc32, VA_LIST16 valist )
893
{
894 895 896
    DWORD args[32];
    unsigned int i;

897
    TRACE("(%d,%d,%p args[",nrofargs,argconvmask,proc32);
898 899 900 901 902 903 904

    for (i=0;i<nrofargs;i++)
    {
        if (argconvmask & (1<<i))
        {
            SEGPTR ptr = VA_ARG16( valist, SEGPTR );
            args[i] = (DWORD)MapSL(ptr);
905
            TRACE("%08x(%p),",ptr,MapSL(ptr));
906 907 908 909 910
        }
        else
        {
            DWORD arg = VA_ARG16( valist, DWORD );
            args[i] = arg;
911
            TRACE("%d,", arg);
912 913 914 915
        }
    }
    TRACE("])\n");
    return WOW_CallProc32W16( proc32, nrofargs, args );
916 917 918 919
}


/**********************************************************************
920
 *           WOW16Call               (KERNEL.500)
921 922 923 924
 *
 * FIXME!!!
 *
 */
925
DWORD WINAPIV WOW16Call(WORD x, WORD y, WORD z, VA_LIST16 args)
926 927 928 929 930 931 932 933 934 935
{
        int     i;
        DWORD   calladdr;
        FIXME("(0x%04x,0x%04x,%d),calling (",x,y,z);

        for (i=0;i<x/2;i++) {
                WORD    a = VA_ARG16(args,WORD);
                DPRINTF("%04x ",a);
        }
        calladdr = VA_ARG16(args,DWORD);
936
        stack16_pop( 3*sizeof(WORD) + x + sizeof(DWORD) );
937
        DPRINTF(") calling address was 0x%08x\n",calladdr);
938 939
        return 0;
}