xref: /linux/scripts/kallsyms.c (revision efe6e3068067212b85c2d0474b5ee3b2d0c7adab)
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. */
11197261e1eSMasahiro Yamada 		".LASANPC",		/* s390 kasan local symbols */
112a41333e0SMasahiro Yamada 		"__crc_",		/* modversions */
113a41333e0SMasahiro Yamada 		"__efistub_",		/* arm64 EFI stub namespace */
11476217129SDavid Brazdil 		"__kvm_nvhe_",		/* arm64 non-VHE KVM namespace */
115*efe6e306SArnd Bergmann 		"__AArch64ADRPThunk_",	/* arm64 lld */
116*efe6e306SArnd Bergmann 		"__ARMV5PILongThunk_",	/* arm lld */
117*efe6e306SArnd Bergmann 		"__ARMV7PILongThunk_",
118*efe6e306SArnd Bergmann 		"__ThumbV7PILongThunk_",
119*efe6e306SArnd Bergmann 		"__LA25Thunk_",		/* mips lld */
120*efe6e306SArnd Bergmann 		"__microLA25Thunk_",
121a41333e0SMasahiro Yamada 		NULL
122a41333e0SMasahiro Yamada 	};
123a41333e0SMasahiro Yamada 
124516d980fSMasahiro Yamada 	/* Symbol names that end with the following are ignored.*/
125a41333e0SMasahiro Yamada 	static const char * const ignored_suffixes[] = {
126a41333e0SMasahiro Yamada 		"_from_arm",		/* arm */
127a41333e0SMasahiro Yamada 		"_from_thumb",		/* arm */
128a41333e0SMasahiro Yamada 		"_veneer",		/* arm */
129a41333e0SMasahiro Yamada 		NULL
130a41333e0SMasahiro Yamada 	};
131a41333e0SMasahiro Yamada 
132516d980fSMasahiro Yamada 	/* Symbol names that contain the following are ignored.*/
133516d980fSMasahiro Yamada 	static const char * const ignored_matches[] = {
134516d980fSMasahiro Yamada 		".long_branch.",	/* ppc stub */
135516d980fSMasahiro Yamada 		".plt_branch.",		/* ppc stub */
136516d980fSMasahiro Yamada 		NULL
137516d980fSMasahiro Yamada 	};
138516d980fSMasahiro Yamada 
139a41333e0SMasahiro Yamada 	const char * const *p;
140a41333e0SMasahiro Yamada 
141a41333e0SMasahiro Yamada 	for (p = ignored_symbols; *p; p++)
142a41333e0SMasahiro Yamada 		if (!strcmp(name, *p))
143a41333e0SMasahiro Yamada 			return true;
144a41333e0SMasahiro Yamada 
145a41333e0SMasahiro Yamada 	for (p = ignored_prefixes; *p; p++)
146a41333e0SMasahiro Yamada 		if (!strncmp(name, *p, strlen(*p)))
147a41333e0SMasahiro Yamada 			return true;
148a41333e0SMasahiro Yamada 
149a41333e0SMasahiro Yamada 	for (p = ignored_suffixes; *p; p++) {
150a41333e0SMasahiro Yamada 		int l = strlen(name) - strlen(*p);
151a41333e0SMasahiro Yamada 
152a41333e0SMasahiro Yamada 		if (l >= 0 && !strcmp(name + l, *p))
153a41333e0SMasahiro Yamada 			return true;
154a41333e0SMasahiro Yamada 	}
155a41333e0SMasahiro Yamada 
156516d980fSMasahiro Yamada 	for (p = ignored_matches; *p; p++) {
157516d980fSMasahiro Yamada 		if (strstr(name, *p))
158516d980fSMasahiro Yamada 			return true;
159516d980fSMasahiro Yamada 	}
160516d980fSMasahiro Yamada 
161887df76dSMasahiro Yamada 	if (type == 'U' || type == 'u')
162887df76dSMasahiro Yamada 		return true;
163887df76dSMasahiro Yamada 	/* exclude debugging symbols */
164887df76dSMasahiro Yamada 	if (type == 'N' || type == 'n')
165887df76dSMasahiro Yamada 		return true;
166887df76dSMasahiro Yamada 
167887df76dSMasahiro Yamada 	if (toupper(type) == 'A') {
168887df76dSMasahiro Yamada 		/* Keep these useful absolute symbols */
169887df76dSMasahiro Yamada 		if (strcmp(name, "__kernel_syscall_via_break") &&
170887df76dSMasahiro Yamada 		    strcmp(name, "__kernel_syscall_via_epc") &&
171887df76dSMasahiro Yamada 		    strcmp(name, "__kernel_sigtramp") &&
172887df76dSMasahiro Yamada 		    strcmp(name, "__gp"))
173887df76dSMasahiro Yamada 			return true;
174887df76dSMasahiro Yamada 	}
175887df76dSMasahiro Yamada 
176a41333e0SMasahiro Yamada 	return false;
177a41333e0SMasahiro Yamada }
178a41333e0SMasahiro Yamada 
179b6233d0dSMasahiro Yamada static void check_symbol_range(const char *sym, unsigned long long addr,
18078eb7159SKees Cook 			       struct addr_range *ranges, int entries)
18117b1f0deSMike Frysinger {
18217b1f0deSMike Frysinger 	size_t i;
18378eb7159SKees Cook 	struct addr_range *ar;
18417b1f0deSMike Frysinger 
18578eb7159SKees Cook 	for (i = 0; i < entries; ++i) {
18678eb7159SKees Cook 		ar = &ranges[i];
18717b1f0deSMike Frysinger 
18878eb7159SKees Cook 		if (strcmp(sym, ar->start_sym) == 0) {
18978eb7159SKees Cook 			ar->start = addr;
190b6233d0dSMasahiro Yamada 			return;
19178eb7159SKees Cook 		} else if (strcmp(sym, ar->end_sym) == 0) {
19278eb7159SKees Cook 			ar->end = addr;
193b6233d0dSMasahiro Yamada 			return;
19417b1f0deSMike Frysinger 		}
19517b1f0deSMike Frysinger 	}
19617b1f0deSMike Frysinger }
19717b1f0deSMike Frysinger 
1988d605269SMasahiro Yamada static struct sym_entry *read_symbol(FILE *in)
1991da177e4SLinus Torvalds {
200be9f6133SMasahiro Yamada 	char name[500], type;
2018d605269SMasahiro Yamada 	unsigned long long addr;
2028d605269SMasahiro Yamada 	unsigned int len;
2038d605269SMasahiro Yamada 	struct sym_entry *sym;
2041da177e4SLinus Torvalds 	int rc;
2051da177e4SLinus Torvalds 
2068d605269SMasahiro Yamada 	rc = fscanf(in, "%llx %c %499s\n", &addr, &type, name);
2071da177e4SLinus Torvalds 	if (rc != 3) {
208be9f6133SMasahiro Yamada 		if (rc != EOF && fgets(name, 500, in) == NULL)
209ef894870SJean Sacren 			fprintf(stderr, "Read error or end of file.\n");
2108d605269SMasahiro Yamada 		return NULL;
2111da177e4SLinus Torvalds 	}
212be9f6133SMasahiro Yamada 	if (strlen(name) >= KSYM_NAME_LEN) {
2136db2983cSEugene Loh 		fprintf(stderr, "Symbol %s too long for kallsyms (%zu >= %d).\n"
214f3462aa9SAndi Kleen 				"Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n",
215be9f6133SMasahiro Yamada 			name, strlen(name), KSYM_NAME_LEN);
2168d605269SMasahiro Yamada 		return NULL;
217f3462aa9SAndi Kleen 	}
2181da177e4SLinus Torvalds 
219be9f6133SMasahiro Yamada 	if (strcmp(name, "_text") == 0)
2208d605269SMasahiro Yamada 		_text = addr;
221b6233d0dSMasahiro Yamada 
2227883a143SMikhail Petrov 	/* Ignore most absolute/undefined (?) symbols. */
2237883a143SMikhail Petrov 	if (is_ignored_symbol(name, type))
2247883a143SMikhail Petrov 		return NULL;
2257883a143SMikhail Petrov 
2268d605269SMasahiro Yamada 	check_symbol_range(name, addr, text_ranges, ARRAY_SIZE(text_ranges));
2278d605269SMasahiro Yamada 	check_symbol_range(name, addr, &percpu_range, 1);
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds 	/* include the type field in the symbol name, so that it gets
2301da177e4SLinus Torvalds 	 * compressed together */
2318d605269SMasahiro Yamada 
2328d605269SMasahiro Yamada 	len = strlen(name) + 1;
2338d605269SMasahiro Yamada 
2349d1b3895SMasahiro Yamada 	sym = malloc(sizeof(*sym) + len + 1);
2358d605269SMasahiro Yamada 	if (!sym) {
236f1a136e0SJesper Juhl 		fprintf(stderr, "kallsyms failure: "
237f1a136e0SJesper Juhl 			"unable to allocate required amount of memory\n");
238f1a136e0SJesper Juhl 		exit(EXIT_FAILURE);
239f1a136e0SJesper Juhl 	}
2408d605269SMasahiro Yamada 	sym->addr = addr;
2418d605269SMasahiro Yamada 	sym->len = len;
2428d605269SMasahiro Yamada 	sym->sym[0] = type;
2439d1b3895SMasahiro Yamada 	strcpy(sym_name(sym), name);
2448d605269SMasahiro Yamada 	sym->percpu_absolute = 0;
2451da177e4SLinus Torvalds 
2468d605269SMasahiro Yamada 	return sym;
2471da177e4SLinus Torvalds }
2481da177e4SLinus Torvalds 
2494bfe2b78SMasahiro Yamada static int symbol_in_range(const struct sym_entry *s,
2504bfe2b78SMasahiro Yamada 			   const struct addr_range *ranges, int entries)
25117b1f0deSMike Frysinger {
25217b1f0deSMike Frysinger 	size_t i;
2534bfe2b78SMasahiro Yamada 	const struct addr_range *ar;
25417b1f0deSMike Frysinger 
25578eb7159SKees Cook 	for (i = 0; i < entries; ++i) {
25678eb7159SKees Cook 		ar = &ranges[i];
25717b1f0deSMike Frysinger 
25878eb7159SKees Cook 		if (s->addr >= ar->start && s->addr <= ar->end)
259ac6ca5c8SMike Frysinger 			return 1;
26017b1f0deSMike Frysinger 	}
26117b1f0deSMike Frysinger 
262ac6ca5c8SMike Frysinger 	return 0;
26317b1f0deSMike Frysinger }
26417b1f0deSMike Frysinger 
2654bfe2b78SMasahiro Yamada static int symbol_valid(const struct sym_entry *s)
2661da177e4SLinus Torvalds {
26729e55ad3SMasahiro Yamada 	const char *name = sym_name(s);
268bd8b22d2SArd Biesheuvel 
2691da177e4SLinus Torvalds 	/* if --all-symbols is not specified, then symbols outside the text
2701da177e4SLinus Torvalds 	 * and inittext sections are discarded */
2711da177e4SLinus Torvalds 	if (!all_symbols) {
27278eb7159SKees Cook 		if (symbol_in_range(s, text_ranges,
27378eb7159SKees Cook 				    ARRAY_SIZE(text_ranges)) == 0)
2741da177e4SLinus Torvalds 			return 0;
2751da177e4SLinus Torvalds 		/* Corner case.  Discard any symbols with the same value as
276a3b81113SRobin Getz 		 * _etext _einittext; they can move between pass 1 and 2 when
277a3b81113SRobin Getz 		 * the kallsyms data are added.  If these symbols move then
278a3b81113SRobin Getz 		 * they may get dropped in pass 2, which breaks the kallsyms
279a3b81113SRobin Getz 		 * rules.
2801da177e4SLinus Torvalds 		 */
28117b1f0deSMike Frysinger 		if ((s->addr == text_range_text->end &&
28229e55ad3SMasahiro Yamada 		     strcmp(name, text_range_text->end_sym)) ||
28317b1f0deSMike Frysinger 		    (s->addr == text_range_inittext->end &&
28429e55ad3SMasahiro Yamada 		     strcmp(name, text_range_inittext->end_sym)))
2851da177e4SLinus Torvalds 			return 0;
2861da177e4SLinus Torvalds 	}
2871da177e4SLinus Torvalds 
2881da177e4SLinus Torvalds 	return 1;
2891da177e4SLinus Torvalds }
2901da177e4SLinus Torvalds 
2915e5c4fa7SMasahiro Yamada /* remove all the invalid symbols from the table */
2925e5c4fa7SMasahiro Yamada static void shrink_table(void)
2935e5c4fa7SMasahiro Yamada {
2945e5c4fa7SMasahiro Yamada 	unsigned int i, pos;
2955e5c4fa7SMasahiro Yamada 
2965e5c4fa7SMasahiro Yamada 	pos = 0;
2975e5c4fa7SMasahiro Yamada 	for (i = 0; i < table_cnt; i++) {
2988d605269SMasahiro Yamada 		if (symbol_valid(table[i])) {
2995e5c4fa7SMasahiro Yamada 			if (pos != i)
3005e5c4fa7SMasahiro Yamada 				table[pos] = table[i];
3015e5c4fa7SMasahiro Yamada 			pos++;
3025e5c4fa7SMasahiro Yamada 		} else {
3038d605269SMasahiro Yamada 			free(table[i]);
3045e5c4fa7SMasahiro Yamada 		}
3055e5c4fa7SMasahiro Yamada 	}
3065e5c4fa7SMasahiro Yamada 	table_cnt = pos;
3075e5c4fa7SMasahiro Yamada 
3085e5c4fa7SMasahiro Yamada 	/* When valid symbol is not registered, exit to error */
3095e5c4fa7SMasahiro Yamada 	if (!table_cnt) {
3105e5c4fa7SMasahiro Yamada 		fprintf(stderr, "No valid symbol.\n");
3115e5c4fa7SMasahiro Yamada 		exit(1);
3125e5c4fa7SMasahiro Yamada 	}
3135e5c4fa7SMasahiro Yamada }
3145e5c4fa7SMasahiro Yamada 
315b3dbb4ecSPaulo Marques static void read_map(FILE *in)
3161da177e4SLinus Torvalds {
3178d605269SMasahiro Yamada 	struct sym_entry *sym;
3188d605269SMasahiro Yamada 
3191da177e4SLinus Torvalds 	while (!feof(in)) {
3208d605269SMasahiro Yamada 		sym = read_symbol(in);
3218d605269SMasahiro Yamada 		if (!sym)
3228d605269SMasahiro Yamada 			continue;
3238d605269SMasahiro Yamada 
3248d605269SMasahiro Yamada 		sym->start_pos = table_cnt;
3258d605269SMasahiro Yamada 
326b3dbb4ecSPaulo Marques 		if (table_cnt >= table_size) {
327b3dbb4ecSPaulo Marques 			table_size += 10000;
328b3dbb4ecSPaulo Marques 			table = realloc(table, sizeof(*table) * table_size);
3291da177e4SLinus Torvalds 			if (!table) {
3301da177e4SLinus Torvalds 				fprintf(stderr, "out of memory\n");
3311da177e4SLinus Torvalds 				exit (1);
3321da177e4SLinus Torvalds 			}
3331da177e4SLinus Torvalds 		}
3348d605269SMasahiro Yamada 
3358d605269SMasahiro Yamada 		table[table_cnt++] = sym;
3361da177e4SLinus Torvalds 	}
337f2df3f65SPaulo Marques }
3381da177e4SLinus Torvalds 
3394bfe2b78SMasahiro Yamada static void output_label(const char *label)
3401da177e4SLinus Torvalds {
3411da177e4SLinus Torvalds 	printf(".globl %s\n", label);
3421da177e4SLinus Torvalds 	printf("\tALGN\n");
3431da177e4SLinus Torvalds 	printf("%s:\n", label);
3441da177e4SLinus Torvalds }
3451da177e4SLinus Torvalds 
346fd2ab2f6SMasahiro Yamada /* Provide proper symbols relocatability by their '_text' relativeness. */
347fd2ab2f6SMasahiro Yamada static void output_address(unsigned long long addr)
348fd2ab2f6SMasahiro Yamada {
349fd2ab2f6SMasahiro Yamada 	if (_text <= addr)
350fd2ab2f6SMasahiro Yamada 		printf("\tPTR\t_text + %#llx\n", addr - _text);
351fd2ab2f6SMasahiro Yamada 	else
352fd2ab2f6SMasahiro Yamada 		printf("\tPTR\t_text - %#llx\n", _text - addr);
353fd2ab2f6SMasahiro Yamada }
354fd2ab2f6SMasahiro Yamada 
3551da177e4SLinus Torvalds /* uncompress a compressed symbol. When this function is called, the best table
3561da177e4SLinus Torvalds  * might still be compressed itself, so the function needs to be recursive */
3574bfe2b78SMasahiro Yamada static int expand_symbol(const unsigned char *data, int len, char *result)
3581da177e4SLinus Torvalds {
3591da177e4SLinus Torvalds 	int c, rlen, total=0;
3601da177e4SLinus Torvalds 
3611da177e4SLinus Torvalds 	while (len) {
3621da177e4SLinus Torvalds 		c = *data;
3631da177e4SLinus Torvalds 		/* if the table holds a single char that is the same as the one
3641da177e4SLinus Torvalds 		 * we are looking for, then end the search */
3651da177e4SLinus Torvalds 		if (best_table[c][0]==c && best_table_len[c]==1) {
3661da177e4SLinus Torvalds 			*result++ = c;
3671da177e4SLinus Torvalds 			total++;
3681da177e4SLinus Torvalds 		} else {
3691da177e4SLinus Torvalds 			/* if not, recurse and expand */
3701da177e4SLinus Torvalds 			rlen = expand_symbol(best_table[c], best_table_len[c], result);
3711da177e4SLinus Torvalds 			total += rlen;
3721da177e4SLinus Torvalds 			result += rlen;
3731da177e4SLinus Torvalds 		}
3741da177e4SLinus Torvalds 		data++;
3751da177e4SLinus Torvalds 		len--;
3761da177e4SLinus Torvalds 	}
3771da177e4SLinus Torvalds 	*result=0;
3781da177e4SLinus Torvalds 
3791da177e4SLinus Torvalds 	return total;
3801da177e4SLinus Torvalds }
3811da177e4SLinus Torvalds 
3824bfe2b78SMasahiro Yamada static int symbol_absolute(const struct sym_entry *s)
38378eb7159SKees Cook {
3848c996940SArd Biesheuvel 	return s->percpu_absolute;
38578eb7159SKees Cook }
38678eb7159SKees Cook 
387b3dbb4ecSPaulo Marques static void write_src(void)
3881da177e4SLinus Torvalds {
389b3dbb4ecSPaulo Marques 	unsigned int i, k, off;
3901da177e4SLinus Torvalds 	unsigned int best_idx[256];
3911da177e4SLinus Torvalds 	unsigned int *markers;
3929281aceaSTejun Heo 	char buf[KSYM_NAME_LEN];
3931da177e4SLinus Torvalds 
394500193ecSMasahiro Yamada 	printf("#include <asm/bitsperlong.h>\n");
3951da177e4SLinus Torvalds 	printf("#if BITS_PER_LONG == 64\n");
3961da177e4SLinus Torvalds 	printf("#define PTR .quad\n");
39772d3ebb9SMathias Krause 	printf("#define ALGN .balign 8\n");
3981da177e4SLinus Torvalds 	printf("#else\n");
3991da177e4SLinus Torvalds 	printf("#define PTR .long\n");
40072d3ebb9SMathias Krause 	printf("#define ALGN .balign 4\n");
4011da177e4SLinus Torvalds 	printf("#endif\n");
4021da177e4SLinus Torvalds 
403aad09470SJan Beulich 	printf("\t.section .rodata, \"a\"\n");
4041da177e4SLinus Torvalds 
4052213e9a6SArd Biesheuvel 	if (!base_relative)
4061da177e4SLinus Torvalds 		output_label("kallsyms_addresses");
4072213e9a6SArd Biesheuvel 	else
4082213e9a6SArd Biesheuvel 		output_label("kallsyms_offsets");
4092213e9a6SArd Biesheuvel 
410b3dbb4ecSPaulo Marques 	for (i = 0; i < table_cnt; i++) {
4112213e9a6SArd Biesheuvel 		if (base_relative) {
412fd2ab2f6SMasahiro Yamada 			/*
413fd2ab2f6SMasahiro Yamada 			 * Use the offset relative to the lowest value
414fd2ab2f6SMasahiro Yamada 			 * encountered of all relative symbols, and emit
415fd2ab2f6SMasahiro Yamada 			 * non-relocatable fixed offsets that will be fixed
416fd2ab2f6SMasahiro Yamada 			 * up at runtime.
417fd2ab2f6SMasahiro Yamada 			 */
418fd2ab2f6SMasahiro Yamada 
4192213e9a6SArd Biesheuvel 			long long offset;
4202213e9a6SArd Biesheuvel 			int overflow;
4212213e9a6SArd Biesheuvel 
4222213e9a6SArd Biesheuvel 			if (!absolute_percpu) {
4238d605269SMasahiro Yamada 				offset = table[i]->addr - relative_base;
4242213e9a6SArd Biesheuvel 				overflow = (offset < 0 || offset > UINT_MAX);
4258d605269SMasahiro Yamada 			} else if (symbol_absolute(table[i])) {
4268d605269SMasahiro Yamada 				offset = table[i]->addr;
4272213e9a6SArd Biesheuvel 				overflow = (offset < 0 || offset > INT_MAX);
4282213e9a6SArd Biesheuvel 			} else {
4298d605269SMasahiro Yamada 				offset = relative_base - table[i]->addr - 1;
4302213e9a6SArd Biesheuvel 				overflow = (offset < INT_MIN || offset >= 0);
4312213e9a6SArd Biesheuvel 			}
4322213e9a6SArd Biesheuvel 			if (overflow) {
4332213e9a6SArd Biesheuvel 				fprintf(stderr, "kallsyms failure: "
4342213e9a6SArd Biesheuvel 					"%s symbol value %#llx out of range in relative mode\n",
4358d605269SMasahiro Yamada 					symbol_absolute(table[i]) ? "absolute" : "relative",
4368d605269SMasahiro Yamada 					table[i]->addr);
4372213e9a6SArd Biesheuvel 				exit(EXIT_FAILURE);
4382213e9a6SArd Biesheuvel 			}
4392213e9a6SArd Biesheuvel 			printf("\t.long\t%#x\n", (int)offset);
4408d605269SMasahiro Yamada 		} else if (!symbol_absolute(table[i])) {
4418d605269SMasahiro Yamada 			output_address(table[i]->addr);
442fd593d12SEric W. Biederman 		} else {
4438d605269SMasahiro Yamada 			printf("\tPTR\t%#llx\n", table[i]->addr);
4441da177e4SLinus Torvalds 		}
445fd593d12SEric W. Biederman 	}
4461da177e4SLinus Torvalds 	printf("\n");
4471da177e4SLinus Torvalds 
4482213e9a6SArd Biesheuvel 	if (base_relative) {
4492213e9a6SArd Biesheuvel 		output_label("kallsyms_relative_base");
450fd2ab2f6SMasahiro Yamada 		output_address(relative_base);
4512213e9a6SArd Biesheuvel 		printf("\n");
4522213e9a6SArd Biesheuvel 	}
4532213e9a6SArd Biesheuvel 
4541da177e4SLinus Torvalds 	output_label("kallsyms_num_syms");
45580ffbaa5SJan Beulich 	printf("\t.long\t%u\n", table_cnt);
4561da177e4SLinus Torvalds 	printf("\n");
4571da177e4SLinus Torvalds 
4581da177e4SLinus Torvalds 	/* table of offset markers, that give the offset in the compressed stream
4591da177e4SLinus Torvalds 	 * every 256 symbols */
460f1a136e0SJesper Juhl 	markers = malloc(sizeof(unsigned int) * ((table_cnt + 255) / 256));
461f1a136e0SJesper Juhl 	if (!markers) {
462f1a136e0SJesper Juhl 		fprintf(stderr, "kallsyms failure: "
463f1a136e0SJesper Juhl 			"unable to allocate required memory\n");
464f1a136e0SJesper Juhl 		exit(EXIT_FAILURE);
465f1a136e0SJesper Juhl 	}
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds 	output_label("kallsyms_names");
4681da177e4SLinus Torvalds 	off = 0;
469b3dbb4ecSPaulo Marques 	for (i = 0; i < table_cnt; i++) {
470b3dbb4ecSPaulo Marques 		if ((i & 0xFF) == 0)
471b3dbb4ecSPaulo Marques 			markers[i >> 8] = off;
4721da177e4SLinus Torvalds 
4738d605269SMasahiro Yamada 		printf("\t.byte 0x%02x", table[i]->len);
4748d605269SMasahiro Yamada 		for (k = 0; k < table[i]->len; k++)
4758d605269SMasahiro Yamada 			printf(", 0x%02x", table[i]->sym[k]);
4761da177e4SLinus Torvalds 		printf("\n");
4771da177e4SLinus Torvalds 
4788d605269SMasahiro Yamada 		off += table[i]->len + 1;
4791da177e4SLinus Torvalds 	}
4801da177e4SLinus Torvalds 	printf("\n");
4811da177e4SLinus Torvalds 
4821da177e4SLinus Torvalds 	output_label("kallsyms_markers");
483b3dbb4ecSPaulo Marques 	for (i = 0; i < ((table_cnt + 255) >> 8); i++)
48480ffbaa5SJan Beulich 		printf("\t.long\t%u\n", markers[i]);
4851da177e4SLinus Torvalds 	printf("\n");
4861da177e4SLinus Torvalds 
4871da177e4SLinus Torvalds 	free(markers);
4881da177e4SLinus Torvalds 
4891da177e4SLinus Torvalds 	output_label("kallsyms_token_table");
4901da177e4SLinus Torvalds 	off = 0;
4911da177e4SLinus Torvalds 	for (i = 0; i < 256; i++) {
4921da177e4SLinus Torvalds 		best_idx[i] = off;
4931da177e4SLinus Torvalds 		expand_symbol(best_table[i], best_table_len[i], buf);
4941da177e4SLinus Torvalds 		printf("\t.asciz\t\"%s\"\n", buf);
4951da177e4SLinus Torvalds 		off += strlen(buf) + 1;
4961da177e4SLinus Torvalds 	}
4971da177e4SLinus Torvalds 	printf("\n");
4981da177e4SLinus Torvalds 
4991da177e4SLinus Torvalds 	output_label("kallsyms_token_index");
5001da177e4SLinus Torvalds 	for (i = 0; i < 256; i++)
5011da177e4SLinus Torvalds 		printf("\t.short\t%d\n", best_idx[i]);
5021da177e4SLinus Torvalds 	printf("\n");
5031da177e4SLinus Torvalds }
5041da177e4SLinus Torvalds 
5051da177e4SLinus Torvalds 
5061da177e4SLinus Torvalds /* table lookup compression functions */
5071da177e4SLinus Torvalds 
5081da177e4SLinus Torvalds /* count all the possible tokens in a symbol */
5094bfe2b78SMasahiro Yamada static void learn_symbol(const unsigned char *symbol, int len)
5101da177e4SLinus Torvalds {
5111da177e4SLinus Torvalds 	int i;
5121da177e4SLinus Torvalds 
5131da177e4SLinus Torvalds 	for (i = 0; i < len - 1; i++)
514b3dbb4ecSPaulo Marques 		token_profit[ symbol[i] + (symbol[i + 1] << 8) ]++;
5151da177e4SLinus Torvalds }
5161da177e4SLinus Torvalds 
5171da177e4SLinus Torvalds /* decrease the count for all the possible tokens in a symbol */
5184bfe2b78SMasahiro Yamada static void forget_symbol(const unsigned char *symbol, int len)
5191da177e4SLinus Torvalds {
5201da177e4SLinus Torvalds 	int i;
5211da177e4SLinus Torvalds 
5221da177e4SLinus Torvalds 	for (i = 0; i < len - 1; i++)
523b3dbb4ecSPaulo Marques 		token_profit[ symbol[i] + (symbol[i + 1] << 8) ]--;
5241da177e4SLinus Torvalds }
5251da177e4SLinus Torvalds 
5265e5c4fa7SMasahiro Yamada /* do the initial token count */
5271da177e4SLinus Torvalds static void build_initial_tok_table(void)
5281da177e4SLinus Torvalds {
5295e5c4fa7SMasahiro Yamada 	unsigned int i;
5301da177e4SLinus Torvalds 
5315e5c4fa7SMasahiro Yamada 	for (i = 0; i < table_cnt; i++)
5328d605269SMasahiro Yamada 		learn_symbol(table[i]->sym, table[i]->len);
5331da177e4SLinus Torvalds }
5341da177e4SLinus Torvalds 
5352558c138SMasahiro Yamada static unsigned char *find_token(unsigned char *str, int len,
5364bfe2b78SMasahiro Yamada 				 const unsigned char *token)
5377c5d249aSPaulo Marques {
5387c5d249aSPaulo Marques 	int i;
5397c5d249aSPaulo Marques 
5407c5d249aSPaulo Marques 	for (i = 0; i < len - 1; i++) {
5417c5d249aSPaulo Marques 		if (str[i] == token[0] && str[i+1] == token[1])
5427c5d249aSPaulo Marques 			return &str[i];
5437c5d249aSPaulo Marques 	}
5447c5d249aSPaulo Marques 	return NULL;
5457c5d249aSPaulo Marques }
5467c5d249aSPaulo Marques 
5471da177e4SLinus Torvalds /* replace a given token in all the valid symbols. Use the sampled symbols
5481da177e4SLinus Torvalds  * to update the counts */
5494bfe2b78SMasahiro Yamada static void compress_symbols(const unsigned char *str, int idx)
5501da177e4SLinus Torvalds {
551b3dbb4ecSPaulo Marques 	unsigned int i, len, size;
552b3dbb4ecSPaulo Marques 	unsigned char *p1, *p2;
5531da177e4SLinus Torvalds 
554b3dbb4ecSPaulo Marques 	for (i = 0; i < table_cnt; i++) {
5551da177e4SLinus Torvalds 
5568d605269SMasahiro Yamada 		len = table[i]->len;
5578d605269SMasahiro Yamada 		p1 = table[i]->sym;
558b3dbb4ecSPaulo Marques 
559b3dbb4ecSPaulo Marques 		/* find the token on the symbol */
5607c5d249aSPaulo Marques 		p2 = find_token(p1, len, str);
561b3dbb4ecSPaulo Marques 		if (!p2) continue;
562b3dbb4ecSPaulo Marques 
563b3dbb4ecSPaulo Marques 		/* decrease the counts for this symbol's tokens */
5648d605269SMasahiro Yamada 		forget_symbol(table[i]->sym, len);
565b3dbb4ecSPaulo Marques 
566b3dbb4ecSPaulo Marques 		size = len;
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds 		do {
569b3dbb4ecSPaulo Marques 			*p2 = idx;
570b3dbb4ecSPaulo Marques 			p2++;
571b3dbb4ecSPaulo Marques 			size -= (p2 - p1);
572b3dbb4ecSPaulo Marques 			memmove(p2, p2 + 1, size);
573b3dbb4ecSPaulo Marques 			p1 = p2;
574b3dbb4ecSPaulo Marques 			len--;
575b3dbb4ecSPaulo Marques 
576b3dbb4ecSPaulo Marques 			if (size < 2) break;
577b3dbb4ecSPaulo Marques 
5781da177e4SLinus Torvalds 			/* find the token on the symbol */
5797c5d249aSPaulo Marques 			p2 = find_token(p1, size, str);
5801da177e4SLinus Torvalds 
581b3dbb4ecSPaulo Marques 		} while (p2);
5821da177e4SLinus Torvalds 
5838d605269SMasahiro Yamada 		table[i]->len = len;
584b3dbb4ecSPaulo Marques 
585b3dbb4ecSPaulo Marques 		/* increase the counts for this symbol's new tokens */
5868d605269SMasahiro Yamada 		learn_symbol(table[i]->sym, len);
5871da177e4SLinus Torvalds 	}
5881da177e4SLinus Torvalds }
5891da177e4SLinus Torvalds 
5901da177e4SLinus Torvalds /* search the token with the maximum profit */
591b3dbb4ecSPaulo Marques static int find_best_token(void)
5921da177e4SLinus Torvalds {
593b3dbb4ecSPaulo Marques 	int i, best, bestprofit;
5941da177e4SLinus Torvalds 
5951da177e4SLinus Torvalds 	bestprofit=-10000;
596b3dbb4ecSPaulo Marques 	best = 0;
5971da177e4SLinus Torvalds 
598b3dbb4ecSPaulo Marques 	for (i = 0; i < 0x10000; i++) {
599b3dbb4ecSPaulo Marques 		if (token_profit[i] > bestprofit) {
600b3dbb4ecSPaulo Marques 			best = i;
601b3dbb4ecSPaulo Marques 			bestprofit = token_profit[i];
6021da177e4SLinus Torvalds 		}
6031da177e4SLinus Torvalds 	}
6041da177e4SLinus Torvalds 	return best;
6051da177e4SLinus Torvalds }
6061da177e4SLinus Torvalds 
6071da177e4SLinus Torvalds /* this is the core of the algorithm: calculate the "best" table */
6081da177e4SLinus Torvalds static void optimize_result(void)
6091da177e4SLinus Torvalds {
610b3dbb4ecSPaulo Marques 	int i, best;
6111da177e4SLinus Torvalds 
6121da177e4SLinus Torvalds 	/* using the '\0' symbol last allows compress_symbols to use standard
6131da177e4SLinus Torvalds 	 * fast string functions */
6141da177e4SLinus Torvalds 	for (i = 255; i >= 0; i--) {
6151da177e4SLinus Torvalds 
6161da177e4SLinus Torvalds 		/* if this table slot is empty (it is not used by an actual
6171da177e4SLinus Torvalds 		 * original char code */
6181da177e4SLinus Torvalds 		if (!best_table_len[i]) {
6191da177e4SLinus Torvalds 
620cbf7a90eSCao jin 			/* find the token with the best profit value */
6211da177e4SLinus Torvalds 			best = find_best_token();
622e0a04b11SXiaochen Wang 			if (token_profit[best] == 0)
623e0a04b11SXiaochen Wang 				break;
6241da177e4SLinus Torvalds 
6251da177e4SLinus Torvalds 			/* place it in the "best" table */
626b3dbb4ecSPaulo Marques 			best_table_len[i] = 2;
627b3dbb4ecSPaulo Marques 			best_table[i][0] = best & 0xFF;
628b3dbb4ecSPaulo Marques 			best_table[i][1] = (best >> 8) & 0xFF;
6291da177e4SLinus Torvalds 
6301da177e4SLinus Torvalds 			/* replace this token in all the valid symbols */
631b3dbb4ecSPaulo Marques 			compress_symbols(best_table[i], i);
6321da177e4SLinus Torvalds 		}
6331da177e4SLinus Torvalds 	}
6341da177e4SLinus Torvalds }
6351da177e4SLinus Torvalds 
6361da177e4SLinus Torvalds /* start by placing the symbols that are actually used on the table */
6371da177e4SLinus Torvalds static void insert_real_symbols_in_table(void)
6381da177e4SLinus Torvalds {
639b3dbb4ecSPaulo Marques 	unsigned int i, j, c;
6401da177e4SLinus Torvalds 
641b3dbb4ecSPaulo Marques 	for (i = 0; i < table_cnt; i++) {
6428d605269SMasahiro Yamada 		for (j = 0; j < table[i]->len; j++) {
6438d605269SMasahiro Yamada 			c = table[i]->sym[j];
6441da177e4SLinus Torvalds 			best_table[c][0]=c;
6451da177e4SLinus Torvalds 			best_table_len[c]=1;
6461da177e4SLinus Torvalds 		}
6471da177e4SLinus Torvalds 	}
6481da177e4SLinus Torvalds }
6491da177e4SLinus Torvalds 
6501da177e4SLinus Torvalds static void optimize_token_table(void)
6511da177e4SLinus Torvalds {
6521da177e4SLinus Torvalds 	build_initial_tok_table();
6531da177e4SLinus Torvalds 
6541da177e4SLinus Torvalds 	insert_real_symbols_in_table();
6551da177e4SLinus Torvalds 
6561da177e4SLinus Torvalds 	optimize_result();
6571da177e4SLinus Torvalds }
6581da177e4SLinus Torvalds 
659b478b782SLai Jiangshan /* guess for "linker script provide" symbol */
660b478b782SLai Jiangshan static int may_be_linker_script_provide_symbol(const struct sym_entry *se)
661b478b782SLai Jiangshan {
66229e55ad3SMasahiro Yamada 	const char *symbol = sym_name(se);
663b478b782SLai Jiangshan 	int len = se->len - 1;
664b478b782SLai Jiangshan 
665b478b782SLai Jiangshan 	if (len < 8)
666b478b782SLai Jiangshan 		return 0;
667b478b782SLai Jiangshan 
668b478b782SLai Jiangshan 	if (symbol[0] != '_' || symbol[1] != '_')
669b478b782SLai Jiangshan 		return 0;
670b478b782SLai Jiangshan 
671b478b782SLai Jiangshan 	/* __start_XXXXX */
672b478b782SLai Jiangshan 	if (!memcmp(symbol + 2, "start_", 6))
673b478b782SLai Jiangshan 		return 1;
674b478b782SLai Jiangshan 
675b478b782SLai Jiangshan 	/* __stop_XXXXX */
676b478b782SLai Jiangshan 	if (!memcmp(symbol + 2, "stop_", 5))
677b478b782SLai Jiangshan 		return 1;
678b478b782SLai Jiangshan 
679b478b782SLai Jiangshan 	/* __end_XXXXX */
680b478b782SLai Jiangshan 	if (!memcmp(symbol + 2, "end_", 4))
681b478b782SLai Jiangshan 		return 1;
682b478b782SLai Jiangshan 
683b478b782SLai Jiangshan 	/* __XXXXX_start */
684b478b782SLai Jiangshan 	if (!memcmp(symbol + len - 6, "_start", 6))
685b478b782SLai Jiangshan 		return 1;
686b478b782SLai Jiangshan 
687b478b782SLai Jiangshan 	/* __XXXXX_end */
688b478b782SLai Jiangshan 	if (!memcmp(symbol + len - 4, "_end", 4))
689b478b782SLai Jiangshan 		return 1;
690b478b782SLai Jiangshan 
691b478b782SLai Jiangshan 	return 0;
692b478b782SLai Jiangshan }
693b478b782SLai Jiangshan 
694f2df3f65SPaulo Marques static int compare_symbols(const void *a, const void *b)
695f2df3f65SPaulo Marques {
6968d605269SMasahiro Yamada 	const struct sym_entry *sa = *(const struct sym_entry **)a;
6978d605269SMasahiro Yamada 	const struct sym_entry *sb = *(const struct sym_entry **)b;
698f2df3f65SPaulo Marques 	int wa, wb;
699f2df3f65SPaulo Marques 
700f2df3f65SPaulo Marques 	/* sort by address first */
701f2df3f65SPaulo Marques 	if (sa->addr > sb->addr)
702f2df3f65SPaulo Marques 		return 1;
703f2df3f65SPaulo Marques 	if (sa->addr < sb->addr)
704f2df3f65SPaulo Marques 		return -1;
705f2df3f65SPaulo Marques 
706f2df3f65SPaulo Marques 	/* sort by "weakness" type */
707f2df3f65SPaulo Marques 	wa = (sa->sym[0] == 'w') || (sa->sym[0] == 'W');
708f2df3f65SPaulo Marques 	wb = (sb->sym[0] == 'w') || (sb->sym[0] == 'W');
709f2df3f65SPaulo Marques 	if (wa != wb)
710f2df3f65SPaulo Marques 		return wa - wb;
711f2df3f65SPaulo Marques 
712b478b782SLai Jiangshan 	/* sort by "linker script provide" type */
713b478b782SLai Jiangshan 	wa = may_be_linker_script_provide_symbol(sa);
714b478b782SLai Jiangshan 	wb = may_be_linker_script_provide_symbol(sb);
715b478b782SLai Jiangshan 	if (wa != wb)
716b478b782SLai Jiangshan 		return wa - wb;
717b478b782SLai Jiangshan 
718b478b782SLai Jiangshan 	/* sort by the number of prefix underscores */
719aa915245SMasahiro Yamada 	wa = strspn(sym_name(sa), "_");
720aa915245SMasahiro Yamada 	wb = strspn(sym_name(sb), "_");
721b478b782SLai Jiangshan 	if (wa != wb)
722b478b782SLai Jiangshan 		return wa - wb;
723b478b782SLai Jiangshan 
724f2df3f65SPaulo Marques 	/* sort by initial order, so that other symbols are left undisturbed */
725f2df3f65SPaulo Marques 	return sa->start_pos - sb->start_pos;
726f2df3f65SPaulo Marques }
727f2df3f65SPaulo Marques 
728f2df3f65SPaulo Marques static void sort_symbols(void)
729f2df3f65SPaulo Marques {
7308d605269SMasahiro Yamada 	qsort(table, table_cnt, sizeof(table[0]), compare_symbols);
731f2df3f65SPaulo Marques }
7321da177e4SLinus Torvalds 
733c6bda7c9SRusty Russell static void make_percpus_absolute(void)
734c6bda7c9SRusty Russell {
735c6bda7c9SRusty Russell 	unsigned int i;
736c6bda7c9SRusty Russell 
737c6bda7c9SRusty Russell 	for (i = 0; i < table_cnt; i++)
7388d605269SMasahiro Yamada 		if (symbol_in_range(table[i], &percpu_range, 1)) {
7398c996940SArd Biesheuvel 			/*
7408c996940SArd Biesheuvel 			 * Keep the 'A' override for percpu symbols to
7418c996940SArd Biesheuvel 			 * ensure consistent behavior compared to older
7428c996940SArd Biesheuvel 			 * versions of this tool.
7438c996940SArd Biesheuvel 			 */
7448d605269SMasahiro Yamada 			table[i]->sym[0] = 'A';
7458d605269SMasahiro Yamada 			table[i]->percpu_absolute = 1;
7468c996940SArd Biesheuvel 		}
747c6bda7c9SRusty Russell }
748c6bda7c9SRusty Russell 
7492213e9a6SArd Biesheuvel /* find the minimum non-absolute symbol address */
7502213e9a6SArd Biesheuvel static void record_relative_base(void)
7512213e9a6SArd Biesheuvel {
7522213e9a6SArd Biesheuvel 	unsigned int i;
7532213e9a6SArd Biesheuvel 
7542213e9a6SArd Biesheuvel 	for (i = 0; i < table_cnt; i++)
7558d605269SMasahiro Yamada 		if (!symbol_absolute(table[i])) {
756f34ea029SMasahiro Yamada 			/*
757f34ea029SMasahiro Yamada 			 * The table is sorted by address.
758f34ea029SMasahiro Yamada 			 * Take the first non-absolute symbol value.
759f34ea029SMasahiro Yamada 			 */
7608d605269SMasahiro Yamada 			relative_base = table[i]->addr;
761f34ea029SMasahiro Yamada 			return;
762f34ea029SMasahiro Yamada 		}
7632213e9a6SArd Biesheuvel }
7642213e9a6SArd Biesheuvel 
765b3dbb4ecSPaulo Marques int main(int argc, char **argv)
7661da177e4SLinus Torvalds {
76741f11a4fSYoshinori Sato 	if (argc >= 2) {
76841f11a4fSYoshinori Sato 		int i;
76941f11a4fSYoshinori Sato 		for (i = 1; i < argc; i++) {
77041f11a4fSYoshinori Sato 			if(strcmp(argv[i], "--all-symbols") == 0)
7711da177e4SLinus Torvalds 				all_symbols = 1;
772c6bda7c9SRusty Russell 			else if (strcmp(argv[i], "--absolute-percpu") == 0)
773c6bda7c9SRusty Russell 				absolute_percpu = 1;
774534c9f2eSMasahiro Yamada 			else if (strcmp(argv[i], "--base-relative") == 0)
7752213e9a6SArd Biesheuvel 				base_relative = 1;
7762213e9a6SArd Biesheuvel 			else
77741f11a4fSYoshinori Sato 				usage();
77841f11a4fSYoshinori Sato 		}
77941f11a4fSYoshinori Sato 	} else if (argc != 1)
7801da177e4SLinus Torvalds 		usage();
7811da177e4SLinus Torvalds 
7821da177e4SLinus Torvalds 	read_map(stdin);
7835e5c4fa7SMasahiro Yamada 	shrink_table();
784c6bda7c9SRusty Russell 	if (absolute_percpu)
785c6bda7c9SRusty Russell 		make_percpus_absolute();
786f34ea029SMasahiro Yamada 	sort_symbols();
7872213e9a6SArd Biesheuvel 	if (base_relative)
7882213e9a6SArd Biesheuvel 		record_relative_base();
7891da177e4SLinus Torvalds 	optimize_token_table();
7901da177e4SLinus Torvalds 	write_src();
7911da177e4SLinus Torvalds 
7921da177e4SLinus Torvalds 	return 0;
7931da177e4SLinus Torvalds }
794