relay.c 36.6 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * Relay calls helper routines
 *
 * Copyright 1993 Robert J. Amstadt
 * Copyright 1995 Martin von Loewis
 * Copyright 1995, 1996, 1997 Alexandre Julliard
 * Copyright 1997 Eric Youngdale
 * Copyright 1999 Ulrich Weigand
9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * 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
22
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 24
 */

25
#include "config.h"
26
#include "wine/port.h"
27

28
#include <ctype.h>
29
#include <stdarg.h>
30 31 32

#include "build.h"

33
/* offset of the stack pointer relative to %fs:(0) */
34
#define STACKOFFSET 0xc0  /* FIELD_OFFSET(TEB,WOW32Reserved) */
35

36
/* fix this if the ntdll_thread_regs structure is changed */
37 38 39 40
#define GS_OFFSET  0x1d8  /* FIELD_OFFSET(TEB,SystemReserved2) + FIELD_OFFSET(ntdll_thread_data,gs) */

#define DPMI_VIF_OFFSET      (0x1fc + 0) /* FIELD_OFFSET(TEB,GdiTebBatch) + FIELD_OFFSET(WINE_VM86_TEB_INFO,dpmi_vif) */
#define VM86_PENDING_OFFSET  (0x1fc + 4) /* FIELD_OFFSET(TEB,GdiTebBatch) + FIELD_OFFSET(WINE_VM86_TEB_INFO,vm86_pending) */
41

42
static void function_header( const char *name )
43
{
44 45 46
    output( "\n\t.align %d\n", get_alignment(4) );
    output( "\t%s\n", func_declaration(name) );
    output( "%s\n", asm_globl(name) );
47 48 49
}


50 51 52 53 54 55 56 57
/*******************************************************************
 *         BuildCallFrom16Core
 *
 * This routine builds the core routines used in 16->32 thunks:
 * CallFrom16Word, CallFrom16Long, CallFrom16Register, and CallFrom16Thunk.
 *
 * These routines are intended to be called via a far call (with 32-bit
 * operand size) from 16-bit code.  The 16-bit code stub must push %bp,
58 59
 * the 32-bit entry point to be called, and the argument conversion
 * routine to be used (see stack layout below).
60 61
 *
 * The core routine completes the STACK16FRAME on the 16-bit stack and
62 63 64 65
 * switches to the 32-bit stack.  Then, the argument conversion routine
 * is called; it gets passed the 32-bit entry point and a pointer to the
 * 16-bit arguments (on the 16-bit stack) as parameters. (You can either
 * use conversion routines automatically generated by BuildCallFrom16,
66
 * or write your own for special purposes.)
67
 *
68
 * The conversion routine must call the 32-bit entry point, passing it
69
 * the converted arguments, and return its return value to the core.
70 71 72 73 74 75 76 77 78 79
 * After the conversion routine has returned, the core switches back
 * to the 16-bit stack, converts the return value to the DX:AX format
 * (CallFrom16Long), and returns to the 16-bit call stub.  All parameters,
 * including %bp, are popped off the stack.
 *
 * The 16-bit call stub now returns to the caller, popping the 16-bit
 * arguments if necessary (pascal calling convention).
 *
 * In the case of a 'register' function, CallFrom16Register fills a
 * CONTEXT86 structure with the values all registers had at the point
80 81
 * the first instruction of the 16-bit call stub was about to be
 * executed.  A pointer to this CONTEXT86 is passed as third parameter
82 83 84
 * to the argument conversion routine, which typically passes it on
 * to the called 32-bit entry point.
 *
85 86
 * CallFrom16Thunk is a special variant used by the implementation of
 * the Win95 16->32 thunk functions C16ThkSL and C16ThkSL01 and is
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
 * implemented as follows:
 * On entry, the EBX register is set up to contain a flat pointer to the
 * 16-bit stack such that EBX+22 points to the first argument.
 * Then, the entry point is called, while EBP is set up to point
 * to the return address (on the 32-bit stack).
 * The called function returns with CX set to the number of bytes
 * to be popped of the caller's stack.
 *
 * Stack layout upon entry to the core routine (STACK16FRAME):
 *  ...           ...
 * (sp+24) word   first 16-bit arg
 * (sp+22) word   cs
 * (sp+20) word   ip
 * (sp+18) word   bp
 * (sp+14) long   32-bit entry point (reused for Win16 mutex recursion count)
 * (sp+12) word   ip of actual entry point (necessary for relay debugging)
 * (sp+8)  long   relay (argument conversion) function entry point
 * (sp+4)  long   cs of 16-bit entry point
 * (sp)    long   ip of 16-bit entry point
 *
 * Added on the stack:
 * (sp-2)  word   saved gs
 * (sp-4)  word   saved fs
 * (sp-6)  word   saved es
 * (sp-8)  word   saved ds
 * (sp-12) long   saved ebp
 * (sp-16) long   saved ecx
 * (sp-20) long   saved edx
 * (sp-24) long   saved previous stack
 */
117
static void BuildCallFrom16Core( int reg_func, int thunk )
118 119
{
    /* Function header */
120 121 122
    if (thunk) function_header( "__wine_call_from_16_thunk" );
    else if (reg_func) function_header( "__wine_call_from_16_regs" );
    else function_header( "__wine_call_from_16" );
123 124

    /* Create STACK16FRAME (except STACK32FRAME link) */
125 126 127 128 129 130 131
    output( "\tpushw %%gs\n" );
    output( "\tpushw %%fs\n" );
    output( "\tpushw %%es\n" );
    output( "\tpushw %%ds\n" );
    output( "\tpushl %%ebp\n" );
    output( "\tpushl %%ecx\n" );
    output( "\tpushl %%edx\n" );
132 133

    /* Save original EFlags register */
134
    if (reg_func) output( "\tpushfl\n" );
135 136 137

    if ( UsePIC )
    {
138 139 140
        output( "\tcall 1f\n" );
        output( "1:\tpopl %%ecx\n" );
        output( "\t.byte 0x2e\n\tmovl %s-1b(%%ecx),%%edx\n", asm_name("CallTo16_DataSelector") );
141 142
    }
    else
143
        output( "\t.byte 0x2e\n\tmovl %s,%%edx\n", asm_name("CallTo16_DataSelector") );
144

145
    /* Load 32-bit segment registers */
146 147
    output( "\tmovw %%dx, %%ds\n" );
    output( "\tmovw %%dx, %%es\n" );
148 149

    if ( UsePIC )
150
        output( "\tmovw %s-1b(%%ecx), %%fs\n", asm_name("CallTo16_TebSelector") );
151
    else
152
        output( "\tmovw %s, %%fs\n", asm_name("CallTo16_TebSelector") );
153

154
    output( "\t.byte 0x64\n\tmov (%d),%%gs\n", GS_OFFSET );
155

156
    /* Translate STACK16FRAME base to flat offset in %edx */
157 158 159
    output( "\tmovw %%ss, %%dx\n" );
    output( "\tandl $0xfff8, %%edx\n" );
    output( "\tshrl $1, %%edx\n" );
160 161
    if (UsePIC)
    {
162 163
        output( "\taddl wine_ldt_copy_ptr-1b(%%ecx),%%edx\n" );
        output( "\tmovl (%%edx), %%edx\n" );
164 165
    }
    else
166 167 168
        output( "\tmovl %s(%%edx), %%edx\n", asm_name("wine_ldt_copy") );
    output( "\tmovzwl %%sp, %%ebp\n" );
    output( "\tleal %d(%%ebp,%%edx), %%edx\n", reg_func ? 0 : -4 );
169 170

    /* Get saved flags into %ecx */
171
    if (reg_func) output( "\tpopl %%ecx\n" );
172 173

    /* Get the 32-bit stack pointer from the TEB and complete STACK16FRAME */
174 175
    output( "\t.byte 0x64\n\tmovl (%d), %%ebp\n", STACKOFFSET );
    output( "\tpushl %%ebp\n" );
176 177

    /* Switch stacks */
178 179 180 181 182
    output( "\t.byte 0x64\n\tmovw %%ss, (%d)\n", STACKOFFSET + 2 );
    output( "\t.byte 0x64\n\tmovw %%sp, (%d)\n", STACKOFFSET );
    output( "\tpushl %%ds\n" );
    output( "\tpopl %%ss\n" );
    output( "\tmovl %%ebp, %%esp\n" );
183
    output( "\taddl $0x20,%%ebp\n");  /* FIELD_OFFSET(STACK32FRAME,ebp) */
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199


    /* At this point:
       STACK16FRAME is completely set up
       DS, ES, SS: flat data segment
       FS: current TEB
       ESP: points to last STACK32FRAME
       EBP: points to ebp member of last STACK32FRAME
       EDX: points to current STACK16FRAME
       ECX: contains saved flags
       all other registers: unchanged */

    /* Special case: C16ThkSL stub */
    if ( thunk )
    {
        /* Set up registers as expected and call thunk */
200
        output( "\tleal 0x1a(%%edx),%%ebx\n" );  /* sizeof(STACK16FRAME)-22 */
201
        output( "\tleal -4(%%esp), %%ebp\n" );
202

203
        output( "\tcall *0x26(%%edx)\n");  /* FIELD_OFFSET(STACK16FRAME,entry_point) */
204 205

        /* Switch stack back */
206 207 208
        output( "\t.byte 0x64\n\tmovw (%d), %%ss\n", STACKOFFSET+2 );
        output( "\t.byte 0x64\n\tmovzwl (%d), %%esp\n", STACKOFFSET );
        output( "\t.byte 0x64\n\tpopl (%d)\n", STACKOFFSET );
209 210

        /* Restore registers and return directly to caller */
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
        output( "\taddl $8, %%esp\n" );
        output( "\tpopl %%ebp\n" );
        output( "\tpopw %%ds\n" );
        output( "\tpopw %%es\n" );
        output( "\tpopw %%fs\n" );
        output( "\tpopw %%gs\n" );
        output( "\taddl $20, %%esp\n" );

        output( "\txorb %%ch, %%ch\n" );
        output( "\tpopl %%ebx\n" );
        output( "\taddw %%cx, %%sp\n" );
        output( "\tpush %%ebx\n" );

        output( "\t.byte 0x66\n" );
        output( "\tlret\n" );
226

227
        output_function_size( "__wine_call_from_16_thunk" );
228 229 230 231 232 233 234
        return;
    }


    /* Build register CONTEXT */
    if ( reg_func )
    {
235
        output( "\tsubl $0x2cc,%%esp\n" );       /* sizeof(CONTEXT86) */
236

237
        output( "\tmovl %%ecx,0xc0(%%esp)\n" );  /* EFlags */
238

239 240 241 242
        output( "\tmovl %%eax,0xb0(%%esp)\n" );  /* Eax */
        output( "\tmovl %%ebx,0xa4(%%esp)\n" );  /* Ebx */
        output( "\tmovl %%esi,0xa0(%%esp)\n" );  /* Esi */
        output( "\tmovl %%edi,0x9c(%%esp)\n" );  /* Edi */
243

244
        output( "\tmovl 0x0c(%%edx),%%eax\n");   /* FIELD_OFFSET(STACK16FRAME,ebp) */
245
        output( "\tmovl %%eax,0xb4(%%esp)\n" );  /* Ebp */
246
        output( "\tmovl 0x08(%%edx),%%eax\n");   /* FIELD_OFFSET(STACK16FRAME,ecx) */
247
        output( "\tmovl %%eax,0xac(%%esp)\n" );  /* Ecx */
248
        output( "\tmovl 0x04(%%edx),%%eax\n");   /* FIELD_OFFSET(STACK16FRAME,edx) */
249
        output( "\tmovl %%eax,0xa8(%%esp)\n" );  /* Edx */
250

251
        output( "\tmovzwl 0x10(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,ds) */
252
        output( "\tmovl %%eax,0x98(%%esp)\n" );  /* SegDs */
253
        output( "\tmovzwl 0x12(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,es) */
254
        output( "\tmovl %%eax,0x94(%%esp)\n" );  /* SegEs */
255
        output( "\tmovzwl 0x14(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,fs) */
256
        output( "\tmovl %%eax,0x90(%%esp)\n" );  /* SegFs */
257
        output( "\tmovzwl 0x16(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,gs) */
258
        output( "\tmovl %%eax,0x8c(%%esp)\n" );  /* SegGs */
259

260
        output( "\tmovzwl 0x2e(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,cs) */
261
        output( "\tmovl %%eax,0xbc(%%esp)\n" );  /* SegCs */
262
        output( "\tmovzwl 0x2c(%%edx),%%eax\n"); /* FIELD_OFFSET(STACK16FRAME,ip) */
263
        output( "\tmovl %%eax,0xb8(%%esp)\n" );  /* Eip */
264 265

        output( "\t.byte 0x64\n\tmovzwl (%d), %%eax\n", STACKOFFSET+2 );
266
        output( "\tmovl %%eax,0xc8(%%esp)\n" );  /* SegSs */
267
        output( "\t.byte 0x64\n\tmovzwl (%d), %%eax\n", STACKOFFSET );
268
        output( "\taddl $0x2c,%%eax\n");         /* FIELD_OFFSET(STACK16FRAME,ip) */
269
        output( "\tmovl %%eax,0xc4(%%esp)\n" );  /* Esp */
270
#if 0
271
        output( "\tfsave 0x1c(%%esp)\n" ); /* FloatSave */
272 273 274
#endif

        /* Push address of CONTEXT86 structure -- popped by the relay routine */
275 276 277 278
        output( "\tmovl %%esp,%%eax\n" );
        output( "\tandl $~15,%%esp\n" );
        output( "\tsubl $4,%%esp\n" );
        output( "\tpushl %%eax\n" );
279 280 281
    }
    else
    {
282 283 284
        output( "\tsubl $8,%%esp\n" );
        output( "\tandl $~15,%%esp\n" );
        output( "\taddl $8,%%esp\n" );
285 286 287
    }

    /* Call relay routine (which will call the API entry point) */
288
    output( "\tleal 0x30(%%edx),%%eax\n" ); /* sizeof(STACK16FRAME) */
289
    output( "\tpushl %%eax\n" );
290 291
    output( "\tpushl 0x26(%%edx)\n");  /* FIELD_OFFSET(STACK16FRAME,entry_point) */
    output( "\tcall *0x20(%%edx)\n");  /* FIELD_OFFSET(STACK16FRAME,relay) */
292 293 294

    if ( reg_func )
    {
295
        output( "\tleal -748(%%ebp),%%ebx\n" ); /* sizeof(CONTEXT) + FIELD_OFFSET(STACK32FRAME,ebp) */
296 297

        /* Switch stack back */
298 299 300
        output( "\t.byte 0x64\n\tmovw (%d), %%ss\n", STACKOFFSET+2 );
        output( "\t.byte 0x64\n\tmovzwl (%d), %%esp\n", STACKOFFSET );
        output( "\t.byte 0x64\n\tpopl (%d)\n", STACKOFFSET );
301 302

        /* Get return address to CallFrom16 stub */
303
        output( "\taddw $0x14,%%sp\n" ); /* FIELD_OFFSET(STACK16FRAME,callfrom_ip)-4 */
304 305
        output( "\tpopl %%eax\n" );
        output( "\tpopl %%edx\n" );
306 307

        /* Restore all registers from CONTEXT */
308 309
        output( "\tmovw 0xc8(%%ebx),%%ss\n");   /* SegSs */
        output( "\tmovl 0xc4(%%ebx),%%esp\n");  /* Esp */
310 311
        output( "\taddl $4, %%esp\n" );  /* room for final return address */

312 313
        output( "\tpushw 0xbc(%%ebx)\n");  /* SegCs */
        output( "\tpushw 0xb8(%%ebx)\n");  /* Eip */
314 315
        output( "\tpushl %%edx\n" );
        output( "\tpushl %%eax\n" );
316 317
        output( "\tpushl 0xc0(%%ebx)\n");  /* EFlags */
        output( "\tpushl 0x98(%%ebx)\n");  /* SegDs */
318

319
        output( "\tpushl 0x94(%%ebx)\n");  /* SegEs */
320
        output( "\tpopl %%es\n" );
321
        output( "\tpushl 0x90(%%ebx)\n");  /* SegFs */
322
        output( "\tpopl %%fs\n" );
323
        output( "\tpushl 0x8c(%%ebx)\n");  /* SegGs */
324 325
        output( "\tpopl %%gs\n" );

326 327 328 329 330 331 332
        output( "\tmovl 0xb4(%%ebx),%%ebp\n");  /* Ebp */
        output( "\tmovl 0xa0(%%ebx),%%esi\n");  /* Esi */
        output( "\tmovl 0x9c(%%ebx),%%edi\n");  /* Edi */
        output( "\tmovl 0xb0(%%ebx),%%eax\n");  /* Eax */
        output( "\tmovl 0xa8(%%ebx),%%edx\n");  /* Edx */
        output( "\tmovl 0xac(%%ebx),%%ecx\n");  /* Ecx */
        output( "\tmovl 0xa4(%%ebx),%%ebx\n");  /* Ebx */
333 334 335 336

        output( "\tpopl %%ds\n" );
        output( "\tpopfl\n" );
        output( "\tlret\n" );
337 338

        output_function_size( "__wine_call_from_16_regs" );
339 340 341 342
    }
    else
    {
        /* Switch stack back */
343 344 345
        output( "\t.byte 0x64\n\tmovw (%d), %%ss\n", STACKOFFSET+2 );
        output( "\t.byte 0x64\n\tmovzwl (%d), %%esp\n", STACKOFFSET );
        output( "\t.byte 0x64\n\tpopl (%d)\n", STACKOFFSET );
346 347

        /* Restore registers */
348 349 350 351 352 353 354
        output( "\tpopl %%edx\n" );
        output( "\tpopl %%ecx\n" );
        output( "\tpopl %%ebp\n" );
        output( "\tpopw %%ds\n" );
        output( "\tpopw %%es\n" );
        output( "\tpopw %%fs\n" );
        output( "\tpopw %%gs\n" );
355 356

        /* Return to return stub which will return to caller */
357
        output( "\tlret $12\n" );
358 359

        output_function_size( "__wine_call_from_16" );
360 361
    }
}
362

363 364 365 366 367 368

/*******************************************************************
 *         BuildCallTo16Core
 *
 * This routine builds the core routines used in 32->16 thunks:
 *
369 370
 * 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 );
371
 *
372
 * These routines can be called directly from 32-bit code.
373
 *
374 375 376
 * All routines expect that the 16-bit stack contents (arguments) and the
 * return address (segptr to CallTo16_Ret) were already set up by the
 * caller; nb_args must contain the number of bytes to be conserved.  The
Austin English's avatar
Austin English committed
377
 * 16-bit SS:SP will be set accordingly.
378
 *
379
 * All other registers are either taken from the CONTEXT86 structure
380 381 382
 * or else set to default values.  The target routine address is either
 * given directly or taken from the CONTEXT86.
 */
383
static void BuildCallTo16Core( int reg_func )
384
{
385
    const char *name = reg_func ? "wine_call_to_16_regs" : "wine_call_to_16";
386 387

    /* Function header */
388
    function_header( name );
389 390

    /* Function entry sequence */
391
    output_cfi( ".cfi_startproc" );
392
    output( "\tpushl %%ebp\n" );
393 394
    output_cfi( ".cfi_adjust_cfa_offset 4" );
    output_cfi( ".cfi_rel_offset %%ebp,0" );
395
    output( "\tmovl %%esp, %%ebp\n" );
396
    output_cfi( ".cfi_def_cfa_register %%ebp" );
397 398

    /* Save the 32-bit registers */
399
    output( "\tpushl %%ebx\n" );
400
    output_cfi( ".cfi_rel_offset %%ebx,-4" );
401
    output( "\tpushl %%esi\n" );
402
    output_cfi( ".cfi_rel_offset %%esi,-8" );
403
    output( "\tpushl %%edi\n" );
404
    output_cfi( ".cfi_rel_offset %%edi,-12" );
405
    output( "\t.byte 0x64\n\tmov %%gs,(%d)\n", GS_OFFSET );
406

407
    /* Setup exception frame */
408 409 410 411
    output( "\t.byte 0x64\n\tpushl (%d)\n", STACKOFFSET );
    output( "\tpushl 16(%%ebp)\n" ); /* handler */
    output( "\t.byte 0x64\n\tpushl (0)\n" );
    output( "\t.byte 0x64\n\tmovl %%esp,(0)\n" );
412

413
    /* Call the actual CallTo16 routine (simulate a lcall) */
414 415
    output( "\tpushl %%cs\n" );
    output( "\tcall .L%s\n", name );
416 417

    /* Remove exception frame */
418 419 420
    output( "\t.byte 0x64\n\tpopl (0)\n" );
    output( "\taddl $4, %%esp\n" );
    output( "\t.byte 0x64\n\tpopl (%d)\n", STACKOFFSET );
421

422
    if ( !reg_func )
423
    {
424
        /* Convert return value */
425 426 427
        output( "\tandl $0xffff,%%eax\n" );
        output( "\tshll $16,%%edx\n" );
        output( "\torl %%edx,%%eax\n" );
428
    }
429
    else
430
    {
431
        /*
432 433 434 435 436 437 438 439
         * Modify CONTEXT86 structure to contain new values
         *
         * NOTE:  We restore only EAX, EBX, EDX, EDX, EBP, and ESP.
         *        The segment registers as well as ESI and EDI should
         *        not be modified by a well-behaved 16-bit routine in
         *        any case.  [If necessary, we could restore them as well,
         *        at the cost of a somewhat less efficient return path.]
         */
440

441
        output( "\tmovl 0x14(%%esp),%%edi\n" ); /* FIELD_OFFSET(STACK32FRAME,target) - FIELD_OFFSET(STACK32FRAME,edi) */
442 443
                /* everything above edi has been popped already */

444 445 446 447 448 449
        output( "\tmovl %%eax,0xb0(%%edi)\n");  /* Eax */
        output( "\tmovl %%ebx,0xa4(%%edi)\n");  /* Ebx */
        output( "\tmovl %%ecx,0xac(%%edi)\n");  /* Ecx */
        output( "\tmovl %%edx,0xa8(%%edi)\n");  /* Edx */
        output( "\tmovl %%ebp,0xb4(%%edi)\n");  /* Ebp */
        output( "\tmovl %%esi,0xc4(%%edi)\n");  /* Esp */
450
                 /* The return glue code saved %esp into %esi */
451 452 453
    }

    /* Restore the 32-bit registers */
454
    output( "\tpopl %%edi\n" );
455
    output_cfi( ".cfi_same_value %%edi" );
456
    output( "\tpopl %%esi\n" );
457
    output_cfi( ".cfi_same_value %%esi" );
458
    output( "\tpopl %%ebx\n" );
459
    output_cfi( ".cfi_same_value %%ebx" );
460 461

    /* Function exit sequence */
462
    output( "\tpopl %%ebp\n" );
463 464
    output_cfi( ".cfi_def_cfa %%esp,4" );
    output_cfi( ".cfi_same_value %%ebp" );
465
    output( "\tret $12\n" );
466
    output_cfi( ".cfi_endproc" );
467 468 469 470


    /* Start of the actual CallTo16 routine */

471
    output( ".L%s:\n", name );
472 473

    /* Switch to the 16-bit stack */
474 475 476 477
    output( "\tmovl %%esp,%%edx\n" );
    output( "\t.byte 0x64\n\tmovw (%d),%%ss\n", STACKOFFSET + 2);
    output( "\t.byte 0x64\n\tmovw (%d),%%sp\n", STACKOFFSET );
    output( "\t.byte 0x64\n\tmovl %%edx,(%d)\n", STACKOFFSET );
478 479

    /* Make %bp point to the previous stackframe (built by CallFrom16) */
480
    output( "\tmovzwl %%sp,%%ebp\n" );
481
    output( "\tleal 0x2a(%%ebp),%%ebp\n");  /* FIELD_OFFSET(STACK16FRAME,bp) */
482 483

    /* Add the specified offset to the new sp */
484
    output( "\tsubw 0x2c(%%edx), %%sp\n");  /* FIELD_OFFSET(STACK32FRAME,nb_args) */
485 486 487 488

    if (reg_func)
    {
        /* Push the called routine address */
489
        output( "\tmovl 0x28(%%edx),%%edx\n");  /* FIELD_OFFSET(STACK32FRAME,target) */
490 491
        output( "\tpushw 0xbc(%%edx)\n");  /* SegCs */
        output( "\tpushw 0xb8(%%edx)\n");  /* Eip */
492 493

        /* Get the registers */
494 495
        output( "\tpushw 0x98(%%edx)\n");  /* SegDs */
        output( "\tpushl 0x94(%%edx)\n");  /* SegEs */
496
        output( "\tpopl %%es\n" );
497
        output( "\tpushl 0x90(%%edx)\n");  /* SegFs */
498
        output( "\tpopl %%fs\n" );
499
        output( "\tpushl 0x8c(%%edx)\n");  /* SegGs */
500
        output( "\tpopl %%gs\n" );
501 502 503 504 505 506 507
        output( "\tmovl 0xb4(%%edx),%%ebp\n");  /* Ebp */
        output( "\tmovl 0xa0(%%edx),%%esi\n");  /* Esi */
        output( "\tmovl 0x9c(%%edx),%%edi\n");  /* Edi */
        output( "\tmovl 0xb0(%%edx),%%eax\n");  /* Eax */
        output( "\tmovl 0xa4(%%edx),%%ebx\n");  /* Ebx */
        output( "\tmovl 0xac(%%edx),%%ecx\n");  /* Ecx */
        output( "\tmovl 0xa8(%%edx),%%edx\n");  /* Edx */
508 509

        /* Get the 16-bit ds */
510
        output( "\tpopw %%ds\n" );
511 512 513 514
    }
    else  /* not a register function */
    {
        /* Push the called routine address */
515
        output( "\tpushl 0x28(%%edx)\n"); /* FIELD_OFFSET(STACK32FRAME,target) */
516

517
        /* Set %fs and %gs to the value saved by the last CallFrom16 */
518
        output( "\tpushw -22(%%ebp)\n" ); /* FIELD_OFFSET(STACK16FRAME,fs)-FIELD_OFFSET(STACK16FRAME,bp) */
519
        output( "\tpopw %%fs\n" );
520
        output( "\tpushw -20(%%ebp)\n" ); /* FIELD_OFFSET(STACK16FRAME,gs)-FIELD_OFFSET(STACK16FRAME,bp) */
521
        output( "\tpopw %%gs\n" );
522 523

        /* Set %ds and %es (and %ax just in case) equal to %ss */
524 525 526
        output( "\tmovw %%ss,%%ax\n" );
        output( "\tmovw %%ax,%%ds\n" );
        output( "\tmovw %%ax,%%es\n" );
527 528 529
    }

    /* Jump to the called routine */
530 531
    output( "\t.byte 0x66\n" );
    output( "\tlret\n" );
532 533

    /* Function footer */
534
    output_function_size( name );
535 536 537 538 539 540 541 542
}


/*******************************************************************
 *         BuildRet16Func
 *
 * Build the return code for 16-bit callbacks
 */
543
static void BuildRet16Func(void)
544
{
545
    function_header( "__wine_call_to_16_ret" );
546

547
    /* Save %esp into %esi */
548
    output( "\tmovl %%esp,%%esi\n" );
549

550 551
    /* Restore 32-bit segment registers */

552 553 554 555
    output( "\t.byte 0x2e\n\tmovl %s", asm_name("CallTo16_DataSelector") );
    output( "-%s,%%edi\n", asm_name("__wine_call16_start") );
    output( "\tmovw %%di,%%ds\n" );
    output( "\tmovw %%di,%%es\n" );
556

557 558
    output( "\t.byte 0x2e\n\tmov %s", asm_name("CallTo16_TebSelector") );
    output( "-%s,%%fs\n", asm_name("__wine_call16_start") );
559

560
    output( "\t.byte 0x64\n\tmov (%d),%%gs\n", GS_OFFSET );
561

562 563
    /* Restore the 32-bit stack */

564 565
    output( "\tmovw %%di,%%ss\n" );
    output( "\t.byte 0x64\n\tmovl (%d),%%esp\n", STACKOFFSET );
566 567 568

    /* Return to caller */

569 570
    output( "\tlret\n" );
    output_function_size( "__wine_call_to_16_ret" );
571 572 573 574 575 576 577 578 579
}


/*******************************************************************
 *         BuildCallTo32CBClient
 *
 * Call a CBClient relay stub from 32-bit code (KERNEL.620).
 *
 * Since the relay stub is itself 32-bit, this should not be a problem;
580
 * unfortunately, the relay stubs are expected to switch back to a
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
 * 16-bit stack (and 16-bit code) after completion :-(
 *
 * This would conflict with our 16- vs. 32-bit stack handling, so
 * we simply switch *back* to our 32-bit stack before returning to
 * the caller ...
 *
 * The CBClient relay stub expects to be called with the following
 * 16-bit stack layout, and with ebp and ebx pointing into the 16-bit
 * stack at the designated places:
 *
 *    ...
 *  (ebp+14) original arguments to the callback routine
 *  (ebp+10) far return address to original caller
 *  (ebp+6)  Thunklet target address
 *  (ebp+2)  Thunklet relay ID code
 *  (ebp)    BP (saved by CBClientGlueSL)
 *  (ebp-2)  SI (saved by CBClientGlueSL)
 *  (ebp-4)  DI (saved by CBClientGlueSL)
 *  (ebp-6)  DS (saved by CBClientGlueSL)
 *
 *   ...     buffer space used by the 16-bit side glue for temp copies
 *
 *  (ebx+4)  far return address to 16-bit side glue code
 *  (ebx)    saved 16-bit ss:sp (pointing to ebx+4)
 *
 * The 32-bit side glue code accesses both the original arguments (via ebp)
 * and the temporary copies prepared by the 16-bit side glue (via ebx).
 * After completion, the stub will load ss:sp from the buffer at ebx
609
 * and perform a far return to 16-bit code.
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
 *
 * To trick the relay stub into returning to us, we replace the 16-bit
 * return address to the glue code by a cs:ip pair pointing to our
 * return entry point (the original return address is saved first).
 * Our return stub thus called will then reload the 32-bit ss:esp and
 * return to 32-bit code (by using and ss:esp value that we have also
 * pushed onto the 16-bit stack before and a cs:eip values found at
 * that position on the 32-bit stack).  The ss:esp to be restored is
 * found relative to the 16-bit stack pointer at:
 *
 *  (ebx-4)   ss  (flat)
 *  (ebx-8)   sp  (32-bit stack pointer)
 *
 * The second variant of this routine, CALL32_CBClientEx, which is used
 * to implement KERNEL.621, has to cope with yet another problem: Here,
 * the 32-bit side directly returns to the caller of the CBClient thunklet,
 * restoring registers saved by CBClientGlueSL and cleaning up the stack.
 * As we have to return to our 32-bit code first, we have to adapt the
 * layout of our temporary area so as to include values for the registers
 * that are to be restored, and later (in the implementation of KERNEL.621)
 * we *really* restore them. The return stub restores DS, DI, SI, and BP
 * from the stack, skips the next 8 bytes (CBClient relay code / target),
 * and then performs a lret NN, where NN is the number of arguments to be
 * removed. Thus, we prepare our temporary area as follows:
 *
 *     (ebx+22) 16-bit cs  (this segment)
 *     (ebx+20) 16-bit ip  ('16-bit' return entry point)
 *     (ebx+16) 32-bit ss  (flat)
 *     (ebx+12) 32-bit sp  (32-bit stack pointer)
 *     (ebx+10) 16-bit bp  (points to ebx+24)
 *     (ebx+8)  16-bit si  (ignored)
 *     (ebx+6)  16-bit di  (ignored)
 *     (ebx+4)  16-bit ds  (we actually use the flat DS here)
 *     (ebx+2)  16-bit ss  (16-bit stack segment)
 *     (ebx+0)  16-bit sp  (points to ebx+4)
 *
 * Note that we ensure that DS is not changed and remains the flat segment,
647
 * and the 32-bit stack pointer our own return stub needs fits just
648 649
 * perfectly into the 8 bytes that are skipped by the Windows stub.
 * One problem is that we have to determine the number of removed arguments,
650 651
 * as these have to be really removed in KERNEL.621. Thus, the BP value
 * that we place in the temporary area to be restored, contains the value
652 653 654 655 656 657 658 659 660 661 662 663
 * that SP would have if no arguments were removed. By comparing the actual
 * value of SP with this value in our return stub we can compute the number
 * of removed arguments. This is then returned to KERNEL.621.
 *
 * The stack layout of this function:
 * (ebp+20)  nArgs     pointer to variable receiving nr. of args (Ex only)
 * (ebp+16)  esi       pointer to caller's esi value
 * (ebp+12)  arg       ebp value to be set for relay stub
 * (ebp+8)   func      CBClient relay stub address
 * (ebp+4)   ret addr
 * (ebp)     ebp
 */
664
static void BuildCallTo32CBClient( int isEx )
665
{
666
    function_header( isEx ? "CALL32_CBClientEx" : "CALL32_CBClient" );
667 668 669

    /* Entry code */

670
    output_cfi( ".cfi_startproc" );
671
    output( "\tpushl %%ebp\n" );
672 673
    output_cfi( ".cfi_adjust_cfa_offset 4" );
    output_cfi( ".cfi_rel_offset %%ebp,0" );
674
    output( "\tmovl %%esp,%%ebp\n" );
675
    output_cfi( ".cfi_def_cfa_register %%ebp" );
676
    output( "\tpushl %%edi\n" );
677
    output_cfi( ".cfi_rel_offset %%edi,-4" );
678
    output( "\tpushl %%esi\n" );
679
    output_cfi( ".cfi_rel_offset %%esi,-8" );
680
    output( "\tpushl %%ebx\n" );
681
    output_cfi( ".cfi_rel_offset %%ebx,-12" );
682

683
    /* Get pointer to temporary area and save the 32-bit stack pointer */
684

685 686
    output( "\tmovl 16(%%ebp), %%ebx\n" );
    output( "\tleal -8(%%esp), %%eax\n" );
687 688

    if ( !isEx )
689
        output( "\tmovl %%eax, -8(%%ebx)\n" );
690
    else
691
        output( "\tmovl %%eax, 12(%%ebx)\n" );
692 693 694

    /* Set up registers and call CBClient relay stub (simulating a far call) */

695 696
    output( "\tmovl 20(%%ebp), %%esi\n" );
    output( "\tmovl (%%esi), %%esi\n" );
697

698 699
    output( "\tmovl 8(%%ebp), %%eax\n" );
    output( "\tmovl 12(%%ebp), %%ebp\n" );
700

701 702
    output( "\tpushl %%cs\n" );
    output( "\tcall *%%eax\n" );
703 704 705

    /* Return new esi value to caller */

706 707
    output( "\tmovl 32(%%esp), %%edi\n" );
    output( "\tmovl %%esi, (%%edi)\n" );
708 709 710 711

    /* Return argument size to caller */
    if ( isEx )
    {
712 713
        output( "\tmovl 36(%%esp), %%ebx\n" );
        output( "\tmovl %%ebp, (%%ebx)\n" );
714 715 716 717
    }

    /* Restore registers and return */

718
    output( "\tpopl %%ebx\n" );
719
    output_cfi( ".cfi_same_value %%ebx" );
720
    output( "\tpopl %%esi\n" );
721
    output_cfi( ".cfi_same_value %%esi" );
722
    output( "\tpopl %%edi\n" );
723
    output_cfi( ".cfi_same_value %%edi" );
724
    output( "\tpopl %%ebp\n" );
725 726
    output_cfi( ".cfi_def_cfa %%esp,4" );
    output_cfi( ".cfi_same_value %%ebp" );
727
    output( "\tret\n" );
728
    output_cfi( ".cfi_endproc" );
729
    output_function_size( isEx ? "CALL32_CBClientEx" : "CALL32_CBClient" );
730 731 732

    /* '16-bit' return stub */

733
    function_header( isEx ? "CALL32_CBClientEx_Ret" : "CALL32_CBClient_Ret" );
734 735
    if ( !isEx )
    {
736 737
        output( "\tmovzwl %%sp, %%ebx\n" );
        output( "\tlssl %%ss:-16(%%ebx), %%esp\n" );
738 739 740
    }
    else
    {
741 742 743 744
        output( "\tmovzwl %%bp, %%ebx\n" );
        output( "\tsubw %%bp, %%sp\n" );
        output( "\tmovzwl %%sp, %%ebp\n" );
        output( "\tlssl %%ss:-12(%%ebx), %%esp\n" );
745
    }
746 747
    output( "\tlret\n" );
    output_function_size( isEx ? "CALL32_CBClientEx_Ret" : "CALL32_CBClient_Ret" );
748 749 750 751
}


/*******************************************************************
752
 *         build_call_from_regs_x86
753 754 755 756 757 758
 *
 * Build a 32-bit-to-Wine call-back function for a 'register' function.
 * 'args' is the number of dword arguments.
 *
 * Stack layout:
 *   ...
759 760 761 762
 * (ebp+20)  first arg
 * (ebp+16)  ret addr to user code
 * (ebp+12)  func to call (relative to relay code ret addr)
 * (ebp+8)   number of args
763 764 765 766
 * (ebp+4)   ret addr to relay code
 * (ebp+0)   saved ebp
 * (ebp-128) buffer area to allow stack frame manipulation
 * (ebp-332) CONTEXT86 struct
767 768
 * (ebp-336) padding for stack alignment
 * (ebp-336-n) CONTEXT86 *argument
769 770 771 772 773 774 775 776
 *  ....     other arguments copied from (ebp+12)
 *
 * The entry point routine is called with a CONTEXT* extra argument,
 * following the normal args. In this context structure, EIP_reg
 * contains the return address to user code, and ESP_reg the stack
 * pointer on return (with the return address and arguments already
 * removed).
 */
777
static void build_call_from_regs_x86(void)
778
{
779
    static const int STACK_SPACE = 128 + 0x2cc /* sizeof(CONTEXT86) */;
780 781 782

    /* Function header */

783 784
    output( "\t.text\n" );
    function_header( "__wine_call_from_regs" );
785 786 787

    /* Allocate some buffer space on the stack */

788
    output_cfi( ".cfi_startproc" );
789
    output( "\tpushl %%ebp\n" );
790 791 792 793
    output_cfi( ".cfi_adjust_cfa_offset 4" );
    output_cfi( ".cfi_rel_offset %%ebp,0" );
    output( "\tmovl %%esp,%%ebp\n" );
    output_cfi( ".cfi_def_cfa_register %%ebp" );
794
    output( "\tleal -%d(%%esp),%%esp\n", STACK_SPACE );
795

796 797
    /* Build the context structure */

798
    output( "\tmovl %%eax,0xb0(%%esp)\n" );  /* Eax */
799 800
    output( "\tpushfl\n" );
    output( "\tpopl %%eax\n" );
801
    output( "\tmovl %%eax,0xc0(%%esp)\n");  /* EFlags */
802
    output( "\tmovl 0(%%ebp),%%eax\n" );
803 804 805 806 807 808
    output( "\tmovl %%eax,0xb4(%%esp)\n");  /* Ebp */
    output( "\tmovl %%ebx,0xa4(%%esp)\n");  /* Ebx */
    output( "\tmovl %%ecx,0xac(%%esp)\n");  /* Ecx */
    output( "\tmovl %%edx,0xa8(%%esp)\n");  /* Edx */
    output( "\tmovl %%esi,0xa0(%%esp)\n");  /* Esi */
    output( "\tmovl %%edi,0x9c(%%esp)\n");  /* Edi */
809 810 811

    output( "\txorl %%eax,%%eax\n" );
    output( "\tmovw %%cs,%%ax\n" );
812
    output( "\tmovl %%eax,0xbc(%%esp)\n");  /* SegCs */
813
    output( "\tmovw %%es,%%ax\n" );
814
    output( "\tmovl %%eax,0x94(%%esp)\n");  /* SegEs */
815
    output( "\tmovw %%fs,%%ax\n" );
816
    output( "\tmovl %%eax,0x90(%%esp)\n");  /* SegFs */
817
    output( "\tmovw %%gs,%%ax\n" );
818
    output( "\tmovl %%eax,0x8c(%%esp)\n");  /* SegGs */
819
    output( "\tmovw %%ss,%%ax\n" );
820
    output( "\tmovl %%eax,0xc8(%%esp)\n");  /* SegSs */
821
    output( "\tmovw %%ds,%%ax\n" );
822
    output( "\tmovl %%eax,0x98(%%esp)\n");  /* SegDs */
823 824
    output( "\tmovw %%ax,%%es\n" );  /* set %es equal to %ds just in case */

825
    output( "\tmovl $0x10007,0(%%esp)\n");  /* ContextFlags */
826

827
    output( "\tmovl 16(%%ebp),%%eax\n" ); /* Get %eip at time of call */
828
    output( "\tmovl %%eax,0xb8(%%esp)\n");  /* Eip */
829 830 831

    /* Transfer the arguments */

832 833 834
    output( "\tmovl 8(%%ebp),%%ecx\n" );    /* fetch number of args to copy */
    output( "\tleal 4(,%%ecx,4),%%edx\n" ); /* add 4 for context arg */
    output( "\tsubl %%edx,%%esp\n" );
835
    output( "\tandl $~15,%%esp\n" );
836
    output( "\tleal 20(%%ebp),%%esi\n" );  /* get %esp at time of call */
837
    output( "\tmovl %%esp,%%edi\n" );
838
    output( "\ttest %%ecx,%%ecx\n" );
839 840 841 842 843
    output( "\tjz 1f\n" );
    output( "\tcld\n" );
    output( "\trep\n\tmovsl\n" );  /* copy args */
    output( "1:\tleal %d(%%ebp),%%eax\n", -STACK_SPACE );  /* get addr of context struct */
    output( "\tmovl %%eax,(%%edi)\n" );    /* and pass it as extra arg */
844
    output( "\tmovl %%esi,%d(%%ebp)\n", 0xc4 /* Esp */ - STACK_SPACE );
845 846 847

    /* Call the entry point */

848 849 850
    output( "\tmovl 4(%%ebp),%%eax\n" );   /* get relay code addr */
    output( "\taddl 12(%%ebp),%%eax\n" );
    output( "\tcall *%%eax\n" );
851
    output( "\tleal -%d(%%ebp),%%ecx\n", STACK_SPACE );
852 853 854

    /* Restore the context structure */

855
    output( "2:\tpushl 0x94(%%ecx)\n");     /* SegEs */
856
    output( "\tpopl %%es\n" );
857
    output( "\tpushl 0x90(%%ecx)\n");       /* SegFs */
858
    output( "\tpopl %%fs\n" );
859
    output( "\tpushl 0x8c(%%ecx)\n");       /* SegGs */
860 861
    output( "\tpopl %%gs\n" );

862 863 864 865 866 867
    output( "\tmovl 0x9c(%%ecx),%%edi\n");  /* Edi */
    output( "\tmovl 0xa0(%%ecx),%%esi\n");  /* Esi */
    output( "\tmovl 0xa8(%%ecx),%%edx\n");  /* Edx */
    output( "\tmovl 0xa4(%%ecx),%%ebx\n");  /* Ebx */
    output( "\tmovl 0xb0(%%ecx),%%eax\n");  /* Eax */
    output( "\tmovl 0xb4(%%ecx),%%ebp\n");  /* Ebp */
868

869
    output( "\tpushl 0xc8(%%ecx)\n");       /* SegSs */
870
    output( "\tpopl %%ss\n" );
871
    output( "\tmovl 0xc4(%%ecx),%%esp\n");  /* Esp */
872

873 874 875 876 877
    output( "\tpushl 0xc0(%%ecx)\n");       /* EFlags */
    output( "\tpushl 0xbc(%%ecx)\n");       /* SegCs */
    output( "\tpushl 0xb8(%%ecx)\n");       /* Eip */
    output( "\tpushl 0x98(%%ecx)\n");       /* SegDs */
    output( "\tmovl 0xac(%%ecx),%%ecx\n");  /* Ecx */
878 879 880

    output( "\tpopl %%ds\n" );
    output( "\tiret\n" );
881
    output_cfi( ".cfi_endproc" );
882
    output_function_size( "__wine_call_from_regs" );
883

884
    function_header( "__wine_restore_regs" );
885
    output_cfi( ".cfi_startproc" );
886 887
    output( "\tmovl 4(%%esp),%%ecx\n" );
    output( "\tjmp 2b\n" );
888
    output_cfi( ".cfi_endproc" );
889
    output_function_size( "__wine_restore_regs" );
890 891 892
}


893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
/*******************************************************************
 *         BuildPendingEventCheck
 *
 * Build a function that checks whether there are any
 * pending DPMI events.
 *
 * Stack layout:
 *   
 * (sp+12) long   eflags
 * (sp+6)  long   cs
 * (sp+2)  long   ip
 * (sp)    word   fs
 *
 * On entry to function, fs register points to a valid TEB.
 * On exit from function, stack will be popped.
 */
909
static void BuildPendingEventCheck(void)
910 911 912
{
    /* Function header */

913
    function_header( "DPMI_PendingEventCheck" );
914 915

    /* Check for pending events. */
916

917
    output( "\t.byte 0x64\n\ttestl $0xffffffff,(%d)\n", VM86_PENDING_OFFSET );
918
    output( "\tje %s\n", asm_name("DPMI_PendingEventCheck_Cleanup") );
919
    output( "\t.byte 0x64\n\ttestl $0xffffffff,(%d)\n", DPMI_VIF_OFFSET );
920
    output( "\tje %s\n", asm_name("DPMI_PendingEventCheck_Cleanup") );
921 922 923

    /* Process pending events. */

924
    output( "\tsti\n" );
925

926 927
    /* Start cleanup. Restore fs register. */

928 929
    output( "%s\n", asm_globl("DPMI_PendingEventCheck_Cleanup") );
    output( "\tpopw %%fs\n" );
930 931 932

    /* Return from function. */

933 934
    output( "%s\n", asm_globl("DPMI_PendingEventCheck_Return") );
    output( "\tiret\n" );
935

936
    output_function_size( "DPMI_PendingEventCheck" );
937 938 939
}


940
/*******************************************************************
941
 *         output_asm_relays16
942
 *
943
 * Build all the 16-bit relay callbacks
944
 */
945
void output_asm_relays16(void)
946 947 948
{
    /* File header */

949 950
    output( "\t.text\n" );
    output( "%s:\n\n", asm_name("__wine_spec_thunk_text_16") );
951

952
    output( "%s\n", asm_globl("__wine_call16_start") );
953

954
    /* Standard CallFrom16 routine */
955
    BuildCallFrom16Core( 0, 0 );
956 957

    /* Register CallFrom16 routine */
958
    BuildCallFrom16Core( 1, 0 );
959 960

    /* C16ThkSL CallFrom16 routine */
961
    BuildCallFrom16Core( 0, 1 );
962

963
    /* Standard CallTo16 routine */
964
    BuildCallTo16Core( 0 );
965

966
    /* Register CallTo16 routine */
967
    BuildCallTo16Core( 1 );
968

969
    /* Standard CallTo16 return stub */
970
    BuildRet16Func();
971

972
    /* CBClientThunkSL routine */
973
    BuildCallTo32CBClient( 0 );
974 975

    /* CBClientThunkSLEx routine */
976
    BuildCallTo32CBClient( 1  );
977

978
    /* Pending DPMI events check stub */
979
    BuildPendingEventCheck();
980

981 982
    output( "%s\n", asm_globl("__wine_call16_end") );
    output_function_size( "__wine_spec_thunk_text_16" );
983 984

    /* Declare the return address and data selector variables */
985 986 987
    output( "\n\t.data\n\t.align %d\n", get_alignment(4) );
    output( "%s\n\t.long 0\n", asm_globl("CallTo16_DataSelector") );
    output( "%s\n\t.long 0\n", asm_globl("CallTo16_TebSelector") );
988 989

    output( "\t.text\n" );
990
    output( "%s:\n", asm_name("__wine_spec_thunk_text_32") );
991
    build_call_from_regs_x86();
992
    output_function_size( "__wine_spec_thunk_text_32" );
993
}
994

995

996
/*******************************************************************
997
 *         output_asm_relays
998
 *
999
 * Build all the assembly relay callbacks
1000
 */
1001
void output_asm_relays(void)
1002
{
1003
    switch (target_cpu)
1004
    {
1005
    case CPU_x86:
1006
        build_call_from_regs_x86();
1007 1008
        break;
    default:
1009
        break;
1010
    }
1011
}