spec16.c 30 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
#include <assert.h>
#include <ctype.h>

31
#include "wine/winbase16.h"
32 33 34

#include "build.h"

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
/* 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 )
{
    return (odp->type == TYPE_CDECL ||
            odp->type == TYPE_PASCAL ||
            odp->type == TYPE_VARARGS ||
            odp->type == TYPE_STUB);
}
51

52
/*******************************************************************
53
 *         output_entries
54
 *
55
 * Output entries for individual symbols in the entry table.
56
 */
57
static void output_entries( FILE *outfile, DLLSPEC *spec, int first, int count )
58
{
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
    int i;

    for (i = 0; i < count; i++)
    {
        ORDDEF *odp = spec->ordinals[first + i];
        fprintf( outfile, "\t.byte 0x03\n" );  /* flags: exported & public data */
        switch (odp->type)
        {
        case TYPE_CDECL:
        case TYPE_PASCAL:
        case TYPE_VARARGS:
        case TYPE_STUB:
            fprintf( outfile, "\t%s .L__wine_%s_%u-.L__wine_spec_code_segment\n",
                     get_asm_short_keyword(),
                     make_c_identifier(spec->dll_name), first + i );
            break;
        case TYPE_VARIABLE:
            fprintf( outfile, "\t%s .L__wine_%s_%u-.L__wine_spec_data_segment\n",
                     get_asm_short_keyword(),
                     make_c_identifier(spec->dll_name), first + i );
            break;
        case TYPE_ABS:
            fprintf( outfile, "\t%s 0x%04x  /* %s */\n",
                     get_asm_short_keyword(), odp->u.abs.value, odp->name );
            break;
        default:
            assert(0);
        }
    }
88 89 90
}


91
/*******************************************************************
92
 *         output_entry_table
93
 */
94
static void output_entry_table( FILE *outfile, DLLSPEC *spec )
95
{
96
    int i, prev = 0, prev_sel = -1, bundle_count = 0;
97

98
    for (i = 1; i <= spec->limit; i++)
99 100
    {
        int selector = 0;
101
        ORDDEF *odp = spec->ordinals[i];
102 103
        if (!odp) continue;

104 105
        switch (odp->type)
        {
106 107
        case TYPE_CDECL:
        case TYPE_PASCAL:
108
        case TYPE_VARARGS:
109 110 111
        case TYPE_STUB:
            selector = 1;  /* Code selector */
            break;
112
        case TYPE_VARIABLE:
113 114 115 116 117 118
            selector = 2;  /* Data selector */
            break;
        case TYPE_ABS:
            selector = 0xfe;  /* Constant selector */
            break;
        default:
119
            continue;
120 121
        }

122
        if (prev + 1 != i || prev_sel != selector || bundle_count == 255)
123
        {
124 125
            /* need to start a new bundle */

126 127 128 129 130 131 132 133 134
            /* flush previous bundle */
            if (bundle_count)
            {
                fprintf( outfile, "\t/* %s.%d - %s.%d */\n",
                         spec->dll_name, prev - bundle_count + 1, spec->dll_name, prev );
                fprintf( outfile, "\t.byte 0x%02x,0x%02x\n", bundle_count, prev_sel );
                output_entries( outfile, spec, prev - bundle_count + 1, bundle_count );
            }

135 136 137 138 139
            if (prev + 1 != i)
            {
                int skip = i - (prev + 1);
                while (skip > 255)
                {
140
                    fprintf( outfile, "\t.byte 0xff,0x00\n" );
141 142
                    skip -= 255;
                }
143
                fprintf( outfile, "\t.byte 0x%02x,0x00\n", skip );
144
            }
145

146
            bundle_count = 0;
147 148
            prev_sel = selector;
        }
149
        bundle_count++;
150
        prev = i;
151
    }
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173

    /* flush last bundle */
    if (bundle_count)
    {
        fprintf( outfile, "\t.byte 0x%02x,0x%02x\n", bundle_count, prev_sel );
        output_entries( outfile, spec, prev - bundle_count + 1, bundle_count );
    }
    fprintf( outfile, "\t.byte 0x00\n" );
}


/*******************************************************************
 *         output_resident_name
 */
static void output_resident_name( FILE *outfile, const char *string, int ordinal )
{
    unsigned int i, len = strlen(string);

    fprintf( outfile, "\t.byte 0x%02x", len );
    for (i = 0; i < len; i++) fprintf( outfile, ",0x%02x", (unsigned char)toupper(string[i]) );
    fprintf( outfile, " /* %s */\n", string );
    fprintf( outfile, "\t%s %u\n", get_asm_short_keyword(), ordinal );
174
}
175 176


177
/*******************************************************************
178
 *         get_callfrom16_name
179
 */
180
static const char *get_callfrom16_name( const ORDDEF *odp )
181
{
182 183 184 185 186 187 188 189 190 191
    static char buffer[80];

    sprintf( buffer, "%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",
             odp->u.func.arg_types );
    return buffer;
}
192

193 194 195 196 197 198 199 200 201 202

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

    switch(odp->type)
203
    {
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
    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);
    }
    strcat( buffer, odp->u.func.arg_types );
    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';
223
    }
224 225
    if (odp->flags & FLAG_REGISTER) strcat( buffer, "_regs" );
    return buffer;
226 227 228
}


229
/*******************************************************************
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
 *         get_function_argsize
 */
static int get_function_argsize( const ORDDEF *odp )
{
    const char *args;
    int argsize = 0;

    for (args = odp->u.func.arg_types; *args; args++)
    {
        switch (*args)
        {
        case 'w':  /* word */
        case 's':  /* s_word */
            argsize += 2;
            break;
        case 'l':  /* long or segmented pointer */
        case 'T':  /* segmented pointer to null-terminated string */
        case 'p':  /* linear pointer */
        case 't':  /* linear pointer to null-terminated string */
            argsize += 4;
            break;
        default:
            assert(0);
        }
    }
    return argsize;
}


/*******************************************************************
 *         output_call16_function
261
 *
262
 * Build a 16-bit-to-Wine callback glue function.
263
 *
264
 * The generated routines are intended to be used as argument conversion
265 266 267
 * routines to be called by the CallFrom16... core. Thus, the prototypes of
 * the generated routines are (see also CallFrom16):
 *
268 269 270
 *  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 );
271
 *
272 273
 * 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,
274 275 276 277
 * '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
278
 * to by 'args'); the offsets of the single argument values are computed
279 280
 * according to the calling convention and the argument types.  Then, the
 * 32-bit entry point is called with these arguments.
281
 *
282
 * For register functions, the arguments (if present) are converted just
283
 * the same as for normal functions, but in addition the CONTEXT86 pointer
284 285
 * filled with the current register values is passed to the 32-bit routine.
 */
286
static void output_call16_function( FILE *outfile, ORDDEF *odp )
287
{
288
    char name[256];
289
    int i, pos, stack_words;
290 291 292 293 294 295 296 297 298 299 300
    const char *args = odp->u.func.arg_types;
    int argsize = get_function_argsize( odp );
    int needs_ldt = strchr( args, 'p' ) || strchr( args, 't' );

    sprintf( name, ".L__wine_spec_call16_%s", get_relay_name(odp) );

    fprintf( outfile, "\t.align %d\n", get_alignment(4) );
    fprintf( outfile, "\t%s\n", func_declaration(name) );
    fprintf( outfile, "%s:\n", name );
    fprintf( outfile, "\tpushl %%ebp\n" );
    fprintf( outfile, "\tmovl %%esp,%%ebp\n" );
301
    stack_words = 2;
302
    if (needs_ldt)
303
    {
304
        fprintf( outfile, "\tpushl %%esi\n" );
305
        stack_words++;
306
        if (UsePIC)
307
        {
308 309
            fprintf( outfile, "\tcall %s\n", asm_name("__wine_spec_get_pc_thunk_eax") );
            fprintf( outfile, "1:\tmovl wine_ldt_copy_ptr-1b(%%eax),%%esi\n" );
310
        }
311 312 313
        else
            fprintf( outfile, "\tmovl $%s,%%esi\n", asm_name("wine_ldt_copy") );
    }
314

315 316 317 318 319
    /* preserve 16-byte stack alignment */
    stack_words += strlen(args);
    if ((odp->flags & FLAG_REGISTER) || (odp->type == TYPE_VARARGS)) stack_words++;
    if (stack_words % 4) fprintf( outfile, "\tsubl $%d,%%esp\n", 16 - 4 * (stack_words % 4) );

320 321
    if (args[0] || odp->type == TYPE_VARARGS)
        fprintf( outfile, "\tmovl 12(%%ebp),%%ecx\n" );  /* args */
322

323
    if (odp->flags & FLAG_REGISTER)
324
    {
325
        fprintf( outfile, "\tpushl 16(%%ebp)\n" );  /* context */
326
    }
327 328 329 330 331 332 333 334
    else if (odp->type == TYPE_VARARGS)
    {
        fprintf( outfile, "\tleal %d(%%ecx),%%eax\n", argsize );
        fprintf( outfile, "\tpushl %%eax\n" );  /* va_list16 */
    }

    pos = (odp->type == TYPE_PASCAL) ? 0 : argsize;
    for (i = strlen(args) - 1; i >= 0; i--)
335 336 337 338
    {
        switch (args[i])
        {
        case 'w':  /* word */
339 340 341 342
            if (odp->type != TYPE_PASCAL) pos -= 2;
            fprintf( outfile, "\tmovzwl %d(%%ecx),%%eax\n", pos );
            fprintf( outfile, "\tpushl %%eax\n" );
            if (odp->type == TYPE_PASCAL) pos += 2;
343 344 345
            break;

        case 's':  /* s_word */
346 347 348 349
            if (odp->type != TYPE_PASCAL) pos -= 2;
            fprintf( outfile, "\tmovswl %d(%%ecx),%%eax\n", pos );
            fprintf( outfile, "\tpushl %%eax\n" );
            if (odp->type == TYPE_PASCAL) pos += 2;
350 351 352 353
            break;

        case 'l':  /* long or segmented pointer */
        case 'T':  /* segmented pointer to null-terminated string */
354 355 356
            if (odp->type != TYPE_PASCAL) pos -= 4;
            fprintf( outfile, "\tpushl %d(%%ecx)\n", pos );
            if (odp->type == TYPE_PASCAL) pos += 4;
357 358 359 360
            break;

        case 'p':  /* linear pointer */
        case 't':  /* linear pointer to null-terminated string */
361 362 363 364 365 366 367
            if (odp->type != TYPE_PASCAL) pos -= 4;
            fprintf( outfile, "\tmovzwl %d(%%ecx),%%edx\n", pos + 2 ); /* sel */
            fprintf( outfile, "\tshr $3,%%edx\n" );
            fprintf( outfile, "\tmovzwl %d(%%ecx),%%eax\n", pos ); /* offset */
            fprintf( outfile, "\taddl (%%esi,%%edx,4),%%eax\n" );
            fprintf( outfile, "\tpushl %%eax\n" );
            if (odp->type == TYPE_PASCAL) pos += 4;
368 369 370
            break;

        default:
371
            assert(0);
372 373 374
        }
    }

375
    fprintf( outfile, "\tcall *8(%%ebp)\n" );
376

377
    if (needs_ldt) fprintf( outfile, "\tmovl -4(%%ebp),%%esi\n" );
378

379 380 381
    fprintf( outfile, "\tleave\n" );
    fprintf( outfile, "\tret\n" );
    output_function_size( outfile, name );
382 383 384 385
}


/*******************************************************************
386 387 388
 *         callfrom16_type_compare
 *
 * Compare two callfrom16 sequences.
389
 */
390
static int callfrom16_type_compare( const void *e1, const void *e2 )
391
{
392 393
    const ORDDEF *odp1 = *(const ORDDEF * const *)e1;
    const ORDDEF *odp2 = *(const ORDDEF * const *)e2;
394
    int retval;
395 396
    int type1 = odp1->type;
    int type2 = odp2->type;
397

398 399
    if (type1 == TYPE_STUB) type1 = TYPE_CDECL;
    if (type2 == TYPE_STUB) type2 = TYPE_CDECL;
400

401
    if ((retval = type1 - type2) != 0) return retval;
402

403 404
    type1 = odp1->flags & (FLAG_RET16|FLAG_REGISTER);
    type2 = odp2->flags & (FLAG_RET16|FLAG_REGISTER);
405

406
    if ((retval = type1 - type2) != 0) return retval;
407

408
    return strcmp( odp1->u.func.arg_types, odp2->u.func.arg_types );
409 410 411
}


412
/*******************************************************************
413
 *         relay_type_compare
414
 *
415 416 417
 * 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 )
418
{
419 420 421
    const ORDDEF *odp1 = *(const ORDDEF * const *)e1;
    const ORDDEF *odp2 = *(const ORDDEF * const *)e2;
    char name1[80];
422

423 424
    strcpy( name1, get_relay_name(odp1) );
    return strcmp( name1, get_relay_name(odp2) );
425 426 427
}


428
/*******************************************************************
429
 *         sort_func_list
430
 *
431
 * Sort a list of functions, removing duplicates.
432
 */
433 434
static int sort_func_list( ORDDEF **list, int count,
                           int (*compare)(const void *, const void *) )
435
{
436
    int i, j;
437

438
    qsort( list, count, sizeof(*list), compare );
439

440
    for (i = j = 0; i < count; i++)
441
    {
442
        if (compare( &list[j], &list[i] )) list[++j] = list[i];
443
    }
444 445
    return j + 1;
}
Dmitry Timoshkov's avatar
Dmitry Timoshkov committed
446

447

448 449 450 451 452 453 454 455
/*******************************************************************
 *         output_init_code
 *
 * Output the dll initialization code.
 */
static void output_init_code( FILE *outfile, const DLLSPEC *spec, const char *header_name )
{
    char name[80];
456

457
    sprintf( name, ".L__wine_spec_%s_init", make_c_identifier(spec->dll_name) );
458

459 460 461 462 463
    fprintf( outfile, "\n/* dll initialization code */\n\n" );
    fprintf( outfile, "\t.text\n" );
    fprintf( outfile, "\t.align 4\n" );
    fprintf( outfile, "\t%s\n", func_declaration(name) );
    fprintf( outfile, "%s:\n", name );
464
    fprintf( outfile, "subl $4,%%esp\n" );
465
    if (UsePIC)
466
    {
467 468 469 470 471 472 473 474 475 476
        fprintf( outfile, "\tcall %s\n", asm_name("__wine_spec_get_pc_thunk_eax") );
        fprintf( outfile, "1:\tleal .L__wine_spec_file_name-1b(%%eax),%%ecx\n" );
        fprintf( outfile, "\tpushl %%ecx\n" );
        fprintf( outfile, "\tleal %s-1b(%%eax),%%ecx\n", header_name );
        fprintf( outfile, "\tpushl %%ecx\n" );
    }
    else
    {
        fprintf( outfile, "\tpushl $.L__wine_spec_file_name\n" );
        fprintf( outfile, "\tpushl $%s\n", header_name );
477
    }
478
    fprintf( outfile, "\tcall %s\n", asm_name("__wine_dll_register_16") );
479
    fprintf( outfile, "\taddl $12,%%esp\n" );
480 481
    fprintf( outfile, "\tret\n" );
    output_function_size( outfile, name );
482

483
    sprintf( name, ".L__wine_spec_%s_fini", make_c_identifier(spec->dll_name) );
484

485 486 487
    fprintf( outfile, "\t.align 4\n" );
    fprintf( outfile, "\t%s\n", func_declaration(name) );
    fprintf( outfile, "%s:\n", name );
488
    fprintf( outfile, "subl $8,%%esp\n" );
489
    if (UsePIC)
490
    {
491 492 493
        fprintf( outfile, "\tcall %s\n", asm_name("__wine_spec_get_pc_thunk_eax") );
        fprintf( outfile, "1:\tleal %s-1b(%%eax),%%ecx\n", header_name );
        fprintf( outfile, "\tpushl %%ecx\n" );
494
    }
495
    else
496
    {
497
        fprintf( outfile, "\tpushl $%s\n", header_name );
498
    }
499
    fprintf( outfile, "\tcall %s\n", asm_name("__wine_dll_unregister_16") );
500
    fprintf( outfile, "\taddl $12,%%esp\n" );
501 502
    fprintf( outfile, "\tret\n" );
    output_function_size( outfile, name );
503

504
    if (target_platform == PLATFORM_APPLE)
505
    {
506 507 508 509 510 511
        fprintf( outfile, "\t.mod_init_func\n" );
        fprintf( outfile, "\t.align %d\n", get_alignment(4) );
        fprintf( outfile, "\t.long .L__wine_spec_%s_init\n", make_c_identifier(spec->dll_name) );
        fprintf( outfile, "\t.mod_term_func\n" );
        fprintf( outfile, "\t.align %d\n", get_alignment(4) );
        fprintf( outfile, "\t.long .L__wine_spec_%s_fini\n", make_c_identifier(spec->dll_name) );
512
    }
513 514 515 516 517 518 519 520
    else
    {
        fprintf( outfile, "\t.section \".init\",\"ax\"\n" );
        fprintf( outfile, "\tcall .L__wine_spec_%s_init\n", make_c_identifier(spec->dll_name) );
        fprintf( outfile, "\t.section \".fini\",\"ax\"\n" );
        fprintf( outfile, "\tcall .L__wine_spec_%s_fini\n", make_c_identifier(spec->dll_name) );
    }
}
521

522

523 524 525 526 527 528 529 530 531 532
/*******************************************************************
 *         BuildSpec16File
 *
 * Build a Win16 assembly file from a spec file.
 */
void BuildSpec16File( FILE *outfile, DLLSPEC *spec )
{
    ORDDEF **typelist;
    int i, j, nb_funcs;
    char header_name[256];
533

534
    /* File header */
535

536
    output_standard_file_header( outfile );
537

538 539 540 541 542 543
    if (!spec->dll_name)  /* set default name from file name */
    {
        char *p;
        spec->dll_name = xstrdup( spec->file_name );
        if ((p = strrchr( spec->dll_name, '.' ))) *p = 0;
    }
544

545
    /* Build sorted list of all argument types, without duplicates */
546

547
    typelist = xmalloc( (spec->limit + 1) * sizeof(*typelist) );
548

549
    for (i = nb_funcs = 0; i <= spec->limit; i++)
550 551
    {
        ORDDEF *odp = spec->ordinals[i];
552 553
        if (!odp) continue;
        if (is_function( odp )) typelist[nb_funcs++] = odp;
554 555
    }

556
    nb_funcs = sort_func_list( typelist, nb_funcs, callfrom16_type_compare );
557

558
    /* Output the module structure */
559

560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 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 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
    sprintf( header_name, "__wine_spec_%s_dos_header", make_c_identifier(spec->dll_name) );
    fprintf( outfile, "\n/* module data */\n\n" );
    fprintf( outfile, "\t.data\n" );
    fprintf( outfile, "\t.align %d\n", get_alignment(4) );
    fprintf( outfile, "%s:\n", header_name );
    fprintf( outfile, "\t%s 0x%04x\n", get_asm_short_keyword(),                      /* e_magic */
             IMAGE_DOS_SIGNATURE );
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* e_cblp */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* e_cp */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* e_crlc */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* e_cparhdr */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* e_minalloc */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* e_maxalloc */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* e_ss */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* e_sp */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* e_csum */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* e_ip */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* e_cs */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* e_lfarlc */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* e_ovno */
    fprintf( outfile, "\t%s 0,0,0,0\n", get_asm_short_keyword() );                   /* e_res */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* e_oemid */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* e_oeminfo */
    fprintf( outfile, "\t%s 0,0,0,0,0,0,0,0,0,0\n", get_asm_short_keyword() );       /* e_res2 */
    fprintf( outfile, "\t.long .L__wine_spec_ne_header-%s\n", header_name );         /* e_lfanew */

    fprintf( outfile, ".L__wine_spec_ne_header:\n" );
    fprintf( outfile, "\t%s 0x%04x\n", get_asm_short_keyword(),                      /* ne_magic */
             IMAGE_OS2_SIGNATURE );
    fprintf( outfile, "\t.byte 0\n" );                                               /* ne_ver */
    fprintf( outfile, "\t.byte 0\n" );                                               /* ne_rev */
    fprintf( outfile, "\t%s .L__wine_spec_ne_enttab-.L__wine_spec_ne_header\n",      /* ne_enttab */
             get_asm_short_keyword() );
    fprintf( outfile, "\t%s .L__wine_spec_ne_enttab_end-.L__wine_spec_ne_enttab\n",  /* ne_cbenttab */
             get_asm_short_keyword() );
    fprintf( outfile, "\t.long 0\n" );                                               /* ne_crc */
    fprintf( outfile, "\t%s 0x%04x\n", get_asm_short_keyword(),                      /* ne_flags */
             NE_FFLAGS_SINGLEDATA | NE_FFLAGS_LIBMODULE );
    fprintf( outfile, "\t%s 2\n", get_asm_short_keyword() );                         /* ne_autodata */
    fprintf( outfile, "\t%s %u\n", get_asm_short_keyword(), spec->heap_size );       /* ne_heap */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* ne_stack */
    fprintf( outfile, "\t.long 0\n" );                                               /* ne_csip */
    fprintf( outfile, "\t.long 0\n" );                                               /* ne_sssp */
    fprintf( outfile, "\t%s 2\n", get_asm_short_keyword() );                         /* ne_cseg */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* ne_cmod */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );                         /* ne_cbnrestab */
    fprintf( outfile, "\t%s .L__wine_spec_ne_segtab-.L__wine_spec_ne_header\n",      /* ne_segtab */
             get_asm_short_keyword() );
    fprintf( outfile, "\t%s .L__wine_spec_ne_rsrctab-.L__wine_spec_ne_header\n",     /* ne_rsrctab */
             get_asm_short_keyword() );
    fprintf( outfile, "\t%s .L__wine_spec_ne_restab-.L__wine_spec_ne_header\n",      /* ne_restab */
             get_asm_short_keyword() );
    fprintf( outfile, "\t%s .L__wine_spec_ne_modtab-.L__wine_spec_ne_header\n",      /* ne_modtab */
             get_asm_short_keyword() );
    fprintf( outfile, "\t%s .L__wine_spec_ne_imptab-.L__wine_spec_ne_header\n",      /* ne_imptab */
             get_asm_short_keyword() );
    fprintf( outfile, "\t.long 0\n" );                                   /* ne_nrestab */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );             /* ne_cmovent */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );             /* ne_align */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );             /* ne_cres */
    fprintf( outfile, "\t.byte 0x%02x\n", NE_OSFLAGS_WINDOWS );          /* ne_exetyp */
    fprintf( outfile, "\t.byte 0x%02x\n", NE_AFLAGS_FASTLOAD );          /* ne_flagsothers */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );             /* ne_pretthunks */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );             /* ne_psegrefbytes */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );             /* ne_swaparea */
    fprintf( outfile, "\t%s 0\n", get_asm_short_keyword() );             /* ne_expver */
626

627
    /* segment table */
628

629
    fprintf( outfile, "\n.L__wine_spec_ne_segtab:\n" );
630

631
    /* code segment entry */
632

633 634 635 636 637 638 639
    fprintf( outfile, "\t%s .L__wine_spec_code_segment-%s\n",  /* filepos */
             get_asm_short_keyword(), header_name );
    fprintf( outfile, "\t%s .L__wine_spec_code_segment_end-.L__wine_spec_code_segment\n", /* size */
             get_asm_short_keyword() );
    fprintf( outfile, "\t%s 0x%04x\n", get_asm_short_keyword(), NE_SEGFLAGS_32BIT );      /* flags */
    fprintf( outfile, "\t%s .L__wine_spec_code_segment_end-.L__wine_spec_code_segment\n", /* minsize */
             get_asm_short_keyword() );
640

641
    /* data segment entry */
642

643 644 645 646 647 648 649
    fprintf( outfile, "\t%s .L__wine_spec_data_segment-%s\n",  /* filepos */
             get_asm_short_keyword(), header_name );
    fprintf( outfile, "\t%s .L__wine_spec_data_segment_end-.L__wine_spec_data_segment\n", /* size */
             get_asm_short_keyword() );
    fprintf( outfile, "\t%s 0x%04x\n", get_asm_short_keyword(), NE_SEGFLAGS_DATA );      /* flags */
    fprintf( outfile, "\t%s .L__wine_spec_data_segment_end-.L__wine_spec_data_segment\n", /* minsize */
             get_asm_short_keyword() );
650 651 652

    /* resource directory */

653
    output_res16_directory( outfile, spec, header_name );
654 655 656

    /* resident names table */

657 658 659
    fprintf( outfile, "\n\t.align %d\n", get_alignment(2) );
    fprintf( outfile, ".L__wine_spec_ne_restab:\n" );
    output_resident_name( outfile, spec->dll_name, 0 );
660 661 662 663
    for (i = 1; i <= spec->limit; i++)
    {
        ORDDEF *odp = spec->ordinals[i];
        if (!odp || !odp->name[0]) continue;
664
        output_resident_name( outfile, odp->name, i );
665
    }
666
    fprintf( outfile, "\t.byte 0\n" );
667 668 669

    /* imported names table */

670 671 672 673
    fprintf( outfile, "\n\t.align %d\n", get_alignment(2) );
    fprintf( outfile, ".L__wine_spec_ne_modtab:\n" );
    fprintf( outfile, ".L__wine_spec_ne_imptab:\n" );
    fprintf( outfile, "\t.byte 0,0\n" );
674 675 676

    /* entry table */

677 678 679
    fprintf( outfile, "\n.L__wine_spec_ne_enttab:\n" );
    output_entry_table( outfile, spec );
    fprintf( outfile, ".L__wine_spec_ne_enttab_end:\n" );
680 681 682

    /* code segment */

683 684 685 686
    fprintf( outfile, "\n\t.align %d\n", get_alignment(2) );
    fprintf( outfile, ".L__wine_spec_code_segment:\n" );

    for ( i = 0; i < nb_funcs; i++ )
687
    {
688
        unsigned int arg_types[2];
689
        int j, nop_words, argsize = 0;
690

691
        if ( typelist[i]->type == TYPE_PASCAL )
692
            argsize = get_function_argsize( typelist[i] );
693

694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
        /* build the arg types bit fields */
        arg_types[0] = arg_types[1] = 0;
        for (j = 0; typelist[i]->u.func.arg_types[j]; j++)
        {
            int type = 0;
            switch(typelist[i]->u.func.arg_types[j])
            {
            case 'w': type = ARG_WORD; break;
            case 's': type = ARG_SWORD; break;
            case 'l': type = ARG_LONG; break;
            case 'p': type = ARG_PTR; break;
            case 't': type = ARG_STR; break;
            case 'T': type = ARG_SEGSTR; break;
            }
            arg_types[j / 10] |= type << (3 * (j % 10));
        }
710
        if (typelist[i]->type == TYPE_VARARGS) arg_types[j / 10] |= ARG_VARARG << (3 * (j % 10));
711

712 713 714
        fprintf( outfile, ".L__wine_spec_callfrom16_%s:\n", get_callfrom16_name(typelist[i]) );
        fprintf( outfile, "\tpushl $.L__wine_spec_call16_%s\n", get_relay_name(typelist[i]) );
        fprintf( outfile, "\tlcall $0,$0\n" );
715 716 717

        if (typelist[i]->flags & FLAG_REGISTER)
        {
718
            nop_words = 4;
719 720 721
        }
        else if (typelist[i]->flags & FLAG_RET16)
        {
722 723 724
            fprintf( outfile, "\torw %%ax,%%ax\n" );
            fprintf( outfile, "\tnop\n" );  /* so that the lretw is aligned */
            nop_words = 2;
725
        }
726
        else
727
        {
728 729
            fprintf( outfile, "\tshld $16,%%eax,%%edx\n" );
            fprintf( outfile, "\torl %%eax,%%eax\n" );
730 731 732 733 734
            nop_words = 1;
        }

        if (argsize)
        {
735
            fprintf( outfile, "\tlretw $%u\n", argsize );
736
            nop_words--;
737
        }
738
        else fprintf( outfile, "\tlretw\n" );
739 740 741

        if (nop_words) fprintf( outfile, "\t%s\n", nop_sequence[nop_words-1] );

742 743
        /* 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 */
744 745
        fprintf( outfile, "\t%s 0x86c7\n", get_asm_short_keyword() );
        fprintf( outfile, "\t.long 0x%08x,0x%08x\n", arg_types[0], arg_types[1] );
746 747
    }

748
    for (i = 0; i <= spec->limit; i++)
749
    {
750
        ORDDEF *odp = spec->ordinals[i];
751 752 753 754 755 756
        if (!odp || !is_function( odp )) continue;
        fprintf( outfile, ".L__wine_%s_%u:\n", make_c_identifier(spec->dll_name), i );
        fprintf( outfile, "\tpushw %%bp\n" );
        fprintf( outfile, "\tpushl $%s\n",
                 asm_name( odp->type == TYPE_STUB ? get_stub_name( odp, spec ) : odp->link_name ));
        fprintf( outfile, "\tcallw .L__wine_spec_callfrom16_%s\n", get_callfrom16_name( odp ) );
757
    }
758
    fprintf( outfile, ".L__wine_spec_code_segment_end:\n" );
759

760
    /* data segment */
761

762 763 764 765 766 767 768 769 770 771 772 773 774
    fprintf( outfile, "\n.L__wine_spec_data_segment:\n" );
    fprintf( outfile, "\t.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n" );  /* instance data */
    for (i = 0; i <= spec->limit; i++)
    {
        ORDDEF *odp = spec->ordinals[i];
        if (!odp || odp->type != TYPE_VARIABLE) continue;
        fprintf( outfile, ".L__wine_%s_%u:\n", make_c_identifier(spec->dll_name), i );
        fprintf( outfile, "\t.long " );
        for (j = 0; j < odp->u.var.n_values-1; j++)
            fprintf( outfile, "0x%08x,", odp->u.var.values[j] );
        fprintf( outfile, "0x%08x\n", odp->u.var.values[j] );
    }
    fprintf( outfile, ".L__wine_spec_data_segment_end:\n" );
775

776
    /* resource data */
777

778
    if (spec->nb_resources)
779
    {
780 781
        fprintf( outfile, "\n.L__wine_spec_resource_data:\n" );
        output_res16_data( outfile, spec );
782
    }
783

784
    fprintf( outfile, "\t.byte 0\n" );  /* make sure the last symbol points to something */
785

786
    /* relay functions */
787

788 789 790 791 792 793 794 795 796 797
    nb_funcs = sort_func_list( typelist, nb_funcs, relay_type_compare );
    if (nb_funcs)
    {
        fprintf( outfile, "\n/* relay functions */\n\n" );
        fprintf( outfile, "\t.text\n" );
        for ( i = 0; i < nb_funcs; i++ ) output_call16_function( outfile, typelist[i] );
        fprintf( outfile, "\t.data\n" );
        fprintf( outfile, "wine_ldt_copy_ptr:\n" );
        fprintf( outfile, "\t.long %s\n", asm_name("wine_ldt_copy") );
    }
798

799 800 801
    fprintf( outfile, "\n\t%s\n", get_asm_string_section() );
    fprintf( outfile, ".L__wine_spec_file_name:\n" );
    fprintf( outfile, "\t%s \"%s\"\n", get_asm_string_keyword(), spec->file_name );
802

803 804 805
    output_stubs( outfile, spec );
    output_get_pc_thunk( outfile );
    output_init_code( outfile, spec, header_name );
806
    output_gnu_stack_note( outfile );
807

808
    free( typelist );
809
}