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>
This commit is contained in:
@@ -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)
|
|||||||
|
|
||||||
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,
|
|||||||
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)
|
|||||||
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)
|
|||||||
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 @@
|
|||||||
#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 *);
|
|||||||
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 ([~%^&*+=|<>/-]=)|(&&)|("||")|(->)|(<<)|(>>)
|
|||||||
|
|
||||||
/* 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:
|
|||||||
|
|
||||||
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:
|
|||||||
++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 @@
|
|||||||
|
|
||||||
#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:
|
|||||||
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:
|
|||||||
| 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; }
|
||||||
;
|
;
|
||||||
|
Reference in New Issue
Block a user