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 | ; |
