spec16.c 33.7 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.short .L__wine_%s_%u-.L__wine_spec_code_segment\n", spec->c_name, first + i );
114 115
            break;
        case TYPE_VARIABLE:
116
            output( "\t.short .L__wine_%s_%u-.L__wine_spec_data_segment\n", spec->c_name, first + i );
117 118
            break;
        case TYPE_ABS:
119 120
            output( "\t.short 0x%04x  /* %s */\n",
                     odp->u.abs.value, odp->name );
121 122 123 124 125
            break;
        default:
            assert(0);
        }
    }
126 127 128
}


129
/*******************************************************************
130
 *         output_entry_table
131
 */
132
static void output_entry_table( DLLSPEC *spec )
133
{
134
    int i, prev = 0, prev_sel = -1, bundle_count = 0;
135

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

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

161
        if (prev + 1 != i || prev_sel != selector || bundle_count == 255)
162
        {
163 164
            /* need to start a new bundle */

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

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

185
            bundle_count = 0;
186 187
            prev_sel = selector;
        }
188
        bundle_count++;
189
        prev = i;
190
    }
191 192 193 194

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


/*******************************************************************
 *         output_resident_name
 */
205
static void output_resident_name( const char *string, int ordinal )
206 207 208
{
    unsigned int i, len = strlen(string);

209 210 211
    output( "\t.byte 0x%02x", len );
    for (i = 0; i < len; i++) output( ",0x%02x", (unsigned char)toupper(string[i]) );
    output( " /* %s */\n", string );
212
    output( "\t.short %u\n", ordinal );
213
}
214 215


216
/*******************************************************************
217
 *         get_callfrom16_name
218
 */
219
static const char *get_callfrom16_name( const ORDDEF *odp )
220
{
221 222 223 224 225 226 227 228
    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",
229
                      get_args_str(odp) );
230 231
    return buffer;
}
232

233 234 235 236 237 238 239 240 241 242

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

    switch(odp->type)
243
    {
244 245 246 247 248 249 250 251 252 253 254 255 256
    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);
    }
257
    strcat( buffer, get_args_str(odp) );
258 259 260 261 262
    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';
263
    }
264 265
    if (odp->flags & FLAG_REGISTER) strcat( buffer, "_regs" );
    return buffer;
266 267 268
}


269
/*******************************************************************
270 271 272 273
 *         get_function_argsize
 */
static int get_function_argsize( const ORDDEF *odp )
{
274
    int i, argsize = 0;
275

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


/*******************************************************************
 *         output_call16_function
306
 *
307
 * Build a 16-bit-to-Wine callback glue function.
308
 *
309
 * The generated routines are intended to be used as argument conversion
310 311 312
 * routines to be called by the CallFrom16... core. Thus, the prototypes of
 * the generated routines are (see also CallFrom16):
 *
313 314 315
 *  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 );
316
 *
317 318
 * 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,
319 320 321 322
 * '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
323
 * to by 'args'); the offsets of the single argument values are computed
324 325
 * according to the calling convention and the argument types.  Then, the
 * 32-bit entry point is called with these arguments.
326
 *
327
 * For register functions, the arguments (if present) are converted just
328
 * the same as for normal functions, but in addition the CONTEXT86 pointer
329 330
 * filled with the current register values is passed to the 32-bit routine.
 */
331
static void output_call16_function( ORDDEF *odp )
332
{
333
    char *name;
334
    int i, pos, stack_words;
335
    int argsize = get_function_argsize( odp );
336
    int needs_ldt = (strpbrk( get_args_str( odp ), "pt" ) != NULL);
337

338
    name = strmake( ".L__wine_spec_call16_%s", get_relay_name(odp) );
339

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

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

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

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

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

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

404
        case ARG_INT64:
405 406 407 408 409 410
        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:
411
        case ARG_FLOAT:
412 413
        case ARG_SEGPTR:
        case ARG_SEGSTR:
414
            if (odp->type != TYPE_PASCAL) pos -= 4;
415
            output( "\tpushl %d(%%ecx)\n", pos );
416
            if (odp->type == TYPE_PASCAL) pos += 4;
417 418
            break;

419 420 421
        case ARG_PTR:
        case ARG_STR:
        case ARG_WSTR:
422
        case ARG_INT128:
423
            if (odp->type != TYPE_PASCAL) pos -= 4;
424 425 426 427 428
            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" );
429
            if (odp->type == TYPE_PASCAL) pos += 4;
430 431 432 433
            break;
        }
    }

434
    output( "\tcall *8(%%ebp)\n" );
435

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


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

465 466
    if (type1 == TYPE_STUB) type1 = TYPE_CDECL;
    if (type2 == TYPE_STUB) type2 = TYPE_CDECL;
467

468
    if ((retval = type1 - type2) != 0) return retval;
469

470 471
    type1 = odp1->flags & (FLAG_RET16|FLAG_REGISTER);
    type2 = odp2->flags & (FLAG_RET16|FLAG_REGISTER);
472

473
    if ((retval = type1 - type2) != 0) return retval;
474

475 476
    strcpy( args1, get_args_str( odp1 ));
    return strcmp( args1, get_args_str( odp2 ));
477 478 479
}


480
/*******************************************************************
481
 *         relay_type_compare
482
 *
483 484 485
 * 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 )
486
{
487 488 489
    const ORDDEF *odp1 = *(const ORDDEF * const *)e1;
    const ORDDEF *odp2 = *(const ORDDEF * const *)e2;
    char name1[80];
490

491 492
    strcpy( name1, get_relay_name(odp1) );
    return strcmp( name1, get_relay_name(odp2) );
493 494 495
}


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

506
    if (!count) return 0;
507
    qsort( list, count, sizeof(*list), compare );
508

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

516

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

528 529 530 531
    /* store the main entry point as ordinal 0 */

    if (!spec->ordinals)
    {
532
        assert(spec->limit == 0);
533 534 535 536 537 538 539 540 541 542 543 544 545
        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;
546
        entry_point->u.func.nb_args = 0;
547 548 549 550
        assert( !spec->ordinals[0] );
        spec->ordinals[0] = entry_point;
    }

551
    /* Build sorted list of all argument types, without duplicates */
552

553
    typelist = xmalloc( (spec->limit + 1) * sizeof(*typelist) );
554

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

562
    nb_funcs = sort_func_list( typelist, nb_funcs, callfrom16_type_compare );
563

564
    /* Output the module structure */
565

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

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

624
    /* segment table */
625

626
    output( "\n.L__wine_spec_ne_segtab:\n" );
627

628
    /* code segment entry */
629

630 631 632 633
    output( "\t.short .L__wine_spec_code_segment-.L__wine_spec_dos_header\n" );         /* filepos */
    output( "\t.short .L__wine_spec_code_segment_end-.L__wine_spec_code_segment\n" );   /* size */
    output( "\t.short 0x2000\n" );                                                      /* flags = NE_SEGFLAGS_32BIT */
    output( "\t.short .L__wine_spec_code_segment_end-.L__wine_spec_code_segment\n" );   /* minsize */
634

635
    /* data segment entry */
636

637 638 639 640
    output( "\t.short .L__wine_spec_data_segment-.L__wine_spec_dos_header\n" );         /* filepos */
    output( "\t.short .L__wine_spec_data_segment_end-.L__wine_spec_data_segment\n" );   /* size */
    output( "\t.short 0x0001\n" );                                                      /* flags = NE_SEGFLAGS_DATA */
    output( "\t.short .L__wine_spec_data_segment_end-.L__wine_spec_data_segment\n" );   /* minsize */
641 642 643

    /* resource directory */

644
    output_res16_directory( spec );
645 646 647

    /* resident names table */

648 649 650
    output( "\n\t.align %d\n", get_alignment(2) );
    output( ".L__wine_spec_ne_restab:\n" );
    output_resident_name( spec->dll_name, 0 );
651 652 653 654
    for (i = 1; i <= spec->limit; i++)
    {
        ORDDEF *odp = spec->ordinals[i];
        if (!odp || !odp->name[0]) continue;
655
        if (odp->flags & FLAG_EXPORT32) continue;
656
        output_resident_name( odp->name, i );
657
    }
658
    output( "\t.byte 0\n" );
659 660 661

    /* imported names table */

662 663 664 665
    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" );
666 667 668

    /* entry table */

669 670 671
    output( "\n.L__wine_spec_ne_enttab:\n" );
    output_entry_table( spec );
    output( ".L__wine_spec_ne_enttab_end:\n" );
672 673 674

    /* code segment */

675 676
    output( "\n\t.align %d\n", get_alignment(2) );
    output( ".L__wine_spec_code_segment:\n" );
677 678

    for ( i = 0; i < nb_funcs; i++ )
679
    {
680
        unsigned int arg_types[2];
681
        int nop_words, pos, argsize = 0;
682

683
        if ( typelist[i]->type == TYPE_PASCAL )
684
            argsize = get_function_argsize( typelist[i] );
685

686 687
        /* build the arg types bit fields */
        arg_types[0] = arg_types[1] = 0;
688
        for (j = pos = 0; j < typelist[i]->u.func.nb_args && pos < 20; j++, pos++)
689 690
        {
            int type = 0;
691
            switch (typelist[i]->u.func.args[j])
692
            {
693 694 695 696 697 698 699 700
            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;
701 702 703
            case ARG_FLOAT:  type = ARG16_LONG; break;
            case ARG_INT128: type = ARG16_PTR; break;
            case ARG_INT64:
704 705 706 707 708
            case ARG_DOUBLE:
                type = ARG16_LONG;
                arg_types[pos / 10] |= type << (3 * (pos % 10));
                pos++;
                break;
709
            }
710
            if (pos < 20) arg_types[pos / 10] |= type << (3 * (pos % 10));
711
        }
712 713
        if (typelist[i]->type == TYPE_VARARGS && pos < 20)
            arg_types[pos / 10] |= ARG16_VARARG << (3 * (pos % 10));
714

715 716 717
        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" );
718 719 720

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

        if (argsize)
        {
738
            output( "\tlretw $%u\n", argsize );
739
            nop_words--;
740
        }
741
        else output( "\tlretw\n" );
742

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

745 746
        /* 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 */
747
        output( "\t.short 0x86c7\n" );
748
        output( "\t.long 0x%08x,0x%08x\n", arg_types[0], arg_types[1] );
749 750
    }

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

763
    /* data segment */
764

765 766
    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 */
767 768 769 770
    for (i = 0; i <= spec->limit; i++)
    {
        ORDDEF *odp = spec->ordinals[i];
        if (!odp || odp->type != TYPE_VARIABLE) continue;
771
        output( ".L__wine_%s_%u:\n", spec->c_name, i );
772
        output( "\t.long " );
773
        for (j = 0; j < odp->u.var.n_values-1; j++)
774 775
            output( "0x%08x,", odp->u.var.values[j] );
        output( "0x%08x\n", odp->u.var.values[j] );
776
    }
777
    output( ".L__wine_spec_data_segment_end:\n" );
778

779
    /* resource data */
780

781
    if (spec->nb_resources)
782
    {
783 784
        output( "\n.L__wine_spec_resource_data:\n" );
        output_res16_data( spec );
785
    }
786

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

789
    /* relay functions */
790

791 792 793
    nb_funcs = sort_func_list( typelist, nb_funcs, relay_type_compare );
    if (nb_funcs)
    {
794 795 796 797 798 799
        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") );
800
    }
801

802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
    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 );

818
    needs_get_pc_thunk = 0;
819 820 821 822 823 824
    output_standard_file_header();
    output_module( spec32 );
    output_module16( spec16 );
    output_stubs( spec16 );
    output_exports( spec32 );
    output_imports( spec16 );
825
    if (is_undefined( "__wine_call_from_16" )) output_asm_relays16();
826
    if (needs_get_pc_thunk) output_get_pc_thunk();
827 828 829 830 831 832
    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 );
    }
833 834
    output_gnu_stack_note();
    free_dll_spec( spec32 );
835
}
836 837 838 839 840 841

/*******************************************************************
 *         output_fake_module16
 *
 * Create a fake 16-bit binary module.
 */
842
void output_fake_module16( DLLSPEC *spec )
843
{
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 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
    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();
978
}