blob: 3a8297b5184cb1e1443c659905fdc41d9346902b [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* Generate kernel symbol version hashes.
2 Copyright 1996, 1997 Linux International.
3
4 New implementation contributed by Richard Henderson <rth@tamu.edu>
5 Based on original work by Bjorn Ekwall <bj0rn@blox.se>
6
7 This file was part of the Linux modutils 2.4.22: moved back into the
8 kernel sources by Rusty Russell/Kai Germaschewski.
9
10 This program is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License as published by the
12 Free Software Foundation; either version 2 of the License, or (at your
13 option) any later version.
14
15 This program is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software Foundation,
22 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
23
24#include <stdio.h>
25#include <string.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <assert.h>
29#include <stdarg.h>
30#ifdef __GNU_LIBRARY__
31#include <getopt.h>
Sam Ravnborg78c041532006-03-12 22:59:36 +010032#endif /* __GNU_LIBRARY__ */
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
34#include "genksyms.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070035/*----------------------------------------------------------------------*/
36
37#define HASH_BUCKETS 4096
38
39static struct symbol *symtab[HASH_BUCKETS];
Sam Ravnborgce560682006-03-12 23:26:29 +010040static FILE *debugfile;
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
42int cur_line = 1;
Sam Ravnborgce560682006-03-12 23:26:29 +010043char *cur_filename;
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -080045static int flag_debug, flag_dump_defs, flag_reference, flag_dump_types,
Sam Ravnborg2ea03892009-01-14 21:38:20 +010046 flag_preserve, flag_warnings;
Sam Ravnborgce560682006-03-12 23:26:29 +010047static const char *arch = "";
48static const char *mod_prefix = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
50static int errors;
51static int nsyms;
52
53static struct symbol *expansion_trail;
Andreas Gruenbacher15fde672006-05-09 20:37:30 +020054static struct symbol *visited_symbols;
Linus Torvalds1da177e2005-04-16 15:20:36 -070055
Sam Ravnborg78c041532006-03-12 22:59:36 +010056static const char *const symbol_type_name[] = {
57 "normal", "typedef", "enum", "struct", "union"
Linus Torvalds1da177e2005-04-16 15:20:36 -070058};
59
Sam Ravnborgce560682006-03-12 23:26:29 +010060static int equal_list(struct string_list *a, struct string_list *b);
61static void print_list(FILE * f, struct string_list *list);
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -080062static void print_location(void);
63static void print_type_name(enum symbol_type type, const char *name);
Sam Ravnborgce560682006-03-12 23:26:29 +010064
Linus Torvalds1da177e2005-04-16 15:20:36 -070065/*----------------------------------------------------------------------*/
66
Sam Ravnborg78c041532006-03-12 22:59:36 +010067static const unsigned int crctab32[] = {
68 0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U,
69 0x706af48fU, 0xe963a535U, 0x9e6495a3U, 0x0edb8832U, 0x79dcb8a4U,
70 0xe0d5e91eU, 0x97d2d988U, 0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U,
71 0x90bf1d91U, 0x1db71064U, 0x6ab020f2U, 0xf3b97148U, 0x84be41deU,
72 0x1adad47dU, 0x6ddde4ebU, 0xf4d4b551U, 0x83d385c7U, 0x136c9856U,
73 0x646ba8c0U, 0xfd62f97aU, 0x8a65c9ecU, 0x14015c4fU, 0x63066cd9U,
74 0xfa0f3d63U, 0x8d080df5U, 0x3b6e20c8U, 0x4c69105eU, 0xd56041e4U,
75 0xa2677172U, 0x3c03e4d1U, 0x4b04d447U, 0xd20d85fdU, 0xa50ab56bU,
76 0x35b5a8faU, 0x42b2986cU, 0xdbbbc9d6U, 0xacbcf940U, 0x32d86ce3U,
77 0x45df5c75U, 0xdcd60dcfU, 0xabd13d59U, 0x26d930acU, 0x51de003aU,
78 0xc8d75180U, 0xbfd06116U, 0x21b4f4b5U, 0x56b3c423U, 0xcfba9599U,
79 0xb8bda50fU, 0x2802b89eU, 0x5f058808U, 0xc60cd9b2U, 0xb10be924U,
80 0x2f6f7c87U, 0x58684c11U, 0xc1611dabU, 0xb6662d3dU, 0x76dc4190U,
81 0x01db7106U, 0x98d220bcU, 0xefd5102aU, 0x71b18589U, 0x06b6b51fU,
82 0x9fbfe4a5U, 0xe8b8d433U, 0x7807c9a2U, 0x0f00f934U, 0x9609a88eU,
83 0xe10e9818U, 0x7f6a0dbbU, 0x086d3d2dU, 0x91646c97U, 0xe6635c01U,
84 0x6b6b51f4U, 0x1c6c6162U, 0x856530d8U, 0xf262004eU, 0x6c0695edU,
85 0x1b01a57bU, 0x8208f4c1U, 0xf50fc457U, 0x65b0d9c6U, 0x12b7e950U,
86 0x8bbeb8eaU, 0xfcb9887cU, 0x62dd1ddfU, 0x15da2d49U, 0x8cd37cf3U,
87 0xfbd44c65U, 0x4db26158U, 0x3ab551ceU, 0xa3bc0074U, 0xd4bb30e2U,
88 0x4adfa541U, 0x3dd895d7U, 0xa4d1c46dU, 0xd3d6f4fbU, 0x4369e96aU,
89 0x346ed9fcU, 0xad678846U, 0xda60b8d0U, 0x44042d73U, 0x33031de5U,
90 0xaa0a4c5fU, 0xdd0d7cc9U, 0x5005713cU, 0x270241aaU, 0xbe0b1010U,
91 0xc90c2086U, 0x5768b525U, 0x206f85b3U, 0xb966d409U, 0xce61e49fU,
92 0x5edef90eU, 0x29d9c998U, 0xb0d09822U, 0xc7d7a8b4U, 0x59b33d17U,
93 0x2eb40d81U, 0xb7bd5c3bU, 0xc0ba6cadU, 0xedb88320U, 0x9abfb3b6U,
94 0x03b6e20cU, 0x74b1d29aU, 0xead54739U, 0x9dd277afU, 0x04db2615U,
95 0x73dc1683U, 0xe3630b12U, 0x94643b84U, 0x0d6d6a3eU, 0x7a6a5aa8U,
96 0xe40ecf0bU, 0x9309ff9dU, 0x0a00ae27U, 0x7d079eb1U, 0xf00f9344U,
97 0x8708a3d2U, 0x1e01f268U, 0x6906c2feU, 0xf762575dU, 0x806567cbU,
98 0x196c3671U, 0x6e6b06e7U, 0xfed41b76U, 0x89d32be0U, 0x10da7a5aU,
99 0x67dd4accU, 0xf9b9df6fU, 0x8ebeeff9U, 0x17b7be43U, 0x60b08ed5U,
100 0xd6d6a3e8U, 0xa1d1937eU, 0x38d8c2c4U, 0x4fdff252U, 0xd1bb67f1U,
101 0xa6bc5767U, 0x3fb506ddU, 0x48b2364bU, 0xd80d2bdaU, 0xaf0a1b4cU,
102 0x36034af6U, 0x41047a60U, 0xdf60efc3U, 0xa867df55U, 0x316e8eefU,
103 0x4669be79U, 0xcb61b38cU, 0xbc66831aU, 0x256fd2a0U, 0x5268e236U,
104 0xcc0c7795U, 0xbb0b4703U, 0x220216b9U, 0x5505262fU, 0xc5ba3bbeU,
105 0xb2bd0b28U, 0x2bb45a92U, 0x5cb36a04U, 0xc2d7ffa7U, 0xb5d0cf31U,
106 0x2cd99e8bU, 0x5bdeae1dU, 0x9b64c2b0U, 0xec63f226U, 0x756aa39cU,
107 0x026d930aU, 0x9c0906a9U, 0xeb0e363fU, 0x72076785U, 0x05005713U,
108 0x95bf4a82U, 0xe2b87a14U, 0x7bb12baeU, 0x0cb61b38U, 0x92d28e9bU,
109 0xe5d5be0dU, 0x7cdcefb7U, 0x0bdbdf21U, 0x86d3d2d4U, 0xf1d4e242U,
110 0x68ddb3f8U, 0x1fda836eU, 0x81be16cdU, 0xf6b9265bU, 0x6fb077e1U,
111 0x18b74777U, 0x88085ae6U, 0xff0f6a70U, 0x66063bcaU, 0x11010b5cU,
112 0x8f659effU, 0xf862ae69U, 0x616bffd3U, 0x166ccf45U, 0xa00ae278U,
113 0xd70dd2eeU, 0x4e048354U, 0x3903b3c2U, 0xa7672661U, 0xd06016f7U,
114 0x4969474dU, 0x3e6e77dbU, 0xaed16a4aU, 0xd9d65adcU, 0x40df0b66U,
115 0x37d83bf0U, 0xa9bcae53U, 0xdebb9ec5U, 0x47b2cf7fU, 0x30b5ffe9U,
116 0xbdbdf21cU, 0xcabac28aU, 0x53b39330U, 0x24b4a3a6U, 0xbad03605U,
117 0xcdd70693U, 0x54de5729U, 0x23d967bfU, 0xb3667a2eU, 0xc4614ab8U,
118 0x5d681b02U, 0x2a6f2b94U, 0xb40bbe37U, 0xc30c8ea1U, 0x5a05df1bU,
119 0x2d02ef8dU
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120};
121
Sam Ravnborgce560682006-03-12 23:26:29 +0100122static unsigned long partial_crc32_one(unsigned char c, unsigned long crc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100124 return crctab32[(crc ^ c) & 0xff] ^ (crc >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125}
126
Sam Ravnborgce560682006-03-12 23:26:29 +0100127static unsigned long partial_crc32(const char *s, unsigned long crc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100129 while (*s)
130 crc = partial_crc32_one(*s++, crc);
131 return crc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132}
133
Sam Ravnborgce560682006-03-12 23:26:29 +0100134static unsigned long crc32(const char *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100136 return partial_crc32(s, 0xffffffff) ^ 0xffffffff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137}
138
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139/*----------------------------------------------------------------------*/
140
Sam Ravnborgce560682006-03-12 23:26:29 +0100141static enum symbol_type map_to_ns(enum symbol_type t)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100143 if (t == SYM_TYPEDEF)
144 t = SYM_NORMAL;
145 else if (t == SYM_UNION)
146 t = SYM_STRUCT;
147 return t;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148}
149
Sam Ravnborg78c041532006-03-12 22:59:36 +0100150struct symbol *find_symbol(const char *name, enum symbol_type ns)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100152 unsigned long h = crc32(name) % HASH_BUCKETS;
153 struct symbol *sym;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154
Sam Ravnborg78c041532006-03-12 22:59:36 +0100155 for (sym = symtab[h]; sym; sym = sym->hash_next)
Sam Ravnborgce560682006-03-12 23:26:29 +0100156 if (map_to_ns(sym->type) == map_to_ns(ns) &&
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800157 strcmp(name, sym->name) == 0 &&
158 sym->is_declared)
Sam Ravnborg78c041532006-03-12 22:59:36 +0100159 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 return sym;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162}
163
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800164static int is_unknown_symbol(struct symbol *sym)
165{
166 struct string_list *defn;
167
168 return ((sym->type == SYM_STRUCT ||
169 sym->type == SYM_UNION ||
170 sym->type == SYM_ENUM) &&
171 (defn = sym->defn) && defn->tag == SYM_NORMAL &&
172 strcmp(defn->string, "}") == 0 &&
173 (defn = defn->next) && defn->tag == SYM_NORMAL &&
174 strcmp(defn->string, "UNKNOWN") == 0 &&
175 (defn = defn->next) && defn->tag == SYM_NORMAL &&
176 strcmp(defn->string, "{") == 0);
177}
178
179struct symbol *__add_symbol(const char *name, enum symbol_type type,
180 struct string_list *defn, int is_extern,
181 int is_reference)
Sam Ravnborg78c041532006-03-12 22:59:36 +0100182{
183 unsigned long h = crc32(name) % HASH_BUCKETS;
184 struct symbol *sym;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800185 enum symbol_status status = STATUS_UNCHANGED;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100186
Sam Ravnborgce560682006-03-12 23:26:29 +0100187 for (sym = symtab[h]; sym; sym = sym->hash_next) {
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800188 if (map_to_ns(sym->type) == map_to_ns(type) &&
189 strcmp(name, sym->name) == 0) {
190 if (is_reference)
191 /* fall through */ ;
192 else if (sym->type == type &&
193 equal_list(sym->defn, defn)) {
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800194 if (!sym->is_declared && sym->is_override) {
195 print_location();
196 print_type_name(type, name);
197 fprintf(stderr, " modversion is "
198 "unchanged\n");
199 }
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800200 sym->is_declared = 1;
201 return sym;
202 } else if (!sym->is_declared) {
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800203 if (sym->is_override && flag_preserve) {
204 print_location();
205 fprintf(stderr, "ignoring ");
206 print_type_name(type, name);
207 fprintf(stderr, " modversion change\n");
208 sym->is_declared = 1;
209 return sym;
210 } else {
211 status = is_unknown_symbol(sym) ?
212 STATUS_DEFINED : STATUS_MODIFIED;
213 }
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800214 } else {
Sam Ravnborg78c041532006-03-12 22:59:36 +0100215 error_with_pos("redefinition of %s", name);
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800216 return sym;
217 }
218 break;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100219 }
Sam Ravnborgce560682006-03-12 23:26:29 +0100220 }
Sam Ravnborg78c041532006-03-12 22:59:36 +0100221
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800222 if (sym) {
223 struct symbol **psym;
224
225 for (psym = &symtab[h]; *psym; psym = &(*psym)->hash_next) {
226 if (*psym == sym) {
227 *psym = sym->hash_next;
228 break;
229 }
230 }
231 --nsyms;
232 }
233
Sam Ravnborg78c041532006-03-12 22:59:36 +0100234 sym = xmalloc(sizeof(*sym));
235 sym->name = name;
236 sym->type = type;
237 sym->defn = defn;
238 sym->expansion_trail = NULL;
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200239 sym->visited = NULL;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100240 sym->is_extern = is_extern;
241
242 sym->hash_next = symtab[h];
243 symtab[h] = sym;
244
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800245 sym->is_declared = !is_reference;
246 sym->status = status;
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800247 sym->is_override = 0;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800248
Sam Ravnborg78c041532006-03-12 22:59:36 +0100249 if (flag_debug) {
250 fprintf(debugfile, "Defn for %s %s == <",
251 symbol_type_name[type], name);
252 if (is_extern)
253 fputs("extern ", debugfile);
254 print_list(debugfile, defn);
255 fputs(">\n", debugfile);
256 }
257
258 ++nsyms;
259 return sym;
260}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800262struct symbol *add_symbol(const char *name, enum symbol_type type,
263 struct string_list *defn, int is_extern)
264{
265 return __add_symbol(name, type, defn, is_extern, 0);
266}
267
268struct symbol *add_reference_symbol(const char *name, enum symbol_type type,
269 struct string_list *defn, int is_extern)
270{
271 return __add_symbol(name, type, defn, is_extern, 1);
272}
273
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274/*----------------------------------------------------------------------*/
275
Sam Ravnborgce560682006-03-12 23:26:29 +0100276void free_node(struct string_list *node)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100278 free(node->string);
279 free(node);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280}
281
Sam Ravnborg78c041532006-03-12 22:59:36 +0100282void free_list(struct string_list *s, struct string_list *e)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100284 while (s != e) {
285 struct string_list *next = s->next;
286 free_node(s);
287 s = next;
288 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289}
290
Sam Ravnborgce560682006-03-12 23:26:29 +0100291struct string_list *copy_node(struct string_list *node)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100293 struct string_list *newnode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294
Sam Ravnborg78c041532006-03-12 22:59:36 +0100295 newnode = xmalloc(sizeof(*newnode));
296 newnode->string = xstrdup(node->string);
297 newnode->tag = node->tag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
Sam Ravnborg78c041532006-03-12 22:59:36 +0100299 return newnode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300}
301
Sam Ravnborgce560682006-03-12 23:26:29 +0100302static int equal_list(struct string_list *a, struct string_list *b)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100304 while (a && b) {
305 if (a->tag != b->tag || strcmp(a->string, b->string))
306 return 0;
307 a = a->next;
308 b = b->next;
309 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
Sam Ravnborg78c041532006-03-12 22:59:36 +0100311 return !a && !b;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312}
313
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800314#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
315
316struct string_list *read_node(FILE *f)
317{
318 char buffer[256];
319 struct string_list node = {
320 .string = buffer,
321 .tag = SYM_NORMAL };
322 int c;
323
324 while ((c = fgetc(f)) != EOF) {
325 if (c == ' ') {
326 if (node.string == buffer)
327 continue;
328 break;
329 } else if (c == '\n') {
330 if (node.string == buffer)
331 return NULL;
332 ungetc(c, f);
333 break;
334 }
335 if (node.string >= buffer + sizeof(buffer) - 1) {
336 fprintf(stderr, "Token too long\n");
337 exit(1);
338 }
339 *node.string++ = c;
340 }
341 if (node.string == buffer)
342 return NULL;
343 *node.string = 0;
344 node.string = buffer;
345
346 if (node.string[1] == '#') {
347 int n;
348
349 for (n = 0; n < ARRAY_SIZE(symbol_type_name); n++) {
350 if (node.string[0] == symbol_type_name[n][0]) {
351 node.tag = n;
352 node.string += 2;
353 return copy_node(&node);
354 }
355 }
356 fprintf(stderr, "Unknown type %c\n", node.string[0]);
357 exit(1);
358 }
359 return copy_node(&node);
360}
361
362static void read_reference(FILE *f)
363{
364 while (!feof(f)) {
365 struct string_list *defn = NULL;
366 struct string_list *sym, *def;
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800367 int is_extern = 0, is_override = 0;
368 struct symbol *subsym;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800369
370 sym = read_node(f);
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800371 if (sym && sym->tag == SYM_NORMAL &&
372 !strcmp(sym->string, "override")) {
373 is_override = 1;
374 free_node(sym);
375 sym = read_node(f);
376 }
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800377 if (!sym)
378 continue;
379 def = read_node(f);
380 if (def && def->tag == SYM_NORMAL &&
381 !strcmp(def->string, "extern")) {
382 is_extern = 1;
383 free_node(def);
384 def = read_node(f);
385 }
386 while (def) {
387 def->next = defn;
388 defn = def;
389 def = read_node(f);
390 }
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800391 subsym = add_reference_symbol(xstrdup(sym->string), sym->tag,
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800392 defn, is_extern);
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800393 subsym->is_override = is_override;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800394 free_node(sym);
395 }
396}
397
Sam Ravnborgce560682006-03-12 23:26:29 +0100398static void print_node(FILE * f, struct string_list *list)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399{
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200400 if (list->tag != SYM_NORMAL) {
401 putc(symbol_type_name[list->tag][0], f);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100402 putc('#', f);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100403 }
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200404 fputs(list->string, f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405}
406
Sam Ravnborgce560682006-03-12 23:26:29 +0100407static void print_list(FILE * f, struct string_list *list)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100409 struct string_list **e, **b;
410 struct string_list *tmp, **tmp2;
411 int elem = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412
Sam Ravnborg78c041532006-03-12 22:59:36 +0100413 if (list == NULL) {
414 fputs("(nil)", f);
415 return;
416 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
Sam Ravnborg78c041532006-03-12 22:59:36 +0100418 tmp = list;
419 while ((tmp = tmp->next) != NULL)
420 elem++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421
Sam Ravnborg78c041532006-03-12 22:59:36 +0100422 b = alloca(elem * sizeof(*e));
423 e = b + elem;
424 tmp2 = e - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425
Sam Ravnborg78c041532006-03-12 22:59:36 +0100426 (*tmp2--) = list;
427 while ((list = list->next) != NULL)
428 *(tmp2--) = list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429
Sam Ravnborg78c041532006-03-12 22:59:36 +0100430 while (b != e) {
431 print_node(f, *b++);
432 putc(' ', f);
433 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434}
435
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200436static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437{
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200438 struct string_list *list = sym->defn;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100439 struct string_list **e, **b;
440 struct string_list *tmp, **tmp2;
441 int elem = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442
Sam Ravnborg78c041532006-03-12 22:59:36 +0100443 if (!list)
444 return crc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445
Sam Ravnborg78c041532006-03-12 22:59:36 +0100446 tmp = list;
447 while ((tmp = tmp->next) != NULL)
448 elem++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449
Sam Ravnborg78c041532006-03-12 22:59:36 +0100450 b = alloca(elem * sizeof(*e));
451 e = b + elem;
452 tmp2 = e - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453
Sam Ravnborg78c041532006-03-12 22:59:36 +0100454 *(tmp2--) = list;
455 while ((list = list->next) != NULL)
456 *(tmp2--) = list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457
Sam Ravnborg78c041532006-03-12 22:59:36 +0100458 while (b != e) {
459 struct string_list *cur;
460 struct symbol *subsym;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461
Sam Ravnborg78c041532006-03-12 22:59:36 +0100462 cur = *(b++);
463 switch (cur->tag) {
464 case SYM_NORMAL:
465 if (flag_dump_defs)
466 fprintf(debugfile, "%s ", cur->string);
467 crc = partial_crc32(cur->string, crc);
468 crc = partial_crc32_one(' ', crc);
469 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
Sam Ravnborg78c041532006-03-12 22:59:36 +0100471 case SYM_TYPEDEF:
472 subsym = find_symbol(cur->string, cur->tag);
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800473 /* FIXME: Bad reference files can segfault here. */
Sam Ravnborg78c041532006-03-12 22:59:36 +0100474 if (subsym->expansion_trail) {
475 if (flag_dump_defs)
476 fprintf(debugfile, "%s ", cur->string);
477 crc = partial_crc32(cur->string, crc);
478 crc = partial_crc32_one(' ', crc);
479 } else {
480 subsym->expansion_trail = expansion_trail;
481 expansion_trail = subsym;
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200482 crc = expand_and_crc_sym(subsym, crc);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100483 }
484 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
Sam Ravnborg78c041532006-03-12 22:59:36 +0100486 case SYM_STRUCT:
487 case SYM_UNION:
488 case SYM_ENUM:
489 subsym = find_symbol(cur->string, cur->tag);
490 if (!subsym) {
491 struct string_list *n, *t = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492
Sam Ravnborg78c041532006-03-12 22:59:36 +0100493 error_with_pos("expand undefined %s %s",
494 symbol_type_name[cur->tag],
495 cur->string);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496
Sam Ravnborg78c041532006-03-12 22:59:36 +0100497 n = xmalloc(sizeof(*n));
498 n->string = xstrdup(symbol_type_name[cur->tag]);
499 n->tag = SYM_NORMAL;
500 n->next = t;
501 t = n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
Sam Ravnborg78c041532006-03-12 22:59:36 +0100503 n = xmalloc(sizeof(*n));
504 n->string = xstrdup(cur->string);
505 n->tag = SYM_NORMAL;
506 n->next = t;
507 t = n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
Sam Ravnborg78c041532006-03-12 22:59:36 +0100509 n = xmalloc(sizeof(*n));
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800510 n->string = xstrdup("{");
Sam Ravnborg78c041532006-03-12 22:59:36 +0100511 n->tag = SYM_NORMAL;
512 n->next = t;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800513 t = n;
514
515 n = xmalloc(sizeof(*n));
516 n->string = xstrdup("UNKNOWN");
517 n->tag = SYM_NORMAL;
518 n->next = t;
519 t = n;
520
521 n = xmalloc(sizeof(*n));
522 n->string = xstrdup("}");
523 n->tag = SYM_NORMAL;
524 n->next = t;
525 t = n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526
Sam Ravnborg78c041532006-03-12 22:59:36 +0100527 subsym =
528 add_symbol(cur->string, cur->tag, n, 0);
529 }
530 if (subsym->expansion_trail) {
531 if (flag_dump_defs) {
532 fprintf(debugfile, "%s %s ",
533 symbol_type_name[cur->tag],
534 cur->string);
535 }
536
Sam Ravnborgce560682006-03-12 23:26:29 +0100537 crc = partial_crc32(symbol_type_name[cur->tag],
538 crc);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100539 crc = partial_crc32_one(' ', crc);
540 crc = partial_crc32(cur->string, crc);
541 crc = partial_crc32_one(' ', crc);
542 } else {
543 subsym->expansion_trail = expansion_trail;
544 expansion_trail = subsym;
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200545 crc = expand_and_crc_sym(subsym, crc);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100546 }
547 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200551 {
552 static struct symbol **end = &visited_symbols;
553
554 if (!sym->visited) {
555 *end = sym;
556 end = &sym->visited;
557 sym->visited = (struct symbol *)-1L;
558 }
559 }
560
Sam Ravnborg78c041532006-03-12 22:59:36 +0100561 return crc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562}
563
Sam Ravnborg78c041532006-03-12 22:59:36 +0100564void export_symbol(const char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100566 struct symbol *sym;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
Sam Ravnborg78c041532006-03-12 22:59:36 +0100568 sym = find_symbol(name, SYM_NORMAL);
569 if (!sym)
570 error_with_pos("export undefined symbol %s", name);
571 else {
572 unsigned long crc;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800573 int has_changed = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574
Sam Ravnborg78c041532006-03-12 22:59:36 +0100575 if (flag_dump_defs)
576 fprintf(debugfile, "Export %s == <", name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
Sam Ravnborg78c041532006-03-12 22:59:36 +0100578 expansion_trail = (struct symbol *)-1L;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800580 sym->expansion_trail = expansion_trail;
581 expansion_trail = sym;
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200582 crc = expand_and_crc_sym(sym, 0xffffffff) ^ 0xffffffff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583
Sam Ravnborg78c041532006-03-12 22:59:36 +0100584 sym = expansion_trail;
585 while (sym != (struct symbol *)-1L) {
586 struct symbol *n = sym->expansion_trail;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800587
588 if (sym->status != STATUS_UNCHANGED) {
589 if (!has_changed) {
590 print_location();
591 fprintf(stderr, "%s: %s: modversion "
592 "changed because of changes "
593 "in ", flag_preserve ? "error" :
594 "warning", name);
595 } else
596 fprintf(stderr, ", ");
597 print_type_name(sym->type, sym->name);
598 if (sym->status == STATUS_DEFINED)
599 fprintf(stderr, " (became defined)");
600 has_changed = 1;
601 if (flag_preserve)
602 errors++;
603 }
Sam Ravnborg78c041532006-03-12 22:59:36 +0100604 sym->expansion_trail = 0;
605 sym = n;
606 }
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800607 if (has_changed)
608 fprintf(stderr, "\n");
Sam Ravnborg78c041532006-03-12 22:59:36 +0100609
610 if (flag_dump_defs)
611 fputs(">\n", debugfile);
612
Sam Ravnborg2ea03892009-01-14 21:38:20 +0100613 /* Used as a linker script. */
614 printf("%s__crc_%s = 0x%08lx ;\n", mod_prefix, name, crc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616}
617
618/*----------------------------------------------------------------------*/
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800619
620static void print_location(void)
621{
622 fprintf(stderr, "%s:%d: ", cur_filename ? : "<stdin>", cur_line);
623}
624
625static void print_type_name(enum symbol_type type, const char *name)
626{
627 if (type != SYM_NORMAL)
628 fprintf(stderr, "%s %s", symbol_type_name[type], name);
629 else
630 fprintf(stderr, "%s", name);
631}
632
Sam Ravnborg78c041532006-03-12 22:59:36 +0100633void error_with_pos(const char *fmt, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100635 va_list args;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636
Sam Ravnborg78c041532006-03-12 22:59:36 +0100637 if (flag_warnings) {
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800638 print_location();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
Sam Ravnborg78c041532006-03-12 22:59:36 +0100640 va_start(args, fmt);
641 vfprintf(stderr, fmt, args);
642 va_end(args);
643 putc('\n', stderr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
Sam Ravnborg78c041532006-03-12 22:59:36 +0100645 errors++;
646 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647}
648
Sam Ravnborgce560682006-03-12 23:26:29 +0100649static void genksyms_usage(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650{
Sam Ravnborg2ea03892009-01-14 21:38:20 +0100651 fputs("Usage:\n" "genksyms [-adDTwqhV] > /path/to/.tmp_obj.ver\n" "\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652#ifdef __GNU_LIBRARY__
Mike Frysinger36091fd2007-11-10 09:32:20 -0500653 " -a, --arch Select architecture\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 " -d, --debug Increment the debug level (repeatable)\n"
655 " -D, --dump Dump expanded symbol defs (for debugging only)\n"
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800656 " -r, --reference file Read reference symbols from a file\n"
657 " -T, --dump-types file Dump expanded types into file\n"
658 " -p, --preserve Preserve reference modversions or fail\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 " -w, --warnings Enable warnings\n"
660 " -q, --quiet Disable warnings (default)\n"
661 " -h, --help Print this message\n"
662 " -V, --version Print the release version\n"
Sam Ravnborg78c041532006-03-12 22:59:36 +0100663#else /* __GNU_LIBRARY__ */
Mike Frysinger36091fd2007-11-10 09:32:20 -0500664 " -a Select architecture\n"
Sam Ravnborg78c041532006-03-12 22:59:36 +0100665 " -d Increment the debug level (repeatable)\n"
666 " -D Dump expanded symbol defs (for debugging only)\n"
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800667 " -r file Read reference symbols from a file\n"
668 " -T file Dump expanded types into file\n"
669 " -p Preserve reference modversions or fail\n"
Sam Ravnborg78c041532006-03-12 22:59:36 +0100670 " -w Enable warnings\n"
671 " -q Disable warnings (default)\n"
672 " -h Print this message\n"
673 " -V Print the release version\n"
674#endif /* __GNU_LIBRARY__ */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 , stderr);
676}
677
Sam Ravnborg78c041532006-03-12 22:59:36 +0100678int main(int argc, char **argv)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679{
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800680 FILE *dumpfile = NULL, *ref_file = NULL;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100681 int o;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682
683#ifdef __GNU_LIBRARY__
Sam Ravnborg78c041532006-03-12 22:59:36 +0100684 struct option long_opts[] = {
685 {"arch", 1, 0, 'a'},
686 {"debug", 0, 0, 'd'},
687 {"warnings", 0, 0, 'w'},
688 {"quiet", 0, 0, 'q'},
689 {"dump", 0, 0, 'D'},
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800690 {"reference", 1, 0, 'r'},
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200691 {"dump-types", 1, 0, 'T'},
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800692 {"preserve", 0, 0, 'p'},
Sam Ravnborg78c041532006-03-12 22:59:36 +0100693 {"version", 0, 0, 'V'},
694 {"help", 0, 0, 'h'},
695 {0, 0, 0, 0}
696 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
Sam Ravnborg2ea03892009-01-14 21:38:20 +0100698 while ((o = getopt_long(argc, argv, "a:dwqVDr:T:ph",
Sam Ravnborg78c041532006-03-12 22:59:36 +0100699 &long_opts[0], NULL)) != EOF)
700#else /* __GNU_LIBRARY__ */
Sam Ravnborg2ea03892009-01-14 21:38:20 +0100701 while ((o = getopt(argc, argv, "a:dwqVDr:T:ph")) != EOF)
Sam Ravnborg78c041532006-03-12 22:59:36 +0100702#endif /* __GNU_LIBRARY__ */
703 switch (o) {
704 case 'a':
705 arch = optarg;
706 break;
707 case 'd':
708 flag_debug++;
709 break;
710 case 'w':
711 flag_warnings = 1;
712 break;
713 case 'q':
714 flag_warnings = 0;
715 break;
716 case 'V':
717 fputs("genksyms version 2.5.60\n", stderr);
718 break;
719 case 'D':
720 flag_dump_defs = 1;
721 break;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800722 case 'r':
723 flag_reference = 1;
724 ref_file = fopen(optarg, "r");
725 if (!ref_file) {
726 perror(optarg);
727 return 1;
728 }
729 break;
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200730 case 'T':
731 flag_dump_types = 1;
732 dumpfile = fopen(optarg, "w");
733 if (!dumpfile) {
734 perror(optarg);
735 return 1;
736 }
737 break;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800738 case 'p':
739 flag_preserve = 1;
740 break;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100741 case 'h':
742 genksyms_usage();
743 return 0;
744 default:
745 genksyms_usage();
746 return 1;
747 }
Adrian Bunkf606ddf2008-07-23 21:28:50 -0700748 if ((strcmp(arch, "h8300") == 0) || (strcmp(arch, "blackfin") == 0))
Sam Ravnborg78c041532006-03-12 22:59:36 +0100749 mod_prefix = "_";
750 {
751 extern int yydebug;
752 extern int yy_flex_debug;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753
Sam Ravnborg78c041532006-03-12 22:59:36 +0100754 yydebug = (flag_debug > 1);
755 yy_flex_debug = (flag_debug > 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
Sam Ravnborg78c041532006-03-12 22:59:36 +0100757 debugfile = stderr;
758 /* setlinebuf(debugfile); */
759 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800761 if (flag_reference)
762 read_reference(ref_file);
763
Sam Ravnborg78c041532006-03-12 22:59:36 +0100764 yyparse();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200766 if (flag_dump_types && visited_symbols) {
767 while (visited_symbols != (struct symbol *)-1L) {
768 struct symbol *sym = visited_symbols;
769
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800770 if (sym->is_override)
771 fputs("override ", dumpfile);
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200772 if (sym->type != SYM_NORMAL) {
773 putc(symbol_type_name[sym->type][0], dumpfile);
774 putc('#', dumpfile);
775 }
776 fputs(sym->name, dumpfile);
777 putc(' ', dumpfile);
Andreas Gruenbacher3b40d382008-07-21 04:28:25 +0200778 if (sym->is_extern)
779 fputs("extern ", dumpfile);
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200780 print_list(dumpfile, sym->defn);
781 putc('\n', dumpfile);
782
783 visited_symbols = sym->visited;
784 sym->visited = NULL;
785 }
786 }
787
Sam Ravnborg78c041532006-03-12 22:59:36 +0100788 if (flag_debug) {
789 fprintf(debugfile, "Hash table occupancy %d/%d = %g\n",
790 nsyms, HASH_BUCKETS,
791 (double)nsyms / (double)HASH_BUCKETS);
792 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
Sam Ravnborg78c041532006-03-12 22:59:36 +0100794 return errors != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795}