xref: /linux/scripts/kallsyms.c (revision 531980b4b17e916f5955bcc53a188827624f5a0f)
1 /* Generate assembler source containing symbol information
2  *
3  * Copyright 2002       by Kai Germaschewski
4  *
5  * This software may be used and distributed according to the terms
6  * of the GNU General Public License, incorporated herein by reference.
7  *
8  * Usage: kallsyms [--all-symbols] [--pc-relative] [--sysmap=out.map] in out.bin > out.S
9  *        kallsyms --sysmap=out.map in
10  *
11  *      in is vmlinux; an empty file stands for the first link, which has no
12  *  symbols yet, and gives an empty table. --sysmap also writes the symbols
13  *  in System.map format.
14  *
15  *      The byte tables go to out.bin and are pulled into out.S with .incbin;
16  *  wider tables stay assembler source for endianness and relocations.
17  *
18  *      Table compression uses all the unused char codes on the symbols and
19  *  maps these to the most used substrings (tokens). For instance, it might
20  *  map char code 0xF7 to represent "write_" and then in every symbol where
21  *  "write_" appears it can be replaced by 0xF7, saving 5 bytes.
22  *      The used codes themselves are also placed in the table so that the
23  *  decompresion can work without "special cases".
24  *      Applied to kernel symbols, this usually produces a compression ratio
25  *  of about 50%.
26  *
27  */
28 
29 #include <getopt.h>
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <limits.h>
36 #include <sys/stat.h>
37 #include <xalloc.h>
38 
39 #include "kallsyms.h"
40 
41 #define KSYM_NAME_LEN		512
42 
43 struct sym_entry {
44 	unsigned long long addr;
45 	unsigned int len;
46 	unsigned int seq;
47 	unsigned char sym[];
48 };
49 
50 struct addr_range {
51 	const char *start_sym, *end_sym;
52 	unsigned long long start, end;
53 };
54 
55 static unsigned long long _text;
56 static struct addr_range text_ranges[] = {
57 	{ "_stext",     "_etext"     },
58 	{ "_sinittext", "_einittext" },
59 };
60 #define text_range_text     (&text_ranges[0])
61 #define text_range_inittext (&text_ranges[1])
62 
63 static struct sym_entry **table;
64 static unsigned int table_size, table_cnt;
65 static int all_symbols;
66 static int pc_relative;
67 
68 /* A dynamic array of symbols, encoded by symbol index. */
69 struct sym_arr {
70 	unsigned int *sym_indexes;
71 	unsigned int cnt, cap;
72 };
73 
74 /* Every pair of bytes is a token. */
75 #define NR_TOKENS	0x10000
76 
77 static int token_profit[NR_TOKENS];
78 static struct sym_arr token_syms[NR_TOKENS];
79 
80 /* the table that holds the result of the compression */
81 static unsigned char best_table[256][2];
82 static unsigned char best_table_len[256];
83 
84 static unsigned int sym_arr_last(const struct sym_arr *arr)
85 {
86 	return arr->cnt ? arr->sym_indexes[arr->cnt - 1] : UINT_MAX;
87 }
88 
89 static void sym_arr_maybe_expand(struct sym_arr *arr)
90 {
91 	if (arr->cap > arr->cnt)
92 		return;
93 
94 	arr->cap = arr->cap ? arr->cap * 2 : 16;
95 	arr->sym_indexes = xrealloc(arr->sym_indexes,
96 				    arr->cap * sizeof(*arr->sym_indexes));
97 }
98 
99 static void sym_arr_add(struct sym_arr *arr, unsigned int sym_idx)
100 {
101 	sym_arr_maybe_expand(arr);
102 	arr->sym_indexes[arr->cnt++] = sym_idx;
103 }
104 
105 static void sym_arr_free(struct sym_arr *arr)
106 {
107 	free(arr->sym_indexes);
108 	arr->sym_indexes = NULL;
109 	arr->cnt = 0;
110 	arr->cap = 0;
111 }
112 
113 static void usage(void)
114 {
115 	fprintf(stderr, "Usage: kallsyms [--all-symbols] [--pc-relative] [--sysmap=out.map]\n"
116 			"                in out.bin > out.S\n"
117 			"       kallsyms --sysmap=out.map vmlinux\n");
118 	exit(1);
119 }
120 
121 static char *sym_entry_name(const struct sym_entry *s)
122 {
123 	return (char *)s->sym + 1;
124 }
125 
126 static bool is_ignored_symbol(const char *name, char type)
127 {
128 	if (type == 'u' || type == 'n')
129 		return true;
130 
131 	if (toupper(type) == 'A') {
132 		/* Keep these useful absolute symbols */
133 		if (strcmp(name, "__kernel_syscall_via_break") &&
134 		    strcmp(name, "__kernel_syscall_via_epc") &&
135 		    strcmp(name, "__kernel_sigtramp") &&
136 		    strcmp(name, "__gp"))
137 			return true;
138 	}
139 
140 	return false;
141 }
142 
143 static void check_symbol_range(const char *sym, unsigned long long addr,
144 			       struct addr_range *ranges, int entries)
145 {
146 	size_t i;
147 	struct addr_range *ar;
148 
149 	for (i = 0; i < entries; ++i) {
150 		ar = &ranges[i];
151 
152 		if (strcmp(sym, ar->start_sym) == 0) {
153 			ar->start = addr;
154 			return;
155 		} else if (strcmp(sym, ar->end_sym) == 0) {
156 			ar->end = addr;
157 			return;
158 		}
159 	}
160 }
161 
162 static struct sym_entry *add_symbol(unsigned long long addr, char type,
163 				    const char *name)
164 {
165 	size_t len = strlen(name);
166 	struct sym_entry *sym;
167 
168 	if (len >= KSYM_NAME_LEN) {
169 		fprintf(stderr, "Symbol %s too long for kallsyms (%zu >= %d).\n"
170 				"Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n",
171 			name, len, KSYM_NAME_LEN);
172 		return NULL;
173 	}
174 
175 	if (strcmp(name, "_text") == 0)
176 		_text = addr;
177 
178 	/* Ignore most absolute/undefined (?) symbols. */
179 	if (is_ignored_symbol(name, type))
180 		return NULL;
181 
182 	check_symbol_range(name, addr, text_ranges, ARRAY_SIZE(text_ranges));
183 
184 	/* include the type field in the symbol name, so that it gets
185 	 * compressed together */
186 	len++;
187 
188 	sym = xmalloc(sizeof(*sym) + len + 1);
189 	sym->addr = addr;
190 	sym->len = len;
191 	sym->sym[0] = type;
192 	strcpy(sym_entry_name(sym), name);
193 
194 	return sym;
195 }
196 
197 static int symbol_in_range(const struct sym_entry *s,
198 			   const struct addr_range *ranges, int entries)
199 {
200 	size_t i;
201 	const struct addr_range *ar;
202 
203 	for (i = 0; i < entries; ++i) {
204 		ar = &ranges[i];
205 
206 		if (s->addr >= ar->start && s->addr <= ar->end)
207 			return 1;
208 	}
209 
210 	return 0;
211 }
212 
213 static int symbol_valid(const struct sym_entry *s)
214 {
215 	const char *name = sym_entry_name(s);
216 
217 	/* if --all-symbols is not specified, then symbols outside the text
218 	 * and inittext sections are discarded */
219 	if (!all_symbols) {
220 		/*
221 		 * Symbols starting with __start and __stop are used to denote
222 		 * section boundaries, and should always be included:
223 		 */
224 		if (string_starts_with(name, "__start_") ||
225 		    string_starts_with(name, "__stop_"))
226 			return 1;
227 
228 		if (symbol_in_range(s, text_ranges,
229 				    ARRAY_SIZE(text_ranges)) == 0)
230 			return 0;
231 		/* Corner case.  Discard any symbols with the same value as
232 		 * _etext _einittext; they can move between pass 1 and 2 when
233 		 * the kallsyms data are added.  If these symbols move then
234 		 * they may get dropped in pass 2, which breaks the kallsyms
235 		 * rules.
236 		 */
237 		if ((s->addr == text_range_text->end &&
238 		     strcmp(name, text_range_text->end_sym)) ||
239 		    (s->addr == text_range_inittext->end &&
240 		     strcmp(name, text_range_inittext->end_sym)))
241 			return 0;
242 	}
243 
244 	return 1;
245 }
246 
247 /* remove all the invalid symbols from the table */
248 static void shrink_table(void)
249 {
250 	unsigned int i, pos;
251 
252 	pos = 0;
253 	for (i = 0; i < table_cnt; i++) {
254 		if (symbol_valid(table[i])) {
255 			if (pos != i)
256 				table[pos] = table[i];
257 			pos++;
258 		} else {
259 			free(table[i]);
260 		}
261 	}
262 	table_cnt = pos;
263 }
264 
265 static void add_table_entry(struct sym_entry *sym)
266 {
267 	sym->seq = table_cnt;
268 
269 	if (table_cnt >= table_size) {
270 		table_size = table_size ? table_size * 2 : 10000;
271 		table = xrealloc(table, sizeof(*table) * table_size);
272 	}
273 
274 	table[table_cnt++] = sym;
275 }
276 
277 static bool file_is_empty(const char *path)
278 {
279 	struct stat st;
280 
281 	if (stat(path, &st)) {
282 		perror(path);
283 		exit(EXIT_FAILURE);
284 	}
285 
286 	return st.st_size == 0;
287 }
288 
289 /*
290  * Read the symbols from vmlinux, writing System.map if asked to. The first
291  * link has no symbols yet: an empty file gives an empty table.
292  */
293 static void read_elf(const char *path, FILE *sysmap_out)
294 {
295 	struct sysmap *map;
296 	size_t i;
297 
298 	if (file_is_empty(path))
299 		return;
300 
301 	map = sysmap_read(path);
302 	if (sysmap_out)
303 		sysmap_write(map, sysmap_out);
304 
305 	for (i = 0; i < map->nr_syms; i++) {
306 		const struct sysmap_symbol *s = &map->syms[i];
307 		struct sym_entry *sym = add_symbol(s->addr, s->type, s->name);
308 
309 		if (sym)
310 			add_table_entry(sym);
311 	}
312 
313 	sysmap_free(map);
314 }
315 
316 static void output_label(const char *label)
317 {
318 	printf(".globl %s\n", label);
319 	printf("\t.balign 4\n");
320 	printf("%s:\n", label);
321 }
322 
323 static long bin_pos(FILE *file)
324 {
325 	const long pos = ftell(file);
326 
327 	if (pos < 0) {
328 		perror("kallsyms: ftell");
329 		exit(EXIT_FAILURE);
330 	}
331 
332 	return pos;
333 }
334 
335 static void write_incbin(const char *filename, long start, long end)
336 {
337 	if (start >= end)
338 		return;
339 
340 	printf("\t.incbin \"%s\", %ld, %ld\n", filename, start, end - start);
341 }
342 
343 /* uncompress a compressed symbol. When this function is called, the best table
344  * might still be compressed itself, so the function needs to be recursive */
345 static int expand_symbol(const unsigned char *data, int len, char *result)
346 {
347 	int c, rlen, total=0;
348 
349 	while (len) {
350 		c = *data;
351 		/* if the table holds a single char that is the same as the one
352 		 * we are looking for, then end the search */
353 		if (best_table[c][0]==c && best_table_len[c]==1) {
354 			*result++ = c;
355 			total++;
356 		} else {
357 			/* if not, recurse and expand */
358 			rlen = expand_symbol(best_table[c], best_table_len[c], result);
359 			total += rlen;
360 			result += rlen;
361 		}
362 		data++;
363 		len--;
364 	}
365 	*result=0;
366 
367 	return total;
368 }
369 
370 static int compare_names(const void *a, const void *b)
371 {
372 	int ret;
373 	const struct sym_entry *sa = *(const struct sym_entry **)a;
374 	const struct sym_entry *sb = *(const struct sym_entry **)b;
375 
376 	ret = strcmp(sym_entry_name(sa), sym_entry_name(sb));
377 	if (!ret) {
378 		if (sa->addr > sb->addr)
379 			return 1;
380 		else if (sa->addr < sb->addr)
381 			return -1;
382 
383 		/* keep old order */
384 		return (int)(sa->seq - sb->seq);
385 	}
386 
387 	return ret;
388 }
389 
390 static void sort_symbols_by_name(void)
391 {
392 	qsort(table, table_cnt, sizeof(table[0]), compare_names);
393 }
394 
395 static void write_src(FILE *out_bin_file, const char *out_bin_name)
396 {
397 	unsigned int i, off;
398 	unsigned int best_idx[256];
399 	unsigned int *markers, markers_cnt;
400 	long bin_start;
401 	char buf[KSYM_NAME_LEN];
402 
403 	printf("\t.section .rodata, \"a\"\n");
404 
405 	output_label("kallsyms_num_syms");
406 	printf("\t.long\t%u\n", table_cnt);
407 	printf("\n");
408 
409 	/* table of offset markers, that give the offset in the compressed stream
410 	 * every 256 symbols */
411 	markers_cnt = (table_cnt + 255) / 256;
412 	markers = xmalloc(sizeof(*markers) * markers_cnt);
413 
414 	output_label("kallsyms_names");
415 	bin_start = bin_pos(out_bin_file);
416 	off = 0;
417 	for (i = 0; i < table_cnt; i++) {
418 		if ((i & 0xFF) == 0)
419 			markers[i >> 8] = off;
420 		table[i]->seq = i;
421 
422 		/* There cannot be any symbol of length zero. */
423 		if (table[i]->len == 0) {
424 			fprintf(stderr, "kallsyms failure: "
425 				"unexpected zero symbol length\n");
426 			exit(EXIT_FAILURE);
427 		}
428 
429 		/* Only lengths that fit in up-to-two-byte ULEB128 are supported. */
430 		if (table[i]->len > 0x3FFF) {
431 			fprintf(stderr, "kallsyms failure: "
432 				"unexpected huge symbol length\n");
433 			exit(EXIT_FAILURE);
434 		}
435 
436 		/* Encode length with ULEB128. */
437 		if (table[i]->len <= 0x7F) {
438 			/* Most symbols use a single byte for the length. */
439 			fputc(table[i]->len, out_bin_file);
440 			off += table[i]->len + 1;
441 		} else {
442 			/* "Big" symbols use two bytes. */
443 			fputc((table[i]->len & 0x7F) | 0x80, out_bin_file);
444 			fputc((table[i]->len >> 7) & 0x7F, out_bin_file);
445 			off += table[i]->len + 2;
446 		}
447 		fwrite(table[i]->sym, 1, table[i]->len, out_bin_file);
448 
449 		/*
450 		 * Now that we wrote out the compressed symbol name, restore the
451 		 * original name for the comments below.
452 		 */
453 		expand_symbol(table[i]->sym, table[i]->len, buf);
454 		strcpy((char *)table[i]->sym, buf);
455 	}
456 	write_incbin(out_bin_name, bin_start, bin_pos(out_bin_file));
457 	printf(".size kallsyms_names, . - kallsyms_names\n");
458 	printf("\n");
459 
460 	output_label("kallsyms_markers");
461 	for (i = 0; i < markers_cnt; i++)
462 		printf("\t.long\t%u\n", markers[i]);
463 	printf(".size kallsyms_markers, . - kallsyms_markers\n");
464 	printf("\n");
465 
466 	free(markers);
467 
468 	output_label("kallsyms_token_table");
469 	bin_start = bin_pos(out_bin_file);
470 	off = 0;
471 	for (i = 0; i < 256; i++) {
472 		best_idx[i] = off;
473 		expand_symbol(best_table[i], best_table_len[i], buf);
474 		fwrite(buf, 1, strlen(buf) + 1, out_bin_file);
475 		off += strlen(buf) + 1;
476 	}
477 	write_incbin(out_bin_name, bin_start, bin_pos(out_bin_file));
478 	printf(".size kallsyms_token_table, . - kallsyms_token_table\n");
479 	printf("\n");
480 
481 	output_label("kallsyms_token_index");
482 	for (i = 0; i < 256; i++)
483 		printf("\t.short\t%d\n", best_idx[i]);
484 	printf("\n");
485 
486 	output_label("kallsyms_offsets");
487 
488 	for (i = 0; i < table_cnt; i++) {
489 		if (pc_relative) {
490 			long long offset = table[i]->addr - _text;
491 
492 			if (offset < INT_MIN || offset > INT_MAX) {
493 				fprintf(stderr, "kallsyms failure: "
494 					"relative symbol value %#llx out of range\n",
495 					table[i]->addr);
496 				exit(EXIT_FAILURE);
497 			}
498 			printf("\t.long\t_text - . + (%d)\t/* %s */\n",
499 			       (int)offset, table[i]->sym);
500 		} else {
501 			printf("\t.long\t%#x\t/* %s */\n",
502 			       (unsigned int)table[i]->addr, table[i]->sym);
503 		}
504 	}
505 	printf(".size kallsyms_offsets, . - kallsyms_offsets\n");
506 	printf("\n");
507 
508 	sort_symbols_by_name();
509 	output_label("kallsyms_seqs_of_names");
510 	bin_start = bin_pos(out_bin_file);
511 	for (i = 0; i < table_cnt; i++) {
512 		fputc(table[i]->seq >> 16, out_bin_file);
513 		fputc(table[i]->seq >> 8, out_bin_file);
514 		fputc(table[i]->seq >> 0, out_bin_file);
515 	}
516 	write_incbin(out_bin_name, bin_start, bin_pos(out_bin_file));
517 	printf("\n");
518 }
519 
520 static unsigned int token_index(unsigned char first, unsigned char second)
521 {
522 	return first + (second << 8);
523 }
524 
525 static unsigned int sym_token_index(const unsigned char *symbol, int first_idx)
526 {
527 	return token_index(symbol[first_idx], symbol[first_idx + 1]);
528 }
529 
530 /* table lookup compression functions */
531 
532 /* count all the possible tokens in a symbol */
533 static void learn_symbol(const unsigned char *symbol, int len)
534 {
535 	int i;
536 
537 	for (i = 0; i < len - 1; i++)
538 		token_profit[sym_token_index(symbol, i)]++;
539 }
540 
541 /* decrease the count for all the possible tokens in a symbol */
542 static void forget_symbol(const unsigned char *symbol, int len)
543 {
544 	int i;
545 
546 	for (i = 0; i < len - 1; i++)
547 		token_profit[sym_token_index(symbol, i)]--;
548 }
549 
550 static void token_add_symbol(unsigned int token_idx, unsigned int sym_idx)
551 {
552 	struct sym_arr *arr = &token_syms[token_idx];
553 
554 	/* Symbol indexes kept in sorted order, check for duplicate. */
555 	if (sym_arr_last(arr) == sym_idx)
556 		return;
557 
558 	sym_arr_add(arr, sym_idx);
559 }
560 
561 static void symbol_index_all_tokens(const unsigned char *symbol, int len,
562 				    unsigned int sym_idx)
563 {
564 	int i;
565 
566 	for (i = 0; i < len - 1; i++) {
567 		const unsigned int token_idx = sym_token_index(symbol, i);
568 
569 		token_add_symbol(token_idx, sym_idx);
570 	}
571 }
572 
573 /*
574  * The symbol just got compressed. The only parts of the symbol that changed
575  * meaningfully are those containing the newly assigned compressed char, so
576  * index those.
577  */
578 static void symbol_index_new_tokens(const unsigned char *symbol, int len,
579 				    unsigned int sym_idx, int compressed_chr)
580 {
581 	int i;
582 
583 	for (i = 0; i < len - 1; i++) {
584 		const unsigned int token_idx = sym_token_index(symbol, i);
585 
586 		if (symbol[i] == compressed_chr ||
587 		    symbol[i + 1] == compressed_chr)
588 			token_add_symbol(token_idx, sym_idx);
589 	}
590 }
591 
592 static void build_initial_token_table(void)
593 {
594 	unsigned int i;
595 
596 	for (i = 0; i < table_cnt; i++)
597 		learn_symbol(table[i]->sym, table[i]->len);
598 
599 	/*
600 	 * The initial occurrence counts tell us exactly how much memory should
601 	 * be reserved for each token's symbol array.
602 	 */
603 	for (i = 0; i < ARRAY_SIZE(token_syms); i++) {
604 		const int nr_syms = token_profit[i];
605 
606 		if (!nr_syms)
607 			continue;
608 
609 		token_syms[i].cap = nr_syms;
610 		token_syms[i].sym_indexes =
611 			xmalloc(nr_syms * sizeof(unsigned int));
612 	}
613 
614 	/* For every symbol, index every token -> symbol it is present in. */
615 	for (i = 0; i < table_cnt; i++)
616 		symbol_index_all_tokens(table[i]->sym, table[i]->len, i);
617 }
618 
619 static unsigned char *find_token(unsigned char *str, int len,
620 				 const unsigned char *token)
621 {
622 	int i;
623 
624 	for (i = 0; i < len - 1; i++) {
625 		if (str[i] == token[0] && str[i+1] == token[1])
626 			return &str[i];
627 	}
628 	return NULL;
629 }
630 
631 /* replace a given token in all the valid symbols. Use the sampled symbols
632  * to update the counts */
633 static void compress_symbols(const unsigned char *str, int compressed_chr)
634 {
635 	const unsigned int token_idx = sym_token_index(str, 0);
636 	struct sym_arr *arr = &token_syms[token_idx];
637 	unsigned int sym_idx, j, len, size;
638 	unsigned char *p1, *p2;
639 
640 	/* Iterate through all symbols this token is found in and compress. */
641 	for (j = 0; j < arr->cnt; j++) {
642 		sym_idx = arr->sym_indexes[j];
643 
644 		len = table[sym_idx]->len;
645 		p1 = table[sym_idx]->sym;
646 
647 		/* find the token on the symbol */
648 		p2 = find_token(p1, len, str);
649 		if (!p2) continue;
650 
651 		/* decrease the counts for this symbol's tokens */
652 		forget_symbol(table[sym_idx]->sym, len);
653 
654 		size = len;
655 
656 		do {
657 			*p2 = compressed_chr;
658 			p2++;
659 			size -= (p2 - p1);
660 			memmove(p2, p2 + 1, size);
661 			p1 = p2;
662 			len--;
663 
664 			if (size < 2) break;
665 
666 			/* find the token on the symbol */
667 			p2 = find_token(p1, size, str);
668 
669 		} while (p2);
670 
671 		table[sym_idx]->len = len;
672 
673 		/* increase the counts for this symbol's new tokens */
674 		learn_symbol(table[sym_idx]->sym, len);
675 		symbol_index_new_tokens(table[sym_idx]->sym, len, sym_idx,
676 					compressed_chr);
677 	}
678 
679 	sym_arr_free(arr); /* No symbol contains this token any more. */
680 }
681 
682 /* search the token with the maximum profit */
683 static int find_best_token(void)
684 {
685 	int i, best, bestprofit;
686 
687 	bestprofit=-10000;
688 	best = 0;
689 
690 	for (i = 0; i < ARRAY_SIZE(token_profit); i++) {
691 		if (token_profit[i] > bestprofit) {
692 			best = i;
693 			bestprofit = token_profit[i];
694 		}
695 	}
696 	return best;
697 }
698 
699 /* this is the core of the algorithm: calculate the "best" table */
700 static void optimize_result(void)
701 {
702 	int i, best;
703 
704 	/* using the '\0' symbol last allows compress_symbols to use standard
705 	 * fast string functions */
706 	for (i = 255; i >= 0; i--) {
707 
708 		/* if this table slot is empty (it is not used by an actual
709 		 * original char code */
710 		if (!best_table_len[i]) {
711 
712 			/* find the token with the best profit value */
713 			best = find_best_token();
714 			if (token_profit[best] == 0)
715 				break;
716 
717 			/* place it in the "best" table */
718 			best_table_len[i] = 2;
719 			best_table[i][0] = best & 0xFF;
720 			best_table[i][1] = (best >> 8) & 0xFF;
721 
722 			/* replace this token in all the valid symbols */
723 			compress_symbols(best_table[i], i);
724 		}
725 	}
726 }
727 
728 /* start by placing the symbols that are actually used on the table */
729 static void insert_real_symbols_in_table(void)
730 {
731 	unsigned int i, j, c;
732 
733 	for (i = 0; i < table_cnt; i++) {
734 		for (j = 0; j < table[i]->len; j++) {
735 			c = table[i]->sym[j];
736 			best_table[c][0]=c;
737 			best_table_len[c]=1;
738 		}
739 	}
740 }
741 
742 static void optimize_token_table(void)
743 {
744 	build_initial_token_table();
745 
746 	insert_real_symbols_in_table();
747 
748 	optimize_result();
749 }
750 
751 /* guess for "linker script provide" symbol */
752 static int may_be_linker_script_provide_symbol(const struct sym_entry *se)
753 {
754 	const char *symbol = sym_entry_name(se);
755 	int len = se->len - 1;
756 
757 	if (len < 8)
758 		return 0;
759 
760 	if (symbol[0] != '_' || symbol[1] != '_')
761 		return 0;
762 
763 	/* __start_XXXXX */
764 	if (!memcmp(symbol + 2, "start_", 6))
765 		return 1;
766 
767 	/* __stop_XXXXX */
768 	if (!memcmp(symbol + 2, "stop_", 5))
769 		return 1;
770 
771 	/* __end_XXXXX */
772 	if (!memcmp(symbol + 2, "end_", 4))
773 		return 1;
774 
775 	/* __XXXXX_start */
776 	if (!memcmp(symbol + len - 6, "_start", 6))
777 		return 1;
778 
779 	/* __XXXXX_end */
780 	if (!memcmp(symbol + len - 4, "_end", 4))
781 		return 1;
782 
783 	return 0;
784 }
785 
786 static int compare_symbols(const void *a, const void *b)
787 {
788 	const struct sym_entry *sa = *(const struct sym_entry **)a;
789 	const struct sym_entry *sb = *(const struct sym_entry **)b;
790 	int wa, wb;
791 
792 	/* sort by address first */
793 	if (sa->addr > sb->addr)
794 		return 1;
795 	if (sa->addr < sb->addr)
796 		return -1;
797 
798 	/* sort by "weakness" type */
799 	wa = (sa->sym[0] == 'w') || (sa->sym[0] == 'W');
800 	wb = (sb->sym[0] == 'w') || (sb->sym[0] == 'W');
801 	if (wa != wb)
802 		return wa - wb;
803 
804 	/* sort by "linker script provide" type */
805 	wa = may_be_linker_script_provide_symbol(sa);
806 	wb = may_be_linker_script_provide_symbol(sb);
807 	if (wa != wb)
808 		return wa - wb;
809 
810 	/* sort by the number of prefix underscores */
811 	wa = strspn(sym_entry_name(sa), "_");
812 	wb = strspn(sym_entry_name(sb), "_");
813 	if (wa != wb)
814 		return wa - wb;
815 
816 	/* sort by initial order, so that other symbols are left undisturbed */
817 	return sa->seq - sb->seq;
818 }
819 
820 static void sort_symbols(void)
821 {
822 	qsort(table, table_cnt, sizeof(table[0]), compare_symbols);
823 }
824 
825 int main(int argc, char **argv)
826 {
827 	const char *in, *sysmap = NULL, *out_bin_name;
828 	FILE *sysmap_out = NULL, *out_bin_file;
829 
830 	while (1) {
831 		static const struct option long_options[] = {
832 			{"all-symbols",     no_argument, &all_symbols,     1},
833 			{"pc-relative",     no_argument, &pc_relative,     1},
834 			{"sysmap",    required_argument, NULL,           's'},
835 			{},
836 		};
837 
838 		int c = getopt_long(argc, argv, "", long_options, NULL);
839 
840 		if (c == -1)
841 			break;
842 		if (c == 's')
843 			sysmap = optarg;
844 		else if (c != 0)
845 			usage();
846 	}
847 
848 	if (optind + 2 != argc && !(sysmap && optind + 1 == argc))
849 		usage();
850 
851 	in = argv[optind];
852 	if (sysmap) {
853 		sysmap_out = fopen(sysmap, "w");
854 		if (!sysmap_out) {
855 			perror(sysmap);
856 			exit(EXIT_FAILURE);
857 		}
858 	}
859 
860 	if (optind + 1 == argc) {
861 		read_elf(in, sysmap_out);
862 		if (ferror(sysmap_out) || fclose(sysmap_out)) {
863 			perror(sysmap);
864 			exit(EXIT_FAILURE);
865 		}
866 		return 0;
867 	}
868 
869 	out_bin_name = argv[optind + 1];
870 	out_bin_file = fopen(out_bin_name, "w");
871 	if (!out_bin_file) {
872 		perror(out_bin_name);
873 		exit(EXIT_FAILURE);
874 	}
875 
876 	read_elf(in, sysmap_out);
877 	if (sysmap_out && (ferror(sysmap_out) || fclose(sysmap_out))) {
878 		perror(sysmap);
879 		exit(EXIT_FAILURE);
880 	}
881 	shrink_table();
882 	sort_symbols();
883 	optimize_token_table();
884 	write_src(out_bin_file, out_bin_name);
885 
886 	/* Write errors are latched in the stream, so one check at the end covers them all. */
887 	if (ferror(out_bin_file) || fclose(out_bin_file)) {
888 		perror(out_bin_name);
889 		exit(EXIT_FAILURE);
890 	}
891 
892 	return 0;
893 }
894