Blame view

scripts/kallsyms.c 15.2 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>
17b1f0de7   Mike Frysinger   kallsyms: general...
25
26
27
  #ifndef ARRAY_SIZE
  #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
  #endif
9281acea6   Tejun Heo   kallsyms: make KS...
28
  #define KSYM_NAME_LEN		128
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
  struct sym_entry {
  	unsigned long long addr;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
32
  	unsigned int len;
f2df3f65d   Paulo Marques   kallsyms should p...
33
  	unsigned int start_pos;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
35
  	unsigned char *sym;
  };
17b1f0de7   Mike Frysinger   kallsyms: general...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  struct text_range {
  	const char *stext, *etext;
  	unsigned long long start, end;
  };
  
  static unsigned long long _text;
  static struct text_range text_ranges[] = {
  	{ "_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])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
  static struct sym_entry *table;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
51
  static unsigned int table_size, table_cnt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
  static int all_symbols = 0;
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
53
  static char symbol_prefix_char = '\0';
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54

b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
55
  int token_profit[0x10000];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
  
  /* the table that holds the result of the compression */
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
58
  unsigned char best_table[256][2];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
  unsigned char best_table_len[256];
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
60
  static void usage(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
  {
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
62
63
  	fprintf(stderr, "Usage: kallsyms [--all-symbols] [--symbol-prefix=<prefix char>] < in.map > out.S
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
65
66
67
68
69
70
  	exit(1);
  }
  
  /*
   * This ignores the intensely annoying "mapping symbols" found
   * in ARM ELF files: $a, $t and $d.
   */
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
71
  static inline int is_arm_mapping_symbol(const char *str)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
73
74
75
  {
  	return str[0] == '$' && strchr("atd", str[1])
  	       && (str[2] == '\0' || str[2] == '.');
  }
17b1f0de7   Mike Frysinger   kallsyms: general...
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
  static int read_symbol_tr(const char *sym, unsigned long long addr)
  {
  	size_t i;
  	struct text_range *tr;
  
  	for (i = 0; i < ARRAY_SIZE(text_ranges); ++i) {
  		tr = &text_ranges[i];
  
  		if (strcmp(sym, tr->stext) == 0) {
  			tr->start = addr;
  			return 0;
  		} else if (strcmp(sym, tr->etext) == 0) {
  			tr->end = addr;
  			return 0;
  		}
  	}
  
  	return 1;
  }
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
95
  static int read_symbol(FILE *in, struct sym_entry *s)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
  {
  	char str[500];
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
98
  	char *sym, stype;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
  	int rc;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
100
101
  	rc = fscanf(in, "%llx %c %499s
  ", &s->addr, &stype, str);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
  	if (rc != 3) {
ef894870c   Jean Sacren   scripts/kallsyms:...
103
104
105
  		if (rc != EOF && fgets(str, 500, in) == NULL)
  			fprintf(stderr, "Read error or end of file.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
  		return -1;
  	}
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
108
109
110
111
  	sym = str;
  	/* skip prefix char */
  	if (symbol_prefix_char && str[0] == symbol_prefix_char)
  		sym++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
  	/* Ignore most absolute/undefined (?) symbols. */
fd593d127   Eric W. Biederman   [PATCH] relocatab...
113
114
  	if (strcmp(sym, "_text") == 0)
  		_text = s->addr;
17b1f0de7   Mike Frysinger   kallsyms: general...
115
116
  	else if (read_symbol_tr(sym, s->addr) == 0)
  		/* nothing to do */;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
117
  	else if (toupper(stype) == 'A')
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
  	{
  		/* Keep these useful absolute symbols */
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
120
121
122
123
  		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
124
125
126
  			return -1;
  
  	}
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
127
  	else if (toupper(stype) == 'U' ||
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
128
  		 is_arm_mapping_symbol(sym))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
  		return -1;
6f00df24e   Ralf Baechle   [PATCH] Strip loc...
130
131
132
  	/* exclude also MIPS ELF local symbols ($L123 instead of .L123) */
  	else if (str[0] == '$')
  		return -1;
aab34ac85   Sam Ravnborg   kbuild: filter aw...
133
134
135
  	/* exclude debugging symbols */
  	else if (stype == 'N')
  		return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
139
  
  	/* include the type field in the symbol name, so that it gets
  	 * compressed together */
  	s->len = strlen(str) + 1;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
140
  	s->sym = malloc(s->len + 1);
f1a136e0d   Jesper Juhl   [PATCH] kallsyms:...
141
142
143
144
145
146
  	if (!s->sym) {
  		fprintf(stderr, "kallsyms failure: "
  			"unable to allocate required amount of memory
  ");
  		exit(EXIT_FAILURE);
  	}
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
147
148
  	strcpy((char *)s->sym + 1, str);
  	s->sym[0] = stype;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
  
  	return 0;
  }
17b1f0de7   Mike Frysinger   kallsyms: general...
152
153
154
155
156
157
158
  static int symbol_valid_tr(struct sym_entry *s)
  {
  	size_t i;
  	struct text_range *tr;
  
  	for (i = 0; i < ARRAY_SIZE(text_ranges); ++i) {
  		tr = &text_ranges[i];
ac6ca5c86   Mike Frysinger   kallsyms: fix inv...
159
160
  		if (s->addr >= tr->start && s->addr <= tr->end)
  			return 1;
17b1f0de7   Mike Frysinger   kallsyms: general...
161
  	}
ac6ca5c86   Mike Frysinger   kallsyms: fix inv...
162
  	return 0;
17b1f0de7   Mike Frysinger   kallsyms: general...
163
  }
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
164
  static int symbol_valid(struct sym_entry *s)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
166
  {
  	/* Symbols which vary between passes.  Passes 1 and 2 must have
2ea038917   Sam Ravnborg   Revert "kbuild: s...
167
168
169
  	 * 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
170
171
  	 */
  	static char *special_symbols[] = {
2ea038917   Sam Ravnborg   Revert "kbuild: s...
172
173
174
175
176
177
  		"kallsyms_addresses",
  		"kallsyms_num_syms",
  		"kallsyms_names",
  		"kallsyms_markers",
  		"kallsyms_token_table",
  		"kallsyms_token_index",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
179
180
181
182
  	/* Exclude linker generated symbols which vary between passes */
  		"_SDA_BASE_",		/* ppc */
  		"_SDA2_BASE_",		/* ppc */
  		NULL };
  	int i;
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
183
184
185
186
187
  	int offset = 1;
  
  	/* skip prefix char */
  	if (symbol_prefix_char && *(s->sym + 1) == symbol_prefix_char)
  		offset++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
190
191
  
  	/* if --all-symbols is not specified, then symbols outside the text
  	 * and inittext sections are discarded */
  	if (!all_symbols) {
17b1f0de7   Mike Frysinger   kallsyms: general...
192
  		if (symbol_valid_tr(s) == 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
194
  			return 0;
  		/* Corner case.  Discard any symbols with the same value as
a3b81113f   Robin Getz   remove support fo...
195
196
197
198
  		 * _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
199
  		 */
17b1f0de7   Mike Frysinger   kallsyms: general...
200
201
202
203
  		if ((s->addr == text_range_text->end &&
  				strcmp((char *)s->sym + offset, text_range_text->etext)) ||
  		    (s->addr == text_range_inittext->end &&
  				strcmp((char *)s->sym + offset, text_range_inittext->etext)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
204
205
206
207
  			return 0;
  	}
  
  	/* Exclude symbols which vary between passes. */
2ea038917   Sam Ravnborg   Revert "kbuild: s...
208
  	if (strstr((char *)s->sym + offset, "_compiled."))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
211
  		return 0;
  
  	for (i = 0; special_symbols[i]; i++)
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
212
  		if( strcmp((char *)s->sym + offset, special_symbols[i]) == 0 )
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
214
215
216
  			return 0;
  
  	return 1;
  }
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
217
  static void read_map(FILE *in)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
219
  {
  	while (!feof(in)) {
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
220
221
222
  		if (table_cnt >= table_size) {
  			table_size += 10000;
  			table = realloc(table, sizeof(*table) * table_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
224
225
226
227
228
  			if (!table) {
  				fprintf(stderr, "out of memory
  ");
  				exit (1);
  			}
  		}
f2df3f65d   Paulo Marques   kallsyms should p...
229
230
  		if (read_symbol(in, &table[table_cnt]) == 0) {
  			table[table_cnt].start_pos = table_cnt;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
231
  			table_cnt++;
f2df3f65d   Paulo Marques   kallsyms should p...
232
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
233
234
235
236
237
  	}
  }
  
  static void output_label(char *label)
  {
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
238
239
240
241
242
243
  	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
244
245
  	printf("\tALGN
  ");
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
246
247
248
249
250
251
  	if (symbol_prefix_char)
  		printf("%c%s:
  ", symbol_prefix_char, label);
  	else
  		printf("%s:
  ", label);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
  }
  
  /* 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;
  }
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
280
  static void write_src(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
  {
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
282
  	unsigned int i, k, off;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
  	unsigned int best_idx[256];
  	unsigned int *markers;
9281acea6   Tejun Heo   kallsyms: make KS...
285
  	char buf[KSYM_NAME_LEN];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
  
  	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...
303
304
  	printf("\t.section .rodata, \"a\"
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305

fd593d127   Eric W. Biederman   [PATCH] relocatab...
306
307
308
309
310
311
312
  	/* Provide proper symbols relocatability by their '_text'
  	 * relativeness.  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.
  	 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
  	output_label("kallsyms_addresses");
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
314
  	for (i = 0; i < table_cnt; i++) {
fd593d127   Eric W. Biederman   [PATCH] relocatab...
315
  		if (toupper(table[i].sym[0]) != 'A') {
2c22d8baa   Vivek Goyal   [PATCH] relocatab...
316
317
318
319
320
321
322
323
  			if (_text <= table[i].addr)
  				printf("\tPTR\t_text + %#llx
  ",
  					table[i].addr - _text);
  			else
  				printf("\tPTR\t_text - %#llx
  ",
  					_text - table[i].addr);
fd593d127   Eric W. Biederman   [PATCH] relocatab...
324
325
326
327
  		} else {
  			printf("\tPTR\t%#llx
  ", table[i].addr);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
329
330
331
332
  	}
  	printf("
  ");
  
  	output_label("kallsyms_num_syms");
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
333
334
  	printf("\tPTR\t%d
  ", table_cnt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
337
338
339
  	printf("
  ");
  
  	/* table of offset markers, that give the offset in the compressed stream
  	 * every 256 symbols */
f1a136e0d   Jesper Juhl   [PATCH] kallsyms:...
340
341
342
343
344
345
346
  	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
347
348
  
  	output_label("kallsyms_names");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
  	off = 0;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
350
351
352
  	for (i = 0; i < table_cnt; i++) {
  		if ((i & 0xFF) == 0)
  			markers[i >> 8] = off;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
354
355
356
357
358
359
360
  
  		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
361
362
363
364
365
  	}
  	printf("
  ");
  
  	output_label("kallsyms_markers");
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
366
  	for (i = 0; i < ((table_cnt + 255) >> 8); i++)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
368
369
370
371
372
373
374
375
376
377
  		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:...
378
  		expand_symbol(best_table[i], best_table_len[i], buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
  		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
396
397
398
399
400
401
  /* 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:...
402
  		token_profit[ symbol[i] + (symbol[i + 1] << 8) ]++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
404
405
406
407
408
409
410
  }
  
  /* 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:...
411
  		token_profit[ symbol[i] + (symbol[i + 1] << 8) ]--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
  }
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
413
  /* remove all the invalid symbols from the table and do the initial token count */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
415
  static void build_initial_tok_table(void)
  {
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
416
  	unsigned int i, pos;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417

b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
418
419
  	pos = 0;
  	for (i = 0; i < table_cnt; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
  		if ( symbol_valid(&table[i]) ) {
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
421
422
423
424
  			if (pos != i)
  				table[pos] = table[i];
  			learn_symbol(table[pos].sym, table[pos].len);
  			pos++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
426
  	}
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
427
  	table_cnt = pos;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
428
  }
7c5d249ad   Paulo Marques   kallsyms: remove ...
429
430
431
432
433
434
435
436
437
438
  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
439
440
  /* replace a given token in all the valid symbols. Use the sampled symbols
   * to update the counts */
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
441
  static void compress_symbols(unsigned char *str, int idx)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
  {
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
443
444
  	unsigned int i, len, size;
  	unsigned char *p1, *p2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445

b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
446
  	for (i = 0; i < table_cnt; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
448
  
  		len = table[i].len;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
449
450
451
  		p1 = table[i].sym;
  
  		/* find the token on the symbol */
7c5d249ad   Paulo Marques   kallsyms: remove ...
452
  		p2 = find_token(p1, len, str);
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
453
454
455
456
457
458
  		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
459
460
  
  		do {
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
461
462
463
464
465
466
467
468
  			*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
469
  			/* find the token on the symbol */
7c5d249ad   Paulo Marques   kallsyms: remove ...
470
  			p2 = find_token(p1, size, str);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
471

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

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

b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
476
477
  		/* increase the counts for this symbol's new tokens */
  		learn_symbol(table[i].sym, len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
478
479
480
481
  	}
  }
  
  /* search the token with the maximum profit */
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
482
  static int find_best_token(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
  {
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
484
  	int i, best, bestprofit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485
486
  
  	bestprofit=-10000;
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
487
  	best = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488

b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
489
490
491
492
  	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
493
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
496
497
498
499
500
  	return best;
  }
  
  /* this is the core of the algorithm: calculate the "best" table */
  static void optimize_result(void)
  {
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
501
  	int i, best;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
503
504
505
506
507
508
509
510
511
512
  
  	/* 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....
513
514
  			if (token_profit[best] == 0)
  				break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
516
  
  			/* place it in the "best" table */
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
517
518
519
  			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
520
521
  
  			/* replace this token in all the valid symbols */
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
522
  			compress_symbols(best_table[i], i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
524
525
526
527
528
529
  		}
  	}
  }
  
  /* 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:...
530
  	unsigned int i, j, c;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
531
532
533
  
  	memset(best_table, 0, sizeof(best_table));
  	memset(best_table_len, 0, sizeof(best_table_len));
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
534
535
536
537
538
  	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
539
540
541
542
543
544
  		}
  	}
  }
  
  static void optimize_token_table(void)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
546
547
  	build_initial_tok_table();
  
  	insert_real_symbols_in_table();
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
548
  	/* When valid symbol is not registered, exit to error */
b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
549
  	if (!table_cnt) {
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
550
551
552
553
  		fprintf(stderr, "No valid symbol.
  ");
  		exit(1);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
554
555
  	optimize_result();
  }
b478b782e   Lai Jiangshan   kallsyms, tracing...
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
  /* 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...
594
  	while (*tail == '_')
b478b782e   Lai Jiangshan   kallsyms, tracing...
595
596
597
598
  		tail++;
  
  	return tail - str;
  }
f2df3f65d   Paulo Marques   kallsyms should p...
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
  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...
619
620
621
622
623
624
625
626
627
628
629
  	/* 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...
630
631
632
633
634
635
636
637
  	/* 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
638

b3dbb4ecd   Paulo Marques   [PATCH] kallsyms:...
639
  int main(int argc, char **argv)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
  {
41f11a4fa   Yoshinori Sato   [PATCH] kallsyms ...
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
  	if (argc >= 2) {
  		int i;
  		for (i = 1; i < argc; i++) {
  			if(strcmp(argv[i], "--all-symbols") == 0)
  				all_symbols = 1;
  			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;
  			} else
  				usage();
  		}
  	} else if (argc != 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656
657
658
  		usage();
  
  	read_map(stdin);
2ea038917   Sam Ravnborg   Revert "kbuild: s...
659
660
  	sort_symbols();
  	optimize_token_table();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
661
662
663
664
  	write_src();
  
  	return 0;
  }