Commit e37ddb82 authored by Michal Marek's avatar Michal Marek

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: default avatarMichal Marek <mmarek@suse.cz>
Acked-by: default avatarSam Ravnborg <sam@ravnborg.org>
parent 01762c4e
...@@ -62,6 +62,7 @@ static const struct { ...@@ -62,6 +62,7 @@ static const struct {
[SYM_ENUM] = {'e', "enum"}, [SYM_ENUM] = {'e', "enum"},
[SYM_STRUCT] = {'s', "struct"}, [SYM_STRUCT] = {'s', "struct"},
[SYM_UNION] = {'u', "union"}, [SYM_UNION] = {'u', "union"},
[SYM_ENUM_CONST] = {'E', "enum constant"},
}; };
static int equal_list(struct string_list *a, struct string_list *b); static int equal_list(struct string_list *a, struct string_list *b);
...@@ -149,10 +150,16 @@ static unsigned long crc32(const char *s) ...@@ -149,10 +150,16 @@ static unsigned long crc32(const char *s)
static enum symbol_type map_to_ns(enum symbol_type t) static enum symbol_type map_to_ns(enum symbol_type t)
{ {
if (t == SYM_TYPEDEF) switch (t) {
t = SYM_NORMAL; case SYM_ENUM_CONST:
else if (t == SYM_UNION) case SYM_NORMAL:
t = SYM_STRUCT; case SYM_TYPEDEF:
return SYM_NORMAL;
case SYM_ENUM:
case SYM_STRUCT:
case SYM_UNION:
return SYM_STRUCT;
}
return t; return t;
} }
...@@ -191,10 +198,47 @@ static struct symbol *__add_symbol(const char *name, enum symbol_type type, ...@@ -191,10 +198,47 @@ static struct symbol *__add_symbol(const char *name, enum symbol_type type,
struct string_list *defn, int is_extern, struct string_list *defn, int is_extern,
int is_reference) int is_reference)
{ {
unsigned long h = crc32(name) % HASH_BUCKETS; unsigned long h;
struct symbol *sym; struct symbol *sym;
enum symbol_status status = STATUS_UNCHANGED; enum symbol_status status = STATUS_UNCHANGED;
/* The parser adds symbols in the order their declaration completes,
* so it is safe to store the value of the previous enum constant in
* a static variable.
*/
static int enum_counter;
static struct string_list *last_enum_expr;
if (type == SYM_ENUM_CONST) {
if (defn) {
free_list(last_enum_expr, NULL);
last_enum_expr = copy_list_range(defn, NULL);
enum_counter = 1;
} else {
struct string_list *expr;
char buf[20];
snprintf(buf, sizeof(buf), "%d", enum_counter++);
if (last_enum_expr) {
expr = copy_list_range(last_enum_expr, NULL);
defn = concat_list(mk_node("("),
expr,
mk_node(")"),
mk_node("+"),
mk_node(buf), NULL);
} else {
defn = mk_node(buf);
}
}
} else if (type == SYM_ENUM) {
free_list(last_enum_expr, NULL);
last_enum_expr = NULL;
enum_counter = 0;
if (!name)
/* Anonymous enum definition, nothing more to do */
return NULL;
}
h = crc32(name) % HASH_BUCKETS;
for (sym = symtab[h]; sym; sym = sym->hash_next) { for (sym = symtab[h]; sym; sym = sym->hash_next) {
if (map_to_ns(sym->type) == map_to_ns(type) && if (map_to_ns(sym->type) == map_to_ns(type) &&
strcmp(name, sym->name) == 0) { strcmp(name, sym->name) == 0) {
...@@ -343,6 +387,22 @@ struct string_list *copy_node(struct string_list *node) ...@@ -343,6 +387,22 @@ struct string_list *copy_node(struct string_list *node)
return newnode; return newnode;
} }
struct string_list *copy_list_range(struct string_list *start,
struct string_list *end)
{
struct string_list *res, *n;
if (start == end)
return NULL;
n = res = copy_node(start);
for (start = start->next; start != end; start = start->next) {
n->next = copy_node(start);
n = n->next;
}
n->next = NULL;
return res;
}
static int equal_list(struct string_list *a, struct string_list *b) static int equal_list(struct string_list *a, struct string_list *b)
{ {
while (a && b) { while (a && b) {
...@@ -512,6 +572,7 @@ static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc) ...@@ -512,6 +572,7 @@ static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc)
crc = partial_crc32_one(' ', crc); crc = partial_crc32_one(' ', crc);
break; break;
case SYM_ENUM_CONST:
case SYM_TYPEDEF: case SYM_TYPEDEF:
subsym = find_symbol(cur->string, cur->tag, 0); subsym = find_symbol(cur->string, cur->tag, 0);
/* FIXME: Bad reference files can segfault here. */ /* FIXME: Bad reference files can segfault here. */
......
...@@ -26,7 +26,8 @@ ...@@ -26,7 +26,8 @@
#include <stdio.h> #include <stdio.h>
enum symbol_type { enum symbol_type {
SYM_NORMAL, SYM_TYPEDEF, SYM_ENUM, SYM_STRUCT, SYM_UNION SYM_NORMAL, SYM_TYPEDEF, SYM_ENUM, SYM_STRUCT, SYM_UNION,
SYM_ENUM_CONST
}; };
enum symbol_status { enum symbol_status {
...@@ -66,6 +67,8 @@ void export_symbol(const char *); ...@@ -66,6 +67,8 @@ void export_symbol(const char *);
void free_node(struct string_list *list); void free_node(struct string_list *list);
void free_list(struct string_list *s, struct string_list *e); void free_list(struct string_list *s, struct string_list *e);
struct string_list *copy_node(struct string_list *); struct string_list *copy_node(struct string_list *);
struct string_list *copy_list_range(struct string_list *start,
struct string_list *end);
int yylex(void); int yylex(void);
int yyparse(void); int yyparse(void);
......
...@@ -99,12 +99,23 @@ MC_TOKEN ([~%^&*+=|<>/-]=)|(&&)|("||")|(->)|(<<)|(>>) ...@@ -99,12 +99,23 @@ MC_TOKEN ([~%^&*+=|<>/-]=)|(&&)|("||")|(->)|(<<)|(>>)
/* Macros to append to our phrase collection list. */ /* Macros to append to our phrase collection list. */
/*
* We mark any token, that that equals to a known enumerator, as
* SYM_ENUM_CONST. The parser will change this for struct and union tags later,
* the only problem is struct and union members:
* enum e { a, b }; struct s { int a, b; }
* but in this case, the only effect will be, that the ABI checksums become
* more volatile, which is acceptable. Also, such collisions are quite rare,
* so far it was only observed in include/linux/telephony.h.
*/
#define _APP(T,L) do { \ #define _APP(T,L) do { \
cur_node = next_node; \ cur_node = next_node; \
next_node = xmalloc(sizeof(*next_node)); \ next_node = xmalloc(sizeof(*next_node)); \
next_node->next = cur_node; \ next_node->next = cur_node; \
cur_node->string = memcpy(xmalloc(L+1), T, L+1); \ cur_node->string = memcpy(xmalloc(L+1), T, L+1); \
cur_node->tag = SYM_NORMAL; \ cur_node->tag = \
find_symbol(cur_node->string, SYM_ENUM_CONST, 1)?\
SYM_ENUM_CONST : SYM_NORMAL ; \
} while (0) } while (0)
#define APP _APP(yytext, yyleng) #define APP _APP(yytext, yyleng)
...@@ -182,8 +193,8 @@ repeat: ...@@ -182,8 +193,8 @@ repeat:
case STRUCT_KEYW: case STRUCT_KEYW:
case UNION_KEYW: case UNION_KEYW:
dont_want_brace_phrase = 3;
case ENUM_KEYW: case ENUM_KEYW:
dont_want_brace_phrase = 3;
suppress_type_lookup = 2; suppress_type_lookup = 2;
goto fini; goto fini;
...@@ -312,7 +323,20 @@ repeat: ...@@ -312,7 +323,20 @@ repeat:
++count; ++count;
APP; APP;
goto repeat; goto repeat;
case ')': case ']': case '}': case '}':
/* is this the last line of an enum declaration? */
if (count == 0)
{
/* Put back the token we just read so's we can find it again
after registering the expression. */
unput(token);
lexstate = ST_NORMAL;
token = EXPRESSION_PHRASE;
break;
}
/* FALLTHRU */
case ')': case ']':
--count; --count;
APP; APP;
goto repeat; goto repeat;
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "genksyms.h" #include "genksyms.h"
static int is_typedef; static int is_typedef;
...@@ -227,16 +228,19 @@ type_specifier: ...@@ -227,16 +228,19 @@ type_specifier:
add_symbol(i->string, SYM_UNION, s, is_extern); add_symbol(i->string, SYM_UNION, s, is_extern);
$$ = $3; $$ = $3;
} }
| ENUM_KEYW IDENT BRACE_PHRASE | ENUM_KEYW IDENT enum_body
{ struct string_list *s = *$3, *i = *$2, *r; { struct string_list *s = *$3, *i = *$2, *r;
r = copy_node(i); r->tag = SYM_ENUM; r = copy_node(i); r->tag = SYM_ENUM;
r->next = (*$1)->next; *$3 = r; (*$1)->next = NULL; r->next = (*$1)->next; *$3 = r; (*$1)->next = NULL;
add_symbol(i->string, SYM_ENUM, s, is_extern); add_symbol(i->string, SYM_ENUM, s, is_extern);
$$ = $3; $$ = $3;
} }
/*
/* Anonymous s/u/e definitions. Nothing needs doing. */ * Anonymous enum definition. Tell add_symbol() to restart its counter.
| ENUM_KEYW BRACE_PHRASE { $$ = $2; } */
| ENUM_KEYW enum_body
{ add_symbol(NULL, SYM_ENUM, NULL, 0); $$ = $2; }
/* Anonymous s/u definitions. Nothing needs doing. */
| STRUCT_KEYW class_body { $$ = $2; } | STRUCT_KEYW class_body { $$ = $2; }
| UNION_KEYW class_body { $$ = $2; } | UNION_KEYW class_body { $$ = $2; }
; ;
...@@ -449,6 +453,28 @@ attribute_opt: ...@@ -449,6 +453,28 @@ attribute_opt:
| attribute_opt ATTRIBUTE_PHRASE | attribute_opt ATTRIBUTE_PHRASE
; ;
enum_body:
'{' enumerator_list '}' { $$ = $3; }
| '{' enumerator_list ',' '}' { $$ = $4; }
;
enumerator_list:
enumerator
| enumerator_list ',' enumerator
enumerator:
IDENT
{
const char *name = strdup((*$1)->string);
add_symbol(name, SYM_ENUM_CONST, NULL, 0);
}
| IDENT '=' EXPRESSION_PHRASE
{
const char *name = strdup((*$1)->string);
struct string_list *expr = copy_list_range(*$3, *$2);
add_symbol(name, SYM_ENUM_CONST, expr, 0);
}
asm_definition: asm_definition:
ASM_PHRASE ';' { $$ = $2; } ASM_PHRASE ';' { $$ = $2; }
; ;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment