Blame view

scripts/kallsyms.c 18.8 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
  /* Generate assembler source containing symbol information
   *
   * Copyright 2002       by Kai Germaschewski
   *
   * This software may be used and distributed according to the terms
   * of the GNU General Public License, incorporated herein by reference.
   *
   * Usage: nm -n vmlinux | scripts/kallsyms [--all-symbols] > symbols.S
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
   *      Table compression uses all the unused char codes on the symbols and
   *  maps these to the most used substrings (tokens). For instance, it might
   *  map char code 0xF7 to represent "write_" and then in every symbol where
   *  "write_" appears it can be replaced by 0xF7, saving 5 bytes.
   *      The used codes themselves are also placed in the table so that the
   *  decompresion can work without "special cases".
   *      Applied to kernel symbols, this usually produces a compression ratio
   *  of about 50%.
   *
   */
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <ctype.h>
2213e9a66   Ard Biesheuvel   kallsyms: add sup...
25
  #include <limits.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
26

17b1f0de7   Mike Frysinger   kallsyms: general...
27
28
29
  #ifndef ARRAY_SIZE
  #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
  #endif
9281acea6   Tejun Heo   kallsyms: make KS...
30
  #define KSYM_NAME_LEN		128
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
33
  struct sym_entry {
  	unsigned long long addr;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
34
  	unsigned int len;
f2df3f65d   Paulo Marques   kallsyms should p...
35
  	unsigned int start_pos;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
  	unsigned char *sym;
8c996940b   Ard Biesheuvel   kallsyms: don't o...
37
  	unsigned int percpu_absolute;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
  };
78eb71594   Kees Cook   kallsyms: general...
39
40
  struct addr_range {
  	const char *start_sym, *end_sym;
17b1f0de7   Mike Frysinger   kallsyms: general...
41
42
43
44
  	unsigned long long start, end;
  };
  
  static unsigned long long _text;
2213e9a66   Ard Biesheuvel   kallsyms: add sup...
45
  static unsigned long long relative_base;
78eb71594   Kees Cook   kallsyms: general...
46
  static struct addr_range text_ranges[] = {
17b1f0de7   Mike Frysinger   kallsyms: general...
47
48
49
50
51
52
53
  	{ "_stext",     "_etext"     },
  	{ "_sinittext", "_einittext" },
  	{ "_stext_l1",  "_etext_l1"  },	/* Blackfin on-chip L1 inst SRAM */
  	{ "_stext_l2",  "_etext_l2"  },	/* Blackfin on-chip L2 SRAM */
  };
  #define text_range_text     (&text_ranges[0])
  #define text_range_inittext (&text_ranges[1])
c6bda7c98   Rusty Russell   kallsyms: fix per...
54
55
56
  static struct addr_range percpu_range = {
  	"__per_cpu_start", "__per_cpu_end", -1ULL, 0
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
  static struct sym_entry *table;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
58
  static unsigned int table_size, table_cnt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
  static int all_symbols = 0;
c6bda7c98   Rusty Russell   kallsyms: fix per...
60
  static int absolute_percpu = 0;
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
61
  static char symbol_prefix_char = '\0';
2213e9a66   Ard Biesheuvel   kallsyms: add sup...
62
  static int base_relative = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63

b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
64
  int token_profit[0x10000];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
66
  
  /* the table that holds the result of the compression */
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
67
  unsigned char best_table[256][2];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
  unsigned char best_table_len[256];
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
69
  static void usage(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
  {
f6537f2f0   Ming Lei   scripts/kallsyms:...
71
72
  	fprintf(stderr, "Usage: kallsyms [--all-symbols] "
  			"[--symbol-prefix=<prefix char>] "
2213e9a66   Ard Biesheuvel   kallsyms: add sup...
73
74
  			"[--base-relative] < in.map > out.S
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
78
79
80
81
  	exit(1);
  }
  
  /*
   * This ignores the intensely annoying "mapping symbols" found
   * in ARM ELF files: $a, $t and $d.
   */
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
82
  static inline int is_arm_mapping_symbol(const char *str)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
  {
6c34f1f54   Kyle McMartin   aarch64: filter $...
84
  	return str[0] == '$' && strchr("axtd", str[1])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
  	       && (str[2] == '\0' || str[2] == '.');
  }
78eb71594   Kees Cook   kallsyms: general...
87
88
  static int check_symbol_range(const char *sym, unsigned long long addr,
  			      struct addr_range *ranges, int entries)
17b1f0de7   Mike Frysinger   kallsyms: general...
89
90
  {
  	size_t i;
78eb71594   Kees Cook   kallsyms: general...
91
  	struct addr_range *ar;
17b1f0de7   Mike Frysinger   kallsyms: general...
92

78eb71594   Kees Cook   kallsyms: general...
93
94
  	for (i = 0; i < entries; ++i) {
  		ar = &ranges[i];
17b1f0de7   Mike Frysinger   kallsyms: general...
95

78eb71594   Kees Cook   kallsyms: general...
96
97
  		if (strcmp(sym, ar->start_sym) == 0) {
  			ar->start = addr;
17b1f0de7   Mike Frysinger   kallsyms: general...
98
  			return 0;
78eb71594   Kees Cook   kallsyms: general...
99
100
  		} else if (strcmp(sym, ar->end_sym) == 0) {
  			ar->end = addr;
17b1f0de7   Mike Frysinger   kallsyms: general...
101
102
103
104
105
106
  			return 0;
  		}
  	}
  
  	return 1;
  }
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
107
  static int read_symbol(FILE *in, struct sym_entry *s)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
  {
  	char str[500];
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
110
  	char *sym, stype;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
  	int rc;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
112
113
  	rc = fscanf(in, "%llx %c %499s
  ", &s->addr, &stype, str);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
  	if (rc != 3) {
ef894870c   Jean Sacren   scripts/kallsyms:...
115
116
117
  		if (rc != EOF && fgets(str, 500, in) == NULL)
  			fprintf(stderr, "Read error or end of file.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
  		return -1;
  	}
f3462aa95   Andi Kleen   Kbuild: Handle lo...
120
  	if (strlen(str) > KSYM_NAME_LEN) {
6f62259b1   Fabio Estevam   scripts: kallsyms...
121
122
  		fprintf(stderr, "Symbol %s too long for kallsyms (%zu vs %d).
  "
bb66fc671   Masahiro Yamada   kbuild: trivial -...
123
124
  				"Please increase KSYM_NAME_LEN both in kernel and kallsyms.c
  ",
f3462aa95   Andi Kleen   Kbuild: Handle lo...
125
126
127
  			str, strlen(str), KSYM_NAME_LEN);
  		return -1;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128

41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
129
130
131
132
  	sym = str;
  	/* skip prefix char */
  	if (symbol_prefix_char && str[0] == symbol_prefix_char)
  		sym++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
  	/* Ignore most absolute/undefined (?) symbols. */
fd593d127   Eric W. Biederman   [PATCH] relocatab...
134
135
  	if (strcmp(sym, "_text") == 0)
  		_text = s->addr;
78eb71594   Kees Cook   kallsyms: general...
136
137
  	else if (check_symbol_range(sym, s->addr, text_ranges,
  				    ARRAY_SIZE(text_ranges)) == 0)
17b1f0de7   Mike Frysinger   kallsyms: general...
138
  		/* nothing to do */;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
139
  	else if (toupper(stype) == 'A')
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
  	{
  		/* Keep these useful absolute symbols */
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
142
143
144
145
  		if (strcmp(sym, "__kernel_syscall_via_break") &&
  		    strcmp(sym, "__kernel_syscall_via_epc") &&
  		    strcmp(sym, "__kernel_sigtramp") &&
  		    strcmp(sym, "__gp"))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
146
147
148
  			return -1;
  
  	}
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
149
  	else if (toupper(stype) == 'U' ||
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
150
  		 is_arm_mapping_symbol(sym))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
  		return -1;
6f00df24e   Ralf Baechle   [PATCH] Strip loc...
152
153
154
  	/* exclude also MIPS ELF local symbols ($L123 instead of .L123) */
  	else if (str[0] == '$')
  		return -1;
aab34ac85   Sam Ravnborg   kbuild: filter aw...
155
  	/* exclude debugging symbols */
51962a9d4   Guenter Roeck   scripts/kallsyms....
156
  	else if (stype == 'N' || stype == 'n')
aab34ac85   Sam Ravnborg   kbuild: filter aw...
157
  		return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
159
160
161
  
  	/* include the type field in the symbol name, so that it gets
  	 * compressed together */
  	s->len = strlen(str) + 1;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
162
  	s->sym = malloc(s->len + 1);
f1a136e0d   Jesper Juhl   [PATCH] kallsyms:...
163
164
165
166
167
168
  	if (!s->sym) {
  		fprintf(stderr, "kallsyms failure: "
  			"unable to allocate required amount of memory
  ");
  		exit(EXIT_FAILURE);
  	}
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
169
170
  	strcpy((char *)s->sym + 1, str);
  	s->sym[0] = stype;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171

8c996940b   Ard Biesheuvel   kallsyms: don't o...
172
  	s->percpu_absolute = 0;
c6bda7c98   Rusty Russell   kallsyms: fix per...
173
174
  	/* Record if we've found __per_cpu_start/end. */
  	check_symbol_range(sym, s->addr, &percpu_range, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
  	return 0;
  }
78eb71594   Kees Cook   kallsyms: general...
177
178
  static int symbol_in_range(struct sym_entry *s, struct addr_range *ranges,
  			   int entries)
17b1f0de7   Mike Frysinger   kallsyms: general...
179
180
  {
  	size_t i;
78eb71594   Kees Cook   kallsyms: general...
181
  	struct addr_range *ar;
17b1f0de7   Mike Frysinger   kallsyms: general...
182

78eb71594   Kees Cook   kallsyms: general...
183
184
  	for (i = 0; i < entries; ++i) {
  		ar = &ranges[i];
17b1f0de7   Mike Frysinger   kallsyms: general...
185

78eb71594   Kees Cook   kallsyms: general...
186
  		if (s->addr >= ar->start && s->addr <= ar->end)
ac6ca5c86   Mike Frysinger   kallsyms: fix inv...
187
  			return 1;
17b1f0de7   Mike Frysinger   kallsyms: general...
188
  	}
ac6ca5c86   Mike Frysinger   kallsyms: fix inv...
189
  	return 0;
17b1f0de7   Mike Frysinger   kallsyms: general...
190
  }
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
191
  static int symbol_valid(struct sym_entry *s)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
  {
  	/* Symbols which vary between passes.  Passes 1 and 2 must have
2ea038917   Sam Ravnborg   Revert "kbuild: s...
194
195
196
  	 * identical symbol lists.  The kallsyms_* symbols below are only added
  	 * after pass 1, they would be included in pass 2 when --all-symbols is
  	 * specified so exclude them to get a stable symbol list.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
  	 */
  	static char *special_symbols[] = {
2ea038917   Sam Ravnborg   Revert "kbuild: s...
199
  		"kallsyms_addresses",
2213e9a66   Ard Biesheuvel   kallsyms: add sup...
200
201
  		"kallsyms_offsets",
  		"kallsyms_relative_base",
2ea038917   Sam Ravnborg   Revert "kbuild: s...
202
203
204
205
206
  		"kallsyms_num_syms",
  		"kallsyms_names",
  		"kallsyms_markers",
  		"kallsyms_token_table",
  		"kallsyms_token_index",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
208
209
210
  	/* Exclude linker generated symbols which vary between passes */
  		"_SDA_BASE_",		/* ppc */
  		"_SDA2_BASE_",		/* ppc */
  		NULL };
bd8b22d28   Ard Biesheuvel   Kbuild: kallsyms:...
211

56067812d   Ard Biesheuvel   kbuild: modversio...
212
213
  	static char *special_prefixes[] = {
  		"__crc_",		/* modversions */
28493b414   Ard Biesheuvel   scripts/kallsyms:...
214
  		"__efistub_",		/* arm64 EFI stub namespace */
56067812d   Ard Biesheuvel   kbuild: modversio...
215
  		NULL };
bd8b22d28   Ard Biesheuvel   Kbuild: kallsyms:...
216
  	static char *special_suffixes[] = {
bd8b22d28   Ard Biesheuvel   Kbuild: kallsyms:...
217
  		"_veneer",		/* arm */
b9b74be16   Ard Biesheuvel   ARM: 8555/1: kall...
218
219
  		"_from_arm",		/* arm */
  		"_from_thumb",		/* arm */
bd8b22d28   Ard Biesheuvel   Kbuild: kallsyms:...
220
  		NULL };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
  	int i;
bd8b22d28   Ard Biesheuvel   Kbuild: kallsyms:...
222
  	char *sym_name = (char *)s->sym + 1;
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
223
  	/* skip prefix char */
bd8b22d28   Ard Biesheuvel   Kbuild: kallsyms:...
224
225
  	if (symbol_prefix_char && *sym_name == symbol_prefix_char)
  		sym_name++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
227
228
229
  
  	/* if --all-symbols is not specified, then symbols outside the text
  	 * and inittext sections are discarded */
  	if (!all_symbols) {
78eb71594   Kees Cook   kallsyms: general...
230
231
  		if (symbol_in_range(s, text_ranges,
  				    ARRAY_SIZE(text_ranges)) == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
  			return 0;
  		/* Corner case.  Discard any symbols with the same value as
a3b81113f   Robin Getz   remove support fo...
234
235
236
237
  		 * _etext _einittext; they can move between pass 1 and 2 when
  		 * the kallsyms data are added.  If these symbols move then
  		 * they may get dropped in pass 2, which breaks the kallsyms
  		 * rules.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
  		 */
17b1f0de7   Mike Frysinger   kallsyms: general...
239
  		if ((s->addr == text_range_text->end &&
bd8b22d28   Ard Biesheuvel   Kbuild: kallsyms:...
240
  				strcmp(sym_name,
78eb71594   Kees Cook   kallsyms: general...
241
  				       text_range_text->end_sym)) ||
17b1f0de7   Mike Frysinger   kallsyms: general...
242
  		    (s->addr == text_range_inittext->end &&
bd8b22d28   Ard Biesheuvel   Kbuild: kallsyms:...
243
  				strcmp(sym_name,
78eb71594   Kees Cook   kallsyms: general...
244
  				       text_range_inittext->end_sym)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
247
248
  			return 0;
  	}
  
  	/* Exclude symbols which vary between passes. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
  	for (i = 0; special_symbols[i]; i++)
bd8b22d28   Ard Biesheuvel   Kbuild: kallsyms:...
250
  		if (strcmp(sym_name, special_symbols[i]) == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
  			return 0;
56067812d   Ard Biesheuvel   kbuild: modversio...
252
253
254
255
256
257
258
  	for (i = 0; special_prefixes[i]; i++) {
  		int l = strlen(special_prefixes[i]);
  
  		if (l <= strlen(sym_name) &&
  		    strncmp(sym_name, special_prefixes[i], l) == 0)
  			return 0;
  	}
bd8b22d28   Ard Biesheuvel   Kbuild: kallsyms:...
259
260
261
262
263
264
  	for (i = 0; special_suffixes[i]; i++) {
  		int l = strlen(sym_name) - strlen(special_suffixes[i]);
  
  		if (l >= 0 && strcmp(sym_name + l, special_suffixes[i]) == 0)
  			return 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
  	return 1;
  }
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
267
  static void read_map(FILE *in)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268
269
  {
  	while (!feof(in)) {
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
270
271
272
  		if (table_cnt >= table_size) {
  			table_size += 10000;
  			table = realloc(table, sizeof(*table) * table_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
275
276
277
278
  			if (!table) {
  				fprintf(stderr, "out of memory
  ");
  				exit (1);
  			}
  		}
f2df3f65d   Paulo Marques   kallsyms should p...
279
280
  		if (read_symbol(in, &table[table_cnt]) == 0) {
  			table[table_cnt].start_pos = table_cnt;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
281
  			table_cnt++;
f2df3f65d   Paulo Marques   kallsyms should p...
282
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
285
286
287
  	}
  }
  
  static void output_label(char *label)
  {
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
288
289
290
291
292
293
  	if (symbol_prefix_char)
  		printf(".globl %c%s
  ", symbol_prefix_char, label);
  	else
  		printf(".globl %s
  ", label);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
295
  	printf("\tALGN
  ");
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
296
297
298
299
300
301
  	if (symbol_prefix_char)
  		printf("%c%s:
  ", symbol_prefix_char, label);
  	else
  		printf("%s:
  ", label);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
  }
  
  /* uncompress a compressed symbol. When this function is called, the best table
   * might still be compressed itself, so the function needs to be recursive */
  static int expand_symbol(unsigned char *data, int len, char *result)
  {
  	int c, rlen, total=0;
  
  	while (len) {
  		c = *data;
  		/* if the table holds a single char that is the same as the one
  		 * we are looking for, then end the search */
  		if (best_table[c][0]==c && best_table_len[c]==1) {
  			*result++ = c;
  			total++;
  		} else {
  			/* if not, recurse and expand */
  			rlen = expand_symbol(best_table[c], best_table_len[c], result);
  			total += rlen;
  			result += rlen;
  		}
  		data++;
  		len--;
  	}
  	*result=0;
  
  	return total;
  }
78eb71594   Kees Cook   kallsyms: general...
330
331
  static int symbol_absolute(struct sym_entry *s)
  {
8c996940b   Ard Biesheuvel   kallsyms: don't o...
332
  	return s->percpu_absolute;
78eb71594   Kees Cook   kallsyms: general...
333
  }
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
334
  static void write_src(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
  {
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
336
  	unsigned int i, k, off;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
337
338
  	unsigned int best_idx[256];
  	unsigned int *markers;
9281acea6   Tejun Heo   kallsyms: make KS...
339
  	char buf[KSYM_NAME_LEN];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
  
  	printf("#include <asm/types.h>
  ");
  	printf("#if BITS_PER_LONG == 64
  ");
  	printf("#define PTR .quad
  ");
  	printf("#define ALGN .align 8
  ");
  	printf("#else
  ");
  	printf("#define PTR .long
  ");
  	printf("#define ALGN .align 4
  ");
  	printf("#endif
  ");
aad094701   Jan Beulich   [PATCH] move kall...
357
358
  	printf("\t.section .rodata, \"a\"
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359

2213e9a66   Ard Biesheuvel   kallsyms: add sup...
360
361
362
363
364
365
366
367
368
369
370
371
  	/* Provide proper symbols relocatability by their relativeness
  	 * to a fixed anchor point in the runtime image, either '_text'
  	 * for absolute address tables, in which case the linker will
  	 * emit the final addresses at build time. Otherwise, use the
  	 * offset relative to the lowest value encountered of all relative
  	 * symbols, and emit non-relocatable fixed offsets that will be fixed
  	 * up at runtime.
  	 *
  	 * The symbol names cannot be used to construct normal symbol
  	 * references as the list of symbols contains symbols that are
  	 * declared static and are private to their .o files.  This prevents
  	 * .tmp_kallsyms.o or any other object from referencing them.
fd593d127   Eric W. Biederman   [PATCH] relocatab...
372
  	 */
2213e9a66   Ard Biesheuvel   kallsyms: add sup...
373
374
375
376
  	if (!base_relative)
  		output_label("kallsyms_addresses");
  	else
  		output_label("kallsyms_offsets");
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
377
  	for (i = 0; i < table_cnt; i++) {
2213e9a66   Ard Biesheuvel   kallsyms: add sup...
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
  		if (base_relative) {
  			long long offset;
  			int overflow;
  
  			if (!absolute_percpu) {
  				offset = table[i].addr - relative_base;
  				overflow = (offset < 0 || offset > UINT_MAX);
  			} else if (symbol_absolute(&table[i])) {
  				offset = table[i].addr;
  				overflow = (offset < 0 || offset > INT_MAX);
  			} else {
  				offset = relative_base - table[i].addr - 1;
  				overflow = (offset < INT_MIN || offset >= 0);
  			}
  			if (overflow) {
  				fprintf(stderr, "kallsyms failure: "
  					"%s symbol value %#llx out of range in relative mode
  ",
  					symbol_absolute(&table[i]) ? "absolute" : "relative",
  					table[i].addr);
  				exit(EXIT_FAILURE);
  			}
  			printf("\t.long\t%#x
  ", (int)offset);
  		} else if (!symbol_absolute(&table[i])) {
2c22d8baa   Vivek Goyal   [PATCH] relocatab...
403
404
405
406
407
  			if (_text <= table[i].addr)
  				printf("\tPTR\t_text + %#llx
  ",
  					table[i].addr - _text);
  			else
2930ffc75   Andrew Morton   revert "kallsyms:...
408
409
410
  				printf("\tPTR\t_text - %#llx
  ",
  					_text - table[i].addr);
fd593d127   Eric W. Biederman   [PATCH] relocatab...
411
412
413
414
  		} else {
  			printf("\tPTR\t%#llx
  ", table[i].addr);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
415
416
417
  	}
  	printf("
  ");
2213e9a66   Ard Biesheuvel   kallsyms: add sup...
418
419
420
421
422
423
424
  	if (base_relative) {
  		output_label("kallsyms_relative_base");
  		printf("\tPTR\t_text - %#llx
  ", _text - relative_base);
  		printf("
  ");
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
  	output_label("kallsyms_num_syms");
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
426
427
  	printf("\tPTR\t%d
  ", table_cnt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
428
429
430
431
432
  	printf("
  ");
  
  	/* table of offset markers, that give the offset in the compressed stream
  	 * every 256 symbols */
f1a136e0d   Jesper Juhl   [PATCH] kallsyms:...
433
434
435
436
437
438
439
  	markers = malloc(sizeof(unsigned int) * ((table_cnt + 255) / 256));
  	if (!markers) {
  		fprintf(stderr, "kallsyms failure: "
  			"unable to allocate required memory
  ");
  		exit(EXIT_FAILURE);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440
441
  
  	output_label("kallsyms_names");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
  	off = 0;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
443
444
445
  	for (i = 0; i < table_cnt; i++) {
  		if ((i & 0xFF) == 0)
  			markers[i >> 8] = off;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
447
448
449
450
451
452
453
  
  		printf("\t.byte 0x%02x", table[i].len);
  		for (k = 0; k < table[i].len; k++)
  			printf(", 0x%02x", table[i].sym[k]);
  		printf("
  ");
  
  		off += table[i].len + 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
456
457
458
  	}
  	printf("
  ");
  
  	output_label("kallsyms_markers");
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
459
  	for (i = 0; i < ((table_cnt + 255) >> 8); i++)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
461
462
463
464
465
466
467
468
469
470
  		printf("\tPTR\t%d
  ", markers[i]);
  	printf("
  ");
  
  	free(markers);
  
  	output_label("kallsyms_token_table");
  	off = 0;
  	for (i = 0; i < 256; i++) {
  		best_idx[i] = off;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
471
  		expand_symbol(best_table[i], best_table_len[i], buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
  		printf("\t.asciz\t\"%s\"
  ", buf);
  		off += strlen(buf) + 1;
  	}
  	printf("
  ");
  
  	output_label("kallsyms_token_index");
  	for (i = 0; i < 256; i++)
  		printf("\t.short\t%d
  ", best_idx[i]);
  	printf("
  ");
  }
  
  
  /* table lookup compression functions */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
491
492
493
494
  /* count all the possible tokens in a symbol */
  static void learn_symbol(unsigned char *symbol, int len)
  {
  	int i;
  
  	for (i = 0; i < len - 1; i++)
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
495
  		token_profit[ symbol[i] + (symbol[i + 1] << 8) ]++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496
497
498
499
500
501
502
503
  }
  
  /* decrease the count for all the possible tokens in a symbol */
  static void forget_symbol(unsigned char *symbol, int len)
  {
  	int i;
  
  	for (i = 0; i < len - 1; i++)
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
504
  		token_profit[ symbol[i] + (symbol[i + 1] << 8) ]--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
  }
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
506
  /* remove all the invalid symbols from the table and do the initial token count */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507
508
  static void build_initial_tok_table(void)
  {
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
509
  	unsigned int i, pos;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510

b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
511
512
  	pos = 0;
  	for (i = 0; i < table_cnt; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
  		if ( symbol_valid(&table[i]) ) {
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
514
515
516
517
  			if (pos != i)
  				table[pos] = table[i];
  			learn_symbol(table[pos].sym, table[pos].len);
  			pos++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
  	}
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
520
  	table_cnt = pos;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
521
  }
7c5d249ad   Paulo Marques   kallsyms: remove ...
522
523
524
525
526
527
528
529
530
531
  static void *find_token(unsigned char *str, int len, unsigned char *token)
  {
  	int i;
  
  	for (i = 0; i < len - 1; i++) {
  		if (str[i] == token[0] && str[i+1] == token[1])
  			return &str[i];
  	}
  	return NULL;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
532
533
  /* replace a given token in all the valid symbols. Use the sampled symbols
   * to update the counts */
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
534
  static void compress_symbols(unsigned char *str, int idx)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
  {
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
536
537
  	unsigned int i, len, size;
  	unsigned char *p1, *p2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
538

b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
539
  	for (i = 0; i < table_cnt; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
541
  
  		len = table[i].len;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
542
543
544
  		p1 = table[i].sym;
  
  		/* find the token on the symbol */
7c5d249ad   Paulo Marques   kallsyms: remove ...
545
  		p2 = find_token(p1, len, str);
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
546
547
548
549
550
551
  		if (!p2) continue;
  
  		/* decrease the counts for this symbol's tokens */
  		forget_symbol(table[i].sym, len);
  
  		size = len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
553
  
  		do {
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
554
555
556
557
558
559
560
561
  			*p2 = idx;
  			p2++;
  			size -= (p2 - p1);
  			memmove(p2, p2 + 1, size);
  			p1 = p2;
  			len--;
  
  			if (size < 2) break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
  			/* find the token on the symbol */
7c5d249ad   Paulo Marques   kallsyms: remove ...
563
  			p2 = find_token(p1, size, str);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564

b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
565
  		} while (p2);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566

b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
567
  		table[i].len = len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568

b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
569
570
  		/* increase the counts for this symbol's new tokens */
  		learn_symbol(table[i].sym, len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
571
572
573
574
  	}
  }
  
  /* search the token with the maximum profit */
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
575
  static int find_best_token(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
576
  {
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
577
  	int i, best, bestprofit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
578
579
  
  	bestprofit=-10000;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
580
  	best = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581

b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
582
583
584
585
  	for (i = 0; i < 0x10000; i++) {
  		if (token_profit[i] > bestprofit) {
  			best = i;
  			bestprofit = token_profit[i];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
587
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
589
590
591
592
593
  	return best;
  }
  
  /* this is the core of the algorithm: calculate the "best" table */
  static void optimize_result(void)
  {
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
594
  	int i, best;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595
596
597
598
599
600
601
602
603
604
605
  
  	/* using the '\0' symbol last allows compress_symbols to use standard
  	 * fast string functions */
  	for (i = 255; i >= 0; i--) {
  
  		/* if this table slot is empty (it is not used by an actual
  		 * original char code */
  		if (!best_table_len[i]) {
  
  			/* find the token with the breates profit value */
  			best = find_best_token();
e0a04b11e   Xiaochen Wang   scripts/kallsyms....
606
607
  			if (token_profit[best] == 0)
  				break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
608
609
  
  			/* place it in the "best" table */
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
610
611
612
  			best_table_len[i] = 2;
  			best_table[i][0] = best & 0xFF;
  			best_table[i][1] = (best >> 8) & 0xFF;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
613
614
  
  			/* replace this token in all the valid symbols */
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
615
  			compress_symbols(best_table[i], i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616
617
618
619
620
621
622
  		}
  	}
  }
  
  /* start by placing the symbols that are actually used on the table */
  static void insert_real_symbols_in_table(void)
  {
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
623
  	unsigned int i, j, c;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
624
625
626
  
  	memset(best_table, 0, sizeof(best_table));
  	memset(best_table_len, 0, sizeof(best_table_len));
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
627
628
629
630
631
  	for (i = 0; i < table_cnt; i++) {
  		for (j = 0; j < table[i].len; j++) {
  			c = table[i].sym[j];
  			best_table[c][0]=c;
  			best_table_len[c]=1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
632
633
634
635
636
637
  		}
  	}
  }
  
  static void optimize_token_table(void)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
640
  	build_initial_tok_table();
  
  	insert_real_symbols_in_table();
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
641
  	/* When valid symbol is not registered, exit to error */
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
642
  	if (!table_cnt) {
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
643
644
645
646
  		fprintf(stderr, "No valid symbol.
  ");
  		exit(1);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
648
  	optimize_result();
  }
b478b782e   Lai Jiangshan   kallsyms, tracing...
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
  /* guess for "linker script provide" symbol */
  static int may_be_linker_script_provide_symbol(const struct sym_entry *se)
  {
  	const char *symbol = (char *)se->sym + 1;
  	int len = se->len - 1;
  
  	if (len < 8)
  		return 0;
  
  	if (symbol[0] != '_' || symbol[1] != '_')
  		return 0;
  
  	/* __start_XXXXX */
  	if (!memcmp(symbol + 2, "start_", 6))
  		return 1;
  
  	/* __stop_XXXXX */
  	if (!memcmp(symbol + 2, "stop_", 5))
  		return 1;
  
  	/* __end_XXXXX */
  	if (!memcmp(symbol + 2, "end_", 4))
  		return 1;
  
  	/* __XXXXX_start */
  	if (!memcmp(symbol + len - 6, "_start", 6))
  		return 1;
  
  	/* __XXXXX_end */
  	if (!memcmp(symbol + len - 4, "_end", 4))
  		return 1;
  
  	return 0;
  }
  
  static int prefix_underscores_count(const char *str)
  {
  	const char *tail = str;
a9ece53c4   Paul Mundt   kallsyms: fix seg...
687
  	while (*tail == '_')
b478b782e   Lai Jiangshan   kallsyms, tracing...
688
689
690
691
  		tail++;
  
  	return tail - str;
  }
f2df3f65d   Paulo Marques   kallsyms should p...
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
  static int compare_symbols(const void *a, const void *b)
  {
  	const struct sym_entry *sa;
  	const struct sym_entry *sb;
  	int wa, wb;
  
  	sa = a;
  	sb = b;
  
  	/* sort by address first */
  	if (sa->addr > sb->addr)
  		return 1;
  	if (sa->addr < sb->addr)
  		return -1;
  
  	/* sort by "weakness" type */
  	wa = (sa->sym[0] == 'w') || (sa->sym[0] == 'W');
  	wb = (sb->sym[0] == 'w') || (sb->sym[0] == 'W');
  	if (wa != wb)
  		return wa - wb;
b478b782e   Lai Jiangshan   kallsyms, tracing...
712
713
714
715
716
717
718
719
720
721
722
  	/* sort by "linker script provide" type */
  	wa = may_be_linker_script_provide_symbol(sa);
  	wb = may_be_linker_script_provide_symbol(sb);
  	if (wa != wb)
  		return wa - wb;
  
  	/* sort by the number of prefix underscores */
  	wa = prefix_underscores_count((const char *)sa->sym + 1);
  	wb = prefix_underscores_count((const char *)sb->sym + 1);
  	if (wa != wb)
  		return wa - wb;
f2df3f65d   Paulo Marques   kallsyms should p...
723
724
725
726
727
728
729
730
  	/* sort by initial order, so that other symbols are left undisturbed */
  	return sa->start_pos - sb->start_pos;
  }
  
  static void sort_symbols(void)
  {
  	qsort(table, table_cnt, sizeof(struct sym_entry), compare_symbols);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
731

c6bda7c98   Rusty Russell   kallsyms: fix per...
732
733
734
735
736
  static void make_percpus_absolute(void)
  {
  	unsigned int i;
  
  	for (i = 0; i < table_cnt; i++)
8c996940b   Ard Biesheuvel   kallsyms: don't o...
737
738
739
740
741
742
  		if (symbol_in_range(&table[i], &percpu_range, 1)) {
  			/*
  			 * Keep the 'A' override for percpu symbols to
  			 * ensure consistent behavior compared to older
  			 * versions of this tool.
  			 */
c6bda7c98   Rusty Russell   kallsyms: fix per...
743
  			table[i].sym[0] = 'A';
8c996940b   Ard Biesheuvel   kallsyms: don't o...
744
745
  			table[i].percpu_absolute = 1;
  		}
c6bda7c98   Rusty Russell   kallsyms: fix per...
746
  }
2213e9a66   Ard Biesheuvel   kallsyms: add sup...
747
748
749
750
751
752
753
754
755
756
757
  /* find the minimum non-absolute symbol address */
  static void record_relative_base(void)
  {
  	unsigned int i;
  
  	relative_base = -1ULL;
  	for (i = 0; i < table_cnt; i++)
  		if (!symbol_absolute(&table[i]) &&
  		    table[i].addr < relative_base)
  			relative_base = table[i].addr;
  }
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
758
  int main(int argc, char **argv)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
759
  {
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
760
761
762
763
764
  	if (argc >= 2) {
  		int i;
  		for (i = 1; i < argc; i++) {
  			if(strcmp(argv[i], "--all-symbols") == 0)
  				all_symbols = 1;
c6bda7c98   Rusty Russell   kallsyms: fix per...
765
766
  			else if (strcmp(argv[i], "--absolute-percpu") == 0)
  				absolute_percpu = 1;
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
767
768
769
770
771
772
  			else if (strncmp(argv[i], "--symbol-prefix=", 16) == 0) {
  				char *p = &argv[i][16];
  				/* skip quote */
  				if ((*p == '"' && *(p+2) == '"') || (*p == '\'' && *(p+2) == '\''))
  					p++;
  				symbol_prefix_char = *p;
2213e9a66   Ard Biesheuvel   kallsyms: add sup...
773
774
775
  			} else if (strcmp(argv[i], "--base-relative") == 0)
  				base_relative = 1;
  			else
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
776
777
778
  				usage();
  		}
  	} else if (argc != 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
779
780
781
  		usage();
  
  	read_map(stdin);
c6bda7c98   Rusty Russell   kallsyms: fix per...
782
783
  	if (absolute_percpu)
  		make_percpus_absolute();
2213e9a66   Ard Biesheuvel   kallsyms: add sup...
784
785
  	if (base_relative)
  		record_relative_base();
2ea038917   Sam Ravnborg   Revert "kbuild: s...
786
787
  	sort_symbols();
  	optimize_token_table();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
789
790
791
  	write_src();
  
  	return 0;
  }