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 4a350816a9e..f9e75531ea0 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 9fdafb667e7..7ec52ae3846 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 c125d06fbd3..e4ddd493fec 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 | |||
