net: remove INIT_RCU_HEAD() usage
[safe/jmp/linux-2.6] / scripts / genksyms / genksyms.c
index b798e28..af6b836 100644 (file)
 #define HASH_BUCKETS  4096
 
 static struct symbol *symtab[HASH_BUCKETS];
-FILE *debugfile;
+static FILE *debugfile;
 
 int cur_line = 1;
-char *cur_filename, *output_directory;
+char *cur_filename;
 
-int flag_debug, flag_dump_defs, flag_warnings;
-const char *arch = "";
-const char *mod_prefix = "";
+static int flag_debug, flag_dump_defs, flag_reference, flag_dump_types,
+          flag_preserve, flag_warnings;
+static const char *arch = "";
+static const char *mod_prefix = "";
 
 static int errors;
 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 int equal_list(struct string_list *a, struct string_list *b);
+static void print_list(FILE * f, struct string_list *list);
+static void print_location(void);
+static void print_type_name(enum symbol_type type, const char *name);
+
 /*----------------------------------------------------------------------*/
 
 static const unsigned int crctab32[] = {
@@ -112,27 +119,26 @@ static const unsigned int crctab32[] = {
        0x2d02ef8dU
 };
 
-static inline unsigned long
-partial_crc32_one(unsigned char c, unsigned long crc)
+static unsigned long partial_crc32_one(unsigned char c, unsigned long crc)
 {
        return crctab32[(crc ^ c) & 0xff] ^ (crc >> 8);
 }
 
-static inline unsigned long partial_crc32(const char *s, unsigned long crc)
+static unsigned long partial_crc32(const char *s, unsigned long crc)
 {
        while (*s)
                crc = partial_crc32_one(*s++, crc);
        return crc;
 }
 
-static inline unsigned long crc32(const char *s)
+static unsigned long crc32(const char *s)
 {
        return partial_crc32(s, 0xffffffff) ^ 0xffffffff;
 }
 
 /*----------------------------------------------------------------------*/
 
-static inline enum symbol_type map_to_ns(enum symbol_type t)
+static enum symbol_type map_to_ns(enum symbol_type t)
 {
        if (t == SYM_TYPEDEF)
                t = SYM_NORMAL;
@@ -147,37 +153,99 @@ struct symbol *find_symbol(const char *name, enum symbol_type ns)
        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)
+               if (map_to_ns(sym->type) == map_to_ns(ns) &&
+                   strcmp(name, sym->name) == 0 &&
+                   sym->is_declared)
                        break;
 
        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)
+{
+       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 = 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(type)
-                   && strcmp(name, sym->name) == 0) {
-                       if (!equal_list(sym->defn, defn))
+       enum symbol_status status = STATUS_UNCHANGED;
+
+       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 (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;
        sym->defn = defn;
        sym->expansion_trail = NULL;
+       sym->visited = NULL;
        sym->is_extern = is_extern;
 
        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);
@@ -191,9 +259,21 @@ 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);
+}
+
 /*----------------------------------------------------------------------*/
 
-inline void free_node(struct string_list *node)
+void free_node(struct string_list *node)
 {
        free(node->string);
        free(node);
@@ -208,7 +288,7 @@ void free_list(struct string_list *s, struct string_list *e)
        }
 }
 
-inline struct string_list *copy_node(struct string_list *node)
+struct string_list *copy_node(struct string_list *node)
 {
        struct string_list *newnode;
 
@@ -219,22 +299,7 @@ inline struct string_list *copy_node(struct string_list *node)
        return newnode;
 }
 
-struct string_list *copy_list(struct string_list *s, struct string_list *e)
-{
-       struct string_list *h, *p;
-
-       if (s == e)
-               return NULL;
-
-       p = h = copy_node(s);
-       while ((s = s->next) != e)
-               p = p->next = copy_node(s);
-       p->next = NULL;
-
-       return h;
-}
-
-int equal_list(struct string_list *a, struct string_list *b)
+static int equal_list(struct string_list *a, struct string_list *b)
 {
        while (a && b) {
                if (a->tag != b->tag || strcmp(a->string, b->string))
@@ -246,31 +311,100 @@ int equal_list(struct string_list *a, struct string_list *b)
        return !a && !b;
 }
 
-static inline void print_node(FILE * f, struct string_list *list)
+#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] == '#') {
+               int n;
+
+               for (n = 0; n < ARRAY_SIZE(symbol_type_name); n++) {
+                       if (node.string[0] == symbol_type_name[n][0]) {
+                               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)
 {
-       switch (list->tag) {
-       case SYM_STRUCT:
-               putc('s', f);
-               goto printit;
-       case SYM_UNION:
-               putc('u', f);
-               goto printit;
-       case SYM_ENUM:
-               putc('e', f);
-               goto printit;
-       case SYM_TYPEDEF:
-               putc('t', f);
-               goto printit;
-
-             printit:
+       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);
                putc('#', f);
-       case SYM_NORMAL:
-               fputs(list->string, f);
-               break;
        }
+       fputs(list->string, f);
 }
 
-void print_list(FILE * f, struct string_list *list)
+static void print_list(FILE * f, struct string_list *list)
 {
        struct string_list **e, **b;
        struct string_list *tmp, **tmp2;
@@ -299,9 +433,9 @@ void print_list(FILE * f, struct string_list *list)
        }
 }
 
-static unsigned long
-expand_and_crc_list(struct string_list *list, unsigned long crc)
+static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc)
 {
+       struct string_list *list = sym->defn;
        struct string_list **e, **b;
        struct string_list *tmp, **tmp2;
        int elem = 1;
@@ -336,6 +470,7 @@ expand_and_crc_list(struct string_list *list, unsigned long crc)
 
                case SYM_TYPEDEF:
                        subsym = find_symbol(cur->string, cur->tag);
+                       /* FIXME: Bad reference files can segfault here. */
                        if (subsym->expansion_trail) {
                                if (flag_dump_defs)
                                        fprintf(debugfile, "%s ", cur->string);
@@ -344,7 +479,7 @@ expand_and_crc_list(struct string_list *list, unsigned long crc)
                        } else {
                                subsym->expansion_trail = expansion_trail;
                                expansion_trail = subsym;
-                               crc = expand_and_crc_list(subsym->defn, crc);
+                               crc = expand_and_crc_sym(subsym, crc);
                        }
                        break;
 
@@ -372,9 +507,22 @@ expand_and_crc_list(struct string_list *list, unsigned long crc)
                                t = n;
 
                                n = xmalloc(sizeof(*n));
-                               n->string = xstrdup("{ UNKNOWN }");
+                               n->string = xstrdup("{");
                                n->tag = SYM_NORMAL;
                                n->next = t;
+                               t = n;
+
+                               n = xmalloc(sizeof(*n));
+                               n->string = xstrdup("UNKNOWN");
+                               n->tag = SYM_NORMAL;
+                               n->next = t;
+                               t = n;
+
+                               n = xmalloc(sizeof(*n));
+                               n->string = xstrdup("}");
+                               n->tag = SYM_NORMAL;
+                               n->next = t;
+                               t = n;
 
                                subsym =
                                    add_symbol(cur->string, cur->tag, n, 0);
@@ -386,21 +534,30 @@ expand_and_crc_list(struct string_list *list, unsigned long crc)
                                                cur->string);
                                }
 
-                               crc =
-                                   partial_crc32(symbol_type_name[cur->tag],
-                                                 crc);
+                               crc = partial_crc32(symbol_type_name[cur->tag],
+                                                   crc);
                                crc = partial_crc32_one(' ', crc);
                                crc = partial_crc32(cur->string, crc);
                                crc = partial_crc32_one(' ', crc);
                        } else {
                                subsym->expansion_trail = expansion_trail;
                                expansion_trail = subsym;
-                               crc = expand_and_crc_list(subsym->defn, crc);
+                               crc = expand_and_crc_sym(subsym, crc);
                        }
                        break;
                }
        }
 
+       {
+               static struct symbol **end = &visited_symbols;
+
+               if (!sym->visited) {
+                       *end = sym;
+                       end = &sym->visited;
+                       sym->visited = (struct symbol *)-1L;
+               }
+       }
+
        return crc;
 }
 
@@ -413,20 +570,42 @@ void export_symbol(const char *name)
                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;
 
-               crc = expand_and_crc_list(sym->defn, 0xffffffff) ^ 0xffffffff;
+               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);
@@ -438,18 +617,17 @@ void export_symbol(const char *name)
 
 /*----------------------------------------------------------------------*/
 
-void error(const char *fmt, ...)
+static void print_location(void)
 {
-       va_list args;
-
-       if (flag_warnings) {
-               va_start(args, fmt);
-               vfprintf(stderr, fmt, args);
-               va_end(args);
-               putc('\n', stderr);
+       fprintf(stderr, "%s:%d: ", cur_filename ? : "<stdin>", cur_line);
+}
 
-               errors++;
-       }
+static void print_type_name(enum symbol_type type, const char *name)
+{
+       if (type != SYM_NORMAL)
+               fprintf(stderr, "%s %s", symbol_type_name[type], name);
+       else
+               fprintf(stderr, "%s", name);
 }
 
 void error_with_pos(const char *fmt, ...)
@@ -457,8 +635,7 @@ 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);
@@ -469,19 +646,27 @@ void error_with_pos(const char *fmt, ...)
        }
 }
 
-void genksyms_usage(void)
+static void genksyms_usage(void)
 {
-       fputs("Usage:\n" "genksyms [-dDwqhV] > /path/to/.tmp_obj.ver\n" "\n"
+       fputs("Usage:\n" "genksyms [-adDTwqhV] > /path/to/.tmp_obj.ver\n" "\n"
 #ifdef __GNU_LIBRARY__
+             "  -a, --arch            Select architecture\n"
              "  -d, --debug           Increment the debug level (repeatable)\n"
              "  -D, --dump            Dump expanded symbol defs (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"
              "  -d                    Increment the debug level (repeatable)\n"
              "  -D                    Dump expanded symbol defs (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"
@@ -492,6 +677,7 @@ void genksyms_usage(void)
 
 int main(int argc, char **argv)
 {
+       FILE *dumpfile = NULL, *ref_file = NULL;
        int o;
 
 #ifdef __GNU_LIBRARY__
@@ -501,15 +687,18 @@ int main(int argc, char **argv)
                {"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:dwqVDk:p:",
+       while ((o = getopt_long(argc, argv, "a:dwqVDr:T:ph",
                                &long_opts[0], NULL)) != EOF)
 #else                          /* __GNU_LIBRARY__ */
-       while ((o = getopt(argc, argv, "a:dwqVDk:p:")) != EOF)
+       while ((o = getopt(argc, argv, "a:dwqVDr:T:ph")) != EOF)
 #endif                         /* __GNU_LIBRARY__ */
                switch (o) {
                case 'a':
@@ -530,6 +719,25 @@ 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");
+                       if (!dumpfile) {
+                               perror(optarg);
+                               return 1;
+                       }
+                       break;
+               case 'p':
+                       flag_preserve = 1;
+                       break;
                case 'h':
                        genksyms_usage();
                        return 0;
@@ -537,7 +745,7 @@ int main(int argc, char **argv)
                        genksyms_usage();
                        return 1;
                }
-       if ((strcmp(arch, "v850") == 0) || (strcmp(arch, "h8300") == 0))
+       if ((strcmp(arch, "h8300") == 0) || (strcmp(arch, "blackfin") == 0))
                mod_prefix = "_";
        {
                extern int yydebug;
@@ -550,8 +758,33 @@ int main(int argc, char **argv)
                /* setlinebuf(debugfile); */
        }
 
+       if (flag_reference)
+               read_reference(ref_file);
+
        yyparse();
 
+       if (flag_dump_types && visited_symbols) {
+               while (visited_symbols != (struct symbol *)-1L) {
+                       struct symbol *sym = visited_symbols;
+
+                       if (sym->is_override)
+                               fputs("override ", dumpfile);
+                       if (sym->type != SYM_NORMAL) {
+                               putc(symbol_type_name[sym->type][0], dumpfile);
+                               putc('#', dumpfile);
+                       }
+                       fputs(sym->name, dumpfile);
+                       putc(' ', dumpfile);
+                       if (sym->is_extern)
+                               fputs("extern ", dumpfile);
+                       print_list(dumpfile, sym->defn);
+                       putc('\n', dumpfile);
+
+                       visited_symbols = sym->visited;
+                       sym->visited = NULL;
+               }
+       }
+
        if (flag_debug) {
                fprintf(debugfile, "Hash table occupancy %d/%d = %g\n",
                        nsyms, HASH_BUCKETS,