diff options
author | Michal Marek <mmarek@suse.cz> | 2011-02-03 17:57:09 -0500 |
---|---|---|
committer | Michal Marek <mmarek@suse.cz> | 2011-03-17 10:13:56 -0400 |
commit | e37ddb82500393cb417c3ab0fe0726d9a8652372 (patch) | |
tree | 6ecc94992cb5affad4fe438d9b586a61b803f928 /scripts | |
parent | 01762c4ec5f6f62c550304b9c70e824293cefdd0 (diff) |
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>
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/genksyms/genksyms.c | 71 | ||||
-rw-r--r-- | scripts/genksyms/genksyms.h | 5 | ||||
-rw-r--r-- | scripts/genksyms/lex.l | 30 | ||||
-rw-r--r-- | scripts/genksyms/parse.y | 34 |
4 files changed, 127 insertions, 13 deletions
diff --git a/scripts/genksyms/genksyms.c b/scripts/genksyms/genksyms.c index 4a350816a9e8..f9e75531ea03 100644 --- a/scripts/genksyms/genksyms.c +++ b/scripts/genksyms/genksyms.c | |||
@@ -62,6 +62,7 @@ static const struct { | |||
62 | [SYM_ENUM] = {'e', "enum"}, | 62 | [SYM_ENUM] = {'e', "enum"}, |
63 | [SYM_STRUCT] = {'s', "struct"}, | 63 | [SYM_STRUCT] = {'s', "struct"}, |
64 | [SYM_UNION] = {'u', "union"}, | 64 | [SYM_UNION] = {'u', "union"}, |
65 | [SYM_ENUM_CONST] = {'E', "enum constant"}, | ||
65 | }; | 66 | }; |
66 | 67 | ||
67 | static int equal_list(struct string_list *a, struct string_list *b); | 68 | static int equal_list(struct string_list *a, struct string_list *b); |
@@ -149,10 +150,16 @@ static unsigned long crc32(const char *s) | |||
149 | 150 | ||
150 | static enum symbol_type map_to_ns(enum symbol_type t) | 151 | static enum symbol_type map_to_ns(enum symbol_type t) |
151 | { | 152 | { |
152 | if (t == SYM_TYPEDEF) | 153 | switch (t) { |
153 | t = SYM_NORMAL; | 154 | case SYM_ENUM_CONST: |
154 | else if (t == SYM_UNION) | 155 | case SYM_NORMAL: |
155 | t = SYM_STRUCT; | 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 | return t; | 163 | return t; |
157 | } | 164 | } |
158 | 165 | ||
@@ -191,10 +198,47 @@ static struct symbol *__add_symbol(const char *name, enum symbol_type type, | |||
191 | struct string_list *defn, int is_extern, | 198 | struct string_list *defn, int is_extern, |
192 | int is_reference) | 199 | int is_reference) |
193 | { | 200 | { |
194 | unsigned long h = crc32(name) % HASH_BUCKETS; | 201 | unsigned long h; |
195 | struct symbol *sym; | 202 | struct symbol *sym; |
196 | enum symbol_status status = STATUS_UNCHANGED; | 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; | ||
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 | } | ||
197 | 240 | ||
241 | h = crc32(name) % HASH_BUCKETS; | ||
198 | for (sym = symtab[h]; sym; sym = sym->hash_next) { | 242 | for (sym = symtab[h]; sym; sym = sym->hash_next) { |
199 | if (map_to_ns(sym->type) == map_to_ns(type) && | 243 | if (map_to_ns(sym->type) == map_to_ns(type) && |
200 | strcmp(name, sym->name) == 0) { | 244 | strcmp(name, sym->name) == 0) { |
@@ -343,6 +387,22 @@ struct string_list *copy_node(struct string_list *node) | |||
343 | return newnode; | 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 | static int equal_list(struct string_list *a, struct string_list *b) | 406 | static int equal_list(struct string_list *a, struct string_list *b) |
347 | { | 407 | { |
348 | while (a && b) { | 408 | while (a && b) { |
@@ -512,6 +572,7 @@ static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc) | |||
512 | crc = partial_crc32_one(' ', crc); | 572 | crc = partial_crc32_one(' ', crc); |
513 | break; | 573 | break; |
514 | 574 | ||
575 | case SYM_ENUM_CONST: | ||
515 | case SYM_TYPEDEF: | 576 | case SYM_TYPEDEF: |
516 | subsym = find_symbol(cur->string, cur->tag, 0); | 577 | subsym = find_symbol(cur->string, cur->tag, 0); |
517 | /* FIXME: Bad reference files can segfault here. */ | 578 | /* FIXME: Bad reference files can segfault here. */ |
diff --git a/scripts/genksyms/genksyms.h b/scripts/genksyms/genksyms.h index 9fdafb667e76..7ec52ae3846a 100644 --- a/scripts/genksyms/genksyms.h +++ b/scripts/genksyms/genksyms.h | |||
@@ -26,7 +26,8 @@ | |||
26 | #include <stdio.h> | 26 | #include <stdio.h> |
27 | 27 | ||
28 | enum symbol_type { | 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 | enum symbol_status { | 33 | enum symbol_status { |
@@ -66,6 +67,8 @@ void export_symbol(const char *); | |||
66 | void free_node(struct string_list *list); | 67 | void free_node(struct string_list *list); |
67 | void free_list(struct string_list *s, struct string_list *e); | 68 | void free_list(struct string_list *s, struct string_list *e); |
68 | struct string_list *copy_node(struct string_list *); | 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 | int yylex(void); | 73 | int yylex(void); |
71 | int yyparse(void); | 74 | int yyparse(void); |
diff --git a/scripts/genksyms/lex.l b/scripts/genksyms/lex.l index c125d06fbd36..e4ddd493fec3 100644 --- a/scripts/genksyms/lex.l +++ b/scripts/genksyms/lex.l | |||
@@ -99,12 +99,23 @@ MC_TOKEN ([~%^&*+=|<>/-]=)|(&&)|("||")|(->)|(<<)|(>>) | |||
99 | 99 | ||
100 | /* Macros to append to our phrase collection list. */ | 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 | #define _APP(T,L) do { \ | 111 | #define _APP(T,L) do { \ |
103 | cur_node = next_node; \ | 112 | cur_node = next_node; \ |
104 | next_node = xmalloc(sizeof(*next_node)); \ | 113 | next_node = xmalloc(sizeof(*next_node)); \ |
105 | next_node->next = cur_node; \ | 114 | next_node->next = cur_node; \ |
106 | cur_node->string = memcpy(xmalloc(L+1), T, L+1); \ | 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 | } while (0) | 119 | } while (0) |
109 | 120 | ||
110 | #define APP _APP(yytext, yyleng) | 121 | #define APP _APP(yytext, yyleng) |
@@ -182,8 +193,8 @@ repeat: | |||
182 | 193 | ||
183 | case STRUCT_KEYW: | 194 | case STRUCT_KEYW: |
184 | case UNION_KEYW: | 195 | case UNION_KEYW: |
185 | dont_want_brace_phrase = 3; | ||
186 | case ENUM_KEYW: | 196 | case ENUM_KEYW: |
197 | dont_want_brace_phrase = 3; | ||
187 | suppress_type_lookup = 2; | 198 | suppress_type_lookup = 2; |
188 | goto fini; | 199 | goto fini; |
189 | 200 | ||
@@ -312,7 +323,20 @@ repeat: | |||
312 | ++count; | 323 | ++count; |
313 | APP; | 324 | APP; |
314 | goto repeat; | 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 | --count; | 340 | --count; |
317 | APP; | 341 | APP; |
318 | goto repeat; | 342 | goto repeat; |
diff --git a/scripts/genksyms/parse.y b/scripts/genksyms/parse.y index 09a265cd7193..ba5c242866c1 100644 --- a/scripts/genksyms/parse.y +++ b/scripts/genksyms/parse.y | |||
@@ -25,6 +25,7 @@ | |||
25 | 25 | ||
26 | #include <assert.h> | 26 | #include <assert.h> |
27 | #include <stdlib.h> | 27 | #include <stdlib.h> |
28 | #include <string.h> | ||
28 | #include "genksyms.h" | 29 | #include "genksyms.h" |
29 | 30 | ||
30 | static int is_typedef; | 31 | static int is_typedef; |
@@ -227,16 +228,19 @@ type_specifier: | |||
227 | add_symbol(i->string, SYM_UNION, s, is_extern); | 228 | add_symbol(i->string, SYM_UNION, s, is_extern); |
228 | $$ = $3; | 229 | $$ = $3; |
229 | } | 230 | } |
230 | | ENUM_KEYW IDENT BRACE_PHRASE | 231 | | ENUM_KEYW IDENT enum_body |
231 | { struct string_list *s = *$3, *i = *$2, *r; | 232 | { struct string_list *s = *$3, *i = *$2, *r; |
232 | r = copy_node(i); r->tag = SYM_ENUM; | 233 | r = copy_node(i); r->tag = SYM_ENUM; |
233 | r->next = (*$1)->next; *$3 = r; (*$1)->next = NULL; | 234 | r->next = (*$1)->next; *$3 = r; (*$1)->next = NULL; |
234 | add_symbol(i->string, SYM_ENUM, s, is_extern); | 235 | add_symbol(i->string, SYM_ENUM, s, is_extern); |
235 | $$ = $3; | 236 | $$ = $3; |
236 | } | 237 | } |
237 | 238 | /* | |
238 | /* Anonymous s/u/e definitions. Nothing needs doing. */ | 239 | * Anonymous enum definition. Tell add_symbol() to restart its counter. |
239 | | ENUM_KEYW BRACE_PHRASE { $$ = $2; } | 240 | */ |
241 | | ENUM_KEYW enum_body | ||
242 | { add_symbol(NULL, SYM_ENUM, NULL, 0); $$ = $2; } | ||
243 | /* Anonymous s/u definitions. Nothing needs doing. */ | ||
240 | | STRUCT_KEYW class_body { $$ = $2; } | 244 | | STRUCT_KEYW class_body { $$ = $2; } |
241 | | UNION_KEYW class_body { $$ = $2; } | 245 | | UNION_KEYW class_body { $$ = $2; } |
242 | ; | 246 | ; |
@@ -449,6 +453,28 @@ attribute_opt: | |||
449 | | attribute_opt ATTRIBUTE_PHRASE | 453 | | attribute_opt ATTRIBUTE_PHRASE |
450 | ; | 454 | ; |
451 | 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 | } | ||
477 | |||
452 | asm_definition: | 478 | asm_definition: |
453 | ASM_PHRASE ';' { $$ = $2; } | 479 | ASM_PHRASE ';' { $$ = $2; } |
454 | ; | 480 | ; |