mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	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:
		
							parent
							
								
									01762c4ec5
								
							
						
					
					
						commit
						e37ddb8250
					
				
					 4 changed files with 127 additions and 13 deletions
				
			
		|  | @ -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; } | ||||||
| 	; | 	; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Michal Marek
						Michal Marek