blob: d17b7a2b941f7c5b4213d75018f72652f315940e [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
Sam Ravnborg78c041532006-03-12 22:59:36 +0100159struct symbol *find_symbol(const char *name, enum symbol_type ns)
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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 return sym;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171}
172
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800173static int is_unknown_symbol(struct symbol *sym)
174{
175 struct string_list *defn;
176
177 return ((sym->type == SYM_STRUCT ||
178 sym->type == SYM_UNION ||
179 sym->type == SYM_ENUM) &&
180 (defn = sym->defn) && defn->tag == SYM_NORMAL &&
181 strcmp(defn->string, "}") == 0 &&
182 (defn = defn->next) && defn->tag == SYM_NORMAL &&
183 strcmp(defn->string, "UNKNOWN") == 0 &&
184 (defn = defn->next) && defn->tag == SYM_NORMAL &&
185 strcmp(defn->string, "{") == 0);
186}
187
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700188static struct symbol *__add_symbol(const char *name, enum symbol_type type,
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800189 struct string_list *defn, int is_extern,
190 int is_reference)
Sam Ravnborg78c041532006-03-12 22:59:36 +0100191{
192 unsigned long h = crc32(name) % HASH_BUCKETS;
193 struct symbol *sym;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800194 enum symbol_status status = STATUS_UNCHANGED;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100195
Sam Ravnborgce560682006-03-12 23:26:29 +0100196 for (sym = symtab[h]; sym; sym = sym->hash_next) {
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800197 if (map_to_ns(sym->type) == map_to_ns(type) &&
198 strcmp(name, sym->name) == 0) {
199 if (is_reference)
200 /* fall through */ ;
201 else if (sym->type == type &&
202 equal_list(sym->defn, defn)) {
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800203 if (!sym->is_declared && sym->is_override) {
204 print_location();
205 print_type_name(type, name);
206 fprintf(stderr, " modversion is "
207 "unchanged\n");
208 }
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800209 sym->is_declared = 1;
210 return sym;
211 } else if (!sym->is_declared) {
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800212 if (sym->is_override && flag_preserve) {
213 print_location();
214 fprintf(stderr, "ignoring ");
215 print_type_name(type, name);
216 fprintf(stderr, " modversion change\n");
217 sym->is_declared = 1;
218 return sym;
219 } else {
220 status = is_unknown_symbol(sym) ?
221 STATUS_DEFINED : STATUS_MODIFIED;
222 }
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800223 } else {
Sam Ravnborg78c041532006-03-12 22:59:36 +0100224 error_with_pos("redefinition of %s", name);
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800225 return sym;
226 }
227 break;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100228 }
Sam Ravnborgce560682006-03-12 23:26:29 +0100229 }
Sam Ravnborg78c041532006-03-12 22:59:36 +0100230
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800231 if (sym) {
232 struct symbol **psym;
233
234 for (psym = &symtab[h]; *psym; psym = &(*psym)->hash_next) {
235 if (*psym == sym) {
236 *psym = sym->hash_next;
237 break;
238 }
239 }
240 --nsyms;
241 }
242
Sam Ravnborg78c041532006-03-12 22:59:36 +0100243 sym = xmalloc(sizeof(*sym));
244 sym->name = name;
245 sym->type = type;
246 sym->defn = defn;
247 sym->expansion_trail = NULL;
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200248 sym->visited = NULL;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100249 sym->is_extern = is_extern;
250
251 sym->hash_next = symtab[h];
252 symtab[h] = sym;
253
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800254 sym->is_declared = !is_reference;
255 sym->status = status;
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800256 sym->is_override = 0;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800257
Sam Ravnborg78c041532006-03-12 22:59:36 +0100258 if (flag_debug) {
Michal Marek7ec8eda2011-01-18 16:28:06 +0100259 if (symbol_types[type].name)
260 fprintf(debugfile, "Defn for %s %s == <",
261 symbol_types[type].name, name);
262 else
263 fprintf(debugfile, "Defn for type%d %s == <",
264 type, name);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100265 if (is_extern)
266 fputs("extern ", debugfile);
267 print_list(debugfile, defn);
268 fputs(">\n", debugfile);
269 }
270
271 ++nsyms;
272 return sym;
273}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800275struct symbol *add_symbol(const char *name, enum symbol_type type,
276 struct string_list *defn, int is_extern)
277{
278 return __add_symbol(name, type, defn, is_extern, 0);
279}
280
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700281static struct symbol *add_reference_symbol(const char *name, enum symbol_type type,
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800282 struct string_list *defn, int is_extern)
283{
284 return __add_symbol(name, type, defn, is_extern, 1);
285}
286
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287/*----------------------------------------------------------------------*/
288
Sam Ravnborgce560682006-03-12 23:26:29 +0100289void free_node(struct string_list *node)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100291 free(node->string);
292 free(node);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293}
294
Sam Ravnborg78c041532006-03-12 22:59:36 +0100295void free_list(struct string_list *s, struct string_list *e)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100297 while (s != e) {
298 struct string_list *next = s->next;
299 free_node(s);
300 s = next;
301 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302}
303
Michal Marek68eb8562011-02-02 23:52:13 +0100304static struct string_list *mk_node(const char *string)
305{
306 struct string_list *newnode;
307
308 newnode = xmalloc(sizeof(*newnode));
309 newnode->string = xstrdup(string);
310 newnode->tag = SYM_NORMAL;
311 newnode->next = NULL;
312
313 return newnode;
314}
315
316static struct string_list *concat_list(struct string_list *start, ...)
317{
318 va_list ap;
319 struct string_list *n, *n2;
320
321 if (!start)
322 return NULL;
323 for (va_start(ap, start); (n = va_arg(ap, struct string_list *));) {
324 for (n2 = n; n2->next; n2 = n2->next)
325 ;
326 n2->next = start;
327 start = n;
328 }
329 va_end(ap);
330 return start;
331}
332
Sam Ravnborgce560682006-03-12 23:26:29 +0100333struct string_list *copy_node(struct string_list *node)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100335 struct string_list *newnode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
Sam Ravnborg78c041532006-03-12 22:59:36 +0100337 newnode = xmalloc(sizeof(*newnode));
338 newnode->string = xstrdup(node->string);
339 newnode->tag = node->tag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340
Sam Ravnborg78c041532006-03-12 22:59:36 +0100341 return newnode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342}
343
Sam Ravnborgce560682006-03-12 23:26:29 +0100344static int equal_list(struct string_list *a, struct string_list *b)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100346 while (a && b) {
347 if (a->tag != b->tag || strcmp(a->string, b->string))
348 return 0;
349 a = a->next;
350 b = b->next;
351 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352
Sam Ravnborg78c041532006-03-12 22:59:36 +0100353 return !a && !b;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354}
355
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800356#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
357
Ladinu Chandrasingheb7ed6982009-09-22 16:43:42 -0700358static struct string_list *read_node(FILE *f)
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800359{
360 char buffer[256];
361 struct string_list node = {
362 .string = buffer,
363 .tag = SYM_NORMAL };
364 int c;
365
366 while ((c = fgetc(f)) != EOF) {
367 if (c == ' ') {
368 if (node.string == buffer)
369 continue;
370 break;
371 } else if (c == '\n') {
372 if (node.string == buffer)
373 return NULL;
374 ungetc(c, f);
375 break;
376 }
377 if (node.string >= buffer + sizeof(buffer) - 1) {
378 fprintf(stderr, "Token too long\n");
379 exit(1);
380 }
381 *node.string++ = c;
382 }
383 if (node.string == buffer)
384 return NULL;
385 *node.string = 0;
386 node.string = buffer;
387
388 if (node.string[1] == '#') {
389 int n;
390
Michal Marek7ec8eda2011-01-18 16:28:06 +0100391 for (n = 0; n < ARRAY_SIZE(symbol_types); n++) {
392 if (node.string[0] == symbol_types[n].n) {
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800393 node.tag = n;
394 node.string += 2;
395 return copy_node(&node);
396 }
397 }
398 fprintf(stderr, "Unknown type %c\n", node.string[0]);
399 exit(1);
400 }
401 return copy_node(&node);
402}
403
404static void read_reference(FILE *f)
405{
406 while (!feof(f)) {
407 struct string_list *defn = NULL;
408 struct string_list *sym, *def;
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800409 int is_extern = 0, is_override = 0;
410 struct symbol *subsym;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800411
412 sym = read_node(f);
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800413 if (sym && sym->tag == SYM_NORMAL &&
414 !strcmp(sym->string, "override")) {
415 is_override = 1;
416 free_node(sym);
417 sym = read_node(f);
418 }
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800419 if (!sym)
420 continue;
421 def = read_node(f);
422 if (def && def->tag == SYM_NORMAL &&
423 !strcmp(def->string, "extern")) {
424 is_extern = 1;
425 free_node(def);
426 def = read_node(f);
427 }
428 while (def) {
429 def->next = defn;
430 defn = def;
431 def = read_node(f);
432 }
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800433 subsym = add_reference_symbol(xstrdup(sym->string), sym->tag,
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800434 defn, is_extern);
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800435 subsym->is_override = is_override;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800436 free_node(sym);
437 }
438}
439
Sam Ravnborgce560682006-03-12 23:26:29 +0100440static void print_node(FILE * f, struct string_list *list)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441{
Michal Marek7ec8eda2011-01-18 16:28:06 +0100442 if (symbol_types[list->tag].n) {
443 putc(symbol_types[list->tag].n, f);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100444 putc('#', f);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100445 }
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200446 fputs(list->string, f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447}
448
Sam Ravnborgce560682006-03-12 23:26:29 +0100449static void print_list(FILE * f, struct string_list *list)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100451 struct string_list **e, **b;
452 struct string_list *tmp, **tmp2;
453 int elem = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454
Sam Ravnborg78c041532006-03-12 22:59:36 +0100455 if (list == NULL) {
456 fputs("(nil)", f);
457 return;
458 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459
Sam Ravnborg78c041532006-03-12 22:59:36 +0100460 tmp = list;
461 while ((tmp = tmp->next) != NULL)
462 elem++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463
Sam Ravnborg78c041532006-03-12 22:59:36 +0100464 b = alloca(elem * sizeof(*e));
465 e = b + elem;
466 tmp2 = e - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467
Sam Ravnborg78c041532006-03-12 22:59:36 +0100468 (*tmp2--) = list;
469 while ((list = list->next) != NULL)
470 *(tmp2--) = list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471
Sam Ravnborg78c041532006-03-12 22:59:36 +0100472 while (b != e) {
473 print_node(f, *b++);
474 putc(' ', f);
475 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476}
477
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200478static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479{
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200480 struct string_list *list = sym->defn;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100481 struct string_list **e, **b;
482 struct string_list *tmp, **tmp2;
483 int elem = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484
Sam Ravnborg78c041532006-03-12 22:59:36 +0100485 if (!list)
486 return crc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487
Sam Ravnborg78c041532006-03-12 22:59:36 +0100488 tmp = list;
489 while ((tmp = tmp->next) != NULL)
490 elem++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491
Sam Ravnborg78c041532006-03-12 22:59:36 +0100492 b = alloca(elem * sizeof(*e));
493 e = b + elem;
494 tmp2 = e - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495
Sam Ravnborg78c041532006-03-12 22:59:36 +0100496 *(tmp2--) = list;
497 while ((list = list->next) != NULL)
498 *(tmp2--) = list;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499
Sam Ravnborg78c041532006-03-12 22:59:36 +0100500 while (b != e) {
501 struct string_list *cur;
502 struct symbol *subsym;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503
Sam Ravnborg78c041532006-03-12 22:59:36 +0100504 cur = *(b++);
505 switch (cur->tag) {
506 case SYM_NORMAL:
507 if (flag_dump_defs)
508 fprintf(debugfile, "%s ", cur->string);
509 crc = partial_crc32(cur->string, crc);
510 crc = partial_crc32_one(' ', crc);
511 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
Sam Ravnborg78c041532006-03-12 22:59:36 +0100513 case SYM_TYPEDEF:
514 subsym = find_symbol(cur->string, cur->tag);
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800515 /* FIXME: Bad reference files can segfault here. */
Sam Ravnborg78c041532006-03-12 22:59:36 +0100516 if (subsym->expansion_trail) {
517 if (flag_dump_defs)
518 fprintf(debugfile, "%s ", cur->string);
519 crc = partial_crc32(cur->string, crc);
520 crc = partial_crc32_one(' ', crc);
521 } else {
522 subsym->expansion_trail = expansion_trail;
523 expansion_trail = subsym;
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200524 crc = expand_and_crc_sym(subsym, crc);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100525 }
526 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527
Sam Ravnborg78c041532006-03-12 22:59:36 +0100528 case SYM_STRUCT:
529 case SYM_UNION:
530 case SYM_ENUM:
531 subsym = find_symbol(cur->string, cur->tag);
532 if (!subsym) {
Michal Marek68eb8562011-02-02 23:52:13 +0100533 struct string_list *n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534
Sam Ravnborg78c041532006-03-12 22:59:36 +0100535 error_with_pos("expand undefined %s %s",
Michal Marek7ec8eda2011-01-18 16:28:06 +0100536 symbol_types[cur->tag].name,
Sam Ravnborg78c041532006-03-12 22:59:36 +0100537 cur->string);
Michal Marek68eb8562011-02-02 23:52:13 +0100538 n = concat_list(mk_node
539 (symbol_types[cur->tag].name),
540 mk_node(cur->string),
541 mk_node("{"),
542 mk_node("UNKNOWN"),
543 mk_node("}"), NULL);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100544 subsym =
545 add_symbol(cur->string, cur->tag, n, 0);
546 }
547 if (subsym->expansion_trail) {
548 if (flag_dump_defs) {
549 fprintf(debugfile, "%s %s ",
Michal Marek7ec8eda2011-01-18 16:28:06 +0100550 symbol_types[cur->tag].name,
Sam Ravnborg78c041532006-03-12 22:59:36 +0100551 cur->string);
552 }
553
Michal Marek7ec8eda2011-01-18 16:28:06 +0100554 crc = partial_crc32(symbol_types[cur->tag].name,
Sam Ravnborgce560682006-03-12 23:26:29 +0100555 crc);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100556 crc = partial_crc32_one(' ', crc);
557 crc = partial_crc32(cur->string, crc);
558 crc = partial_crc32_one(' ', crc);
559 } else {
560 subsym->expansion_trail = expansion_trail;
561 expansion_trail = subsym;
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200562 crc = expand_and_crc_sym(subsym, crc);
Sam Ravnborg78c041532006-03-12 22:59:36 +0100563 }
564 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200568 {
569 static struct symbol **end = &visited_symbols;
570
571 if (!sym->visited) {
572 *end = sym;
573 end = &sym->visited;
574 sym->visited = (struct symbol *)-1L;
575 }
576 }
577
Sam Ravnborg78c041532006-03-12 22:59:36 +0100578 return crc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579}
580
Sam Ravnborg78c041532006-03-12 22:59:36 +0100581void export_symbol(const char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100583 struct symbol *sym;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
Sam Ravnborg78c041532006-03-12 22:59:36 +0100585 sym = find_symbol(name, SYM_NORMAL);
586 if (!sym)
587 error_with_pos("export undefined symbol %s", name);
588 else {
589 unsigned long crc;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800590 int has_changed = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591
Sam Ravnborg78c041532006-03-12 22:59:36 +0100592 if (flag_dump_defs)
593 fprintf(debugfile, "Export %s == <", name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594
Sam Ravnborg78c041532006-03-12 22:59:36 +0100595 expansion_trail = (struct symbol *)-1L;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800597 sym->expansion_trail = expansion_trail;
598 expansion_trail = sym;
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200599 crc = expand_and_crc_sym(sym, 0xffffffff) ^ 0xffffffff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600
Sam Ravnborg78c041532006-03-12 22:59:36 +0100601 sym = expansion_trail;
602 while (sym != (struct symbol *)-1L) {
603 struct symbol *n = sym->expansion_trail;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800604
605 if (sym->status != STATUS_UNCHANGED) {
606 if (!has_changed) {
607 print_location();
608 fprintf(stderr, "%s: %s: modversion "
609 "changed because of changes "
610 "in ", flag_preserve ? "error" :
611 "warning", name);
612 } else
613 fprintf(stderr, ", ");
614 print_type_name(sym->type, sym->name);
615 if (sym->status == STATUS_DEFINED)
616 fprintf(stderr, " (became defined)");
617 has_changed = 1;
618 if (flag_preserve)
619 errors++;
620 }
Sam Ravnborg78c041532006-03-12 22:59:36 +0100621 sym->expansion_trail = 0;
622 sym = n;
623 }
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800624 if (has_changed)
625 fprintf(stderr, "\n");
Sam Ravnborg78c041532006-03-12 22:59:36 +0100626
627 if (flag_dump_defs)
628 fputs(">\n", debugfile);
629
Sam Ravnborg2ea03892009-01-14 21:38:20 +0100630 /* Used as a linker script. */
631 printf("%s__crc_%s = 0x%08lx ;\n", mod_prefix, name, crc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633}
634
635/*----------------------------------------------------------------------*/
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800636
637static void print_location(void)
638{
639 fprintf(stderr, "%s:%d: ", cur_filename ? : "<stdin>", cur_line);
640}
641
642static void print_type_name(enum symbol_type type, const char *name)
643{
Michal Marek7ec8eda2011-01-18 16:28:06 +0100644 if (symbol_types[type].name)
645 fprintf(stderr, "%s %s", symbol_types[type].name, name);
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800646 else
647 fprintf(stderr, "%s", name);
648}
649
Sam Ravnborg78c041532006-03-12 22:59:36 +0100650void error_with_pos(const char *fmt, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651{
Sam Ravnborg78c041532006-03-12 22:59:36 +0100652 va_list args;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653
Sam Ravnborg78c041532006-03-12 22:59:36 +0100654 if (flag_warnings) {
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800655 print_location();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656
Sam Ravnborg78c041532006-03-12 22:59:36 +0100657 va_start(args, fmt);
658 vfprintf(stderr, fmt, args);
659 va_end(args);
660 putc('\n', stderr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661
Sam Ravnborg78c041532006-03-12 22:59:36 +0100662 errors++;
663 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664}
665
Sam Ravnborgce560682006-03-12 23:26:29 +0100666static void genksyms_usage(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667{
Sam Ravnborg2ea03892009-01-14 21:38:20 +0100668 fputs("Usage:\n" "genksyms [-adDTwqhV] > /path/to/.tmp_obj.ver\n" "\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669#ifdef __GNU_LIBRARY__
Mike Frysinger36091fd2007-11-10 09:32:20 -0500670 " -a, --arch Select architecture\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 " -d, --debug Increment the debug level (repeatable)\n"
672 " -D, --dump Dump expanded symbol defs (for debugging only)\n"
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800673 " -r, --reference file Read reference symbols from a file\n"
674 " -T, --dump-types file Dump expanded types into file\n"
675 " -p, --preserve Preserve reference modversions or fail\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 " -w, --warnings Enable warnings\n"
677 " -q, --quiet Disable warnings (default)\n"
678 " -h, --help Print this message\n"
679 " -V, --version Print the release version\n"
Sam Ravnborg78c041532006-03-12 22:59:36 +0100680#else /* __GNU_LIBRARY__ */
Mike Frysinger36091fd2007-11-10 09:32:20 -0500681 " -a Select architecture\n"
Sam Ravnborg78c041532006-03-12 22:59:36 +0100682 " -d Increment the debug level (repeatable)\n"
683 " -D Dump expanded symbol defs (for debugging only)\n"
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800684 " -r file Read reference symbols from a file\n"
685 " -T file Dump expanded types into file\n"
686 " -p Preserve reference modversions or fail\n"
Sam Ravnborg78c041532006-03-12 22:59:36 +0100687 " -w Enable warnings\n"
688 " -q Disable warnings (default)\n"
689 " -h Print this message\n"
690 " -V Print the release version\n"
691#endif /* __GNU_LIBRARY__ */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 , stderr);
693}
694
Sam Ravnborg78c041532006-03-12 22:59:36 +0100695int main(int argc, char **argv)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696{
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800697 FILE *dumpfile = NULL, *ref_file = NULL;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100698 int o;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699
700#ifdef __GNU_LIBRARY__
Sam Ravnborg78c041532006-03-12 22:59:36 +0100701 struct option long_opts[] = {
702 {"arch", 1, 0, 'a'},
703 {"debug", 0, 0, 'd'},
704 {"warnings", 0, 0, 'w'},
705 {"quiet", 0, 0, 'q'},
706 {"dump", 0, 0, 'D'},
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800707 {"reference", 1, 0, 'r'},
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200708 {"dump-types", 1, 0, 'T'},
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800709 {"preserve", 0, 0, 'p'},
Sam Ravnborg78c041532006-03-12 22:59:36 +0100710 {"version", 0, 0, 'V'},
711 {"help", 0, 0, 'h'},
712 {0, 0, 0, 0}
713 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
Sam Ravnborg2ea03892009-01-14 21:38:20 +0100715 while ((o = getopt_long(argc, argv, "a:dwqVDr:T:ph",
Sam Ravnborg78c041532006-03-12 22:59:36 +0100716 &long_opts[0], NULL)) != EOF)
717#else /* __GNU_LIBRARY__ */
Sam Ravnborg2ea03892009-01-14 21:38:20 +0100718 while ((o = getopt(argc, argv, "a:dwqVDr:T:ph")) != EOF)
Sam Ravnborg78c041532006-03-12 22:59:36 +0100719#endif /* __GNU_LIBRARY__ */
720 switch (o) {
721 case 'a':
722 arch = optarg;
723 break;
724 case 'd':
725 flag_debug++;
726 break;
727 case 'w':
728 flag_warnings = 1;
729 break;
730 case 'q':
731 flag_warnings = 0;
732 break;
733 case 'V':
734 fputs("genksyms version 2.5.60\n", stderr);
735 break;
736 case 'D':
737 flag_dump_defs = 1;
738 break;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800739 case 'r':
740 flag_reference = 1;
741 ref_file = fopen(optarg, "r");
742 if (!ref_file) {
743 perror(optarg);
744 return 1;
745 }
746 break;
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200747 case 'T':
748 flag_dump_types = 1;
749 dumpfile = fopen(optarg, "w");
750 if (!dumpfile) {
751 perror(optarg);
752 return 1;
753 }
754 break;
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800755 case 'p':
756 flag_preserve = 1;
757 break;
Sam Ravnborg78c041532006-03-12 22:59:36 +0100758 case 'h':
759 genksyms_usage();
760 return 0;
761 default:
762 genksyms_usage();
763 return 1;
764 }
Adrian Bunkf606ddf2008-07-23 21:28:50 -0700765 if ((strcmp(arch, "h8300") == 0) || (strcmp(arch, "blackfin") == 0))
Sam Ravnborg78c041532006-03-12 22:59:36 +0100766 mod_prefix = "_";
767 {
768 extern int yydebug;
769 extern int yy_flex_debug;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770
Sam Ravnborg78c041532006-03-12 22:59:36 +0100771 yydebug = (flag_debug > 1);
772 yy_flex_debug = (flag_debug > 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773
Sam Ravnborg78c041532006-03-12 22:59:36 +0100774 debugfile = stderr;
775 /* setlinebuf(debugfile); */
776 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777
Alexander Beregalovc64152b2010-01-07 05:22:41 +0300778 if (flag_reference) {
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800779 read_reference(ref_file);
Alexander Beregalovc64152b2010-01-07 05:22:41 +0300780 fclose(ref_file);
781 }
Andreas Gruenbacher64e6c1e2008-12-01 14:21:01 -0800782
Sam Ravnborg78c041532006-03-12 22:59:36 +0100783 yyparse();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200785 if (flag_dump_types && visited_symbols) {
786 while (visited_symbols != (struct symbol *)-1L) {
787 struct symbol *sym = visited_symbols;
788
Andreas Gruenbacher5dae9a52008-12-01 14:21:03 -0800789 if (sym->is_override)
790 fputs("override ", dumpfile);
Michal Marek7ec8eda2011-01-18 16:28:06 +0100791 if (symbol_types[sym->type].n) {
792 putc(symbol_types[sym->type].n, dumpfile);
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200793 putc('#', dumpfile);
794 }
795 fputs(sym->name, dumpfile);
796 putc(' ', dumpfile);
Andreas Gruenbacher3b40d382008-07-21 04:28:25 +0200797 if (sym->is_extern)
798 fputs("extern ", dumpfile);
Andreas Gruenbacher15fde672006-05-09 20:37:30 +0200799 print_list(dumpfile, sym->defn);
800 putc('\n', dumpfile);
801
802 visited_symbols = sym->visited;
803 sym->visited = NULL;
804 }
805 }
806
Sam Ravnborg78c041532006-03-12 22:59:36 +0100807 if (flag_debug) {
808 fprintf(debugfile, "Hash table occupancy %d/%d = %g\n",
809 nsyms, HASH_BUCKETS,
810 (double)nsyms / (double)HASH_BUCKETS);
811 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812
Sam Ravnborg78c041532006-03-12 22:59:36 +0100813 return errors != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814}