summaryrefslogtreecommitdiffstats
path: root/scripts/genksyms
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/genksyms')
-rw-r--r--scripts/genksyms/.gitignore2
-rw-r--r--scripts/genksyms/Makefile30
-rw-r--r--scripts/genksyms/genksyms.c863
-rw-r--r--scripts/genksyms/genksyms.h83
-rw-r--r--scripts/genksyms/keywords.c78
-rw-r--r--scripts/genksyms/lex.l440
-rw-r--r--scripts/genksyms/parse.y510
7 files changed, 2006 insertions, 0 deletions
diff --git a/scripts/genksyms/.gitignore b/scripts/genksyms/.gitignore
new file mode 100644
index 0000000000..0b275abf94
--- /dev/null
+++ b/scripts/genksyms/.gitignore
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+/genksyms
diff --git a/scripts/genksyms/Makefile b/scripts/genksyms/Makefile
new file mode 100644
index 0000000000..d6a422a63b
--- /dev/null
+++ b/scripts/genksyms/Makefile
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0
+
+hostprogs-always-y += genksyms
+
+genksyms-objs := genksyms.o parse.tab.o lex.lex.o
+
+# FIXME: fix the ambiguous grammar in parse.y and delete this hack
+#
+# Suppress shift/reduce, reduce/reduce conflicts warnings
+# unless W=1 is specified.
+#
+# Just in case, run "$(YACC) --version" without suppressing stderr
+# so that 'bison: not found' will be displayed if it is missing.
+ifeq ($(findstring 1,$(KBUILD_EXTRA_WARN)),)
+
+quiet_cmd_bison_no_warn = $(quiet_cmd_bison)
+ cmd_bison_no_warn = $(YACC) --version >/dev/null; \
+ $(cmd_bison) 2>/dev/null
+
+$(obj)/pars%.tab.c $(obj)/pars%.tab.h: $(src)/pars%.y FORCE
+ $(call if_changed,bison_no_warn)
+
+endif
+
+# -I needed for generated C source to include headers in source tree
+HOSTCFLAGS_parse.tab.o := -I $(srctree)/$(src)
+HOSTCFLAGS_lex.lex.o := -I $(srctree)/$(src)
+
+# dependencies on generated files need to be listed explicitly
+$(obj)/lex.lex.o: $(obj)/parse.tab.h
diff --git a/scripts/genksyms/genksyms.c b/scripts/genksyms/genksyms.c
new file mode 100644
index 0000000000..f5dfdb9d80
--- /dev/null
+++ b/scripts/genksyms/genksyms.c
@@ -0,0 +1,863 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Generate kernel symbol version hashes.
+ Copyright 1996, 1997 Linux International.
+
+ New implementation contributed by Richard Henderson <rth@tamu.edu>
+ Based on original work by Bjorn Ekwall <bj0rn@blox.se>
+
+ This file was part of the Linux modutils 2.4.22: moved back into the
+ kernel sources by Rusty Russell/Kai Germaschewski.
+
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <stdarg.h>
+#ifdef __GNU_LIBRARY__
+#include <getopt.h>
+#endif /* __GNU_LIBRARY__ */
+
+#include "genksyms.h"
+/*----------------------------------------------------------------------*/
+
+#define HASH_BUCKETS 4096
+
+static struct symbol *symtab[HASH_BUCKETS];
+static FILE *debugfile;
+
+int cur_line = 1;
+char *cur_filename;
+int in_source_file;
+
+static int flag_debug, flag_dump_defs, flag_reference, flag_dump_types,
+ flag_preserve, flag_warnings;
+
+static int errors;
+static int nsyms;
+
+static struct symbol *expansion_trail;
+static struct symbol *visited_symbols;
+
+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);
+
+/*----------------------------------------------------------------------*/
+
+static const unsigned int crctab32[] = {
+ 0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U,
+ 0x706af48fU, 0xe963a535U, 0x9e6495a3U, 0x0edb8832U, 0x79dcb8a4U,
+ 0xe0d5e91eU, 0x97d2d988U, 0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U,
+ 0x90bf1d91U, 0x1db71064U, 0x6ab020f2U, 0xf3b97148U, 0x84be41deU,
+ 0x1adad47dU, 0x6ddde4ebU, 0xf4d4b551U, 0x83d385c7U, 0x136c9856U,
+ 0x646ba8c0U, 0xfd62f97aU, 0x8a65c9ecU, 0x14015c4fU, 0x63066cd9U,
+ 0xfa0f3d63U, 0x8d080df5U, 0x3b6e20c8U, 0x4c69105eU, 0xd56041e4U,
+ 0xa2677172U, 0x3c03e4d1U, 0x4b04d447U, 0xd20d85fdU, 0xa50ab56bU,
+ 0x35b5a8faU, 0x42b2986cU, 0xdbbbc9d6U, 0xacbcf940U, 0x32d86ce3U,
+ 0x45df5c75U, 0xdcd60dcfU, 0xabd13d59U, 0x26d930acU, 0x51de003aU,
+ 0xc8d75180U, 0xbfd06116U, 0x21b4f4b5U, 0x56b3c423U, 0xcfba9599U,
+ 0xb8bda50fU, 0x2802b89eU, 0x5f058808U, 0xc60cd9b2U, 0xb10be924U,
+ 0x2f6f7c87U, 0x58684c11U, 0xc1611dabU, 0xb6662d3dU, 0x76dc4190U,
+ 0x01db7106U, 0x98d220bcU, 0xefd5102aU, 0x71b18589U, 0x06b6b51fU,
+ 0x9fbfe4a5U, 0xe8b8d433U, 0x7807c9a2U, 0x0f00f934U, 0x9609a88eU,
+ 0xe10e9818U, 0x7f6a0dbbU, 0x086d3d2dU, 0x91646c97U, 0xe6635c01U,
+ 0x6b6b51f4U, 0x1c6c6162U, 0x856530d8U, 0xf262004eU, 0x6c0695edU,
+ 0x1b01a57bU, 0x8208f4c1U, 0xf50fc457U, 0x65b0d9c6U, 0x12b7e950U,
+ 0x8bbeb8eaU, 0xfcb9887cU, 0x62dd1ddfU, 0x15da2d49U, 0x8cd37cf3U,
+ 0xfbd44c65U, 0x4db26158U, 0x3ab551ceU, 0xa3bc0074U, 0xd4bb30e2U,
+ 0x4adfa541U, 0x3dd895d7U, 0xa4d1c46dU, 0xd3d6f4fbU, 0x4369e96aU,
+ 0x346ed9fcU, 0xad678846U, 0xda60b8d0U, 0x44042d73U, 0x33031de5U,
+ 0xaa0a4c5fU, 0xdd0d7cc9U, 0x5005713cU, 0x270241aaU, 0xbe0b1010U,
+ 0xc90c2086U, 0x5768b525U, 0x206f85b3U, 0xb966d409U, 0xce61e49fU,
+ 0x5edef90eU, 0x29d9c998U, 0xb0d09822U, 0xc7d7a8b4U, 0x59b33d17U,
+ 0x2eb40d81U, 0xb7bd5c3bU, 0xc0ba6cadU, 0xedb88320U, 0x9abfb3b6U,
+ 0x03b6e20cU, 0x74b1d29aU, 0xead54739U, 0x9dd277afU, 0x04db2615U,
+ 0x73dc1683U, 0xe3630b12U, 0x94643b84U, 0x0d6d6a3eU, 0x7a6a5aa8U,
+ 0xe40ecf0bU, 0x9309ff9dU, 0x0a00ae27U, 0x7d079eb1U, 0xf00f9344U,
+ 0x8708a3d2U, 0x1e01f268U, 0x6906c2feU, 0xf762575dU, 0x806567cbU,
+ 0x196c3671U, 0x6e6b06e7U, 0xfed41b76U, 0x89d32be0U, 0x10da7a5aU,
+ 0x67dd4accU, 0xf9b9df6fU, 0x8ebeeff9U, 0x17b7be43U, 0x60b08ed5U,
+ 0xd6d6a3e8U, 0xa1d1937eU, 0x38d8c2c4U, 0x4fdff252U, 0xd1bb67f1U,
+ 0xa6bc5767U, 0x3fb506ddU, 0x48b2364bU, 0xd80d2bdaU, 0xaf0a1b4cU,
+ 0x36034af6U, 0x41047a60U, 0xdf60efc3U, 0xa867df55U, 0x316e8eefU,
+ 0x4669be79U, 0xcb61b38cU, 0xbc66831aU, 0x256fd2a0U, 0x5268e236U,
+ 0xcc0c7795U, 0xbb0b4703U, 0x220216b9U, 0x5505262fU, 0xc5ba3bbeU,
+ 0xb2bd0b28U, 0x2bb45a92U, 0x5cb36a04U, 0xc2d7ffa7U, 0xb5d0cf31U,
+ 0x2cd99e8bU, 0x5bdeae1dU, 0x9b64c2b0U, 0xec63f226U, 0x756aa39cU,
+ 0x026d930aU, 0x9c0906a9U, 0xeb0e363fU, 0x72076785U, 0x05005713U,
+ 0x95bf4a82U, 0xe2b87a14U, 0x7bb12baeU, 0x0cb61b38U, 0x92d28e9bU,
+ 0xe5d5be0dU, 0x7cdcefb7U, 0x0bdbdf21U, 0x86d3d2d4U, 0xf1d4e242U,
+ 0x68ddb3f8U, 0x1fda836eU, 0x81be16cdU, 0xf6b9265bU, 0x6fb077e1U,
+ 0x18b74777U, 0x88085ae6U, 0xff0f6a70U, 0x66063bcaU, 0x11010b5cU,
+ 0x8f659effU, 0xf862ae69U, 0x616bffd3U, 0x166ccf45U, 0xa00ae278U,
+ 0xd70dd2eeU, 0x4e048354U, 0x3903b3c2U, 0xa7672661U, 0xd06016f7U,
+ 0x4969474dU, 0x3e6e77dbU, 0xaed16a4aU, 0xd9d65adcU, 0x40df0b66U,
+ 0x37d83bf0U, 0xa9bcae53U, 0xdebb9ec5U, 0x47b2cf7fU, 0x30b5ffe9U,
+ 0xbdbdf21cU, 0xcabac28aU, 0x53b39330U, 0x24b4a3a6U, 0xbad03605U,
+ 0xcdd70693U, 0x54de5729U, 0x23d967bfU, 0xb3667a2eU, 0xc4614ab8U,
+ 0x5d681b02U, 0x2a6f2b94U, 0xb40bbe37U, 0xc30c8ea1U, 0x5a05df1bU,
+ 0x2d02ef8dU
+};
+
+static unsigned long partial_crc32_one(unsigned char c, unsigned long crc)
+{
+ return crctab32[(crc ^ c) & 0xff] ^ (crc >> 8);
+}
+
+static unsigned long partial_crc32(const char *s, unsigned long crc)
+{
+ while (*s)
+ crc = partial_crc32_one(*s++, crc);
+ return crc;
+}
+
+static unsigned long crc32(const char *s)
+{
+ return partial_crc32(s, 0xffffffff) ^ 0xffffffff;
+}
+
+/*----------------------------------------------------------------------*/
+
+static enum symbol_type map_to_ns(enum symbol_type t)
+{
+ 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, 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 &&
+ sym->is_declared)
+ break;
+
+ if (exact && sym && sym->type != ns)
+ return NULL;
+ return sym;
+}
+
+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;
+ 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 (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;
+ }
+ 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) {
+ 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);
+ fputs(">\n", debugfile);
+ }
+
+ ++nsyms;
+ 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)
+{
+ free(node->string);
+ free(node);
+}
+
+void free_list(struct string_list *s, struct string_list *e)
+{
+ while (s != e) {
+ struct string_list *next = s->next;
+ free_node(s);
+ s = next;
+ }
+}
+
+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;
+
+ newnode = xmalloc(sizeof(*newnode));
+ newnode->string = xstrdup(node->string);
+ newnode->tag = node->tag;
+
+ 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) {
+ if (a->tag != b->tag || strcmp(a->string, b->string))
+ return 0;
+ a = a->next;
+ b = b->next;
+ }
+
+ 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, in_string = 0;
+
+ while ((c = fgetc(f)) != EOF) {
+ if (!in_string && c == ' ') {
+ if (node.string == buffer)
+ continue;
+ break;
+ } else if (c == '"') {
+ in_string = !in_string;
+ } 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 (symbol_types[list->tag].n) {
+ putc(symbol_types[list->tag].n, f);
+ putc('#', f);
+ }
+ fputs(list->string, f);
+}
+
+static void print_list(FILE * f, struct string_list *list)
+{
+ struct string_list **e, **b;
+ struct string_list *tmp, **tmp2;
+ int elem = 1;
+
+ if (list == NULL) {
+ fputs("(nil)", f);
+ return;
+ }
+
+ tmp = list;
+ while ((tmp = tmp->next) != NULL)
+ elem++;
+
+ b = alloca(elem * sizeof(*e));
+ e = b + elem;
+ tmp2 = e - 1;
+
+ (*tmp2--) = list;
+ while ((list = list->next) != NULL)
+ *(tmp2--) = list;
+
+ while (b != e) {
+ print_node(f, *b++);
+ putc(' ', f);
+ }
+}
+
+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;
+
+ if (!list)
+ return crc;
+
+ tmp = list;
+ while ((tmp = tmp->next) != NULL)
+ elem++;
+
+ b = alloca(elem * sizeof(*e));
+ e = b + elem;
+ tmp2 = e - 1;
+
+ *(tmp2--) = list;
+ while ((list = list->next) != NULL)
+ *(tmp2--) = list;
+
+ while (b != e) {
+ struct string_list *cur;
+ struct symbol *subsym;
+
+ cur = *(b++);
+ switch (cur->tag) {
+ case SYM_NORMAL:
+ if (flag_dump_defs)
+ fprintf(debugfile, "%s ", cur->string);
+ crc = partial_crc32(cur->string, crc);
+ crc = partial_crc32_one(' ', crc);
+ break;
+
+ case SYM_ENUM_CONST:
+ case SYM_TYPEDEF:
+ 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);
+ crc = partial_crc32(cur->string, crc);
+ crc = partial_crc32_one(' ', crc);
+ } else {
+ subsym->expansion_trail = expansion_trail;
+ expansion_trail = subsym;
+ crc = expand_and_crc_sym(subsym, crc);
+ }
+ break;
+
+ case SYM_STRUCT:
+ case SYM_UNION:
+ case SYM_ENUM:
+ subsym = find_symbol(cur->string, cur->tag, 0);
+ if (!subsym) {
+ struct string_list *n;
+
+ error_with_pos("expand undefined %s %s",
+ symbol_types[cur->tag].name,
+ cur->string);
+ 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_types[cur->tag].name,
+ cur->string);
+ }
+
+ crc = partial_crc32(symbol_types[cur->tag].name,
+ 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_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;
+}
+
+void export_symbol(const char *name)
+{
+ struct symbol *sym;
+
+ 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);
+
+ printf("#SYMVER %s 0x%08lx\n", name, crc);
+ }
+}
+
+/*----------------------------------------------------------------------*/
+
+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) {
+ print_location();
+
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ putc('\n', stderr);
+
+ errors++;
+ }
+}
+
+static void genksyms_usage(void)
+{
+ fputs("Usage:\n" "genksyms [-adDTwqhVR] > /path/to/.tmp_obj.ver\n" "\n"
+#ifdef __GNU_LIBRARY__
+ " -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"
+ " -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__ */
+ " -s Select symbol prefix\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"
+ " -V Print the release version\n"
+#endif /* __GNU_LIBRARY__ */
+ , stderr);
+}
+
+int main(int argc, char **argv)
+{
+ FILE *dumpfile = NULL, *ref_file = NULL;
+ int o;
+
+#ifdef __GNU_LIBRARY__
+ struct option long_opts[] = {
+ {"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, "s:dwqVDr:T:ph",
+ &long_opts[0], NULL)) != EOF)
+#else /* __GNU_LIBRARY__ */
+ while ((o = getopt(argc, argv, "s:dwqVDr:T:ph")) != EOF)
+#endif /* __GNU_LIBRARY__ */
+ switch (o) {
+ case 'd':
+ flag_debug++;
+ break;
+ case 'w':
+ flag_warnings = 1;
+ break;
+ case 'q':
+ flag_warnings = 0;
+ break;
+ case 'V':
+ fputs("genksyms version 2.5.60\n", stderr);
+ break;
+ 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;
+ default:
+ genksyms_usage();
+ return 1;
+ }
+ {
+ extern int yydebug;
+ extern int yy_flex_debug;
+
+ yydebug = (flag_debug > 1);
+ yy_flex_debug = (flag_debug > 2);
+
+ debugfile = stderr;
+ /* 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->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);
+
+ visited_symbols = sym->visited;
+ sym->visited = NULL;
+ }
+ }
+
+ if (flag_debug) {
+ fprintf(debugfile, "Hash table occupancy %d/%d = %g\n",
+ nsyms, HASH_BUCKETS,
+ (double)nsyms / (double)HASH_BUCKETS);
+ }
+
+ if (dumpfile)
+ fclose(dumpfile);
+
+ return errors != 0;
+}
diff --git a/scripts/genksyms/genksyms.h b/scripts/genksyms/genksyms.h
new file mode 100644
index 0000000000..21ed2ec2d9
--- /dev/null
+++ b/scripts/genksyms/genksyms.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Generate kernel symbol version hashes.
+ Copyright 1996, 1997 Linux International.
+
+ New implementation contributed by Richard Henderson <rth@tamu.edu>
+ Based on original work by Bjorn Ekwall <bj0rn@blox.se>
+
+ This file is part of the Linux modutils.
+
+ */
+
+#ifndef MODUTILS_GENKSYMS_H
+#define MODUTILS_GENKSYMS_H 1
+
+#include <stdio.h>
+
+enum symbol_type {
+ SYM_NORMAL, SYM_TYPEDEF, SYM_ENUM, SYM_STRUCT, SYM_UNION,
+ SYM_ENUM_CONST
+};
+
+enum symbol_status {
+ STATUS_UNCHANGED, STATUS_DEFINED, STATUS_MODIFIED
+};
+
+struct string_list {
+ struct string_list *next;
+ enum symbol_type tag;
+ int in_source_file;
+ char *string;
+};
+
+struct symbol {
+ struct symbol *hash_next;
+ const char *name;
+ enum symbol_type type;
+ struct string_list *defn;
+ struct symbol *expansion_trail;
+ struct symbol *visited;
+ int is_extern;
+ int is_declared;
+ enum symbol_status status;
+ int is_override;
+};
+
+typedef struct string_list **yystype;
+#define YYSTYPE yystype
+
+extern int cur_line;
+extern char *cur_filename;
+extern int in_source_file;
+
+struct symbol *find_symbol(const char *name, enum symbol_type ns, int exact);
+struct symbol *add_symbol(const char *name, enum symbol_type type,
+ struct string_list *defn, int is_extern);
+void export_symbol(const char *);
+
+void free_node(struct string_list *list);
+void free_list(struct string_list *s, struct string_list *e);
+struct string_list *copy_node(struct string_list *);
+struct string_list *copy_list_range(struct string_list *start,
+ struct string_list *end);
+
+int yylex(void);
+int yyparse(void);
+
+void error_with_pos(const char *, ...) __attribute__ ((format(printf, 1, 2)));
+
+/*----------------------------------------------------------------------*/
+#define xmalloc(size) ({ void *__ptr = malloc(size); \
+ if(!__ptr && size != 0) { \
+ fprintf(stderr, "out of memory\n"); \
+ exit(1); \
+ } \
+ __ptr; })
+#define xstrdup(str) ({ char *__str = strdup(str); \
+ if (!__str) { \
+ fprintf(stderr, "out of memory\n"); \
+ exit(1); \
+ } \
+ __str; })
+
+#endif /* genksyms.h */
diff --git a/scripts/genksyms/keywords.c b/scripts/genksyms/keywords.c
new file mode 100644
index 0000000000..b85e0979a0
--- /dev/null
+++ b/scripts/genksyms/keywords.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0-only
+static struct resword {
+ const char *name;
+ int token;
+} keywords[] = {
+ { "__GENKSYMS_EXPORT_SYMBOL", EXPORT_SYMBOL_KEYW },
+ { "__asm", ASM_KEYW },
+ { "__asm__", ASM_KEYW },
+ { "__attribute", ATTRIBUTE_KEYW },
+ { "__attribute__", ATTRIBUTE_KEYW },
+ { "__const", CONST_KEYW },
+ { "__const__", CONST_KEYW },
+ { "__extension__", EXTENSION_KEYW },
+ { "__inline", INLINE_KEYW },
+ { "__inline__", INLINE_KEYW },
+ { "__signed", SIGNED_KEYW },
+ { "__signed__", SIGNED_KEYW },
+ { "__typeof", TYPEOF_KEYW },
+ { "__typeof__", TYPEOF_KEYW },
+ { "__volatile", VOLATILE_KEYW },
+ { "__volatile__", VOLATILE_KEYW },
+ { "__builtin_va_list", VA_LIST_KEYW },
+
+ { "__int128", BUILTIN_INT_KEYW },
+ { "__int128_t", BUILTIN_INT_KEYW },
+ { "__uint128_t", BUILTIN_INT_KEYW },
+
+ // According to rth, c99 defines "_Bool", "__restrict", "__restrict__", "restrict". KAO
+ { "_Bool", BOOL_KEYW },
+ { "__restrict", RESTRICT_KEYW },
+ { "__restrict__", RESTRICT_KEYW },
+ { "restrict", RESTRICT_KEYW },
+ { "asm", ASM_KEYW },
+
+ // c11 keywords that can be used at module scope
+ { "_Static_assert", STATIC_ASSERT_KEYW },
+
+ // attribute commented out in modutils 2.4.2. People are using 'attribute' as a
+ // field name which breaks the genksyms parser. It is not a gcc keyword anyway.
+ // KAO. },
+ // { "attribute", ATTRIBUTE_KEYW },
+
+ { "auto", AUTO_KEYW },
+ { "char", CHAR_KEYW },
+ { "const", CONST_KEYW },
+ { "double", DOUBLE_KEYW },
+ { "enum", ENUM_KEYW },
+ { "extern", EXTERN_KEYW },
+ { "float", FLOAT_KEYW },
+ { "inline", INLINE_KEYW },
+ { "int", INT_KEYW },
+ { "long", LONG_KEYW },
+ { "register", REGISTER_KEYW },
+ { "short", SHORT_KEYW },
+ { "signed", SIGNED_KEYW },
+ { "static", STATIC_KEYW },
+ { "struct", STRUCT_KEYW },
+ { "typedef", TYPEDEF_KEYW },
+ { "typeof", TYPEOF_KEYW },
+ { "union", UNION_KEYW },
+ { "unsigned", UNSIGNED_KEYW },
+ { "void", VOID_KEYW },
+ { "volatile", VOLATILE_KEYW },
+};
+
+#define NR_KEYWORDS (sizeof(keywords)/sizeof(struct resword))
+
+static int is_reserved_word(register const char *str, register unsigned int len)
+{
+ int i;
+ for (i = 0; i < NR_KEYWORDS; i++) {
+ struct resword *r = keywords + i;
+ int l = strlen(r->name);
+ if (len == l && !memcmp(str, r->name, len))
+ return r->token;
+ }
+ return -1;
+}
diff --git a/scripts/genksyms/lex.l b/scripts/genksyms/lex.l
new file mode 100644
index 0000000000..a4d7495eaf
--- /dev/null
+++ b/scripts/genksyms/lex.l
@@ -0,0 +1,440 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Lexical analysis for genksyms.
+ * Copyright 1996, 1997 Linux International.
+ *
+ * New implementation contributed by Richard Henderson <rth@tamu.edu>
+ * Based on original work by Bjorn Ekwall <bj0rn@blox.se>
+ *
+ * Taken from Linux modutils 2.4.22.
+ */
+
+%{
+
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "genksyms.h"
+#include "parse.tab.h"
+
+/* We've got a two-level lexer here. We let flex do basic tokenization
+ and then we categorize those basic tokens in the second stage. */
+#define YY_DECL static int yylex1(void)
+
+%}
+
+IDENT [A-Za-z_\$][A-Za-z0-9_\$]*
+
+O_INT 0[0-7]*
+D_INT [1-9][0-9]*
+X_INT 0[Xx][0-9A-Fa-f]+
+I_SUF [Uu]|[Ll]|[Uu][Ll]|[Ll][Uu]
+INT ({O_INT}|{D_INT}|{X_INT}){I_SUF}?
+
+FRAC ([0-9]*\.[0-9]+)|([0-9]+\.)
+EXP [Ee][+-]?[0-9]+
+F_SUF [FfLl]
+REAL ({FRAC}{EXP}?{F_SUF}?)|([0-9]+{EXP}{F_SUF}?)
+
+STRING L?\"([^\\\"]*\\.)*[^\\\"]*\"
+CHAR L?\'([^\\\']*\\.)*[^\\\']*\'
+
+MC_TOKEN ([~%^&*+=|<>/-]=)|(&&)|("||")|(->)|(<<)|(>>)
+
+/* We don't do multiple input files. */
+%option noyywrap
+
+%option noinput
+
+%%
+
+
+ /* Keep track of our location in the original source files. */
+^#[ \t]+{INT}[ \t]+\"[^\"\n]+\".*\n return FILENAME;
+^#.*\n cur_line++;
+\n cur_line++;
+
+ /* Ignore all other whitespace. */
+[ \t\f\v\r]+ ;
+
+
+{STRING} return STRING;
+{CHAR} return CHAR;
+{IDENT} return IDENT;
+
+ /* The Pedant requires that the other C multi-character tokens be
+ recognized as tokens. We don't actually use them since we don't
+ parse expressions, but we do want whitespace to be arranged
+ around them properly. */
+{MC_TOKEN} return OTHER;
+{INT} return INT;
+{REAL} return REAL;
+
+"..." return DOTS;
+
+ /* All other tokens are single characters. */
+. return yytext[0];
+
+
+%%
+
+/* Bring in the keyword recognizer. */
+
+#include "keywords.c"
+
+
+/* Macros to append to our phrase collection list. */
+
+/*
+ * We mark any token, that that equals to a known enumerator, as
+ * SYM_ENUM_CONST. The parser will change this for struct and union tags later,
+ * the only problem is struct and union members:
+ * enum e { a, b }; struct s { int a, b; }
+ * but in this case, the only effect will be, that the ABI checksums become
+ * more volatile, which is acceptable. Also, such collisions are quite rare,
+ * so far it was only observed in include/linux/telephony.h.
+ */
+#define _APP(T,L) do { \
+ cur_node = next_node; \
+ next_node = xmalloc(sizeof(*next_node)); \
+ next_node->next = cur_node; \
+ cur_node->string = memcpy(xmalloc(L+1), T, L+1); \
+ cur_node->tag = \
+ find_symbol(cur_node->string, SYM_ENUM_CONST, 1)?\
+ SYM_ENUM_CONST : SYM_NORMAL ; \
+ cur_node->in_source_file = in_source_file; \
+ } while (0)
+
+#define APP _APP(yytext, yyleng)
+
+
+/* The second stage lexer. Here we incorporate knowledge of the state
+ of the parser to tailor the tokens that are returned. */
+
+int
+yylex(void)
+{
+ static enum {
+ ST_NOTSTARTED, ST_NORMAL, ST_ATTRIBUTE, ST_ASM, ST_TYPEOF, ST_TYPEOF_1,
+ ST_BRACKET, ST_BRACE, ST_EXPRESSION, ST_STATIC_ASSERT,
+ } lexstate = ST_NOTSTARTED;
+
+ static int suppress_type_lookup, dont_want_brace_phrase;
+ static struct string_list *next_node;
+ static char *source_file;
+
+ int token, count = 0;
+ struct string_list *cur_node;
+
+ if (lexstate == ST_NOTSTARTED)
+ {
+ next_node = xmalloc(sizeof(*next_node));
+ next_node->next = NULL;
+ lexstate = ST_NORMAL;
+ }
+
+repeat:
+ token = yylex1();
+
+ if (token == 0)
+ return 0;
+ else if (token == FILENAME)
+ {
+ char *file, *e;
+
+ /* Save the filename and line number for later error messages. */
+
+ if (cur_filename)
+ free(cur_filename);
+
+ file = strchr(yytext, '\"')+1;
+ e = strchr(file, '\"');
+ *e = '\0';
+ cur_filename = memcpy(xmalloc(e-file+1), file, e-file+1);
+ cur_line = atoi(yytext+2);
+
+ if (!source_file) {
+ source_file = xstrdup(cur_filename);
+ in_source_file = 1;
+ } else {
+ in_source_file = (strcmp(cur_filename, source_file) == 0);
+ }
+
+ goto repeat;
+ }
+
+ switch (lexstate)
+ {
+ case ST_NORMAL:
+ switch (token)
+ {
+ case IDENT:
+ APP;
+ {
+ int r = is_reserved_word(yytext, yyleng);
+ if (r >= 0)
+ {
+ switch (token = r)
+ {
+ case ATTRIBUTE_KEYW:
+ lexstate = ST_ATTRIBUTE;
+ count = 0;
+ goto repeat;
+ case ASM_KEYW:
+ lexstate = ST_ASM;
+ count = 0;
+ goto repeat;
+ case TYPEOF_KEYW:
+ lexstate = ST_TYPEOF;
+ count = 0;
+ goto repeat;
+
+ case STRUCT_KEYW:
+ case UNION_KEYW:
+ case ENUM_KEYW:
+ dont_want_brace_phrase = 3;
+ suppress_type_lookup = 2;
+ goto fini;
+
+ case EXPORT_SYMBOL_KEYW:
+ goto fini;
+
+ case STATIC_ASSERT_KEYW:
+ lexstate = ST_STATIC_ASSERT;
+ count = 0;
+ goto repeat;
+ }
+ }
+ if (!suppress_type_lookup)
+ {
+ if (find_symbol(yytext, SYM_TYPEDEF, 1))
+ token = TYPE;
+ }
+ }
+ break;
+
+ case '[':
+ APP;
+ lexstate = ST_BRACKET;
+ count = 1;
+ goto repeat;
+
+ case '{':
+ APP;
+ if (dont_want_brace_phrase)
+ break;
+ lexstate = ST_BRACE;
+ count = 1;
+ goto repeat;
+
+ case '=': case ':':
+ APP;
+ lexstate = ST_EXPRESSION;
+ break;
+
+ default:
+ APP;
+ break;
+ }
+ break;
+
+ case ST_ATTRIBUTE:
+ APP;
+ switch (token)
+ {
+ case '(':
+ ++count;
+ goto repeat;
+ case ')':
+ if (--count == 0)
+ {
+ lexstate = ST_NORMAL;
+ token = ATTRIBUTE_PHRASE;
+ break;
+ }
+ goto repeat;
+ default:
+ goto repeat;
+ }
+ break;
+
+ case ST_ASM:
+ APP;
+ switch (token)
+ {
+ case '(':
+ ++count;
+ goto repeat;
+ case ')':
+ if (--count == 0)
+ {
+ lexstate = ST_NORMAL;
+ token = ASM_PHRASE;
+ break;
+ }
+ goto repeat;
+ default:
+ goto repeat;
+ }
+ break;
+
+ case ST_TYPEOF_1:
+ if (token == IDENT)
+ {
+ if (is_reserved_word(yytext, yyleng) >= 0
+ || find_symbol(yytext, SYM_TYPEDEF, 1))
+ {
+ yyless(0);
+ unput('(');
+ lexstate = ST_NORMAL;
+ token = TYPEOF_KEYW;
+ break;
+ }
+ _APP("(", 1);
+ }
+ lexstate = ST_TYPEOF;
+ /* FALLTHRU */
+
+ case ST_TYPEOF:
+ switch (token)
+ {
+ case '(':
+ if ( ++count == 1 )
+ lexstate = ST_TYPEOF_1;
+ else
+ APP;
+ goto repeat;
+ case ')':
+ APP;
+ if (--count == 0)
+ {
+ lexstate = ST_NORMAL;
+ token = TYPEOF_PHRASE;
+ break;
+ }
+ goto repeat;
+ default:
+ APP;
+ goto repeat;
+ }
+ break;
+
+ case ST_BRACKET:
+ APP;
+ switch (token)
+ {
+ case '[':
+ ++count;
+ goto repeat;
+ case ']':
+ if (--count == 0)
+ {
+ lexstate = ST_NORMAL;
+ token = BRACKET_PHRASE;
+ break;
+ }
+ goto repeat;
+ default:
+ goto repeat;
+ }
+ break;
+
+ case ST_BRACE:
+ APP;
+ switch (token)
+ {
+ case '{':
+ ++count;
+ goto repeat;
+ case '}':
+ if (--count == 0)
+ {
+ lexstate = ST_NORMAL;
+ token = BRACE_PHRASE;
+ break;
+ }
+ goto repeat;
+ default:
+ goto repeat;
+ }
+ break;
+
+ case ST_EXPRESSION:
+ switch (token)
+ {
+ case '(': case '[': case '{':
+ ++count;
+ APP;
+ goto repeat;
+ case '}':
+ /* is this the last line of an enum declaration? */
+ if (count == 0)
+ {
+ /* Put back the token we just read so's we can find it again
+ after registering the expression. */
+ unput(token);
+
+ lexstate = ST_NORMAL;
+ token = EXPRESSION_PHRASE;
+ break;
+ }
+ /* FALLTHRU */
+ case ')': case ']':
+ --count;
+ APP;
+ goto repeat;
+ case ',': case ';':
+ if (count == 0)
+ {
+ /* Put back the token we just read so's we can find it again
+ after registering the expression. */
+ unput(token);
+
+ lexstate = ST_NORMAL;
+ token = EXPRESSION_PHRASE;
+ break;
+ }
+ APP;
+ goto repeat;
+ default:
+ APP;
+ goto repeat;
+ }
+ break;
+
+ case ST_STATIC_ASSERT:
+ APP;
+ switch (token)
+ {
+ case '(':
+ ++count;
+ goto repeat;
+ case ')':
+ if (--count == 0)
+ {
+ lexstate = ST_NORMAL;
+ token = STATIC_ASSERT_PHRASE;
+ break;
+ }
+ goto repeat;
+ default:
+ goto repeat;
+ }
+ break;
+
+ default:
+ exit(1);
+ }
+fini:
+
+ if (suppress_type_lookup > 0)
+ --suppress_type_lookup;
+ if (dont_want_brace_phrase > 0)
+ --dont_want_brace_phrase;
+
+ yylval = &next_node->next;
+
+ return token;
+}
diff --git a/scripts/genksyms/parse.y b/scripts/genksyms/parse.y
new file mode 100644
index 0000000000..8e9b5e69e8
--- /dev/null
+++ b/scripts/genksyms/parse.y
@@ -0,0 +1,510 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * C global declaration parser for genksyms.
+ * Copyright 1996, 1997 Linux International.
+ *
+ * New implementation contributed by Richard Henderson <rth@tamu.edu>
+ * Based on original work by Bjorn Ekwall <bj0rn@blox.se>
+ *
+ * This file is part of the Linux modutils.
+ */
+
+%{
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include "genksyms.h"
+
+static int is_typedef;
+static int is_extern;
+static char *current_name;
+static struct string_list *decl_spec;
+
+static void yyerror(const char *);
+
+static inline void
+remove_node(struct string_list **p)
+{
+ struct string_list *node = *p;
+ *p = node->next;
+ free_node(node);
+}
+
+static inline void
+remove_list(struct string_list **pb, struct string_list **pe)
+{
+ struct string_list *b = *pb, *e = *pe;
+ *pb = e;
+ free_list(b, e);
+}
+
+/* Record definition of a struct/union/enum */
+static void record_compound(struct string_list **keyw,
+ struct string_list **ident,
+ struct string_list **body,
+ enum symbol_type type)
+{
+ struct string_list *b = *body, *i = *ident, *r;
+
+ if (i->in_source_file) {
+ remove_node(keyw);
+ (*ident)->tag = type;
+ remove_list(body, ident);
+ return;
+ }
+ r = copy_node(i); r->tag = type;
+ r->next = (*keyw)->next; *body = r; (*keyw)->next = NULL;
+ add_symbol(i->string, type, b, is_extern);
+}
+
+%}
+
+%token ASM_KEYW
+%token ATTRIBUTE_KEYW
+%token AUTO_KEYW
+%token BOOL_KEYW
+%token BUILTIN_INT_KEYW
+%token CHAR_KEYW
+%token CONST_KEYW
+%token DOUBLE_KEYW
+%token ENUM_KEYW
+%token EXTERN_KEYW
+%token EXTENSION_KEYW
+%token FLOAT_KEYW
+%token INLINE_KEYW
+%token INT_KEYW
+%token LONG_KEYW
+%token REGISTER_KEYW
+%token RESTRICT_KEYW
+%token SHORT_KEYW
+%token SIGNED_KEYW
+%token STATIC_KEYW
+%token STATIC_ASSERT_KEYW
+%token STRUCT_KEYW
+%token TYPEDEF_KEYW
+%token UNION_KEYW
+%token UNSIGNED_KEYW
+%token VOID_KEYW
+%token VOLATILE_KEYW
+%token TYPEOF_KEYW
+%token VA_LIST_KEYW
+
+%token EXPORT_SYMBOL_KEYW
+
+%token ASM_PHRASE
+%token ATTRIBUTE_PHRASE
+%token TYPEOF_PHRASE
+%token BRACE_PHRASE
+%token BRACKET_PHRASE
+%token EXPRESSION_PHRASE
+%token STATIC_ASSERT_PHRASE
+
+%token CHAR
+%token DOTS
+%token IDENT
+%token INT
+%token REAL
+%token STRING
+%token TYPE
+%token OTHER
+%token FILENAME
+
+%%
+
+declaration_seq:
+ declaration
+ | declaration_seq declaration
+ ;
+
+declaration:
+ { is_typedef = 0; is_extern = 0; current_name = NULL; decl_spec = NULL; }
+ declaration1
+ { free_list(*$2, NULL); *$2 = NULL; }
+ ;
+
+declaration1:
+ EXTENSION_KEYW TYPEDEF_KEYW { is_typedef = 1; } simple_declaration
+ { $$ = $4; }
+ | TYPEDEF_KEYW { is_typedef = 1; } simple_declaration
+ { $$ = $3; }
+ | simple_declaration
+ | function_definition
+ | asm_definition
+ | export_definition
+ | static_assert
+ | error ';' { $$ = $2; }
+ | error '}' { $$ = $2; }
+ ;
+
+simple_declaration:
+ decl_specifier_seq_opt init_declarator_list_opt ';'
+ { if (current_name) {
+ struct string_list *decl = (*$3)->next;
+ (*$3)->next = NULL;
+ add_symbol(current_name,
+ is_typedef ? SYM_TYPEDEF : SYM_NORMAL,
+ decl, is_extern);
+ current_name = NULL;
+ }
+ $$ = $3;
+ }
+ ;
+
+init_declarator_list_opt:
+ /* empty */ { $$ = NULL; }
+ | init_declarator_list
+ ;
+
+init_declarator_list:
+ init_declarator
+ { struct string_list *decl = *$1;
+ *$1 = NULL;
+ add_symbol(current_name,
+ is_typedef ? SYM_TYPEDEF : SYM_NORMAL, decl, is_extern);
+ current_name = NULL;
+ $$ = $1;
+ }
+ | init_declarator_list ',' init_declarator
+ { struct string_list *decl = *$3;
+ *$3 = NULL;
+ free_list(*$2, NULL);
+ *$2 = decl_spec;
+ add_symbol(current_name,
+ is_typedef ? SYM_TYPEDEF : SYM_NORMAL, decl, is_extern);
+ current_name = NULL;
+ $$ = $3;
+ }
+ ;
+
+init_declarator:
+ declarator asm_phrase_opt attribute_opt initializer_opt
+ { $$ = $4 ? $4 : $3 ? $3 : $2 ? $2 : $1; }
+ ;
+
+/* Hang on to the specifiers so that we can reuse them. */
+decl_specifier_seq_opt:
+ /* empty */ { decl_spec = NULL; }
+ | decl_specifier_seq
+ ;
+
+decl_specifier_seq:
+ decl_specifier { decl_spec = *$1; }
+ | decl_specifier_seq decl_specifier { decl_spec = *$2; }
+ ;
+
+decl_specifier:
+ storage_class_specifier
+ { /* Version 2 checksumming ignores storage class, as that
+ is really irrelevant to the linkage. */
+ remove_node($1);
+ $$ = $1;
+ }
+ | type_specifier
+ ;
+
+storage_class_specifier:
+ AUTO_KEYW
+ | REGISTER_KEYW
+ | STATIC_KEYW
+ | EXTERN_KEYW { is_extern = 1; $$ = $1; }
+ | INLINE_KEYW { is_extern = 0; $$ = $1; }
+ ;
+
+type_specifier:
+ simple_type_specifier
+ | cvar_qualifier
+ | TYPEOF_KEYW '(' parameter_declaration ')'
+ | TYPEOF_PHRASE
+
+ /* References to s/u/e's defined elsewhere. Rearrange things
+ so that it is easier to expand the definition fully later. */
+ | STRUCT_KEYW IDENT
+ { remove_node($1); (*$2)->tag = SYM_STRUCT; $$ = $2; }
+ | UNION_KEYW IDENT
+ { remove_node($1); (*$2)->tag = SYM_UNION; $$ = $2; }
+ | ENUM_KEYW IDENT
+ { remove_node($1); (*$2)->tag = SYM_ENUM; $$ = $2; }
+
+ /* Full definitions of an s/u/e. Record it. */
+ | STRUCT_KEYW IDENT class_body
+ { record_compound($1, $2, $3, SYM_STRUCT); $$ = $3; }
+ | UNION_KEYW IDENT class_body
+ { record_compound($1, $2, $3, SYM_UNION); $$ = $3; }
+ | ENUM_KEYW IDENT enum_body
+ { record_compound($1, $2, $3, SYM_ENUM); $$ = $3; }
+ /*
+ * Anonymous enum definition. Tell add_symbol() to restart its counter.
+ */
+ | ENUM_KEYW enum_body
+ { add_symbol(NULL, SYM_ENUM, NULL, 0); $$ = $2; }
+ /* Anonymous s/u definitions. Nothing needs doing. */
+ | STRUCT_KEYW class_body { $$ = $2; }
+ | UNION_KEYW class_body { $$ = $2; }
+ ;
+
+simple_type_specifier:
+ CHAR_KEYW
+ | SHORT_KEYW
+ | INT_KEYW
+ | LONG_KEYW
+ | SIGNED_KEYW
+ | UNSIGNED_KEYW
+ | FLOAT_KEYW
+ | DOUBLE_KEYW
+ | VOID_KEYW
+ | BOOL_KEYW
+ | VA_LIST_KEYW
+ | BUILTIN_INT_KEYW
+ | TYPE { (*$1)->tag = SYM_TYPEDEF; $$ = $1; }
+ ;
+
+ptr_operator:
+ '*' cvar_qualifier_seq_opt
+ { $$ = $2 ? $2 : $1; }
+ ;
+
+cvar_qualifier_seq_opt:
+ /* empty */ { $$ = NULL; }
+ | cvar_qualifier_seq
+ ;
+
+cvar_qualifier_seq:
+ cvar_qualifier
+ | cvar_qualifier_seq cvar_qualifier { $$ = $2; }
+ ;
+
+cvar_qualifier:
+ CONST_KEYW | VOLATILE_KEYW | ATTRIBUTE_PHRASE
+ | RESTRICT_KEYW
+ { /* restrict has no effect in prototypes so ignore it */
+ remove_node($1);
+ $$ = $1;
+ }
+ ;
+
+declarator:
+ ptr_operator declarator { $$ = $2; }
+ | direct_declarator
+ ;
+
+direct_declarator:
+ IDENT
+ { if (current_name != NULL) {
+ error_with_pos("unexpected second declaration name");
+ YYERROR;
+ } else {
+ current_name = (*$1)->string;
+ $$ = $1;
+ }
+ }
+ | TYPE
+ { if (current_name != NULL) {
+ error_with_pos("unexpected second declaration name");
+ YYERROR;
+ } else {
+ current_name = (*$1)->string;
+ $$ = $1;
+ }
+ }
+ | direct_declarator '(' parameter_declaration_clause ')'
+ { $$ = $4; }
+ | direct_declarator '(' error ')'
+ { $$ = $4; }
+ | direct_declarator BRACKET_PHRASE
+ { $$ = $2; }
+ | '(' declarator ')'
+ { $$ = $3; }
+ ;
+
+/* Nested declarators differ from regular declarators in that they do
+ not record the symbols they find in the global symbol table. */
+nested_declarator:
+ ptr_operator nested_declarator { $$ = $2; }
+ | direct_nested_declarator
+ ;
+
+direct_nested_declarator:
+ IDENT
+ | TYPE
+ | direct_nested_declarator '(' parameter_declaration_clause ')'
+ { $$ = $4; }
+ | direct_nested_declarator '(' error ')'
+ { $$ = $4; }
+ | direct_nested_declarator BRACKET_PHRASE
+ { $$ = $2; }
+ | '(' nested_declarator ')'
+ { $$ = $3; }
+ | '(' error ')'
+ { $$ = $3; }
+ ;
+
+parameter_declaration_clause:
+ parameter_declaration_list_opt DOTS { $$ = $2; }
+ | parameter_declaration_list_opt
+ | parameter_declaration_list ',' DOTS { $$ = $3; }
+ ;
+
+parameter_declaration_list_opt:
+ /* empty */ { $$ = NULL; }
+ | parameter_declaration_list
+ ;
+
+parameter_declaration_list:
+ parameter_declaration
+ | parameter_declaration_list ',' parameter_declaration
+ { $$ = $3; }
+ ;
+
+parameter_declaration:
+ decl_specifier_seq m_abstract_declarator
+ { $$ = $2 ? $2 : $1; }
+ ;
+
+m_abstract_declarator:
+ ptr_operator m_abstract_declarator
+ { $$ = $2 ? $2 : $1; }
+ | direct_m_abstract_declarator
+ ;
+
+direct_m_abstract_declarator:
+ /* empty */ { $$ = NULL; }
+ | IDENT
+ { /* For version 2 checksums, we don't want to remember
+ private parameter names. */
+ remove_node($1);
+ $$ = $1;
+ }
+ /* This wasn't really a typedef name but an identifier that
+ shadows one. */
+ | TYPE
+ { remove_node($1);
+ $$ = $1;
+ }
+ | direct_m_abstract_declarator '(' parameter_declaration_clause ')'
+ { $$ = $4; }
+ | direct_m_abstract_declarator '(' error ')'
+ { $$ = $4; }
+ | direct_m_abstract_declarator BRACKET_PHRASE
+ { $$ = $2; }
+ | '(' m_abstract_declarator ')'
+ { $$ = $3; }
+ | '(' error ')'
+ { $$ = $3; }
+ ;
+
+function_definition:
+ decl_specifier_seq_opt declarator BRACE_PHRASE
+ { struct string_list *decl = *$2;
+ *$2 = NULL;
+ add_symbol(current_name, SYM_NORMAL, decl, is_extern);
+ $$ = $3;
+ }
+ ;
+
+initializer_opt:
+ /* empty */ { $$ = NULL; }
+ | initializer
+ ;
+
+/* We never care about the contents of an initializer. */
+initializer:
+ '=' EXPRESSION_PHRASE
+ { remove_list($2, &(*$1)->next); $$ = $2; }
+ ;
+
+class_body:
+ '{' member_specification_opt '}' { $$ = $3; }
+ | '{' error '}' { $$ = $3; }
+ ;
+
+member_specification_opt:
+ /* empty */ { $$ = NULL; }
+ | member_specification
+ ;
+
+member_specification:
+ member_declaration
+ | member_specification member_declaration { $$ = $2; }
+ ;
+
+member_declaration:
+ decl_specifier_seq_opt member_declarator_list_opt ';'
+ { $$ = $3; }
+ | error ';'
+ { $$ = $2; }
+ ;
+
+member_declarator_list_opt:
+ /* empty */ { $$ = NULL; }
+ | member_declarator_list
+ ;
+
+member_declarator_list:
+ member_declarator
+ | member_declarator_list ',' member_declarator { $$ = $3; }
+ ;
+
+member_declarator:
+ nested_declarator attribute_opt { $$ = $2 ? $2 : $1; }
+ | IDENT member_bitfield_declarator { $$ = $2; }
+ | member_bitfield_declarator
+ ;
+
+member_bitfield_declarator:
+ ':' EXPRESSION_PHRASE { $$ = $2; }
+ ;
+
+attribute_opt:
+ /* empty */ { $$ = NULL; }
+ | attribute_opt ATTRIBUTE_PHRASE
+ ;
+
+enum_body:
+ '{' enumerator_list '}' { $$ = $3; }
+ | '{' enumerator_list ',' '}' { $$ = $4; }
+ ;
+
+enumerator_list:
+ enumerator
+ | enumerator_list ',' enumerator
+
+enumerator:
+ IDENT
+ {
+ const char *name = strdup((*$1)->string);
+ add_symbol(name, SYM_ENUM_CONST, NULL, 0);
+ }
+ | IDENT '=' EXPRESSION_PHRASE
+ {
+ const char *name = strdup((*$1)->string);
+ struct string_list *expr = copy_list_range(*$3, *$2);
+ add_symbol(name, SYM_ENUM_CONST, expr, 0);
+ }
+
+asm_definition:
+ ASM_PHRASE ';' { $$ = $2; }
+ ;
+
+asm_phrase_opt:
+ /* empty */ { $$ = NULL; }
+ | ASM_PHRASE
+ ;
+
+export_definition:
+ EXPORT_SYMBOL_KEYW '(' IDENT ')' ';'
+ { export_symbol((*$3)->string); $$ = $5; }
+ ;
+
+/* Ignore any module scoped _Static_assert(...) */
+static_assert:
+ STATIC_ASSERT_PHRASE ';' { $$ = $2; }
+ ;
+
+%%
+
+static void
+yyerror(const char *e)
+{
+ error_with_pos("%s", e);
+}