kallsyms.c 18.0 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
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))

29
#define KSYM_NAME_LEN		128
L
Linus Torvalds 已提交
30 31 32

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

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

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

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

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

63
static int token_profit[0x10000];
L
Linus Torvalds 已提交
64 65

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


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

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

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

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

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

	return 1;
}

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

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

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

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

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

167 168
	s->percpu_absolute = 0;

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

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

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

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

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

188
	return 0;
189 190
}

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

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

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

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

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

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

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

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

261 262 263 264 265 266 267
	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 已提交
268 269 270
	return 1;
}

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

static void output_label(char *label)
{
291
	printf(".globl %s\n", label);
L
Linus Torvalds 已提交
292
	printf("\tALGN\n");
293
	printf("%s:\n", label);
L
Linus Torvalds 已提交
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 322
}

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

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

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

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

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

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

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

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

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

	/* table of offset markers, that give the offset in the compressed stream
	 * every 256 symbols */
412 413 414 415 416 417
	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 已提交
418 419 420

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

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

	free(markers);

	output_label("kallsyms_token_table");
	off = 0;
	for (i = 0; i < 256; i++) {
		best_idx[i] = off;
445
		expand_symbol(best_table[i], best_table_len[i], buf);
L
Linus Torvalds 已提交
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
		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++)
466
		token_profit[ symbol[i] + (symbol[i + 1] << 8) ]++;
L
Linus Torvalds 已提交
467 468 469 470 471 472 473 474
}

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

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

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

495 496 497 498 499 500 501 502 503 504 505
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 已提交
506 507
/* replace a given token in all the valid symbols. Use the sampled symbols
 * to update the counts */
508
static void compress_symbols(unsigned char *str, int idx)
L
Linus Torvalds 已提交
509
{
510 511
	unsigned int i, len, size;
	unsigned char *p1, *p2;
L
Linus Torvalds 已提交
512

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

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

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

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

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

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

			if (size < 2) break;

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

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

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

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

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

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

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

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

	/* 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 已提交
579
			/* find the token with the best profit value */
L
Linus Torvalds 已提交
580
			best = find_best_token();
581 582
			if (token_profit[best] == 0)
				break;
L
Linus Torvalds 已提交
583 584

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

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

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

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

static void optimize_token_table(void)
{
	build_initial_tok_table();

	insert_real_symbols_in_table();

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

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

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

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

	return tail - str;
}

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

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

702 703 704 705 706 707 708 709
	/* 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 已提交
710

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

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

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

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

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

	return 0;
}