Blame view
scripts/kallsyms.c
17.5 KB
1da177e4c
|
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
|
10 11 12 13 14 15 16 17 18 19 |
* 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%. * */ |
a41333e06
|
20 |
#include <stdbool.h> |
1da177e4c
|
21 22 23 24 |
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> |
2213e9a66
|
25 |
#include <limits.h> |
1da177e4c
|
26 |
|
17b1f0de7
|
27 |
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) |
17b1f0de7
|
28 |
|
9281acea6
|
29 |
#define KSYM_NAME_LEN 128 |
1da177e4c
|
30 |
|
1da177e4c
|
31 32 |
struct sym_entry { unsigned long long addr; |
b3dbb4ecd
|
33 |
unsigned int len; |
f2df3f65d
|
34 |
unsigned int start_pos; |
8c996940b
|
35 |
unsigned int percpu_absolute; |
9d82973e0
|
36 |
unsigned char sym[]; |
1da177e4c
|
37 |
}; |
78eb71594
|
38 39 |
struct addr_range { const char *start_sym, *end_sym; |
17b1f0de7
|
40 41 42 43 |
unsigned long long start, end; }; static unsigned long long _text; |
2213e9a66
|
44 |
static unsigned long long relative_base; |
78eb71594
|
45 |
static struct addr_range text_ranges[] = { |
17b1f0de7
|
46 47 |
{ "_stext", "_etext" }, { "_sinittext", "_einittext" }, |
17b1f0de7
|
48 49 50 |
}; #define text_range_text (&text_ranges[0]) #define text_range_inittext (&text_ranges[1]) |
c6bda7c98
|
51 52 53 |
static struct addr_range percpu_range = { "__per_cpu_start", "__per_cpu_end", -1ULL, 0 }; |
8d6052699
|
54 |
static struct sym_entry **table; |
b3dbb4ecd
|
55 |
static unsigned int table_size, table_cnt; |
831362fc3
|
56 57 58 |
static int all_symbols; static int absolute_percpu; static int base_relative; |
1da177e4c
|
59 |
|
f43e9daac
|
60 |
static int token_profit[0x10000]; |
1da177e4c
|
61 62 |
/* the table that holds the result of the compression */ |
f43e9daac
|
63 64 |
static unsigned char best_table[256][2]; static unsigned char best_table_len[256]; |
1da177e4c
|
65 |
|
b3dbb4ecd
|
66 |
static void usage(void) |
1da177e4c
|
67 |
{ |
f6537f2f0
|
68 |
fprintf(stderr, "Usage: kallsyms [--all-symbols] " |
2213e9a66
|
69 70 |
"[--base-relative] < in.map > out.S "); |
1da177e4c
|
71 72 |
exit(1); } |
29e55ad3d
|
73 74 75 76 |
static char *sym_name(const struct sym_entry *s) { return (char *)s->sym + 1; } |
a41333e06
|
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
static bool is_ignored_symbol(const char *name, char type) { static const char * const ignored_symbols[] = { /* * Symbols which vary between passes. Passes 1 and 2 must have * 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. */ "kallsyms_addresses", "kallsyms_offsets", "kallsyms_relative_base", "kallsyms_num_syms", "kallsyms_names", "kallsyms_markers", "kallsyms_token_table", "kallsyms_token_index", /* Exclude linker generated symbols which vary between passes */ "_SDA_BASE_", /* ppc */ "_SDA2_BASE_", /* ppc */ NULL }; static const char * const ignored_prefixes[] = { |
97261e1e2
|
102 103 |
"$", /* local symbols for ARM, MIPS, etc. */ ".LASANPC", /* s390 kasan local symbols */ |
a41333e06
|
104 105 |
"__crc_", /* modversions */ "__efistub_", /* arm64 EFI stub namespace */ |
762171291
|
106 |
"__kvm_nvhe_", /* arm64 non-VHE KVM namespace */ |
a41333e06
|
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
NULL }; static const char * const ignored_suffixes[] = { "_from_arm", /* arm */ "_from_thumb", /* arm */ "_veneer", /* arm */ NULL }; const char * const *p; /* Exclude symbols which vary between passes. */ for (p = ignored_symbols; *p; p++) if (!strcmp(name, *p)) return true; for (p = ignored_prefixes; *p; p++) if (!strncmp(name, *p, strlen(*p))) return true; for (p = ignored_suffixes; *p; p++) { int l = strlen(name) - strlen(*p); if (l >= 0 && !strcmp(name + l, *p)) return true; } |
887df76de
|
134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
if (type == 'U' || type == 'u') return true; /* exclude debugging symbols */ if (type == 'N' || type == 'n') return true; if (toupper(type) == 'A') { /* Keep these useful absolute symbols */ if (strcmp(name, "__kernel_syscall_via_break") && strcmp(name, "__kernel_syscall_via_epc") && strcmp(name, "__kernel_sigtramp") && strcmp(name, "__gp")) return true; } |
a41333e06
|
148 149 |
return false; } |
b6233d0de
|
150 151 |
static void check_symbol_range(const char *sym, unsigned long long addr, struct addr_range *ranges, int entries) |
17b1f0de7
|
152 153 |
{ size_t i; |
78eb71594
|
154 |
struct addr_range *ar; |
17b1f0de7
|
155 |
|
78eb71594
|
156 157 |
for (i = 0; i < entries; ++i) { ar = &ranges[i]; |
17b1f0de7
|
158 |
|
78eb71594
|
159 160 |
if (strcmp(sym, ar->start_sym) == 0) { ar->start = addr; |
b6233d0de
|
161 |
return; |
78eb71594
|
162 163 |
} else if (strcmp(sym, ar->end_sym) == 0) { ar->end = addr; |
b6233d0de
|
164 |
return; |
17b1f0de7
|
165 166 |
} } |
17b1f0de7
|
167 |
} |
8d6052699
|
168 |
static struct sym_entry *read_symbol(FILE *in) |
1da177e4c
|
169 |
{ |
be9f6133f
|
170 |
char name[500], type; |
8d6052699
|
171 172 173 |
unsigned long long addr; unsigned int len; struct sym_entry *sym; |
1da177e4c
|
174 |
int rc; |
8d6052699
|
175 176 |
rc = fscanf(in, "%llx %c %499s ", &addr, &type, name); |
1da177e4c
|
177 |
if (rc != 3) { |
be9f6133f
|
178 |
if (rc != EOF && fgets(name, 500, in) == NULL) |
ef894870c
|
179 180 |
fprintf(stderr, "Read error or end of file. "); |
8d6052699
|
181 |
return NULL; |
1da177e4c
|
182 |
} |
be9f6133f
|
183 |
if (strlen(name) >= KSYM_NAME_LEN) { |
6db2983cd
|
184 185 |
fprintf(stderr, "Symbol %s too long for kallsyms (%zu >= %d). " |
bb66fc671
|
186 187 |
"Please increase KSYM_NAME_LEN both in kernel and kallsyms.c ", |
be9f6133f
|
188 |
name, strlen(name), KSYM_NAME_LEN); |
8d6052699
|
189 |
return NULL; |
f3462aa95
|
190 |
} |
1da177e4c
|
191 |
|
be9f6133f
|
192 |
if (strcmp(name, "_text") == 0) |
8d6052699
|
193 |
_text = addr; |
b6233d0de
|
194 |
|
7883a1433
|
195 196 197 |
/* Ignore most absolute/undefined (?) symbols. */ if (is_ignored_symbol(name, type)) return NULL; |
8d6052699
|
198 199 |
check_symbol_range(name, addr, text_ranges, ARRAY_SIZE(text_ranges)); check_symbol_range(name, addr, &percpu_range, 1); |
1da177e4c
|
200 201 202 |
/* include the type field in the symbol name, so that it gets * compressed together */ |
8d6052699
|
203 204 |
len = strlen(name) + 1; |
9d1b38958
|
205 |
sym = malloc(sizeof(*sym) + len + 1); |
8d6052699
|
206 |
if (!sym) { |
f1a136e0d
|
207 208 209 210 211 |
fprintf(stderr, "kallsyms failure: " "unable to allocate required amount of memory "); exit(EXIT_FAILURE); } |
8d6052699
|
212 213 214 |
sym->addr = addr; sym->len = len; sym->sym[0] = type; |
9d1b38958
|
215 |
strcpy(sym_name(sym), name); |
8d6052699
|
216 |
sym->percpu_absolute = 0; |
8c996940b
|
217 |
|
8d6052699
|
218 |
return sym; |
1da177e4c
|
219 |
} |
4bfe2b781
|
220 221 |
static int symbol_in_range(const struct sym_entry *s, const struct addr_range *ranges, int entries) |
17b1f0de7
|
222 223 |
{ size_t i; |
4bfe2b781
|
224 |
const struct addr_range *ar; |
17b1f0de7
|
225 |
|
78eb71594
|
226 227 |
for (i = 0; i < entries; ++i) { ar = &ranges[i]; |
17b1f0de7
|
228 |
|
78eb71594
|
229 |
if (s->addr >= ar->start && s->addr <= ar->end) |
ac6ca5c86
|
230 |
return 1; |
17b1f0de7
|
231 |
} |
ac6ca5c86
|
232 |
return 0; |
17b1f0de7
|
233 |
} |
4bfe2b781
|
234 |
static int symbol_valid(const struct sym_entry *s) |
1da177e4c
|
235 |
{ |
29e55ad3d
|
236 |
const char *name = sym_name(s); |
bd8b22d28
|
237 |
|
1da177e4c
|
238 239 240 |
/* if --all-symbols is not specified, then symbols outside the text * and inittext sections are discarded */ if (!all_symbols) { |
78eb71594
|
241 242 |
if (symbol_in_range(s, text_ranges, ARRAY_SIZE(text_ranges)) == 0) |
1da177e4c
|
243 244 |
return 0; /* Corner case. Discard any symbols with the same value as |
a3b81113f
|
245 246 247 248 |
* _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
|
249 |
*/ |
17b1f0de7
|
250 |
if ((s->addr == text_range_text->end && |
29e55ad3d
|
251 |
strcmp(name, text_range_text->end_sym)) || |
17b1f0de7
|
252 |
(s->addr == text_range_inittext->end && |
29e55ad3d
|
253 |
strcmp(name, text_range_inittext->end_sym))) |
1da177e4c
|
254 255 |
return 0; } |
1da177e4c
|
256 257 |
return 1; } |
5e5c4fa78
|
258 259 260 261 262 263 264 |
/* remove all the invalid symbols from the table */ static void shrink_table(void) { unsigned int i, pos; pos = 0; for (i = 0; i < table_cnt; i++) { |
8d6052699
|
265 |
if (symbol_valid(table[i])) { |
5e5c4fa78
|
266 267 268 269 |
if (pos != i) table[pos] = table[i]; pos++; } else { |
8d6052699
|
270 |
free(table[i]); |
5e5c4fa78
|
271 272 273 274 275 276 277 278 279 280 281 |
} } table_cnt = pos; /* When valid symbol is not registered, exit to error */ if (!table_cnt) { fprintf(stderr, "No valid symbol. "); exit(1); } } |
b3dbb4ecd
|
282 |
static void read_map(FILE *in) |
1da177e4c
|
283 |
{ |
8d6052699
|
284 |
struct sym_entry *sym; |
1da177e4c
|
285 |
while (!feof(in)) { |
8d6052699
|
286 287 288 289 290 |
sym = read_symbol(in); if (!sym) continue; sym->start_pos = table_cnt; |
b3dbb4ecd
|
291 292 293 |
if (table_cnt >= table_size) { table_size += 10000; table = realloc(table, sizeof(*table) * table_size); |
1da177e4c
|
294 295 296 297 298 299 |
if (!table) { fprintf(stderr, "out of memory "); exit (1); } } |
8d6052699
|
300 301 |
table[table_cnt++] = sym; |
1da177e4c
|
302 303 |
} } |
4bfe2b781
|
304 |
static void output_label(const char *label) |
1da177e4c
|
305 |
{ |
534c9f2ec
|
306 307 |
printf(".globl %s ", label); |
1da177e4c
|
308 309 |
printf("\tALGN "); |
534c9f2ec
|
310 311 |
printf("%s: ", label); |
1da177e4c
|
312 |
} |
fd2ab2f66
|
313 314 315 316 317 318 319 320 321 322 |
/* Provide proper symbols relocatability by their '_text' relativeness. */ static void output_address(unsigned long long addr) { if (_text <= addr) printf("\tPTR\t_text + %#llx ", addr - _text); else printf("\tPTR\t_text - %#llx ", _text - addr); } |
1da177e4c
|
323 324 |
/* uncompress a compressed symbol. When this function is called, the best table * might still be compressed itself, so the function needs to be recursive */ |
4bfe2b781
|
325 |
static int expand_symbol(const unsigned char *data, int len, char *result) |
1da177e4c
|
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 |
{ 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; } |
4bfe2b781
|
349 |
static int symbol_absolute(const struct sym_entry *s) |
78eb71594
|
350 |
{ |
8c996940b
|
351 |
return s->percpu_absolute; |
78eb71594
|
352 |
} |
b3dbb4ecd
|
353 |
static void write_src(void) |
1da177e4c
|
354 |
{ |
b3dbb4ecd
|
355 |
unsigned int i, k, off; |
1da177e4c
|
356 357 |
unsigned int best_idx[256]; unsigned int *markers; |
9281acea6
|
358 |
char buf[KSYM_NAME_LEN]; |
1da177e4c
|
359 |
|
500193ec5
|
360 361 |
printf("#include <asm/bitsperlong.h> "); |
1da177e4c
|
362 363 364 365 |
printf("#if BITS_PER_LONG == 64 "); printf("#define PTR .quad "); |
72d3ebb92
|
366 367 |
printf("#define ALGN .balign 8 "); |
1da177e4c
|
368 369 370 371 |
printf("#else "); printf("#define PTR .long "); |
72d3ebb92
|
372 373 |
printf("#define ALGN .balign 4 "); |
1da177e4c
|
374 375 |
printf("#endif "); |
aad094701
|
376 377 |
printf("\t.section .rodata, \"a\" "); |
1da177e4c
|
378 |
|
2213e9a66
|
379 380 381 382 |
if (!base_relative) output_label("kallsyms_addresses"); else output_label("kallsyms_offsets"); |
b3dbb4ecd
|
383 |
for (i = 0; i < table_cnt; i++) { |
2213e9a66
|
384 |
if (base_relative) { |
fd2ab2f66
|
385 386 387 388 389 390 |
/* * 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. */ |
2213e9a66
|
391 392 393 394 |
long long offset; int overflow; if (!absolute_percpu) { |
8d6052699
|
395 |
offset = table[i]->addr - relative_base; |
2213e9a66
|
396 |
overflow = (offset < 0 || offset > UINT_MAX); |
8d6052699
|
397 398 |
} else if (symbol_absolute(table[i])) { offset = table[i]->addr; |
2213e9a66
|
399 400 |
overflow = (offset < 0 || offset > INT_MAX); } else { |
8d6052699
|
401 |
offset = relative_base - table[i]->addr - 1; |
2213e9a66
|
402 403 404 405 406 407 |
overflow = (offset < INT_MIN || offset >= 0); } if (overflow) { fprintf(stderr, "kallsyms failure: " "%s symbol value %#llx out of range in relative mode ", |
8d6052699
|
408 409 |
symbol_absolute(table[i]) ? "absolute" : "relative", table[i]->addr); |
2213e9a66
|
410 411 412 413 |
exit(EXIT_FAILURE); } printf("\t.long\t%#x ", (int)offset); |
8d6052699
|
414 415 |
} else if (!symbol_absolute(table[i])) { output_address(table[i]->addr); |
fd593d127
|
416 |
} else { |
8d6052699
|
417 418 |
printf("\tPTR\t%#llx ", table[i]->addr); |
fd593d127
|
419 |
} |
1da177e4c
|
420 421 422 |
} printf(" "); |
2213e9a66
|
423 424 |
if (base_relative) { output_label("kallsyms_relative_base"); |
fd2ab2f66
|
425 |
output_address(relative_base); |
2213e9a66
|
426 427 428 |
printf(" "); } |
1da177e4c
|
429 |
output_label("kallsyms_num_syms"); |
80ffbaa5b
|
430 431 |
printf("\t.long\t%u ", table_cnt); |
1da177e4c
|
432 433 434 435 436 |
printf(" "); /* table of offset markers, that give the offset in the compressed stream * every 256 symbols */ |
f1a136e0d
|
437 438 439 440 441 442 443 |
markers = malloc(sizeof(unsigned int) * ((table_cnt + 255) / 256)); if (!markers) { fprintf(stderr, "kallsyms failure: " "unable to allocate required memory "); exit(EXIT_FAILURE); } |
1da177e4c
|
444 445 |
output_label("kallsyms_names"); |
1da177e4c
|
446 |
off = 0; |
b3dbb4ecd
|
447 448 449 |
for (i = 0; i < table_cnt; i++) { if ((i & 0xFF) == 0) markers[i >> 8] = off; |
1da177e4c
|
450 |
|
8d6052699
|
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]); |
1da177e4c
|
454 455 |
printf(" "); |
8d6052699
|
456 |
off += table[i]->len + 1; |
1da177e4c
|
457 458 459 460 461 |
} printf(" "); output_label("kallsyms_markers"); |
b3dbb4ecd
|
462 |
for (i = 0; i < ((table_cnt + 255) >> 8); i++) |
80ffbaa5b
|
463 464 |
printf("\t.long\t%u ", markers[i]); |
1da177e4c
|
465 466 467 468 469 470 471 472 473 |
printf(" "); free(markers); output_label("kallsyms_token_table"); off = 0; for (i = 0; i < 256; i++) { best_idx[i] = off; |
b3dbb4ecd
|
474 |
expand_symbol(best_table[i], best_table_len[i], buf); |
1da177e4c
|
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 |
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
|
492 |
/* count all the possible tokens in a symbol */ |
4bfe2b781
|
493 |
static void learn_symbol(const unsigned char *symbol, int len) |
1da177e4c
|
494 495 496 497 |
{ int i; for (i = 0; i < len - 1; i++) |
b3dbb4ecd
|
498 |
token_profit[ symbol[i] + (symbol[i + 1] << 8) ]++; |
1da177e4c
|
499 500 501 |
} /* decrease the count for all the possible tokens in a symbol */ |
4bfe2b781
|
502 |
static void forget_symbol(const unsigned char *symbol, int len) |
1da177e4c
|
503 504 505 506 |
{ int i; for (i = 0; i < len - 1; i++) |
b3dbb4ecd
|
507 |
token_profit[ symbol[i] + (symbol[i + 1] << 8) ]--; |
1da177e4c
|
508 |
} |
5e5c4fa78
|
509 |
/* do the initial token count */ |
1da177e4c
|
510 511 |
static void build_initial_tok_table(void) { |
5e5c4fa78
|
512 |
unsigned int i; |
1da177e4c
|
513 |
|
5e5c4fa78
|
514 |
for (i = 0; i < table_cnt; i++) |
8d6052699
|
515 |
learn_symbol(table[i]->sym, table[i]->len); |
1da177e4c
|
516 |
} |
2558c138a
|
517 |
static unsigned char *find_token(unsigned char *str, int len, |
4bfe2b781
|
518 |
const unsigned char *token) |
7c5d249ad
|
519 520 521 522 523 524 525 526 527 |
{ 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
|
528 529 |
/* replace a given token in all the valid symbols. Use the sampled symbols * to update the counts */ |
4bfe2b781
|
530 |
static void compress_symbols(const unsigned char *str, int idx) |
1da177e4c
|
531 |
{ |
b3dbb4ecd
|
532 533 |
unsigned int i, len, size; unsigned char *p1, *p2; |
1da177e4c
|
534 |
|
b3dbb4ecd
|
535 |
for (i = 0; i < table_cnt; i++) { |
1da177e4c
|
536 |
|
8d6052699
|
537 538 |
len = table[i]->len; p1 = table[i]->sym; |
b3dbb4ecd
|
539 540 |
/* find the token on the symbol */ |
7c5d249ad
|
541 |
p2 = find_token(p1, len, str); |
b3dbb4ecd
|
542 543 544 |
if (!p2) continue; /* decrease the counts for this symbol's tokens */ |
8d6052699
|
545 |
forget_symbol(table[i]->sym, len); |
b3dbb4ecd
|
546 547 |
size = len; |
1da177e4c
|
548 549 |
do { |
b3dbb4ecd
|
550 551 552 553 554 555 556 557 |
*p2 = idx; p2++; size -= (p2 - p1); memmove(p2, p2 + 1, size); p1 = p2; len--; if (size < 2) break; |
1da177e4c
|
558 |
/* find the token on the symbol */ |
7c5d249ad
|
559 |
p2 = find_token(p1, size, str); |
1da177e4c
|
560 |
|
b3dbb4ecd
|
561 |
} while (p2); |
1da177e4c
|
562 |
|
8d6052699
|
563 |
table[i]->len = len; |
1da177e4c
|
564 |
|
b3dbb4ecd
|
565 |
/* increase the counts for this symbol's new tokens */ |
8d6052699
|
566 |
learn_symbol(table[i]->sym, len); |
1da177e4c
|
567 568 569 570 |
} } /* search the token with the maximum profit */ |
b3dbb4ecd
|
571 |
static int find_best_token(void) |
1da177e4c
|
572 |
{ |
b3dbb4ecd
|
573 |
int i, best, bestprofit; |
1da177e4c
|
574 575 |
bestprofit=-10000; |
b3dbb4ecd
|
576 |
best = 0; |
1da177e4c
|
577 |
|
b3dbb4ecd
|
578 579 580 581 |
for (i = 0; i < 0x10000; i++) { if (token_profit[i] > bestprofit) { best = i; bestprofit = token_profit[i]; |
1da177e4c
|
582 |
} |
1da177e4c
|
583 |
} |
1da177e4c
|
584 585 586 587 588 589 |
return best; } /* this is the core of the algorithm: calculate the "best" table */ static void optimize_result(void) { |
b3dbb4ecd
|
590 |
int i, best; |
1da177e4c
|
591 592 593 594 595 596 597 598 |
/* 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]) { |
cbf7a90e3
|
599 |
/* find the token with the best profit value */ |
1da177e4c
|
600 |
best = find_best_token(); |
e0a04b11e
|
601 602 |
if (token_profit[best] == 0) break; |
1da177e4c
|
603 604 |
/* place it in the "best" table */ |
b3dbb4ecd
|
605 606 607 |
best_table_len[i] = 2; best_table[i][0] = best & 0xFF; best_table[i][1] = (best >> 8) & 0xFF; |
1da177e4c
|
608 609 |
/* replace this token in all the valid symbols */ |
b3dbb4ecd
|
610 |
compress_symbols(best_table[i], i); |
1da177e4c
|
611 612 613 614 615 616 617 |
} } } /* start by placing the symbols that are actually used on the table */ static void insert_real_symbols_in_table(void) { |
b3dbb4ecd
|
618 |
unsigned int i, j, c; |
1da177e4c
|
619 |
|
b3dbb4ecd
|
620 |
for (i = 0; i < table_cnt; i++) { |
8d6052699
|
621 622 |
for (j = 0; j < table[i]->len; j++) { c = table[i]->sym[j]; |
b3dbb4ecd
|
623 624 |
best_table[c][0]=c; best_table_len[c]=1; |
1da177e4c
|
625 626 627 628 629 630 |
} } } static void optimize_token_table(void) { |
1da177e4c
|
631 632 633 634 635 636 |
build_initial_tok_table(); insert_real_symbols_in_table(); optimize_result(); } |
b478b782e
|
637 638 639 |
/* guess for "linker script provide" symbol */ static int may_be_linker_script_provide_symbol(const struct sym_entry *se) { |
29e55ad3d
|
640 |
const char *symbol = sym_name(se); |
b478b782e
|
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 |
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; } |
f2df3f65d
|
671 672 |
static int compare_symbols(const void *a, const void *b) { |
8d6052699
|
673 674 |
const struct sym_entry *sa = *(const struct sym_entry **)a; const struct sym_entry *sb = *(const struct sym_entry **)b; |
f2df3f65d
|
675 |
int wa, wb; |
f2df3f65d
|
676 677 678 679 680 681 682 683 684 685 686 |
/* 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
|
687 688 689 690 691 692 693 |
/* 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 */ |
aa9152450
|
694 695 |
wa = strspn(sym_name(sa), "_"); wb = strspn(sym_name(sb), "_"); |
b478b782e
|
696 697 |
if (wa != wb) return wa - wb; |
f2df3f65d
|
698 699 700 701 702 703 |
/* sort by initial order, so that other symbols are left undisturbed */ return sa->start_pos - sb->start_pos; } static void sort_symbols(void) { |
8d6052699
|
704 |
qsort(table, table_cnt, sizeof(table[0]), compare_symbols); |
f2df3f65d
|
705 |
} |
1da177e4c
|
706 |
|
c6bda7c98
|
707 708 709 710 711 |
static void make_percpus_absolute(void) { unsigned int i; for (i = 0; i < table_cnt; i++) |
8d6052699
|
712 |
if (symbol_in_range(table[i], &percpu_range, 1)) { |
8c996940b
|
713 714 715 716 717 |
/* * Keep the 'A' override for percpu symbols to * ensure consistent behavior compared to older * versions of this tool. */ |
8d6052699
|
718 719 |
table[i]->sym[0] = 'A'; table[i]->percpu_absolute = 1; |
8c996940b
|
720 |
} |
c6bda7c98
|
721 |
} |
2213e9a66
|
722 723 724 725 |
/* find the minimum non-absolute symbol address */ static void record_relative_base(void) { unsigned int i; |
2213e9a66
|
726 |
for (i = 0; i < table_cnt; i++) |
8d6052699
|
727 |
if (!symbol_absolute(table[i])) { |
f34ea0291
|
728 729 730 731 |
/* * The table is sorted by address. * Take the first non-absolute symbol value. */ |
8d6052699
|
732 |
relative_base = table[i]->addr; |
f34ea0291
|
733 734 |
return; } |
2213e9a66
|
735 |
} |
b3dbb4ecd
|
736 |
int main(int argc, char **argv) |
1da177e4c
|
737 |
{ |
41f11a4fa
|
738 739 740 741 742 |
if (argc >= 2) { int i; for (i = 1; i < argc; i++) { if(strcmp(argv[i], "--all-symbols") == 0) all_symbols = 1; |
c6bda7c98
|
743 744 |
else if (strcmp(argv[i], "--absolute-percpu") == 0) absolute_percpu = 1; |
534c9f2ec
|
745 |
else if (strcmp(argv[i], "--base-relative") == 0) |
2213e9a66
|
746 747 |
base_relative = 1; else |
41f11a4fa
|
748 749 750 |
usage(); } } else if (argc != 1) |
1da177e4c
|
751 752 753 |
usage(); read_map(stdin); |
5e5c4fa78
|
754 |
shrink_table(); |
c6bda7c98
|
755 756 |
if (absolute_percpu) make_percpus_absolute(); |
f34ea0291
|
757 |
sort_symbols(); |
2213e9a66
|
758 759 |
if (base_relative) record_relative_base(); |
2ea038917
|
760 |
optimize_token_table(); |
1da177e4c
|
761 762 763 764 |
write_src(); return 0; } |