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_STRUCT]     = {'s', "struct"}, | ||||
| 	[SYM_UNION]      = {'u', "union"}, | ||||
| 	[SYM_ENUM_CONST] = {'E', "enum constant"}, | ||||
| }; | ||||
| 
 | ||||
| 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) | ||||
| { | ||||
| 	if (t == SYM_TYPEDEF) | ||||
| 		t = SYM_NORMAL; | ||||
| 	else if (t == SYM_UNION) | ||||
| 		t = SYM_STRUCT; | ||||
| 	switch (t) { | ||||
| 	case SYM_ENUM_CONST: | ||||
| 	case SYM_NORMAL: | ||||
| 	case SYM_TYPEDEF: | ||||
| 		return SYM_NORMAL; | ||||
| 	case SYM_ENUM: | ||||
| 	case SYM_STRUCT: | ||||
| 	case SYM_UNION: | ||||
| 		return SYM_STRUCT; | ||||
| 	} | ||||
| 	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, | ||||
| 			    int is_reference) | ||||
| { | ||||
| 	unsigned long h = crc32(name) % HASH_BUCKETS; | ||||
| 	unsigned long h; | ||||
| 	struct symbol *sym; | ||||
| 	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) { | ||||
| 		if (map_to_ns(sym->type) == map_to_ns(type) && | ||||
| 		    strcmp(name, sym->name) == 0) { | ||||
|  | @ -343,6 +387,22 @@ struct string_list *copy_node(struct string_list *node) | |||
| 	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) | ||||
| { | ||||
| 	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); | ||||
| 			break; | ||||
| 
 | ||||
| 		case SYM_ENUM_CONST: | ||||
| 		case SYM_TYPEDEF: | ||||
| 			subsym = find_symbol(cur->string, cur->tag, 0); | ||||
| 			/* FIXME: Bad reference files can segfault here. */ | ||||
|  |  | |||
|  | @ -26,7 +26,8 @@ | |||
| #include <stdio.h> | ||||
| 
 | ||||
| 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 { | ||||
|  | @ -66,6 +67,8 @@ void export_symbol(const char *); | |||
| void free_node(struct string_list *list); | ||||
| void free_list(struct string_list *s, struct string_list *e); | ||||
| 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 yyparse(void); | ||||
|  |  | |||
|  | @ -99,12 +99,23 @@ MC_TOKEN		([~%^&*+=|<>/-]=)|(&&)|("||")|(->)|(<<)|(>>) | |||
| 
 | ||||
| /* 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 {						   \ | ||||
| 			  cur_node = next_node;				   \ | ||||
| 			  next_node = xmalloc(sizeof(*next_node));	   \ | ||||
| 			  next_node->next = cur_node;			   \ | ||||
| 			  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) | ||||
| 
 | ||||
| #define APP		_APP(yytext, yyleng) | ||||
|  | @ -182,8 +193,8 @@ repeat: | |||
| 
 | ||||
| 		  case STRUCT_KEYW: | ||||
| 		  case UNION_KEYW: | ||||
| 		    dont_want_brace_phrase = 3; | ||||
| 		  case ENUM_KEYW: | ||||
| 		    dont_want_brace_phrase = 3; | ||||
| 		    suppress_type_lookup = 2; | ||||
| 		    goto fini; | ||||
| 
 | ||||
|  | @ -312,7 +323,20 @@ repeat: | |||
| 	  ++count; | ||||
| 	  APP; | ||||
| 	  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; | ||||
| 	  APP; | ||||
| 	  goto repeat; | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ | |||
| 
 | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include "genksyms.h" | ||||
| 
 | ||||
| static int is_typedef; | ||||
|  | @ -227,16 +228,19 @@ type_specifier: | |||
| 		  add_symbol(i->string, SYM_UNION, s, is_extern); | ||||
| 		  $$ = $3; | ||||
| 		} | ||||
| 	| ENUM_KEYW IDENT BRACE_PHRASE | ||||
| 	| ENUM_KEYW IDENT enum_body | ||||
| 		{ struct string_list *s = *$3, *i = *$2, *r; | ||||
| 		  r = copy_node(i); r->tag = SYM_ENUM; | ||||
| 		  r->next = (*$1)->next; *$3 = r; (*$1)->next = NULL; | ||||
| 		  add_symbol(i->string, SYM_ENUM, s, is_extern); | ||||
| 		  $$ = $3; | ||||
| 		} | ||||
| 
 | ||||
| 	/* Anonymous s/u/e definitions.  Nothing needs doing.  */ | ||||
| 	| ENUM_KEYW BRACE_PHRASE			{ $$ = $2; } | ||||
| 	/* | ||||
| 	 * Anonymous enum definition. Tell add_symbol() to restart its counter. | ||||
| 	 */ | ||||
| 	| ENUM_KEYW enum_body | ||||
| 		{ add_symbol(NULL, SYM_ENUM, NULL, 0); $$ = $2; } | ||||
| 	/* Anonymous s/u definitions.  Nothing needs doing.  */ | ||||
| 	| STRUCT_KEYW class_body			{ $$ = $2; } | ||||
| 	| UNION_KEYW class_body				{ $$ = $2; } | ||||
| 	; | ||||
|  | @ -449,6 +453,28 @@ attribute_opt: | |||
| 	| 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_PHRASE ';'					{ $$ = $2; } | ||||
| 	; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Michal Marek
						Michal Marek