1 From: Andreas Gruenbacher <agruen@suse.de>
2 Subject: genksyms: track symbol checksum changes
4 Sometimes it is preferable to avoid changes of exported symbol checksums (to
5 avoid breaking externally provided modules). When a checksum change occurs, it
6 can be hard to figure out what caused this change: underlying types may have
7 changed, or additional type information may simply have become available at the
8 point where a symbol is exported.
10 Add a new --reference option to genksyms which allows it to report why
11 checksums change, based on the type information dumps it creates with
12 the --dump-types flag. Genksyms will read in such a dump from a previous run,
13 and report which symbols have changed (and why).
15 The behavior can be controlled for an entire build as follows: If
16 KBUILD_SYMTYPES is set, genksyms uses --dump-types to produce *.symtypes dump
17 files. If any *.symref files exist, those will be used as the reference to
18 check against. If KBUILD_PRESERVE is set, checksum changes will fail the
21 Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
24 scripts/Makefile.build | 16 ++
25 scripts/genksyms/genksyms.c | 236 +++++++++++++++++++++++++++++++++++++++++---
26 scripts/genksyms/genksyms.h | 6 +
27 3 files changed, 239 insertions(+), 19 deletions(-)
29 --- a/scripts/Makefile.build
30 +++ b/scripts/Makefile.build
31 @@ -153,12 +153,18 @@ $(obj)/%.i: $(src)/%.c FORCE
33 quiet_cmd_cc_symtypes_c = SYM $(quiet_modtag) $@
36 $(CPP) -D__GENKSYMS__ $(c_flags) $< \
37 - | $(GENKSYMS) -T $@ >/dev/null; \
38 + | $(GENKSYMS) -T $@ \
39 + -r $(firstword $(wildcard \
40 + $(@:.symtypes=.symref) /dev/null)) \
41 + $(if $(KBUILD_PRESERVE),-p) \
44 test -s $@ || rm -f $@
46 $(obj)/%.symtypes : $(src)/%.c FORCE
47 - $(call if_changed_dep,cc_symtypes_c)
48 + $(call cmd,cc_symtypes_c)
51 # The C file is compiled and updated dependency information is generated.
52 @@ -187,7 +193,11 @@ cmd_modversions = \
53 if $(OBJDUMP) -h $(@D)/.tmp_$(@F) | grep -q __ksymtab; then \
54 $(CPP) -D__GENKSYMS__ $(c_flags) $< \
55 | $(GENKSYMS) $(if $(KBUILD_SYMTYPES), \
56 - -T $(@D)/$(@F:.o=.symtypes)) -a $(ARCH) \
57 + -T $(@:.o=.symtypes)) \
58 + -r $(firstword $(wildcard \
59 + $(@:.o=.symref) /dev/null)) \
60 + $(if $(KBUILD_PRESERVE),-p) \
62 > $(@D)/.tmp_$(@F:.o=.ver); \
64 $(LD) $(LDFLAGS) -r -o $@ $(@D)/.tmp_$(@F) \
65 --- a/scripts/genksyms/genksyms.c
66 +++ b/scripts/genksyms/genksyms.c
67 @@ -42,7 +42,8 @@ static FILE *debugfile;
71 -static int flag_debug, flag_dump_defs, flag_dump_types, flag_warnings;
72 +static int flag_debug, flag_dump_defs, flag_reference, flag_dump_types,
73 + flag_preserve, flag_warnings;
74 static const char *arch = "";
75 static const char *mod_prefix = "";
77 @@ -58,6 +59,8 @@ static const char *const symbol_type_nam
79 static int equal_list(struct string_list *a, struct string_list *b);
80 static void print_list(FILE * f, struct string_list *list);
81 +static void print_location(void);
82 +static void print_type_name(enum symbol_type type, const char *name);
84 /*----------------------------------------------------------------------*/
86 @@ -151,25 +154,66 @@ struct symbol *find_symbol(const char *n
88 for (sym = symtab[h]; sym; sym = sym->hash_next)
89 if (map_to_ns(sym->type) == map_to_ns(ns) &&
90 - strcmp(name, sym->name) == 0)
91 + strcmp(name, sym->name) == 0 &&
98 -struct symbol *add_symbol(const char *name, enum symbol_type type,
99 - struct string_list *defn, int is_extern)
100 +static int is_unknown_symbol(struct symbol *sym)
102 + struct string_list *defn;
104 + return ((sym->type == SYM_STRUCT ||
105 + sym->type == SYM_UNION ||
106 + sym->type == SYM_ENUM) &&
107 + (defn = sym->defn) && defn->tag == SYM_NORMAL &&
108 + strcmp(defn->string, "}") == 0 &&
109 + (defn = defn->next) && defn->tag == SYM_NORMAL &&
110 + strcmp(defn->string, "UNKNOWN") == 0 &&
111 + (defn = defn->next) && defn->tag == SYM_NORMAL &&
112 + strcmp(defn->string, "{") == 0);
115 +struct symbol *__add_symbol(const char *name, enum symbol_type type,
116 + struct string_list *defn, int is_extern,
119 unsigned long h = crc32(name) % HASH_BUCKETS;
121 + enum symbol_status status = STATUS_UNCHANGED;
123 for (sym = symtab[h]; sym; sym = sym->hash_next) {
124 - if (map_to_ns(sym->type) == map_to_ns(type)
125 - && strcmp(name, sym->name) == 0) {
126 - if (!equal_list(sym->defn, defn))
127 + if (map_to_ns(sym->type) == map_to_ns(type) &&
128 + strcmp(name, sym->name) == 0) {
130 + /* fall through */ ;
131 + else if (sym->type == type &&
132 + equal_list(sym->defn, defn)) {
133 + sym->is_declared = 1;
135 + } else if (!sym->is_declared) {
136 + status = is_unknown_symbol(sym) ?
137 + STATUS_DEFINED : STATUS_MODIFIED;
139 error_with_pos("redefinition of %s", name);
148 + struct symbol **psym;
150 + for (psym = &symtab[h]; *psym; psym = &(*psym)->hash_next) {
151 + if (*psym == sym) {
152 + *psym = sym->hash_next;
159 sym = xmalloc(sizeof(*sym));
160 @@ -183,6 +227,9 @@ struct symbol *add_symbol(const char *na
161 sym->hash_next = symtab[h];
164 + sym->is_declared = !is_reference;
165 + sym->status = status;
168 fprintf(debugfile, "Defn for %s %s == <",
169 symbol_type_name[type], name);
170 @@ -196,6 +243,18 @@ struct symbol *add_symbol(const char *na
174 +struct symbol *add_symbol(const char *name, enum symbol_type type,
175 + struct string_list *defn, int is_extern)
177 + return __add_symbol(name, type, defn, is_extern, 0);
180 +struct symbol *add_reference_symbol(const char *name, enum symbol_type type,
181 + struct string_list *defn, int is_extern)
183 + return __add_symbol(name, type, defn, is_extern, 1);
186 /*----------------------------------------------------------------------*/
188 void free_node(struct string_list *node)
189 @@ -236,6 +295,82 @@ static int equal_list(struct string_list
193 +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
195 +struct string_list *read_node(FILE *f)
198 + struct string_list node = {
200 + .tag = SYM_NORMAL };
203 + while ((c = fgetc(f)) != EOF) {
205 + if (node.string == buffer)
208 + } else if (c == '\n') {
209 + if (node.string == buffer)
214 + if (node.string >= buffer + sizeof(buffer) - 1) {
215 + fprintf(stderr, "Token too long\n");
218 + *node.string++ = c;
220 + if (node.string == buffer)
223 + node.string = buffer;
225 + if (node.string[1] == '#') {
228 + for (n = 0; n < ARRAY_SIZE(symbol_type_name); n++) {
229 + if (node.string[0] == symbol_type_name[n][0]) {
232 + return copy_node(&node);
235 + fprintf(stderr, "Unknown type %c\n", node.string[0]);
238 + return copy_node(&node);
241 +static void read_reference(FILE *f)
244 + struct string_list *defn = NULL;
245 + struct string_list *sym, *def;
248 + sym = read_node(f);
251 + def = read_node(f);
252 + if (def && def->tag == SYM_NORMAL &&
253 + !strcmp(def->string, "extern")) {
256 + def = read_node(f);
261 + def = read_node(f);
263 + add_reference_symbol(xstrdup(sym->string), sym->tag,
269 static void print_node(FILE * f, struct string_list *list)
271 if (list->tag != SYM_NORMAL) {
272 @@ -311,6 +446,7 @@ static unsigned long expand_and_crc_sym(
275 subsym = find_symbol(cur->string, cur->tag);
276 + /* FIXME: Bad reference files can segfault here. */
277 if (subsym->expansion_trail) {
279 fprintf(debugfile, "%s ", cur->string);
280 @@ -347,9 +483,22 @@ static unsigned long expand_and_crc_sym(
283 n = xmalloc(sizeof(*n));
284 - n->string = xstrdup("{ UNKNOWN }");
285 + n->string = xstrdup("{");
286 + n->tag = SYM_NORMAL;
290 + n = xmalloc(sizeof(*n));
291 + n->string = xstrdup("UNKNOWN");
292 + n->tag = SYM_NORMAL;
296 + n = xmalloc(sizeof(*n));
297 + n->string = xstrdup("}");
303 add_symbol(cur->string, cur->tag, n, 0);
304 @@ -397,20 +546,42 @@ void export_symbol(const char *name)
305 error_with_pos("export undefined symbol %s", name);
308 + int has_changed = 0;
311 fprintf(debugfile, "Export %s == <", name);
313 expansion_trail = (struct symbol *)-1L;
315 + sym->expansion_trail = expansion_trail;
316 + expansion_trail = sym;
317 crc = expand_and_crc_sym(sym, 0xffffffff) ^ 0xffffffff;
319 sym = expansion_trail;
320 while (sym != (struct symbol *)-1L) {
321 struct symbol *n = sym->expansion_trail;
323 + if (sym->status != STATUS_UNCHANGED) {
324 + if (!has_changed) {
326 + fprintf(stderr, "%s: %s: modversion "
327 + "changed because of changes "
328 + "in ", flag_preserve ? "error" :
331 + fprintf(stderr, ", ");
332 + print_type_name(sym->type, sym->name);
333 + if (sym->status == STATUS_DEFINED)
334 + fprintf(stderr, " (became defined)");
339 sym->expansion_trail = 0;
343 + fprintf(stderr, "\n");
346 fputs(">\n", debugfile);
347 @@ -421,13 +592,26 @@ void export_symbol(const char *name)
350 /*----------------------------------------------------------------------*/
352 +static void print_location(void)
354 + fprintf(stderr, "%s:%d: ", cur_filename ? : "<stdin>", cur_line);
357 +static void print_type_name(enum symbol_type type, const char *name)
359 + if (type != SYM_NORMAL)
360 + fprintf(stderr, "%s %s", symbol_type_name[type], name);
362 + fprintf(stderr, "%s", name);
365 void error_with_pos(const char *fmt, ...)
370 - fprintf(stderr, "%s:%d: ", cur_filename ? : "<stdin>",
375 vfprintf(stderr, fmt, args);
376 @@ -445,7 +629,9 @@ static void genksyms_usage(void)
377 " -a, --arch Select architecture\n"
378 " -d, --debug Increment the debug level (repeatable)\n"
379 " -D, --dump Dump expanded symbol defs (for debugging only)\n"
380 - " -T, --dump-types file Dump expanded types into file (for debugging only)\n"
381 + " -r, --reference file Read reference symbols from a file\n"
382 + " -T, --dump-types file Dump expanded types into file\n"
383 + " -p, --preserve Preserve reference modversions or fail\n"
384 " -w, --warnings Enable warnings\n"
385 " -q, --quiet Disable warnings (default)\n"
386 " -h, --help Print this message\n"
387 @@ -454,7 +640,9 @@ static void genksyms_usage(void)
388 " -a Select architecture\n"
389 " -d Increment the debug level (repeatable)\n"
390 " -D Dump expanded symbol defs (for debugging only)\n"
391 - " -T file Dump expanded types into file (for debugging only)\n"
392 + " -r file Read reference symbols from a file\n"
393 + " -T file Dump expanded types into file\n"
394 + " -p Preserve reference modversions or fail\n"
395 " -w Enable warnings\n"
396 " -q Disable warnings (default)\n"
397 " -h Print this message\n"
398 @@ -465,7 +653,7 @@ static void genksyms_usage(void)
400 int main(int argc, char **argv)
402 - FILE *dumpfile = NULL;
403 + FILE *dumpfile = NULL, *ref_file = NULL;
406 #ifdef __GNU_LIBRARY__
407 @@ -475,16 +663,18 @@ int main(int argc, char **argv)
408 {"warnings", 0, 0, 'w'},
409 {"quiet", 0, 0, 'q'},
411 + {"reference", 1, 0, 'r'},
412 {"dump-types", 1, 0, 'T'},
413 + {"preserve", 0, 0, 'p'},
414 {"version", 0, 0, 'V'},
419 - while ((o = getopt_long(argc, argv, "a:dwqVDT:h",
420 + while ((o = getopt_long(argc, argv, "a:dwqVDr:T:ph",
421 &long_opts[0], NULL)) != EOF)
422 #else /* __GNU_LIBRARY__ */
423 - while ((o = getopt(argc, argv, "a:dwqVDT:h")) != EOF)
424 + while ((o = getopt(argc, argv, "a:dwqVDr:T:ph")) != EOF)
425 #endif /* __GNU_LIBRARY__ */
428 @@ -505,6 +695,14 @@ int main(int argc, char **argv)
433 + flag_reference = 1;
434 + ref_file = fopen(optarg, "r");
442 dumpfile = fopen(optarg, "w");
443 @@ -513,6 +711,9 @@ int main(int argc, char **argv)
453 @@ -533,6 +734,9 @@ int main(int argc, char **argv)
454 /* setlinebuf(debugfile); */
457 + if (flag_reference)
458 + read_reference(ref_file);
462 if (flag_dump_types && visited_symbols) {
463 --- a/scripts/genksyms/genksyms.h
464 +++ b/scripts/genksyms/genksyms.h
465 @@ -29,6 +29,10 @@ enum symbol_type {
466 SYM_NORMAL, SYM_TYPEDEF, SYM_ENUM, SYM_STRUCT, SYM_UNION
469 +enum symbol_status {
470 + STATUS_UNCHANGED, STATUS_DEFINED, STATUS_MODIFIED
474 struct string_list *next;
475 enum symbol_type tag;
476 @@ -43,6 +47,8 @@ struct symbol {
477 struct symbol *expansion_trail;
478 struct symbol *visited;
481 + enum symbol_status status;
484 typedef struct string_list **yystype;