xref: /linux/scripts/kallsyms.c (revision 6ccf9cb557bd32073b0d68baed97f1bd8a40ff1d)
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 
21a41333e0SMasahiro Yamada #include <stdbool.h>
221da177e4SLinus Torvalds #include <stdio.h>
231da177e4SLinus Torvalds #include <stdlib.h>
241da177e4SLinus Torvalds #include <string.h>
251da177e4SLinus Torvalds #include <ctype.h>
262213e9a6SArd Biesheuvel #include <limits.h>
271da177e4SLinus Torvalds 
2817b1f0deSMike Frysinger #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
2917b1f0deSMike Frysinger 
309281aceaSTejun Heo #define KSYM_NAME_LEN		128
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds struct sym_entry {
331da177e4SLinus Torvalds 	unsigned long long addr;
34b3dbb4ecSPaulo Marques 	unsigned int len;
35f2df3f65SPaulo Marques 	unsigned int start_pos;
368c996940SArd Biesheuvel 	unsigned int percpu_absolute;
379d82973eSLinus Torvalds 	unsigned char sym[];
381da177e4SLinus Torvalds };
391da177e4SLinus Torvalds 
4078eb7159SKees Cook struct addr_range {
4178eb7159SKees Cook 	const char *start_sym, *end_sym;
4217b1f0deSMike Frysinger 	unsigned long long start, end;
4317b1f0deSMike Frysinger };
4417b1f0deSMike Frysinger 
4517b1f0deSMike Frysinger static unsigned long long _text;
462213e9a6SArd Biesheuvel static unsigned long long relative_base;
4778eb7159SKees Cook static struct addr_range text_ranges[] = {
4817b1f0deSMike Frysinger 	{ "_stext",     "_etext"     },
4917b1f0deSMike Frysinger 	{ "_sinittext", "_einittext" },
5017b1f0deSMike Frysinger };
5117b1f0deSMike Frysinger #define text_range_text     (&text_ranges[0])
5217b1f0deSMike Frysinger #define text_range_inittext (&text_ranges[1])
5317b1f0deSMike Frysinger 
54c6bda7c9SRusty Russell static struct addr_range percpu_range = {
55c6bda7c9SRusty Russell 	"__per_cpu_start", "__per_cpu_end", -1ULL, 0
56c6bda7c9SRusty Russell };
57c6bda7c9SRusty Russell 
588d605269SMasahiro Yamada static struct sym_entry **table;
59b3dbb4ecSPaulo Marques static unsigned int table_size, table_cnt;
60831362fcSMasahiro Yamada static int all_symbols;
61831362fcSMasahiro Yamada static int absolute_percpu;
62831362fcSMasahiro Yamada static int base_relative;
631da177e4SLinus Torvalds 
64f43e9daaSMasahiro Yamada static int token_profit[0x10000];
651da177e4SLinus Torvalds 
661da177e4SLinus Torvalds /* the table that holds the result of the compression */
67f43e9daaSMasahiro Yamada static unsigned char best_table[256][2];
68f43e9daaSMasahiro Yamada static unsigned char best_table_len[256];
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds 
71b3dbb4ecSPaulo Marques static void usage(void)
721da177e4SLinus Torvalds {
73f6537f2fSMing Lei 	fprintf(stderr, "Usage: kallsyms [--all-symbols] "
742213e9a6SArd Biesheuvel 			"[--base-relative] < in.map > out.S\n");
751da177e4SLinus Torvalds 	exit(1);
761da177e4SLinus Torvalds }
771da177e4SLinus Torvalds 
7829e55ad3SMasahiro Yamada static char *sym_name(const struct sym_entry *s)
7929e55ad3SMasahiro Yamada {
8029e55ad3SMasahiro Yamada 	return (char *)s->sym + 1;
8129e55ad3SMasahiro Yamada }
8229e55ad3SMasahiro Yamada 
83a41333e0SMasahiro Yamada static bool is_ignored_symbol(const char *name, char type)
84a41333e0SMasahiro Yamada {
85516d980fSMasahiro Yamada 	/* Symbol names that exactly match to the following are ignored.*/
86a41333e0SMasahiro Yamada 	static const char * const ignored_symbols[] = {
87a41333e0SMasahiro Yamada 		/*
88a41333e0SMasahiro Yamada 		 * Symbols which vary between passes. Passes 1 and 2 must have
89a41333e0SMasahiro Yamada 		 * identical symbol lists. The kallsyms_* symbols below are
90a41333e0SMasahiro Yamada 		 * only added after pass 1, they would be included in pass 2
91a41333e0SMasahiro Yamada 		 * when --all-symbols is specified so exclude them to get a
92a41333e0SMasahiro Yamada 		 * stable symbol list.
93a41333e0SMasahiro Yamada 		 */
94a41333e0SMasahiro Yamada 		"kallsyms_addresses",
95a41333e0SMasahiro Yamada 		"kallsyms_offsets",
96a41333e0SMasahiro Yamada 		"kallsyms_relative_base",
97a41333e0SMasahiro Yamada 		"kallsyms_num_syms",
98a41333e0SMasahiro Yamada 		"kallsyms_names",
99a41333e0SMasahiro Yamada 		"kallsyms_markers",
100a41333e0SMasahiro Yamada 		"kallsyms_token_table",
101a41333e0SMasahiro Yamada 		"kallsyms_token_index",
102a41333e0SMasahiro Yamada 		/* Exclude linker generated symbols which vary between passes */
103a41333e0SMasahiro Yamada 		"_SDA_BASE_",		/* ppc */
104a41333e0SMasahiro Yamada 		"_SDA2_BASE_",		/* ppc */
105a41333e0SMasahiro Yamada 		NULL
106a41333e0SMasahiro Yamada 	};
107a41333e0SMasahiro Yamada 
108516d980fSMasahiro Yamada 	/* Symbol names that begin with the following are ignored.*/
109a41333e0SMasahiro Yamada 	static const char * const ignored_prefixes[] = {
11097261e1eSMasahiro Yamada 		"$",			/* local symbols for ARM, MIPS, etc. */
111d4c85864SChangbin Du 		".L",			/* local labels, .LBB,.Ltmpxxx,.L__unnamed_xx,.LASANPC, etc. */
112a41333e0SMasahiro Yamada 		"__crc_",		/* modversions */
113a41333e0SMasahiro Yamada 		"__efistub_",		/* arm64 EFI stub namespace */
114*6ccf9cb5SKalesh Singh 		"__kvm_nvhe_$",		/* arm64 local symbols in non-VHE KVM namespace */
115*6ccf9cb5SKalesh Singh 		"__kvm_nvhe_.L",	/* arm64 local symbols in non-VHE KVM namespace */
116efe6e306SArnd Bergmann 		"__AArch64ADRPThunk_",	/* arm64 lld */
117efe6e306SArnd Bergmann 		"__ARMV5PILongThunk_",	/* arm lld */
118efe6e306SArnd Bergmann 		"__ARMV7PILongThunk_",
119efe6e306SArnd Bergmann 		"__ThumbV7PILongThunk_",
120efe6e306SArnd Bergmann 		"__LA25Thunk_",		/* mips lld */
121efe6e306SArnd Bergmann 		"__microLA25Thunk_",
122a41333e0SMasahiro Yamada 		NULL
123a41333e0SMasahiro Yamada 	};
124a41333e0SMasahiro Yamada 
125516d980fSMasahiro Yamada 	/* Symbol names that end with the following are ignored.*/
126a41333e0SMasahiro Yamada 	static const char * const ignored_suffixes[] = {
127a41333e0SMasahiro Yamada 		"_from_arm",		/* arm */
128a41333e0SMasahiro Yamada 		"_from_thumb",		/* arm */
129a41333e0SMasahiro Yamada 		"_veneer",		/* arm */
130a41333e0SMasahiro Yamada 		NULL
131a41333e0SMasahiro Yamada 	};
132a41333e0SMasahiro Yamada 
133516d980fSMasahiro Yamada 	/* Symbol names that contain the following are ignored.*/
134516d980fSMasahiro Yamada 	static const char * const ignored_matches[] = {
135516d980fSMasahiro Yamada 		".long_branch.",	/* ppc stub */
136516d980fSMasahiro Yamada 		".plt_branch.",		/* ppc stub */
137516d980fSMasahiro Yamada 		NULL
138516d980fSMasahiro Yamada 	};
139516d980fSMasahiro Yamada 
140a41333e0SMasahiro Yamada 	const char * const *p;
141a41333e0SMasahiro Yamada 
142a41333e0SMasahiro Yamada 	for (p = ignored_symbols; *p; p++)
143a41333e0SMasahiro Yamada 		if (!strcmp(name, *p))
144a41333e0SMasahiro Yamada 			return true;
145a41333e0SMasahiro Yamada 
146a41333e0SMasahiro Yamada 	for (p = ignored_prefixes; *p; p++)
147a41333e0SMasahiro Yamada 		if (!strncmp(name, *p, strlen(*p)))
148a41333e0SMasahiro Yamada 			return true;
149a41333e0SMasahiro Yamada 
150a41333e0SMasahiro Yamada 	for (p = ignored_suffixes; *p; p++) {
151a41333e0SMasahiro Yamada 		int l = strlen(name) - strlen(*p);
152a41333e0SMasahiro Yamada 
153a41333e0SMasahiro Yamada 		if (l >= 0 && !strcmp(name + l, *p))
154a41333e0SMasahiro Yamada 			return true;
155a41333e0SMasahiro Yamada 	}
156a41333e0SMasahiro Yamada 
157516d980fSMasahiro Yamada 	for (p = ignored_matches; *p; p++) {
158516d980fSMasahiro Yamada 		if (strstr(name, *p))
159516d980fSMasahiro Yamada 			return true;
160516d980fSMasahiro Yamada 	}
161516d980fSMasahiro Yamada 
162887df76dSMasahiro Yamada 	if (type == 'U' || type == 'u')
163887df76dSMasahiro Yamada 		return true;
164887df76dSMasahiro Yamada 	/* exclude debugging symbols */
165887df76dSMasahiro Yamada 	if (type == 'N' || type == 'n')
166887df76dSMasahiro Yamada 		return true;
167887df76dSMasahiro Yamada 
168887df76dSMasahiro Yamada 	if (toupper(type) == 'A') {
169887df76dSMasahiro Yamada 		/* Keep these useful absolute symbols */
170887df76dSMasahiro Yamada 		if (strcmp(name, "__kernel_syscall_via_break") &&
171887df76dSMasahiro Yamada 		    strcmp(name, "__kernel_syscall_via_epc") &&
172887df76dSMasahiro Yamada 		    strcmp(name, "__kernel_sigtramp") &&
173887df76dSMasahiro Yamada 		    strcmp(name, "__gp"))
174887df76dSMasahiro Yamada 			return true;
175887df76dSMasahiro Yamada 	}
176887df76dSMasahiro Yamada 
177a41333e0SMasahiro Yamada 	return false;
178a41333e0SMasahiro Yamada }
179a41333e0SMasahiro Yamada 
180b6233d0dSMasahiro Yamada static void check_symbol_range(const char *sym, unsigned long long addr,
18178eb7159SKees Cook 			       struct addr_range *ranges, int entries)
18217b1f0deSMike Frysinger {
18317b1f0deSMike Frysinger 	size_t i;
18478eb7159SKees Cook 	struct addr_range *ar;
18517b1f0deSMike Frysinger 
18678eb7159SKees Cook 	for (i = 0; i < entries; ++i) {
18778eb7159SKees Cook 		ar = &ranges[i];
18817b1f0deSMike Frysinger 
18978eb7159SKees Cook 		if (strcmp(sym, ar->start_sym) == 0) {
19078eb7159SKees Cook 			ar->start = addr;
191b6233d0dSMasahiro Yamada 			return;
19278eb7159SKees Cook 		} else if (strcmp(sym, ar->end_sym) == 0) {
19378eb7159SKees Cook 			ar->end = addr;
194b6233d0dSMasahiro Yamada 			return;
19517b1f0deSMike Frysinger 		}
19617b1f0deSMike Frysinger 	}
19717b1f0deSMike Frysinger }
19817b1f0deSMike Frysinger 
1998d605269SMasahiro Yamada static struct sym_entry *read_symbol(FILE *in)
2001da177e4SLinus Torvalds {
201be9f6133SMasahiro Yamada 	char name[500], type;
2028d605269SMasahiro Yamada 	unsigned long long addr;
2038d605269SMasahiro Yamada 	unsigned int len;
2048d605269SMasahiro Yamada 	struct sym_entry *sym;
2051da177e4SLinus Torvalds 	int rc;
2061da177e4SLinus Torvalds 
2078d605269SMasahiro Yamada 	rc = fscanf(in, "%llx %c %499s\n", &addr, &type, name);
2081da177e4SLinus Torvalds 	if (rc != 3) {
209be9f6133SMasahiro Yamada 		if (rc != EOF && fgets(name, 500, in) == NULL)
210ef894870SJean Sacren 			fprintf(stderr, "Read error or end of file.\n");
2118d605269SMasahiro Yamada 		return NULL;
2121da177e4SLinus Torvalds 	}
213be9f6133SMasahiro Yamada 	if (strlen(name) >= KSYM_NAME_LEN) {
2146db2983cSEugene Loh 		fprintf(stderr, "Symbol %s too long for kallsyms (%zu >= %d).\n"
215f3462aa9SAndi Kleen 				"Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n",
216be9f6133SMasahiro Yamada 			name, strlen(name), KSYM_NAME_LEN);
2178d605269SMasahiro Yamada 		return NULL;
218f3462aa9SAndi Kleen 	}
2191da177e4SLinus Torvalds 
220be9f6133SMasahiro Yamada 	if (strcmp(name, "_text") == 0)
2218d605269SMasahiro Yamada 		_text = addr;
222b6233d0dSMasahiro Yamada 
2237883a143SMikhail Petrov 	/* Ignore most absolute/undefined (?) symbols. */
2247883a143SMikhail Petrov 	if (is_ignored_symbol(name, type))
2257883a143SMikhail Petrov 		return NULL;
2267883a143SMikhail Petrov 
2278d605269SMasahiro Yamada 	check_symbol_range(name, addr, text_ranges, ARRAY_SIZE(text_ranges));
2288d605269SMasahiro Yamada 	check_symbol_range(name, addr, &percpu_range, 1);
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds 	/* include the type field in the symbol name, so that it gets
2311da177e4SLinus Torvalds 	 * compressed together */
2328d605269SMasahiro Yamada 
2338d605269SMasahiro Yamada 	len = strlen(name) + 1;
2348d605269SMasahiro Yamada 
2359d1b3895SMasahiro Yamada 	sym = malloc(sizeof(*sym) + len + 1);
2368d605269SMasahiro Yamada 	if (!sym) {
237f1a136e0SJesper Juhl 		fprintf(stderr, "kallsyms failure: "
238f1a136e0SJesper Juhl 			"unable to allocate required amount of memory\n");
239f1a136e0SJesper Juhl 		exit(EXIT_FAILURE);
240f1a136e0SJesper Juhl 	}
2418d605269SMasahiro Yamada 	sym->addr = addr;
2428d605269SMasahiro Yamada 	sym->len = len;
2438d605269SMasahiro Yamada 	sym->sym[0] = type;
2449d1b3895SMasahiro Yamada 	strcpy(sym_name(sym), name);
2458d605269SMasahiro Yamada 	sym->percpu_absolute = 0;
2461da177e4SLinus Torvalds 
2478d605269SMasahiro Yamada 	return sym;
2481da177e4SLinus Torvalds }
2491da177e4SLinus Torvalds 
2504bfe2b78SMasahiro Yamada static int symbol_in_range(const struct sym_entry *s,
2514bfe2b78SMasahiro Yamada 			   const struct addr_range *ranges, int entries)
25217b1f0deSMike Frysinger {
25317b1f0deSMike Frysinger 	size_t i;
2544bfe2b78SMasahiro Yamada 	const struct addr_range *ar;
25517b1f0deSMike Frysinger 
25678eb7159SKees Cook 	for (i = 0; i < entries; ++i) {
25778eb7159SKees Cook 		ar = &ranges[i];
25817b1f0deSMike Frysinger 
25978eb7159SKees Cook 		if (s->addr >= ar->start && s->addr <= ar->end)
260ac6ca5c8SMike Frysinger 			return 1;
26117b1f0deSMike Frysinger 	}
26217b1f0deSMike Frysinger 
263ac6ca5c8SMike Frysinger 	return 0;
26417b1f0deSMike Frysinger }
26517b1f0deSMike Frysinger 
2664bfe2b78SMasahiro Yamada static int symbol_valid(const struct sym_entry *s)
2671da177e4SLinus Torvalds {
26829e55ad3SMasahiro Yamada 	const char *name = sym_name(s);
269bd8b22d2SArd Biesheuvel 
2701da177e4SLinus Torvalds 	/* if --all-symbols is not specified, then symbols outside the text
2711da177e4SLinus Torvalds 	 * and inittext sections are discarded */
2721da177e4SLinus Torvalds 	if (!all_symbols) {
27378eb7159SKees Cook 		if (symbol_in_range(s, text_ranges,
27478eb7159SKees Cook 				    ARRAY_SIZE(text_ranges)) == 0)
2751da177e4SLinus Torvalds 			return 0;
2761da177e4SLinus Torvalds 		/* Corner case.  Discard any symbols with the same value as
277a3b81113SRobin Getz 		 * _etext _einittext; they can move between pass 1 and 2 when
278a3b81113SRobin Getz 		 * the kallsyms data are added.  If these symbols move then
279a3b81113SRobin Getz 		 * they may get dropped in pass 2, which breaks the kallsyms
280a3b81113SRobin Getz 		 * rules.
2811da177e4SLinus Torvalds 		 */
28217b1f0deSMike Frysinger 		if ((s->addr == text_range_text->end &&
28329e55ad3SMasahiro Yamada 		     strcmp(name, text_range_text->end_sym)) ||
28417b1f0deSMike Frysinger 		    (s->addr == text_range_inittext->end &&
28529e55ad3SMasahiro Yamada 		     strcmp(name, text_range_inittext->end_sym)))
2861da177e4SLinus Torvalds 			return 0;
2871da177e4SLinus Torvalds 	}
2881da177e4SLinus Torvalds 
2891da177e4SLinus Torvalds 	return 1;
2901da177e4SLinus Torvalds }
2911da177e4SLinus Torvalds 
2925e5c4fa7SMasahiro Yamada /* remove all the invalid symbols from the table */
2935e5c4fa7SMasahiro Yamada static void shrink_table(void)
2945e5c4fa7SMasahiro Yamada {
2955e5c4fa7SMasahiro Yamada 	unsigned int i, pos;
2965e5c4fa7SMasahiro Yamada 
2975e5c4fa7SMasahiro Yamada 	pos = 0;
2985e5c4fa7SMasahiro Yamada 	for (i = 0; i < table_cnt; i++) {
2998d605269SMasahiro Yamada 		if (symbol_valid(table[i])) {
3005e5c4fa7SMasahiro Yamada 			if (pos != i)
3015e5c4fa7SMasahiro Yamada 				table[pos] = table[i];
3025e5c4fa7SMasahiro Yamada 			pos++;
3035e5c4fa7SMasahiro Yamada 		} else {
3048d605269SMasahiro Yamada 			free(table[i]);
3055e5c4fa7SMasahiro Yamada 		}
3065e5c4fa7SMasahiro Yamada 	}
3075e5c4fa7SMasahiro Yamada 	table_cnt = pos;
3085e5c4fa7SMasahiro Yamada 
3095e5c4fa7SMasahiro Yamada 	/* When valid symbol is not registered, exit to error */
3105e5c4fa7SMasahiro Yamada 	if (!table_cnt) {
3115e5c4fa7SMasahiro Yamada 		fprintf(stderr, "No valid symbol.\n");
3125e5c4fa7SMasahiro Yamada 		exit(1);
3135e5c4fa7SMasahiro Yamada 	}
3145e5c4fa7SMasahiro Yamada }
3155e5c4fa7SMasahiro Yamada 
316b3dbb4ecSPaulo Marques static void read_map(FILE *in)
3171da177e4SLinus Torvalds {
3188d605269SMasahiro Yamada 	struct sym_entry *sym;
3198d605269SMasahiro Yamada 
3201da177e4SLinus Torvalds 	while (!feof(in)) {
3218d605269SMasahiro Yamada 		sym = read_symbol(in);
3228d605269SMasahiro Yamada 		if (!sym)
3238d605269SMasahiro Yamada 			continue;
3248d605269SMasahiro Yamada 
3258d605269SMasahiro Yamada 		sym->start_pos = table_cnt;
3268d605269SMasahiro Yamada 
327b3dbb4ecSPaulo Marques 		if (table_cnt >= table_size) {
328b3dbb4ecSPaulo Marques 			table_size += 10000;
329b3dbb4ecSPaulo Marques 			table = realloc(table, sizeof(*table) * table_size);
3301da177e4SLinus Torvalds 			if (!table) {
3311da177e4SLinus Torvalds 				fprintf(stderr, "out of memory\n");
3321da177e4SLinus Torvalds 				exit (1);
3331da177e4SLinus Torvalds 			}
3341da177e4SLinus Torvalds 		}
3358d605269SMasahiro Yamada 
3368d605269SMasahiro Yamada 		table[table_cnt++] = sym;
3371da177e4SLinus Torvalds 	}
338f2df3f65SPaulo Marques }
3391da177e4SLinus Torvalds 
3404bfe2b78SMasahiro Yamada static void output_label(const char *label)
3411da177e4SLinus Torvalds {
3421da177e4SLinus Torvalds 	printf(".globl %s\n", label);
3431da177e4SLinus Torvalds 	printf("\tALGN\n");
3441da177e4SLinus Torvalds 	printf("%s:\n", label);
3451da177e4SLinus Torvalds }
3461da177e4SLinus Torvalds 
347fd2ab2f6SMasahiro Yamada /* Provide proper symbols relocatability by their '_text' relativeness. */
348fd2ab2f6SMasahiro Yamada static void output_address(unsigned long long addr)
349fd2ab2f6SMasahiro Yamada {
350fd2ab2f6SMasahiro Yamada 	if (_text <= addr)
351fd2ab2f6SMasahiro Yamada 		printf("\tPTR\t_text + %#llx\n", addr - _text);
352fd2ab2f6SMasahiro Yamada 	else
353fd2ab2f6SMasahiro Yamada 		printf("\tPTR\t_text - %#llx\n", _text - addr);
354fd2ab2f6SMasahiro Yamada }
355fd2ab2f6SMasahiro Yamada 
3561da177e4SLinus Torvalds /* uncompress a compressed symbol. When this function is called, the best table
3571da177e4SLinus Torvalds  * might still be compressed itself, so the function needs to be recursive */
3584bfe2b78SMasahiro Yamada static int expand_symbol(const unsigned char *data, int len, char *result)
3591da177e4SLinus Torvalds {
3601da177e4SLinus Torvalds 	int c, rlen, total=0;
3611da177e4SLinus Torvalds 
3621da177e4SLinus Torvalds 	while (len) {
3631da177e4SLinus Torvalds 		c = *data;
3641da177e4SLinus Torvalds 		/* if the table holds a single char that is the same as the one
3651da177e4SLinus Torvalds 		 * we are looking for, then end the search */
3661da177e4SLinus Torvalds 		if (best_table[c][0]==c && best_table_len[c]==1) {
3671da177e4SLinus Torvalds 			*result++ = c;
3681da177e4SLinus Torvalds 			total++;
3691da177e4SLinus Torvalds 		} else {
3701da177e4SLinus Torvalds 			/* if not, recurse and expand */
3711da177e4SLinus Torvalds 			rlen = expand_symbol(best_table[c], best_table_len[c], result);
3721da177e4SLinus Torvalds 			total += rlen;
3731da177e4SLinus Torvalds 			result += rlen;
3741da177e4SLinus Torvalds 		}
3751da177e4SLinus Torvalds 		data++;
3761da177e4SLinus Torvalds 		len--;
3771da177e4SLinus Torvalds 	}
3781da177e4SLinus Torvalds 	*result=0;
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds 	return total;
3811da177e4SLinus Torvalds }
3821da177e4SLinus Torvalds 
3834bfe2b78SMasahiro Yamada static int symbol_absolute(const struct sym_entry *s)
38478eb7159SKees Cook {
3858c996940SArd Biesheuvel 	return s->percpu_absolute;
38678eb7159SKees Cook }
38778eb7159SKees Cook 
388b3dbb4ecSPaulo Marques static void write_src(void)
3891da177e4SLinus Torvalds {
390b3dbb4ecSPaulo Marques 	unsigned int i, k, off;
3911da177e4SLinus Torvalds 	unsigned int best_idx[256];
3921da177e4SLinus Torvalds 	unsigned int *markers;
3939281aceaSTejun Heo 	char buf[KSYM_NAME_LEN];
3941da177e4SLinus Torvalds 
395500193ecSMasahiro Yamada 	printf("#include <asm/bitsperlong.h>\n");
3961da177e4SLinus Torvalds 	printf("#if BITS_PER_LONG == 64\n");
3971da177e4SLinus Torvalds 	printf("#define PTR .quad\n");
39872d3ebb9SMathias Krause 	printf("#define ALGN .balign 8\n");
3991da177e4SLinus Torvalds 	printf("#else\n");
4001da177e4SLinus Torvalds 	printf("#define PTR .long\n");
40172d3ebb9SMathias Krause 	printf("#define ALGN .balign 4\n");
4021da177e4SLinus Torvalds 	printf("#endif\n");
4031da177e4SLinus Torvalds 
404aad09470SJan Beulich 	printf("\t.section .rodata, \"a\"\n");
4051da177e4SLinus Torvalds 
4062213e9a6SArd Biesheuvel 	if (!base_relative)
4071da177e4SLinus Torvalds 		output_label("kallsyms_addresses");
4082213e9a6SArd Biesheuvel 	else
4092213e9a6SArd Biesheuvel 		output_label("kallsyms_offsets");
4102213e9a6SArd Biesheuvel 
411b3dbb4ecSPaulo Marques 	for (i = 0; i < table_cnt; i++) {
4122213e9a6SArd Biesheuvel 		if (base_relative) {
413fd2ab2f6SMasahiro Yamada 			/*
414fd2ab2f6SMasahiro Yamada 			 * Use the offset relative to the lowest value
415fd2ab2f6SMasahiro Yamada 			 * encountered of all relative symbols, and emit
416fd2ab2f6SMasahiro Yamada 			 * non-relocatable fixed offsets that will be fixed
417fd2ab2f6SMasahiro Yamada 			 * up at runtime.
418fd2ab2f6SMasahiro Yamada 			 */
419fd2ab2f6SMasahiro Yamada 
4202213e9a6SArd Biesheuvel 			long long offset;
4212213e9a6SArd Biesheuvel 			int overflow;
4222213e9a6SArd Biesheuvel 
4232213e9a6SArd Biesheuvel 			if (!absolute_percpu) {
4248d605269SMasahiro Yamada 				offset = table[i]->addr - relative_base;
4252213e9a6SArd Biesheuvel 				overflow = (offset < 0 || offset > UINT_MAX);
4268d605269SMasahiro Yamada 			} else if (symbol_absolute(table[i])) {
4278d605269SMasahiro Yamada 				offset = table[i]->addr;
4282213e9a6SArd Biesheuvel 				overflow = (offset < 0 || offset > INT_MAX);
4292213e9a6SArd Biesheuvel 			} else {
4308d605269SMasahiro Yamada 				offset = relative_base - table[i]->addr - 1;
4312213e9a6SArd Biesheuvel 				overflow = (offset < INT_MIN || offset >= 0);
4322213e9a6SArd Biesheuvel 			}
4332213e9a6SArd Biesheuvel 			if (overflow) {
4342213e9a6SArd Biesheuvel 				fprintf(stderr, "kallsyms failure: "
4352213e9a6SArd Biesheuvel 					"%s symbol value %#llx out of range in relative mode\n",
4368d605269SMasahiro Yamada 					symbol_absolute(table[i]) ? "absolute" : "relative",
4378d605269SMasahiro Yamada 					table[i]->addr);
4382213e9a6SArd Biesheuvel 				exit(EXIT_FAILURE);
4392213e9a6SArd Biesheuvel 			}
4402213e9a6SArd Biesheuvel 			printf("\t.long\t%#x\n", (int)offset);
4418d605269SMasahiro Yamada 		} else if (!symbol_absolute(table[i])) {
4428d605269SMasahiro Yamada 			output_address(table[i]->addr);
443fd593d12SEric W. Biederman 		} else {
4448d605269SMasahiro Yamada 			printf("\tPTR\t%#llx\n", table[i]->addr);
4451da177e4SLinus Torvalds 		}
446fd593d12SEric W. Biederman 	}
4471da177e4SLinus Torvalds 	printf("\n");
4481da177e4SLinus Torvalds 
4492213e9a6SArd Biesheuvel 	if (base_relative) {
4502213e9a6SArd Biesheuvel 		output_label("kallsyms_relative_base");
451fd2ab2f6SMasahiro Yamada 		output_address(relative_base);
4522213e9a6SArd Biesheuvel 		printf("\n");
4532213e9a6SArd Biesheuvel 	}
4542213e9a6SArd Biesheuvel 
4551da177e4SLinus Torvalds 	output_label("kallsyms_num_syms");
45680ffbaa5SJan Beulich 	printf("\t.long\t%u\n", table_cnt);
4571da177e4SLinus Torvalds 	printf("\n");
4581da177e4SLinus Torvalds 
4591da177e4SLinus Torvalds 	/* table of offset markers, that give the offset in the compressed stream
4601da177e4SLinus Torvalds 	 * every 256 symbols */
461f1a136e0SJesper Juhl 	markers = malloc(sizeof(unsigned int) * ((table_cnt + 255) / 256));
462f1a136e0SJesper Juhl 	if (!markers) {
463f1a136e0SJesper Juhl 		fprintf(stderr, "kallsyms failure: "
464f1a136e0SJesper Juhl 			"unable to allocate required memory\n");
465f1a136e0SJesper Juhl 		exit(EXIT_FAILURE);
466f1a136e0SJesper Juhl 	}
4671da177e4SLinus Torvalds 
4681da177e4SLinus Torvalds 	output_label("kallsyms_names");
4691da177e4SLinus Torvalds 	off = 0;
470b3dbb4ecSPaulo Marques 	for (i = 0; i < table_cnt; i++) {
471b3dbb4ecSPaulo Marques 		if ((i & 0xFF) == 0)
472b3dbb4ecSPaulo Marques 			markers[i >> 8] = off;
4731da177e4SLinus Torvalds 
4748d605269SMasahiro Yamada 		printf("\t.byte 0x%02x", table[i]->len);
4758d605269SMasahiro Yamada 		for (k = 0; k < table[i]->len; k++)
4768d605269SMasahiro Yamada 			printf(", 0x%02x", table[i]->sym[k]);
4771da177e4SLinus Torvalds 		printf("\n");
4781da177e4SLinus Torvalds 
4798d605269SMasahiro Yamada 		off += table[i]->len + 1;
4801da177e4SLinus Torvalds 	}
4811da177e4SLinus Torvalds 	printf("\n");
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds 	output_label("kallsyms_markers");
484b3dbb4ecSPaulo Marques 	for (i = 0; i < ((table_cnt + 255) >> 8); i++)
48580ffbaa5SJan Beulich 		printf("\t.long\t%u\n", markers[i]);
4861da177e4SLinus Torvalds 	printf("\n");
4871da177e4SLinus Torvalds 
4881da177e4SLinus Torvalds 	free(markers);
4891da177e4SLinus Torvalds 
4901da177e4SLinus Torvalds 	output_label("kallsyms_token_table");
4911da177e4SLinus Torvalds 	off = 0;
4921da177e4SLinus Torvalds 	for (i = 0; i < 256; i++) {
4931da177e4SLinus Torvalds 		best_idx[i] = off;
4941da177e4SLinus Torvalds 		expand_symbol(best_table[i], best_table_len[i], buf);
4951da177e4SLinus Torvalds 		printf("\t.asciz\t\"%s\"\n", buf);
4961da177e4SLinus Torvalds 		off += strlen(buf) + 1;
4971da177e4SLinus Torvalds 	}
4981da177e4SLinus Torvalds 	printf("\n");
4991da177e4SLinus Torvalds 
5001da177e4SLinus Torvalds 	output_label("kallsyms_token_index");
5011da177e4SLinus Torvalds 	for (i = 0; i < 256; i++)
5021da177e4SLinus Torvalds 		printf("\t.short\t%d\n", best_idx[i]);
5031da177e4SLinus Torvalds 	printf("\n");
5041da177e4SLinus Torvalds }
5051da177e4SLinus Torvalds 
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds /* table lookup compression functions */
5081da177e4SLinus Torvalds 
5091da177e4SLinus Torvalds /* count all the possible tokens in a symbol */
5104bfe2b78SMasahiro Yamada static void learn_symbol(const unsigned char *symbol, int len)
5111da177e4SLinus Torvalds {
5121da177e4SLinus Torvalds 	int i;
5131da177e4SLinus Torvalds 
5141da177e4SLinus Torvalds 	for (i = 0; i < len - 1; i++)
515b3dbb4ecSPaulo Marques 		token_profit[ symbol[i] + (symbol[i + 1] << 8) ]++;
5161da177e4SLinus Torvalds }
5171da177e4SLinus Torvalds 
5181da177e4SLinus Torvalds /* decrease the count for all the possible tokens in a symbol */
5194bfe2b78SMasahiro Yamada static void forget_symbol(const unsigned char *symbol, int len)
5201da177e4SLinus Torvalds {
5211da177e4SLinus Torvalds 	int i;
5221da177e4SLinus Torvalds 
5231da177e4SLinus Torvalds 	for (i = 0; i < len - 1; i++)
524b3dbb4ecSPaulo Marques 		token_profit[ symbol[i] + (symbol[i + 1] << 8) ]--;
5251da177e4SLinus Torvalds }
5261da177e4SLinus Torvalds 
5275e5c4fa7SMasahiro Yamada /* do the initial token count */
5281da177e4SLinus Torvalds static void build_initial_tok_table(void)
5291da177e4SLinus Torvalds {
5305e5c4fa7SMasahiro Yamada 	unsigned int i;
5311da177e4SLinus Torvalds 
5325e5c4fa7SMasahiro Yamada 	for (i = 0; i < table_cnt; i++)
5338d605269SMasahiro Yamada 		learn_symbol(table[i]->sym, table[i]->len);
5341da177e4SLinus Torvalds }
5351da177e4SLinus Torvalds 
5362558c138SMasahiro Yamada static unsigned char *find_token(unsigned char *str, int len,
5374bfe2b78SMasahiro Yamada 				 const unsigned char *token)
5387c5d249aSPaulo Marques {
5397c5d249aSPaulo Marques 	int i;
5407c5d249aSPaulo Marques 
5417c5d249aSPaulo Marques 	for (i = 0; i < len - 1; i++) {
5427c5d249aSPaulo Marques 		if (str[i] == token[0] && str[i+1] == token[1])
5437c5d249aSPaulo Marques 			return &str[i];
5447c5d249aSPaulo Marques 	}
5457c5d249aSPaulo Marques 	return NULL;
5467c5d249aSPaulo Marques }
5477c5d249aSPaulo Marques 
5481da177e4SLinus Torvalds /* replace a given token in all the valid symbols. Use the sampled symbols
5491da177e4SLinus Torvalds  * to update the counts */
5504bfe2b78SMasahiro Yamada static void compress_symbols(const unsigned char *str, int idx)
5511da177e4SLinus Torvalds {
552b3dbb4ecSPaulo Marques 	unsigned int i, len, size;
553b3dbb4ecSPaulo Marques 	unsigned char *p1, *p2;
5541da177e4SLinus Torvalds 
555b3dbb4ecSPaulo Marques 	for (i = 0; i < table_cnt; i++) {
5561da177e4SLinus Torvalds 
5578d605269SMasahiro Yamada 		len = table[i]->len;
5588d605269SMasahiro Yamada 		p1 = table[i]->sym;
559b3dbb4ecSPaulo Marques 
560b3dbb4ecSPaulo Marques 		/* find the token on the symbol */
5617c5d249aSPaulo Marques 		p2 = find_token(p1, len, str);
562b3dbb4ecSPaulo Marques 		if (!p2) continue;
563b3dbb4ecSPaulo Marques 
564b3dbb4ecSPaulo Marques 		/* decrease the counts for this symbol's tokens */
5658d605269SMasahiro Yamada 		forget_symbol(table[i]->sym, len);
566b3dbb4ecSPaulo Marques 
567b3dbb4ecSPaulo Marques 		size = len;
5681da177e4SLinus Torvalds 
5691da177e4SLinus Torvalds 		do {
570b3dbb4ecSPaulo Marques 			*p2 = idx;
571b3dbb4ecSPaulo Marques 			p2++;
572b3dbb4ecSPaulo Marques 			size -= (p2 - p1);
573b3dbb4ecSPaulo Marques 			memmove(p2, p2 + 1, size);
574b3dbb4ecSPaulo Marques 			p1 = p2;
575b3dbb4ecSPaulo Marques 			len--;
576b3dbb4ecSPaulo Marques 
577b3dbb4ecSPaulo Marques 			if (size < 2) break;
578b3dbb4ecSPaulo Marques 
5791da177e4SLinus Torvalds 			/* find the token on the symbol */
5807c5d249aSPaulo Marques 			p2 = find_token(p1, size, str);
5811da177e4SLinus Torvalds 
582b3dbb4ecSPaulo Marques 		} while (p2);
5831da177e4SLinus Torvalds 
5848d605269SMasahiro Yamada 		table[i]->len = len;
585b3dbb4ecSPaulo Marques 
586b3dbb4ecSPaulo Marques 		/* increase the counts for this symbol's new tokens */
5878d605269SMasahiro Yamada 		learn_symbol(table[i]->sym, len);
5881da177e4SLinus Torvalds 	}
5891da177e4SLinus Torvalds }
5901da177e4SLinus Torvalds 
5911da177e4SLinus Torvalds /* search the token with the maximum profit */
592b3dbb4ecSPaulo Marques static int find_best_token(void)
5931da177e4SLinus Torvalds {
594b3dbb4ecSPaulo Marques 	int i, best, bestprofit;
5951da177e4SLinus Torvalds 
5961da177e4SLinus Torvalds 	bestprofit=-10000;
597b3dbb4ecSPaulo Marques 	best = 0;
5981da177e4SLinus Torvalds 
599b3dbb4ecSPaulo Marques 	for (i = 0; i < 0x10000; i++) {
600b3dbb4ecSPaulo Marques 		if (token_profit[i] > bestprofit) {
601b3dbb4ecSPaulo Marques 			best = i;
602b3dbb4ecSPaulo Marques 			bestprofit = token_profit[i];
6031da177e4SLinus Torvalds 		}
6041da177e4SLinus Torvalds 	}
6051da177e4SLinus Torvalds 	return best;
6061da177e4SLinus Torvalds }
6071da177e4SLinus Torvalds 
6081da177e4SLinus Torvalds /* this is the core of the algorithm: calculate the "best" table */
6091da177e4SLinus Torvalds static void optimize_result(void)
6101da177e4SLinus Torvalds {
611b3dbb4ecSPaulo Marques 	int i, best;
6121da177e4SLinus Torvalds 
6131da177e4SLinus Torvalds 	/* using the '\0' symbol last allows compress_symbols to use standard
6141da177e4SLinus Torvalds 	 * fast string functions */
6151da177e4SLinus Torvalds 	for (i = 255; i >= 0; i--) {
6161da177e4SLinus Torvalds 
6171da177e4SLinus Torvalds 		/* if this table slot is empty (it is not used by an actual
6181da177e4SLinus Torvalds 		 * original char code */
6191da177e4SLinus Torvalds 		if (!best_table_len[i]) {
6201da177e4SLinus Torvalds 
621cbf7a90eSCao jin 			/* find the token with the best profit value */
6221da177e4SLinus Torvalds 			best = find_best_token();
623e0a04b11SXiaochen Wang 			if (token_profit[best] == 0)
624e0a04b11SXiaochen Wang 				break;
6251da177e4SLinus Torvalds 
6261da177e4SLinus Torvalds 			/* place it in the "best" table */
627b3dbb4ecSPaulo Marques 			best_table_len[i] = 2;
628b3dbb4ecSPaulo Marques 			best_table[i][0] = best & 0xFF;
629b3dbb4ecSPaulo Marques 			best_table[i][1] = (best >> 8) & 0xFF;
6301da177e4SLinus Torvalds 
6311da177e4SLinus Torvalds 			/* replace this token in all the valid symbols */
632b3dbb4ecSPaulo Marques 			compress_symbols(best_table[i], i);
6331da177e4SLinus Torvalds 		}
6341da177e4SLinus Torvalds 	}
6351da177e4SLinus Torvalds }
6361da177e4SLinus Torvalds 
6371da177e4SLinus Torvalds /* start by placing the symbols that are actually used on the table */
6381da177e4SLinus Torvalds static void insert_real_symbols_in_table(void)
6391da177e4SLinus Torvalds {
640b3dbb4ecSPaulo Marques 	unsigned int i, j, c;
6411da177e4SLinus Torvalds 
642b3dbb4ecSPaulo Marques 	for (i = 0; i < table_cnt; i++) {
6438d605269SMasahiro Yamada 		for (j = 0; j < table[i]->len; j++) {
6448d605269SMasahiro Yamada 			c = table[i]->sym[j];
6451da177e4SLinus Torvalds 			best_table[c][0]=c;
6461da177e4SLinus Torvalds 			best_table_len[c]=1;
6471da177e4SLinus Torvalds 		}
6481da177e4SLinus Torvalds 	}
6491da177e4SLinus Torvalds }
6501da177e4SLinus Torvalds 
6511da177e4SLinus Torvalds static void optimize_token_table(void)
6521da177e4SLinus Torvalds {
6531da177e4SLinus Torvalds 	build_initial_tok_table();
6541da177e4SLinus Torvalds 
6551da177e4SLinus Torvalds 	insert_real_symbols_in_table();
6561da177e4SLinus Torvalds 
6571da177e4SLinus Torvalds 	optimize_result();
6581da177e4SLinus Torvalds }
6591da177e4SLinus Torvalds 
660b478b782SLai Jiangshan /* guess for "linker script provide" symbol */
661b478b782SLai Jiangshan static int may_be_linker_script_provide_symbol(const struct sym_entry *se)
662b478b782SLai Jiangshan {
66329e55ad3SMasahiro Yamada 	const char *symbol = sym_name(se);
664b478b782SLai Jiangshan 	int len = se->len - 1;
665b478b782SLai Jiangshan 
666b478b782SLai Jiangshan 	if (len < 8)
667b478b782SLai Jiangshan 		return 0;
668b478b782SLai Jiangshan 
669b478b782SLai Jiangshan 	if (symbol[0] != '_' || symbol[1] != '_')
670b478b782SLai Jiangshan 		return 0;
671b478b782SLai Jiangshan 
672b478b782SLai Jiangshan 	/* __start_XXXXX */
673b478b782SLai Jiangshan 	if (!memcmp(symbol + 2, "start_", 6))
674b478b782SLai Jiangshan 		return 1;
675b478b782SLai Jiangshan 
676b478b782SLai Jiangshan 	/* __stop_XXXXX */
677b478b782SLai Jiangshan 	if (!memcmp(symbol + 2, "stop_", 5))
678b478b782SLai Jiangshan 		return 1;
679b478b782SLai Jiangshan 
680b478b782SLai Jiangshan 	/* __end_XXXXX */
681b478b782SLai Jiangshan 	if (!memcmp(symbol + 2, "end_", 4))
682b478b782SLai Jiangshan 		return 1;
683b478b782SLai Jiangshan 
684b478b782SLai Jiangshan 	/* __XXXXX_start */
685b478b782SLai Jiangshan 	if (!memcmp(symbol + len - 6, "_start", 6))
686b478b782SLai Jiangshan 		return 1;
687b478b782SLai Jiangshan 
688b478b782SLai Jiangshan 	/* __XXXXX_end */
689b478b782SLai Jiangshan 	if (!memcmp(symbol + len - 4, "_end", 4))
690b478b782SLai Jiangshan 		return 1;
691b478b782SLai Jiangshan 
692b478b782SLai Jiangshan 	return 0;
693b478b782SLai Jiangshan }
694b478b782SLai Jiangshan 
695f2df3f65SPaulo Marques static int compare_symbols(const void *a, const void *b)
696f2df3f65SPaulo Marques {
6978d605269SMasahiro Yamada 	const struct sym_entry *sa = *(const struct sym_entry **)a;
6988d605269SMasahiro Yamada 	const struct sym_entry *sb = *(const struct sym_entry **)b;
699f2df3f65SPaulo Marques 	int wa, wb;
700f2df3f65SPaulo Marques 
701f2df3f65SPaulo Marques 	/* sort by address first */
702f2df3f65SPaulo Marques 	if (sa->addr > sb->addr)
703f2df3f65SPaulo Marques 		return 1;
704f2df3f65SPaulo Marques 	if (sa->addr < sb->addr)
705f2df3f65SPaulo Marques 		return -1;
706f2df3f65SPaulo Marques 
707f2df3f65SPaulo Marques 	/* sort by "weakness" type */
708f2df3f65SPaulo Marques 	wa = (sa->sym[0] == 'w') || (sa->sym[0] == 'W');
709f2df3f65SPaulo Marques 	wb = (sb->sym[0] == 'w') || (sb->sym[0] == 'W');
710f2df3f65SPaulo Marques 	if (wa != wb)
711f2df3f65SPaulo Marques 		return wa - wb;
712f2df3f65SPaulo Marques 
713b478b782SLai Jiangshan 	/* sort by "linker script provide" type */
714b478b782SLai Jiangshan 	wa = may_be_linker_script_provide_symbol(sa);
715b478b782SLai Jiangshan 	wb = may_be_linker_script_provide_symbol(sb);
716b478b782SLai Jiangshan 	if (wa != wb)
717b478b782SLai Jiangshan 		return wa - wb;
718b478b782SLai Jiangshan 
719b478b782SLai Jiangshan 	/* sort by the number of prefix underscores */
720aa915245SMasahiro Yamada 	wa = strspn(sym_name(sa), "_");
721aa915245SMasahiro Yamada 	wb = strspn(sym_name(sb), "_");
722b478b782SLai Jiangshan 	if (wa != wb)
723b478b782SLai Jiangshan 		return wa - wb;
724b478b782SLai Jiangshan 
725f2df3f65SPaulo Marques 	/* sort by initial order, so that other symbols are left undisturbed */
726f2df3f65SPaulo Marques 	return sa->start_pos - sb->start_pos;
727f2df3f65SPaulo Marques }
728f2df3f65SPaulo Marques 
729f2df3f65SPaulo Marques static void sort_symbols(void)
730f2df3f65SPaulo Marques {
7318d605269SMasahiro Yamada 	qsort(table, table_cnt, sizeof(table[0]), compare_symbols);
732f2df3f65SPaulo Marques }
7331da177e4SLinus Torvalds 
734c6bda7c9SRusty Russell static void make_percpus_absolute(void)
735c6bda7c9SRusty Russell {
736c6bda7c9SRusty Russell 	unsigned int i;
737c6bda7c9SRusty Russell 
738c6bda7c9SRusty Russell 	for (i = 0; i < table_cnt; i++)
7398d605269SMasahiro Yamada 		if (symbol_in_range(table[i], &percpu_range, 1)) {
7408c996940SArd Biesheuvel 			/*
7418c996940SArd Biesheuvel 			 * Keep the 'A' override for percpu symbols to
7428c996940SArd Biesheuvel 			 * ensure consistent behavior compared to older
7438c996940SArd Biesheuvel 			 * versions of this tool.
7448c996940SArd Biesheuvel 			 */
7458d605269SMasahiro Yamada 			table[i]->sym[0] = 'A';
7468d605269SMasahiro Yamada 			table[i]->percpu_absolute = 1;
7478c996940SArd Biesheuvel 		}
748c6bda7c9SRusty Russell }
749c6bda7c9SRusty Russell 
7502213e9a6SArd Biesheuvel /* find the minimum non-absolute symbol address */
7512213e9a6SArd Biesheuvel static void record_relative_base(void)
7522213e9a6SArd Biesheuvel {
7532213e9a6SArd Biesheuvel 	unsigned int i;
7542213e9a6SArd Biesheuvel 
7552213e9a6SArd Biesheuvel 	for (i = 0; i < table_cnt; i++)
7568d605269SMasahiro Yamada 		if (!symbol_absolute(table[i])) {
757f34ea029SMasahiro Yamada 			/*
758f34ea029SMasahiro Yamada 			 * The table is sorted by address.
759f34ea029SMasahiro Yamada 			 * Take the first non-absolute symbol value.
760f34ea029SMasahiro Yamada 			 */
7618d605269SMasahiro Yamada 			relative_base = table[i]->addr;
762f34ea029SMasahiro Yamada 			return;
763f34ea029SMasahiro Yamada 		}
7642213e9a6SArd Biesheuvel }
7652213e9a6SArd Biesheuvel 
766b3dbb4ecSPaulo Marques int main(int argc, char **argv)
7671da177e4SLinus Torvalds {
76841f11a4fSYoshinori Sato 	if (argc >= 2) {
76941f11a4fSYoshinori Sato 		int i;
77041f11a4fSYoshinori Sato 		for (i = 1; i < argc; i++) {
77141f11a4fSYoshinori Sato 			if(strcmp(argv[i], "--all-symbols") == 0)
7721da177e4SLinus Torvalds 				all_symbols = 1;
773c6bda7c9SRusty Russell 			else if (strcmp(argv[i], "--absolute-percpu") == 0)
774c6bda7c9SRusty Russell 				absolute_percpu = 1;
775534c9f2eSMasahiro Yamada 			else if (strcmp(argv[i], "--base-relative") == 0)
7762213e9a6SArd Biesheuvel 				base_relative = 1;
7772213e9a6SArd Biesheuvel 			else
77841f11a4fSYoshinori Sato 				usage();
77941f11a4fSYoshinori Sato 		}
78041f11a4fSYoshinori Sato 	} else if (argc != 1)
7811da177e4SLinus Torvalds 		usage();
7821da177e4SLinus Torvalds 
7831da177e4SLinus Torvalds 	read_map(stdin);
7845e5c4fa7SMasahiro Yamada 	shrink_table();
785c6bda7c9SRusty Russell 	if (absolute_percpu)
786c6bda7c9SRusty Russell 		make_percpus_absolute();
787f34ea029SMasahiro Yamada 	sort_symbols();
7882213e9a6SArd Biesheuvel 	if (base_relative)
7892213e9a6SArd Biesheuvel 		record_relative_base();
7901da177e4SLinus Torvalds 	optimize_token_table();
7911da177e4SLinus Torvalds 	write_src();
7921da177e4SLinus Torvalds 
7931da177e4SLinus Torvalds 	return 0;
7941da177e4SLinus Torvalds }
795