diff options
Diffstat (limited to 'scripts/genksyms/genksyms.c')
| -rw-r--r-- | scripts/genksyms/genksyms.c | 438 |
1 files changed, 375 insertions, 63 deletions
diff --git a/scripts/genksyms/genksyms.c b/scripts/genksyms/genksyms.c index 4f8a3007e45..88632df4381 100644 --- a/scripts/genksyms/genksyms.c +++ b/scripts/genksyms/genksyms.c @@ -40,10 +40,11 @@ static struct symbol *symtab[HASH_BUCKETS]; static FILE *debugfile; int cur_line = 1; -char *cur_filename; +char *cur_filename, *source_file; +int in_source_file; -static int flag_debug, flag_dump_defs, flag_dump_types, flag_warnings; -static const char *arch = ""; +static int flag_debug, flag_dump_defs, flag_reference, flag_dump_types, + flag_preserve, flag_warnings; static const char *mod_prefix = ""; static int errors; @@ -52,12 +53,24 @@ static int nsyms; static struct symbol *expansion_trail; static struct symbol *visited_symbols; -static const char *const symbol_type_name[] = { - "normal", "typedef", "enum", "struct", "union" +static const struct { + int n; + const char *name; +} symbol_types[] = { + [SYM_NORMAL] = { 0, NULL}, + [SYM_TYPEDEF] = {'t', "typedef"}, + [SYM_ENUM] = {'e', "enum"}, + [SYM_STRUCT] = {'s', "struct"}, + [SYM_UNION] = {'u', "union"}, + [SYM_ENUM_CONST] = {'E', "enum constant"}, }; static int equal_list(struct string_list *a, struct string_list *b); static void print_list(FILE * f, struct string_list *list); +static struct string_list *concat_list(struct string_list *start, ...); +static struct string_list *mk_node(const char *string); +static void print_location(void); +static void print_type_name(enum symbol_type type, const char *name); /*----------------------------------------------------------------------*/ @@ -137,41 +150,142 @@ static unsigned long crc32(const char *s) static enum symbol_type map_to_ns(enum symbol_type t) { - if (t == SYM_TYPEDEF) - t = SYM_NORMAL; - else if (t == SYM_UNION) - t = SYM_STRUCT; + switch (t) { + case SYM_ENUM_CONST: + case SYM_NORMAL: + case SYM_TYPEDEF: + return SYM_NORMAL; + case SYM_ENUM: + case SYM_STRUCT: + case SYM_UNION: + return SYM_STRUCT; + } return t; } -struct symbol *find_symbol(const char *name, enum symbol_type ns) +struct symbol *find_symbol(const char *name, enum symbol_type ns, int exact) { unsigned long h = crc32(name) % HASH_BUCKETS; struct symbol *sym; for (sym = symtab[h]; sym; sym = sym->hash_next) if (map_to_ns(sym->type) == map_to_ns(ns) && - strcmp(name, sym->name) == 0) + strcmp(name, sym->name) == 0 && + sym->is_declared) break; + if (exact && sym && sym->type != ns) + return NULL; return sym; } -struct symbol *add_symbol(const char *name, enum symbol_type type, - struct string_list *defn, int is_extern) +static int is_unknown_symbol(struct symbol *sym) { - unsigned long h = crc32(name) % HASH_BUCKETS; + struct string_list *defn; + + return ((sym->type == SYM_STRUCT || + sym->type == SYM_UNION || + sym->type == SYM_ENUM) && + (defn = sym->defn) && defn->tag == SYM_NORMAL && + strcmp(defn->string, "}") == 0 && + (defn = defn->next) && defn->tag == SYM_NORMAL && + strcmp(defn->string, "UNKNOWN") == 0 && + (defn = defn->next) && defn->tag == SYM_NORMAL && + strcmp(defn->string, "{") == 0); +} + +static struct symbol *__add_symbol(const char *name, enum symbol_type type, + struct string_list *defn, int is_extern, + int is_reference) +{ + unsigned long h; struct symbol *sym; + enum symbol_status status = STATUS_UNCHANGED; + /* The parser adds symbols in the order their declaration completes, + * so it is safe to store the value of the previous enum constant in + * a static variable. + */ + static int enum_counter; + static struct string_list *last_enum_expr; + + if (type == SYM_ENUM_CONST) { + if (defn) { + free_list(last_enum_expr, NULL); + last_enum_expr = copy_list_range(defn, NULL); + enum_counter = 1; + } else { + struct string_list *expr; + char buf[20]; + + snprintf(buf, sizeof(buf), "%d", enum_counter++); + if (last_enum_expr) { + expr = copy_list_range(last_enum_expr, NULL); + defn = concat_list(mk_node("("), + expr, + mk_node(")"), + mk_node("+"), + mk_node(buf), NULL); + } else { + defn = mk_node(buf); + } + } + } else if (type == SYM_ENUM) { + free_list(last_enum_expr, NULL); + last_enum_expr = NULL; + enum_counter = 0; + if (!name) + /* Anonymous enum definition, nothing more to do */ + return NULL; + } + h = crc32(name) % HASH_BUCKETS; for (sym = symtab[h]; sym; sym = sym->hash_next) { - if (map_to_ns(sym->type) == map_to_ns(type) - && strcmp(name, sym->name) == 0) { - if (!equal_list(sym->defn, defn)) + if (map_to_ns(sym->type) == map_to_ns(type) && + strcmp(name, sym->name) == 0) { + if (is_reference) + /* fall through */ ; + else if (sym->type == type && + equal_list(sym->defn, defn)) { + if (!sym->is_declared && sym->is_override) { + print_location(); + print_type_name(type, name); + fprintf(stderr, " modversion is " + "unchanged\n"); + } + sym->is_declared = 1; + return sym; + } else if (!sym->is_declared) { + if (sym->is_override && flag_preserve) { + print_location(); + fprintf(stderr, "ignoring "); + print_type_name(type, name); + fprintf(stderr, " modversion change\n"); + sym->is_declared = 1; + return sym; + } else { + status = is_unknown_symbol(sym) ? + STATUS_DEFINED : STATUS_MODIFIED; + } + } else { error_with_pos("redefinition of %s", name); - return sym; + return sym; + } + break; } } + if (sym) { + struct symbol **psym; + + for (psym = &symtab[h]; *psym; psym = &(*psym)->hash_next) { + if (*psym == sym) { + *psym = sym->hash_next; + break; + } + } + --nsyms; + } + sym = xmalloc(sizeof(*sym)); sym->name = name; sym->type = type; @@ -183,9 +297,17 @@ struct symbol *add_symbol(const char *name, enum symbol_type type, sym->hash_next = symtab[h]; symtab[h] = sym; + sym->is_declared = !is_reference; + sym->status = status; + sym->is_override = 0; + if (flag_debug) { - fprintf(debugfile, "Defn for %s %s == <", - symbol_type_name[type], name); + if (symbol_types[type].name) + fprintf(debugfile, "Defn for %s %s == <", + symbol_types[type].name, name); + else + fprintf(debugfile, "Defn for type%d %s == <", + type, name); if (is_extern) fputs("extern ", debugfile); print_list(debugfile, defn); @@ -196,6 +318,18 @@ struct symbol *add_symbol(const char *name, enum symbol_type type, return sym; } +struct symbol *add_symbol(const char *name, enum symbol_type type, + struct string_list *defn, int is_extern) +{ + return __add_symbol(name, type, defn, is_extern, 0); +} + +static struct symbol *add_reference_symbol(const char *name, enum symbol_type type, + struct string_list *defn, int is_extern) +{ + return __add_symbol(name, type, defn, is_extern, 1); +} + /*----------------------------------------------------------------------*/ void free_node(struct string_list *node) @@ -213,6 +347,35 @@ void free_list(struct string_list *s, struct string_list *e) } } +static struct string_list *mk_node(const char *string) +{ + struct string_list *newnode; + + newnode = xmalloc(sizeof(*newnode)); + newnode->string = xstrdup(string); + newnode->tag = SYM_NORMAL; + newnode->next = NULL; + + return newnode; +} + +static struct string_list *concat_list(struct string_list *start, ...) +{ + va_list ap; + struct string_list *n, *n2; + + if (!start) + return NULL; + for (va_start(ap, start); (n = va_arg(ap, struct string_list *));) { + for (n2 = n; n2->next; n2 = n2->next) + ; + n2->next = start; + start = n; + } + va_end(ap); + return start; +} + struct string_list *copy_node(struct string_list *node) { struct string_list *newnode; @@ -224,6 +387,22 @@ struct string_list *copy_node(struct string_list *node) return newnode; } +struct string_list *copy_list_range(struct string_list *start, + struct string_list *end) +{ + struct string_list *res, *n; + + if (start == end) + return NULL; + n = res = copy_node(start); + for (start = start->next; start != end; start = start->next) { + n->next = copy_node(start); + n = n->next; + } + n->next = NULL; + return res; +} + static int equal_list(struct string_list *a, struct string_list *b) { while (a && b) { @@ -236,10 +415,94 @@ static int equal_list(struct string_list *a, struct string_list *b) return !a && !b; } +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +static struct string_list *read_node(FILE *f) +{ + char buffer[256]; + struct string_list node = { + .string = buffer, + .tag = SYM_NORMAL }; + int c; + + while ((c = fgetc(f)) != EOF) { + if (c == ' ') { + if (node.string == buffer) + continue; + break; + } else if (c == '\n') { + if (node.string == buffer) + return NULL; + ungetc(c, f); + break; + } + if (node.string >= buffer + sizeof(buffer) - 1) { + fprintf(stderr, "Token too long\n"); + exit(1); + } + *node.string++ = c; + } + if (node.string == buffer) + return NULL; + *node.string = 0; + node.string = buffer; + + if (node.string[1] == '#') { + size_t n; + + for (n = 0; n < ARRAY_SIZE(symbol_types); n++) { + if (node.string[0] == symbol_types[n].n) { + node.tag = n; + node.string += 2; + return copy_node(&node); + } + } + fprintf(stderr, "Unknown type %c\n", node.string[0]); + exit(1); + } + return copy_node(&node); +} + +static void read_reference(FILE *f) +{ + while (!feof(f)) { + struct string_list *defn = NULL; + struct string_list *sym, *def; + int is_extern = 0, is_override = 0; + struct symbol *subsym; + + sym = read_node(f); + if (sym && sym->tag == SYM_NORMAL && + !strcmp(sym->string, "override")) { + is_override = 1; + free_node(sym); + sym = read_node(f); + } + if (!sym) + continue; + def = read_node(f); + if (def && def->tag == SYM_NORMAL && + !strcmp(def->string, "extern")) { + is_extern = 1; + free_node(def); + def = read_node(f); + } + while (def) { + def->next = defn; + defn = def; + def = read_node(f); + } + subsym = add_reference_symbol(xstrdup(sym->string), sym->tag, + defn, is_extern); + subsym->is_override = is_override; + free_node(sym); + } +} + static void print_node(FILE * f, struct string_list *list) { - if (list->tag != SYM_NORMAL) { - putc(symbol_type_name[list->tag][0], f); + if (symbol_types[list->tag].n) { + putc(symbol_types[list->tag].n, f); putc('#', f); } fputs(list->string, f); @@ -309,8 +572,10 @@ static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc) crc = partial_crc32_one(' ', crc); break; + case SYM_ENUM_CONST: case SYM_TYPEDEF: - subsym = find_symbol(cur->string, cur->tag); + subsym = find_symbol(cur->string, cur->tag, 0); + /* FIXME: Bad reference files can segfault here. */ if (subsym->expansion_trail) { if (flag_dump_defs) fprintf(debugfile, "%s ", cur->string); @@ -326,42 +591,30 @@ static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc) case SYM_STRUCT: case SYM_UNION: case SYM_ENUM: - subsym = find_symbol(cur->string, cur->tag); + subsym = find_symbol(cur->string, cur->tag, 0); if (!subsym) { - struct string_list *n, *t = NULL; + struct string_list *n; error_with_pos("expand undefined %s %s", - symbol_type_name[cur->tag], + symbol_types[cur->tag].name, cur->string); - - n = xmalloc(sizeof(*n)); - n->string = xstrdup(symbol_type_name[cur->tag]); - n->tag = SYM_NORMAL; - n->next = t; - t = n; - - n = xmalloc(sizeof(*n)); - n->string = xstrdup(cur->string); - n->tag = SYM_NORMAL; - n->next = t; - t = n; - - n = xmalloc(sizeof(*n)); - n->string = xstrdup("{ UNKNOWN }"); - n->tag = SYM_NORMAL; - n->next = t; - + n = concat_list(mk_node + (symbol_types[cur->tag].name), + mk_node(cur->string), + mk_node("{"), + mk_node("UNKNOWN"), + mk_node("}"), NULL); subsym = add_symbol(cur->string, cur->tag, n, 0); } if (subsym->expansion_trail) { if (flag_dump_defs) { fprintf(debugfile, "%s %s ", - symbol_type_name[cur->tag], + symbol_types[cur->tag].name, cur->string); } - crc = partial_crc32(symbol_type_name[cur->tag], + crc = partial_crc32(symbol_types[cur->tag].name, crc); crc = partial_crc32_one(' ', crc); crc = partial_crc32(cur->string, crc); @@ -392,25 +645,47 @@ void export_symbol(const char *name) { struct symbol *sym; - sym = find_symbol(name, SYM_NORMAL); + sym = find_symbol(name, SYM_NORMAL, 0); if (!sym) error_with_pos("export undefined symbol %s", name); else { unsigned long crc; + int has_changed = 0; if (flag_dump_defs) fprintf(debugfile, "Export %s == <", name); expansion_trail = (struct symbol *)-1L; + sym->expansion_trail = expansion_trail; + expansion_trail = sym; crc = expand_and_crc_sym(sym, 0xffffffff) ^ 0xffffffff; sym = expansion_trail; while (sym != (struct symbol *)-1L) { struct symbol *n = sym->expansion_trail; + + if (sym->status != STATUS_UNCHANGED) { + if (!has_changed) { + print_location(); + fprintf(stderr, "%s: %s: modversion " + "changed because of changes " + "in ", flag_preserve ? "error" : + "warning", name); + } else + fprintf(stderr, ", "); + print_type_name(sym->type, sym->name); + if (sym->status == STATUS_DEFINED) + fprintf(stderr, " (became defined)"); + has_changed = 1; + if (flag_preserve) + errors++; + } sym->expansion_trail = 0; sym = n; } + if (has_changed) + fprintf(stderr, "\n"); if (flag_dump_defs) fputs(">\n", debugfile); @@ -421,13 +696,26 @@ void export_symbol(const char *name) } /*----------------------------------------------------------------------*/ + +static void print_location(void) +{ + fprintf(stderr, "%s:%d: ", cur_filename ? : "<stdin>", cur_line); +} + +static void print_type_name(enum symbol_type type, const char *name) +{ + if (symbol_types[type].name) + fprintf(stderr, "%s %s", symbol_types[type].name, name); + else + fprintf(stderr, "%s", name); +} + void error_with_pos(const char *fmt, ...) { va_list args; if (flag_warnings) { - fprintf(stderr, "%s:%d: ", cur_filename ? : "<stdin>", - cur_line); + print_location(); va_start(args, fmt); vfprintf(stderr, fmt, args); @@ -442,19 +730,23 @@ static void genksyms_usage(void) { fputs("Usage:\n" "genksyms [-adDTwqhV] > /path/to/.tmp_obj.ver\n" "\n" #ifdef __GNU_LIBRARY__ - " -a, --arch Select architecture\n" + " -s, --symbol-prefix Select symbol prefix\n" " -d, --debug Increment the debug level (repeatable)\n" " -D, --dump Dump expanded symbol defs (for debugging only)\n" - " -T, --dump-types file Dump expanded types into file (for debugging only)\n" + " -r, --reference file Read reference symbols from a file\n" + " -T, --dump-types file Dump expanded types into file\n" + " -p, --preserve Preserve reference modversions or fail\n" " -w, --warnings Enable warnings\n" " -q, --quiet Disable warnings (default)\n" " -h, --help Print this message\n" " -V, --version Print the release version\n" #else /* __GNU_LIBRARY__ */ - " -a Select architecture\n" + " -s Select symbol prefix\n" " -d Increment the debug level (repeatable)\n" " -D Dump expanded symbol defs (for debugging only)\n" - " -T file Dump expanded types into file (for debugging only)\n" + " -r file Read reference symbols from a file\n" + " -T file Dump expanded types into file\n" + " -p Preserve reference modversions or fail\n" " -w Enable warnings\n" " -q Disable warnings (default)\n" " -h Print this message\n" @@ -465,30 +757,32 @@ static void genksyms_usage(void) int main(int argc, char **argv) { - FILE *dumpfile = NULL; + FILE *dumpfile = NULL, *ref_file = NULL; int o; #ifdef __GNU_LIBRARY__ struct option long_opts[] = { - {"arch", 1, 0, 'a'}, + {"symbol-prefix", 1, 0, 's'}, {"debug", 0, 0, 'd'}, {"warnings", 0, 0, 'w'}, {"quiet", 0, 0, 'q'}, {"dump", 0, 0, 'D'}, + {"reference", 1, 0, 'r'}, {"dump-types", 1, 0, 'T'}, + {"preserve", 0, 0, 'p'}, {"version", 0, 0, 'V'}, {"help", 0, 0, 'h'}, {0, 0, 0, 0} }; - while ((o = getopt_long(argc, argv, "a:dwqVDT:h", + while ((o = getopt_long(argc, argv, "s:dwqVDr:T:ph", &long_opts[0], NULL)) != EOF) #else /* __GNU_LIBRARY__ */ - while ((o = getopt(argc, argv, "a:dwqVDT:h")) != EOF) + while ((o = getopt(argc, argv, "s:dwqVDr:T:ph")) != EOF) #endif /* __GNU_LIBRARY__ */ switch (o) { - case 'a': - arch = optarg; + case 's': + mod_prefix = optarg; break; case 'd': flag_debug++; @@ -505,6 +799,14 @@ int main(int argc, char **argv) case 'D': flag_dump_defs = 1; break; + case 'r': + flag_reference = 1; + ref_file = fopen(optarg, "r"); + if (!ref_file) { + perror(optarg); + return 1; + } + break; case 'T': flag_dump_types = 1; dumpfile = fopen(optarg, "w"); @@ -513,6 +815,9 @@ int main(int argc, char **argv) return 1; } break; + case 'p': + flag_preserve = 1; + break; case 'h': genksyms_usage(); return 0; @@ -520,8 +825,6 @@ int main(int argc, char **argv) genksyms_usage(); return 1; } - if ((strcmp(arch, "h8300") == 0) || (strcmp(arch, "blackfin") == 0)) - mod_prefix = "_"; { extern int yydebug; extern int yy_flex_debug; @@ -533,18 +836,27 @@ int main(int argc, char **argv) /* setlinebuf(debugfile); */ } + if (flag_reference) { + read_reference(ref_file); + fclose(ref_file); + } + yyparse(); if (flag_dump_types && visited_symbols) { while (visited_symbols != (struct symbol *)-1L) { struct symbol *sym = visited_symbols; - if (sym->type != SYM_NORMAL) { - putc(symbol_type_name[sym->type][0], dumpfile); + if (sym->is_override) + fputs("override ", dumpfile); + if (symbol_types[sym->type].n) { + putc(symbol_types[sym->type].n, dumpfile); putc('#', dumpfile); } fputs(sym->name, dumpfile); putc(' ', dumpfile); + if (sym->is_extern) + fputs("extern ", dumpfile); print_list(dumpfile, sym->defn); putc('\n', dumpfile); |
