spec16.c 34.3 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * 16-bit spec files
 *
 * 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 29 30 31 32
#include <assert.h>
#include <ctype.h>

#include "build.h"

33 34 35 36 37 38
#define NE_FFLAGS_SINGLEDATA 0x0001
#define NE_FFLAGS_LIBMODULE  0x8000

/* argument type flags for relay debugging */
enum arg_types
{
39 40 41 42 43 44 45 46
    ARG16_NONE = 0, /* indicates end of arg list */
    ARG16_WORD,     /* unsigned word */
    ARG16_SWORD,    /* signed word */
    ARG16_LONG,     /* long or segmented pointer */
    ARG16_PTR,      /* linear pointer */
    ARG16_STR,      /* linear pointer to null-terminated string */
    ARG16_SEGSTR,   /* segmented pointer to null-terminated string */
    ARG16_VARARG    /* start of varargs */
47 48
};

49 50 51 52 53 54 55 56 57 58 59
/* sequences of nops to fill a certain number of words */
static const char * const nop_sequence[4] =
{
    ".byte 0x89,0xf6",  /* mov %esi,%esi */
    ".byte 0x8d,0x74,0x26,0x00",  /* lea 0x00(%esi),%esi */
    ".byte 0x8d,0xb6,0x00,0x00,0x00,0x00",  /* lea 0x00000000(%esi),%esi */
    ".byte 0x8d,0x74,0x26,0x00,0x8d,0x74,0x26,0x00" /* lea 0x00(%esi),%esi; lea 0x00(%esi),%esi */
};

static inline int is_function( const ORDDEF *odp )
{
60
    if (odp->flags & FLAG_EXPORT32) return 0;
61 62 63 64 65
    return (odp->type == TYPE_CDECL ||
            odp->type == TYPE_PASCAL ||
            odp->type == TYPE_VARARGS ||
            odp->type == TYPE_STUB);
}
66

67 68 69
static const char *get_args_str( const ORDDEF *odp )
{
    static char buffer[MAX_ARGUMENTS*2+1];
70
    int i;
71 72 73 74 75 76 77 78 79 80 81

    buffer[0] = 0;
    for (i = 0; i < odp->u.func.nb_args; i++)
    {
        switch (odp->u.func.args[i])
        {
        case ARG_WORD:   strcat( buffer, "w" ); break;
        case ARG_SWORD:  strcat( buffer, "s" ); break;
        case ARG_SEGSTR: strcat( buffer, "T" ); break;
        case ARG_STR:    strcat( buffer, "t" ); break;
        case ARG_LONG:
82
        case ARG_FLOAT:
83 84
        case ARG_SEGPTR: strcat( buffer, "l" ); break;
        case ARG_PTR:
85 86 87 88
        case ARG_WSTR:
        case ARG_INT128: strcat( buffer, "p" ); break;
        case ARG_INT64:
        case ARG_DOUBLE: strcat( buffer, "ll" ); break;
89 90 91 92 93
        }
    }
    return buffer;
}

94
/*******************************************************************
95
 *         output_entries
96
 *
97
 * Output entries for individual symbols in the entry table.
98
 */
99
static void output_entries( DLLSPEC *spec, int first, int count )
100
{
101 102 103 104 105
    int i;

    for (i = 0; i < count; i++)
    {
        ORDDEF *odp = spec->ordinals[first + i];
106
        output( "\t.byte 0x03\n" );  /* flags: exported & public data */
107 108 109 110 111 112
        switch (odp->type)
        {
        case TYPE_CDECL:
        case TYPE_PASCAL:
        case TYPE_VARARGS:
        case TYPE_STUB:
113
            output( "\t%s .L__wine_%s_%u-.L__wine_spec_code_segment\n",
114 115 116 117
                     get_asm_short_keyword(),
                     make_c_identifier(spec->dll_name), first + i );
            break;
        case TYPE_VARIABLE:
118
            output( "\t%s .L__wine_%s_%u-.L__wine_spec_data_segment\n",
119 120 121 122
                     get_asm_short_keyword(),
                     make_c_identifier(spec->dll_name), first + i );
            break;
        case TYPE_ABS:
123
            output( "\t%s 0x%04x  /* %s */\n",
124 125 126 127 128 129
                     get_asm_short_keyword(), odp->u.abs.value, odp->name );
            break;
        default:
            assert(0);
        }
    }
130 131 132
}


133
/*******************************************************************
134
 *         output_entry_table
135
 */
136
static void output_entry_table( DLLSPEC *spec )
137
{
138
    int i, prev = 0, prev_sel = -1, bundle_count = 0;
139

140
    for (i = 1; i <= spec->limit; i++)
141 142
    {
        int selector = 0;
143
        ORDDEF *odp = spec->ordinals[i];
144
        if (!odp) continue;
145
        if (odp->flags & FLAG_EXPORT32) continue;
146

147 148
        switch (odp->type)
        {
149 150
        case TYPE_CDECL:
        case TYPE_PASCAL:
151
        case TYPE_VARARGS:
152 153 154
        case TYPE_STUB:
            selector = 1;  /* Code selector */
            break;
155
        case TYPE_VARIABLE:
156 157 158 159 160 161
            selector = 2;  /* Data selector */
            break;
        case TYPE_ABS:
            selector = 0xfe;  /* Constant selector */
            break;
        default:
162
            continue;
163 164
        }

165
        if (prev + 1 != i || prev_sel != selector || bundle_count == 255)
166
        {
167 168
            /* need to start a new bundle */

169 170 171
            /* flush previous bundle */
            if (bundle_count)
            {
172
                output( "\t/* %s.%d - %s.%d */\n",
173
                         spec->dll_name, prev - bundle_count + 1, spec->dll_name, prev );
174 175
                output( "\t.byte 0x%02x,0x%02x\n", bundle_count, prev_sel );
                output_entries( spec, prev - bundle_count + 1, bundle_count );
176 177
            }

178 179 180 181 182
            if (prev + 1 != i)
            {
                int skip = i - (prev + 1);
                while (skip > 255)
                {
183
                    output( "\t.byte 0xff,0x00\n" );
184 185
                    skip -= 255;
                }
186
                output( "\t.byte 0x%02x,0x00\n", skip );
187
            }
188

189
            bundle_count = 0;
190 191
            prev_sel = selector;
        }
192
        bundle_count++;
193
        prev = i;
194
    }
195 196 197 198

    /* flush last bundle */
    if (bundle_count)
    {
199 200
        output( "\t.byte 0x%02x,0x%02x\n", bundle_count, prev_sel );
        output_entries( spec, prev - bundle_count + 1, bundle_count );
201
    }
202
    output( "\t.byte 0x00\n" );
203 204 205 206 207 208
}


/*******************************************************************
 *         output_resident_name
 */
209
static void output_resident_name( const char *string, int ordinal )
210 211 212
{
    unsigned int i, len = strlen(string);

213 214 215 216
    output( "\t.byte 0x%02x", len );
    for (i = 0; i < len; i++) output( ",0x%02x", (unsigned char)toupper(string[i]) );
    output( " /* %s */\n", string );
    output( "\t%s %u\n", get_asm_short_keyword(), ordinal );
217
}
218 219


220
/*******************************************************************
221
 *         get_callfrom16_name
222
 */
223
static const char *get_callfrom16_name( const ORDDEF *odp )
224
{
225 226 227 228 229 230 231 232
    static char *buffer;

    free( buffer );
    buffer = strmake( "%s_%s_%s",
                      (odp->type == TYPE_PASCAL) ? "p" :
                      (odp->type == TYPE_VARARGS) ? "v" : "c",
                      (odp->flags & FLAG_REGISTER) ? "regs" :
                      (odp->flags & FLAG_RET16) ? "word" : "long",
233
                      get_args_str(odp) );
234 235
    return buffer;
}
236

237 238 239 240 241 242 243 244 245 246

/*******************************************************************
 *         get_relay_name
 */
static const char *get_relay_name( const ORDDEF *odp )
{
    static char buffer[80];
    char *p;

    switch(odp->type)
247
    {
248 249 250 251 252 253 254 255 256 257 258 259 260
    case TYPE_PASCAL:
        strcpy( buffer, "p_" );
        break;
    case TYPE_VARARGS:
        strcpy( buffer, "v_" );
        break;
    case TYPE_CDECL:
    case TYPE_STUB:
        strcpy( buffer, "c_" );
        break;
    default:
        assert(0);
    }
261
    strcat( buffer, get_args_str(odp) );
262 263 264 265 266
    for (p = buffer + 2; *p; p++)
    {
        /* map string types to the corresponding plain pointer type */
        if (*p == 't') *p = 'p';
        else if (*p == 'T') *p = 'l';
267
    }
268 269
    if (odp->flags & FLAG_REGISTER) strcat( buffer, "_regs" );
    return buffer;
270 271 272
}


273
/*******************************************************************
274 275 276 277
 *         get_function_argsize
 */
static int get_function_argsize( const ORDDEF *odp )
{
278
    int i, argsize = 0;
279

280
    for (i = 0; i < odp->u.func.nb_args; i++)
281
    {
282
        switch (odp->u.func.args[i])
283
        {
284 285
        case ARG_WORD:
        case ARG_SWORD:
286 287
            argsize += 2;
            break;
288 289 290 291 292 293
        case ARG_SEGPTR:
        case ARG_SEGSTR:
        case ARG_LONG:
        case ARG_PTR:
        case ARG_STR:
        case ARG_WSTR:
294 295
        case ARG_FLOAT:
        case ARG_INT128:
296 297
            argsize += 4;
            break;
298
        case ARG_INT64:
299 300 301
        case ARG_DOUBLE:
            argsize += 8;
            break;
302 303 304 305 306 307 308 309
        }
    }
    return argsize;
}


/*******************************************************************
 *         output_call16_function
310
 *
311
 * Build a 16-bit-to-Wine callback glue function.
312
 *
313
 * The generated routines are intended to be used as argument conversion
314 315 316
 * routines to be called by the CallFrom16... core. Thus, the prototypes of
 * the generated routines are (see also CallFrom16):
 *
317 318 319
 *  extern WORD WINAPI __wine_spec_call16_C_xxx( FARPROC func, LPBYTE args );
 *  extern LONG WINAPI __wine_spec_call16_C_xxx( FARPROC func, LPBYTE args );
 *  extern void WINAPI __wine_spec_call16_C_xxx_regs( FARPROC func, LPBYTE args, CONTEXT86 *context );
320
 *
321 322
 * where 'C' is the calling convention ('p' for pascal or 'c' for cdecl),
 * and each 'x' is an argument  ('w'=word, 's'=signed word, 'l'=long,
323 324 325 326
 * 'p'=linear pointer, 't'=linear pointer to null-terminated string,
 * 'T'=segmented pointer to null-terminated string).
 *
 * The generated routines fetch the arguments from the 16-bit stack (pointed
327
 * to by 'args'); the offsets of the single argument values are computed
328 329
 * according to the calling convention and the argument types.  Then, the
 * 32-bit entry point is called with these arguments.
330
 *
331
 * For register functions, the arguments (if present) are converted just
332
 * the same as for normal functions, but in addition the CONTEXT86 pointer
333 334
 * filled with the current register values is passed to the 32-bit routine.
 */
335
static void output_call16_function( ORDDEF *odp )
336
{
337
    char *name;
338
    int i, pos, stack_words;
339
    int argsize = get_function_argsize( odp );
340
    int needs_ldt = (strpbrk( get_args_str( odp ), "pt" ) != NULL);
341

342
    name = strmake( ".L__wine_spec_call16_%s", get_relay_name(odp) );
343

344 345 346
    output( "\t.align %d\n", get_alignment(4) );
    output( "\t%s\n", func_declaration(name) );
    output( "%s:\n", name );
347
    output_cfi( ".cfi_startproc" );
348
    output( "\tpushl %%ebp\n" );
349 350
    output_cfi( ".cfi_adjust_cfa_offset 4" );
    output_cfi( ".cfi_rel_offset %%ebp,0" );
351
    output( "\tmovl %%esp,%%ebp\n" );
352
    output_cfi( ".cfi_def_cfa_register %%ebp" );
353
    stack_words = 2;
354
    if (needs_ldt)
355
    {
356
        output( "\tpushl %%esi\n" );
357
        output_cfi( ".cfi_rel_offset %%esi,-4" );
358
        stack_words++;
359
        if (UsePIC)
360
        {
361 362
            output( "\tcall %s\n", asm_name("__wine_spec_get_pc_thunk_eax") );
            output( "1:\tmovl wine_ldt_copy_ptr-1b(%%eax),%%esi\n" );
363
        }
364
        else
365
            output( "\tmovl $%s,%%esi\n", asm_name("wine_ldt_copy") );
366
    }
367

368
    /* preserve 16-byte stack alignment */
369 370
    stack_words += odp->u.func.nb_args;
    for (i = 0; i < odp->u.func.nb_args; i++)
371
        if (odp->u.func.args[i] == ARG_DOUBLE || odp->u.func.args[i] == ARG_INT64) stack_words++;
372
    if ((odp->flags & FLAG_REGISTER) || (odp->type == TYPE_VARARGS)) stack_words++;
373
    if (stack_words % 4) output( "\tsubl $%d,%%esp\n", 16 - 4 * (stack_words % 4) );
374

375
    if (odp->u.func.nb_args || odp->type == TYPE_VARARGS)
376
        output( "\tmovl 12(%%ebp),%%ecx\n" );  /* args */
377

378
    if (odp->flags & FLAG_REGISTER)
379
    {
380
        output( "\tpushl 16(%%ebp)\n" );  /* context */
381
    }
382 383
    else if (odp->type == TYPE_VARARGS)
    {
384 385
        output( "\tleal %d(%%ecx),%%eax\n", argsize );
        output( "\tpushl %%eax\n" );  /* va_list16 */
386 387 388
    }

    pos = (odp->type == TYPE_PASCAL) ? 0 : argsize;
389
    for (i = odp->u.func.nb_args - 1; i >= 0; i--)
390
    {
391
        switch (odp->u.func.args[i])
392
        {
393
        case ARG_WORD:
394
            if (odp->type != TYPE_PASCAL) pos -= 2;
395 396
            output( "\tmovzwl %d(%%ecx),%%eax\n", pos );
            output( "\tpushl %%eax\n" );
397
            if (odp->type == TYPE_PASCAL) pos += 2;
398 399
            break;

400
        case ARG_SWORD:
401
            if (odp->type != TYPE_PASCAL) pos -= 2;
402 403
            output( "\tmovswl %d(%%ecx),%%eax\n", pos );
            output( "\tpushl %%eax\n" );
404
            if (odp->type == TYPE_PASCAL) pos += 2;
405 406
            break;

407
        case ARG_INT64:
408 409 410 411 412 413
        case ARG_DOUBLE:
            if (odp->type != TYPE_PASCAL) pos -= 4;
            output( "\tpushl %d(%%ecx)\n", pos );
            if (odp->type == TYPE_PASCAL) pos += 4;
            /* fall through */
        case ARG_LONG:
414
        case ARG_FLOAT:
415 416
        case ARG_SEGPTR:
        case ARG_SEGSTR:
417
            if (odp->type != TYPE_PASCAL) pos -= 4;
418
            output( "\tpushl %d(%%ecx)\n", pos );
419
            if (odp->type == TYPE_PASCAL) pos += 4;
420 421
            break;

422 423 424
        case ARG_PTR:
        case ARG_STR:
        case ARG_WSTR:
425
        case ARG_INT128:
426
            if (odp->type != TYPE_PASCAL) pos -= 4;
427 428 429 430 431
            output( "\tmovzwl %d(%%ecx),%%edx\n", pos + 2 ); /* sel */
            output( "\tshr $3,%%edx\n" );
            output( "\tmovzwl %d(%%ecx),%%eax\n", pos ); /* offset */
            output( "\taddl (%%esi,%%edx,4),%%eax\n" );
            output( "\tpushl %%eax\n" );
432
            if (odp->type == TYPE_PASCAL) pos += 4;
433 434 435 436
            break;
        }
    }

437
    output( "\tcall *8(%%ebp)\n" );
438

439 440 441 442 443
    if (needs_ldt)
    {
        output( "\tmovl -4(%%ebp),%%esi\n" );
        output_cfi( ".cfi_same_value %%esi" );
    }
444
    output( "\tleave\n" );
445 446
    output_cfi( ".cfi_def_cfa %%esp,4" );
    output_cfi( ".cfi_same_value %%ebp" );
447
    output( "\tret\n" );
448
    output_cfi( ".cfi_endproc" );
449
    output_function_size( name );
450
    free( name );
451 452 453 454
}


/*******************************************************************
455 456 457
 *         callfrom16_type_compare
 *
 * Compare two callfrom16 sequences.
458
 */
459
static int callfrom16_type_compare( const void *e1, const void *e2 )
460
{
461 462
    const ORDDEF *odp1 = *(const ORDDEF * const *)e1;
    const ORDDEF *odp2 = *(const ORDDEF * const *)e2;
463
    int retval;
464 465
    int type1 = odp1->type;
    int type2 = odp2->type;
466
    char args1[80];
467

468 469
    if (type1 == TYPE_STUB) type1 = TYPE_CDECL;
    if (type2 == TYPE_STUB) type2 = TYPE_CDECL;
470

471
    if ((retval = type1 - type2) != 0) return retval;
472

473 474
    type1 = odp1->flags & (FLAG_RET16|FLAG_REGISTER);
    type2 = odp2->flags & (FLAG_RET16|FLAG_REGISTER);
475

476
    if ((retval = type1 - type2) != 0) return retval;
477

478 479
    strcpy( args1, get_args_str( odp1 ));
    return strcmp( args1, get_args_str( odp2 ));
480 481 482
}


483
/*******************************************************************
484
 *         relay_type_compare
485
 *
486 487 488
 * Same as callfrom16_type_compare but ignores differences that don't affect the resulting relay function.
 */
static int relay_type_compare( const void *e1, const void *e2 )
489
{
490 491 492
    const ORDDEF *odp1 = *(const ORDDEF * const *)e1;
    const ORDDEF *odp2 = *(const ORDDEF * const *)e2;
    char name1[80];
493

494 495
    strcpy( name1, get_relay_name(odp1) );
    return strcmp( name1, get_relay_name(odp2) );
496 497 498
}


499
/*******************************************************************
500
 *         sort_func_list
501
 *
502
 * Sort a list of functions, removing duplicates.
503
 */
504 505
static int sort_func_list( ORDDEF **list, int count,
                           int (*compare)(const void *, const void *) )
506
{
507
    int i, j;
508

509
    if (!count) return 0;
510
    qsort( list, count, sizeof(*list), compare );
511

512
    for (i = j = 0; i < count; i++)
513
    {
514
        if (compare( &list[j], &list[i] )) list[++j] = list[i];
515
    }
516 517
    return j + 1;
}
Dmitry Timoshkov's avatar
Dmitry Timoshkov committed
518

519

520
/*******************************************************************
521
 *         output_module16
522
 *
523
 * Output code for a 16-bit module.
524
 */
525
static void output_module16( DLLSPEC *spec )
526 527
{
    ORDDEF **typelist;
528
    ORDDEF *entry_point = NULL;
529
    int i, j, nb_funcs;
530

531 532 533 534
    /* store the main entry point as ordinal 0 */

    if (!spec->ordinals)
    {
535
        assert(spec->limit == 0);
536 537 538 539 540 541 542 543 544 545 546 547 548
        spec->ordinals = xmalloc( sizeof(spec->ordinals[0]) );
        spec->ordinals[0] = NULL;
    }
    if (spec->init_func && !(spec->characteristics & IMAGE_FILE_DLL))
    {
        entry_point = xmalloc( sizeof(*entry_point) );
        entry_point->type = TYPE_PASCAL;
        entry_point->ordinal = 0;
        entry_point->lineno = 0;
        entry_point->flags = FLAG_REGISTER;
        entry_point->name = NULL;
        entry_point->link_name = xstrdup( spec->init_func );
        entry_point->export_name = NULL;
549
        entry_point->u.func.nb_args = 0;
550 551 552 553
        assert( !spec->ordinals[0] );
        spec->ordinals[0] = entry_point;
    }

554
    /* Build sorted list of all argument types, without duplicates */
555

556
    typelist = xmalloc( (spec->limit + 1) * sizeof(*typelist) );
557

558
    for (i = nb_funcs = 0; i <= spec->limit; i++)
559 560
    {
        ORDDEF *odp = spec->ordinals[i];
561 562
        if (!odp) continue;
        if (is_function( odp )) typelist[nb_funcs++] = odp;
563 564
    }

565
    nb_funcs = sort_func_list( typelist, nb_funcs, callfrom16_type_compare );
566

567
    /* Output the module structure */
568

569 570 571
    output( "\n/* module data */\n\n" );
    output( "\t.data\n" );
    output( "\t.align %d\n", get_alignment(4) );
572
    output( ".L__wine_spec_dos_header:\n" );
573
    output( "\t%s 0x5a4d\n", get_asm_short_keyword() );                    /* e_magic */
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* e_cblp */
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* e_cp */
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* e_crlc */
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* e_cparhdr */
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* e_minalloc */
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* e_maxalloc */
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* e_ss */
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* e_sp */
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* e_csum */
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* e_ip */
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* e_cs */
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* e_lfarlc */
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* e_ovno */
    output( "\t%s 0,0,0,0\n", get_asm_short_keyword() );                   /* e_res */
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* e_oemid */
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* e_oeminfo */
    output( "\t%s 0,0,0,0,0,0,0,0,0,0\n", get_asm_short_keyword() );       /* e_res2 */
591
    output( "\t.long .L__wine_spec_ne_header-.L__wine_spec_dos_header\n" );/* e_lfanew */
592 593

    output( ".L__wine_spec_ne_header:\n" );
594
    output( "\t%s 0x454e\n", get_asm_short_keyword() );                    /* ne_magic */
595 596 597
    output( "\t.byte 0\n" );                                               /* ne_ver */
    output( "\t.byte 0\n" );                                               /* ne_rev */
    output( "\t%s .L__wine_spec_ne_enttab-.L__wine_spec_ne_header\n",      /* ne_enttab */
598
             get_asm_short_keyword() );
599
    output( "\t%s .L__wine_spec_ne_enttab_end-.L__wine_spec_ne_enttab\n",  /* ne_cbenttab */
600
             get_asm_short_keyword() );
601 602
    output( "\t.long 0\n" );                                               /* ne_crc */
    output( "\t%s 0x%04x\n", get_asm_short_keyword(),                      /* ne_flags */
603 604
             NE_FFLAGS_SINGLEDATA |
             ((spec->characteristics & IMAGE_FILE_DLL) ? NE_FFLAGS_LIBMODULE : 0) );
605 606 607
    output( "\t%s 2\n", get_asm_short_keyword() );                         /* ne_autodata */
    output( "\t%s %u\n", get_asm_short_keyword(), spec->heap_size );       /* ne_heap */
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* ne_stack */
608 609 610 611
    if (!entry_point) output( "\t.long 0\n" );                             /* ne_csip */
    else output( "\t%s .L__wine_%s_0-.L__wine_spec_code_segment,1\n",
                 get_asm_short_keyword(), make_c_identifier(spec->dll_name) );
    output( "\t%s 0,2\n", get_asm_short_keyword() );                       /* ne_sssp */
612 613 614 615
    output( "\t%s 2\n", get_asm_short_keyword() );                         /* ne_cseg */
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* ne_cmod */
    output( "\t%s 0\n", get_asm_short_keyword() );                         /* ne_cbnrestab */
    output( "\t%s .L__wine_spec_ne_segtab-.L__wine_spec_ne_header\n",      /* ne_segtab */
616
             get_asm_short_keyword() );
617
    output( "\t%s .L__wine_spec_ne_rsrctab-.L__wine_spec_ne_header\n",     /* ne_rsrctab */
618
             get_asm_short_keyword() );
619
    output( "\t%s .L__wine_spec_ne_restab-.L__wine_spec_ne_header\n",      /* ne_restab */
620
             get_asm_short_keyword() );
621
    output( "\t%s .L__wine_spec_ne_modtab-.L__wine_spec_ne_header\n",      /* ne_modtab */
622
             get_asm_short_keyword() );
623
    output( "\t%s .L__wine_spec_ne_imptab-.L__wine_spec_ne_header\n",      /* ne_imptab */
624
             get_asm_short_keyword() );
625 626 627 628
    output( "\t.long 0\n" );                                   /* ne_nrestab */
    output( "\t%s 0\n", get_asm_short_keyword() );             /* ne_cmovent */
    output( "\t%s 0\n", get_asm_short_keyword() );             /* ne_align */
    output( "\t%s 0\n", get_asm_short_keyword() );             /* ne_cres */
629
    output( "\t.byte 0x02\n" );                                /* ne_exetyp = NE_OSFLAGS_WINDOWS */
630
    output( "\t.byte 0x08\n" );                                /* ne_flagsothers = NE_AFLAGS_FASTLOAD */
631 632 633 634
    output( "\t%s 0\n", get_asm_short_keyword() );             /* ne_pretthunks */
    output( "\t%s 0\n", get_asm_short_keyword() );             /* ne_psegrefbytes */
    output( "\t%s 0\n", get_asm_short_keyword() );             /* ne_swaparea */
    output( "\t%s 0\n", get_asm_short_keyword() );             /* ne_expver */
635

636
    /* segment table */
637

638
    output( "\n.L__wine_spec_ne_segtab:\n" );
639

640
    /* code segment entry */
641

642 643
    output( "\t%s .L__wine_spec_code_segment-.L__wine_spec_dos_header\n",  /* filepos */
             get_asm_short_keyword() );
644
    output( "\t%s .L__wine_spec_code_segment_end-.L__wine_spec_code_segment\n", /* size */
645
             get_asm_short_keyword() );
646
    output( "\t%s 0x2000\n", get_asm_short_keyword() ); /* flags = NE_SEGFLAGS_32BIT */
647
    output( "\t%s .L__wine_spec_code_segment_end-.L__wine_spec_code_segment\n", /* minsize */
648
             get_asm_short_keyword() );
649

650
    /* data segment entry */
651

652 653
    output( "\t%s .L__wine_spec_data_segment-.L__wine_spec_dos_header\n",  /* filepos */
             get_asm_short_keyword() );
654
    output( "\t%s .L__wine_spec_data_segment_end-.L__wine_spec_data_segment\n", /* size */
655
             get_asm_short_keyword() );
656
    output( "\t%s 0x0001\n", get_asm_short_keyword() ); /* flags = NE_SEGFLAGS_DATA */
657
    output( "\t%s .L__wine_spec_data_segment_end-.L__wine_spec_data_segment\n", /* minsize */
658
             get_asm_short_keyword() );
659 660 661

    /* resource directory */

662
    output_res16_directory( spec );
663 664 665

    /* resident names table */

666 667 668
    output( "\n\t.align %d\n", get_alignment(2) );
    output( ".L__wine_spec_ne_restab:\n" );
    output_resident_name( spec->dll_name, 0 );
669 670 671 672
    for (i = 1; i <= spec->limit; i++)
    {
        ORDDEF *odp = spec->ordinals[i];
        if (!odp || !odp->name[0]) continue;
673
        if (odp->flags & FLAG_EXPORT32) continue;
674
        output_resident_name( odp->name, i );
675
    }
676
    output( "\t.byte 0\n" );
677 678 679

    /* imported names table */

680 681 682 683
    output( "\n\t.align %d\n", get_alignment(2) );
    output( ".L__wine_spec_ne_modtab:\n" );
    output( ".L__wine_spec_ne_imptab:\n" );
    output( "\t.byte 0,0\n" );
684 685 686

    /* entry table */

687 688 689
    output( "\n.L__wine_spec_ne_enttab:\n" );
    output_entry_table( spec );
    output( ".L__wine_spec_ne_enttab_end:\n" );
690 691 692

    /* code segment */

693 694
    output( "\n\t.align %d\n", get_alignment(2) );
    output( ".L__wine_spec_code_segment:\n" );
695 696

    for ( i = 0; i < nb_funcs; i++ )
697
    {
698
        unsigned int arg_types[2];
699
        int nop_words, pos, argsize = 0;
700

701
        if ( typelist[i]->type == TYPE_PASCAL )
702
            argsize = get_function_argsize( typelist[i] );
703

704 705
        /* build the arg types bit fields */
        arg_types[0] = arg_types[1] = 0;
706
        for (j = pos = 0; j < typelist[i]->u.func.nb_args && pos < 20; j++, pos++)
707 708
        {
            int type = 0;
709
            switch (typelist[i]->u.func.args[j])
710
            {
711 712 713 714 715 716 717 718
            case ARG_WORD:   type = ARG16_WORD; break;
            case ARG_SWORD:  type = ARG16_SWORD; break;
            case ARG_SEGPTR: type = ARG16_LONG; break;
            case ARG_SEGSTR: type = ARG16_SEGSTR; break;
            case ARG_LONG:   type = ARG16_LONG; break;
            case ARG_PTR:    type = ARG16_PTR; break;
            case ARG_STR:    type = ARG16_STR; break;
            case ARG_WSTR:   type = ARG16_PTR; break;
719 720 721
            case ARG_FLOAT:  type = ARG16_LONG; break;
            case ARG_INT128: type = ARG16_PTR; break;
            case ARG_INT64:
722 723 724 725 726
            case ARG_DOUBLE:
                type = ARG16_LONG;
                arg_types[pos / 10] |= type << (3 * (pos % 10));
                pos++;
                break;
727
            }
728
            if (pos < 20) arg_types[pos / 10] |= type << (3 * (pos % 10));
729
        }
730 731
        if (typelist[i]->type == TYPE_VARARGS && pos < 20)
            arg_types[pos / 10] |= ARG16_VARARG << (3 * (pos % 10));
732

733 734 735
        output( ".L__wine_spec_callfrom16_%s:\n", get_callfrom16_name(typelist[i]) );
        output( "\tpushl $.L__wine_spec_call16_%s\n", get_relay_name(typelist[i]) );
        output( "\tlcall $0,$0\n" );
736 737 738

        if (typelist[i]->flags & FLAG_REGISTER)
        {
739
            nop_words = 4;
740 741 742
        }
        else if (typelist[i]->flags & FLAG_RET16)
        {
743 744
            output( "\torw %%ax,%%ax\n" );
            output( "\tnop\n" );  /* so that the lretw is aligned */
745
            nop_words = 2;
746
        }
747
        else
748
        {
749 750
            output( "\tshld $16,%%eax,%%edx\n" );
            output( "\torl %%eax,%%eax\n" );
751 752 753 754 755
            nop_words = 1;
        }

        if (argsize)
        {
756
            output( "\tlretw $%u\n", argsize );
757
            nop_words--;
758
        }
759
        else output( "\tlretw\n" );
760

761
        if (nop_words) output( "\t%s\n", nop_sequence[nop_words-1] );
762

763 764
        /* the movl is here so that the code contains only valid instructions, */
        /* it's never actually executed, we only care about the arg_types[] values */
765 766
        output( "\t%s 0x86c7\n", get_asm_short_keyword() );
        output( "\t.long 0x%08x,0x%08x\n", arg_types[0], arg_types[1] );
767 768
    }

769
    for (i = 0; i <= spec->limit; i++)
770
    {
771
        ORDDEF *odp = spec->ordinals[i];
772
        if (!odp || !is_function( odp )) continue;
773 774 775
        output( ".L__wine_%s_%u:\n", make_c_identifier(spec->dll_name), i );
        output( "\tpushw %%bp\n" );
        output( "\tpushl $%s\n",
776
                 asm_name( odp->type == TYPE_STUB ? get_stub_name( odp, spec ) : odp->link_name ));
777
        output( "\tcallw .L__wine_spec_callfrom16_%s\n", get_callfrom16_name( odp ) );
778
    }
779
    output( ".L__wine_spec_code_segment_end:\n" );
780

781
    /* data segment */
782

783 784
    output( "\n.L__wine_spec_data_segment:\n" );
    output( "\t.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n" );  /* instance data */
785 786 787 788
    for (i = 0; i <= spec->limit; i++)
    {
        ORDDEF *odp = spec->ordinals[i];
        if (!odp || odp->type != TYPE_VARIABLE) continue;
789 790
        output( ".L__wine_%s_%u:\n", make_c_identifier(spec->dll_name), i );
        output( "\t.long " );
791
        for (j = 0; j < odp->u.var.n_values-1; j++)
792 793
            output( "0x%08x,", odp->u.var.values[j] );
        output( "0x%08x\n", odp->u.var.values[j] );
794
    }
795
    output( ".L__wine_spec_data_segment_end:\n" );
796

797
    /* resource data */
798

799
    if (spec->nb_resources)
800
    {
801 802
        output( "\n.L__wine_spec_resource_data:\n" );
        output_res16_data( spec );
803
    }
804

805
    output( "\t.byte 0\n" );  /* make sure the last symbol points to something */
806

807
    /* relay functions */
808

809 810 811
    nb_funcs = sort_func_list( typelist, nb_funcs, relay_type_compare );
    if (nb_funcs)
    {
812 813 814 815 816 817
        output( "\n/* relay functions */\n\n" );
        output( "\t.text\n" );
        for ( i = 0; i < nb_funcs; i++ ) output_call16_function( typelist[i] );
        output( "\t.data\n" );
        output( "wine_ldt_copy_ptr:\n" );
        output( "\t.long %s\n", asm_name("wine_ldt_copy") );
818
    }
819

820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841
    free( typelist );
}


/*******************************************************************
 *         output_spec16_file
 *
 * Output the complete data for a spec 16-bit file.
 */
void output_spec16_file( DLLSPEC *spec16 )
{
    DLLSPEC *spec32 = alloc_dll_spec();

    resolve_imports( spec16 );
    add_16bit_exports( spec32, spec16 );

    output_standard_file_header();
    output_module( spec32 );
    output_module16( spec16 );
    output_stubs( spec16 );
    output_exports( spec32 );
    output_imports( spec16 );
842
    if (is_undefined( "__wine_call_from_16" )) output_asm_relays16();
843 844 845 846 847 848
    if (spec16->main_module)
    {
        output( "\n\t%s\n", get_asm_string_section() );
        output( ".L__wine_spec_main_module:\n" );
        output( "\t%s \"%s\"\n", get_asm_string_keyword(), spec16->main_module );
    }
849 850
    output_gnu_stack_note();
    free_dll_spec( spec32 );
851
}
852 853 854 855 856 857

/*******************************************************************
 *         output_fake_module16
 *
 * Create a fake 16-bit binary module.
 */
858
void output_fake_module16( DLLSPEC *spec )
859
{
860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993
    static const unsigned char code_segment[] = { 0x90, 0xc3 };
    static const unsigned char data_segment[16] = { 0 };
    static const char fakedll_signature[] = "Wine placeholder DLL";
    const unsigned int cseg = 2;
    const unsigned int lfanew = (0x40 + sizeof(fakedll_signature) + 15) & ~15;
    const unsigned int segtab = lfanew + 0x40;

    unsigned int i, rsrctab, restab, namelen, modtab, imptab, enttab, cbenttab, codeseg, dataseg, rsrcdata;

    init_output_buffer();

    rsrctab = lfanew;
    restab = segtab + 8 * cseg;
    if (spec->nb_resources)
    {
        output_bin_res16_directory( spec, 0 );
        align_output( 2 );
        rsrctab = restab;
        restab += output_buffer_pos;
        free( output_buffer );
        init_output_buffer();
    }

    namelen  = strlen( spec->dll_name );
    modtab   = restab + ((namelen + 3) & ~1);
    imptab   = modtab;
    enttab   = modtab + 2;
    cbenttab = 1;
    codeseg  = (enttab + cbenttab + 1) & ~1;
    dataseg  = codeseg + sizeof(code_segment);
    rsrcdata = dataseg + sizeof(data_segment);

    init_output_buffer();

    put_word( 0x5a4d );       /* e_magic */
    put_word( 0x40 );         /* e_cblp */
    put_word( 0x01 );         /* e_cp */
    put_word( 0 );            /* e_crlc */
    put_word( lfanew / 16 );  /* e_cparhdr */
    put_word( 0x0000 );       /* e_minalloc */
    put_word( 0xffff );       /* e_maxalloc */
    put_word( 0x0000 );       /* e_ss */
    put_word( 0x00b8 );       /* e_sp */
    put_word( 0 );            /* e_csum */
    put_word( 0 );            /* e_ip */
    put_word( 0 );            /* e_cs */
    put_word( lfanew );       /* e_lfarlc */
    put_word( 0 );            /* e_ovno */
    put_dword( 0 );           /* e_res */
    put_dword( 0 );
    put_word( 0 );            /* e_oemid */
    put_word( 0 );            /* e_oeminfo */
    put_dword( 0 );           /* e_res2 */
    put_dword( 0 );
    put_dword( 0 );
    put_dword( 0 );
    put_dword( 0 );
    put_dword( lfanew );

    put_data( fakedll_signature, sizeof(fakedll_signature) );
    align_output( 16 );

    put_word( 0x454e );                    /* ne_magic */
    put_byte( 0 );                         /* ne_ver */
    put_byte( 0 );                         /* ne_rev */
    put_word( enttab - lfanew );           /* ne_enttab */
    put_word( cbenttab );                  /* ne_cbenttab */
    put_dword( 0 );                        /* ne_crc */
    put_word( NE_FFLAGS_SINGLEDATA |       /* ne_flags */
              ((spec->characteristics & IMAGE_FILE_DLL) ? NE_FFLAGS_LIBMODULE : 0) );
    put_word( 2 );                         /* ne_autodata */
    put_word( spec->heap_size );           /* ne_heap */
    put_word( 0 );                         /* ne_stack */
    put_word( 0 ); put_word( 0 );          /* ne_csip */
    put_word( 0 ); put_word( 2 );          /* ne_sssp */
    put_word( cseg );                      /* ne_cseg */
    put_word( 0 );                         /* ne_cmod */
    put_word( 0 );                         /* ne_cbnrestab */
    put_word( segtab - lfanew );           /* ne_segtab */
    put_word( rsrctab - lfanew );          /* ne_rsrctab */
    put_word( restab - lfanew );           /* ne_restab */
    put_word( modtab - lfanew );           /* ne_modtab */
    put_word( imptab - lfanew );           /* ne_imptab */
    put_dword( 0 );                        /* ne_nrestab */
    put_word( 0 );                         /* ne_cmovent */
    put_word( 0 );                         /* ne_align */
    put_word( 0 );                         /* ne_cres */
    put_byte( 2 /*NE_OSFLAGS_WINDOWS*/ );  /* ne_exetyp */
    put_byte( 8 /*NE_AFLAGS_FASTLOAD*/ );  /* ne_flagsothers */
    put_word( 0 );                         /* ne_pretthunks */
    put_word( 0 );                         /* ne_psegrefbytes */
    put_word( 0 );                         /* ne_swaparea */
    put_word( 0 );                         /* ne_expver */

    /* segment table */
    put_word( codeseg );
    put_word( sizeof(code_segment) );
    put_word( 0x2000 /* NE_SEGFLAGS_32BIT */ );
    put_word( sizeof(code_segment) );
    put_word( dataseg );
    put_word( sizeof(data_segment) );
    put_word( 0x0001 /* NE_SEGFLAGS_DATA */ );
    put_word( sizeof(data_segment) );

    /* resource directory */
    if (spec->nb_resources)
    {
        output_bin_res16_directory( spec, rsrcdata );
        align_output( 2 );
    }

    /* resident names table */
    put_byte( namelen );
    for (i = 0; i < namelen; i++) put_byte( toupper(spec->dll_name[i]) );
    put_byte( 0 );
    align_output( 2 );

    /* imported names table */
    put_word( 0 );

    /* entry table */
    put_byte( 0 );
    align_output( 2 );

    /* code segment */
    put_data( code_segment, sizeof(code_segment) );

    /* data segment */
    put_data( data_segment, sizeof(data_segment) );

    /* resource data */
    output_bin_res16_data( spec );

    flush_output_buffer();
994
}