Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/tools/winebuild/parser.c
4389 views
1
/*
2
* Spec file parser
3
*
4
* Copyright 1993 Robert J. Amstadt
5
* Copyright 1995 Martin von Loewis
6
* Copyright 1995, 1996, 1997, 2004 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
27
#include <assert.h>
28
#include <ctype.h>
29
#include <stdarg.h>
30
#include <stdio.h>
31
#include <stdlib.h>
32
#include <string.h>
33
34
#include "build.h"
35
36
int current_line = 0;
37
38
static char ParseBuffer[512];
39
static char TokenBuffer[512];
40
static char *ParseNext = ParseBuffer;
41
static FILE *input_file;
42
43
static const char *separator_chars;
44
static const char *comment_chars;
45
46
/* valid characters in ordinal names */
47
static const char valid_ordname_chars[] = "/$:-_@?<>abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
48
49
static const char * const TypeNames[TYPE_NBTYPES] =
50
{
51
"variable", /* TYPE_VARIABLE */
52
"pascal", /* TYPE_PASCAL */
53
"equate", /* TYPE_ABS */
54
"stub", /* TYPE_STUB */
55
"stdcall", /* TYPE_STDCALL */
56
"cdecl", /* TYPE_CDECL */
57
"varargs", /* TYPE_VARARGS */
58
"extern" /* TYPE_EXTERN */
59
};
60
61
static const char * const FlagNames[] =
62
{
63
"norelay", /* FLAG_NORELAY */
64
"noname", /* FLAG_NONAME */
65
"ret16", /* FLAG_RET16 */
66
"ret64", /* FLAG_RET64 */
67
"register", /* FLAG_REGISTER */
68
"private", /* FLAG_PRIVATE */
69
"ordinal", /* FLAG_ORDINAL */
70
"thiscall", /* FLAG_THISCALL */
71
"fastcall", /* FLAG_FASTCALL */
72
"syscall", /* FLAG_SYSCALL */
73
"import", /* FLAG_IMPORT */
74
NULL
75
};
76
77
static const char * const ArgNames[ARG_MAXARG + 1] =
78
{
79
"word", /* ARG_WORD */
80
"s_word", /* ARG_SWORD */
81
"segptr", /* ARG_SEGPTR */
82
"segstr", /* ARG_SEGSTR */
83
"long", /* ARG_LONG */
84
"ptr", /* ARG_PTR */
85
"str", /* ARG_STR */
86
"wstr", /* ARG_WSTR */
87
"int64", /* ARG_INT64 */
88
"int128", /* ARG_INT128 */
89
"float", /* ARG_FLOAT */
90
"double" /* ARG_DOUBLE */
91
};
92
93
static int IsNumberString(const char *s)
94
{
95
while (*s) if (!isdigit(*s++)) return 0;
96
return 1;
97
}
98
99
static inline int is_token_separator( char ch )
100
{
101
return strchr( separator_chars, ch ) != NULL;
102
}
103
104
static inline int is_token_comment( char ch )
105
{
106
return strchr( comment_chars, ch ) != NULL;
107
}
108
109
/* get the next line from the input file, or return 0 if at eof */
110
static int get_next_line(void)
111
{
112
ParseNext = ParseBuffer;
113
current_line++;
114
return (fgets(ParseBuffer, sizeof(ParseBuffer), input_file) != NULL);
115
}
116
117
static const char * GetToken( int allow_eol )
118
{
119
char *p;
120
char *token = TokenBuffer;
121
122
for (;;)
123
{
124
/* remove initial white space */
125
p = ParseNext;
126
while (isspace(*p)) p++;
127
128
if (*p == '\\' && p[1] == '\n') /* line continuation */
129
{
130
if (!get_next_line())
131
{
132
if (!allow_eol) error( "Unexpected end of file\n" );
133
return NULL;
134
}
135
}
136
else break;
137
}
138
139
if ((*p == '\0') || is_token_comment(*p))
140
{
141
if (!allow_eol) error( "Declaration not terminated properly\n" );
142
return NULL;
143
}
144
145
/*
146
* Find end of token.
147
*/
148
if (is_token_separator(*p))
149
{
150
/* a separator is always a complete token */
151
*token++ = *p++;
152
}
153
else while (*p != '\0' && !is_token_separator(*p) && !isspace(*p))
154
{
155
if (*p == '\\') p++;
156
if (*p) *token++ = *p++;
157
}
158
*token = '\0';
159
ParseNext = p;
160
return TokenBuffer;
161
}
162
163
164
static ORDDEF *add_entry_point( DLLSPEC *spec )
165
{
166
ORDDEF *ret;
167
168
if (spec->nb_entry_points == spec->alloc_entry_points)
169
{
170
spec->alloc_entry_points += 128;
171
spec->entry_points = xrealloc( spec->entry_points,
172
spec->alloc_entry_points * sizeof(*spec->entry_points) );
173
}
174
ret = &spec->entry_points[spec->nb_entry_points++];
175
memset( ret, 0, sizeof(*ret) );
176
return ret;
177
}
178
179
/*******************************************************************
180
* parse_spec_variable
181
*
182
* Parse a variable definition in a .spec file.
183
*/
184
static int parse_spec_variable( ORDDEF *odp, DLLSPEC *spec )
185
{
186
char *endptr;
187
unsigned int *value_array;
188
int n_values;
189
int value_array_size;
190
const char *token;
191
192
if (spec->type == SPEC_WIN32)
193
{
194
error( "'variable' not supported in Win32, use 'extern' instead\n" );
195
return 0;
196
}
197
198
if (!(token = GetToken(0))) return 0;
199
if (*token != '(')
200
{
201
error( "Expected '(' got '%s'\n", token );
202
return 0;
203
}
204
205
n_values = 0;
206
value_array_size = 25;
207
value_array = xmalloc(sizeof(*value_array) * value_array_size);
208
209
for (;;)
210
{
211
if (!(token = GetToken(0)))
212
{
213
free( value_array );
214
return 0;
215
}
216
if (*token == ')')
217
break;
218
219
value_array[n_values++] = strtoul(token, &endptr, 0);
220
if (n_values == value_array_size)
221
{
222
value_array_size += 25;
223
value_array = xrealloc(value_array,
224
sizeof(*value_array) * value_array_size);
225
}
226
227
if (endptr == NULL || *endptr != '\0')
228
{
229
error( "Expected number value, got '%s'\n", token );
230
free( value_array );
231
return 0;
232
}
233
}
234
235
odp->u.var.n_values = n_values;
236
odp->u.var.values = xrealloc(value_array, sizeof(*value_array) * n_values);
237
return 1;
238
}
239
240
241
/*******************************************************************
242
* parse_spec_arguments
243
*
244
* Parse the arguments of an entry point.
245
*/
246
static int parse_spec_arguments( ORDDEF *odp, DLLSPEC *spec, int optional )
247
{
248
const char *token;
249
unsigned int i, arg;
250
int is_win32 = (spec->type == SPEC_WIN32) || (odp->flags & FLAG_EXPORT32);
251
252
odp->u.func.nb_args = 0;
253
254
if (!(token = GetToken( optional ))) return optional;
255
if (*token != '(')
256
{
257
error( "Expected '(' got '%s'\n", token );
258
return 0;
259
}
260
261
for (i = 0; i < MAX_ARGUMENTS; i++)
262
{
263
if (!(token = GetToken(0))) return 0;
264
if (*token == ')')
265
break;
266
267
for (arg = 0; arg <= ARG_MAXARG; arg++)
268
if (!strcmp( ArgNames[arg], token )) break;
269
270
if (arg > ARG_MAXARG)
271
{
272
error( "Unknown argument type '%s'\n", token );
273
return 0;
274
}
275
switch (arg)
276
{
277
case ARG_WORD:
278
case ARG_SWORD:
279
case ARG_SEGPTR:
280
case ARG_SEGSTR:
281
if (!is_win32) break;
282
error( "Argument type '%s' only allowed for Win16\n", token );
283
return 0;
284
case ARG_FLOAT:
285
case ARG_DOUBLE:
286
if (!(odp->flags & FLAG_SYSCALL)) break;
287
error( "Argument type '%s' not allowed for syscall function\n", token );
288
return 0;
289
}
290
odp->u.func.args[i] = arg;
291
}
292
if (*token != ')')
293
{
294
error( "Too many arguments\n" );
295
return 0;
296
}
297
298
odp->u.func.nb_args = i;
299
if (odp->flags & FLAG_THISCALL)
300
{
301
if (odp->type != TYPE_STDCALL)
302
{
303
error( "A thiscall function must use the stdcall convention\n" );
304
return 0;
305
}
306
if (!i || odp->u.func.args[0] != ARG_PTR)
307
{
308
error( "First argument of a thiscall function must be a pointer\n" );
309
return 0;
310
}
311
if (odp->flags & FLAG_CPU_MASK & ~FLAG_CPU(CPU_i386))
312
{
313
error( "A thiscall function can only be exported on i386\n" );
314
return 0;
315
}
316
}
317
if (odp->flags & FLAG_FASTCALL)
318
{
319
if (odp->type != TYPE_STDCALL)
320
{
321
error( "A fastcall function must use the stdcall convention\n" );
322
return 0;
323
}
324
if ((i && odp->u.func.args[0] != ARG_PTR && odp->u.func.args[0] != ARG_LONG) ||
325
(i > 1 && odp->u.func.args[1] != ARG_PTR && odp->u.func.args[1] != ARG_LONG))
326
odp->flags |= FLAG_NORELAY; /* no relay debug possible for non-standard fastcall args */
327
}
328
if (odp->flags & FLAG_SYSCALL)
329
{
330
if (odp->type != TYPE_STDCALL && odp->type != TYPE_STUB)
331
{
332
error( "A syscall function must use the stdcall convention\n" );
333
return 0;
334
}
335
}
336
return 1;
337
}
338
339
340
/*******************************************************************
341
* parse_spec_export
342
*
343
* Parse an exported function definition in a .spec file.
344
*/
345
static int parse_spec_export( ORDDEF *odp, DLLSPEC *spec )
346
{
347
const char *token;
348
int is_win32 = (spec->type == SPEC_WIN32) || (odp->flags & FLAG_EXPORT32);
349
350
if (!is_win32 && odp->type == TYPE_STDCALL)
351
{
352
error( "'stdcall' not supported for Win16\n" );
353
return 0;
354
}
355
if (is_win32 && odp->type == TYPE_PASCAL)
356
{
357
error( "'pascal' not supported for Win32\n" );
358
return 0;
359
}
360
361
if (!parse_spec_arguments( odp, spec, 0 )) return 0;
362
363
if (odp->type == TYPE_VARARGS)
364
odp->flags |= FLAG_NORELAY; /* no relay debug possible for varags entry point */
365
366
if (target.cpu != CPU_i386)
367
odp->flags &= ~FLAG_FASTCALL;
368
369
if (!(token = GetToken(1)))
370
{
371
if (!strcmp( odp->name, "@" ))
372
{
373
error( "Missing handler name for anonymous function\n" );
374
return 0;
375
}
376
odp->link_name = xstrdup( odp->name );
377
}
378
else
379
{
380
odp->link_name = xstrdup( token );
381
if (strchr( odp->link_name, '.' ))
382
{
383
if (!is_win32)
384
{
385
error( "Forwarded functions not supported for Win16\n" );
386
return 0;
387
}
388
odp->flags |= FLAG_FORWARD;
389
}
390
}
391
return 1;
392
}
393
394
395
/*******************************************************************
396
* parse_spec_equate
397
*
398
* Parse an 'equate' definition in a .spec file.
399
*/
400
static int parse_spec_equate( ORDDEF *odp, DLLSPEC *spec )
401
{
402
char *endptr;
403
int value;
404
const char *token;
405
406
if (spec->type == SPEC_WIN32)
407
{
408
error( "'equate' not supported for Win32\n" );
409
return 0;
410
}
411
if (!(token = GetToken(0))) return 0;
412
value = strtol(token, &endptr, 0);
413
if (endptr == NULL || *endptr != '\0')
414
{
415
error( "Expected number value, got '%s'\n", token );
416
return 0;
417
}
418
if (value < -0x8000 || value > 0xffff)
419
{
420
error( "Value %d for absolute symbol doesn't fit in 16 bits\n", value );
421
value = 0;
422
}
423
odp->u.abs.value = value;
424
return 1;
425
}
426
427
428
/*******************************************************************
429
* parse_spec_stub
430
*
431
* Parse a 'stub' definition in a .spec file
432
*/
433
static int parse_spec_stub( ORDDEF *odp, DLLSPEC *spec )
434
{
435
const char *token;
436
437
if (!parse_spec_arguments( odp, spec, 1 )) return 0;
438
439
if (!(token = GetToken(1)))
440
odp->link_name = xstrdup( odp->name );
441
else
442
odp->link_name = xstrdup( token );
443
444
return 1;
445
}
446
447
448
/*******************************************************************
449
* parse_spec_extern
450
*
451
* Parse an 'extern' definition in a .spec file.
452
*/
453
static int parse_spec_extern( ORDDEF *odp, DLLSPEC *spec )
454
{
455
const char *token;
456
457
if (spec->type == SPEC_WIN16)
458
{
459
error( "'extern' not supported for Win16, use 'variable' instead\n" );
460
return 0;
461
}
462
if (!(token = GetToken(1)))
463
{
464
if (!strcmp( odp->name, "@" ))
465
{
466
error( "Missing handler name for anonymous extern\n" );
467
return 0;
468
}
469
odp->link_name = xstrdup( odp->name );
470
}
471
else
472
{
473
odp->link_name = xstrdup( token );
474
if (strchr( odp->link_name, '.' )) odp->flags |= FLAG_FORWARD;
475
}
476
return 1;
477
}
478
479
480
/*******************************************************************
481
* parse_spec_flags
482
*
483
* Parse the optional flags for an entry point in a .spec file.
484
*/
485
static const char *parse_spec_flags( DLLSPEC *spec, ORDDEF *odp, const char *token )
486
{
487
unsigned int i, cpu_mask = 0;
488
489
do
490
{
491
token++;
492
if (!strncmp( token, "arch=", 5))
493
{
494
char *args = xstrdup( token + 5 );
495
char *cpu_name = strtok( args, "," );
496
while (cpu_name)
497
{
498
if (!strcmp( cpu_name, "win32" ))
499
{
500
if (spec->type == SPEC_WIN32)
501
odp->flags |= FLAG_CPU_WIN32;
502
else
503
odp->flags |= FLAG_EXPORT32;
504
}
505
else if (!strcmp( cpu_name, "win64" ))
506
odp->flags |= FLAG_CPU_WIN64;
507
else
508
{
509
int cpu = get_cpu_from_name( cpu_name + (cpu_name[0] == '!') );
510
if (cpu == -1)
511
{
512
error( "Unknown architecture '%s'\n", cpu_name );
513
return NULL;
514
}
515
if (cpu_name[0] == '!') cpu_mask |= FLAG_CPU( cpu );
516
else odp->flags |= FLAG_CPU( cpu );
517
}
518
cpu_name = strtok( NULL, "," );
519
}
520
free( args );
521
}
522
else if (!strncmp( token, "syscall=", 8 ))
523
{
524
char *end;
525
unsigned int id = strtoul( token + 8, &end, 0 );
526
527
if (*end || id >= 0x4000)
528
error( "Invalid syscall number '%s', should be in range 0-0x3fff\n", token + 8 );
529
odp->flags |= FLAG_SYSCALL;
530
}
531
else if (!strcmp( token, "i386" )) /* backwards compatibility */
532
{
533
odp->flags |= FLAG_CPU(CPU_i386);
534
}
535
else
536
{
537
for (i = 0; FlagNames[i]; i++)
538
if (!strcmp( FlagNames[i], token )) break;
539
if (!FlagNames[i])
540
{
541
error( "Unknown flag '%s'\n", token );
542
return NULL;
543
}
544
switch (1 << i)
545
{
546
case FLAG_RET16:
547
case FLAG_REGISTER:
548
if (spec->type == SPEC_WIN32)
549
error( "Flag '%s' is not supported in Win32\n", FlagNames[i] );
550
break;
551
case FLAG_RET64:
552
case FLAG_THISCALL:
553
case FLAG_FASTCALL:
554
if (spec->type == SPEC_WIN16)
555
error( "Flag '%s' is not supported in Win16\n", FlagNames[i] );
556
break;
557
}
558
odp->flags |= 1 << i;
559
}
560
token = GetToken(0);
561
} while (token && *token == '-');
562
563
/* x86-64 implies arm64ec */
564
if (odp->flags & FLAG_CPU(CPU_x86_64)) odp->flags |= FLAG_CPU(CPU_ARM64EC);
565
if (cpu_mask & FLAG_CPU(CPU_x86_64)) cpu_mask |= FLAG_CPU(CPU_ARM64EC);
566
567
if (cpu_mask) odp->flags |= FLAG_CPU_MASK & ~cpu_mask;
568
return token;
569
}
570
571
572
/*******************************************************************
573
* parse_spec_ordinal
574
*
575
* Parse an ordinal definition in a .spec file.
576
*/
577
static int parse_spec_ordinal( int ordinal, DLLSPEC *spec )
578
{
579
const char *token;
580
size_t len;
581
ORDDEF *odp = add_entry_point( spec );
582
583
if (!(token = GetToken(0))) goto error;
584
585
for (odp->type = 0; odp->type < TYPE_NBTYPES; odp->type++)
586
if (TypeNames[odp->type] && !strcmp( token, TypeNames[odp->type] ))
587
break;
588
589
if (odp->type >= TYPE_NBTYPES)
590
{
591
if (!strcmp( token, "thiscall" )) /* for backwards compatibility */
592
{
593
odp->type = TYPE_STDCALL;
594
odp->flags |= FLAG_THISCALL;
595
}
596
else
597
{
598
error( "Expected type after ordinal, found '%s' instead\n", token );
599
goto error;
600
}
601
}
602
603
if (!(token = GetToken(0))) goto error;
604
if (*token == '-' && !(token = parse_spec_flags( spec, odp, token ))) goto error;
605
606
if (ordinal == -1 && spec->type != SPEC_WIN32 && !(odp->flags & FLAG_EXPORT32))
607
{
608
error( "'@' ordinals not supported for Win16\n" );
609
goto error;
610
}
611
612
odp->name = xstrdup( token );
613
odp->lineno = current_line;
614
odp->ordinal = ordinal;
615
616
len = strspn( odp->name, valid_ordname_chars );
617
if (len < strlen( odp->name ))
618
{
619
error( "Character '%c' is not allowed in exported name '%s'\n", odp->name[len], odp->name );
620
goto error;
621
}
622
623
switch(odp->type)
624
{
625
case TYPE_VARIABLE:
626
if (!parse_spec_variable( odp, spec )) goto error;
627
break;
628
case TYPE_PASCAL:
629
case TYPE_STDCALL:
630
case TYPE_VARARGS:
631
case TYPE_CDECL:
632
if (!parse_spec_export( odp, spec )) goto error;
633
break;
634
case TYPE_ABS:
635
if (!parse_spec_equate( odp, spec )) goto error;
636
break;
637
case TYPE_STUB:
638
if (!parse_spec_stub( odp, spec )) goto error;
639
break;
640
case TYPE_EXTERN:
641
if (!parse_spec_extern( odp, spec )) goto error;
642
break;
643
default:
644
assert( 0 );
645
}
646
647
if (data_only && !(odp->flags & FLAG_FORWARD))
648
{
649
error( "Only forwarded entry points are allowed in data-only mode\n" );
650
goto error;
651
}
652
653
if (ordinal != -1)
654
{
655
if (!ordinal)
656
{
657
error( "Ordinal 0 is not valid\n" );
658
goto error;
659
}
660
if (ordinal >= MAX_ORDINALS)
661
{
662
error( "Ordinal number %d too large\n", ordinal );
663
goto error;
664
}
665
odp->ordinal = ordinal;
666
}
667
668
if (odp->type == TYPE_STDCALL && !(odp->flags & FLAG_PRIVATE))
669
{
670
if (!strcmp( odp->name, "DllRegisterServer" ) ||
671
!strcmp( odp->name, "DllUnregisterServer" ) ||
672
!strcmp( odp->name, "DllMain" ) ||
673
!strcmp( odp->name, "DllGetClassObject" ) ||
674
!strcmp( odp->name, "DllGetVersion" ) ||
675
!strcmp( odp->name, "DllInstall" ) ||
676
!strcmp( odp->name, "DllCanUnloadNow" ))
677
{
678
warning( "Function %s should be marked private\n", odp->name );
679
if (strcmp( odp->name, odp->link_name ))
680
warning( "Function %s should not use a different internal name (%s)\n",
681
odp->name, odp->link_name );
682
}
683
}
684
685
if (!strcmp( odp->name, "@" ) || odp->flags & (FLAG_NONAME | FLAG_ORDINAL))
686
{
687
if (ordinal == -1)
688
{
689
if (!strcmp( odp->name, "@" ))
690
error( "Nameless function needs an explicit ordinal number\n" );
691
else
692
error( "Function imported by ordinal needs an explicit ordinal number\n" );
693
goto error;
694
}
695
if (spec->type != SPEC_WIN32)
696
{
697
error( "Nameless functions not supported for Win16\n" );
698
goto error;
699
}
700
if (!strcmp( odp->name, "@" ))
701
{
702
free( odp->name );
703
odp->name = NULL;
704
}
705
else if (!(odp->flags & FLAG_ORDINAL)) /* -ordinal only affects the import library */
706
{
707
odp->export_name = odp->name;
708
odp->name = NULL;
709
}
710
}
711
return 1;
712
713
error:
714
spec->nb_entry_points--;
715
free( odp->name );
716
return 0;
717
}
718
719
720
static unsigned int apiset_hash_len( const char *str )
721
{
722
return strrchr( str, '-' ) - str;
723
}
724
725
static unsigned int apiset_hash( const char *str )
726
{
727
unsigned int ret = 0, len = apiset_hash_len( str );
728
while (len--) ret = ret * apiset_hash_factor + *str++;
729
return ret;
730
}
731
732
static unsigned int apiset_add_str( struct apiset *apiset, const char *str, unsigned int len )
733
{
734
char *ret;
735
736
if (!apiset->strings || !(ret = strstr( apiset->strings, str )))
737
{
738
if (apiset->str_pos + len >= apiset->str_size)
739
{
740
apiset->str_size = max( apiset->str_size * 2, 1024 );
741
apiset->strings = xrealloc( apiset->strings, apiset->str_size );
742
}
743
ret = apiset->strings + apiset->str_pos;
744
memcpy( ret, str, len );
745
ret[len] = 0;
746
apiset->str_pos += len;
747
}
748
return ret - apiset->strings;
749
}
750
751
static void add_apiset( struct apiset *apiset, const char *api )
752
{
753
struct apiset_entry *entry;
754
755
if (apiset->count == apiset->size)
756
{
757
apiset->size = max( apiset->size * 2, 64 );
758
apiset->entries = xrealloc( apiset->entries, apiset->size * sizeof(*apiset->entries) );
759
}
760
entry = &apiset->entries[apiset->count++];
761
entry->name_len = strlen( api );
762
entry->name_off = apiset_add_str( apiset, api, entry->name_len );
763
entry->hash = apiset_hash( api );
764
entry->hash_len = apiset_hash_len( api );
765
entry->val_count = 0;
766
}
767
768
static void add_apiset_value( struct apiset *apiset, const char *value )
769
{
770
struct apiset_entry *entry = &apiset->entries[apiset->count - 1];
771
772
if (entry->val_count < ARRAY_SIZE(entry->values) - 1)
773
{
774
struct apiset_value *val = &entry->values[entry->val_count++];
775
char *sep = strchr( value, ':' );
776
777
if (sep)
778
{
779
val->name_len = sep - value;
780
val->name_off = apiset_add_str( apiset, value, val->name_len );
781
val->val_len = strlen( sep + 1 );
782
val->val_off = apiset_add_str( apiset, sep + 1, val->val_len );
783
}
784
else
785
{
786
val->name_len = val->name_off = 0;
787
val->val_len = strlen( value );
788
val->val_off = apiset_add_str( apiset, value, val->val_len );
789
}
790
}
791
else error( "Too many values for api '%.*s'\n", entry->name_len, apiset->strings + entry->name_off );
792
}
793
794
/*******************************************************************
795
* parse_spec_apiset
796
*/
797
static int parse_spec_apiset( DLLSPEC *spec )
798
{
799
struct apiset_entry *entry;
800
const char *token;
801
unsigned int i, hash;
802
803
if (!data_only)
804
{
805
error( "Apiset definitions are only allowed in data-only mode\n" );
806
return 0;
807
}
808
809
if (!(token = GetToken(0))) return 0;
810
811
if (!strncmp( token, "api-", 4 ) && !strncmp( token, "ext-", 4 ))
812
{
813
error( "Unrecognized API set name '%s'\n", token );
814
return 0;
815
}
816
817
hash = apiset_hash( token );
818
for (i = 0, entry = spec->apiset.entries; i < spec->apiset.count; i++, entry++)
819
{
820
if (entry->name_len == strlen( token ) &&
821
!strncmp( spec->apiset.strings + entry->name_off, token, entry->name_len ))
822
{
823
error( "Duplicate API set '%s'\n", token );
824
return 0;
825
}
826
if (entry->hash == hash)
827
{
828
error( "Duplicate hash code '%.*s' and '%s'\n",
829
entry->name_len, spec->apiset.strings + entry->name_off, token );
830
return 0;
831
}
832
}
833
add_apiset( &spec->apiset, token );
834
835
if (!(token = GetToken(0)) || strcmp( token, "=" ))
836
{
837
error( "Syntax error near '%s'\n", token );
838
return 0;
839
}
840
841
while ((token = GetToken(1))) add_apiset_value( &spec->apiset, token );
842
return 1;
843
}
844
845
846
static int name_compare( const void *ptr1, const void *ptr2 )
847
{
848
const ORDDEF *odp1 = *(const ORDDEF * const *)ptr1;
849
const ORDDEF *odp2 = *(const ORDDEF * const *)ptr2;
850
const char *name1 = odp1->name ? odp1->name : odp1->export_name;
851
const char *name2 = odp2->name ? odp2->name : odp2->export_name;
852
return strcmp( name1, name2 );
853
}
854
855
/*******************************************************************
856
* assign_names
857
*
858
* Build the name array and catch duplicates.
859
*/
860
static void assign_names( struct exports *exports )
861
{
862
int i, j, nb_exp_names = 0;
863
ORDDEF **all_names;
864
865
exports->nb_names = 0;
866
for (i = 0; i < exports->nb_entry_points; i++)
867
if (exports->entry_points[i]->name) exports->nb_names++;
868
else if (exports->entry_points[i]->export_name) nb_exp_names++;
869
870
if (!exports->nb_names && !nb_exp_names) return;
871
872
/* check for duplicates */
873
874
all_names = xmalloc( (exports->nb_names + nb_exp_names) * sizeof(all_names[0]) );
875
for (i = j = 0; i < exports->nb_entry_points; i++)
876
if (exports->entry_points[i]->name || exports->entry_points[i]->export_name)
877
all_names[j++] = exports->entry_points[i];
878
879
qsort( all_names, j, sizeof(all_names[0]), name_compare );
880
881
for (i = 0; i < j - 1; i++)
882
{
883
const char *name1 = all_names[i]->name ? all_names[i]->name : all_names[i]->export_name;
884
const char *name2 = all_names[i+1]->name ? all_names[i+1]->name : all_names[i+1]->export_name;
885
if (!strcmp( name1, name2 ) &&
886
!((all_names[i]->flags ^ all_names[i+1]->flags) & FLAG_EXPORT32))
887
{
888
current_line = max( all_names[i]->lineno, all_names[i+1]->lineno );
889
error( "'%s' redefined\n%s:%d: First defined here\n",
890
name1, input_file_name,
891
min( all_names[i]->lineno, all_names[i+1]->lineno ) );
892
}
893
}
894
free( all_names );
895
896
if (exports->nb_names)
897
{
898
exports->names = xmalloc( exports->nb_names * sizeof(exports->names[0]) );
899
for (i = j = 0; i < exports->nb_entry_points; i++)
900
if (exports->entry_points[i]->name) exports->names[j++] = exports->entry_points[i];
901
902
/* sort the list of names */
903
qsort( exports->names, exports->nb_names, sizeof(exports->names[0]), name_compare );
904
for (i = 0; i < exports->nb_names; i++) exports->names[i]->hint = i;
905
}
906
}
907
908
/*******************************************************************
909
* assign_ordinals
910
*
911
* Build the ordinal array.
912
*/
913
static void assign_ordinals( struct exports *exports )
914
{
915
int i, count, ordinal;
916
917
/* start assigning from base, or from 1 if no ordinal defined yet */
918
919
exports->base = MAX_ORDINALS;
920
exports->limit = 0;
921
for (i = 0; i < exports->nb_entry_points; i++)
922
{
923
ordinal = exports->entry_points[i]->ordinal;
924
if (ordinal == -1) continue;
925
if (ordinal > exports->limit) exports->limit = ordinal;
926
if (ordinal < exports->base) exports->base = ordinal;
927
}
928
if (exports->base == MAX_ORDINALS) exports->base = 1;
929
if (exports->limit < exports->base) exports->limit = exports->base;
930
931
count = max( exports->limit + 1, exports->base + exports->nb_entry_points );
932
exports->ordinals = xmalloc( count * sizeof(exports->ordinals[0]) );
933
memset( exports->ordinals, 0, count * sizeof(exports->ordinals[0]) );
934
935
/* fill in all explicitly specified ordinals */
936
for (i = 0; i < exports->nb_entry_points; i++)
937
{
938
ordinal = exports->entry_points[i]->ordinal;
939
if (ordinal == -1) continue;
940
if (exports->ordinals[ordinal])
941
{
942
current_line = max( exports->entry_points[i]->lineno, exports->ordinals[ordinal]->lineno );
943
error( "ordinal %d redefined\n%s:%d: First defined here\n",
944
ordinal, input_file_name,
945
min( exports->entry_points[i]->lineno, exports->ordinals[ordinal]->lineno ) );
946
}
947
else exports->ordinals[ordinal] = exports->entry_points[i];
948
}
949
950
/* now assign ordinals to the rest */
951
for (i = 0, ordinal = exports->base; i < exports->nb_entry_points; i++)
952
{
953
if (exports->entry_points[i]->ordinal != -1) continue;
954
while (exports->ordinals[ordinal]) ordinal++;
955
if (ordinal >= MAX_ORDINALS)
956
{
957
current_line = exports->entry_points[i]->lineno;
958
fatal_error( "Too many functions defined (max %d)\n", MAX_ORDINALS );
959
}
960
exports->entry_points[i]->ordinal = ordinal;
961
exports->ordinals[ordinal] = exports->entry_points[i];
962
}
963
if (ordinal > exports->limit) exports->limit = ordinal;
964
}
965
966
967
static void assign_exports( DLLSPEC *spec, unsigned int cpu, struct exports *exports )
968
{
969
unsigned int i;
970
971
exports->entry_points = xmalloc( spec->nb_entry_points * sizeof(*exports->entry_points) );
972
for (i = 0; i < spec->nb_entry_points; i++)
973
{
974
ORDDEF *entry = &spec->entry_points[i];
975
if ((entry->flags & FLAG_CPU_MASK) && !(entry->flags & FLAG_CPU(cpu)))
976
continue;
977
exports->entry_points[exports->nb_entry_points++] = entry;
978
}
979
980
assign_names( exports );
981
assign_ordinals( exports );
982
}
983
984
985
/*******************************************************************
986
* add_16bit_exports
987
*
988
* Add the necessary exports to the 32-bit counterpart of a 16-bit module.
989
*/
990
void add_16bit_exports( DLLSPEC *spec32, DLLSPEC *spec16 )
991
{
992
int i;
993
ORDDEF *odp;
994
995
spec32->file_name = xstrdup( spec16->file_name );
996
spec32->characteristics = IMAGE_FILE_DLL;
997
spec32->init_func = xstrdup( "DllMain" );
998
999
/* add an export for the NE module */
1000
1001
odp = add_entry_point( spec32 );
1002
odp->type = TYPE_EXTERN;
1003
odp->flags = FLAG_PRIVATE;
1004
odp->name = xstrdup( "__wine_spec_dos_header" );
1005
odp->lineno = 0;
1006
odp->ordinal = 1;
1007
odp->link_name = xstrdup( ".L__wine_spec_dos_header" );
1008
1009
if (spec16->main_module)
1010
{
1011
odp = add_entry_point( spec32 );
1012
odp->type = TYPE_EXTERN;
1013
odp->flags = FLAG_PRIVATE;
1014
odp->name = xstrdup( "__wine_spec_main_module" );
1015
odp->lineno = 0;
1016
odp->ordinal = 2;
1017
odp->link_name = xstrdup( ".L__wine_spec_main_module" );
1018
}
1019
1020
/* add the explicit win32 exports */
1021
1022
for (i = 1; i <= spec16->exports.limit; i++)
1023
{
1024
ORDDEF *odp16 = spec16->exports.ordinals[i];
1025
1026
if (!odp16 || !odp16->name) continue;
1027
if (!(odp16->flags & FLAG_EXPORT32)) continue;
1028
1029
odp = add_entry_point( spec32 );
1030
odp->flags = odp16->flags & ~FLAG_EXPORT32;
1031
odp->type = odp16->type;
1032
odp->name = xstrdup( odp16->name );
1033
odp->lineno = odp16->lineno;
1034
odp->ordinal = -1;
1035
odp->link_name = xstrdup( odp16->link_name );
1036
odp->u.func.nb_args = odp16->u.func.nb_args;
1037
if (odp->u.func.nb_args > 0) memcpy( odp->u.func.args, odp16->u.func.args,
1038
odp->u.func.nb_args * sizeof(odp->u.func.args[0]) );
1039
}
1040
1041
assign_exports( spec32, target.cpu, &spec32->exports );
1042
}
1043
1044
1045
/*******************************************************************
1046
* parse_spec_file
1047
*
1048
* Parse a .spec file.
1049
*/
1050
int parse_spec_file( FILE *file, DLLSPEC *spec )
1051
{
1052
const char *token;
1053
1054
input_file = file;
1055
current_line = 0;
1056
1057
comment_chars = "#;";
1058
separator_chars = "()";
1059
1060
while (get_next_line())
1061
{
1062
if (!(token = GetToken(1))) continue;
1063
if (strcmp(token, "@") == 0)
1064
{
1065
if (!parse_spec_ordinal( -1, spec )) continue;
1066
}
1067
else if (IsNumberString(token))
1068
{
1069
if (!parse_spec_ordinal( atoi(token), spec )) continue;
1070
}
1071
else if (strcmp(token, "apiset") == 0)
1072
{
1073
if (!parse_spec_apiset( spec )) continue;
1074
}
1075
else
1076
{
1077
error( "Expected ordinal declaration, got '%s'\n", token );
1078
continue;
1079
}
1080
if ((token = GetToken(1))) error( "Syntax error near '%s'\n", token );
1081
}
1082
1083
current_line = 0; /* no longer parsing the input file */
1084
assign_exports( spec, target.cpu, &spec->exports );
1085
if (native_arch != -1) assign_exports( spec, native_arch, &spec->native_exports );
1086
return !nb_errors;
1087
}
1088
1089
1090
/*******************************************************************
1091
* parse_def_library
1092
*
1093
* Parse a LIBRARY declaration in a .def file.
1094
*/
1095
static int parse_def_library( DLLSPEC *spec )
1096
{
1097
const char *token = GetToken(1);
1098
1099
if (!token) return 1;
1100
if (strcmp( token, "BASE" ))
1101
{
1102
free( spec->file_name );
1103
spec->file_name = xstrdup( token );
1104
if (!(token = GetToken(1))) return 1;
1105
}
1106
if (strcmp( token, "BASE" ))
1107
{
1108
error( "Expected library name or BASE= declaration, got '%s'\n", token );
1109
return 0;
1110
}
1111
if (!(token = GetToken(0))) return 0;
1112
if (strcmp( token, "=" ))
1113
{
1114
error( "Expected '=' after BASE, got '%s'\n", token );
1115
return 0;
1116
}
1117
if (!(token = GetToken(0))) return 0;
1118
/* FIXME: do something with base address */
1119
1120
return 1;
1121
}
1122
1123
1124
/*******************************************************************
1125
* parse_def_stack_heap_size
1126
*
1127
* Parse a STACKSIZE or HEAPSIZE declaration in a .def file.
1128
*/
1129
static int parse_def_stack_heap_size( int is_stack, DLLSPEC *spec )
1130
{
1131
const char *token = GetToken(0);
1132
char *end;
1133
unsigned long size;
1134
1135
if (!token) return 0;
1136
size = strtoul( token, &end, 0 );
1137
if (*end)
1138
{
1139
error( "Invalid number '%s'\n", token );
1140
return 0;
1141
}
1142
if (is_stack) spec->stack_size = size / 1024;
1143
else spec->heap_size = size / 1024;
1144
if (!(token = GetToken(1))) return 1;
1145
if (strcmp( token, "," ))
1146
{
1147
error( "Expected ',' after size, got '%s'\n", token );
1148
return 0;
1149
}
1150
if (!(token = GetToken(0))) return 0;
1151
/* FIXME: do something with reserve size */
1152
return 1;
1153
}
1154
1155
1156
/*******************************************************************
1157
* parse_def_export
1158
*
1159
* Parse an export declaration in a .def file.
1160
*/
1161
static int parse_def_export( char *name, DLLSPEC *spec )
1162
{
1163
int i, args;
1164
const char *token = GetToken(1);
1165
ORDDEF *odp = add_entry_point( spec );
1166
1167
odp->lineno = current_line;
1168
odp->ordinal = -1;
1169
odp->name = name;
1170
args = remove_stdcall_decoration( odp->name );
1171
if (args == -1)
1172
{
1173
odp->type = TYPE_CDECL;
1174
args = 0;
1175
}
1176
else
1177
{
1178
odp->type = TYPE_STDCALL;
1179
args /= get_ptr_size();
1180
if (args >= MAX_ARGUMENTS)
1181
{
1182
error( "Too many arguments in stdcall function '%s'\n", odp->name );
1183
return 0;
1184
}
1185
for (i = 0; i < args; i++) odp->u.func.args[i] = ARG_LONG;
1186
}
1187
odp->u.func.nb_args = args;
1188
1189
/* check for optional internal name */
1190
1191
if (token && !strcmp( token, "=" ))
1192
{
1193
if (!(token = GetToken(0))) goto error;
1194
odp->link_name = xstrdup( token );
1195
remove_stdcall_decoration( odp->link_name );
1196
token = GetToken(1);
1197
}
1198
else
1199
{
1200
odp->link_name = xstrdup( name );
1201
}
1202
1203
/* check for optional ordinal */
1204
1205
if (token && token[0] == '@')
1206
{
1207
int ordinal;
1208
1209
if (!IsNumberString( token+1 ))
1210
{
1211
error( "Expected number after '@', got '%s'\n", token+1 );
1212
goto error;
1213
}
1214
ordinal = atoi( token+1 );
1215
if (!ordinal)
1216
{
1217
error( "Ordinal 0 is not valid\n" );
1218
goto error;
1219
}
1220
if (ordinal >= MAX_ORDINALS)
1221
{
1222
error( "Ordinal number %d too large\n", ordinal );
1223
goto error;
1224
}
1225
odp->ordinal = ordinal;
1226
token = GetToken(1);
1227
}
1228
1229
/* check for other optional keywords */
1230
1231
while (token)
1232
{
1233
if (!strcmp( token, "NONAME" ))
1234
{
1235
if (odp->ordinal == -1)
1236
{
1237
error( "NONAME requires an ordinal\n" );
1238
goto error;
1239
}
1240
odp->export_name = odp->name;
1241
odp->name = NULL;
1242
odp->flags |= FLAG_NONAME;
1243
}
1244
else if (!strcmp( token, "PRIVATE" ))
1245
{
1246
odp->flags |= FLAG_PRIVATE;
1247
}
1248
else if (!strcmp( token, "DATA" ))
1249
{
1250
odp->type = TYPE_EXTERN;
1251
}
1252
else
1253
{
1254
error( "Garbage text '%s' found at end of export declaration\n", token );
1255
goto error;
1256
}
1257
token = GetToken(1);
1258
}
1259
return 1;
1260
1261
error:
1262
spec->nb_entry_points--;
1263
free( odp->name );
1264
return 0;
1265
}
1266
1267
1268
/*******************************************************************
1269
* parse_def_file
1270
*
1271
* Parse a .def file.
1272
*/
1273
int parse_def_file( FILE *file, DLLSPEC *spec )
1274
{
1275
const char *token;
1276
int in_exports = 0;
1277
1278
input_file = file;
1279
current_line = 0;
1280
1281
comment_chars = ";";
1282
separator_chars = ",=";
1283
1284
while (get_next_line())
1285
{
1286
if (!(token = GetToken(1))) continue;
1287
1288
if (!strcmp( token, "LIBRARY" ) || !strcmp( token, "NAME" ))
1289
{
1290
if (!parse_def_library( spec )) continue;
1291
goto end_of_line;
1292
}
1293
else if (!strcmp( token, "STACKSIZE" ))
1294
{
1295
if (!parse_def_stack_heap_size( 1, spec )) continue;
1296
goto end_of_line;
1297
}
1298
else if (!strcmp( token, "HEAPSIZE" ))
1299
{
1300
if (!parse_def_stack_heap_size( 0, spec )) continue;
1301
goto end_of_line;
1302
}
1303
else if (!strcmp( token, "EXPORTS" ))
1304
{
1305
in_exports = 1;
1306
if (!(token = GetToken(1))) continue;
1307
}
1308
else if (!strcmp( token, "IMPORTS" ))
1309
{
1310
in_exports = 0;
1311
if (!(token = GetToken(1))) continue;
1312
}
1313
else if (!strcmp( token, "SECTIONS" ))
1314
{
1315
in_exports = 0;
1316
if (!(token = GetToken(1))) continue;
1317
}
1318
1319
if (!in_exports) continue; /* ignore this line */
1320
if (!parse_def_export( xstrdup(token), spec )) continue;
1321
1322
end_of_line:
1323
if ((token = GetToken(1))) error( "Syntax error near '%s'\n", token );
1324
}
1325
1326
current_line = 0; /* no longer parsing the input file */
1327
assign_exports( spec, target.cpu, &spec->exports );
1328
return !nb_errors;
1329
}
1330
1331