xref: /linux/scripts/kallsyms.c (revision fcdf7197cf23ef452c30f376d31d73bdf7946de8)
11da177e4SLinus Torvalds /* Generate assembler source containing symbol information
21da177e4SLinus Torvalds  *
31da177e4SLinus Torvalds  * Copyright 2002       by Kai Germaschewski
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * This software may be used and distributed according to the terms
61da177e4SLinus Torvalds  * of the GNU General Public License, incorporated herein by reference.
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * Usage: nm -n vmlinux | scripts/kallsyms [--all-symbols] > symbols.S
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  *      Table compression uses all the unused char codes on the symbols and
111da177e4SLinus Torvalds  *  maps these to the most used substrings (tokens). For instance, it might
121da177e4SLinus Torvalds  *  map char code 0xF7 to represent "write_" and then in every symbol where
131da177e4SLinus Torvalds  *  "write_" appears it can be replaced by 0xF7, saving 5 bytes.
141da177e4SLinus Torvalds  *      The used codes themselves are also placed in the table so that the
151da177e4SLinus Torvalds  *  decompresion can work without "special cases".
161da177e4SLinus Torvalds  *      Applied to kernel symbols, this usually produces a compression ratio
171da177e4SLinus Torvalds  *  of about 50%.
181da177e4SLinus Torvalds  *
191da177e4SLinus Torvalds  */
201da177e4SLinus Torvalds 
21aa221f2eSMasahiro Yamada #include <getopt.h>
22a41333e0SMasahiro Yamada #include <stdbool.h>
231da177e4SLinus Torvalds #include <stdio.h>
241da177e4SLinus Torvalds #include <stdlib.h>
251da177e4SLinus Torvalds #include <string.h>
261da177e4SLinus Torvalds #include <ctype.h>
272213e9a6SArd Biesheuvel #include <limits.h>
281da177e4SLinus Torvalds 
2917b1f0deSMike Frysinger #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
3017b1f0deSMike Frysinger 
31b471927eSBoqun Feng #define _stringify_1(x)	#x
32b471927eSBoqun Feng #define _stringify(x)	_stringify_1(x)
33b471927eSBoqun Feng 
34b8a94bfbSMiguel Ojeda #define KSYM_NAME_LEN		512
351da177e4SLinus Torvalds 
366e8c5bbdSMiguel Ojeda /*
376e8c5bbdSMiguel Ojeda  * A substantially bigger size than the current maximum.
386e8c5bbdSMiguel Ojeda  *
396e8c5bbdSMiguel Ojeda  * It cannot be defined as an expression because it gets stringified
406e8c5bbdSMiguel Ojeda  * for the fscanf() format string. Therefore, a _Static_assert() is
416e8c5bbdSMiguel Ojeda  * used instead to maintain the relationship with KSYM_NAME_LEN.
426e8c5bbdSMiguel Ojeda  */
43b8a94bfbSMiguel Ojeda #define KSYM_NAME_LEN_BUFFER	2048
446e8c5bbdSMiguel Ojeda _Static_assert(
456e8c5bbdSMiguel Ojeda 	KSYM_NAME_LEN_BUFFER == KSYM_NAME_LEN * 4,
466e8c5bbdSMiguel Ojeda 	"Please keep KSYM_NAME_LEN_BUFFER in sync with KSYM_NAME_LEN"
476e8c5bbdSMiguel Ojeda );
48b471927eSBoqun Feng 
491da177e4SLinus Torvalds struct sym_entry {
501da177e4SLinus Torvalds 	unsigned long long addr;
51b3dbb4ecSPaulo Marques 	unsigned int len;
52f2df3f65SPaulo Marques 	unsigned int start_pos;
538c996940SArd Biesheuvel 	unsigned int percpu_absolute;
549d82973eSLinus Torvalds 	unsigned char sym[];
551da177e4SLinus Torvalds };
561da177e4SLinus Torvalds 
5778eb7159SKees Cook struct addr_range {
5878eb7159SKees Cook 	const char *start_sym, *end_sym;
5917b1f0deSMike Frysinger 	unsigned long long start, end;
6017b1f0deSMike Frysinger };
6117b1f0deSMike Frysinger 
6217b1f0deSMike Frysinger static unsigned long long _text;
632213e9a6SArd Biesheuvel static unsigned long long relative_base;
6478eb7159SKees Cook static struct addr_range text_ranges[] = {
6517b1f0deSMike Frysinger 	{ "_stext",     "_etext"     },
6617b1f0deSMike Frysinger 	{ "_sinittext", "_einittext" },
6717b1f0deSMike Frysinger };
6817b1f0deSMike Frysinger #define text_range_text     (&text_ranges[0])
6917b1f0deSMike Frysinger #define text_range_inittext (&text_ranges[1])
7017b1f0deSMike Frysinger 
71c6bda7c9SRusty Russell static struct addr_range percpu_range = {
72c6bda7c9SRusty Russell 	"__per_cpu_start", "__per_cpu_end", -1ULL, 0
73c6bda7c9SRusty Russell };
74c6bda7c9SRusty Russell 
758d605269SMasahiro Yamada static struct sym_entry **table;
76b3dbb4ecSPaulo Marques static unsigned int table_size, table_cnt;
77831362fcSMasahiro Yamada static int all_symbols;
78831362fcSMasahiro Yamada static int absolute_percpu;
79831362fcSMasahiro Yamada static int base_relative;
801da177e4SLinus Torvalds 
81f43e9daaSMasahiro Yamada static int token_profit[0x10000];
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds /* the table that holds the result of the compression */
84f43e9daaSMasahiro Yamada static unsigned char best_table[256][2];
85f43e9daaSMasahiro Yamada static unsigned char best_table_len[256];
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds 
88b3dbb4ecSPaulo Marques static void usage(void)
891da177e4SLinus Torvalds {
908d3a7507SYuntao Wang 	fprintf(stderr, "Usage: kallsyms [--all-symbols] [--absolute-percpu] "
91aa221f2eSMasahiro Yamada 			"[--base-relative] in.map > out.S\n");
921da177e4SLinus Torvalds 	exit(1);
931da177e4SLinus Torvalds }
941da177e4SLinus Torvalds 
9529e55ad3SMasahiro Yamada static char *sym_name(const struct sym_entry *s)
9629e55ad3SMasahiro Yamada {
9729e55ad3SMasahiro Yamada 	return (char *)s->sym + 1;
9829e55ad3SMasahiro Yamada }
9929e55ad3SMasahiro Yamada 
100a41333e0SMasahiro Yamada static bool is_ignored_symbol(const char *name, char type)
101a41333e0SMasahiro Yamada {
102516d980fSMasahiro Yamada 	/* Symbol names that exactly match to the following are ignored.*/
103a41333e0SMasahiro Yamada 	static const char * const ignored_symbols[] = {
104a41333e0SMasahiro Yamada 		/*
105a41333e0SMasahiro Yamada 		 * Symbols which vary between passes. Passes 1 and 2 must have
106a41333e0SMasahiro Yamada 		 * identical symbol lists. The kallsyms_* symbols below are
107a41333e0SMasahiro Yamada 		 * only added after pass 1, they would be included in pass 2
108a41333e0SMasahiro Yamada 		 * when --all-symbols is specified so exclude them to get a
109a41333e0SMasahiro Yamada 		 * stable symbol list.
110a41333e0SMasahiro Yamada 		 */
111a41333e0SMasahiro Yamada 		"kallsyms_addresses",
112a41333e0SMasahiro Yamada 		"kallsyms_offsets",
113a41333e0SMasahiro Yamada 		"kallsyms_relative_base",
114a41333e0SMasahiro Yamada 		"kallsyms_num_syms",
115a41333e0SMasahiro Yamada 		"kallsyms_names",
116a41333e0SMasahiro Yamada 		"kallsyms_markers",
117a41333e0SMasahiro Yamada 		"kallsyms_token_table",
118a41333e0SMasahiro Yamada 		"kallsyms_token_index",
119a41333e0SMasahiro Yamada 		/* Exclude linker generated symbols which vary between passes */
120a41333e0SMasahiro Yamada 		"_SDA_BASE_",		/* ppc */
121a41333e0SMasahiro Yamada 		"_SDA2_BASE_",		/* ppc */
122a41333e0SMasahiro Yamada 		NULL
123a41333e0SMasahiro Yamada 	};
124a41333e0SMasahiro Yamada 
125516d980fSMasahiro Yamada 	/* Symbol names that begin with the following are ignored.*/
126a41333e0SMasahiro Yamada 	static const char * const ignored_prefixes[] = {
127a41333e0SMasahiro Yamada 		"__efistub_",		/* arm64 EFI stub namespace */
1286ccf9cb5SKalesh Singh 		"__kvm_nvhe_$",		/* arm64 local symbols in non-VHE KVM namespace */
1296ccf9cb5SKalesh Singh 		"__kvm_nvhe_.L",	/* arm64 local symbols in non-VHE KVM namespace */
130efe6e306SArnd Bergmann 		"__AArch64ADRPThunk_",	/* arm64 lld */
131efe6e306SArnd Bergmann 		"__ARMV5PILongThunk_",	/* arm lld */
132efe6e306SArnd Bergmann 		"__ARMV7PILongThunk_",
133efe6e306SArnd Bergmann 		"__ThumbV7PILongThunk_",
134efe6e306SArnd Bergmann 		"__LA25Thunk_",		/* mips lld */
135efe6e306SArnd Bergmann 		"__microLA25Thunk_",
136d0f9562eSSami Tolvanen 		"__kcfi_typeid_",	/* CFI type identifiers */
137a41333e0SMasahiro Yamada 		NULL
138a41333e0SMasahiro Yamada 	};
139a41333e0SMasahiro Yamada 
140516d980fSMasahiro Yamada 	/* Symbol names that end with the following are ignored.*/
141a41333e0SMasahiro Yamada 	static const char * const ignored_suffixes[] = {
142a41333e0SMasahiro Yamada 		"_from_arm",		/* arm */
143a41333e0SMasahiro Yamada 		"_from_thumb",		/* arm */
144a41333e0SMasahiro Yamada 		"_veneer",		/* arm */
145a41333e0SMasahiro Yamada 		NULL
146a41333e0SMasahiro Yamada 	};
147a41333e0SMasahiro Yamada 
148516d980fSMasahiro Yamada 	/* Symbol names that contain the following are ignored.*/
149516d980fSMasahiro Yamada 	static const char * const ignored_matches[] = {
150516d980fSMasahiro Yamada 		".long_branch.",	/* ppc stub */
151516d980fSMasahiro Yamada 		".plt_branch.",		/* ppc stub */
152516d980fSMasahiro Yamada 		NULL
153516d980fSMasahiro Yamada 	};
154516d980fSMasahiro Yamada 
155a41333e0SMasahiro Yamada 	const char * const *p;
156a41333e0SMasahiro Yamada 
157a41333e0SMasahiro Yamada 	for (p = ignored_symbols; *p; p++)
158a41333e0SMasahiro Yamada 		if (!strcmp(name, *p))
159a41333e0SMasahiro Yamada 			return true;
160a41333e0SMasahiro Yamada 
161a41333e0SMasahiro Yamada 	for (p = ignored_prefixes; *p; p++)
162a41333e0SMasahiro Yamada 		if (!strncmp(name, *p, strlen(*p)))
163a41333e0SMasahiro Yamada 			return true;
164a41333e0SMasahiro Yamada 
165a41333e0SMasahiro Yamada 	for (p = ignored_suffixes; *p; p++) {
166a41333e0SMasahiro Yamada 		int l = strlen(name) - strlen(*p);
167a41333e0SMasahiro Yamada 
168a41333e0SMasahiro Yamada 		if (l >= 0 && !strcmp(name + l, *p))
169a41333e0SMasahiro Yamada 			return true;
170a41333e0SMasahiro Yamada 	}
171a41333e0SMasahiro Yamada 
172516d980fSMasahiro Yamada 	for (p = ignored_matches; *p; p++) {
173516d980fSMasahiro Yamada 		if (strstr(name, *p))
174516d980fSMasahiro Yamada 			return true;
175516d980fSMasahiro Yamada 	}
176516d980fSMasahiro Yamada 
177887df76dSMasahiro Yamada 	if (type == 'U' || type == 'u')
178887df76dSMasahiro Yamada 		return true;
179887df76dSMasahiro Yamada 	/* exclude debugging symbols */
180887df76dSMasahiro Yamada 	if (type == 'N' || type == 'n')
181887df76dSMasahiro Yamada 		return true;
182887df76dSMasahiro Yamada 
183887df76dSMasahiro Yamada 	if (toupper(type) == 'A') {
184887df76dSMasahiro Yamada 		/* Keep these useful absolute symbols */
185887df76dSMasahiro Yamada 		if (strcmp(name, "__kernel_syscall_via_break") &&
186887df76dSMasahiro Yamada 		    strcmp(name, "__kernel_syscall_via_epc") &&
187887df76dSMasahiro Yamada 		    strcmp(name, "__kernel_sigtramp") &&
188887df76dSMasahiro Yamada 		    strcmp(name, "__gp"))
189887df76dSMasahiro Yamada 			return true;
190887df76dSMasahiro Yamada 	}
191887df76dSMasahiro Yamada 
192a41333e0SMasahiro Yamada 	return false;
193a41333e0SMasahiro Yamada }
194a41333e0SMasahiro Yamada 
195b6233d0dSMasahiro Yamada static void check_symbol_range(const char *sym, unsigned long long addr,
19678eb7159SKees Cook 			       struct addr_range *ranges, int entries)
19717b1f0deSMike Frysinger {
19817b1f0deSMike Frysinger 	size_t i;
19978eb7159SKees Cook 	struct addr_range *ar;
20017b1f0deSMike Frysinger 
20178eb7159SKees Cook 	for (i = 0; i < entries; ++i) {
20278eb7159SKees Cook 		ar = &ranges[i];
20317b1f0deSMike Frysinger 
20478eb7159SKees Cook 		if (strcmp(sym, ar->start_sym) == 0) {
20578eb7159SKees Cook 			ar->start = addr;
206b6233d0dSMasahiro Yamada 			return;
20778eb7159SKees Cook 		} else if (strcmp(sym, ar->end_sym) == 0) {
20878eb7159SKees Cook 			ar->end = addr;
209b6233d0dSMasahiro Yamada 			return;
21017b1f0deSMike Frysinger 		}
21117b1f0deSMike Frysinger 	}
21217b1f0deSMike Frysinger }
21317b1f0deSMike Frysinger 
2148d605269SMasahiro Yamada static struct sym_entry *read_symbol(FILE *in)
2151da177e4SLinus Torvalds {
216b471927eSBoqun Feng 	char name[KSYM_NAME_LEN_BUFFER+1], type;
2178d605269SMasahiro Yamada 	unsigned long long addr;
2188d605269SMasahiro Yamada 	unsigned int len;
2198d605269SMasahiro Yamada 	struct sym_entry *sym;
2201da177e4SLinus Torvalds 	int rc;
2211da177e4SLinus Torvalds 
222b471927eSBoqun Feng 	rc = fscanf(in, "%llx %c %" _stringify(KSYM_NAME_LEN_BUFFER) "s\n", &addr, &type, name);
2231da177e4SLinus Torvalds 	if (rc != 3) {
224b66c874fSBoqun Feng 		if (rc != EOF && fgets(name, ARRAY_SIZE(name), in) == NULL)
225ef894870SJean Sacren 			fprintf(stderr, "Read error or end of file.\n");
2268d605269SMasahiro Yamada 		return NULL;
2271da177e4SLinus Torvalds 	}
228be9f6133SMasahiro Yamada 	if (strlen(name) >= KSYM_NAME_LEN) {
2296db2983cSEugene Loh 		fprintf(stderr, "Symbol %s too long for kallsyms (%zu >= %d).\n"
230f3462aa9SAndi Kleen 				"Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n",
231be9f6133SMasahiro Yamada 			name, strlen(name), KSYM_NAME_LEN);
2328d605269SMasahiro Yamada 		return NULL;
233f3462aa9SAndi Kleen 	}
2341da177e4SLinus Torvalds 
235be9f6133SMasahiro Yamada 	if (strcmp(name, "_text") == 0)
2368d605269SMasahiro Yamada 		_text = addr;
237b6233d0dSMasahiro Yamada 
2387883a143SMikhail Petrov 	/* Ignore most absolute/undefined (?) symbols. */
2397883a143SMikhail Petrov 	if (is_ignored_symbol(name, type))
2407883a143SMikhail Petrov 		return NULL;
2417883a143SMikhail Petrov 
2428d605269SMasahiro Yamada 	check_symbol_range(name, addr, text_ranges, ARRAY_SIZE(text_ranges));
2438d605269SMasahiro Yamada 	check_symbol_range(name, addr, &percpu_range, 1);
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 	/* include the type field in the symbol name, so that it gets
2461da177e4SLinus Torvalds 	 * compressed together */
2478d605269SMasahiro Yamada 
2488d605269SMasahiro Yamada 	len = strlen(name) + 1;
2498d605269SMasahiro Yamada 
2509d1b3895SMasahiro Yamada 	sym = malloc(sizeof(*sym) + len + 1);
2518d605269SMasahiro Yamada 	if (!sym) {
252f1a136e0SJesper Juhl 		fprintf(stderr, "kallsyms failure: "
253f1a136e0SJesper Juhl 			"unable to allocate required amount of memory\n");
254f1a136e0SJesper Juhl 		exit(EXIT_FAILURE);
255f1a136e0SJesper Juhl 	}
2568d605269SMasahiro Yamada 	sym->addr = addr;
2578d605269SMasahiro Yamada 	sym->len = len;
2588d605269SMasahiro Yamada 	sym->sym[0] = type;
2599d1b3895SMasahiro Yamada 	strcpy(sym_name(sym), name);
2608d605269SMasahiro Yamada 	sym->percpu_absolute = 0;
2611da177e4SLinus Torvalds 
2628d605269SMasahiro Yamada 	return sym;
2631da177e4SLinus Torvalds }
2641da177e4SLinus Torvalds 
2654bfe2b78SMasahiro Yamada static int symbol_in_range(const struct sym_entry *s,
2664bfe2b78SMasahiro Yamada 			   const struct addr_range *ranges, int entries)
26717b1f0deSMike Frysinger {
26817b1f0deSMike Frysinger 	size_t i;
2694bfe2b78SMasahiro Yamada 	const struct addr_range *ar;
27017b1f0deSMike Frysinger 
27178eb7159SKees Cook 	for (i = 0; i < entries; ++i) {
27278eb7159SKees Cook 		ar = &ranges[i];
27317b1f0deSMike Frysinger 
27478eb7159SKees Cook 		if (s->addr >= ar->start && s->addr <= ar->end)
275ac6ca5c8SMike Frysinger 			return 1;
27617b1f0deSMike Frysinger 	}
27717b1f0deSMike Frysinger 
278ac6ca5c8SMike Frysinger 	return 0;
27917b1f0deSMike Frysinger }
28017b1f0deSMike Frysinger 
2814bfe2b78SMasahiro Yamada static int symbol_valid(const struct sym_entry *s)
2821da177e4SLinus Torvalds {
28329e55ad3SMasahiro Yamada 	const char *name = sym_name(s);
284bd8b22d2SArd Biesheuvel 
2851da177e4SLinus Torvalds 	/* if --all-symbols is not specified, then symbols outside the text
2861da177e4SLinus Torvalds 	 * and inittext sections are discarded */
2871da177e4SLinus Torvalds 	if (!all_symbols) {
28878eb7159SKees Cook 		if (symbol_in_range(s, text_ranges,
28978eb7159SKees Cook 				    ARRAY_SIZE(text_ranges)) == 0)
2901da177e4SLinus Torvalds 			return 0;
2911da177e4SLinus Torvalds 		/* Corner case.  Discard any symbols with the same value as
292a3b81113SRobin Getz 		 * _etext _einittext; they can move between pass 1 and 2 when
293a3b81113SRobin Getz 		 * the kallsyms data are added.  If these symbols move then
294a3b81113SRobin Getz 		 * they may get dropped in pass 2, which breaks the kallsyms
295a3b81113SRobin Getz 		 * rules.
2961da177e4SLinus Torvalds 		 */
29717b1f0deSMike Frysinger 		if ((s->addr == text_range_text->end &&
29829e55ad3SMasahiro Yamada 		     strcmp(name, text_range_text->end_sym)) ||
29917b1f0deSMike Frysinger 		    (s->addr == text_range_inittext->end &&
30029e55ad3SMasahiro Yamada 		     strcmp(name, text_range_inittext->end_sym)))
3011da177e4SLinus Torvalds 			return 0;
3021da177e4SLinus Torvalds 	}
3031da177e4SLinus Torvalds 
3041da177e4SLinus Torvalds 	return 1;
3051da177e4SLinus Torvalds }
3061da177e4SLinus Torvalds 
3075e5c4fa7SMasahiro Yamada /* remove all the invalid symbols from the table */
3085e5c4fa7SMasahiro Yamada static void shrink_table(void)
3095e5c4fa7SMasahiro Yamada {
3105e5c4fa7SMasahiro Yamada 	unsigned int i, pos;
3115e5c4fa7SMasahiro Yamada 
3125e5c4fa7SMasahiro Yamada 	pos = 0;
3135e5c4fa7SMasahiro Yamada 	for (i = 0; i < table_cnt; i++) {
3148d605269SMasahiro Yamada 		if (symbol_valid(table[i])) {
3155e5c4fa7SMasahiro Yamada 			if (pos != i)
3165e5c4fa7SMasahiro Yamada 				table[pos] = table[i];
3175e5c4fa7SMasahiro Yamada 			pos++;
3185e5c4fa7SMasahiro Yamada 		} else {
3198d605269SMasahiro Yamada 			free(table[i]);
3205e5c4fa7SMasahiro Yamada 		}
3215e5c4fa7SMasahiro Yamada 	}
3225e5c4fa7SMasahiro Yamada 	table_cnt = pos;
3235e5c4fa7SMasahiro Yamada 
3245e5c4fa7SMasahiro Yamada 	/* When valid symbol is not registered, exit to error */
3255e5c4fa7SMasahiro Yamada 	if (!table_cnt) {
3265e5c4fa7SMasahiro Yamada 		fprintf(stderr, "No valid symbol.\n");
3275e5c4fa7SMasahiro Yamada 		exit(1);
3285e5c4fa7SMasahiro Yamada 	}
3295e5c4fa7SMasahiro Yamada }
3305e5c4fa7SMasahiro Yamada 
331aa221f2eSMasahiro Yamada static void read_map(const char *in)
3321da177e4SLinus Torvalds {
333aa221f2eSMasahiro Yamada 	FILE *fp;
3348d605269SMasahiro Yamada 	struct sym_entry *sym;
3358d605269SMasahiro Yamada 
336aa221f2eSMasahiro Yamada 	fp = fopen(in, "r");
337aa221f2eSMasahiro Yamada 	if (!fp) {
338aa221f2eSMasahiro Yamada 		perror(in);
339aa221f2eSMasahiro Yamada 		exit(1);
340aa221f2eSMasahiro Yamada 	}
341aa221f2eSMasahiro Yamada 
342aa221f2eSMasahiro Yamada 	while (!feof(fp)) {
343aa221f2eSMasahiro Yamada 		sym = read_symbol(fp);
3448d605269SMasahiro Yamada 		if (!sym)
3458d605269SMasahiro Yamada 			continue;
3468d605269SMasahiro Yamada 
3478d605269SMasahiro Yamada 		sym->start_pos = table_cnt;
3488d605269SMasahiro Yamada 
349b3dbb4ecSPaulo Marques 		if (table_cnt >= table_size) {
350b3dbb4ecSPaulo Marques 			table_size += 10000;
351b3dbb4ecSPaulo Marques 			table = realloc(table, sizeof(*table) * table_size);
3521da177e4SLinus Torvalds 			if (!table) {
3531da177e4SLinus Torvalds 				fprintf(stderr, "out of memory\n");
354aa221f2eSMasahiro Yamada 				fclose(fp);
3551da177e4SLinus Torvalds 				exit (1);
3561da177e4SLinus Torvalds 			}
3571da177e4SLinus Torvalds 		}
3588d605269SMasahiro Yamada 
3598d605269SMasahiro Yamada 		table[table_cnt++] = sym;
3601da177e4SLinus Torvalds 	}
361aa221f2eSMasahiro Yamada 
362aa221f2eSMasahiro Yamada 	fclose(fp);
363f2df3f65SPaulo Marques }
3641da177e4SLinus Torvalds 
3654bfe2b78SMasahiro Yamada static void output_label(const char *label)
3661da177e4SLinus Torvalds {
3671da177e4SLinus Torvalds 	printf(".globl %s\n", label);
3681da177e4SLinus Torvalds 	printf("\tALGN\n");
3691da177e4SLinus Torvalds 	printf("%s:\n", label);
3701da177e4SLinus Torvalds }
3711da177e4SLinus Torvalds 
372fd2ab2f6SMasahiro Yamada /* Provide proper symbols relocatability by their '_text' relativeness. */
373fd2ab2f6SMasahiro Yamada static void output_address(unsigned long long addr)
374fd2ab2f6SMasahiro Yamada {
375fd2ab2f6SMasahiro Yamada 	if (_text <= addr)
376fd2ab2f6SMasahiro Yamada 		printf("\tPTR\t_text + %#llx\n", addr - _text);
377fd2ab2f6SMasahiro Yamada 	else
378fd2ab2f6SMasahiro Yamada 		printf("\tPTR\t_text - %#llx\n", _text - addr);
379fd2ab2f6SMasahiro Yamada }
380fd2ab2f6SMasahiro Yamada 
3811da177e4SLinus Torvalds /* uncompress a compressed symbol. When this function is called, the best table
3821da177e4SLinus Torvalds  * might still be compressed itself, so the function needs to be recursive */
3834bfe2b78SMasahiro Yamada static int expand_symbol(const unsigned char *data, int len, char *result)
3841da177e4SLinus Torvalds {
3851da177e4SLinus Torvalds 	int c, rlen, total=0;
3861da177e4SLinus Torvalds 
3871da177e4SLinus Torvalds 	while (len) {
3881da177e4SLinus Torvalds 		c = *data;
3891da177e4SLinus Torvalds 		/* if the table holds a single char that is the same as the one
3901da177e4SLinus Torvalds 		 * we are looking for, then end the search */
3911da177e4SLinus Torvalds 		if (best_table[c][0]==c && best_table_len[c]==1) {
3921da177e4SLinus Torvalds 			*result++ = c;
3931da177e4SLinus Torvalds 			total++;
3941da177e4SLinus Torvalds 		} else {
3951da177e4SLinus Torvalds 			/* if not, recurse and expand */
3961da177e4SLinus Torvalds 			rlen = expand_symbol(best_table[c], best_table_len[c], result);
3971da177e4SLinus Torvalds 			total += rlen;
3981da177e4SLinus Torvalds 			result += rlen;
3991da177e4SLinus Torvalds 		}
4001da177e4SLinus Torvalds 		data++;
4011da177e4SLinus Torvalds 		len--;
4021da177e4SLinus Torvalds 	}
4031da177e4SLinus Torvalds 	*result=0;
4041da177e4SLinus Torvalds 
4051da177e4SLinus Torvalds 	return total;
4061da177e4SLinus Torvalds }
4071da177e4SLinus Torvalds 
4084bfe2b78SMasahiro Yamada static int symbol_absolute(const struct sym_entry *s)
40978eb7159SKees Cook {
4108c996940SArd Biesheuvel 	return s->percpu_absolute;
41178eb7159SKees Cook }
41278eb7159SKees Cook 
413b3dbb4ecSPaulo Marques static void write_src(void)
4141da177e4SLinus Torvalds {
415b3dbb4ecSPaulo Marques 	unsigned int i, k, off;
4161da177e4SLinus Torvalds 	unsigned int best_idx[256];
4171da177e4SLinus Torvalds 	unsigned int *markers;
4189281aceaSTejun Heo 	char buf[KSYM_NAME_LEN];
4191da177e4SLinus Torvalds 
420500193ecSMasahiro Yamada 	printf("#include <asm/bitsperlong.h>\n");
4211da177e4SLinus Torvalds 	printf("#if BITS_PER_LONG == 64\n");
4221da177e4SLinus Torvalds 	printf("#define PTR .quad\n");
42372d3ebb9SMathias Krause 	printf("#define ALGN .balign 8\n");
4241da177e4SLinus Torvalds 	printf("#else\n");
4251da177e4SLinus Torvalds 	printf("#define PTR .long\n");
42672d3ebb9SMathias Krause 	printf("#define ALGN .balign 4\n");
4271da177e4SLinus Torvalds 	printf("#endif\n");
4281da177e4SLinus Torvalds 
429aad09470SJan Beulich 	printf("\t.section .rodata, \"a\"\n");
4301da177e4SLinus Torvalds 
4312213e9a6SArd Biesheuvel 	if (!base_relative)
4321da177e4SLinus Torvalds 		output_label("kallsyms_addresses");
4332213e9a6SArd Biesheuvel 	else
4342213e9a6SArd Biesheuvel 		output_label("kallsyms_offsets");
4352213e9a6SArd Biesheuvel 
436b3dbb4ecSPaulo Marques 	for (i = 0; i < table_cnt; i++) {
4372213e9a6SArd Biesheuvel 		if (base_relative) {
438fd2ab2f6SMasahiro Yamada 			/*
439fd2ab2f6SMasahiro Yamada 			 * Use the offset relative to the lowest value
440fd2ab2f6SMasahiro Yamada 			 * encountered of all relative symbols, and emit
441fd2ab2f6SMasahiro Yamada 			 * non-relocatable fixed offsets that will be fixed
442fd2ab2f6SMasahiro Yamada 			 * up at runtime.
443fd2ab2f6SMasahiro Yamada 			 */
444fd2ab2f6SMasahiro Yamada 
4452213e9a6SArd Biesheuvel 			long long offset;
4462213e9a6SArd Biesheuvel 			int overflow;
4472213e9a6SArd Biesheuvel 
4482213e9a6SArd Biesheuvel 			if (!absolute_percpu) {
4498d605269SMasahiro Yamada 				offset = table[i]->addr - relative_base;
4502213e9a6SArd Biesheuvel 				overflow = (offset < 0 || offset > UINT_MAX);
4518d605269SMasahiro Yamada 			} else if (symbol_absolute(table[i])) {
4528d605269SMasahiro Yamada 				offset = table[i]->addr;
4532213e9a6SArd Biesheuvel 				overflow = (offset < 0 || offset > INT_MAX);
4542213e9a6SArd Biesheuvel 			} else {
4558d605269SMasahiro Yamada 				offset = relative_base - table[i]->addr - 1;
4562213e9a6SArd Biesheuvel 				overflow = (offset < INT_MIN || offset >= 0);
4572213e9a6SArd Biesheuvel 			}
4582213e9a6SArd Biesheuvel 			if (overflow) {
4592213e9a6SArd Biesheuvel 				fprintf(stderr, "kallsyms failure: "
4602213e9a6SArd Biesheuvel 					"%s symbol value %#llx out of range in relative mode\n",
4618d605269SMasahiro Yamada 					symbol_absolute(table[i]) ? "absolute" : "relative",
4628d605269SMasahiro Yamada 					table[i]->addr);
4632213e9a6SArd Biesheuvel 				exit(EXIT_FAILURE);
4642213e9a6SArd Biesheuvel 			}
4652213e9a6SArd Biesheuvel 			printf("\t.long\t%#x\n", (int)offset);
4668d605269SMasahiro Yamada 		} else if (!symbol_absolute(table[i])) {
4678d605269SMasahiro Yamada 			output_address(table[i]->addr);
468fd593d12SEric W. Biederman 		} else {
4698d605269SMasahiro Yamada 			printf("\tPTR\t%#llx\n", table[i]->addr);
4701da177e4SLinus Torvalds 		}
471fd593d12SEric W. Biederman 	}
4721da177e4SLinus Torvalds 	printf("\n");
4731da177e4SLinus Torvalds 
4742213e9a6SArd Biesheuvel 	if (base_relative) {
4752213e9a6SArd Biesheuvel 		output_label("kallsyms_relative_base");
476fd2ab2f6SMasahiro Yamada 		output_address(relative_base);
4772213e9a6SArd Biesheuvel 		printf("\n");
4782213e9a6SArd Biesheuvel 	}
4792213e9a6SArd Biesheuvel 
4801da177e4SLinus Torvalds 	output_label("kallsyms_num_syms");
48180ffbaa5SJan Beulich 	printf("\t.long\t%u\n", table_cnt);
4821da177e4SLinus Torvalds 	printf("\n");
4831da177e4SLinus Torvalds 
4841da177e4SLinus Torvalds 	/* table of offset markers, that give the offset in the compressed stream
4851da177e4SLinus Torvalds 	 * every 256 symbols */
486f1a136e0SJesper Juhl 	markers = malloc(sizeof(unsigned int) * ((table_cnt + 255) / 256));
487f1a136e0SJesper Juhl 	if (!markers) {
488f1a136e0SJesper Juhl 		fprintf(stderr, "kallsyms failure: "
489f1a136e0SJesper Juhl 			"unable to allocate required memory\n");
490f1a136e0SJesper Juhl 		exit(EXIT_FAILURE);
491f1a136e0SJesper Juhl 	}
4921da177e4SLinus Torvalds 
4931da177e4SLinus Torvalds 	output_label("kallsyms_names");
4941da177e4SLinus Torvalds 	off = 0;
495b3dbb4ecSPaulo Marques 	for (i = 0; i < table_cnt; i++) {
496b3dbb4ecSPaulo Marques 		if ((i & 0xFF) == 0)
497b3dbb4ecSPaulo Marques 			markers[i >> 8] = off;
4981da177e4SLinus Torvalds 
49973bbb944SMiguel Ojeda 		/* There cannot be any symbol of length zero. */
50073bbb944SMiguel Ojeda 		if (table[i]->len == 0) {
50173bbb944SMiguel Ojeda 			fprintf(stderr, "kallsyms failure: "
50273bbb944SMiguel Ojeda 				"unexpected zero symbol length\n");
50373bbb944SMiguel Ojeda 			exit(EXIT_FAILURE);
50473bbb944SMiguel Ojeda 		}
50573bbb944SMiguel Ojeda 
50673bbb944SMiguel Ojeda 		/* Only lengths that fit in up-to-two-byte ULEB128 are supported. */
50773bbb944SMiguel Ojeda 		if (table[i]->len > 0x3FFF) {
50873bbb944SMiguel Ojeda 			fprintf(stderr, "kallsyms failure: "
50973bbb944SMiguel Ojeda 				"unexpected huge symbol length\n");
51073bbb944SMiguel Ojeda 			exit(EXIT_FAILURE);
51173bbb944SMiguel Ojeda 		}
51273bbb944SMiguel Ojeda 
51373bbb944SMiguel Ojeda 		/* Encode length with ULEB128. */
51473bbb944SMiguel Ojeda 		if (table[i]->len <= 0x7F) {
51573bbb944SMiguel Ojeda 			/* Most symbols use a single byte for the length. */
5168d605269SMasahiro Yamada 			printf("\t.byte 0x%02x", table[i]->len);
51773bbb944SMiguel Ojeda 			off += table[i]->len + 1;
51873bbb944SMiguel Ojeda 		} else {
51973bbb944SMiguel Ojeda 			/* "Big" symbols use two bytes. */
52073bbb944SMiguel Ojeda 			printf("\t.byte 0x%02x, 0x%02x",
52173bbb944SMiguel Ojeda 				(table[i]->len & 0x7F) | 0x80,
52273bbb944SMiguel Ojeda 				(table[i]->len >> 7) & 0x7F);
52373bbb944SMiguel Ojeda 			off += table[i]->len + 2;
52473bbb944SMiguel Ojeda 		}
5258d605269SMasahiro Yamada 		for (k = 0; k < table[i]->len; k++)
5268d605269SMasahiro Yamada 			printf(", 0x%02x", table[i]->sym[k]);
5271da177e4SLinus Torvalds 		printf("\n");
5281da177e4SLinus Torvalds 	}
5291da177e4SLinus Torvalds 	printf("\n");
5301da177e4SLinus Torvalds 
5311da177e4SLinus Torvalds 	output_label("kallsyms_markers");
532b3dbb4ecSPaulo Marques 	for (i = 0; i < ((table_cnt + 255) >> 8); i++)
53380ffbaa5SJan Beulich 		printf("\t.long\t%u\n", markers[i]);
5341da177e4SLinus Torvalds 	printf("\n");
5351da177e4SLinus Torvalds 
5361da177e4SLinus Torvalds 	free(markers);
5371da177e4SLinus Torvalds 
5381da177e4SLinus Torvalds 	output_label("kallsyms_token_table");
5391da177e4SLinus Torvalds 	off = 0;
5401da177e4SLinus Torvalds 	for (i = 0; i < 256; i++) {
5411da177e4SLinus Torvalds 		best_idx[i] = off;
5421da177e4SLinus Torvalds 		expand_symbol(best_table[i], best_table_len[i], buf);
5431da177e4SLinus Torvalds 		printf("\t.asciz\t\"%s\"\n", buf);
5441da177e4SLinus Torvalds 		off += strlen(buf) + 1;
5451da177e4SLinus Torvalds 	}
5461da177e4SLinus Torvalds 	printf("\n");
5471da177e4SLinus Torvalds 
5481da177e4SLinus Torvalds 	output_label("kallsyms_token_index");
5491da177e4SLinus Torvalds 	for (i = 0; i < 256; i++)
5501da177e4SLinus Torvalds 		printf("\t.short\t%d\n", best_idx[i]);
5511da177e4SLinus Torvalds 	printf("\n");
5521da177e4SLinus Torvalds }
5531da177e4SLinus Torvalds 
5541da177e4SLinus Torvalds 
5551da177e4SLinus Torvalds /* table lookup compression functions */
5561da177e4SLinus Torvalds 
5571da177e4SLinus Torvalds /* count all the possible tokens in a symbol */
5584bfe2b78SMasahiro Yamada static void learn_symbol(const unsigned char *symbol, int len)
5591da177e4SLinus Torvalds {
5601da177e4SLinus Torvalds 	int i;
5611da177e4SLinus Torvalds 
5621da177e4SLinus Torvalds 	for (i = 0; i < len - 1; i++)
563b3dbb4ecSPaulo Marques 		token_profit[ symbol[i] + (symbol[i + 1] << 8) ]++;
5641da177e4SLinus Torvalds }
5651da177e4SLinus Torvalds 
5661da177e4SLinus Torvalds /* decrease the count for all the possible tokens in a symbol */
5674bfe2b78SMasahiro Yamada static void forget_symbol(const unsigned char *symbol, int len)
5681da177e4SLinus Torvalds {
5691da177e4SLinus Torvalds 	int i;
5701da177e4SLinus Torvalds 
5711da177e4SLinus Torvalds 	for (i = 0; i < len - 1; i++)
572b3dbb4ecSPaulo Marques 		token_profit[ symbol[i] + (symbol[i + 1] << 8) ]--;
5731da177e4SLinus Torvalds }
5741da177e4SLinus Torvalds 
5755e5c4fa7SMasahiro Yamada /* do the initial token count */
576*fcdf7197SZhen Lei static void build_initial_token_table(void)
5771da177e4SLinus Torvalds {
5785e5c4fa7SMasahiro Yamada 	unsigned int i;
5791da177e4SLinus Torvalds 
5805e5c4fa7SMasahiro Yamada 	for (i = 0; i < table_cnt; i++)
5818d605269SMasahiro Yamada 		learn_symbol(table[i]->sym, table[i]->len);
5821da177e4SLinus Torvalds }
5831da177e4SLinus Torvalds 
5842558c138SMasahiro Yamada static unsigned char *find_token(unsigned char *str, int len,
5854bfe2b78SMasahiro Yamada 				 const unsigned char *token)
5867c5d249aSPaulo Marques {
5877c5d249aSPaulo Marques 	int i;
5887c5d249aSPaulo Marques 
5897c5d249aSPaulo Marques 	for (i = 0; i < len - 1; i++) {
5907c5d249aSPaulo Marques 		if (str[i] == token[0] && str[i+1] == token[1])
5917c5d249aSPaulo Marques 			return &str[i];
5927c5d249aSPaulo Marques 	}
5937c5d249aSPaulo Marques 	return NULL;
5947c5d249aSPaulo Marques }
5957c5d249aSPaulo Marques 
5961da177e4SLinus Torvalds /* replace a given token in all the valid symbols. Use the sampled symbols
5971da177e4SLinus Torvalds  * to update the counts */
5984bfe2b78SMasahiro Yamada static void compress_symbols(const unsigned char *str, int idx)
5991da177e4SLinus Torvalds {
600b3dbb4ecSPaulo Marques 	unsigned int i, len, size;
601b3dbb4ecSPaulo Marques 	unsigned char *p1, *p2;
6021da177e4SLinus Torvalds 
603b3dbb4ecSPaulo Marques 	for (i = 0; i < table_cnt; i++) {
6041da177e4SLinus Torvalds 
6058d605269SMasahiro Yamada 		len = table[i]->len;
6068d605269SMasahiro Yamada 		p1 = table[i]->sym;
607b3dbb4ecSPaulo Marques 
608b3dbb4ecSPaulo Marques 		/* find the token on the symbol */
6097c5d249aSPaulo Marques 		p2 = find_token(p1, len, str);
610b3dbb4ecSPaulo Marques 		if (!p2) continue;
611b3dbb4ecSPaulo Marques 
612b3dbb4ecSPaulo Marques 		/* decrease the counts for this symbol's tokens */
6138d605269SMasahiro Yamada 		forget_symbol(table[i]->sym, len);
614b3dbb4ecSPaulo Marques 
615b3dbb4ecSPaulo Marques 		size = len;
6161da177e4SLinus Torvalds 
6171da177e4SLinus Torvalds 		do {
618b3dbb4ecSPaulo Marques 			*p2 = idx;
619b3dbb4ecSPaulo Marques 			p2++;
620b3dbb4ecSPaulo Marques 			size -= (p2 - p1);
621b3dbb4ecSPaulo Marques 			memmove(p2, p2 + 1, size);
622b3dbb4ecSPaulo Marques 			p1 = p2;
623b3dbb4ecSPaulo Marques 			len--;
624b3dbb4ecSPaulo Marques 
625b3dbb4ecSPaulo Marques 			if (size < 2) break;
626b3dbb4ecSPaulo Marques 
6271da177e4SLinus Torvalds 			/* find the token on the symbol */
6287c5d249aSPaulo Marques 			p2 = find_token(p1, size, str);
6291da177e4SLinus Torvalds 
630b3dbb4ecSPaulo Marques 		} while (p2);
6311da177e4SLinus Torvalds 
6328d605269SMasahiro Yamada 		table[i]->len = len;
633b3dbb4ecSPaulo Marques 
634b3dbb4ecSPaulo Marques 		/* increase the counts for this symbol's new tokens */
6358d605269SMasahiro Yamada 		learn_symbol(table[i]->sym, len);
6361da177e4SLinus Torvalds 	}
6371da177e4SLinus Torvalds }
6381da177e4SLinus Torvalds 
6391da177e4SLinus Torvalds /* search the token with the maximum profit */
640b3dbb4ecSPaulo Marques static int find_best_token(void)
6411da177e4SLinus Torvalds {
642b3dbb4ecSPaulo Marques 	int i, best, bestprofit;
6431da177e4SLinus Torvalds 
6441da177e4SLinus Torvalds 	bestprofit=-10000;
645b3dbb4ecSPaulo Marques 	best = 0;
6461da177e4SLinus Torvalds 
647b3dbb4ecSPaulo Marques 	for (i = 0; i < 0x10000; i++) {
648b3dbb4ecSPaulo Marques 		if (token_profit[i] > bestprofit) {
649b3dbb4ecSPaulo Marques 			best = i;
650b3dbb4ecSPaulo Marques 			bestprofit = token_profit[i];
6511da177e4SLinus Torvalds 		}
6521da177e4SLinus Torvalds 	}
6531da177e4SLinus Torvalds 	return best;
6541da177e4SLinus Torvalds }
6551da177e4SLinus Torvalds 
6561da177e4SLinus Torvalds /* this is the core of the algorithm: calculate the "best" table */
6571da177e4SLinus Torvalds static void optimize_result(void)
6581da177e4SLinus Torvalds {
659b3dbb4ecSPaulo Marques 	int i, best;
6601da177e4SLinus Torvalds 
6611da177e4SLinus Torvalds 	/* using the '\0' symbol last allows compress_symbols to use standard
6621da177e4SLinus Torvalds 	 * fast string functions */
6631da177e4SLinus Torvalds 	for (i = 255; i >= 0; i--) {
6641da177e4SLinus Torvalds 
6651da177e4SLinus Torvalds 		/* if this table slot is empty (it is not used by an actual
6661da177e4SLinus Torvalds 		 * original char code */
6671da177e4SLinus Torvalds 		if (!best_table_len[i]) {
6681da177e4SLinus Torvalds 
669cbf7a90eSCao jin 			/* find the token with the best profit value */
6701da177e4SLinus Torvalds 			best = find_best_token();
671e0a04b11SXiaochen Wang 			if (token_profit[best] == 0)
672e0a04b11SXiaochen Wang 				break;
6731da177e4SLinus Torvalds 
6741da177e4SLinus Torvalds 			/* place it in the "best" table */
675b3dbb4ecSPaulo Marques 			best_table_len[i] = 2;
676b3dbb4ecSPaulo Marques 			best_table[i][0] = best & 0xFF;
677b3dbb4ecSPaulo Marques 			best_table[i][1] = (best >> 8) & 0xFF;
6781da177e4SLinus Torvalds 
6791da177e4SLinus Torvalds 			/* replace this token in all the valid symbols */
680b3dbb4ecSPaulo Marques 			compress_symbols(best_table[i], i);
6811da177e4SLinus Torvalds 		}
6821da177e4SLinus Torvalds 	}
6831da177e4SLinus Torvalds }
6841da177e4SLinus Torvalds 
6851da177e4SLinus Torvalds /* start by placing the symbols that are actually used on the table */
6861da177e4SLinus Torvalds static void insert_real_symbols_in_table(void)
6871da177e4SLinus Torvalds {
688b3dbb4ecSPaulo Marques 	unsigned int i, j, c;
6891da177e4SLinus Torvalds 
690b3dbb4ecSPaulo Marques 	for (i = 0; i < table_cnt; i++) {
6918d605269SMasahiro Yamada 		for (j = 0; j < table[i]->len; j++) {
6928d605269SMasahiro Yamada 			c = table[i]->sym[j];
6931da177e4SLinus Torvalds 			best_table[c][0]=c;
6941da177e4SLinus Torvalds 			best_table_len[c]=1;
6951da177e4SLinus Torvalds 		}
6961da177e4SLinus Torvalds 	}
6971da177e4SLinus Torvalds }
6981da177e4SLinus Torvalds 
6991da177e4SLinus Torvalds static void optimize_token_table(void)
7001da177e4SLinus Torvalds {
701*fcdf7197SZhen Lei 	build_initial_token_table();
7021da177e4SLinus Torvalds 
7031da177e4SLinus Torvalds 	insert_real_symbols_in_table();
7041da177e4SLinus Torvalds 
7051da177e4SLinus Torvalds 	optimize_result();
7061da177e4SLinus Torvalds }
7071da177e4SLinus Torvalds 
708b478b782SLai Jiangshan /* guess for "linker script provide" symbol */
709b478b782SLai Jiangshan static int may_be_linker_script_provide_symbol(const struct sym_entry *se)
710b478b782SLai Jiangshan {
71129e55ad3SMasahiro Yamada 	const char *symbol = sym_name(se);
712b478b782SLai Jiangshan 	int len = se->len - 1;
713b478b782SLai Jiangshan 
714b478b782SLai Jiangshan 	if (len < 8)
715b478b782SLai Jiangshan 		return 0;
716b478b782SLai Jiangshan 
717b478b782SLai Jiangshan 	if (symbol[0] != '_' || symbol[1] != '_')
718b478b782SLai Jiangshan 		return 0;
719b478b782SLai Jiangshan 
720b478b782SLai Jiangshan 	/* __start_XXXXX */
721b478b782SLai Jiangshan 	if (!memcmp(symbol + 2, "start_", 6))
722b478b782SLai Jiangshan 		return 1;
723b478b782SLai Jiangshan 
724b478b782SLai Jiangshan 	/* __stop_XXXXX */
725b478b782SLai Jiangshan 	if (!memcmp(symbol + 2, "stop_", 5))
726b478b782SLai Jiangshan 		return 1;
727b478b782SLai Jiangshan 
728b478b782SLai Jiangshan 	/* __end_XXXXX */
729b478b782SLai Jiangshan 	if (!memcmp(symbol + 2, "end_", 4))
730b478b782SLai Jiangshan 		return 1;
731b478b782SLai Jiangshan 
732b478b782SLai Jiangshan 	/* __XXXXX_start */
733b478b782SLai Jiangshan 	if (!memcmp(symbol + len - 6, "_start", 6))
734b478b782SLai Jiangshan 		return 1;
735b478b782SLai Jiangshan 
736b478b782SLai Jiangshan 	/* __XXXXX_end */
737b478b782SLai Jiangshan 	if (!memcmp(symbol + len - 4, "_end", 4))
738b478b782SLai Jiangshan 		return 1;
739b478b782SLai Jiangshan 
740b478b782SLai Jiangshan 	return 0;
741b478b782SLai Jiangshan }
742b478b782SLai Jiangshan 
743f2df3f65SPaulo Marques static int compare_symbols(const void *a, const void *b)
744f2df3f65SPaulo Marques {
7458d605269SMasahiro Yamada 	const struct sym_entry *sa = *(const struct sym_entry **)a;
7468d605269SMasahiro Yamada 	const struct sym_entry *sb = *(const struct sym_entry **)b;
747f2df3f65SPaulo Marques 	int wa, wb;
748f2df3f65SPaulo Marques 
749f2df3f65SPaulo Marques 	/* sort by address first */
750f2df3f65SPaulo Marques 	if (sa->addr > sb->addr)
751f2df3f65SPaulo Marques 		return 1;
752f2df3f65SPaulo Marques 	if (sa->addr < sb->addr)
753f2df3f65SPaulo Marques 		return -1;
754f2df3f65SPaulo Marques 
755f2df3f65SPaulo Marques 	/* sort by "weakness" type */
756f2df3f65SPaulo Marques 	wa = (sa->sym[0] == 'w') || (sa->sym[0] == 'W');
757f2df3f65SPaulo Marques 	wb = (sb->sym[0] == 'w') || (sb->sym[0] == 'W');
758f2df3f65SPaulo Marques 	if (wa != wb)
759f2df3f65SPaulo Marques 		return wa - wb;
760f2df3f65SPaulo Marques 
761b478b782SLai Jiangshan 	/* sort by "linker script provide" type */
762b478b782SLai Jiangshan 	wa = may_be_linker_script_provide_symbol(sa);
763b478b782SLai Jiangshan 	wb = may_be_linker_script_provide_symbol(sb);
764b478b782SLai Jiangshan 	if (wa != wb)
765b478b782SLai Jiangshan 		return wa - wb;
766b478b782SLai Jiangshan 
767b478b782SLai Jiangshan 	/* sort by the number of prefix underscores */
768aa915245SMasahiro Yamada 	wa = strspn(sym_name(sa), "_");
769aa915245SMasahiro Yamada 	wb = strspn(sym_name(sb), "_");
770b478b782SLai Jiangshan 	if (wa != wb)
771b478b782SLai Jiangshan 		return wa - wb;
772b478b782SLai Jiangshan 
773f2df3f65SPaulo Marques 	/* sort by initial order, so that other symbols are left undisturbed */
774f2df3f65SPaulo Marques 	return sa->start_pos - sb->start_pos;
775f2df3f65SPaulo Marques }
776f2df3f65SPaulo Marques 
777f2df3f65SPaulo Marques static void sort_symbols(void)
778f2df3f65SPaulo Marques {
7798d605269SMasahiro Yamada 	qsort(table, table_cnt, sizeof(table[0]), compare_symbols);
780f2df3f65SPaulo Marques }
7811da177e4SLinus Torvalds 
782c6bda7c9SRusty Russell static void make_percpus_absolute(void)
783c6bda7c9SRusty Russell {
784c6bda7c9SRusty Russell 	unsigned int i;
785c6bda7c9SRusty Russell 
786c6bda7c9SRusty Russell 	for (i = 0; i < table_cnt; i++)
7878d605269SMasahiro Yamada 		if (symbol_in_range(table[i], &percpu_range, 1)) {
7888c996940SArd Biesheuvel 			/*
7898c996940SArd Biesheuvel 			 * Keep the 'A' override for percpu symbols to
7908c996940SArd Biesheuvel 			 * ensure consistent behavior compared to older
7918c996940SArd Biesheuvel 			 * versions of this tool.
7928c996940SArd Biesheuvel 			 */
7938d605269SMasahiro Yamada 			table[i]->sym[0] = 'A';
7948d605269SMasahiro Yamada 			table[i]->percpu_absolute = 1;
7958c996940SArd Biesheuvel 		}
796c6bda7c9SRusty Russell }
797c6bda7c9SRusty Russell 
7982213e9a6SArd Biesheuvel /* find the minimum non-absolute symbol address */
7992213e9a6SArd Biesheuvel static void record_relative_base(void)
8002213e9a6SArd Biesheuvel {
8012213e9a6SArd Biesheuvel 	unsigned int i;
8022213e9a6SArd Biesheuvel 
8032213e9a6SArd Biesheuvel 	for (i = 0; i < table_cnt; i++)
8048d605269SMasahiro Yamada 		if (!symbol_absolute(table[i])) {
805f34ea029SMasahiro Yamada 			/*
806f34ea029SMasahiro Yamada 			 * The table is sorted by address.
807f34ea029SMasahiro Yamada 			 * Take the first non-absolute symbol value.
808f34ea029SMasahiro Yamada 			 */
8098d605269SMasahiro Yamada 			relative_base = table[i]->addr;
810f34ea029SMasahiro Yamada 			return;
811f34ea029SMasahiro Yamada 		}
8122213e9a6SArd Biesheuvel }
8132213e9a6SArd Biesheuvel 
814b3dbb4ecSPaulo Marques int main(int argc, char **argv)
8151da177e4SLinus Torvalds {
816aa221f2eSMasahiro Yamada 	while (1) {
817aa221f2eSMasahiro Yamada 		static struct option long_options[] = {
818aa221f2eSMasahiro Yamada 			{"all-symbols",     no_argument, &all_symbols,     1},
819aa221f2eSMasahiro Yamada 			{"absolute-percpu", no_argument, &absolute_percpu, 1},
820aa221f2eSMasahiro Yamada 			{"base-relative",   no_argument, &base_relative,   1},
821aa221f2eSMasahiro Yamada 			{},
822aa221f2eSMasahiro Yamada 		};
823aa221f2eSMasahiro Yamada 
824aa221f2eSMasahiro Yamada 		int c = getopt_long(argc, argv, "", long_options, NULL);
825aa221f2eSMasahiro Yamada 
826aa221f2eSMasahiro Yamada 		if (c == -1)
827aa221f2eSMasahiro Yamada 			break;
828aa221f2eSMasahiro Yamada 		if (c != 0)
82941f11a4fSYoshinori Sato 			usage();
83041f11a4fSYoshinori Sato 	}
831aa221f2eSMasahiro Yamada 
832aa221f2eSMasahiro Yamada 	if (optind >= argc)
8331da177e4SLinus Torvalds 		usage();
8341da177e4SLinus Torvalds 
835aa221f2eSMasahiro Yamada 	read_map(argv[optind]);
8365e5c4fa7SMasahiro Yamada 	shrink_table();
837c6bda7c9SRusty Russell 	if (absolute_percpu)
838c6bda7c9SRusty Russell 		make_percpus_absolute();
839f34ea029SMasahiro Yamada 	sort_symbols();
8402213e9a6SArd Biesheuvel 	if (base_relative)
8412213e9a6SArd Biesheuvel 		record_relative_base();
8421da177e4SLinus Torvalds 	optimize_token_table();
8431da177e4SLinus Torvalds 	write_src();
8441da177e4SLinus Torvalds 
8451da177e4SLinus Torvalds 	return 0;
8461da177e4SLinus Torvalds }
847