Commit e37ddb82500393cb417c3ab0fe0726d9a8652372
1 parent
01762c4ec5
Exists in
master
and in
20 other branches
genksyms: Track changes to enum constants
Enum constants can be used as array sizes; if the enum itself does not appear in the symbol expansion, a change in the enum constant will go unnoticed. Example patch that changes the ABI but does not change the checksum with current genksyms: | enum e { | E1, | E2, |+ E3, | E_MAX | }; | | struct s { | int a[E_MAX]; | } | | int f(struct s *s) { ... } | EXPORT_SYMBOL(f) Therefore, remember the value of each enum constant and expand each occurence to <constant> <value>. The value is not actually computed, but instead an expression in the form (last explicitly assigned value) + N is used. This avoids having to parse and semantically understand whole of C. Note: The changes won't take effect until the lexer and parser are rebuilt by the next patch. Signed-off-by: Michal Marek <mmarek@suse.cz> Acked-by: Sam Ravnborg <sam@ravnborg.org>
Showing 4 changed files with 127 additions and 13 deletions Side-by-side Diff
scripts/genksyms/genksyms.c
... | ... | @@ -62,6 +62,7 @@ |
62 | 62 | [SYM_ENUM] = {'e', "enum"}, |
63 | 63 | [SYM_STRUCT] = {'s', "struct"}, |
64 | 64 | [SYM_UNION] = {'u', "union"}, |
65 | + [SYM_ENUM_CONST] = {'E', "enum constant"}, | |
65 | 66 | }; |
66 | 67 | |
67 | 68 | static int equal_list(struct string_list *a, struct string_list *b); |
... | ... | @@ -149,10 +150,16 @@ |
149 | 150 | |
150 | 151 | static enum symbol_type map_to_ns(enum symbol_type t) |
151 | 152 | { |
152 | - if (t == SYM_TYPEDEF) | |
153 | - t = SYM_NORMAL; | |
154 | - else if (t == SYM_UNION) | |
155 | - t = SYM_STRUCT; | |
153 | + switch (t) { | |
154 | + case SYM_ENUM_CONST: | |
155 | + case SYM_NORMAL: | |
156 | + case SYM_TYPEDEF: | |
157 | + return SYM_NORMAL; | |
158 | + case SYM_ENUM: | |
159 | + case SYM_STRUCT: | |
160 | + case SYM_UNION: | |
161 | + return SYM_STRUCT; | |
162 | + } | |
156 | 163 | return t; |
157 | 164 | } |
158 | 165 | |
159 | 166 | |
160 | 167 | |
... | ... | @@ -191,10 +198,47 @@ |
191 | 198 | struct string_list *defn, int is_extern, |
192 | 199 | int is_reference) |
193 | 200 | { |
194 | - unsigned long h = crc32(name) % HASH_BUCKETS; | |
201 | + unsigned long h; | |
195 | 202 | struct symbol *sym; |
196 | 203 | enum symbol_status status = STATUS_UNCHANGED; |
204 | + /* The parser adds symbols in the order their declaration completes, | |
205 | + * so it is safe to store the value of the previous enum constant in | |
206 | + * a static variable. | |
207 | + */ | |
208 | + static int enum_counter; | |
209 | + static struct string_list *last_enum_expr; | |
197 | 210 | |
211 | + if (type == SYM_ENUM_CONST) { | |
212 | + if (defn) { | |
213 | + free_list(last_enum_expr, NULL); | |
214 | + last_enum_expr = copy_list_range(defn, NULL); | |
215 | + enum_counter = 1; | |
216 | + } else { | |
217 | + struct string_list *expr; | |
218 | + char buf[20]; | |
219 | + | |
220 | + snprintf(buf, sizeof(buf), "%d", enum_counter++); | |
221 | + if (last_enum_expr) { | |
222 | + expr = copy_list_range(last_enum_expr, NULL); | |
223 | + defn = concat_list(mk_node("("), | |
224 | + expr, | |
225 | + mk_node(")"), | |
226 | + mk_node("+"), | |
227 | + mk_node(buf), NULL); | |
228 | + } else { | |
229 | + defn = mk_node(buf); | |
230 | + } | |
231 | + } | |
232 | + } else if (type == SYM_ENUM) { | |
233 | + free_list(last_enum_expr, NULL); | |
234 | + last_enum_expr = NULL; | |
235 | + enum_counter = 0; | |
236 | + if (!name) | |
237 | + /* Anonymous enum definition, nothing more to do */ | |
238 | + return NULL; | |
239 | + } | |
240 | + | |
241 | + h = crc32(name) % HASH_BUCKETS; | |
198 | 242 | for (sym = symtab[h]; sym; sym = sym->hash_next) { |
199 | 243 | if (map_to_ns(sym->type) == map_to_ns(type) && |
200 | 244 | strcmp(name, sym->name) == 0) { |
... | ... | @@ -343,6 +387,22 @@ |
343 | 387 | return newnode; |
344 | 388 | } |
345 | 389 | |
390 | +struct string_list *copy_list_range(struct string_list *start, | |
391 | + struct string_list *end) | |
392 | +{ | |
393 | + struct string_list *res, *n; | |
394 | + | |
395 | + if (start == end) | |
396 | + return NULL; | |
397 | + n = res = copy_node(start); | |
398 | + for (start = start->next; start != end; start = start->next) { | |
399 | + n->next = copy_node(start); | |
400 | + n = n->next; | |
401 | + } | |
402 | + n->next = NULL; | |
403 | + return res; | |
404 | +} | |
405 | + | |
346 | 406 | static int equal_list(struct string_list *a, struct string_list *b) |
347 | 407 | { |
348 | 408 | while (a && b) { |
... | ... | @@ -512,6 +572,7 @@ |
512 | 572 | crc = partial_crc32_one(' ', crc); |
513 | 573 | break; |
514 | 574 | |
575 | + case SYM_ENUM_CONST: | |
515 | 576 | case SYM_TYPEDEF: |
516 | 577 | subsym = find_symbol(cur->string, cur->tag, 0); |
517 | 578 | /* FIXME: Bad reference files can segfault here. */ |
scripts/genksyms/genksyms.h
... | ... | @@ -26,7 +26,8 @@ |
26 | 26 | #include <stdio.h> |
27 | 27 | |
28 | 28 | enum symbol_type { |
29 | - SYM_NORMAL, SYM_TYPEDEF, SYM_ENUM, SYM_STRUCT, SYM_UNION | |
29 | + SYM_NORMAL, SYM_TYPEDEF, SYM_ENUM, SYM_STRUCT, SYM_UNION, | |
30 | + SYM_ENUM_CONST | |
30 | 31 | }; |
31 | 32 | |
32 | 33 | enum symbol_status { |
... | ... | @@ -66,6 +67,8 @@ |
66 | 67 | void free_node(struct string_list *list); |
67 | 68 | void free_list(struct string_list *s, struct string_list *e); |
68 | 69 | struct string_list *copy_node(struct string_list *); |
70 | +struct string_list *copy_list_range(struct string_list *start, | |
71 | + struct string_list *end); | |
69 | 72 | |
70 | 73 | int yylex(void); |
71 | 74 | int yyparse(void); |
scripts/genksyms/lex.l
... | ... | @@ -99,12 +99,23 @@ |
99 | 99 | |
100 | 100 | /* Macros to append to our phrase collection list. */ |
101 | 101 | |
102 | +/* | |
103 | + * We mark any token, that that equals to a known enumerator, as | |
104 | + * SYM_ENUM_CONST. The parser will change this for struct and union tags later, | |
105 | + * the only problem is struct and union members: | |
106 | + * enum e { a, b }; struct s { int a, b; } | |
107 | + * but in this case, the only effect will be, that the ABI checksums become | |
108 | + * more volatile, which is acceptable. Also, such collisions are quite rare, | |
109 | + * so far it was only observed in include/linux/telephony.h. | |
110 | + */ | |
102 | 111 | #define _APP(T,L) do { \ |
103 | 112 | cur_node = next_node; \ |
104 | 113 | next_node = xmalloc(sizeof(*next_node)); \ |
105 | 114 | next_node->next = cur_node; \ |
106 | 115 | cur_node->string = memcpy(xmalloc(L+1), T, L+1); \ |
107 | - cur_node->tag = SYM_NORMAL; \ | |
116 | + cur_node->tag = \ | |
117 | + find_symbol(cur_node->string, SYM_ENUM_CONST, 1)?\ | |
118 | + SYM_ENUM_CONST : SYM_NORMAL ; \ | |
108 | 119 | } while (0) |
109 | 120 | |
110 | 121 | #define APP _APP(yytext, yyleng) |
111 | 122 | |
... | ... | @@ -182,8 +193,8 @@ |
182 | 193 | |
183 | 194 | case STRUCT_KEYW: |
184 | 195 | case UNION_KEYW: |
185 | - dont_want_brace_phrase = 3; | |
186 | 196 | case ENUM_KEYW: |
197 | + dont_want_brace_phrase = 3; | |
187 | 198 | suppress_type_lookup = 2; |
188 | 199 | goto fini; |
189 | 200 | |
... | ... | @@ -312,7 +323,20 @@ |
312 | 323 | ++count; |
313 | 324 | APP; |
314 | 325 | goto repeat; |
315 | - case ')': case ']': case '}': | |
326 | + case '}': | |
327 | + /* is this the last line of an enum declaration? */ | |
328 | + if (count == 0) | |
329 | + { | |
330 | + /* Put back the token we just read so's we can find it again | |
331 | + after registering the expression. */ | |
332 | + unput(token); | |
333 | + | |
334 | + lexstate = ST_NORMAL; | |
335 | + token = EXPRESSION_PHRASE; | |
336 | + break; | |
337 | + } | |
338 | + /* FALLTHRU */ | |
339 | + case ')': case ']': | |
316 | 340 | --count; |
317 | 341 | APP; |
318 | 342 | goto repeat; |
scripts/genksyms/parse.y
... | ... | @@ -25,6 +25,7 @@ |
25 | 25 | |
26 | 26 | #include <assert.h> |
27 | 27 | #include <stdlib.h> |
28 | +#include <string.h> | |
28 | 29 | #include "genksyms.h" |
29 | 30 | |
30 | 31 | static int is_typedef; |
31 | 32 | |
... | ... | @@ -227,16 +228,19 @@ |
227 | 228 | add_symbol(i->string, SYM_UNION, s, is_extern); |
228 | 229 | $$ = $3; |
229 | 230 | } |
230 | - | ENUM_KEYW IDENT BRACE_PHRASE | |
231 | + | ENUM_KEYW IDENT enum_body | |
231 | 232 | { struct string_list *s = *$3, *i = *$2, *r; |
232 | 233 | r = copy_node(i); r->tag = SYM_ENUM; |
233 | 234 | r->next = (*$1)->next; *$3 = r; (*$1)->next = NULL; |
234 | 235 | add_symbol(i->string, SYM_ENUM, s, is_extern); |
235 | 236 | $$ = $3; |
236 | 237 | } |
237 | - | |
238 | - /* Anonymous s/u/e definitions. Nothing needs doing. */ | |
239 | - | ENUM_KEYW BRACE_PHRASE { $$ = $2; } | |
238 | + /* | |
239 | + * Anonymous enum definition. Tell add_symbol() to restart its counter. | |
240 | + */ | |
241 | + | ENUM_KEYW enum_body | |
242 | + { add_symbol(NULL, SYM_ENUM, NULL, 0); $$ = $2; } | |
243 | + /* Anonymous s/u definitions. Nothing needs doing. */ | |
240 | 244 | | STRUCT_KEYW class_body { $$ = $2; } |
241 | 245 | | UNION_KEYW class_body { $$ = $2; } |
242 | 246 | ; |
... | ... | @@ -448,6 +452,28 @@ |
448 | 452 | /* empty */ { $$ = NULL; } |
449 | 453 | | attribute_opt ATTRIBUTE_PHRASE |
450 | 454 | ; |
455 | + | |
456 | +enum_body: | |
457 | + '{' enumerator_list '}' { $$ = $3; } | |
458 | + | '{' enumerator_list ',' '}' { $$ = $4; } | |
459 | + ; | |
460 | + | |
461 | +enumerator_list: | |
462 | + enumerator | |
463 | + | enumerator_list ',' enumerator | |
464 | + | |
465 | +enumerator: | |
466 | + IDENT | |
467 | + { | |
468 | + const char *name = strdup((*$1)->string); | |
469 | + add_symbol(name, SYM_ENUM_CONST, NULL, 0); | |
470 | + } | |
471 | + | IDENT '=' EXPRESSION_PHRASE | |
472 | + { | |
473 | + const char *name = strdup((*$1)->string); | |
474 | + struct string_list *expr = copy_list_range(*$3, *$2); | |
475 | + add_symbol(name, SYM_ENUM_CONST, expr, 0); | |
476 | + } | |
451 | 477 | |
452 | 478 | asm_definition: |
453 | 479 | ASM_PHRASE ';' { $$ = $2; } |