]>
Commit | Line | Data |
---|---|---|
6a930a95 BS |
1 | From: Andreas Gruenbacher <agruen@suse.de> |
2 | Subject: genksyms: track symbol checksum changes | |
3 | ||
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. | |
9 | ||
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). | |
14 | ||
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 | |
19 | build. | |
20 | ||
21 | Signed-off-by: Andreas Gruenbacher <agruen@suse.de> | |
22 | ||
23 | --- | |
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(-) | |
28 | ||
29 | --- a/scripts/Makefile.build | |
30 | +++ b/scripts/Makefile.build | |
31 | @@ -153,12 +153,18 @@ $(obj)/%.i: $(src)/%.c FORCE | |
32 | ||
33 | quiet_cmd_cc_symtypes_c = SYM $(quiet_modtag) $@ | |
34 | cmd_cc_symtypes_c = \ | |
35 | + set -e; \ | |
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) \ | |
42 | + -a $(ARCH) \ | |
43 | + >/dev/null; \ | |
44 | test -s $@ || rm -f $@ | |
45 | ||
46 | $(obj)/%.symtypes : $(src)/%.c FORCE | |
47 | - $(call if_changed_dep,cc_symtypes_c) | |
48 | + $(call cmd,cc_symtypes_c) | |
49 | ||
50 | # C (.c) files | |
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) \ | |
61 | + -a $(ARCH) \ | |
62 | > $(@D)/.tmp_$(@F:.o=.ver); \ | |
63 | \ | |
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; | |
68 | int cur_line = 1; | |
69 | char *cur_filename; | |
70 | ||
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 = ""; | |
76 | ||
77 | @@ -58,6 +59,8 @@ static const char *const symbol_type_nam | |
78 | ||
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); | |
83 | ||
84 | /*----------------------------------------------------------------------*/ | |
85 | ||
86 | @@ -151,25 +154,66 @@ struct symbol *find_symbol(const char *n | |
87 | ||
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 && | |
92 | + sym->is_declared) | |
93 | break; | |
94 | ||
95 | return sym; | |
96 | } | |
97 | ||
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) | |
101 | +{ | |
102 | + struct string_list *defn; | |
103 | + | |
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); | |
113 | +} | |
114 | + | |
115 | +struct symbol *__add_symbol(const char *name, enum symbol_type type, | |
116 | + struct string_list *defn, int is_extern, | |
117 | + int is_reference) | |
118 | { | |
119 | unsigned long h = crc32(name) % HASH_BUCKETS; | |
120 | struct symbol *sym; | |
121 | + enum symbol_status status = STATUS_UNCHANGED; | |
122 | ||
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) { | |
129 | + if (is_reference) | |
130 | + /* fall through */ ; | |
131 | + else if (sym->type == type && | |
132 | + equal_list(sym->defn, defn)) { | |
133 | + sym->is_declared = 1; | |
134 | + return sym; | |
135 | + } else if (!sym->is_declared) { | |
136 | + status = is_unknown_symbol(sym) ? | |
137 | + STATUS_DEFINED : STATUS_MODIFIED; | |
138 | + } else { | |
139 | error_with_pos("redefinition of %s", name); | |
140 | - return sym; | |
141 | + return sym; | |
142 | + } | |
143 | + break; | |
144 | + } | |
145 | + } | |
146 | + | |
147 | + if (sym) { | |
148 | + struct symbol **psym; | |
149 | + | |
150 | + for (psym = &symtab[h]; *psym; psym = &(*psym)->hash_next) { | |
151 | + if (*psym == sym) { | |
152 | + *psym = sym->hash_next; | |
153 | + break; | |
154 | + } | |
155 | } | |
156 | + --nsyms; | |
157 | } | |
158 | ||
159 | sym = xmalloc(sizeof(*sym)); | |
160 | @@ -183,6 +227,9 @@ struct symbol *add_symbol(const char *na | |
161 | sym->hash_next = symtab[h]; | |
162 | symtab[h] = sym; | |
163 | ||
164 | + sym->is_declared = !is_reference; | |
165 | + sym->status = status; | |
166 | + | |
167 | if (flag_debug) { | |
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 | |
171 | return sym; | |
172 | } | |
173 | ||
174 | +struct symbol *add_symbol(const char *name, enum symbol_type type, | |
175 | + struct string_list *defn, int is_extern) | |
176 | +{ | |
177 | + return __add_symbol(name, type, defn, is_extern, 0); | |
178 | +} | |
179 | + | |
180 | +struct symbol *add_reference_symbol(const char *name, enum symbol_type type, | |
181 | + struct string_list *defn, int is_extern) | |
182 | +{ | |
183 | + return __add_symbol(name, type, defn, is_extern, 1); | |
184 | +} | |
185 | + | |
186 | /*----------------------------------------------------------------------*/ | |
187 | ||
188 | void free_node(struct string_list *node) | |
189 | @@ -236,6 +295,82 @@ static int equal_list(struct string_list | |
190 | return !a && !b; | |
191 | } | |
192 | ||
193 | +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) | |
194 | + | |
195 | +struct string_list *read_node(FILE *f) | |
196 | +{ | |
197 | + char buffer[256]; | |
198 | + struct string_list node = { | |
199 | + .string = buffer, | |
200 | + .tag = SYM_NORMAL }; | |
201 | + int c; | |
202 | + | |
203 | + while ((c = fgetc(f)) != EOF) { | |
204 | + if (c == ' ') { | |
205 | + if (node.string == buffer) | |
206 | + continue; | |
207 | + break; | |
208 | + } else if (c == '\n') { | |
209 | + if (node.string == buffer) | |
210 | + return NULL; | |
211 | + ungetc(c, f); | |
212 | + break; | |
213 | + } | |
214 | + if (node.string >= buffer + sizeof(buffer) - 1) { | |
215 | + fprintf(stderr, "Token too long\n"); | |
216 | + exit(1); | |
217 | + } | |
218 | + *node.string++ = c; | |
219 | + } | |
220 | + if (node.string == buffer) | |
221 | + return NULL; | |
222 | + *node.string = 0; | |
223 | + node.string = buffer; | |
224 | + | |
225 | + if (node.string[1] == '#') { | |
226 | + int n; | |
227 | + | |
228 | + for (n = 0; n < ARRAY_SIZE(symbol_type_name); n++) { | |
229 | + if (node.string[0] == symbol_type_name[n][0]) { | |
230 | + node.tag = n; | |
231 | + node.string += 2; | |
232 | + return copy_node(&node); | |
233 | + } | |
234 | + } | |
235 | + fprintf(stderr, "Unknown type %c\n", node.string[0]); | |
236 | + exit(1); | |
237 | + } | |
238 | + return copy_node(&node); | |
239 | +} | |
240 | + | |
241 | +static void read_reference(FILE *f) | |
242 | +{ | |
243 | + while (!feof(f)) { | |
244 | + struct string_list *defn = NULL; | |
245 | + struct string_list *sym, *def; | |
246 | + int is_extern = 0; | |
247 | + | |
248 | + sym = read_node(f); | |
249 | + if (!sym) | |
250 | + continue; | |
251 | + def = read_node(f); | |
252 | + if (def && def->tag == SYM_NORMAL && | |
253 | + !strcmp(def->string, "extern")) { | |
254 | + is_extern = 1; | |
255 | + free_node(def); | |
256 | + def = read_node(f); | |
257 | + } | |
258 | + while (def) { | |
259 | + def->next = defn; | |
260 | + defn = def; | |
261 | + def = read_node(f); | |
262 | + } | |
263 | + add_reference_symbol(xstrdup(sym->string), sym->tag, | |
264 | + defn, is_extern); | |
265 | + free_node(sym); | |
266 | + } | |
267 | +} | |
268 | + | |
269 | static void print_node(FILE * f, struct string_list *list) | |
270 | { | |
271 | if (list->tag != SYM_NORMAL) { | |
272 | @@ -311,6 +446,7 @@ static unsigned long expand_and_crc_sym( | |
273 | ||
274 | case SYM_TYPEDEF: | |
275 | subsym = find_symbol(cur->string, cur->tag); | |
276 | + /* FIXME: Bad reference files can segfault here. */ | |
277 | if (subsym->expansion_trail) { | |
278 | if (flag_dump_defs) | |
279 | fprintf(debugfile, "%s ", cur->string); | |
280 | @@ -347,9 +483,22 @@ static unsigned long expand_and_crc_sym( | |
281 | t = n; | |
282 | ||
283 | n = xmalloc(sizeof(*n)); | |
284 | - n->string = xstrdup("{ UNKNOWN }"); | |
285 | + n->string = xstrdup("{"); | |
286 | + n->tag = SYM_NORMAL; | |
287 | + n->next = t; | |
288 | + t = n; | |
289 | + | |
290 | + n = xmalloc(sizeof(*n)); | |
291 | + n->string = xstrdup("UNKNOWN"); | |
292 | + n->tag = SYM_NORMAL; | |
293 | + n->next = t; | |
294 | + t = n; | |
295 | + | |
296 | + n = xmalloc(sizeof(*n)); | |
297 | + n->string = xstrdup("}"); | |
298 | n->tag = SYM_NORMAL; | |
299 | n->next = t; | |
300 | + t = n; | |
301 | ||
302 | subsym = | |
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); | |
306 | else { | |
307 | unsigned long crc; | |
308 | + int has_changed = 0; | |
309 | ||
310 | if (flag_dump_defs) | |
311 | fprintf(debugfile, "Export %s == <", name); | |
312 | ||
313 | expansion_trail = (struct symbol *)-1L; | |
314 | ||
315 | + sym->expansion_trail = expansion_trail; | |
316 | + expansion_trail = sym; | |
317 | crc = expand_and_crc_sym(sym, 0xffffffff) ^ 0xffffffff; | |
318 | ||
319 | sym = expansion_trail; | |
320 | while (sym != (struct symbol *)-1L) { | |
321 | struct symbol *n = sym->expansion_trail; | |
322 | + | |
323 | + if (sym->status != STATUS_UNCHANGED) { | |
324 | + if (!has_changed) { | |
325 | + print_location(); | |
326 | + fprintf(stderr, "%s: %s: modversion " | |
327 | + "changed because of changes " | |
328 | + "in ", flag_preserve ? "error" : | |
329 | + "warning", name); | |
330 | + } else | |
331 | + fprintf(stderr, ", "); | |
332 | + print_type_name(sym->type, sym->name); | |
333 | + if (sym->status == STATUS_DEFINED) | |
334 | + fprintf(stderr, " (became defined)"); | |
335 | + has_changed = 1; | |
336 | + if (flag_preserve) | |
337 | + errors++; | |
338 | + } | |
339 | sym->expansion_trail = 0; | |
340 | sym = n; | |
341 | } | |
342 | + if (has_changed) | |
343 | + fprintf(stderr, "\n"); | |
344 | ||
345 | if (flag_dump_defs) | |
346 | fputs(">\n", debugfile); | |
347 | @@ -421,13 +592,26 @@ void export_symbol(const char *name) | |
348 | } | |
349 | ||
350 | /*----------------------------------------------------------------------*/ | |
351 | + | |
352 | +static void print_location(void) | |
353 | +{ | |
354 | + fprintf(stderr, "%s:%d: ", cur_filename ? : "<stdin>", cur_line); | |
355 | +} | |
356 | + | |
357 | +static void print_type_name(enum symbol_type type, const char *name) | |
358 | +{ | |
359 | + if (type != SYM_NORMAL) | |
360 | + fprintf(stderr, "%s %s", symbol_type_name[type], name); | |
361 | + else | |
362 | + fprintf(stderr, "%s", name); | |
363 | +} | |
364 | + | |
365 | void error_with_pos(const char *fmt, ...) | |
366 | { | |
367 | va_list args; | |
368 | ||
369 | if (flag_warnings) { | |
370 | - fprintf(stderr, "%s:%d: ", cur_filename ? : "<stdin>", | |
371 | - cur_line); | |
372 | + print_location(); | |
373 | ||
374 | va_start(args, fmt); | |
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) | |
399 | ||
400 | int main(int argc, char **argv) | |
401 | { | |
402 | - FILE *dumpfile = NULL; | |
403 | + FILE *dumpfile = NULL, *ref_file = NULL; | |
404 | int o; | |
405 | ||
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'}, | |
410 | {"dump", 0, 0, 'D'}, | |
411 | + {"reference", 1, 0, 'r'}, | |
412 | {"dump-types", 1, 0, 'T'}, | |
413 | + {"preserve", 0, 0, 'p'}, | |
414 | {"version", 0, 0, 'V'}, | |
415 | {"help", 0, 0, 'h'}, | |
416 | {0, 0, 0, 0} | |
417 | }; | |
418 | ||
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__ */ | |
426 | switch (o) { | |
427 | case 'a': | |
428 | @@ -505,6 +695,14 @@ int main(int argc, char **argv) | |
429 | case 'D': | |
430 | flag_dump_defs = 1; | |
431 | break; | |
432 | + case 'r': | |
433 | + flag_reference = 1; | |
434 | + ref_file = fopen(optarg, "r"); | |
435 | + if (!ref_file) { | |
436 | + perror(optarg); | |
437 | + return 1; | |
438 | + } | |
439 | + break; | |
440 | case 'T': | |
441 | flag_dump_types = 1; | |
442 | dumpfile = fopen(optarg, "w"); | |
443 | @@ -513,6 +711,9 @@ int main(int argc, char **argv) | |
444 | return 1; | |
445 | } | |
446 | break; | |
447 | + case 'p': | |
448 | + flag_preserve = 1; | |
449 | + break; | |
450 | case 'h': | |
451 | genksyms_usage(); | |
452 | return 0; | |
453 | @@ -533,6 +734,9 @@ int main(int argc, char **argv) | |
454 | /* setlinebuf(debugfile); */ | |
455 | } | |
456 | ||
457 | + if (flag_reference) | |
458 | + read_reference(ref_file); | |
459 | + | |
460 | yyparse(); | |
461 | ||
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 | |
467 | }; | |
468 | ||
469 | +enum symbol_status { | |
470 | + STATUS_UNCHANGED, STATUS_DEFINED, STATUS_MODIFIED | |
471 | +}; | |
472 | + | |
473 | struct string_list { | |
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; | |
479 | int is_extern; | |
480 | + int is_declared; | |
481 | + enum symbol_status status; | |
482 | }; | |
483 | ||
484 | typedef struct string_list **yystype; |