xref: /linux/scripts/genksyms/genksyms.c (revision 2759bd908f3cc8d286e1fa64ec7ee7f5d1124837)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Generate kernel symbol version hashes.
3    Copyright 1996, 1997 Linux International.
4 
5    New implementation contributed by Richard Henderson <rth@tamu.edu>
6    Based on original work by Bjorn Ekwall <bj0rn@blox.se>
7 
8    This file was part of the Linux modutils 2.4.22: moved back into the
9    kernel sources by Rusty Russell/Kai Germaschewski.
10 
11  */
12 
13 #include <stdio.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <assert.h>
18 #include <stdarg.h>
19 #include <getopt.h>
20 
21 #include <hashtable.h>
22 
23 #include "genksyms.h"
24 /*----------------------------------------------------------------------*/
25 
26 static HASHTABLE_DEFINE(symbol_hashtable, 1U << 12);
27 static FILE *debugfile;
28 
29 int cur_line = 1;
30 char *cur_filename;
31 int in_source_file;
32 
33 static int flag_debug, flag_dump_defs, flag_reference, flag_dump_types,
34 	   flag_preserve, flag_warnings;
35 
36 static int errors;
37 static int nsyms;
38 
39 static struct symbol *expansion_trail;
40 static struct symbol *visited_symbols;
41 
42 static const struct {
43 	int n;
44 	const char *name;
45 } symbol_types[] = {
46 	[SYM_NORMAL]     = { 0, NULL},
47 	[SYM_TYPEDEF]    = {'t', "typedef"},
48 	[SYM_ENUM]       = {'e', "enum"},
49 	[SYM_STRUCT]     = {'s', "struct"},
50 	[SYM_UNION]      = {'u', "union"},
51 	[SYM_ENUM_CONST] = {'E', "enum constant"},
52 };
53 
54 static int equal_list(struct string_list *a, struct string_list *b);
55 static void print_list(FILE * f, struct string_list *list);
56 static struct string_list *concat_list(struct string_list *start, ...);
57 static struct string_list *mk_node(const char *string);
58 static void print_location(void);
59 static void print_type_name(enum symbol_type type, const char *name);
60 
61 /*----------------------------------------------------------------------*/
62 
63 static const unsigned int crctab32[] = {
64 	0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U,
65 	0x706af48fU, 0xe963a535U, 0x9e6495a3U, 0x0edb8832U, 0x79dcb8a4U,
66 	0xe0d5e91eU, 0x97d2d988U, 0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U,
67 	0x90bf1d91U, 0x1db71064U, 0x6ab020f2U, 0xf3b97148U, 0x84be41deU,
68 	0x1adad47dU, 0x6ddde4ebU, 0xf4d4b551U, 0x83d385c7U, 0x136c9856U,
69 	0x646ba8c0U, 0xfd62f97aU, 0x8a65c9ecU, 0x14015c4fU, 0x63066cd9U,
70 	0xfa0f3d63U, 0x8d080df5U, 0x3b6e20c8U, 0x4c69105eU, 0xd56041e4U,
71 	0xa2677172U, 0x3c03e4d1U, 0x4b04d447U, 0xd20d85fdU, 0xa50ab56bU,
72 	0x35b5a8faU, 0x42b2986cU, 0xdbbbc9d6U, 0xacbcf940U, 0x32d86ce3U,
73 	0x45df5c75U, 0xdcd60dcfU, 0xabd13d59U, 0x26d930acU, 0x51de003aU,
74 	0xc8d75180U, 0xbfd06116U, 0x21b4f4b5U, 0x56b3c423U, 0xcfba9599U,
75 	0xb8bda50fU, 0x2802b89eU, 0x5f058808U, 0xc60cd9b2U, 0xb10be924U,
76 	0x2f6f7c87U, 0x58684c11U, 0xc1611dabU, 0xb6662d3dU, 0x76dc4190U,
77 	0x01db7106U, 0x98d220bcU, 0xefd5102aU, 0x71b18589U, 0x06b6b51fU,
78 	0x9fbfe4a5U, 0xe8b8d433U, 0x7807c9a2U, 0x0f00f934U, 0x9609a88eU,
79 	0xe10e9818U, 0x7f6a0dbbU, 0x086d3d2dU, 0x91646c97U, 0xe6635c01U,
80 	0x6b6b51f4U, 0x1c6c6162U, 0x856530d8U, 0xf262004eU, 0x6c0695edU,
81 	0x1b01a57bU, 0x8208f4c1U, 0xf50fc457U, 0x65b0d9c6U, 0x12b7e950U,
82 	0x8bbeb8eaU, 0xfcb9887cU, 0x62dd1ddfU, 0x15da2d49U, 0x8cd37cf3U,
83 	0xfbd44c65U, 0x4db26158U, 0x3ab551ceU, 0xa3bc0074U, 0xd4bb30e2U,
84 	0x4adfa541U, 0x3dd895d7U, 0xa4d1c46dU, 0xd3d6f4fbU, 0x4369e96aU,
85 	0x346ed9fcU, 0xad678846U, 0xda60b8d0U, 0x44042d73U, 0x33031de5U,
86 	0xaa0a4c5fU, 0xdd0d7cc9U, 0x5005713cU, 0x270241aaU, 0xbe0b1010U,
87 	0xc90c2086U, 0x5768b525U, 0x206f85b3U, 0xb966d409U, 0xce61e49fU,
88 	0x5edef90eU, 0x29d9c998U, 0xb0d09822U, 0xc7d7a8b4U, 0x59b33d17U,
89 	0x2eb40d81U, 0xb7bd5c3bU, 0xc0ba6cadU, 0xedb88320U, 0x9abfb3b6U,
90 	0x03b6e20cU, 0x74b1d29aU, 0xead54739U, 0x9dd277afU, 0x04db2615U,
91 	0x73dc1683U, 0xe3630b12U, 0x94643b84U, 0x0d6d6a3eU, 0x7a6a5aa8U,
92 	0xe40ecf0bU, 0x9309ff9dU, 0x0a00ae27U, 0x7d079eb1U, 0xf00f9344U,
93 	0x8708a3d2U, 0x1e01f268U, 0x6906c2feU, 0xf762575dU, 0x806567cbU,
94 	0x196c3671U, 0x6e6b06e7U, 0xfed41b76U, 0x89d32be0U, 0x10da7a5aU,
95 	0x67dd4accU, 0xf9b9df6fU, 0x8ebeeff9U, 0x17b7be43U, 0x60b08ed5U,
96 	0xd6d6a3e8U, 0xa1d1937eU, 0x38d8c2c4U, 0x4fdff252U, 0xd1bb67f1U,
97 	0xa6bc5767U, 0x3fb506ddU, 0x48b2364bU, 0xd80d2bdaU, 0xaf0a1b4cU,
98 	0x36034af6U, 0x41047a60U, 0xdf60efc3U, 0xa867df55U, 0x316e8eefU,
99 	0x4669be79U, 0xcb61b38cU, 0xbc66831aU, 0x256fd2a0U, 0x5268e236U,
100 	0xcc0c7795U, 0xbb0b4703U, 0x220216b9U, 0x5505262fU, 0xc5ba3bbeU,
101 	0xb2bd0b28U, 0x2bb45a92U, 0x5cb36a04U, 0xc2d7ffa7U, 0xb5d0cf31U,
102 	0x2cd99e8bU, 0x5bdeae1dU, 0x9b64c2b0U, 0xec63f226U, 0x756aa39cU,
103 	0x026d930aU, 0x9c0906a9U, 0xeb0e363fU, 0x72076785U, 0x05005713U,
104 	0x95bf4a82U, 0xe2b87a14U, 0x7bb12baeU, 0x0cb61b38U, 0x92d28e9bU,
105 	0xe5d5be0dU, 0x7cdcefb7U, 0x0bdbdf21U, 0x86d3d2d4U, 0xf1d4e242U,
106 	0x68ddb3f8U, 0x1fda836eU, 0x81be16cdU, 0xf6b9265bU, 0x6fb077e1U,
107 	0x18b74777U, 0x88085ae6U, 0xff0f6a70U, 0x66063bcaU, 0x11010b5cU,
108 	0x8f659effU, 0xf862ae69U, 0x616bffd3U, 0x166ccf45U, 0xa00ae278U,
109 	0xd70dd2eeU, 0x4e048354U, 0x3903b3c2U, 0xa7672661U, 0xd06016f7U,
110 	0x4969474dU, 0x3e6e77dbU, 0xaed16a4aU, 0xd9d65adcU, 0x40df0b66U,
111 	0x37d83bf0U, 0xa9bcae53U, 0xdebb9ec5U, 0x47b2cf7fU, 0x30b5ffe9U,
112 	0xbdbdf21cU, 0xcabac28aU, 0x53b39330U, 0x24b4a3a6U, 0xbad03605U,
113 	0xcdd70693U, 0x54de5729U, 0x23d967bfU, 0xb3667a2eU, 0xc4614ab8U,
114 	0x5d681b02U, 0x2a6f2b94U, 0xb40bbe37U, 0xc30c8ea1U, 0x5a05df1bU,
115 	0x2d02ef8dU
116 };
117 
118 static unsigned long partial_crc32_one(unsigned char c, unsigned long crc)
119 {
120 	return crctab32[(crc ^ c) & 0xff] ^ (crc >> 8);
121 }
122 
123 static unsigned long partial_crc32(const char *s, unsigned long crc)
124 {
125 	while (*s)
126 		crc = partial_crc32_one(*s++, crc);
127 	return crc;
128 }
129 
130 static unsigned long crc32(const char *s)
131 {
132 	return partial_crc32(s, 0xffffffff) ^ 0xffffffff;
133 }
134 
135 /*----------------------------------------------------------------------*/
136 
137 static enum symbol_type map_to_ns(enum symbol_type t)
138 {
139 	switch (t) {
140 	case SYM_ENUM_CONST:
141 	case SYM_NORMAL:
142 	case SYM_TYPEDEF:
143 		return SYM_NORMAL;
144 	case SYM_ENUM:
145 	case SYM_STRUCT:
146 	case SYM_UNION:
147 		return SYM_STRUCT;
148 	}
149 	return t;
150 }
151 
152 struct symbol *find_symbol(const char *name, enum symbol_type ns, int exact)
153 {
154 	struct symbol *sym;
155 
156 	hash_for_each_possible(symbol_hashtable, sym, hnode, crc32(name)) {
157 		if (map_to_ns(sym->type) == map_to_ns(ns) &&
158 		    strcmp(name, sym->name) == 0 &&
159 		    sym->is_declared)
160 			break;
161 	}
162 
163 	if (exact && sym && sym->type != ns)
164 		return NULL;
165 	return sym;
166 }
167 
168 static int is_unknown_symbol(struct symbol *sym)
169 {
170 	struct string_list *defn;
171 
172 	return ((sym->type == SYM_STRUCT ||
173 		 sym->type == SYM_UNION ||
174 		 sym->type == SYM_ENUM) &&
175 		(defn = sym->defn)  && defn->tag == SYM_NORMAL &&
176 			strcmp(defn->string, "}") == 0 &&
177 		(defn = defn->next) && defn->tag == SYM_NORMAL &&
178 			strcmp(defn->string, "UNKNOWN") == 0 &&
179 		(defn = defn->next) && defn->tag == SYM_NORMAL &&
180 			strcmp(defn->string, "{") == 0);
181 }
182 
183 static struct symbol *__add_symbol(const char *name, enum symbol_type type,
184 			    struct string_list *defn, int is_extern,
185 			    int is_reference)
186 {
187 	unsigned long h;
188 	struct symbol *sym;
189 	enum symbol_status status = STATUS_UNCHANGED;
190 	/* The parser adds symbols in the order their declaration completes,
191 	 * so it is safe to store the value of the previous enum constant in
192 	 * a static variable.
193 	 */
194 	static int enum_counter;
195 	static struct string_list *last_enum_expr;
196 
197 	if (type == SYM_ENUM_CONST) {
198 		if (defn) {
199 			free_list(last_enum_expr, NULL);
200 			last_enum_expr = copy_list_range(defn, NULL);
201 			enum_counter = 1;
202 		} else {
203 			struct string_list *expr;
204 			char buf[20];
205 
206 			snprintf(buf, sizeof(buf), "%d", enum_counter++);
207 			if (last_enum_expr) {
208 				expr = copy_list_range(last_enum_expr, NULL);
209 				defn = concat_list(mk_node("("),
210 						   expr,
211 						   mk_node(")"),
212 						   mk_node("+"),
213 						   mk_node(buf), NULL);
214 			} else {
215 				defn = mk_node(buf);
216 			}
217 		}
218 	} else if (type == SYM_ENUM) {
219 		free_list(last_enum_expr, NULL);
220 		last_enum_expr = NULL;
221 		enum_counter = 0;
222 		if (!name)
223 			/* Anonymous enum definition, nothing more to do */
224 			return NULL;
225 	}
226 
227 	h = crc32(name);
228 	hash_for_each_possible(symbol_hashtable, sym, hnode, h) {
229 		if (map_to_ns(sym->type) != map_to_ns(type) ||
230 		    strcmp(name, sym->name))
231 			continue;
232 
233 		if (is_reference) {
234 			break;
235 		} else if (sym->type == type && equal_list(sym->defn, defn)) {
236 			if (!sym->is_declared && sym->is_override) {
237 				print_location();
238 				print_type_name(type, name);
239 				fprintf(stderr, " modversion is unchanged\n");
240 			}
241 			sym->is_declared = 1;
242 		} else if (sym->is_declared) {
243 			error_with_pos("redefinition of %s", name);
244 		} else if (sym->is_override && flag_preserve) {
245 			print_location();
246 			fprintf(stderr, "ignoring ");
247 			print_type_name(type, name);
248 			fprintf(stderr, " modversion change\n");
249 			sym->is_declared = 1;
250 		} else {
251 			status = is_unknown_symbol(sym) ?
252 					STATUS_DEFINED : STATUS_MODIFIED;
253 			break;
254 		}
255 		free_list(defn, NULL);
256 		return sym;
257 	}
258 
259 	if (sym) {
260 		hash_del(&sym->hnode);
261 
262 		free_list(sym->defn, NULL);
263 		free(sym->name);
264 		free(sym);
265 		--nsyms;
266 	}
267 
268 	sym = xmalloc(sizeof(*sym));
269 	sym->name = xstrdup(name);
270 	sym->type = type;
271 	sym->defn = defn;
272 	sym->expansion_trail = NULL;
273 	sym->visited = NULL;
274 	sym->is_extern = is_extern;
275 
276 	hash_add(symbol_hashtable, &sym->hnode, h);
277 
278 	sym->is_declared = !is_reference;
279 	sym->status = status;
280 	sym->is_override = 0;
281 
282 	if (flag_debug) {
283 		if (symbol_types[type].name)
284 			fprintf(debugfile, "Defn for %s %s == <",
285 				symbol_types[type].name, name);
286 		else
287 			fprintf(debugfile, "Defn for type%d %s == <",
288 				type, name);
289 		if (is_extern)
290 			fputs("extern ", debugfile);
291 		print_list(debugfile, defn);
292 		fputs(">\n", debugfile);
293 	}
294 
295 	++nsyms;
296 	return sym;
297 }
298 
299 struct symbol *add_symbol(const char *name, enum symbol_type type,
300 			  struct string_list *defn, int is_extern)
301 {
302 	return __add_symbol(name, type, defn, is_extern, 0);
303 }
304 
305 static struct symbol *add_reference_symbol(const char *name, enum symbol_type type,
306 				    struct string_list *defn, int is_extern)
307 {
308 	return __add_symbol(name, type, defn, is_extern, 1);
309 }
310 
311 /*----------------------------------------------------------------------*/
312 
313 void free_node(struct string_list *node)
314 {
315 	free(node->string);
316 	free(node);
317 }
318 
319 void free_list(struct string_list *s, struct string_list *e)
320 {
321 	while (s != e) {
322 		struct string_list *next = s->next;
323 		free_node(s);
324 		s = next;
325 	}
326 }
327 
328 static struct string_list *mk_node(const char *string)
329 {
330 	struct string_list *newnode;
331 
332 	newnode = xmalloc(sizeof(*newnode));
333 	newnode->string = xstrdup(string);
334 	newnode->tag = SYM_NORMAL;
335 	newnode->next = NULL;
336 
337 	return newnode;
338 }
339 
340 static struct string_list *concat_list(struct string_list *start, ...)
341 {
342 	va_list ap;
343 	struct string_list *n, *n2;
344 
345 	if (!start)
346 		return NULL;
347 	for (va_start(ap, start); (n = va_arg(ap, struct string_list *));) {
348 		for (n2 = n; n2->next; n2 = n2->next)
349 			;
350 		n2->next = start;
351 		start = n;
352 	}
353 	va_end(ap);
354 	return start;
355 }
356 
357 struct string_list *copy_node(struct string_list *node)
358 {
359 	struct string_list *newnode;
360 
361 	newnode = xmalloc(sizeof(*newnode));
362 	newnode->string = xstrdup(node->string);
363 	newnode->tag = node->tag;
364 
365 	return newnode;
366 }
367 
368 struct string_list *copy_list_range(struct string_list *start,
369 				    struct string_list *end)
370 {
371 	struct string_list *res, *n;
372 
373 	if (start == end)
374 		return NULL;
375 	n = res = copy_node(start);
376 	for (start = start->next; start != end; start = start->next) {
377 		n->next = copy_node(start);
378 		n = n->next;
379 	}
380 	n->next = NULL;
381 	return res;
382 }
383 
384 static int equal_list(struct string_list *a, struct string_list *b)
385 {
386 	while (a && b) {
387 		if (a->tag != b->tag || strcmp(a->string, b->string))
388 			return 0;
389 		a = a->next;
390 		b = b->next;
391 	}
392 
393 	return !a && !b;
394 }
395 
396 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
397 
398 static struct string_list *read_node(FILE *f)
399 {
400 	char buffer[256];
401 	struct string_list node = {
402 		.string = buffer,
403 		.tag = SYM_NORMAL };
404 	int c, in_string = 0;
405 
406 	while ((c = fgetc(f)) != EOF) {
407 		if (!in_string && c == ' ') {
408 			if (node.string == buffer)
409 				continue;
410 			break;
411 		} else if (c == '"') {
412 			in_string = !in_string;
413 		} else if (c == '\n') {
414 			if (node.string == buffer)
415 				return NULL;
416 			ungetc(c, f);
417 			break;
418 		}
419 		if (node.string >= buffer + sizeof(buffer) - 1) {
420 			fprintf(stderr, "Token too long\n");
421 			exit(1);
422 		}
423 		*node.string++ = c;
424 	}
425 	if (node.string == buffer)
426 		return NULL;
427 	*node.string = 0;
428 	node.string = buffer;
429 
430 	if (node.string[1] == '#') {
431 		size_t n;
432 
433 		for (n = 0; n < ARRAY_SIZE(symbol_types); n++) {
434 			if (node.string[0] == symbol_types[n].n) {
435 				node.tag = n;
436 				node.string += 2;
437 				return copy_node(&node);
438 			}
439 		}
440 		fprintf(stderr, "Unknown type %c\n", node.string[0]);
441 		exit(1);
442 	}
443 	return copy_node(&node);
444 }
445 
446 static void read_reference(FILE *f)
447 {
448 	while (!feof(f)) {
449 		struct string_list *defn = NULL;
450 		struct string_list *sym, *def;
451 		int is_extern = 0, is_override = 0;
452 		struct symbol *subsym;
453 
454 		sym = read_node(f);
455 		if (sym && sym->tag == SYM_NORMAL &&
456 		    !strcmp(sym->string, "override")) {
457 			is_override = 1;
458 			free_node(sym);
459 			sym = read_node(f);
460 		}
461 		if (!sym)
462 			continue;
463 		def = read_node(f);
464 		if (def && def->tag == SYM_NORMAL &&
465 		    !strcmp(def->string, "extern")) {
466 			is_extern = 1;
467 			free_node(def);
468 			def = read_node(f);
469 		}
470 		while (def) {
471 			def->next = defn;
472 			defn = def;
473 			def = read_node(f);
474 		}
475 		subsym = add_reference_symbol(sym->string, sym->tag,
476 					      defn, is_extern);
477 		subsym->is_override = is_override;
478 		free_node(sym);
479 	}
480 }
481 
482 static void print_node(FILE * f, struct string_list *list)
483 {
484 	if (symbol_types[list->tag].n) {
485 		putc(symbol_types[list->tag].n, f);
486 		putc('#', f);
487 	}
488 	fputs(list->string, f);
489 }
490 
491 static void print_list(FILE * f, struct string_list *list)
492 {
493 	struct string_list **e, **b;
494 	struct string_list *tmp, **tmp2;
495 	int elem = 1;
496 
497 	if (list == NULL) {
498 		fputs("(nil)", f);
499 		return;
500 	}
501 
502 	tmp = list;
503 	while ((tmp = tmp->next) != NULL)
504 		elem++;
505 
506 	b = alloca(elem * sizeof(*e));
507 	e = b + elem;
508 	tmp2 = e - 1;
509 
510 	(*tmp2--) = list;
511 	while ((list = list->next) != NULL)
512 		*(tmp2--) = list;
513 
514 	while (b != e) {
515 		print_node(f, *b++);
516 		putc(' ', f);
517 	}
518 }
519 
520 static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc)
521 {
522 	struct string_list *list = sym->defn;
523 	struct string_list **e, **b;
524 	struct string_list *tmp, **tmp2;
525 	int elem = 1;
526 
527 	if (!list)
528 		return crc;
529 
530 	tmp = list;
531 	while ((tmp = tmp->next) != NULL)
532 		elem++;
533 
534 	b = alloca(elem * sizeof(*e));
535 	e = b + elem;
536 	tmp2 = e - 1;
537 
538 	*(tmp2--) = list;
539 	while ((list = list->next) != NULL)
540 		*(tmp2--) = list;
541 
542 	while (b != e) {
543 		struct string_list *cur;
544 		struct symbol *subsym;
545 
546 		cur = *(b++);
547 		switch (cur->tag) {
548 		case SYM_NORMAL:
549 			if (flag_dump_defs)
550 				fprintf(debugfile, "%s ", cur->string);
551 			crc = partial_crc32(cur->string, crc);
552 			crc = partial_crc32_one(' ', crc);
553 			break;
554 
555 		case SYM_ENUM_CONST:
556 		case SYM_TYPEDEF:
557 			subsym = find_symbol(cur->string, cur->tag, 0);
558 			/* FIXME: Bad reference files can segfault here. */
559 			if (subsym->expansion_trail) {
560 				if (flag_dump_defs)
561 					fprintf(debugfile, "%s ", cur->string);
562 				crc = partial_crc32(cur->string, crc);
563 				crc = partial_crc32_one(' ', crc);
564 			} else {
565 				subsym->expansion_trail = expansion_trail;
566 				expansion_trail = subsym;
567 				crc = expand_and_crc_sym(subsym, crc);
568 			}
569 			break;
570 
571 		case SYM_STRUCT:
572 		case SYM_UNION:
573 		case SYM_ENUM:
574 			subsym = find_symbol(cur->string, cur->tag, 0);
575 			if (!subsym) {
576 				struct string_list *n;
577 
578 				error_with_pos("expand undefined %s %s",
579 					       symbol_types[cur->tag].name,
580 					       cur->string);
581 				n = concat_list(mk_node
582 						(symbol_types[cur->tag].name),
583 						mk_node(cur->string),
584 						mk_node("{"),
585 						mk_node("UNKNOWN"),
586 						mk_node("}"), NULL);
587 				subsym =
588 				    add_symbol(cur->string, cur->tag, n, 0);
589 			}
590 			if (subsym->expansion_trail) {
591 				if (flag_dump_defs) {
592 					fprintf(debugfile, "%s %s ",
593 						symbol_types[cur->tag].name,
594 						cur->string);
595 				}
596 
597 				crc = partial_crc32(symbol_types[cur->tag].name,
598 						    crc);
599 				crc = partial_crc32_one(' ', crc);
600 				crc = partial_crc32(cur->string, crc);
601 				crc = partial_crc32_one(' ', crc);
602 			} else {
603 				subsym->expansion_trail = expansion_trail;
604 				expansion_trail = subsym;
605 				crc = expand_and_crc_sym(subsym, crc);
606 			}
607 			break;
608 		}
609 	}
610 
611 	{
612 		static struct symbol **end = &visited_symbols;
613 
614 		if (!sym->visited) {
615 			*end = sym;
616 			end = &sym->visited;
617 			sym->visited = (struct symbol *)-1L;
618 		}
619 	}
620 
621 	return crc;
622 }
623 
624 void export_symbol(const char *name)
625 {
626 	struct symbol *sym;
627 	unsigned long crc;
628 	int has_changed = 0;
629 
630 	sym = find_symbol(name, SYM_NORMAL, 0);
631 	if (!sym) {
632 		error_with_pos("export undefined symbol %s", name);
633 		return;
634 	}
635 
636 	if (flag_dump_defs)
637 		fprintf(debugfile, "Export %s == <", name);
638 
639 	expansion_trail = (struct symbol *)-1L;
640 
641 	sym->expansion_trail = expansion_trail;
642 	expansion_trail = sym;
643 	crc = expand_and_crc_sym(sym, 0xffffffff) ^ 0xffffffff;
644 
645 	sym = expansion_trail;
646 	while (sym != (struct symbol *)-1L) {
647 		struct symbol *n = sym->expansion_trail;
648 
649 		if (sym->status != STATUS_UNCHANGED) {
650 			if (!has_changed) {
651 				print_location();
652 				fprintf(stderr,
653 					"%s: %s: modversion changed because of changes in ",
654 					flag_preserve ? "error" : "warning",
655 					name);
656 			} else {
657 				fprintf(stderr, ", ");
658 			}
659 			print_type_name(sym->type, sym->name);
660 			if (sym->status == STATUS_DEFINED)
661 				fprintf(stderr, " (became defined)");
662 			has_changed = 1;
663 			if (flag_preserve)
664 				errors++;
665 		}
666 		sym->expansion_trail = 0;
667 		sym = n;
668 	}
669 	if (has_changed)
670 		fprintf(stderr, "\n");
671 
672 	if (flag_dump_defs)
673 		fputs(">\n", debugfile);
674 
675 	printf("#SYMVER %s 0x%08lx\n", name, crc);
676 }
677 
678 /*----------------------------------------------------------------------*/
679 
680 static void print_location(void)
681 {
682 	fprintf(stderr, "%s:%d: ", cur_filename ? : "<stdin>", cur_line);
683 }
684 
685 static void print_type_name(enum symbol_type type, const char *name)
686 {
687 	if (symbol_types[type].name)
688 		fprintf(stderr, "%s %s", symbol_types[type].name, name);
689 	else
690 		fprintf(stderr, "%s", name);
691 }
692 
693 void error_with_pos(const char *fmt, ...)
694 {
695 	va_list args;
696 
697 	if (flag_warnings) {
698 		print_location();
699 
700 		va_start(args, fmt);
701 		vfprintf(stderr, fmt, args);
702 		va_end(args);
703 		putc('\n', stderr);
704 
705 		errors++;
706 	}
707 }
708 
709 static void genksyms_usage(void)
710 {
711 	fputs("Usage:\n" "genksyms [-adDTwqhVR] > /path/to/.tmp_obj.ver\n" "\n"
712 	      "  -d, --debug           Increment the debug level (repeatable)\n"
713 	      "  -D, --dump            Dump expanded symbol defs (for debugging only)\n"
714 	      "  -r, --reference file  Read reference symbols from a file\n"
715 	      "  -T, --dump-types file Dump expanded types into file\n"
716 	      "  -p, --preserve        Preserve reference modversions or fail\n"
717 	      "  -w, --warnings        Enable warnings\n"
718 	      "  -q, --quiet           Disable warnings (default)\n"
719 	      "  -h, --help            Print this message\n"
720 	      "  -V, --version         Print the release version\n"
721 	      , stderr);
722 }
723 
724 int main(int argc, char **argv)
725 {
726 	FILE *dumpfile = NULL, *ref_file = NULL;
727 	int o;
728 
729 	struct option long_opts[] = {
730 		{"debug", 0, 0, 'd'},
731 		{"warnings", 0, 0, 'w'},
732 		{"quiet", 0, 0, 'q'},
733 		{"dump", 0, 0, 'D'},
734 		{"reference", 1, 0, 'r'},
735 		{"dump-types", 1, 0, 'T'},
736 		{"preserve", 0, 0, 'p'},
737 		{"version", 0, 0, 'V'},
738 		{"help", 0, 0, 'h'},
739 		{0, 0, 0, 0}
740 	};
741 
742 	while ((o = getopt_long(argc, argv, "dwqVDr:T:ph",
743 				&long_opts[0], NULL)) != EOF)
744 		switch (o) {
745 		case 'd':
746 			flag_debug++;
747 			break;
748 		case 'w':
749 			flag_warnings = 1;
750 			break;
751 		case 'q':
752 			flag_warnings = 0;
753 			break;
754 		case 'V':
755 			fputs("genksyms version 2.5.60\n", stderr);
756 			break;
757 		case 'D':
758 			flag_dump_defs = 1;
759 			break;
760 		case 'r':
761 			flag_reference = 1;
762 			ref_file = fopen(optarg, "r");
763 			if (!ref_file) {
764 				perror(optarg);
765 				return 1;
766 			}
767 			break;
768 		case 'T':
769 			flag_dump_types = 1;
770 			dumpfile = fopen(optarg, "w");
771 			if (!dumpfile) {
772 				perror(optarg);
773 				return 1;
774 			}
775 			break;
776 		case 'p':
777 			flag_preserve = 1;
778 			break;
779 		case 'h':
780 			genksyms_usage();
781 			return 0;
782 		default:
783 			genksyms_usage();
784 			return 1;
785 		}
786 	{
787 		extern int yydebug;
788 		extern int yy_flex_debug;
789 
790 		yydebug = (flag_debug > 1);
791 		yy_flex_debug = (flag_debug > 2);
792 
793 		debugfile = stderr;
794 		/* setlinebuf(debugfile); */
795 	}
796 
797 	if (flag_reference) {
798 		read_reference(ref_file);
799 		fclose(ref_file);
800 	}
801 
802 	yyparse();
803 
804 	if (flag_dump_types && visited_symbols) {
805 		while (visited_symbols != (struct symbol *)-1L) {
806 			struct symbol *sym = visited_symbols;
807 
808 			if (sym->is_override)
809 				fputs("override ", dumpfile);
810 			if (symbol_types[sym->type].n) {
811 				putc(symbol_types[sym->type].n, dumpfile);
812 				putc('#', dumpfile);
813 			}
814 			fputs(sym->name, dumpfile);
815 			putc(' ', dumpfile);
816 			if (sym->is_extern)
817 				fputs("extern ", dumpfile);
818 			print_list(dumpfile, sym->defn);
819 			putc('\n', dumpfile);
820 
821 			visited_symbols = sym->visited;
822 			sym->visited = NULL;
823 		}
824 	}
825 
826 	if (flag_debug) {
827 		fprintf(debugfile, "Hash table occupancy %d/%zd = %g\n",
828 			nsyms, HASH_SIZE(symbol_hashtable),
829 			(double)nsyms / HASH_SIZE(symbol_hashtable));
830 	}
831 
832 	if (dumpfile)
833 		fclose(dumpfile);
834 
835 	return errors != 0;
836 }
837