blob: 4a350816a9e8eb737e2c7170dc7f3c20ca7bb6af [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
Michal Marek7ec8eda2011-01-18 16:28:06 +010056static const struct {
57 int n;
58 const char *name;
59} symbol_types[] = {
60 [SYM_NORMAL] = { 0, NULL},
61 [SYM_TYPEDEF] = {'t', "typedef"},
62 [SYM_ENUM] = {'e', "enum"},
63 [SYM_STRUCT] = {'s', "struct"},
64 [SYM_UNION] = {'u', "union"},
Linus Torvalds1da177e2005-04-16 15:20:36 -070065};
66
Sam Ravnborgce560682006-03-12 23:26:29 +010067static int equal_list(struct string_list *a, struct string_list *b);
68static void print_list(FILE * f, struct string_list *list);
Michal Marek68eb8562011-02-02 23:52:13 +010069static struct string_list *concat_list(struct string_list *start, ...);
70static struct string_list *mk_node(const char *string);
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -080071static void print_location(void);
72static void print_type_name(enum symbol_type type, const char *name);
Sam Ravnborgce560682006-03-12 23:26:29 +010073
Linus Torvalds1da177e2005-04-16 15:20:36 -070074/*----------------------------------------------------------------------*/
75
Sam Ravnborg78c041532006-03-12 22:59:36 +010076static const unsigned int crctab32[] = {
77 0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U,
78 0x706af48fU, 0xe963a535U, 0x9e6495a3U, 0x0edb8832U, 0x79dcb8a4U,
79 0xe0d5e91eU, 0x97d2d988U, 0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U,
80 0x90bf1d91U, 0x1db71064U, 0x6ab020f2U, 0xf3b97148U, 0x84be41deU,
81 0x1adad47dU, 0x6ddde4ebU, 0xf4d4b551U, 0x83d385c7U, 0x136c9856U,
82 0x646ba8c0U, 0xfd62f97aU, 0x8a65c9ecU, 0x14015c4fU, 0x63066cd9U,
83 0xfa0f3d63U, 0x8d080df5U, 0x3b6e20c8U, 0x4c69105eU, 0xd56041e4U,
84 0xa2677172U, 0x3c03e4d1U, 0x4b04d447U, 0xd20d85fdU, 0xa50ab56bU,
85 0x35b5a8faU, 0x42b2986cU, 0xdbbbc9d6U, 0xacbcf940U, 0x32d86ce3U,
86 0x45df5c75U, 0xdcd60dcfU, 0xabd13d59U, 0x26d930acU, 0x51de003aU,
87 0xc8d75180U, 0xbfd06116U, 0x21b4f4b5U, 0x56b3c423U, 0xcfba9599U,
88 0xb8bda50fU, 0x2802b89eU, 0x5f058808U, 0xc60cd9b2U, 0xb10be924U,
89 0x2f6f7c87U, 0x58684c11U, 0xc1611dabU, 0xb6662d3dU, 0x76dc4190U,
90 0x01db7106U, 0x98d220bcU, 0xefd5102aU, 0x71b18589U, 0x06b6b51fU,
91 0x9fbfe4a5U, 0xe8b8d433U, 0x7807c9a2U, 0x0f00f934U, 0x9609a88eU,
92 0xe10e9818U, 0x7f6a0dbbU, 0x086d3d2dU, 0x91646c97U, 0xe6635c01U,
93 0x6b6b51f4U, 0x1c6c6162U, 0x856530d8U, 0xf262004eU, 0x6c0695edU,
94 0x1b01a57bU, 0x8208f4c1U, 0xf50fc457U, 0x65b0d9c6U, 0x12b7e950U,
95 0x8bbeb8eaU, 0xfcb9887cU, 0x62dd1ddfU, 0x15da2d49U, 0x8cd37cf3U,
96 0xfbd44c65U, 0x4db26158U, 0x3ab551ceU, 0xa3bc0074U, 0xd4bb30e2U,
97 0x4adfa541U, 0x3dd895d7U, 0xa4d1c46dU, 0xd3d6f4fbU, 0x4369e96aU,
98 0x346ed9fcU, 0xad678846U, 0xda60b8d0U, 0x44042d73U, 0x33031de5U,
99 0xaa0a4c5fU, 0xdd0d7cc9U, 0x5005713cU, 0x270241aaU, 0xbe0b1010U,
100 0xc90c2086U, 0x5768b525U, 0x206f85b3U, 0xb966d409U, 0xce61e49fU,
101 0x5edef90eU, 0x29d9c998U, 0xb0d09822U, 0xc7d7a8b4U, 0x59b33d17U,
102 0x2eb40d81U, 0xb7bd5c3bU, 0xc0ba6cadU, 0xedb88320U, 0x9abfb3b6U,
103 0x03b6e20cU, 0x74b1d29aU, 0xead54739U, 0x9dd277afU, 0x04db2615U,
104 0x73dc1683U, 0xe3630b12U, 0x94643b84U, 0x0d6d6a3eU, 0x7a6a5aa8U,
105 0xe40ecf0bU, 0x9309ff9dU, 0x0a00ae27U, 0x7d079eb1U, 0xf00f9344U,
106 0x8708a3d2U, 0x1e01f268U, 0x6906c2feU, 0xf762575dU, 0x806567cbU,
107 0x196c3671U, 0x6e6b06e7U, 0xfed41b76U, 0x89d32be0U, 0x10da7a5aU,
108 0x67dd4accU, 0xf9b9df6fU, 0x8ebeeff9U, 0x17b7be43U, 0x60b08ed5U,
109 0xd6d6a3e8U, 0xa1d1937eU, 0x38d8c2c4U, 0x4fdff252U, 0xd1bb67f1U,
110 0xa6bc5767U, 0x3fb506ddU, 0x48b2364bU, 0xd80d2bdaU, 0xaf0a1b4cU,
111 0x36034af6U, 0x41047a60U, 0xdf60efc3U, 0xa867df55U, 0x316e8eefU,
112 0x4669be79U, 0xcb61b38cU, 0xbc66831aU, 0x256fd2a0U, 0x5268e236U,
113 0xcc0c7795U, 0xbb0b4703U, 0x220216b9U, 0x5505262fU, 0xc5ba3bbeU,
114 0xb2bd0b28U, 0x2bb45a92U, 0x5cb36a04U, 0xc2d7ffa7U, 0xb5d0cf31U,
115 0x2cd99e8bU, 0x5bdeae1dU, 0x9b64c2b0U, 0xec63f226U, 0x756aa39cU,
116 0x026d930aU, 0x9c0906a9U, 0xeb0e363fU, 0x72076785U, 0x05005713U,
117 0x95bf4a82U, 0xe2b87a14U, 0x7bb12baeU, 0x0cb61b38U, 0x92d28e9bU,
118 0xe5d5be0dU, 0x7cdcefb7U, 0x0bdbdf21U, 0x86d3d2d4U, 0xf1d4e242U,
119 0x68ddb3f8U, 0x1fda836eU, 0x81be16cdU, 0xf6b9265bU, 0x6fb077e1U,
120 0x18b74777U, 0x88085ae6U, 0xff0f6a70U, 0x66063bcaU, 0x11010b5cU,
121 0x8f659effU, 0xf862ae69U, 0x616bffd3U, 0x166ccf45U, 0xa00ae278U,
122 0xd70dd2eeU, 0x4e048354U, 0x3903b3c2U, 0xa7672661U, 0xd06016f7U,
123 0x4969474dU, 0x3e6e77dbU, 0xaed16a4aU, 0xd9d65adcU, 0x40df0b66U,
124 0x37d83bf0U, 0xa9bcae53U, 0xdebb9ec5U, 0x47b2cf7fU, 0x30b5ffe9U,
125 0xbdbdf21cU, 0xcabac28aU, 0x53b39330U, 0x24b4a3a6U, 0xbad03605U,
126 0xcdd70693U, 0x54de5729U, 0x23d967bfU, 0xb3667a2eU, 0xc4614ab8U,
127 0x5d681b02U, 0x2a6f2b94U, 0xb40bbe37U, 0xc30c8ea1U, 0x5a05df1bU,
128 0x2d02ef8dU
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129};
130
Sam Ravnborgce560682006-03-12 23:26:29 +0100131static unsigned long partial_crc32_one(unsigned char c, unsigned long crc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100133 return crctab32[(crc ^ c) & 0xff] ^ (crc >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134}
135
Sam Ravnborgce560682006-03-12 23:26:29 +0100136static unsigned long partial_crc32(const char *s, unsigned long crc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100138 while (*s)
139 crc = partial_crc32_one(*s++, crc);
140 return crc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141}
142
Sam Ravnborgce560682006-03-12 23:26:29 +0100143static unsigned long crc32(const char *s)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100145 return partial_crc32(s, 0xffffffff) ^ 0xffffffff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146}
147
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148/*----------------------------------------------------------------------*/
149
Sam Ravnborgce560682006-03-12 23:26:29 +0100150static enum symbol_type map_to_ns(enum symbol_type t)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100152 if (t == SYM_TYPEDEF)
153 t = SYM_NORMAL;
154 else if (t == SYM_UNION)
155 t = SYM_STRUCT;
156 return t;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157}
158
Michal Marek01762c42011-02-15 15:11:36 +0100159struct symbol *find_symbol(const char *name, enum symbol_type ns, int exact)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100161 unsigned long h = crc32(name) % HASH_BUCKETS;
162 struct symbol *sym;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163
Sam Ravnborg78c041532006-03-12 22:59:36 +0100164 for (sym = symtab[h]; sym; sym = sym->hash_next)
Sam Ravnborgce560682006-03-12 23:26:29 +0100165 if (map_to_ns(sym->type) == map_to_ns(ns) &&
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800166 strcmp(name, sym->name) == 0 &&
167 sym->is_declared)
Sam Ravnborg78c041532006-03-12 22:59:36 +0100168 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169
Michal Marek01762c42011-02-15 15:11:36 +0100170 if (exact && sym && sym->type != ns)
171 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 return sym;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173}
174
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800175static int is_unknown_symbol(struct symbol *sym)
176{
177 struct string_list *defn;
178
179 return ((sym->type == SYM_STRUCT ||
180 sym->type == SYM_UNION ||
181 sym->type == SYM_ENUM) &&
182 (defn = sym->defn) && defn->tag == SYM_NORMAL &&
183 strcmp(defn->string, "}") == 0 &&
184 (defn = defn->next) && defn->tag == SYM_NORMAL &&
185 strcmp(defn->string, "UNKNOWN") == 0 &&
186 (defn = defn->next) && defn->tag == SYM_NORMAL &&
187 strcmp(defn->string, "{") == 0);
188}
189
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700190static struct symbol *__add_symbol(const char *name, enum symbol_type type,
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800191 struct string_list *defn, int is_extern,
192 int is_reference)
Sam Ravnborg78c041532006-03-12 22:59:36 +0100193{
194 unsigned long h = crc32(name) % HASH_BUCKETS;
195 struct symbol *sym;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800196 enum symbol_status status = STATUS_UNCHANGED;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100197
Sam Ravnborgce560682006-03-12 23:26:29 +0100198 for (sym = symtab[h]; sym; sym = sym->hash_next) {
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800199 if (map_to_ns(sym->type) == map_to_ns(type) &&
200 strcmp(name, sym->name) == 0) {
201 if (is_reference)
202 /* fall through */ ;
203 else if (sym->type == type &&
204 equal_list(sym->defn, defn)) {
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800205 if (!sym->is_declared && sym->is_override) {
206 print_location();
207 print_type_name(type, name);
208 fprintf(stderr, " modversion is "
209 "unchanged\n");
210 }
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800211 sym->is_declared = 1;
212 return sym;
213 } else if (!sym->is_declared) {
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800214 if (sym->is_override && flag_preserve) {
215 print_location();
216 fprintf(stderr, "ignoring ");
217 print_type_name(type, name);
218 fprintf(stderr, " modversion change\n");
219 sym->is_declared = 1;
220 return sym;
221 } else {
222 status = is_unknown_symbol(sym) ?
223 STATUS_DEFINED : STATUS_MODIFIED;
224 }
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800225 } else {
Sam Ravnborg78c041532006-03-12 22:59:36 +0100226 error_with_pos("redefinition of %s", name);
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800227 return sym;
228 }
229 break;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100230 }
Sam Ravnborgce560682006-03-12 23:26:29 +0100231 }
Sam Ravnborg78c041532006-03-12 22:59:36 +0100232
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800233 if (sym) {
234 struct symbol **psym;
235
236 for (psym = &symtab[h]; *psym; psym = &(*psym)->hash_next) {
237 if (*psym == sym) {
238 *psym = sym->hash_next;
239 break;
240 }
241 }
242 --nsyms;
243 }
244
Sam Ravnborg78c041532006-03-12 22:59:36 +0100245 sym = xmalloc(sizeof(*sym));
246 sym->name = name;
247 sym->type = type;
248 sym->defn = defn;
249 sym->expansion_trail = NULL;
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200250 sym->visited = NULL;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100251 sym->is_extern = is_extern;
252
253 sym->hash_next = symtab[h];
254 symtab[h] = sym;
255
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800256 sym->is_declared = !is_reference;
257 sym->status = status;
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800258 sym->is_override = 0;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800259
Sam Ravnborg78c041532006-03-12 22:59:36 +0100260 if (flag_debug) {
Michal Marek7ec8eda2011-01-18 16:28:06 +0100261 if (symbol_types[type].name)
262 fprintf(debugfile, "Defn for %s %s == <",
263 symbol_types[type].name, name);
264 else
265 fprintf(debugfile, "Defn for type%d %s == <",
266 type, name);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100267 if (is_extern)
268 fputs("extern ", debugfile);
269 print_list(debugfile, defn);
270 fputs(">\n", debugfile);
271 }
272
273 ++nsyms;
274 return sym;
275}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800277struct symbol *add_symbol(const char *name, enum symbol_type type,
278 struct string_list *defn, int is_extern)
279{
280 return __add_symbol(name, type, defn, is_extern, 0);
281}
282
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700283static struct symbol *add_reference_symbol(const char *name, enum symbol_type type,
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800284 struct string_list *defn, int is_extern)
285{
286 return __add_symbol(name, type, defn, is_extern, 1);
287}
288
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289/*----------------------------------------------------------------------*/
290
Sam Ravnborgce560682006-03-12 23:26:29 +0100291void free_node(struct string_list *node)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100293 free(node->string);
294 free(node);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295}
296
Sam Ravnborg78c041532006-03-12 22:59:36 +0100297void free_list(struct string_list *s, struct string_list *e)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100299 while (s != e) {
300 struct string_list *next = s->next;
301 free_node(s);
302 s = next;
303 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304}
305
Michal Marek68eb8562011-02-02 23:52:13 +0100306static struct string_list *mk_node(const char *string)
307{
308 struct string_list *newnode;
309
310 newnode = xmalloc(sizeof(*newnode));
311 newnode->string = xstrdup(string);
312 newnode->tag = SYM_NORMAL;
313 newnode->next = NULL;
314
315 return newnode;
316}
317
318static struct string_list *concat_list(struct string_list *start, ...)
319{
320 va_list ap;
321 struct string_list *n, *n2;
322
323 if (!start)
324 return NULL;
325 for (va_start(ap, start); (n = va_arg(ap, struct string_list *));) {
326 for (n2 = n; n2->next; n2 = n2->next)
327 ;
328 n2->next = start;
329 start = n;
330 }
331 va_end(ap);
332 return start;
333}
334
Sam Ravnborgce560682006-03-12 23:26:29 +0100335struct string_list *copy_node(struct string_list *node)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100337 struct string_list *newnode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338
Sam Ravnborg78c041532006-03-12 22:59:36 +0100339 newnode = xmalloc(sizeof(*newnode));
340 newnode->string = xstrdup(node->string);
341 newnode->tag = node->tag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
Sam Ravnborg78c041532006-03-12 22:59:36 +0100343 return newnode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344}
345
Sam Ravnborgce560682006-03-12 23:26:29 +0100346static int equal_list(struct string_list *a, struct string_list *b)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100348 while (a && b) {
349 if (a->tag != b->tag || strcmp(a->string, b->string))
350 return 0;
351 a = a->next;
352 b = b->next;
353 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354
Sam Ravnborg78c041532006-03-12 22:59:36 +0100355 return !a && !b;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356}
357
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800358#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
359
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700360static struct string_list *read_node(FILE *f)
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800361{
362 char buffer[256];
363 struct string_list node = {
364 .string = buffer,
365 .tag = SYM_NORMAL };
366 int c;
367
368 while ((c = fgetc(f)) != EOF) {
369 if (c == ' ') {
370 if (node.string == buffer)
371 continue;
372 break;
373 } else if (c == '\n') {
374 if (node.string == buffer)
375 return NULL;
376 ungetc(c, f);
377 break;
378 }
379 if (node.string >= buffer + sizeof(buffer) - 1) {
380 fprintf(stderr, "Token too long\n");
381 exit(1);
382 }
383 *node.string++ = c;
384 }
385 if (node.string == buffer)
386 return NULL;
387 *node.string = 0;
388 node.string = buffer;
389
390 if (node.string[1] == '#') {
391 int n;
392
Michal Marek7ec8eda2011-01-18 16:28:06 +0100393 for (n = 0; n < ARRAY_SIZE(symbol_types); n++) {
394 if (node.string[0] == symbol_types[n].n) {
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800395 node.tag = n;
396 node.string += 2;
397 return copy_node(&node);
398 }
399 }
400 fprintf(stderr, "Unknown type %c\n", node.string[0]);
401 exit(1);
402 }
403 return copy_node(&node);
404}
405
406static void read_reference(FILE *f)
407{
408 while (!feof(f)) {
409 struct string_list *defn = NULL;
410 struct string_list *sym, *def;
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800411 int is_extern = 0, is_override = 0;
412 struct symbol *subsym;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800413
414 sym = read_node(f);
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800415 if (sym && sym->tag == SYM_NORMAL &&
416 !strcmp(sym->string, "override")) {
417 is_override = 1;
418 free_node(sym);
419 sym = read_node(f);
420 }
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800421 if (!sym)
422 continue;
423 def = read_node(f);
424 if (def && def->tag == SYM_NORMAL &&
425 !strcmp(def->string, "extern")) {
426 is_extern = 1;
427 free_node(def);
428 def = read_node(f);
429 }
430 while (def) {
431 def->next = defn;
432 defn = def;
433 def = read_node(f);
434 }
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800435 subsym = add_reference_symbol(xstrdup(sym->string), sym->tag,
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800436 defn, is_extern);
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800437 subsym->is_override = is_override;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800438 free_node(sym);
439 }
440}
441
Sam Ravnborgce560682006-03-12 23:26:29 +0100442static void print_node(FILE * f, struct string_list *list)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443{
Michal Marek7ec8eda2011-01-18 16:28:06 +0100444 if (symbol_types[list->tag].n) {
445 putc(symbol_types[list->tag].n, f);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100446 putc('#', f);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100447 }
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200448 fputs(list->string, f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449}
450
Sam Ravnborgce560682006-03-12 23:26:29 +0100451static void print_list(FILE * f, struct string_list *list)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100453 struct string_list **e, **b;
454 struct string_list *tmp, **tmp2;
455 int elem = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456
Sam Ravnborg78c041532006-03-12 22:59:36 +0100457 if (list == NULL) {
458 fputs("(nil)", f);
459 return;
460 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461
Sam Ravnborg78c041532006-03-12 22:59:36 +0100462 tmp = list;
463 while ((tmp = tmp->next) != NULL)
464 elem++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465
Sam Ravnborg78c041532006-03-12 22:59:36 +0100466 b = alloca(elem * sizeof(*e));
467 e = b + elem;
468 tmp2 = e - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469
Sam Ravnborg78c041532006-03-12 22:59:36 +0100470 (*tmp2--) = list;
471 while ((list = list->next) != NULL)
472 *(tmp2--) = list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473
Sam Ravnborg78c041532006-03-12 22:59:36 +0100474 while (b != e) {
475 print_node(f, *b++);
476 putc(' ', f);
477 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478}
479
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200480static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481{
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200482 struct string_list *list = sym->defn;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100483 struct string_list **e, **b;
484 struct string_list *tmp, **tmp2;
485 int elem = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486
Sam Ravnborg78c041532006-03-12 22:59:36 +0100487 if (!list)
488 return crc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489
Sam Ravnborg78c041532006-03-12 22:59:36 +0100490 tmp = list;
491 while ((tmp = tmp->next) != NULL)
492 elem++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493
Sam Ravnborg78c041532006-03-12 22:59:36 +0100494 b = alloca(elem * sizeof(*e));
495 e = b + elem;
496 tmp2 = e - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
Sam Ravnborg78c041532006-03-12 22:59:36 +0100498 *(tmp2--) = list;
499 while ((list = list->next) != NULL)
500 *(tmp2--) = list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
Sam Ravnborg78c041532006-03-12 22:59:36 +0100502 while (b != e) {
503 struct string_list *cur;
504 struct symbol *subsym;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505
Sam Ravnborg78c041532006-03-12 22:59:36 +0100506 cur = *(b++);
507 switch (cur->tag) {
508 case SYM_NORMAL:
509 if (flag_dump_defs)
510 fprintf(debugfile, "%s ", cur->string);
511 crc = partial_crc32(cur->string, crc);
512 crc = partial_crc32_one(' ', crc);
513 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
Sam Ravnborg78c041532006-03-12 22:59:36 +0100515 case SYM_TYPEDEF:
Michal Marek01762c42011-02-15 15:11:36 +0100516 subsym = find_symbol(cur->string, cur->tag, 0);
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800517 /* FIXME: Bad reference files can segfault here. */
Sam Ravnborg78c041532006-03-12 22:59:36 +0100518 if (subsym->expansion_trail) {
519 if (flag_dump_defs)
520 fprintf(debugfile, "%s ", cur->string);
521 crc = partial_crc32(cur->string, crc);
522 crc = partial_crc32_one(' ', crc);
523 } else {
524 subsym->expansion_trail = expansion_trail;
525 expansion_trail = subsym;
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200526 crc = expand_and_crc_sym(subsym, crc);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100527 }
528 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529
Sam Ravnborg78c041532006-03-12 22:59:36 +0100530 case SYM_STRUCT:
531 case SYM_UNION:
532 case SYM_ENUM:
Michal Marek01762c42011-02-15 15:11:36 +0100533 subsym = find_symbol(cur->string, cur->tag, 0);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100534 if (!subsym) {
Michal Marek68eb8562011-02-02 23:52:13 +0100535 struct string_list *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
Sam Ravnborg78c041532006-03-12 22:59:36 +0100537 error_with_pos("expand undefined %s %s",
Michal Marek7ec8eda2011-01-18 16:28:06 +0100538 symbol_types[cur->tag].name,
Sam Ravnborg78c041532006-03-12 22:59:36 +0100539 cur->string);
Michal Marek68eb8562011-02-02 23:52:13 +0100540 n = concat_list(mk_node
541 (symbol_types[cur->tag].name),
542 mk_node(cur->string),
543 mk_node("{"),
544 mk_node("UNKNOWN"),
545 mk_node("}"), NULL);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100546 subsym =
547 add_symbol(cur->string, cur->tag, n, 0);
548 }
549 if (subsym->expansion_trail) {
550 if (flag_dump_defs) {
551 fprintf(debugfile, "%s %s ",
Michal Marek7ec8eda2011-01-18 16:28:06 +0100552 symbol_types[cur->tag].name,
Sam Ravnborg78c041532006-03-12 22:59:36 +0100553 cur->string);
554 }
555
Michal Marek7ec8eda2011-01-18 16:28:06 +0100556 crc = partial_crc32(symbol_types[cur->tag].name,
Sam Ravnborgce560682006-03-12 23:26:29 +0100557 crc);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100558 crc = partial_crc32_one(' ', crc);
559 crc = partial_crc32(cur->string, crc);
560 crc = partial_crc32_one(' ', crc);
561 } else {
562 subsym->expansion_trail = expansion_trail;
563 expansion_trail = subsym;
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200564 crc = expand_and_crc_sym(subsym, crc);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100565 }
566 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200570 {
571 static struct symbol **end = &visited_symbols;
572
573 if (!sym->visited) {
574 *end = sym;
575 end = &sym->visited;
576 sym->visited = (struct symbol *)-1L;
577 }
578 }
579
Sam Ravnborg78c041532006-03-12 22:59:36 +0100580 return crc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581}
582
Sam Ravnborg78c041532006-03-12 22:59:36 +0100583void export_symbol(const char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100585 struct symbol *sym;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586
Michal Marek01762c42011-02-15 15:11:36 +0100587 sym = find_symbol(name, SYM_NORMAL, 0);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100588 if (!sym)
589 error_with_pos("export undefined symbol %s", name);
590 else {
591 unsigned long crc;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800592 int has_changed = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
Sam Ravnborg78c041532006-03-12 22:59:36 +0100594 if (flag_dump_defs)
595 fprintf(debugfile, "Export %s == <", name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596
Sam Ravnborg78c041532006-03-12 22:59:36 +0100597 expansion_trail = (struct symbol *)-1L;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800599 sym->expansion_trail = expansion_trail;
600 expansion_trail = sym;
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200601 crc = expand_and_crc_sym(sym, 0xffffffff) ^ 0xffffffff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602
Sam Ravnborg78c041532006-03-12 22:59:36 +0100603 sym = expansion_trail;
604 while (sym != (struct symbol *)-1L) {
605 struct symbol *n = sym->expansion_trail;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800606
607 if (sym->status != STATUS_UNCHANGED) {
608 if (!has_changed) {
609 print_location();
610 fprintf(stderr, "%s: %s: modversion "
611 "changed because of changes "
612 "in ", flag_preserve ? "error" :
613 "warning", name);
614 } else
615 fprintf(stderr, ", ");
616 print_type_name(sym->type, sym->name);
617 if (sym->status == STATUS_DEFINED)
618 fprintf(stderr, " (became defined)");
619 has_changed = 1;
620 if (flag_preserve)
621 errors++;
622 }
Sam Ravnborg78c041532006-03-12 22:59:36 +0100623 sym->expansion_trail = 0;
624 sym = n;
625 }
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800626 if (has_changed)
627 fprintf(stderr, "\n");
Sam Ravnborg78c041532006-03-12 22:59:36 +0100628
629 if (flag_dump_defs)
630 fputs(">\n", debugfile);
631
Sam Ravnborg2ea03892009-01-14 21:38:20 +0100632 /* Used as a linker script. */
633 printf("%s__crc_%s = 0x%08lx ;\n", mod_prefix, name, crc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635}
636
637/*----------------------------------------------------------------------*/
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800638
639static void print_location(void)
640{
641 fprintf(stderr, "%s:%d: ", cur_filename ? : "<stdin>", cur_line);
642}
643
644static void print_type_name(enum symbol_type type, const char *name)
645{
Michal Marek7ec8eda2011-01-18 16:28:06 +0100646 if (symbol_types[type].name)
647 fprintf(stderr, "%s %s", symbol_types[type].name, name);
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800648 else
649 fprintf(stderr, "%s", name);
650}
651
Sam Ravnborg78c041532006-03-12 22:59:36 +0100652void error_with_pos(const char *fmt, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100654 va_list args;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655
Sam Ravnborg78c041532006-03-12 22:59:36 +0100656 if (flag_warnings) {
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800657 print_location();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658
Sam Ravnborg78c041532006-03-12 22:59:36 +0100659 va_start(args, fmt);
660 vfprintf(stderr, fmt, args);
661 va_end(args);
662 putc('\n', stderr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
Sam Ravnborg78c041532006-03-12 22:59:36 +0100664 errors++;
665 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666}
667
Sam Ravnborgce560682006-03-12 23:26:29 +0100668static void genksyms_usage(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669{
Sam Ravnborg2ea03892009-01-14 21:38:20 +0100670 fputs("Usage:\n" "genksyms [-adDTwqhV] > /path/to/.tmp_obj.ver\n" "\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671#ifdef __GNU_LIBRARY__
Mike Frysinger36091fd2007-11-10 09:32:20 -0500672 " -a, --arch Select architecture\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 " -d, --debug Increment the debug level (repeatable)\n"
674 " -D, --dump Dump expanded symbol defs (for debugging only)\n"
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800675 " -r, --reference file Read reference symbols from a file\n"
676 " -T, --dump-types file Dump expanded types into file\n"
677 " -p, --preserve Preserve reference modversions or fail\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 " -w, --warnings Enable warnings\n"
679 " -q, --quiet Disable warnings (default)\n"
680 " -h, --help Print this message\n"
681 " -V, --version Print the release version\n"
Sam Ravnborg78c041532006-03-12 22:59:36 +0100682#else /* __GNU_LIBRARY__ */
Mike Frysinger36091fd2007-11-10 09:32:20 -0500683 " -a Select architecture\n"
Sam Ravnborg78c041532006-03-12 22:59:36 +0100684 " -d Increment the debug level (repeatable)\n"
685 " -D Dump expanded symbol defs (for debugging only)\n"
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800686 " -r file Read reference symbols from a file\n"
687 " -T file Dump expanded types into file\n"
688 " -p Preserve reference modversions or fail\n"
Sam Ravnborg78c041532006-03-12 22:59:36 +0100689 " -w Enable warnings\n"
690 " -q Disable warnings (default)\n"
691 " -h Print this message\n"
692 " -V Print the release version\n"
693#endif /* __GNU_LIBRARY__ */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 , stderr);
695}
696
Sam Ravnborg78c041532006-03-12 22:59:36 +0100697int main(int argc, char **argv)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698{
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800699 FILE *dumpfile = NULL, *ref_file = NULL;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100700 int o;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701
702#ifdef __GNU_LIBRARY__
Sam Ravnborg78c041532006-03-12 22:59:36 +0100703 struct option long_opts[] = {
704 {"arch", 1, 0, 'a'},
705 {"debug", 0, 0, 'd'},
706 {"warnings", 0, 0, 'w'},
707 {"quiet", 0, 0, 'q'},
708 {"dump", 0, 0, 'D'},
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800709 {"reference", 1, 0, 'r'},
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200710 {"dump-types", 1, 0, 'T'},
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800711 {"preserve", 0, 0, 'p'},
Sam Ravnborg78c041532006-03-12 22:59:36 +0100712 {"version", 0, 0, 'V'},
713 {"help", 0, 0, 'h'},
714 {0, 0, 0, 0}
715 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
Sam Ravnborg2ea03892009-01-14 21:38:20 +0100717 while ((o = getopt_long(argc, argv, "a:dwqVDr:T:ph",
Sam Ravnborg78c041532006-03-12 22:59:36 +0100718 &long_opts[0], NULL)) != EOF)
719#else /* __GNU_LIBRARY__ */
Sam Ravnborg2ea03892009-01-14 21:38:20 +0100720 while ((o = getopt(argc, argv, "a:dwqVDr:T:ph")) != EOF)
Sam Ravnborg78c041532006-03-12 22:59:36 +0100721#endif /* __GNU_LIBRARY__ */
722 switch (o) {
723 case 'a':
724 arch = optarg;
725 break;
726 case 'd':
727 flag_debug++;
728 break;
729 case 'w':
730 flag_warnings = 1;
731 break;
732 case 'q':
733 flag_warnings = 0;
734 break;
735 case 'V':
736 fputs("genksyms version 2.5.60\n", stderr);
737 break;
738 case 'D':
739 flag_dump_defs = 1;
740 break;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800741 case 'r':
742 flag_reference = 1;
743 ref_file = fopen(optarg, "r");
744 if (!ref_file) {
745 perror(optarg);
746 return 1;
747 }
748 break;
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200749 case 'T':
750 flag_dump_types = 1;
751 dumpfile = fopen(optarg, "w");
752 if (!dumpfile) {
753 perror(optarg);
754 return 1;
755 }
756 break;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800757 case 'p':
758 flag_preserve = 1;
759 break;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100760 case 'h':
761 genksyms_usage();
762 return 0;
763 default:
764 genksyms_usage();
765 return 1;
766 }
Adrian Bunkf606ddf2008-07-23 21:28:50 -0700767 if ((strcmp(arch, "h8300") == 0) || (strcmp(arch, "blackfin") == 0))
Sam Ravnborg78c041532006-03-12 22:59:36 +0100768 mod_prefix = "_";
769 {
770 extern int yydebug;
771 extern int yy_flex_debug;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772
Sam Ravnborg78c041532006-03-12 22:59:36 +0100773 yydebug = (flag_debug > 1);
774 yy_flex_debug = (flag_debug > 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775
Sam Ravnborg78c041532006-03-12 22:59:36 +0100776 debugfile = stderr;
777 /* setlinebuf(debugfile); */
778 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779
Alexander Beregalovc64152b2010-01-07 05:22:41 +0300780 if (flag_reference) {
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800781 read_reference(ref_file);
Alexander Beregalovc64152b2010-01-07 05:22:41 +0300782 fclose(ref_file);
783 }
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800784
Sam Ravnborg78c041532006-03-12 22:59:36 +0100785 yyparse();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200787 if (flag_dump_types && visited_symbols) {
788 while (visited_symbols != (struct symbol *)-1L) {
789 struct symbol *sym = visited_symbols;
790
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800791 if (sym->is_override)
792 fputs("override ", dumpfile);
Michal Marek7ec8eda2011-01-18 16:28:06 +0100793 if (symbol_types[sym->type].n) {
794 putc(symbol_types[sym->type].n, dumpfile);
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200795 putc('#', dumpfile);
796 }
797 fputs(sym->name, dumpfile);
798 putc(' ', dumpfile);
Andreas Gruenbacher3b40d382008-07-21 04:28:25 +0200799 if (sym->is_extern)
800 fputs("extern ", dumpfile);
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200801 print_list(dumpfile, sym->defn);
802 putc('\n', dumpfile);
803
804 visited_symbols = sym->visited;
805 sym->visited = NULL;
806 }
807 }
808
Sam Ravnborg78c041532006-03-12 22:59:36 +0100809 if (flag_debug) {
810 fprintf(debugfile, "Hash table occupancy %d/%d = %g\n",
811 nsyms, HASH_BUCKETS,
812 (double)nsyms / (double)HASH_BUCKETS);
813 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
Sam Ravnborg78c041532006-03-12 22:59:36 +0100815 return errors != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816}