wowthunk.c 28.7 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

27
#include "wine/winbase16.h"
28
#include "windef.h"
29
#include "winbase.h"
30
#include "winerror.h"
31
#include "wownt32.h"
32
#include "excpt.h"
33
#include "thread.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 = INSTR_EmulateInstruction( record, context );
260

261 262 263 264 265 266 267 268 269
            /*
             * 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.
             */
            if(NtCurrentTeb()->dpmi_vif)
                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 313 314 315 316 317 318 319

/*************************************************************
 *            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)
    {
        return INSTR_EmulateInstruction( record, context );
    }

    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 590 591 592
            EXCEPTION_REGISTRATION_RECORD frame;
            frame.Handler = vm86_handler;
            __wine_push_frame( &frame );
            __wine_enter_vm86( context );
            __wine_pop_frame( &frame );
593
        }
594
        else
595
        {
596 597 598
            /* push return address */
            if (dwFlags & WCB16_REGS_LONG)
            {
599 600 601 602
                stack -= sizeof(DWORD);
                *((DWORD *)stack) = HIWORD(call16_ret_addr);
                stack -= sizeof(DWORD);
                *((DWORD *)stack) = LOWORD(call16_ret_addr);
603 604 605 606
                cbArgs += 2 * sizeof(DWORD);
            }
            else
            {
607 608
                stack -= sizeof(SEGPTR);
                *((SEGPTR *)stack) = call16_ret_addr;
609 610
                cbArgs += sizeof(SEGPTR);
            }
611

612 613 614 615 616 617 618 619 620 621 622 623 624
            /*
             * 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.
             */
            if (NtCurrentTeb()->dpmi_vif)
            {
                context->SegSs = wine_get_ds();
                context->Esp   = (DWORD)stack;
                insert_event_check( context );
                cbArgs += (DWORD)stack - context->Esp;
            }

625 626 627 628
            _EnterWin16Lock();
            wine_call_to_16_regs( context, cbArgs, call16_handler );
            _LeaveWin16Lock();
        }
629 630 631

        if (TRACE_ON(relay))
        {
632
            DPRINTF("%04x:RetFrom16() ss:sp=%04x:%04x ",
633 634
                    GetCurrentThreadId(), SELECTOROF(NtCurrentTeb()->WOW32Reserved),
                    OFFSETOF(NtCurrentTeb()->WOW32Reserved));
635 636 637 638 639 640 641 642 643 644 645 646 647
            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);
648
            WORD * wstack = (WORD *)stack;
649

650
            DPRINTF("%04x:CallTo16(func=%04x:%04x,ds=%04x",
651
                    GetCurrentThreadId(), HIWORD(vpfn16), LOWORD(vpfn16),
652
                    SELECTOROF(NtCurrentTeb()->WOW32Reserved) );
653
            while (count) DPRINTF( ",%04x", wstack[--count] );
654
            DPRINTF(") ss:sp=%04x:%04x\n",
655
                    SELECTOROF(NtCurrentTeb()->WOW32Reserved), OFFSETOF(NtCurrentTeb()->WOW32Reserved) );
656 657 658
            SYSLEVEL_CheckNotLevel( 2 );
        }

659
        /* push return address */
660 661
        stack -= sizeof(SEGPTR);
        *((SEGPTR *)stack) = call16_ret_addr;
662 663
        cbArgs += sizeof(SEGPTR);

664 665 666 667 668 669 670
        /*
         * 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();
671
        ret = wine_call_to_16( (FARPROC16)vpfn16, cbArgs, call16_handler );
672 673 674 675 676
        if (pdwRetCode) *pdwRetCode = ret;
        _LeaveWin16Lock();

        if (TRACE_ON(relay))
        {
677
            DPRINTF("%04x:RetFrom16() ss:sp=%04x:%04x retval=%08x\n",
678 679
                    GetCurrentThreadId(), SELECTOROF(NtCurrentTeb()->WOW32Reserved),
                    OFFSETOF(NtCurrentTeb()->WOW32Reserved), ret);
680 681 682 683 684 685
            SYSLEVEL_CheckNotLevel( 2 );
        }
    }
#else
    assert(0);  /* cannot call to 16-bit on non-Intel architectures */
#endif  /* __i386__ */
686 687 688 689

    return TRUE;  /* success */
}

690
/**********************************************************************
691
 *           K32WOWCallback16            (KERNEL32.54)
692 693 694 695 696
 */
DWORD WINAPI K32WOWCallback16( DWORD vpfn16, DWORD dwParam )
{
    DWORD ret;

697
    if ( !K32WOWCallback16Ex( vpfn16, WCB16_PASCAL,
698 699 700 701 702
                           sizeof(DWORD), &dwParam, &ret ) )
        ret = 0L;

    return ret;
}
703 704 705 706 707 708 709


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

/**********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
710
 *           GetVDMPointer32W      (KERNEL.516)
711 712 713
 */
DWORD WINAPI GetVDMPointer32W16( SEGPTR vp, UINT16 fMode )
{
714
    GlobalPageLock16(GlobalHandle16(SELECTOROF(vp)));
715
    return (DWORD)K32WOWGetVDMPointer( vp, 0, (DWORD)fMode );
716 717 718
}

/***********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
719
 *           LoadLibraryEx32W      (KERNEL.513)
720 721 722 723
 */
DWORD WINAPI LoadLibraryEx32W16( LPCSTR lpszLibFile, DWORD hFile, DWORD dwFlags )
{
    HMODULE hModule;
724
    DWORD mutex_count;
725 726
    OFSTRUCT ofs;
    const char *p;
727 728 729 730 731 732 733

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

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

737 738 739 740 741 742 743 744 745 746 747 748 749
    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;
    }
750

751
    ReleaseThunkLock( &mutex_count );
752
    hModule = LoadLibraryExA( lpszLibFile, (HANDLE)hFile, dwFlags );
753
    RestoreThunkLock( mutex_count );
754

755 756 757 758
    return (DWORD)hModule;
}

/***********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
759
 *           GetProcAddress32W     (KERNEL.515)
760 761 762 763 764 765 766
 */
DWORD WINAPI GetProcAddress32W16( DWORD hModule, LPCSTR lpszProc )
{
    return (DWORD)GetProcAddress( (HMODULE)hModule, lpszProc );
}

/***********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
767
 *           FreeLibrary32W        (KERNEL.514)
768 769 770 771
 */
DWORD WINAPI FreeLibrary32W16( DWORD hLibModule )
{
    BOOL retv;
772
    DWORD mutex_count;
773

774
    ReleaseThunkLock( &mutex_count );
775
    retv = FreeLibrary( (HMODULE)hLibModule );
776
    RestoreThunkLock( mutex_count );
777 778 779 780 781 782 783
    return (DWORD)retv;
}


/**********************************************************************
 *           WOW_CallProc32W
 */
784
static DWORD WOW_CallProc32W16( FARPROC proc32, DWORD nrofargs, DWORD *args )
785
{
786
    DWORD ret;
787
    DWORD mutex_count;
788

789
    ReleaseThunkLock( &mutex_count );
790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822

    /*
     * 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;
823 824 825 826
    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;
827 828 829 830
    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;
831 832
    default:
            /* FIXME: should go up to 32  arguments */
833
            ERR("Unsupported number of arguments %d, please report.\n",nrofargs);
834 835 836
            ret = 0;
            break;
    }
837

838
    RestoreThunkLock( mutex_count );
839

840
    TRACE("returns %08x\n",ret);
841 842 843 844
    return ret;
}

/**********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
845
 *           CallProc32W           (KERNEL.517)
846
 */
847
DWORD WINAPIV CallProc32W16( DWORD nrofargs, DWORD argconvmask, FARPROC proc32, VA_LIST16 valist )
848
{
849 850 851
    DWORD args[32];
    unsigned int i;

852
    TRACE("(%d,%d,%p args[",nrofargs,argconvmask,proc32);
853 854 855 856 857 858 859 860

    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);
861
            TRACE("%08x(%p),",ptr,MapSL(ptr));
862 863 864 865 866 867
        }
        else
        {
            DWORD arg = VA_ARG16( valist, DWORD );
            /* pascal convention, have to reverse the arguments order */
            args[nrofargs - i - 1] = arg;
868
            TRACE("%d,", arg);
869 870 871 872 873 874 875 876
        }
    }
    TRACE("])\n");

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

    return WOW_CallProc32W16( proc32, nrofargs, args );
877 878 879
}

/**********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
880
 *           _CallProcEx32W         (KERNEL.518)
881
 */
882
DWORD WINAPIV CallProcEx32W16( DWORD nrofargs, DWORD argconvmask, FARPROC proc32, VA_LIST16 valist )
883
{
884 885 886
    DWORD args[32];
    unsigned int i;

887
    TRACE("(%d,%d,%p args[",nrofargs,argconvmask,proc32);
888 889 890 891 892 893 894

    for (i=0;i<nrofargs;i++)
    {
        if (argconvmask & (1<<i))
        {
            SEGPTR ptr = VA_ARG16( valist, SEGPTR );
            args[i] = (DWORD)MapSL(ptr);
895
            TRACE("%08x(%p),",ptr,MapSL(ptr));
896 897 898 899 900
        }
        else
        {
            DWORD arg = VA_ARG16( valist, DWORD );
            args[i] = arg;
901
            TRACE("%d,", arg);
902 903 904 905
        }
    }
    TRACE("])\n");
    return WOW_CallProc32W16( proc32, nrofargs, args );
906 907 908 909
}


/**********************************************************************
910
 *           WOW16Call               (KERNEL.500)
911 912 913 914
 *
 * FIXME!!!
 *
 */
915
DWORD WINAPIV WOW16Call(WORD x, WORD y, WORD z, VA_LIST16 args)
916 917 918 919 920 921 922 923 924 925
{
        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);
926
        stack16_pop( 3*sizeof(WORD) + x + sizeof(DWORD) );
927
        DPRINTF(") calling address was 0x%08x\n",calladdr);
928 929
        return 0;
}