aboutsummaryrefslogtreecommitdiff
path: root/scripts/genksyms/genksyms.c
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/genksyms/genksyms.c')
-rw-r--r--scripts/genksyms/genksyms.c438
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);