wined3d: Pack hardcoded local constants in ARB.
[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( "\tmovq %%rcx,8(%%rsp)\n" );
175             output( "\tmovq %%rdx,16(%%rsp)\n" );
176             output( "\tmovq %%r8,24(%%rsp)\n" );
177             output( "\tmovq %%r9,32(%%rsp)\n" );
178             output( "\tmovq %%rsp,%%r8\n" );
179             output( "\tmovq $%u,%%rdx\n", (flags << 24) | (args << 16) | (i - spec->base) );
180             output( "\tleaq .L__wine_spec_relay_descr(%%rip),%%rcx\n" );
181             output( "\tsubq $40,%%rsp\n" );
182             output( "\tcallq *%u(%%rcx)\n", (odp->flags & FLAG_REGISTER) ? 16 : 8 );
183             output( "\taddq $40,%%rsp\n" );
184             output( "\tret\n" );
185             break;
186
187         default:
188             assert(0);
189         }
190     }
191 }
192
193 /*******************************************************************
194  *         output_exports
195  *
196  * Output the export table for a Win32 module.
197  */
198 void output_exports( DLLSPEC *spec )
199 {
200     int i, fwd_size = 0;
201     int nr_exports = spec->base <= spec->limit ? spec->limit - spec->base + 1 : 0;
202
203     if (!nr_exports) return;
204
205     output( "\n/* export table */\n\n" );
206     output( "\t.data\n" );
207     output( "\t.align %d\n", get_alignment(4) );
208     output( ".L__wine_spec_exports:\n" );
209
210     /* export directory header */
211
212     output( "\t.long 0\n" );                       /* Characteristics */
213     output( "\t.long 0\n" );                       /* TimeDateStamp */
214     output( "\t.long 0\n" );                       /* MajorVersion/MinorVersion */
215     output( "\t.long .L__wine_spec_exp_names-.L__wine_spec_rva_base\n" ); /* Name */
216     output( "\t.long %u\n", spec->base );          /* Base */
217     output( "\t.long %u\n", nr_exports );          /* NumberOfFunctions */
218     output( "\t.long %u\n", spec->nb_names );      /* NumberOfNames */
219     output( "\t.long .L__wine_spec_exports_funcs-.L__wine_spec_rva_base\n" ); /* AddressOfFunctions */
220     if (spec->nb_names)
221     {
222         output( "\t.long .L__wine_spec_exp_name_ptrs-.L__wine_spec_rva_base\n" ); /* AddressOfNames */
223         output( "\t.long .L__wine_spec_exp_ordinals-.L__wine_spec_rva_base\n" );  /* AddressOfNameOrdinals */
224     }
225     else
226     {
227         output( "\t.long 0\n" );  /* AddressOfNames */
228         output( "\t.long 0\n" );  /* AddressOfNameOrdinals */
229     }
230
231     /* output the function pointers */
232
233     output( "\n.L__wine_spec_exports_funcs:\n" );
234     for (i = spec->base; i <= spec->limit; i++)
235     {
236         ORDDEF *odp = spec->ordinals[i];
237         if (!odp) output( "\t%s 0\n", get_asm_ptr_keyword() );
238         else switch(odp->type)
239         {
240         case TYPE_EXTERN:
241         case TYPE_STDCALL:
242         case TYPE_VARARGS:
243         case TYPE_CDECL:
244             if (odp->flags & FLAG_FORWARD)
245             {
246                 output( "\t%s .L__wine_spec_forwards+%u\n", get_asm_ptr_keyword(), fwd_size );
247                 fwd_size += strlen(odp->link_name) + 1;
248             }
249             else if (odp->flags & FLAG_EXT_LINK)
250             {
251                 output( "\t%s %s_%s\n",
252                          get_asm_ptr_keyword(), asm_name("__wine_spec_ext_link"), odp->link_name );
253             }
254             else
255             {
256                 output( "\t%s %s\n", get_asm_ptr_keyword(), asm_name(odp->link_name) );
257             }
258             break;
259         case TYPE_STUB:
260             output( "\t%s %s\n", get_asm_ptr_keyword(),
261                      asm_name( get_stub_name( odp, spec )) );
262             break;
263         default:
264             assert(0);
265         }
266     }
267
268     if (spec->nb_names)
269     {
270         /* output the function name pointers */
271
272         int namepos = strlen(spec->file_name) + 1;
273
274         output( "\n.L__wine_spec_exp_name_ptrs:\n" );
275         for (i = 0; i < spec->nb_names; i++)
276         {
277             output( "\t.long .L__wine_spec_exp_names+%u-.L__wine_spec_rva_base\n", namepos );
278             namepos += strlen(spec->names[i]->name) + 1;
279         }
280
281         /* output the function ordinals */
282
283         output( "\n.L__wine_spec_exp_ordinals:\n" );
284         for (i = 0; i < spec->nb_names; i++)
285         {
286             output( "\t%s %d\n",
287                      get_asm_short_keyword(), spec->names[i]->ordinal - spec->base );
288         }
289         if (spec->nb_names % 2)
290         {
291             output( "\t%s 0\n", get_asm_short_keyword() );
292         }
293     }
294
295     /* output the export name strings */
296
297     output( "\n.L__wine_spec_exp_names:\n" );
298     output( "\t%s \"%s\"\n", get_asm_string_keyword(), spec->file_name );
299     for (i = 0; i < spec->nb_names; i++)
300         output( "\t%s \"%s\"\n",
301                  get_asm_string_keyword(), spec->names[i]->name );
302
303     /* output forward strings */
304
305     if (fwd_size)
306     {
307         output( "\n.L__wine_spec_forwards:\n" );
308         for (i = spec->base; i <= spec->limit; i++)
309         {
310             ORDDEF *odp = spec->ordinals[i];
311             if (odp && (odp->flags & FLAG_FORWARD))
312                 output( "\t%s \"%s\"\n", get_asm_string_keyword(), odp->link_name );
313         }
314     }
315     output( "\t.align %d\n", get_alignment(get_ptr_size()) );
316     output( ".L__wine_spec_exports_end:\n" );
317
318     /* output relays */
319
320     if (!has_relays( spec ))
321     {
322         output( "\t%s 0\n", get_asm_ptr_keyword() );
323         return;
324     }
325
326     output( ".L__wine_spec_relay_descr:\n" );
327     output( "\t%s 0xdeb90001\n", get_asm_ptr_keyword() );  /* magic */
328     output( "\t%s 0,0\n", get_asm_ptr_keyword() );         /* relay funcs */
329     output( "\t%s 0\n", get_asm_ptr_keyword() );           /* private data */
330     output( "\t%s __wine_spec_relay_entry_points\n", get_asm_ptr_keyword() );
331     output( "\t%s .L__wine_spec_relay_entry_point_offsets\n", get_asm_ptr_keyword() );
332     output( "\t%s .L__wine_spec_relay_arg_types\n", get_asm_ptr_keyword() );
333
334     output_relay_debug( spec );
335 }
336
337
338 /*******************************************************************
339  *         output_asm_constructor
340  *
341  * Output code for calling a dll constructor.
342  */
343 static void output_asm_constructor( const char *constructor )
344 {
345     if (target_platform == PLATFORM_APPLE)
346     {
347         /* Mach-O doesn't have an init section */
348         output( "\n\t.mod_init_func\n" );
349         output( "\t.align %d\n", get_alignment(4) );
350         output( "\t.long %s\n", asm_name(constructor) );
351     }
352     else
353     {
354         output( "\n\t.section \".init\",\"ax\"\n" );
355         switch(target_cpu)
356         {
357         case CPU_x86:
358         case CPU_x86_64:
359             output( "\tcall %s\n", asm_name(constructor) );
360             break;
361         case CPU_SPARC:
362             output( "\tcall %s\n", asm_name(constructor) );
363             output( "\tnop\n" );
364             break;
365         case CPU_ALPHA:
366             output( "\tjsr $26,%s\n", asm_name(constructor) );
367             break;
368         case CPU_POWERPC:
369             output( "\tbl %s\n", asm_name(constructor) );
370             break;
371         }
372     }
373 }
374
375
376 /*******************************************************************
377  *         output_module
378  *
379  * Output the module data.
380  */
381 void output_module( DLLSPEC *spec )
382 {
383     int machine = 0;
384     unsigned int page_size = get_page_size();
385
386     /* Reserve some space for the PE header */
387
388     switch (target_platform)
389     {
390     case PLATFORM_APPLE:
391         output( "\t.text\n" );
392         output( "\t.align %d\n", get_alignment(page_size) );
393         output( "__wine_spec_pe_header:\n" );
394         output( "\t.space 65536\n" );
395         break;
396     case PLATFORM_SOLARIS:
397         output( "\n\t.section \".text\",\"ax\"\n" );
398         output( "__wine_spec_pe_header:\n" );
399         output( "\t.skip %u\n", 65536 + page_size );
400         break;
401     default:
402         output( "\n\t.section \".init\",\"ax\"\n" );
403         switch(target_cpu)
404         {
405         case CPU_x86:
406         case CPU_x86_64:
407         case CPU_ALPHA:
408         case CPU_SPARC:
409             output( "\tjmp 1f\n" );
410             break;
411         case CPU_POWERPC:
412             output( "\tb 1f\n" );
413             break;
414         }
415         output( "__wine_spec_pe_header:\n" );
416         output( "\t.skip %u\n", 65536 + page_size );
417         output( "1:\n" );
418         break;
419     }
420
421     /* Output the NT header */
422
423     output( "\n\t.data\n" );
424     output( "\t.align %d\n", get_alignment(get_ptr_size()) );
425     output( "%s\n", asm_globl("__wine_spec_nt_header") );
426     output( ".L__wine_spec_rva_base:\n" );
427
428     output( "\t.long 0x4550\n" );         /* Signature */
429     switch(target_cpu)
430     {
431     case CPU_x86:     machine = IMAGE_FILE_MACHINE_I386; break;
432     case CPU_x86_64:  machine = IMAGE_FILE_MACHINE_AMD64; break;
433     case CPU_POWERPC: machine = IMAGE_FILE_MACHINE_POWERPC; break;
434     case CPU_ALPHA:   machine = IMAGE_FILE_MACHINE_ALPHA; break;
435     case CPU_SPARC:   machine = IMAGE_FILE_MACHINE_UNKNOWN; break;
436     }
437     output( "\t%s 0x%04x\n",              /* Machine */
438              get_asm_short_keyword(), machine );
439     output( "\t%s 0\n",                   /* NumberOfSections */
440              get_asm_short_keyword() );
441     output( "\t.long 0\n" );              /* TimeDateStamp */
442     output( "\t.long 0\n" );              /* PointerToSymbolTable */
443     output( "\t.long 0\n" );              /* NumberOfSymbols */
444     output( "\t%s %d\n",                  /* SizeOfOptionalHeader */
445              get_asm_short_keyword(),
446              get_ptr_size() == 8 ? IMAGE_SIZEOF_NT_OPTIONAL64_HEADER : IMAGE_SIZEOF_NT_OPTIONAL32_HEADER );
447     output( "\t%s 0x%04x\n",              /* Characteristics */
448              get_asm_short_keyword(), spec->characteristics );
449     output( "\t%s 0x%04x\n",              /* Magic */
450              get_asm_short_keyword(),
451              get_ptr_size() == 8 ? IMAGE_NT_OPTIONAL_HDR64_MAGIC : IMAGE_NT_OPTIONAL_HDR32_MAGIC );
452     output( "\t.byte 0\n" );              /* MajorLinkerVersion */
453     output( "\t.byte 0\n" );              /* MinorLinkerVersion */
454     output( "\t.long 0\n" );              /* SizeOfCode */
455     output( "\t.long 0\n" );              /* SizeOfInitializedData */
456     output( "\t.long 0\n" );              /* SizeOfUninitializedData */
457     /* note: we expand the AddressOfEntryPoint field on 64-bit by overwriting the BaseOfCode field */
458     output( "\t%s %s\n",                  /* AddressOfEntryPoint */
459             get_asm_ptr_keyword(), spec->init_func ? asm_name(spec->init_func) : "0" );
460     if (get_ptr_size() == 4)
461     {
462         output( "\t.long 0\n" );          /* BaseOfCode */
463         output( "\t.long 0\n" );          /* BaseOfData */
464     }
465     output( "\t%s __wine_spec_pe_header\n",         /* ImageBase */
466              get_asm_ptr_keyword() );
467     output( "\t.long %u\n", page_size );  /* SectionAlignment */
468     output( "\t.long %u\n", page_size );  /* FileAlignment */
469     output( "\t%s 1,0\n",                 /* Major/MinorOperatingSystemVersion */
470              get_asm_short_keyword() );
471     output( "\t%s 0,0\n",                 /* Major/MinorImageVersion */
472              get_asm_short_keyword() );
473     output( "\t%s %u,%u\n",               /* Major/MinorSubsystemVersion */
474              get_asm_short_keyword(), spec->subsystem_major, spec->subsystem_minor );
475     output( "\t.long 0\n" );                          /* Win32VersionValue */
476     output( "\t.long %s-.L__wine_spec_rva_base\n",    /* SizeOfImage */
477              asm_name("_end") );
478     output( "\t.long %u\n", page_size );  /* SizeOfHeaders */
479     output( "\t.long 0\n" );              /* CheckSum */
480     output( "\t%s 0x%04x\n",              /* Subsystem */
481              get_asm_short_keyword(), spec->subsystem );
482     output( "\t%s 0x%04x\n",              /* DllCharacteristics */
483             get_asm_short_keyword(), spec->dll_characteristics );
484     output( "\t%s %u,%u\n",               /* SizeOfStackReserve/Commit */
485              get_asm_ptr_keyword(), (spec->stack_size ? spec->stack_size : 1024) * 1024, page_size );
486     output( "\t%s %u,%u\n",               /* SizeOfHeapReserve/Commit */
487              get_asm_ptr_keyword(), (spec->heap_size ? spec->heap_size : 1024) * 1024, page_size );
488     output( "\t.long 0\n" );              /* LoaderFlags */
489     output( "\t.long 16\n" );             /* NumberOfRvaAndSizes */
490
491     if (spec->base <= spec->limit)   /* DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] */
492         output( "\t.long .L__wine_spec_exports-.L__wine_spec_rva_base,"
493                  ".L__wine_spec_exports_end-.L__wine_spec_exports\n" );
494     else
495         output( "\t.long 0,0\n" );
496
497     if (has_imports())   /* DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] */
498         output( "\t.long .L__wine_spec_imports-.L__wine_spec_rva_base,"
499                  ".L__wine_spec_imports_end-.L__wine_spec_imports\n" );
500     else
501         output( "\t.long 0,0\n" );
502
503     if (spec->nb_resources)   /* DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE] */
504         output( "\t.long .L__wine_spec_resources-.L__wine_spec_rva_base,"
505                  ".L__wine_spec_resources_end-.L__wine_spec_resources\n" );
506     else
507         output( "\t.long 0,0\n" );
508
509     output( "\t.long 0,0\n" );  /* DataDirectory[3] */
510     output( "\t.long 0,0\n" );  /* DataDirectory[4] */
511     output( "\t.long 0,0\n" );  /* DataDirectory[5] */
512     output( "\t.long 0,0\n" );  /* DataDirectory[6] */
513     output( "\t.long 0,0\n" );  /* DataDirectory[7] */
514     output( "\t.long 0,0\n" );  /* DataDirectory[8] */
515     output( "\t.long 0,0\n" );  /* DataDirectory[9] */
516     output( "\t.long 0,0\n" );  /* DataDirectory[10] */
517     output( "\t.long 0,0\n" );  /* DataDirectory[11] */
518     output( "\t.long 0,0\n" );  /* DataDirectory[12] */
519     output( "\t.long 0,0\n" );  /* DataDirectory[13] */
520     output( "\t.long 0,0\n" );  /* DataDirectory[14] */
521     output( "\t.long 0,0\n" );  /* DataDirectory[15] */
522
523     output( "\n\t%s\n", get_asm_string_section() );
524     output( "%s\n", asm_globl("__wine_spec_file_name") );
525     output( ".L__wine_spec_file_name:\n" );
526     output( "\t%s \"%s\"\n", get_asm_string_keyword(), spec->file_name );
527     if (target_platform == PLATFORM_APPLE)
528         output( "\t.lcomm %s,4\n", asm_name("_end") );
529
530     output_asm_constructor( "__wine_spec_init_ctor" );
531 }
532
533
534 /*******************************************************************
535  *         BuildSpec32File
536  *
537  * Build a Win32 C file from a spec file.
538  */
539 void BuildSpec32File( DLLSPEC *spec )
540 {
541     resolve_imports( spec );
542     output_standard_file_header();
543     output_module( spec );
544     output_stubs( spec );
545     output_exports( spec );
546     output_imports( spec );
547     output_resources( spec );
548     output_gnu_stack_note();
549 }
550
551
552 /*******************************************************************
553  *         BuildDef32File
554  *
555  * Build a Win32 def file from a spec file.
556  */
557 void BuildDef32File( DLLSPEC *spec )
558 {
559     const char *name;
560     int i, total;
561
562     if (spec_file_name)
563         output( "; File generated automatically from %s; do not edit!\n\n",
564                  spec_file_name );
565     else
566         output( "; File generated automatically; do not edit!\n\n" );
567
568     output( "LIBRARY %s\n\n", spec->file_name);
569     output( "EXPORTS\n");
570
571     /* Output the exports and relay entry points */
572
573     for (i = total = 0; i < spec->nb_entry_points; i++)
574     {
575         const ORDDEF *odp = &spec->entry_points[i];
576         int is_data = 0;
577
578         if (!odp) continue;
579
580         if (odp->name) name = odp->name;
581         else if (odp->export_name) name = odp->export_name;
582         else continue;
583
584         if (!(odp->flags & FLAG_PRIVATE)) total++;
585
586         if (odp->type == TYPE_STUB) continue;
587
588         output( "  %s", name );
589
590         switch(odp->type)
591         {
592         case TYPE_EXTERN:
593             is_data = 1;
594             /* fall through */
595         case TYPE_VARARGS:
596         case TYPE_CDECL:
597             /* try to reduce output */
598             if(strcmp(name, odp->link_name) || (odp->flags & FLAG_FORWARD))
599                 output( "=%s", odp->link_name );
600             break;
601         case TYPE_STDCALL:
602         {
603             int at_param = strlen(odp->u.func.arg_types) * get_ptr_size();
604             if (!kill_at && target_cpu == CPU_x86) output( "@%d", at_param );
605             if  (odp->flags & FLAG_FORWARD)
606             {
607                 output( "=%s", odp->link_name );
608             }
609             else if (strcmp(name, odp->link_name)) /* try to reduce output */
610             {
611                 output( "=%s", odp->link_name );
612                 if (!kill_at && target_cpu == CPU_x86) output( "@%d", at_param );
613             }
614             break;
615         }
616         default:
617             assert(0);
618         }
619         output( " @%d", odp->ordinal );
620         if (!odp->name || (odp->flags & FLAG_ORDINAL)) output( " NONAME" );
621         if (is_data) output( " DATA" );
622         if (odp->flags & FLAG_PRIVATE) output( " PRIVATE" );
623         output( "\n" );
624     }
625     if (!total) warning( "%s: Import library doesn't export anything\n", spec->file_name );
626 }