msgsm32.acm: Implement a stub dll.
[wine] / tools / winebuild / spec32.c
1 /*
2  * 32-bit spec files
3  *
4  * Copyright 1993 Robert J. Amstadt
5  * Copyright 1995 Martin von Loewis
6  * Copyright 1995, 1996, 1997 Alexandre Julliard
7  * Copyright 1997 Eric Youngdale
8  * Copyright 1999 Ulrich Weigand
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24
25 #include "config.h"
26 #include "wine/port.h"
27
28 #include <assert.h>
29 #include <ctype.h>
30 #include <stdarg.h>
31 #include <string.h>
32
33 #include "build.h"
34
35 #define IMAGE_FILE_MACHINE_UNKNOWN 0
36 #define IMAGE_FILE_MACHINE_I386    0x014c
37 #define IMAGE_FILE_MACHINE_ALPHA   0x0184
38 #define IMAGE_FILE_MACHINE_POWERPC 0x01f0
39 #define IMAGE_FILE_MACHINE_AMD64   0x8664
40
41 #define IMAGE_SIZEOF_NT_OPTIONAL32_HEADER 224
42 #define IMAGE_SIZEOF_NT_OPTIONAL64_HEADER 240
43
44 #define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b
45 #define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b
46 #define IMAGE_ROM_OPTIONAL_HDR_MAGIC  0x107
47
48 /* check if entry point needs a relay thunk */
49 static inline int needs_relay( const ORDDEF *odp )
50 {
51     /* skip nonexistent entry points */
52     if (!odp) return 0;
53     /* skip non-functions */
54     if ((odp->type != TYPE_STDCALL) && (odp->type != TYPE_CDECL)) return 0;
55     /* skip norelay and forward entry points */
56     if (odp->flags & (FLAG_NORELAY|FLAG_FORWARD)) return 0;
57     return 1;
58 }
59
60 /* check if dll will output relay thunks */
61 int has_relays( DLLSPEC *spec )
62 {
63     int i;
64
65     if (target_cpu != CPU_x86 && target_cpu != CPU_x86_64) return 0;
66
67     for (i = spec->base; i <= spec->limit; i++)
68     {
69         ORDDEF *odp = spec->ordinals[i];
70         if (needs_relay( odp )) return 1;
71     }
72     return 0;
73 }
74
75 /*******************************************************************
76  *         output_relay_debug
77  *
78  * Output entry points for relay debugging
79  */
80 static void output_relay_debug( DLLSPEC *spec )
81 {
82     int i;
83     unsigned int j, args, flags;
84
85     /* first the table of entry point offsets */
86
87     output( "\t%s\n", get_asm_rodata_section() );
88     output( "\t.align %d\n", get_alignment(4) );
89     output( ".L__wine_spec_relay_entry_point_offsets:\n" );
90
91     for (i = spec->base; i <= spec->limit; i++)
92     {
93         ORDDEF *odp = spec->ordinals[i];
94
95         if (needs_relay( odp ))
96             output( "\t.long .L__wine_spec_relay_entry_point_%d-__wine_spec_relay_entry_points\n", i );
97         else
98             output( "\t.long 0\n" );
99     }
100
101     /* then the table of argument types */
102
103     output( "\t.align %d\n", get_alignment(4) );
104     output( ".L__wine_spec_relay_arg_types:\n" );
105
106     for (i = spec->base; i <= spec->limit; i++)
107     {
108         ORDDEF *odp = spec->ordinals[i];
109         unsigned int mask = 0;
110
111         if (needs_relay( odp ))
112         {
113             for (j = 0; j < 16 && odp->u.func.arg_types[j]; j++)
114             {
115                 if (odp->u.func.arg_types[j] == 't') mask |= 1<< (j*2);
116                 if (odp->u.func.arg_types[j] == 'W') mask |= 2<< (j*2);
117             }
118         }
119         output( "\t.long 0x%08x\n", mask );
120     }
121
122     /* then the relay thunks */
123
124     output( "\t.text\n" );
125     output( "__wine_spec_relay_entry_points:\n" );
126     output( "\tnop\n" );  /* to avoid 0 offset */
127
128     for (i = spec->base; i <= spec->limit; i++)
129     {
130         ORDDEF *odp = spec->ordinals[i];
131
132         if (!needs_relay( odp )) continue;
133
134         output( "\t.align %d\n", get_alignment(4) );
135         output( ".L__wine_spec_relay_entry_point_%d:\n", i );
136
137         args = strlen(odp->u.func.arg_types);
138         flags = 0;
139
140         switch (target_cpu)
141         {
142         case CPU_x86:
143             if (odp->flags & FLAG_REGISTER)
144                 output( "\tpushl %%eax\n" );
145             else
146                 output( "\tpushl %%esp\n" );
147
148             if (odp->flags & FLAG_RET64) flags |= 1;
149             output( "\tpushl $%u\n", (flags << 24) | (args << 16) | (i - spec->base) );
150
151             if (UsePIC)
152             {
153                 output( "\tcall %s\n", asm_name("__wine_spec_get_pc_thunk_eax") );
154                 output( "1:\tleal .L__wine_spec_relay_descr-1b(%%eax),%%eax\n" );
155             }
156             else output( "\tmovl $.L__wine_spec_relay_descr,%%eax\n" );
157             output( "\tpushl %%eax\n" );
158
159             if (odp->flags & FLAG_REGISTER)
160             {
161                 output( "\tcall *8(%%eax)\n" );
162             }
163             else
164             {
165                 output( "\tcall *4(%%eax)\n" );
166                 if (odp->type == TYPE_STDCALL)
167                     output( "\tret $%u\n", args * get_ptr_size() );
168                 else
169                     output( "\tret\n" );
170             }
171             break;
172
173         case CPU_x86_64:
174             output( "\t.cfi_startproc\n" );
175             output( "\tsubq $40,%%rsp\n" );
176             output( "\t.cfi_adjust_cfa_offset 40\n" );
177             output( "\tmovq %%rcx,48(%%rsp)\n" );
178             output( "\tmovq %%rdx,56(%%rsp)\n" );
179             output( "\tmovq %%r8,64(%%rsp)\n" );
180             output( "\tmovq %%r9,72(%%rsp)\n" );
181             output( "\tleaq 40(%%rsp),%%r8\n" );
182             output( "\tmovq $%u,%%rdx\n", (flags << 24) | (args << 16) | (i - spec->base) );
183             output( "\tleaq .L__wine_spec_relay_descr(%%rip),%%rcx\n" );
184             output( "\tcallq *%u(%%rcx)\n", (odp->flags & FLAG_REGISTER) ? 16 : 8 );
185             output( "\taddq $40,%%rsp\n" );
186             output( "\t.cfi_adjust_cfa_offset -40\n" );
187             output( "\tret\n" );
188             output( "\t.cfi_endproc\n" );
189             break;
190
191         default:
192             assert(0);
193         }
194     }
195 }
196
197 /*******************************************************************
198  *         output_exports
199  *
200  * Output the export table for a Win32 module.
201  */
202 void output_exports( DLLSPEC *spec )
203 {
204     int i, fwd_size = 0;
205     int nr_exports = spec->base <= spec->limit ? spec->limit - spec->base + 1 : 0;
206
207     if (!nr_exports) return;
208
209     output( "\n/* export table */\n\n" );
210     output( "\t.data\n" );
211     output( "\t.align %d\n", get_alignment(4) );
212     output( ".L__wine_spec_exports:\n" );
213
214     /* export directory header */
215
216     output( "\t.long 0\n" );                       /* Characteristics */
217     output( "\t.long 0\n" );                       /* TimeDateStamp */
218     output( "\t.long 0\n" );                       /* MajorVersion/MinorVersion */
219     output( "\t.long .L__wine_spec_exp_names-.L__wine_spec_rva_base\n" ); /* Name */
220     output( "\t.long %u\n", spec->base );          /* Base */
221     output( "\t.long %u\n", nr_exports );          /* NumberOfFunctions */
222     output( "\t.long %u\n", spec->nb_names );      /* NumberOfNames */
223     output( "\t.long .L__wine_spec_exports_funcs-.L__wine_spec_rva_base\n" ); /* AddressOfFunctions */
224     if (spec->nb_names)
225     {
226         output( "\t.long .L__wine_spec_exp_name_ptrs-.L__wine_spec_rva_base\n" ); /* AddressOfNames */
227         output( "\t.long .L__wine_spec_exp_ordinals-.L__wine_spec_rva_base\n" );  /* AddressOfNameOrdinals */
228     }
229     else
230     {
231         output( "\t.long 0\n" );  /* AddressOfNames */
232         output( "\t.long 0\n" );  /* AddressOfNameOrdinals */
233     }
234
235     /* output the function pointers */
236
237     output( "\n.L__wine_spec_exports_funcs:\n" );
238     for (i = spec->base; i <= spec->limit; i++)
239     {
240         ORDDEF *odp = spec->ordinals[i];
241         if (!odp) output( "\t%s 0\n", get_asm_ptr_keyword() );
242         else switch(odp->type)
243         {
244         case TYPE_EXTERN:
245         case TYPE_STDCALL:
246         case TYPE_VARARGS:
247         case TYPE_CDECL:
248             if (odp->flags & FLAG_FORWARD)
249             {
250                 output( "\t%s .L__wine_spec_forwards+%u\n", get_asm_ptr_keyword(), fwd_size );
251                 fwd_size += strlen(odp->link_name) + 1;
252             }
253             else if (odp->flags & FLAG_EXT_LINK)
254             {
255                 output( "\t%s %s_%s\n",
256                          get_asm_ptr_keyword(), asm_name("__wine_spec_ext_link"), odp->link_name );
257             }
258             else
259             {
260                 output( "\t%s %s\n", get_asm_ptr_keyword(), asm_name(odp->link_name) );
261             }
262             break;
263         case TYPE_STUB:
264             output( "\t%s %s\n", get_asm_ptr_keyword(),
265                      asm_name( get_stub_name( odp, spec )) );
266             break;
267         default:
268             assert(0);
269         }
270     }
271
272     if (spec->nb_names)
273     {
274         /* output the function name pointers */
275
276         int namepos = strlen(spec->file_name) + 1;
277
278         output( "\n.L__wine_spec_exp_name_ptrs:\n" );
279         for (i = 0; i < spec->nb_names; i++)
280         {
281             output( "\t.long .L__wine_spec_exp_names+%u-.L__wine_spec_rva_base\n", namepos );
282             namepos += strlen(spec->names[i]->name) + 1;
283         }
284
285         /* output the function ordinals */
286
287         output( "\n.L__wine_spec_exp_ordinals:\n" );
288         for (i = 0; i < spec->nb_names; i++)
289         {
290             output( "\t%s %d\n",
291                      get_asm_short_keyword(), spec->names[i]->ordinal - spec->base );
292         }
293         if (spec->nb_names % 2)
294         {
295             output( "\t%s 0\n", get_asm_short_keyword() );
296         }
297     }
298
299     /* output the export name strings */
300
301     output( "\n.L__wine_spec_exp_names:\n" );
302     output( "\t%s \"%s\"\n", get_asm_string_keyword(), spec->file_name );
303     for (i = 0; i < spec->nb_names; i++)
304         output( "\t%s \"%s\"\n",
305                  get_asm_string_keyword(), spec->names[i]->name );
306
307     /* output forward strings */
308
309     if (fwd_size)
310     {
311         output( "\n.L__wine_spec_forwards:\n" );
312         for (i = spec->base; i <= spec->limit; i++)
313         {
314             ORDDEF *odp = spec->ordinals[i];
315             if (odp && (odp->flags & FLAG_FORWARD))
316                 output( "\t%s \"%s\"\n", get_asm_string_keyword(), odp->link_name );
317         }
318     }
319     output( "\t.align %d\n", get_alignment(get_ptr_size()) );
320     output( ".L__wine_spec_exports_end:\n" );
321
322     /* output relays */
323
324     if (!has_relays( spec ))
325     {
326         output( "\t%s 0\n", get_asm_ptr_keyword() );
327         return;
328     }
329
330     output( ".L__wine_spec_relay_descr:\n" );
331     output( "\t%s 0xdeb90001\n", get_asm_ptr_keyword() );  /* magic */
332     output( "\t%s 0,0\n", get_asm_ptr_keyword() );         /* relay funcs */
333     output( "\t%s 0\n", get_asm_ptr_keyword() );           /* private data */
334     output( "\t%s __wine_spec_relay_entry_points\n", get_asm_ptr_keyword() );
335     output( "\t%s .L__wine_spec_relay_entry_point_offsets\n", get_asm_ptr_keyword() );
336     output( "\t%s .L__wine_spec_relay_arg_types\n", get_asm_ptr_keyword() );
337
338     output_relay_debug( spec );
339 }
340
341
342 /*******************************************************************
343  *         output_asm_constructor
344  *
345  * Output code for calling a dll constructor.
346  */
347 static void output_asm_constructor( const char *constructor )
348 {
349     if (target_platform == PLATFORM_APPLE)
350     {
351         /* Mach-O doesn't have an init section */
352         output( "\n\t.mod_init_func\n" );
353         output( "\t.align %d\n", get_alignment(4) );
354         output( "\t.long %s\n", asm_name(constructor) );
355     }
356     else
357     {
358         output( "\n\t.section \".init\",\"ax\"\n" );
359         switch(target_cpu)
360         {
361         case CPU_x86:
362         case CPU_x86_64:
363             output( "\tcall %s\n", asm_name(constructor) );
364             break;
365         case CPU_SPARC:
366             output( "\tcall %s\n", asm_name(constructor) );
367             output( "\tnop\n" );
368             break;
369         case CPU_ALPHA:
370             output( "\tjsr $26,%s\n", asm_name(constructor) );
371             break;
372         case CPU_POWERPC:
373             output( "\tbl %s\n", asm_name(constructor) );
374             break;
375         }
376     }
377 }
378
379
380 /*******************************************************************
381  *         output_module
382  *
383  * Output the module data.
384  */
385 void output_module( DLLSPEC *spec )
386 {
387     int machine = 0;
388     unsigned int page_size = get_page_size();
389
390     /* Reserve some space for the PE header */
391
392     switch (target_platform)
393     {
394     case PLATFORM_APPLE:
395         output( "\t.text\n" );
396         output( "\t.align %d\n", get_alignment(page_size) );
397         output( "__wine_spec_pe_header:\n" );
398         output( "\t.space 65536\n" );
399         break;
400     case PLATFORM_SOLARIS:
401         output( "\n\t.section \".text\",\"ax\"\n" );
402         output( "__wine_spec_pe_header:\n" );
403         output( "\t.skip %u\n", 65536 + page_size );
404         break;
405     default:
406         output( "\n\t.section \".init\",\"ax\"\n" );
407         switch(target_cpu)
408         {
409         case CPU_x86:
410         case CPU_x86_64:
411         case CPU_ALPHA:
412         case CPU_SPARC:
413             output( "\tjmp 1f\n" );
414             break;
415         case CPU_POWERPC:
416             output( "\tb 1f\n" );
417             break;
418         }
419         output( "__wine_spec_pe_header:\n" );
420         output( "\t.skip %u\n", 65536 + page_size );
421         output( "1:\n" );
422         break;
423     }
424
425     /* Output the NT header */
426
427     output( "\n\t.data\n" );
428     output( "\t.align %d\n", get_alignment(get_ptr_size()) );
429     output( "%s\n", asm_globl("__wine_spec_nt_header") );
430     output( ".L__wine_spec_rva_base:\n" );
431
432     output( "\t.long 0x4550\n" );         /* Signature */
433     switch(target_cpu)
434     {
435     case CPU_x86:     machine = IMAGE_FILE_MACHINE_I386; break;
436     case CPU_x86_64:  machine = IMAGE_FILE_MACHINE_AMD64; break;
437     case CPU_POWERPC: machine = IMAGE_FILE_MACHINE_POWERPC; break;
438     case CPU_ALPHA:   machine = IMAGE_FILE_MACHINE_ALPHA; break;
439     case CPU_SPARC:   machine = IMAGE_FILE_MACHINE_UNKNOWN; break;
440     }
441     output( "\t%s 0x%04x\n",              /* Machine */
442              get_asm_short_keyword(), machine );
443     output( "\t%s 0\n",                   /* NumberOfSections */
444              get_asm_short_keyword() );
445     output( "\t.long 0\n" );              /* TimeDateStamp */
446     output( "\t.long 0\n" );              /* PointerToSymbolTable */
447     output( "\t.long 0\n" );              /* NumberOfSymbols */
448     output( "\t%s %d\n",                  /* SizeOfOptionalHeader */
449              get_asm_short_keyword(),
450              get_ptr_size() == 8 ? IMAGE_SIZEOF_NT_OPTIONAL64_HEADER : IMAGE_SIZEOF_NT_OPTIONAL32_HEADER );
451     output( "\t%s 0x%04x\n",              /* Characteristics */
452              get_asm_short_keyword(), spec->characteristics );
453     output( "\t%s 0x%04x\n",              /* Magic */
454              get_asm_short_keyword(),
455              get_ptr_size() == 8 ? IMAGE_NT_OPTIONAL_HDR64_MAGIC : IMAGE_NT_OPTIONAL_HDR32_MAGIC );
456     output( "\t.byte 0\n" );              /* MajorLinkerVersion */
457     output( "\t.byte 0\n" );              /* MinorLinkerVersion */
458     output( "\t.long 0\n" );              /* SizeOfCode */
459     output( "\t.long 0\n" );              /* SizeOfInitializedData */
460     output( "\t.long 0\n" );              /* SizeOfUninitializedData */
461     /* note: we expand the AddressOfEntryPoint field on 64-bit by overwriting the BaseOfCode field */
462     output( "\t%s %s\n",                  /* AddressOfEntryPoint */
463             get_asm_ptr_keyword(), spec->init_func ? asm_name(spec->init_func) : "0" );
464     if (get_ptr_size() == 4)
465     {
466         output( "\t.long 0\n" );          /* BaseOfCode */
467         output( "\t.long 0\n" );          /* BaseOfData */
468     }
469     output( "\t%s __wine_spec_pe_header\n",         /* ImageBase */
470              get_asm_ptr_keyword() );
471     output( "\t.long %u\n", page_size );  /* SectionAlignment */
472     output( "\t.long %u\n", page_size );  /* FileAlignment */
473     output( "\t%s 1,0\n",                 /* Major/MinorOperatingSystemVersion */
474              get_asm_short_keyword() );
475     output( "\t%s 0,0\n",                 /* Major/MinorImageVersion */
476              get_asm_short_keyword() );
477     output( "\t%s %u,%u\n",               /* Major/MinorSubsystemVersion */
478              get_asm_short_keyword(), spec->subsystem_major, spec->subsystem_minor );
479     output( "\t.long 0\n" );                          /* Win32VersionValue */
480     output( "\t.long %s-.L__wine_spec_rva_base\n",    /* SizeOfImage */
481              asm_name("_end") );
482     output( "\t.long %u\n", page_size );  /* SizeOfHeaders */
483     output( "\t.long 0\n" );              /* CheckSum */
484     output( "\t%s 0x%04x\n",              /* Subsystem */
485              get_asm_short_keyword(), spec->subsystem );
486     output( "\t%s 0x%04x\n",              /* DllCharacteristics */
487             get_asm_short_keyword(), spec->dll_characteristics );
488     output( "\t%s %u,%u\n",               /* SizeOfStackReserve/Commit */
489              get_asm_ptr_keyword(), (spec->stack_size ? spec->stack_size : 1024) * 1024, page_size );
490     output( "\t%s %u,%u\n",               /* SizeOfHeapReserve/Commit */
491              get_asm_ptr_keyword(), (spec->heap_size ? spec->heap_size : 1024) * 1024, page_size );
492     output( "\t.long 0\n" );              /* LoaderFlags */
493     output( "\t.long 16\n" );             /* NumberOfRvaAndSizes */
494
495     if (spec->base <= spec->limit)   /* DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] */
496         output( "\t.long .L__wine_spec_exports-.L__wine_spec_rva_base,"
497                  ".L__wine_spec_exports_end-.L__wine_spec_exports\n" );
498     else
499         output( "\t.long 0,0\n" );
500
501     if (has_imports())   /* DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] */
502         output( "\t.long .L__wine_spec_imports-.L__wine_spec_rva_base,"
503                  ".L__wine_spec_imports_end-.L__wine_spec_imports\n" );
504     else
505         output( "\t.long 0,0\n" );
506
507     if (spec->nb_resources)   /* DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE] */
508         output( "\t.long .L__wine_spec_resources-.L__wine_spec_rva_base,"
509                  ".L__wine_spec_resources_end-.L__wine_spec_resources\n" );
510     else
511         output( "\t.long 0,0\n" );
512
513     output( "\t.long 0,0\n" );  /* DataDirectory[3] */
514     output( "\t.long 0,0\n" );  /* DataDirectory[4] */
515     output( "\t.long 0,0\n" );  /* DataDirectory[5] */
516     output( "\t.long 0,0\n" );  /* DataDirectory[6] */
517     output( "\t.long 0,0\n" );  /* DataDirectory[7] */
518     output( "\t.long 0,0\n" );  /* DataDirectory[8] */
519     output( "\t.long 0,0\n" );  /* DataDirectory[9] */
520     output( "\t.long 0,0\n" );  /* DataDirectory[10] */
521     output( "\t.long 0,0\n" );  /* DataDirectory[11] */
522     output( "\t.long 0,0\n" );  /* DataDirectory[12] */
523     output( "\t.long 0,0\n" );  /* DataDirectory[13] */
524     output( "\t.long 0,0\n" );  /* DataDirectory[14] */
525     output( "\t.long 0,0\n" );  /* DataDirectory[15] */
526
527     output( "\n\t%s\n", get_asm_string_section() );
528     output( "%s\n", asm_globl("__wine_spec_file_name") );
529     output( ".L__wine_spec_file_name:\n" );
530     output( "\t%s \"%s\"\n", get_asm_string_keyword(), spec->file_name );
531     if (target_platform == PLATFORM_APPLE)
532         output( "\t.lcomm %s,4\n", asm_name("_end") );
533
534     output_asm_constructor( "__wine_spec_init_ctor" );
535 }
536
537
538 /*******************************************************************
539  *         BuildSpec32File
540  *
541  * Build a Win32 C file from a spec file.
542  */
543 void BuildSpec32File( DLLSPEC *spec )
544 {
545     resolve_imports( spec );
546     output_standard_file_header();
547     output_module( spec );
548     output_stubs( spec );
549     output_exports( spec );
550     output_imports( spec );
551     output_resources( spec );
552     output_gnu_stack_note();
553 }
554
555
556 /*******************************************************************
557  *         BuildDef32File
558  *
559  * Build a Win32 def file from a spec file.
560  */
561 void BuildDef32File( DLLSPEC *spec )
562 {
563     const char *name;
564     int i, total;
565
566     if (spec_file_name)
567         output( "; File generated automatically from %s; do not edit!\n\n",
568                  spec_file_name );
569     else
570         output( "; File generated automatically; do not edit!\n\n" );
571
572     output( "LIBRARY %s\n\n", spec->file_name);
573     output( "EXPORTS\n");
574
575     /* Output the exports and relay entry points */
576
577     for (i = total = 0; i < spec->nb_entry_points; i++)
578     {
579         const ORDDEF *odp = &spec->entry_points[i];
580         int is_data = 0;
581
582         if (!odp) continue;
583
584         if (odp->name) name = odp->name;
585         else if (odp->export_name) name = odp->export_name;
586         else continue;
587
588         if (!(odp->flags & FLAG_PRIVATE)) total++;
589
590         if (odp->type == TYPE_STUB) continue;
591
592         output( "  %s", name );
593
594         switch(odp->type)
595         {
596         case TYPE_EXTERN:
597             is_data = 1;
598             /* fall through */
599         case TYPE_VARARGS:
600         case TYPE_CDECL:
601             /* try to reduce output */
602             if(strcmp(name, odp->link_name) || (odp->flags & FLAG_FORWARD))
603                 output( "=%s", odp->link_name );
604             break;
605         case TYPE_STDCALL:
606         {
607             int at_param = strlen(odp->u.func.arg_types) * get_ptr_size();
608             if (!kill_at && target_cpu == CPU_x86) output( "@%d", at_param );
609             if  (odp->flags & FLAG_FORWARD)
610             {
611                 output( "=%s", odp->link_name );
612             }
613             else if (strcmp(name, odp->link_name)) /* try to reduce output */
614             {
615                 output( "=%s", odp->link_name );
616                 if (!kill_at && target_cpu == CPU_x86) output( "@%d", at_param );
617             }
618             break;
619         }
620         default:
621             assert(0);
622         }
623         output( " @%d", odp->ordinal );
624         if (!odp->name || (odp->flags & FLAG_ORDINAL)) output( " NONAME" );
625         if (is_data) output( " DATA" );
626         if (odp->flags & FLAG_PRIVATE) output( " PRIVATE" );
627         output( "\n" );
628     }
629     if (!total) warning( "%s: Import library doesn't export anything\n", spec->file_name );
630 }