kallsyms.c 17.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/* 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
 *
 *      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>
25
#include <limits.h>
L
Linus Torvalds 已提交
26

27 28 29 30
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
#endif

31
#define KSYM_NAME_LEN		128
L
Linus Torvalds 已提交
32 33 34

struct sym_entry {
	unsigned long long addr;
35
	unsigned int len;
36
	unsigned int start_pos;
L
Linus Torvalds 已提交
37
	unsigned char *sym;
38
	unsigned int percpu_absolute;
L
Linus Torvalds 已提交
39 40
};

41 42
struct addr_range {
	const char *start_sym, *end_sym;
43 44 45 46
	unsigned long long start, end;
};

static unsigned long long _text;
47
static unsigned long long relative_base;
48
static struct addr_range text_ranges[] = {
49 50 51 52 53 54
	{ "_stext",     "_etext"     },
	{ "_sinittext", "_einittext" },
};
#define text_range_text     (&text_ranges[0])
#define text_range_inittext (&text_ranges[1])

55 56 57 58
static struct addr_range percpu_range = {
	"__per_cpu_start", "__per_cpu_end", -1ULL, 0
};

L
Linus Torvalds 已提交
59
static struct sym_entry *table;
60
static unsigned int table_size, table_cnt;
L
Linus Torvalds 已提交
61
static int all_symbols = 0;
62
static int absolute_percpu = 0;
63
static int base_relative = 0;
L
Linus Torvalds 已提交
64

65
static int token_profit[0x10000];
L
Linus Torvalds 已提交
66 67

/* the table that holds the result of the compression */
68 69
static unsigned char best_table[256][2];
static unsigned char best_table_len[256];
L
Linus Torvalds 已提交
70 71


72
static void usage(void)
L
Linus Torvalds 已提交
73
{
74
	fprintf(stderr, "Usage: kallsyms [--all-symbols] "
75
			"[--base-relative] < in.map > out.S\n");
L
Linus Torvalds 已提交
76 77 78 79 80 81 82
	exit(1);
}

/*
 * This ignores the intensely annoying "mapping symbols" found
 * in ARM ELF files: $a, $t and $d.
 */
83
static int is_arm_mapping_symbol(const char *str)
L
Linus Torvalds 已提交
84
{
K
Kyle McMartin 已提交
85
	return str[0] == '$' && strchr("axtd", str[1])
L
Linus Torvalds 已提交
86 87 88
	       && (str[2] == '\0' || str[2] == '.');
}

89 90
static int check_symbol_range(const char *sym, unsigned long long addr,
			      struct addr_range *ranges, int entries)
91 92
{
	size_t i;
93
	struct addr_range *ar;
94

95 96
	for (i = 0; i < entries; ++i) {
		ar = &ranges[i];
97

98 99
		if (strcmp(sym, ar->start_sym) == 0) {
			ar->start = addr;
100
			return 0;
101 102
		} else if (strcmp(sym, ar->end_sym) == 0) {
			ar->end = addr;
103 104 105 106 107 108 109
			return 0;
		}
	}

	return 1;
}

110
static int read_symbol(FILE *in, struct sym_entry *s)
L
Linus Torvalds 已提交
111
{
112
	char sym[500], stype;
L
Linus Torvalds 已提交
113 114
	int rc;

115
	rc = fscanf(in, "%llx %c %499s\n", &s->addr, &stype, sym);
L
Linus Torvalds 已提交
116
	if (rc != 3) {
117
		if (rc != EOF && fgets(sym, 500, in) == NULL)
118
			fprintf(stderr, "Read error or end of file.\n");
L
Linus Torvalds 已提交
119 120
		return -1;
	}
121
	if (strlen(sym) > KSYM_NAME_LEN) {
122
		fprintf(stderr, "Symbol %s too long for kallsyms (%zu vs %d).\n"
123
				"Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n",
124
			sym, strlen(sym), KSYM_NAME_LEN);
125 126
		return -1;
	}
L
Linus Torvalds 已提交
127 128

	/* Ignore most absolute/undefined (?) symbols. */
129 130
	if (strcmp(sym, "_text") == 0)
		_text = s->addr;
131 132
	else if (check_symbol_range(sym, s->addr, text_ranges,
				    ARRAY_SIZE(text_ranges)) == 0)
133
		/* nothing to do */;
134
	else if (toupper(stype) == 'A')
L
Linus Torvalds 已提交
135 136
	{
		/* Keep these useful absolute symbols */
137 138 139 140
		if (strcmp(sym, "__kernel_syscall_via_break") &&
		    strcmp(sym, "__kernel_syscall_via_epc") &&
		    strcmp(sym, "__kernel_sigtramp") &&
		    strcmp(sym, "__gp"))
L
Linus Torvalds 已提交
141 142 143
			return -1;

	}
144
	else if (toupper(stype) == 'U' ||
145
		 is_arm_mapping_symbol(sym))
L
Linus Torvalds 已提交
146
		return -1;
147
	/* exclude also MIPS ELF local symbols ($L123 instead of .L123) */
148
	else if (sym[0] == '$')
149
		return -1;
150
	/* exclude debugging symbols */
151
	else if (stype == 'N' || stype == 'n')
152
		return -1;
L
Linus Torvalds 已提交
153 154 155

	/* include the type field in the symbol name, so that it gets
	 * compressed together */
156
	s->len = strlen(sym) + 1;
157
	s->sym = malloc(s->len + 1);
158 159 160 161 162
	if (!s->sym) {
		fprintf(stderr, "kallsyms failure: "
			"unable to allocate required amount of memory\n");
		exit(EXIT_FAILURE);
	}
163
	strcpy((char *)s->sym + 1, sym);
164
	s->sym[0] = stype;
L
Linus Torvalds 已提交
165

166 167
	s->percpu_absolute = 0;

168 169 170
	/* Record if we've found __per_cpu_start/end. */
	check_symbol_range(sym, s->addr, &percpu_range, 1);

L
Linus Torvalds 已提交
171 172 173
	return 0;
}

174 175
static int symbol_in_range(struct sym_entry *s, struct addr_range *ranges,
			   int entries)
176 177
{
	size_t i;
178
	struct addr_range *ar;
179

180 181
	for (i = 0; i < entries; ++i) {
		ar = &ranges[i];
182

183
		if (s->addr >= ar->start && s->addr <= ar->end)
184
			return 1;
185 186
	}

187
	return 0;
188 189
}

190
static int symbol_valid(struct sym_entry *s)
L
Linus Torvalds 已提交
191 192
{
	/* Symbols which vary between passes.  Passes 1 and 2 must have
193 194 195
	 * 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.
L
Linus Torvalds 已提交
196 197
	 */
	static char *special_symbols[] = {
198
		"kallsyms_addresses",
199 200
		"kallsyms_offsets",
		"kallsyms_relative_base",
201 202 203 204 205 206
		"kallsyms_num_syms",
		"kallsyms_names",
		"kallsyms_markers",
		"kallsyms_token_table",
		"kallsyms_token_index",

L
Linus Torvalds 已提交
207 208 209 210
	/* Exclude linker generated symbols which vary between passes */
		"_SDA_BASE_",		/* ppc */
		"_SDA2_BASE_",		/* ppc */
		NULL };
211

212 213
	static char *special_prefixes[] = {
		"__crc_",		/* modversions */
214
		"__efistub_",		/* arm64 EFI stub namespace */
215 216
		NULL };

217 218
	static char *special_suffixes[] = {
		"_veneer",		/* arm */
219 220
		"_from_arm",		/* arm */
		"_from_thumb",		/* arm */
221 222
		NULL };

L
Linus Torvalds 已提交
223
	int i;
224 225
	char *sym_name = (char *)s->sym + 1;

L
Linus Torvalds 已提交
226 227 228
	/* if --all-symbols is not specified, then symbols outside the text
	 * and inittext sections are discarded */
	if (!all_symbols) {
229 230
		if (symbol_in_range(s, text_ranges,
				    ARRAY_SIZE(text_ranges)) == 0)
L
Linus Torvalds 已提交
231 232
			return 0;
		/* Corner case.  Discard any symbols with the same value as
233 234 235 236
		 * _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.
L
Linus Torvalds 已提交
237
		 */
238
		if ((s->addr == text_range_text->end &&
239
				strcmp(sym_name,
240
				       text_range_text->end_sym)) ||
241
		    (s->addr == text_range_inittext->end &&
242
				strcmp(sym_name,
243
				       text_range_inittext->end_sym)))
L
Linus Torvalds 已提交
244 245 246 247 248
			return 0;
	}

	/* Exclude symbols which vary between passes. */
	for (i = 0; special_symbols[i]; i++)
249
		if (strcmp(sym_name, special_symbols[i]) == 0)
L
Linus Torvalds 已提交
250 251
			return 0;

252 253 254 255 256 257 258 259
	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;
	}

260 261 262 263 264 265 266
	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;
	}

L
Linus Torvalds 已提交
267 268 269
	return 1;
}

270
static void read_map(FILE *in)
L
Linus Torvalds 已提交
271 272
{
	while (!feof(in)) {
273 274 275
		if (table_cnt >= table_size) {
			table_size += 10000;
			table = realloc(table, sizeof(*table) * table_size);
L
Linus Torvalds 已提交
276 277 278 279 280
			if (!table) {
				fprintf(stderr, "out of memory\n");
				exit (1);
			}
		}
281 282
		if (read_symbol(in, &table[table_cnt]) == 0) {
			table[table_cnt].start_pos = table_cnt;
283
			table_cnt++;
284
		}
L
Linus Torvalds 已提交
285 286 287 288 289
	}
}

static void output_label(char *label)
{
290
	printf(".globl %s\n", label);
L
Linus Torvalds 已提交
291
	printf("\tALGN\n");
292
	printf("%s:\n", label);
L
Linus Torvalds 已提交
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
}

/* 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;
}

322 323
static int symbol_absolute(struct sym_entry *s)
{
324
	return s->percpu_absolute;
325 326
}

327
static void write_src(void)
L
Linus Torvalds 已提交
328
{
329
	unsigned int i, k, off;
L
Linus Torvalds 已提交
330 331
	unsigned int best_idx[256];
	unsigned int *markers;
332
	char buf[KSYM_NAME_LEN];
L
Linus Torvalds 已提交
333

334
	printf("#include <asm/bitsperlong.h>\n");
L
Linus Torvalds 已提交
335 336
	printf("#if BITS_PER_LONG == 64\n");
	printf("#define PTR .quad\n");
337
	printf("#define ALGN .balign 8\n");
L
Linus Torvalds 已提交
338 339
	printf("#else\n");
	printf("#define PTR .long\n");
340
	printf("#define ALGN .balign 4\n");
L
Linus Torvalds 已提交
341 342
	printf("#endif\n");

343
	printf("\t.section .rodata, \"a\"\n");
L
Linus Torvalds 已提交
344

345 346 347 348 349 350 351 352 353 354 355 356
	/* 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.
357
	 */
358 359 360 361 362
	if (!base_relative)
		output_label("kallsyms_addresses");
	else
		output_label("kallsyms_offsets");

363
	for (i = 0; i < table_cnt; i++) {
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
		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\n",
					symbol_absolute(&table[i]) ? "absolute" : "relative",
					table[i].addr);
				exit(EXIT_FAILURE);
			}
			printf("\t.long\t%#x\n", (int)offset);
		} else if (!symbol_absolute(&table[i])) {
387 388 389 390
			if (_text <= table[i].addr)
				printf("\tPTR\t_text + %#llx\n",
					table[i].addr - _text);
			else
391 392
				printf("\tPTR\t_text - %#llx\n",
					_text - table[i].addr);
393 394 395
		} else {
			printf("\tPTR\t%#llx\n", table[i].addr);
		}
L
Linus Torvalds 已提交
396 397 398
	}
	printf("\n");

399 400 401 402 403 404
	if (base_relative) {
		output_label("kallsyms_relative_base");
		printf("\tPTR\t_text - %#llx\n", _text - relative_base);
		printf("\n");
	}

L
Linus Torvalds 已提交
405
	output_label("kallsyms_num_syms");
406
	printf("\t.long\t%u\n", table_cnt);
L
Linus Torvalds 已提交
407 408 409 410
	printf("\n");

	/* table of offset markers, that give the offset in the compressed stream
	 * every 256 symbols */
411 412 413 414 415 416
	markers = malloc(sizeof(unsigned int) * ((table_cnt + 255) / 256));
	if (!markers) {
		fprintf(stderr, "kallsyms failure: "
			"unable to allocate required memory\n");
		exit(EXIT_FAILURE);
	}
L
Linus Torvalds 已提交
417 418 419

	output_label("kallsyms_names");
	off = 0;
420 421 422
	for (i = 0; i < table_cnt; i++) {
		if ((i & 0xFF) == 0)
			markers[i >> 8] = off;
L
Linus Torvalds 已提交
423 424 425 426 427 428 429 430 431 432 433

		printf("\t.byte 0x%02x", table[i].len);
		for (k = 0; k < table[i].len; k++)
			printf(", 0x%02x", table[i].sym[k]);
		printf("\n");

		off += table[i].len + 1;
	}
	printf("\n");

	output_label("kallsyms_markers");
434
	for (i = 0; i < ((table_cnt + 255) >> 8); i++)
435
		printf("\t.long\t%u\n", markers[i]);
L
Linus Torvalds 已提交
436 437 438 439 440 441 442 443
	printf("\n");

	free(markers);

	output_label("kallsyms_token_table");
	off = 0;
	for (i = 0; i < 256; i++) {
		best_idx[i] = off;
444
		expand_symbol(best_table[i], best_table_len[i], buf);
L
Linus Torvalds 已提交
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
		printf("\t.asciz\t\"%s\"\n", buf);
		off += strlen(buf) + 1;
	}
	printf("\n");

	output_label("kallsyms_token_index");
	for (i = 0; i < 256; i++)
		printf("\t.short\t%d\n", best_idx[i]);
	printf("\n");
}


/* table lookup compression functions */

/* 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++)
465
		token_profit[ symbol[i] + (symbol[i + 1] << 8) ]++;
L
Linus Torvalds 已提交
466 467 468 469 470 471 472 473
}

/* 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++)
474
		token_profit[ symbol[i] + (symbol[i + 1] << 8) ]--;
L
Linus Torvalds 已提交
475 476
}

477
/* remove all the invalid symbols from the table and do the initial token count */
L
Linus Torvalds 已提交
478 479
static void build_initial_tok_table(void)
{
480
	unsigned int i, pos;
L
Linus Torvalds 已提交
481

482 483
	pos = 0;
	for (i = 0; i < table_cnt; i++) {
L
Linus Torvalds 已提交
484
		if ( symbol_valid(&table[i]) ) {
485 486 487 488
			if (pos != i)
				table[pos] = table[i];
			learn_symbol(table[pos].sym, table[pos].len);
			pos++;
L
Linus Torvalds 已提交
489 490
		}
	}
491
	table_cnt = pos;
L
Linus Torvalds 已提交
492 493
}

494 495 496 497 498 499 500 501 502 503 504
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;
}

L
Linus Torvalds 已提交
505 506
/* replace a given token in all the valid symbols. Use the sampled symbols
 * to update the counts */
507
static void compress_symbols(unsigned char *str, int idx)
L
Linus Torvalds 已提交
508
{
509 510
	unsigned int i, len, size;
	unsigned char *p1, *p2;
L
Linus Torvalds 已提交
511

512
	for (i = 0; i < table_cnt; i++) {
L
Linus Torvalds 已提交
513 514

		len = table[i].len;
515 516 517
		p1 = table[i].sym;

		/* find the token on the symbol */
518
		p2 = find_token(p1, len, str);
519 520 521 522 523 524
		if (!p2) continue;

		/* decrease the counts for this symbol's tokens */
		forget_symbol(table[i].sym, len);

		size = len;
L
Linus Torvalds 已提交
525 526

		do {
527 528 529 530 531 532 533 534 535
			*p2 = idx;
			p2++;
			size -= (p2 - p1);
			memmove(p2, p2 + 1, size);
			p1 = p2;
			len--;

			if (size < 2) break;

L
Linus Torvalds 已提交
536
			/* find the token on the symbol */
537
			p2 = find_token(p1, size, str);
L
Linus Torvalds 已提交
538

539
		} while (p2);
L
Linus Torvalds 已提交
540

541
		table[i].len = len;
L
Linus Torvalds 已提交
542

543 544
		/* increase the counts for this symbol's new tokens */
		learn_symbol(table[i].sym, len);
L
Linus Torvalds 已提交
545 546 547 548
	}
}

/* search the token with the maximum profit */
549
static int find_best_token(void)
L
Linus Torvalds 已提交
550
{
551
	int i, best, bestprofit;
L
Linus Torvalds 已提交
552 553

	bestprofit=-10000;
554
	best = 0;
L
Linus Torvalds 已提交
555

556 557 558 559
	for (i = 0; i < 0x10000; i++) {
		if (token_profit[i] > bestprofit) {
			best = i;
			bestprofit = token_profit[i];
L
Linus Torvalds 已提交
560 561 562 563 564 565 566 567
		}
	}
	return best;
}

/* this is the core of the algorithm: calculate the "best" table */
static void optimize_result(void)
{
568
	int i, best;
L
Linus Torvalds 已提交
569 570 571 572 573 574 575 576 577

	/* 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]) {

C
Cao jin 已提交
578
			/* find the token with the best profit value */
L
Linus Torvalds 已提交
579
			best = find_best_token();
580 581
			if (token_profit[best] == 0)
				break;
L
Linus Torvalds 已提交
582 583

			/* place it in the "best" table */
584 585 586
			best_table_len[i] = 2;
			best_table[i][0] = best & 0xFF;
			best_table[i][1] = (best >> 8) & 0xFF;
L
Linus Torvalds 已提交
587 588

			/* replace this token in all the valid symbols */
589
			compress_symbols(best_table[i], i);
L
Linus Torvalds 已提交
590 591 592 593 594 595 596
		}
	}
}

/* start by placing the symbols that are actually used on the table */
static void insert_real_symbols_in_table(void)
{
597
	unsigned int i, j, c;
L
Linus Torvalds 已提交
598

599 600 601 602 603
	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;
L
Linus Torvalds 已提交
604 605 606 607 608 609 610 611 612 613
		}
	}
}

static void optimize_token_table(void)
{
	build_initial_tok_table();

	insert_real_symbols_in_table();

614
	/* When valid symbol is not registered, exit to error */
615
	if (!table_cnt) {
616 617 618 619
		fprintf(stderr, "No valid symbol.\n");
		exit(1);
	}

L
Linus Torvalds 已提交
620 621 622
	optimize_result();
}

623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
/* 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;

662
	while (*tail == '_')
663 664 665 666 667
		tail++;

	return tail - str;
}

668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
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;

689 690 691 692 693 694 695 696 697 698 699 700
	/* 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;

701 702 703 704 705 706 707 708
	/* 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);
}
L
Linus Torvalds 已提交
709

710 711 712 713 714
static void make_percpus_absolute(void)
{
	unsigned int i;

	for (i = 0; i < table_cnt; i++)
715 716 717 718 719 720
		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.
			 */
721
			table[i].sym[0] = 'A';
722 723
			table[i].percpu_absolute = 1;
		}
724 725
}

726 727 728 729 730 731 732 733 734 735 736 737
/* 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;
}

738
int main(int argc, char **argv)
L
Linus Torvalds 已提交
739
{
740 741 742 743 744
	if (argc >= 2) {
		int i;
		for (i = 1; i < argc; i++) {
			if(strcmp(argv[i], "--all-symbols") == 0)
				all_symbols = 1;
745 746
			else if (strcmp(argv[i], "--absolute-percpu") == 0)
				absolute_percpu = 1;
747
			else if (strcmp(argv[i], "--base-relative") == 0)
748 749
				base_relative = 1;
			else
750 751 752
				usage();
		}
	} else if (argc != 1)
L
Linus Torvalds 已提交
753 754 755
		usage();

	read_map(stdin);
756 757
	if (absolute_percpu)
		make_percpus_absolute();
758 759
	if (base_relative)
		record_relative_base();
760 761
	sort_symbols();
	optimize_token_table();
L
Linus Torvalds 已提交
762 763 764 765
	write_src();

	return 0;
}