diff options
author | Andreas Gruenbacher <agruen@suse.de> | 2008-12-01 17:21:01 -0500 |
---|---|---|
committer | Sam Ravnborg <sam@ravnborg.org> | 2008-12-03 16:33:11 -0500 |
commit | 64e6c1e12372840e7caf8e25325a9e9c5fd370e6 (patch) | |
tree | aa47aa4e170dd4bb39c99cc7356231e2c61d64d2 /scripts/genksyms | |
parent | a680eedc6c621c75695c68198533fc3c98f4053b (diff) |
genksyms: track symbol checksum changes
Sometimes it is preferable to avoid changes of exported symbol checksums
(to avoid breaking externally provided modules). When a checksum change
occurs, it can be hard to figure out what caused this change: underlying
types may have changed, or additional type information may simply have
become available at the point where a symbol is exported.
Add a new --reference option to genksyms which allows it to report why
checksums change, based on the type information dumps it creates with the
--dump-types flag. Genksyms will read in such a dump from a previous run,
and report which symbols have changed (and why).
The behavior can be controlled for an entire build as follows: If
KBUILD_SYMTYPES is set, genksyms uses --dump-types to produce *.symtypes
dump files. If any *.symref files exist, those will be used as the
reference to check against. If KBUILD_PRESERVE is set, checksum changes
will fail the build.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Cc: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
Diffstat (limited to 'scripts/genksyms')
-rw-r--r-- | scripts/genksyms/genksyms.c | 236 | ||||
-rw-r--r-- | scripts/genksyms/genksyms.h | 6 |
2 files changed, 226 insertions, 16 deletions
diff --git a/scripts/genksyms/genksyms.c b/scripts/genksyms/genksyms.c index c249274e005a..ddac1746908e 100644 --- a/scripts/genksyms/genksyms.c +++ b/scripts/genksyms/genksyms.c | |||
@@ -42,7 +42,8 @@ static FILE *debugfile; | |||
42 | int cur_line = 1; | 42 | int cur_line = 1; |
43 | char *cur_filename; | 43 | char *cur_filename; |
44 | 44 | ||
45 | static int flag_debug, flag_dump_defs, flag_dump_types, flag_warnings; | 45 | static int flag_debug, flag_dump_defs, flag_reference, flag_dump_types, |
46 | flag_preserve, flag_warnings; | ||
46 | static const char *arch = ""; | 47 | static const char *arch = ""; |
47 | static const char *mod_prefix = ""; | 48 | static const char *mod_prefix = ""; |
48 | 49 | ||
@@ -58,6 +59,8 @@ static const char *const symbol_type_name[] = { | |||
58 | 59 | ||
59 | static int equal_list(struct string_list *a, struct string_list *b); | 60 | static int equal_list(struct string_list *a, struct string_list *b); |
60 | static void print_list(FILE * f, struct string_list *list); | 61 | static void print_list(FILE * f, struct string_list *list); |
62 | static void print_location(void); | ||
63 | static void print_type_name(enum symbol_type type, const char *name); | ||
61 | 64 | ||
62 | /*----------------------------------------------------------------------*/ | 65 | /*----------------------------------------------------------------------*/ |
63 | 66 | ||
@@ -151,27 +154,68 @@ struct symbol *find_symbol(const char *name, enum symbol_type ns) | |||
151 | 154 | ||
152 | for (sym = symtab[h]; sym; sym = sym->hash_next) | 155 | for (sym = symtab[h]; sym; sym = sym->hash_next) |
153 | if (map_to_ns(sym->type) == map_to_ns(ns) && | 156 | if (map_to_ns(sym->type) == map_to_ns(ns) && |
154 | strcmp(name, sym->name) == 0) | 157 | strcmp(name, sym->name) == 0 && |
158 | sym->is_declared) | ||
155 | break; | 159 | break; |
156 | 160 | ||
157 | return sym; | 161 | return sym; |
158 | } | 162 | } |
159 | 163 | ||
160 | struct symbol *add_symbol(const char *name, enum symbol_type type, | 164 | static int is_unknown_symbol(struct symbol *sym) |
161 | struct string_list *defn, int is_extern) | 165 | { |
166 | struct string_list *defn; | ||
167 | |||
168 | return ((sym->type == SYM_STRUCT || | ||
169 | sym->type == SYM_UNION || | ||
170 | sym->type == SYM_ENUM) && | ||
171 | (defn = sym->defn) && defn->tag == SYM_NORMAL && | ||
172 | strcmp(defn->string, "}") == 0 && | ||
173 | (defn = defn->next) && defn->tag == SYM_NORMAL && | ||
174 | strcmp(defn->string, "UNKNOWN") == 0 && | ||
175 | (defn = defn->next) && defn->tag == SYM_NORMAL && | ||
176 | strcmp(defn->string, "{") == 0); | ||
177 | } | ||
178 | |||
179 | struct symbol *__add_symbol(const char *name, enum symbol_type type, | ||
180 | struct string_list *defn, int is_extern, | ||
181 | int is_reference) | ||
162 | { | 182 | { |
163 | unsigned long h = crc32(name) % HASH_BUCKETS; | 183 | unsigned long h = crc32(name) % HASH_BUCKETS; |
164 | struct symbol *sym; | 184 | struct symbol *sym; |
185 | enum symbol_status status = STATUS_UNCHANGED; | ||
165 | 186 | ||
166 | for (sym = symtab[h]; sym; sym = sym->hash_next) { | 187 | for (sym = symtab[h]; sym; sym = sym->hash_next) { |
167 | if (map_to_ns(sym->type) == map_to_ns(type) | 188 | if (map_to_ns(sym->type) == map_to_ns(type) && |
168 | && strcmp(name, sym->name) == 0) { | 189 | strcmp(name, sym->name) == 0) { |
169 | if (!equal_list(sym->defn, defn)) | 190 | if (is_reference) |
191 | /* fall through */ ; | ||
192 | else if (sym->type == type && | ||
193 | equal_list(sym->defn, defn)) { | ||
194 | sym->is_declared = 1; | ||
195 | return sym; | ||
196 | } else if (!sym->is_declared) { | ||
197 | status = is_unknown_symbol(sym) ? | ||
198 | STATUS_DEFINED : STATUS_MODIFIED; | ||
199 | } else { | ||
170 | error_with_pos("redefinition of %s", name); | 200 | error_with_pos("redefinition of %s", name); |
171 | return sym; | 201 | return sym; |
202 | } | ||
203 | break; | ||
172 | } | 204 | } |
173 | } | 205 | } |
174 | 206 | ||
207 | if (sym) { | ||
208 | struct symbol **psym; | ||
209 | |||
210 | for (psym = &symtab[h]; *psym; psym = &(*psym)->hash_next) { | ||
211 | if (*psym == sym) { | ||
212 | *psym = sym->hash_next; | ||
213 | break; | ||
214 | } | ||
215 | } | ||
216 | --nsyms; | ||
217 | } | ||
218 | |||
175 | sym = xmalloc(sizeof(*sym)); | 219 | sym = xmalloc(sizeof(*sym)); |
176 | sym->name = name; | 220 | sym->name = name; |
177 | sym->type = type; | 221 | sym->type = type; |
@@ -183,6 +227,9 @@ struct symbol *add_symbol(const char *name, enum symbol_type type, | |||
183 | sym->hash_next = symtab[h]; | 227 | sym->hash_next = symtab[h]; |
184 | symtab[h] = sym; | 228 | symtab[h] = sym; |
185 | 229 | ||
230 | sym->is_declared = !is_reference; | ||
231 | sym->status = status; | ||
232 | |||
186 | if (flag_debug) { | 233 | if (flag_debug) { |
187 | fprintf(debugfile, "Defn for %s %s == <", | 234 | fprintf(debugfile, "Defn for %s %s == <", |
188 | symbol_type_name[type], name); | 235 | symbol_type_name[type], name); |
@@ -196,6 +243,18 @@ struct symbol *add_symbol(const char *name, enum symbol_type type, | |||
196 | return sym; | 243 | return sym; |
197 | } | 244 | } |
198 | 245 | ||
246 | struct symbol *add_symbol(const char *name, enum symbol_type type, | ||
247 | struct string_list *defn, int is_extern) | ||
248 | { | ||
249 | return __add_symbol(name, type, defn, is_extern, 0); | ||
250 | } | ||
251 | |||
252 | struct symbol *add_reference_symbol(const char *name, enum symbol_type type, | ||
253 | struct string_list *defn, int is_extern) | ||
254 | { | ||
255 | return __add_symbol(name, type, defn, is_extern, 1); | ||
256 | } | ||
257 | |||
199 | /*----------------------------------------------------------------------*/ | 258 | /*----------------------------------------------------------------------*/ |
200 | 259 | ||
201 | void free_node(struct string_list *node) | 260 | void free_node(struct string_list *node) |
@@ -236,6 +295,82 @@ static int equal_list(struct string_list *a, struct string_list *b) | |||
236 | return !a && !b; | 295 | return !a && !b; |
237 | } | 296 | } |
238 | 297 | ||
298 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) | ||
299 | |||
300 | struct string_list *read_node(FILE *f) | ||
301 | { | ||
302 | char buffer[256]; | ||
303 | struct string_list node = { | ||
304 | .string = buffer, | ||
305 | .tag = SYM_NORMAL }; | ||
306 | int c; | ||
307 | |||
308 | while ((c = fgetc(f)) != EOF) { | ||
309 | if (c == ' ') { | ||
310 | if (node.string == buffer) | ||
311 | continue; | ||
312 | break; | ||
313 | } else if (c == '\n') { | ||
314 | if (node.string == buffer) | ||
315 | return NULL; | ||
316 | ungetc(c, f); | ||
317 | break; | ||
318 | } | ||
319 | if (node.string >= buffer + sizeof(buffer) - 1) { | ||
320 | fprintf(stderr, "Token too long\n"); | ||
321 | exit(1); | ||
322 | } | ||
323 | *node.string++ = c; | ||
324 | } | ||
325 | if (node.string == buffer) | ||
326 | return NULL; | ||
327 | *node.string = 0; | ||
328 | node.string = buffer; | ||
329 | |||
330 | if (node.string[1] == '#') { | ||
331 | int n; | ||
332 | |||
333 | for (n = 0; n < ARRAY_SIZE(symbol_type_name); n++) { | ||
334 | if (node.string[0] == symbol_type_name[n][0]) { | ||
335 | node.tag = n; | ||
336 | node.string += 2; | ||
337 | return copy_node(&node); | ||
338 | } | ||
339 | } | ||
340 | fprintf(stderr, "Unknown type %c\n", node.string[0]); | ||
341 | exit(1); | ||
342 | } | ||
343 | return copy_node(&node); | ||
344 | } | ||
345 | |||
346 | static void read_reference(FILE *f) | ||
347 | { | ||
348 | while (!feof(f)) { | ||
349 | struct string_list *defn = NULL; | ||
350 | struct string_list *sym, *def; | ||
351 | int is_extern = 0; | ||
352 | |||
353 | sym = read_node(f); | ||
354 | if (!sym) | ||
355 | continue; | ||
356 | def = read_node(f); | ||
357 | if (def && def->tag == SYM_NORMAL && | ||
358 | !strcmp(def->string, "extern")) { | ||
359 | is_extern = 1; | ||
360 | free_node(def); | ||
361 | def = read_node(f); | ||
362 | } | ||
363 | while (def) { | ||
364 | def->next = defn; | ||
365 | defn = def; | ||
366 | def = read_node(f); | ||
367 | } | ||
368 | add_reference_symbol(xstrdup(sym->string), sym->tag, | ||
369 | defn, is_extern); | ||
370 | free_node(sym); | ||
371 | } | ||
372 | } | ||
373 | |||
239 | static void print_node(FILE * f, struct string_list *list) | 374 | static void print_node(FILE * f, struct string_list *list) |
240 | { | 375 | { |
241 | if (list->tag != SYM_NORMAL) { | 376 | if (list->tag != SYM_NORMAL) { |
@@ -311,6 +446,7 @@ static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc) | |||
311 | 446 | ||
312 | case SYM_TYPEDEF: | 447 | case SYM_TYPEDEF: |
313 | subsym = find_symbol(cur->string, cur->tag); | 448 | subsym = find_symbol(cur->string, cur->tag); |
449 | /* FIXME: Bad reference files can segfault here. */ | ||
314 | if (subsym->expansion_trail) { | 450 | if (subsym->expansion_trail) { |
315 | if (flag_dump_defs) | 451 | if (flag_dump_defs) |
316 | fprintf(debugfile, "%s ", cur->string); | 452 | fprintf(debugfile, "%s ", cur->string); |
@@ -347,9 +483,22 @@ static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc) | |||
347 | t = n; | 483 | t = n; |
348 | 484 | ||
349 | n = xmalloc(sizeof(*n)); | 485 | n = xmalloc(sizeof(*n)); |
350 | n->string = xstrdup("{ UNKNOWN }"); | 486 | n->string = xstrdup("{"); |
351 | n->tag = SYM_NORMAL; | 487 | n->tag = SYM_NORMAL; |
352 | n->next = t; | 488 | n->next = t; |
489 | t = n; | ||
490 | |||
491 | n = xmalloc(sizeof(*n)); | ||
492 | n->string = xstrdup("UNKNOWN"); | ||
493 | n->tag = SYM_NORMAL; | ||
494 | n->next = t; | ||
495 | t = n; | ||
496 | |||
497 | n = xmalloc(sizeof(*n)); | ||
498 | n->string = xstrdup("}"); | ||
499 | n->tag = SYM_NORMAL; | ||
500 | n->next = t; | ||
501 | t = n; | ||
353 | 502 | ||
354 | subsym = | 503 | subsym = |
355 | add_symbol(cur->string, cur->tag, n, 0); | 504 | add_symbol(cur->string, cur->tag, n, 0); |
@@ -397,20 +546,42 @@ void export_symbol(const char *name) | |||
397 | error_with_pos("export undefined symbol %s", name); | 546 | error_with_pos("export undefined symbol %s", name); |
398 | else { | 547 | else { |
399 | unsigned long crc; | 548 | unsigned long crc; |
549 | int has_changed = 0; | ||
400 | 550 | ||
401 | if (flag_dump_defs) | 551 | if (flag_dump_defs) |
402 | fprintf(debugfile, "Export %s == <", name); | 552 | fprintf(debugfile, "Export %s == <", name); |
403 | 553 | ||
404 | expansion_trail = (struct symbol *)-1L; | 554 | expansion_trail = (struct symbol *)-1L; |
405 | 555 | ||
556 | sym->expansion_trail = expansion_trail; | ||
557 | expansion_trail = sym; | ||
406 | crc = expand_and_crc_sym(sym, 0xffffffff) ^ 0xffffffff; | 558 | crc = expand_and_crc_sym(sym, 0xffffffff) ^ 0xffffffff; |
407 | 559 | ||
408 | sym = expansion_trail; | 560 | sym = expansion_trail; |
409 | while (sym != (struct symbol *)-1L) { | 561 | while (sym != (struct symbol *)-1L) { |
410 | struct symbol *n = sym->expansion_trail; | 562 | struct symbol *n = sym->expansion_trail; |
563 | |||
564 | if (sym->status != STATUS_UNCHANGED) { | ||
565 | if (!has_changed) { | ||
566 | print_location(); | ||
567 | fprintf(stderr, "%s: %s: modversion " | ||
568 | "changed because of changes " | ||
569 | "in ", flag_preserve ? "error" : | ||
570 | "warning", name); | ||
571 | } else | ||
572 | fprintf(stderr, ", "); | ||
573 | print_type_name(sym->type, sym->name); | ||
574 | if (sym->status == STATUS_DEFINED) | ||
575 | fprintf(stderr, " (became defined)"); | ||
576 | has_changed = 1; | ||
577 | if (flag_preserve) | ||
578 | errors++; | ||
579 | } | ||
411 | sym->expansion_trail = 0; | 580 | sym->expansion_trail = 0; |
412 | sym = n; | 581 | sym = n; |
413 | } | 582 | } |
583 | if (has_changed) | ||
584 | fprintf(stderr, "\n"); | ||
414 | 585 | ||
415 | if (flag_dump_defs) | 586 | if (flag_dump_defs) |
416 | fputs(">\n", debugfile); | 587 | fputs(">\n", debugfile); |
@@ -421,13 +592,26 @@ void export_symbol(const char *name) | |||
421 | } | 592 | } |
422 | 593 | ||
423 | /*----------------------------------------------------------------------*/ | 594 | /*----------------------------------------------------------------------*/ |
595 | |||
596 | static void print_location(void) | ||
597 | { | ||
598 | fprintf(stderr, "%s:%d: ", cur_filename ? : "<stdin>", cur_line); | ||
599 | } | ||
600 | |||
601 | static void print_type_name(enum symbol_type type, const char *name) | ||
602 | { | ||
603 | if (type != SYM_NORMAL) | ||
604 | fprintf(stderr, "%s %s", symbol_type_name[type], name); | ||
605 | else | ||
606 | fprintf(stderr, "%s", name); | ||
607 | } | ||
608 | |||
424 | void error_with_pos(const char *fmt, ...) | 609 | void error_with_pos(const char *fmt, ...) |
425 | { | 610 | { |
426 | va_list args; | 611 | va_list args; |
427 | 612 | ||
428 | if (flag_warnings) { | 613 | if (flag_warnings) { |
429 | fprintf(stderr, "%s:%d: ", cur_filename ? : "<stdin>", | 614 | print_location(); |
430 | cur_line); | ||
431 | 615 | ||
432 | va_start(args, fmt); | 616 | va_start(args, fmt); |
433 | vfprintf(stderr, fmt, args); | 617 | vfprintf(stderr, fmt, args); |
@@ -445,7 +629,9 @@ static void genksyms_usage(void) | |||
445 | " -a, --arch Select architecture\n" | 629 | " -a, --arch Select architecture\n" |
446 | " -d, --debug Increment the debug level (repeatable)\n" | 630 | " -d, --debug Increment the debug level (repeatable)\n" |
447 | " -D, --dump Dump expanded symbol defs (for debugging only)\n" | 631 | " -D, --dump Dump expanded symbol defs (for debugging only)\n" |
448 | " -T, --dump-types file Dump expanded types into file (for debugging only)\n" | 632 | " -r, --reference file Read reference symbols from a file\n" |
633 | " -T, --dump-types file Dump expanded types into file\n" | ||
634 | " -p, --preserve Preserve reference modversions or fail\n" | ||
449 | " -w, --warnings Enable warnings\n" | 635 | " -w, --warnings Enable warnings\n" |
450 | " -q, --quiet Disable warnings (default)\n" | 636 | " -q, --quiet Disable warnings (default)\n" |
451 | " -h, --help Print this message\n" | 637 | " -h, --help Print this message\n" |
@@ -454,7 +640,9 @@ static void genksyms_usage(void) | |||
454 | " -a Select architecture\n" | 640 | " -a Select architecture\n" |
455 | " -d Increment the debug level (repeatable)\n" | 641 | " -d Increment the debug level (repeatable)\n" |
456 | " -D Dump expanded symbol defs (for debugging only)\n" | 642 | " -D Dump expanded symbol defs (for debugging only)\n" |
457 | " -T file Dump expanded types into file (for debugging only)\n" | 643 | " -r file Read reference symbols from a file\n" |
644 | " -T file Dump expanded types into file\n" | ||
645 | " -p Preserve reference modversions or fail\n" | ||
458 | " -w Enable warnings\n" | 646 | " -w Enable warnings\n" |
459 | " -q Disable warnings (default)\n" | 647 | " -q Disable warnings (default)\n" |
460 | " -h Print this message\n" | 648 | " -h Print this message\n" |
@@ -465,7 +653,7 @@ static void genksyms_usage(void) | |||
465 | 653 | ||
466 | int main(int argc, char **argv) | 654 | int main(int argc, char **argv) |
467 | { | 655 | { |
468 | FILE *dumpfile = NULL; | 656 | FILE *dumpfile = NULL, *ref_file = NULL; |
469 | int o; | 657 | int o; |
470 | 658 | ||
471 | #ifdef __GNU_LIBRARY__ | 659 | #ifdef __GNU_LIBRARY__ |
@@ -475,16 +663,18 @@ int main(int argc, char **argv) | |||
475 | {"warnings", 0, 0, 'w'}, | 663 | {"warnings", 0, 0, 'w'}, |
476 | {"quiet", 0, 0, 'q'}, | 664 | {"quiet", 0, 0, 'q'}, |
477 | {"dump", 0, 0, 'D'}, | 665 | {"dump", 0, 0, 'D'}, |
666 | {"reference", 1, 0, 'r'}, | ||
478 | {"dump-types", 1, 0, 'T'}, | 667 | {"dump-types", 1, 0, 'T'}, |
668 | {"preserve", 0, 0, 'p'}, | ||
479 | {"version", 0, 0, 'V'}, | 669 | {"version", 0, 0, 'V'}, |
480 | {"help", 0, 0, 'h'}, | 670 | {"help", 0, 0, 'h'}, |
481 | {0, 0, 0, 0} | 671 | {0, 0, 0, 0} |
482 | }; | 672 | }; |
483 | 673 | ||
484 | while ((o = getopt_long(argc, argv, "a:dwqVDT:h", | 674 | while ((o = getopt_long(argc, argv, "a:dwqVDr:T:ph", |
485 | &long_opts[0], NULL)) != EOF) | 675 | &long_opts[0], NULL)) != EOF) |
486 | #else /* __GNU_LIBRARY__ */ | 676 | #else /* __GNU_LIBRARY__ */ |
487 | while ((o = getopt(argc, argv, "a:dwqVDT:h")) != EOF) | 677 | while ((o = getopt(argc, argv, "a:dwqVDr:T:ph")) != EOF) |
488 | #endif /* __GNU_LIBRARY__ */ | 678 | #endif /* __GNU_LIBRARY__ */ |
489 | switch (o) { | 679 | switch (o) { |
490 | case 'a': | 680 | case 'a': |
@@ -505,6 +695,14 @@ int main(int argc, char **argv) | |||
505 | case 'D': | 695 | case 'D': |
506 | flag_dump_defs = 1; | 696 | flag_dump_defs = 1; |
507 | break; | 697 | break; |
698 | case 'r': | ||
699 | flag_reference = 1; | ||
700 | ref_file = fopen(optarg, "r"); | ||
701 | if (!ref_file) { | ||
702 | perror(optarg); | ||
703 | return 1; | ||
704 | } | ||
705 | break; | ||
508 | case 'T': | 706 | case 'T': |
509 | flag_dump_types = 1; | 707 | flag_dump_types = 1; |
510 | dumpfile = fopen(optarg, "w"); | 708 | dumpfile = fopen(optarg, "w"); |
@@ -513,6 +711,9 @@ int main(int argc, char **argv) | |||
513 | return 1; | 711 | return 1; |
514 | } | 712 | } |
515 | break; | 713 | break; |
714 | case 'p': | ||
715 | flag_preserve = 1; | ||
716 | break; | ||
516 | case 'h': | 717 | case 'h': |
517 | genksyms_usage(); | 718 | genksyms_usage(); |
518 | return 0; | 719 | return 0; |
@@ -533,6 +734,9 @@ int main(int argc, char **argv) | |||
533 | /* setlinebuf(debugfile); */ | 734 | /* setlinebuf(debugfile); */ |
534 | } | 735 | } |
535 | 736 | ||
737 | if (flag_reference) | ||
738 | read_reference(ref_file); | ||
739 | |||
536 | yyparse(); | 740 | yyparse(); |
537 | 741 | ||
538 | if (flag_dump_types && visited_symbols) { | 742 | if (flag_dump_types && visited_symbols) { |
diff --git a/scripts/genksyms/genksyms.h b/scripts/genksyms/genksyms.h index 2668287aa498..2831158426cd 100644 --- a/scripts/genksyms/genksyms.h +++ b/scripts/genksyms/genksyms.h | |||
@@ -29,6 +29,10 @@ enum symbol_type { | |||
29 | SYM_NORMAL, SYM_TYPEDEF, SYM_ENUM, SYM_STRUCT, SYM_UNION | 29 | SYM_NORMAL, SYM_TYPEDEF, SYM_ENUM, SYM_STRUCT, SYM_UNION |
30 | }; | 30 | }; |
31 | 31 | ||
32 | enum symbol_status { | ||
33 | STATUS_UNCHANGED, STATUS_DEFINED, STATUS_MODIFIED | ||
34 | }; | ||
35 | |||
32 | struct string_list { | 36 | struct string_list { |
33 | struct string_list *next; | 37 | struct string_list *next; |
34 | enum symbol_type tag; | 38 | enum symbol_type tag; |
@@ -43,6 +47,8 @@ struct symbol { | |||
43 | struct symbol *expansion_trail; | 47 | struct symbol *expansion_trail; |
44 | struct symbol *visited; | 48 | struct symbol *visited; |
45 | int is_extern; | 49 | int is_extern; |
50 | int is_declared; | ||
51 | enum symbol_status status; | ||
46 | }; | 52 | }; |
47 | 53 | ||
48 | typedef struct string_list **yystype; | 54 | typedef struct string_list **yystype; |