162306a36Sopenharmony_ci/* Generate assembler source containing symbol information
262306a36Sopenharmony_ci *
362306a36Sopenharmony_ci * Copyright 2002       by Kai Germaschewski
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This software may be used and distributed according to the terms
662306a36Sopenharmony_ci * of the GNU General Public License, incorporated herein by reference.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Usage: kallsyms [--all-symbols] [--absolute-percpu]
962306a36Sopenharmony_ci *                         [--base-relative] [--lto-clang] in.map > out.S
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *      Table compression uses all the unused char codes on the symbols and
1262306a36Sopenharmony_ci *  maps these to the most used substrings (tokens). For instance, it might
1362306a36Sopenharmony_ci *  map char code 0xF7 to represent "write_" and then in every symbol where
1462306a36Sopenharmony_ci *  "write_" appears it can be replaced by 0xF7, saving 5 bytes.
1562306a36Sopenharmony_ci *      The used codes themselves are also placed in the table so that the
1662306a36Sopenharmony_ci *  decompresion can work without "special cases".
1762306a36Sopenharmony_ci *      Applied to kernel symbols, this usually produces a compression ratio
1862306a36Sopenharmony_ci *  of about 50%.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <errno.h>
2362306a36Sopenharmony_ci#include <getopt.h>
2462306a36Sopenharmony_ci#include <stdbool.h>
2562306a36Sopenharmony_ci#include <stdio.h>
2662306a36Sopenharmony_ci#include <stdlib.h>
2762306a36Sopenharmony_ci#include <string.h>
2862306a36Sopenharmony_ci#include <ctype.h>
2962306a36Sopenharmony_ci#include <limits.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define KSYM_NAME_LEN		512
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct sym_entry {
3662306a36Sopenharmony_ci	unsigned long long addr;
3762306a36Sopenharmony_ci	unsigned int len;
3862306a36Sopenharmony_ci	unsigned int seq;
3962306a36Sopenharmony_ci	unsigned int start_pos;
4062306a36Sopenharmony_ci	unsigned int percpu_absolute;
4162306a36Sopenharmony_ci	unsigned char sym[];
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct addr_range {
4562306a36Sopenharmony_ci	const char *start_sym, *end_sym;
4662306a36Sopenharmony_ci	unsigned long long start, end;
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic unsigned long long _text;
5062306a36Sopenharmony_cistatic unsigned long long relative_base;
5162306a36Sopenharmony_cistatic struct addr_range text_ranges[] = {
5262306a36Sopenharmony_ci	{ "_stext",     "_etext"     },
5362306a36Sopenharmony_ci	{ "_sinittext", "_einittext" },
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci#define text_range_text     (&text_ranges[0])
5662306a36Sopenharmony_ci#define text_range_inittext (&text_ranges[1])
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic struct addr_range percpu_range = {
5962306a36Sopenharmony_ci	"__per_cpu_start", "__per_cpu_end", -1ULL, 0
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic struct sym_entry **table;
6362306a36Sopenharmony_cistatic unsigned int table_size, table_cnt;
6462306a36Sopenharmony_cistatic int all_symbols;
6562306a36Sopenharmony_cistatic int absolute_percpu;
6662306a36Sopenharmony_cistatic int base_relative;
6762306a36Sopenharmony_cistatic int lto_clang;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int token_profit[0x10000];
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* the table that holds the result of the compression */
7262306a36Sopenharmony_cistatic unsigned char best_table[256][2];
7362306a36Sopenharmony_cistatic unsigned char best_table_len[256];
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic void usage(void)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	fprintf(stderr, "Usage: kallsyms [--all-symbols] [--absolute-percpu] "
7962306a36Sopenharmony_ci			"[--base-relative] [--lto-clang] in.map > out.S\n");
8062306a36Sopenharmony_ci	exit(1);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic char *sym_name(const struct sym_entry *s)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	return (char *)s->sym + 1;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic bool is_ignored_symbol(const char *name, char type)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	if (type == 'u' || type == 'n')
9162306a36Sopenharmony_ci		return true;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (toupper(type) == 'A') {
9462306a36Sopenharmony_ci		/* Keep these useful absolute symbols */
9562306a36Sopenharmony_ci		if (strcmp(name, "__kernel_syscall_via_break") &&
9662306a36Sopenharmony_ci		    strcmp(name, "__kernel_syscall_via_epc") &&
9762306a36Sopenharmony_ci		    strcmp(name, "__kernel_sigtramp") &&
9862306a36Sopenharmony_ci		    strcmp(name, "__gp"))
9962306a36Sopenharmony_ci			return true;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return false;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic void check_symbol_range(const char *sym, unsigned long long addr,
10662306a36Sopenharmony_ci			       struct addr_range *ranges, int entries)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	size_t i;
10962306a36Sopenharmony_ci	struct addr_range *ar;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	for (i = 0; i < entries; ++i) {
11262306a36Sopenharmony_ci		ar = &ranges[i];
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		if (strcmp(sym, ar->start_sym) == 0) {
11562306a36Sopenharmony_ci			ar->start = addr;
11662306a36Sopenharmony_ci			return;
11762306a36Sopenharmony_ci		} else if (strcmp(sym, ar->end_sym) == 0) {
11862306a36Sopenharmony_ci			ar->end = addr;
11962306a36Sopenharmony_ci			return;
12062306a36Sopenharmony_ci		}
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic struct sym_entry *read_symbol(FILE *in, char **buf, size_t *buf_len)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	char *name, type, *p;
12762306a36Sopenharmony_ci	unsigned long long addr;
12862306a36Sopenharmony_ci	size_t len;
12962306a36Sopenharmony_ci	ssize_t readlen;
13062306a36Sopenharmony_ci	struct sym_entry *sym;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	errno = 0;
13362306a36Sopenharmony_ci	readlen = getline(buf, buf_len, in);
13462306a36Sopenharmony_ci	if (readlen < 0) {
13562306a36Sopenharmony_ci		if (errno) {
13662306a36Sopenharmony_ci			perror("read_symbol");
13762306a36Sopenharmony_ci			exit(EXIT_FAILURE);
13862306a36Sopenharmony_ci		}
13962306a36Sopenharmony_ci		return NULL;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if ((*buf)[readlen - 1] == '\n')
14362306a36Sopenharmony_ci		(*buf)[readlen - 1] = 0;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	addr = strtoull(*buf, &p, 16);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (*buf == p || *p++ != ' ' || !isascii((type = *p++)) || *p++ != ' ') {
14862306a36Sopenharmony_ci		fprintf(stderr, "line format error\n");
14962306a36Sopenharmony_ci		exit(EXIT_FAILURE);
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	name = p;
15362306a36Sopenharmony_ci	len = strlen(name);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if (len >= KSYM_NAME_LEN) {
15662306a36Sopenharmony_ci		fprintf(stderr, "Symbol %s too long for kallsyms (%zu >= %d).\n"
15762306a36Sopenharmony_ci				"Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n",
15862306a36Sopenharmony_ci			name, len, KSYM_NAME_LEN);
15962306a36Sopenharmony_ci		return NULL;
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (strcmp(name, "_text") == 0)
16362306a36Sopenharmony_ci		_text = addr;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/* Ignore most absolute/undefined (?) symbols. */
16662306a36Sopenharmony_ci	if (is_ignored_symbol(name, type))
16762306a36Sopenharmony_ci		return NULL;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	check_symbol_range(name, addr, text_ranges, ARRAY_SIZE(text_ranges));
17062306a36Sopenharmony_ci	check_symbol_range(name, addr, &percpu_range, 1);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* include the type field in the symbol name, so that it gets
17362306a36Sopenharmony_ci	 * compressed together */
17462306a36Sopenharmony_ci	len++;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	sym = malloc(sizeof(*sym) + len + 1);
17762306a36Sopenharmony_ci	if (!sym) {
17862306a36Sopenharmony_ci		fprintf(stderr, "kallsyms failure: "
17962306a36Sopenharmony_ci			"unable to allocate required amount of memory\n");
18062306a36Sopenharmony_ci		exit(EXIT_FAILURE);
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci	sym->addr = addr;
18362306a36Sopenharmony_ci	sym->len = len;
18462306a36Sopenharmony_ci	sym->sym[0] = type;
18562306a36Sopenharmony_ci	strcpy(sym_name(sym), name);
18662306a36Sopenharmony_ci	sym->percpu_absolute = 0;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	return sym;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic int symbol_in_range(const struct sym_entry *s,
19262306a36Sopenharmony_ci			   const struct addr_range *ranges, int entries)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	size_t i;
19562306a36Sopenharmony_ci	const struct addr_range *ar;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	for (i = 0; i < entries; ++i) {
19862306a36Sopenharmony_ci		ar = &ranges[i];
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci		if (s->addr >= ar->start && s->addr <= ar->end)
20162306a36Sopenharmony_ci			return 1;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return 0;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic int symbol_valid(const struct sym_entry *s)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	const char *name = sym_name(s);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* if --all-symbols is not specified, then symbols outside the text
21262306a36Sopenharmony_ci	 * and inittext sections are discarded */
21362306a36Sopenharmony_ci	if (!all_symbols) {
21462306a36Sopenharmony_ci		if (symbol_in_range(s, text_ranges,
21562306a36Sopenharmony_ci				    ARRAY_SIZE(text_ranges)) == 0)
21662306a36Sopenharmony_ci			return 0;
21762306a36Sopenharmony_ci		/* Corner case.  Discard any symbols with the same value as
21862306a36Sopenharmony_ci		 * _etext _einittext; they can move between pass 1 and 2 when
21962306a36Sopenharmony_ci		 * the kallsyms data are added.  If these symbols move then
22062306a36Sopenharmony_ci		 * they may get dropped in pass 2, which breaks the kallsyms
22162306a36Sopenharmony_ci		 * rules.
22262306a36Sopenharmony_ci		 */
22362306a36Sopenharmony_ci		if ((s->addr == text_range_text->end &&
22462306a36Sopenharmony_ci		     strcmp(name, text_range_text->end_sym)) ||
22562306a36Sopenharmony_ci		    (s->addr == text_range_inittext->end &&
22662306a36Sopenharmony_ci		     strcmp(name, text_range_inittext->end_sym)))
22762306a36Sopenharmony_ci			return 0;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	return 1;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci/* remove all the invalid symbols from the table */
23462306a36Sopenharmony_cistatic void shrink_table(void)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	unsigned int i, pos;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	pos = 0;
23962306a36Sopenharmony_ci	for (i = 0; i < table_cnt; i++) {
24062306a36Sopenharmony_ci		if (symbol_valid(table[i])) {
24162306a36Sopenharmony_ci			if (pos != i)
24262306a36Sopenharmony_ci				table[pos] = table[i];
24362306a36Sopenharmony_ci			pos++;
24462306a36Sopenharmony_ci		} else {
24562306a36Sopenharmony_ci			free(table[i]);
24662306a36Sopenharmony_ci		}
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci	table_cnt = pos;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/* When valid symbol is not registered, exit to error */
25162306a36Sopenharmony_ci	if (!table_cnt) {
25262306a36Sopenharmony_ci		fprintf(stderr, "No valid symbol.\n");
25362306a36Sopenharmony_ci		exit(1);
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic void read_map(const char *in)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	FILE *fp;
26062306a36Sopenharmony_ci	struct sym_entry *sym;
26162306a36Sopenharmony_ci	char *buf = NULL;
26262306a36Sopenharmony_ci	size_t buflen = 0;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	fp = fopen(in, "r");
26562306a36Sopenharmony_ci	if (!fp) {
26662306a36Sopenharmony_ci		perror(in);
26762306a36Sopenharmony_ci		exit(1);
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	while (!feof(fp)) {
27162306a36Sopenharmony_ci		sym = read_symbol(fp, &buf, &buflen);
27262306a36Sopenharmony_ci		if (!sym)
27362306a36Sopenharmony_ci			continue;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci		sym->start_pos = table_cnt;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci		if (table_cnt >= table_size) {
27862306a36Sopenharmony_ci			table_size += 10000;
27962306a36Sopenharmony_ci			table = realloc(table, sizeof(*table) * table_size);
28062306a36Sopenharmony_ci			if (!table) {
28162306a36Sopenharmony_ci				fprintf(stderr, "out of memory\n");
28262306a36Sopenharmony_ci				fclose(fp);
28362306a36Sopenharmony_ci				exit (1);
28462306a36Sopenharmony_ci			}
28562306a36Sopenharmony_ci		}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci		table[table_cnt++] = sym;
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	free(buf);
29162306a36Sopenharmony_ci	fclose(fp);
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic void output_label(const char *label)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	printf(".globl %s\n", label);
29762306a36Sopenharmony_ci	printf("\tALGN\n");
29862306a36Sopenharmony_ci	printf("%s:\n", label);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci/* Provide proper symbols relocatability by their '_text' relativeness. */
30262306a36Sopenharmony_cistatic void output_address(unsigned long long addr)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	if (_text <= addr)
30562306a36Sopenharmony_ci		printf("\tPTR\t_text + %#llx\n", addr - _text);
30662306a36Sopenharmony_ci	else
30762306a36Sopenharmony_ci		printf("\tPTR\t_text - %#llx\n", _text - addr);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci/* uncompress a compressed symbol. When this function is called, the best table
31162306a36Sopenharmony_ci * might still be compressed itself, so the function needs to be recursive */
31262306a36Sopenharmony_cistatic int expand_symbol(const unsigned char *data, int len, char *result)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	int c, rlen, total=0;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	while (len) {
31762306a36Sopenharmony_ci		c = *data;
31862306a36Sopenharmony_ci		/* if the table holds a single char that is the same as the one
31962306a36Sopenharmony_ci		 * we are looking for, then end the search */
32062306a36Sopenharmony_ci		if (best_table[c][0]==c && best_table_len[c]==1) {
32162306a36Sopenharmony_ci			*result++ = c;
32262306a36Sopenharmony_ci			total++;
32362306a36Sopenharmony_ci		} else {
32462306a36Sopenharmony_ci			/* if not, recurse and expand */
32562306a36Sopenharmony_ci			rlen = expand_symbol(best_table[c], best_table_len[c], result);
32662306a36Sopenharmony_ci			total += rlen;
32762306a36Sopenharmony_ci			result += rlen;
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci		data++;
33062306a36Sopenharmony_ci		len--;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci	*result=0;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return total;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic int symbol_absolute(const struct sym_entry *s)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	return s->percpu_absolute;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic void cleanup_symbol_name(char *s)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	char *p;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	/*
34762306a36Sopenharmony_ci	 * ASCII[.]   = 2e
34862306a36Sopenharmony_ci	 * ASCII[0-9] = 30,39
34962306a36Sopenharmony_ci	 * ASCII[A-Z] = 41,5a
35062306a36Sopenharmony_ci	 * ASCII[_]   = 5f
35162306a36Sopenharmony_ci	 * ASCII[a-z] = 61,7a
35262306a36Sopenharmony_ci	 *
35362306a36Sopenharmony_ci	 * As above, replacing the first '.' in ".llvm." with '\0' does not
35462306a36Sopenharmony_ci	 * affect the main sorting, but it helps us with subsorting.
35562306a36Sopenharmony_ci	 */
35662306a36Sopenharmony_ci	p = strstr(s, ".llvm.");
35762306a36Sopenharmony_ci	if (p)
35862306a36Sopenharmony_ci		*p = '\0';
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic int compare_names(const void *a, const void *b)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	int ret;
36462306a36Sopenharmony_ci	const struct sym_entry *sa = *(const struct sym_entry **)a;
36562306a36Sopenharmony_ci	const struct sym_entry *sb = *(const struct sym_entry **)b;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	ret = strcmp(sym_name(sa), sym_name(sb));
36862306a36Sopenharmony_ci	if (!ret) {
36962306a36Sopenharmony_ci		if (sa->addr > sb->addr)
37062306a36Sopenharmony_ci			return 1;
37162306a36Sopenharmony_ci		else if (sa->addr < sb->addr)
37262306a36Sopenharmony_ci			return -1;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci		/* keep old order */
37562306a36Sopenharmony_ci		return (int)(sa->seq - sb->seq);
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	return ret;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic void sort_symbols_by_name(void)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	qsort(table, table_cnt, sizeof(table[0]), compare_names);
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic void write_src(void)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	unsigned int i, k, off;
38962306a36Sopenharmony_ci	unsigned int best_idx[256];
39062306a36Sopenharmony_ci	unsigned int *markers;
39162306a36Sopenharmony_ci	char buf[KSYM_NAME_LEN];
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	printf("#include <asm/bitsperlong.h>\n");
39462306a36Sopenharmony_ci	printf("#if BITS_PER_LONG == 64\n");
39562306a36Sopenharmony_ci	printf("#define PTR .quad\n");
39662306a36Sopenharmony_ci	printf("#define ALGN .balign 8\n");
39762306a36Sopenharmony_ci	printf("#else\n");
39862306a36Sopenharmony_ci	printf("#define PTR .long\n");
39962306a36Sopenharmony_ci	printf("#define ALGN .balign 4\n");
40062306a36Sopenharmony_ci	printf("#endif\n");
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	printf("\t.section .rodata, \"a\"\n");
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	output_label("kallsyms_num_syms");
40562306a36Sopenharmony_ci	printf("\t.long\t%u\n", table_cnt);
40662306a36Sopenharmony_ci	printf("\n");
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	/* table of offset markers, that give the offset in the compressed stream
40962306a36Sopenharmony_ci	 * every 256 symbols */
41062306a36Sopenharmony_ci	markers = malloc(sizeof(unsigned int) * ((table_cnt + 255) / 256));
41162306a36Sopenharmony_ci	if (!markers) {
41262306a36Sopenharmony_ci		fprintf(stderr, "kallsyms failure: "
41362306a36Sopenharmony_ci			"unable to allocate required memory\n");
41462306a36Sopenharmony_ci		exit(EXIT_FAILURE);
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	output_label("kallsyms_names");
41862306a36Sopenharmony_ci	off = 0;
41962306a36Sopenharmony_ci	for (i = 0; i < table_cnt; i++) {
42062306a36Sopenharmony_ci		if ((i & 0xFF) == 0)
42162306a36Sopenharmony_ci			markers[i >> 8] = off;
42262306a36Sopenharmony_ci		table[i]->seq = i;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci		/* There cannot be any symbol of length zero. */
42562306a36Sopenharmony_ci		if (table[i]->len == 0) {
42662306a36Sopenharmony_ci			fprintf(stderr, "kallsyms failure: "
42762306a36Sopenharmony_ci				"unexpected zero symbol length\n");
42862306a36Sopenharmony_ci			exit(EXIT_FAILURE);
42962306a36Sopenharmony_ci		}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		/* Only lengths that fit in up-to-two-byte ULEB128 are supported. */
43262306a36Sopenharmony_ci		if (table[i]->len > 0x3FFF) {
43362306a36Sopenharmony_ci			fprintf(stderr, "kallsyms failure: "
43462306a36Sopenharmony_ci				"unexpected huge symbol length\n");
43562306a36Sopenharmony_ci			exit(EXIT_FAILURE);
43662306a36Sopenharmony_ci		}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		/* Encode length with ULEB128. */
43962306a36Sopenharmony_ci		if (table[i]->len <= 0x7F) {
44062306a36Sopenharmony_ci			/* Most symbols use a single byte for the length. */
44162306a36Sopenharmony_ci			printf("\t.byte 0x%02x", table[i]->len);
44262306a36Sopenharmony_ci			off += table[i]->len + 1;
44362306a36Sopenharmony_ci		} else {
44462306a36Sopenharmony_ci			/* "Big" symbols use two bytes. */
44562306a36Sopenharmony_ci			printf("\t.byte 0x%02x, 0x%02x",
44662306a36Sopenharmony_ci				(table[i]->len & 0x7F) | 0x80,
44762306a36Sopenharmony_ci				(table[i]->len >> 7) & 0x7F);
44862306a36Sopenharmony_ci			off += table[i]->len + 2;
44962306a36Sopenharmony_ci		}
45062306a36Sopenharmony_ci		for (k = 0; k < table[i]->len; k++)
45162306a36Sopenharmony_ci			printf(", 0x%02x", table[i]->sym[k]);
45262306a36Sopenharmony_ci		printf("\n");
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci	printf("\n");
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	/*
45762306a36Sopenharmony_ci	 * Now that we wrote out the compressed symbol names, restore the
45862306a36Sopenharmony_ci	 * original names, which are needed in some of the later steps.
45962306a36Sopenharmony_ci	 */
46062306a36Sopenharmony_ci	for (i = 0; i < table_cnt; i++) {
46162306a36Sopenharmony_ci		expand_symbol(table[i]->sym, table[i]->len, buf);
46262306a36Sopenharmony_ci		strcpy((char *)table[i]->sym, buf);
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	output_label("kallsyms_markers");
46662306a36Sopenharmony_ci	for (i = 0; i < ((table_cnt + 255) >> 8); i++)
46762306a36Sopenharmony_ci		printf("\t.long\t%u\n", markers[i]);
46862306a36Sopenharmony_ci	printf("\n");
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	free(markers);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	output_label("kallsyms_token_table");
47362306a36Sopenharmony_ci	off = 0;
47462306a36Sopenharmony_ci	for (i = 0; i < 256; i++) {
47562306a36Sopenharmony_ci		best_idx[i] = off;
47662306a36Sopenharmony_ci		expand_symbol(best_table[i], best_table_len[i], buf);
47762306a36Sopenharmony_ci		printf("\t.asciz\t\"%s\"\n", buf);
47862306a36Sopenharmony_ci		off += strlen(buf) + 1;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci	printf("\n");
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	output_label("kallsyms_token_index");
48362306a36Sopenharmony_ci	for (i = 0; i < 256; i++)
48462306a36Sopenharmony_ci		printf("\t.short\t%d\n", best_idx[i]);
48562306a36Sopenharmony_ci	printf("\n");
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if (!base_relative)
48862306a36Sopenharmony_ci		output_label("kallsyms_addresses");
48962306a36Sopenharmony_ci	else
49062306a36Sopenharmony_ci		output_label("kallsyms_offsets");
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	for (i = 0; i < table_cnt; i++) {
49362306a36Sopenharmony_ci		if (base_relative) {
49462306a36Sopenharmony_ci			/*
49562306a36Sopenharmony_ci			 * Use the offset relative to the lowest value
49662306a36Sopenharmony_ci			 * encountered of all relative symbols, and emit
49762306a36Sopenharmony_ci			 * non-relocatable fixed offsets that will be fixed
49862306a36Sopenharmony_ci			 * up at runtime.
49962306a36Sopenharmony_ci			 */
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci			long long offset;
50262306a36Sopenharmony_ci			int overflow;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci			if (!absolute_percpu) {
50562306a36Sopenharmony_ci				offset = table[i]->addr - relative_base;
50662306a36Sopenharmony_ci				overflow = (offset < 0 || offset > UINT_MAX);
50762306a36Sopenharmony_ci			} else if (symbol_absolute(table[i])) {
50862306a36Sopenharmony_ci				offset = table[i]->addr;
50962306a36Sopenharmony_ci				overflow = (offset < 0 || offset > INT_MAX);
51062306a36Sopenharmony_ci			} else {
51162306a36Sopenharmony_ci				offset = relative_base - table[i]->addr - 1;
51262306a36Sopenharmony_ci				overflow = (offset < INT_MIN || offset >= 0);
51362306a36Sopenharmony_ci			}
51462306a36Sopenharmony_ci			if (overflow) {
51562306a36Sopenharmony_ci				fprintf(stderr, "kallsyms failure: "
51662306a36Sopenharmony_ci					"%s symbol value %#llx out of range in relative mode\n",
51762306a36Sopenharmony_ci					symbol_absolute(table[i]) ? "absolute" : "relative",
51862306a36Sopenharmony_ci					table[i]->addr);
51962306a36Sopenharmony_ci				exit(EXIT_FAILURE);
52062306a36Sopenharmony_ci			}
52162306a36Sopenharmony_ci			printf("\t.long\t%#x	/* %s */\n", (int)offset, table[i]->sym);
52262306a36Sopenharmony_ci		} else if (!symbol_absolute(table[i])) {
52362306a36Sopenharmony_ci			output_address(table[i]->addr);
52462306a36Sopenharmony_ci		} else {
52562306a36Sopenharmony_ci			printf("\tPTR\t%#llx\n", table[i]->addr);
52662306a36Sopenharmony_ci		}
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci	printf("\n");
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	if (base_relative) {
53162306a36Sopenharmony_ci		output_label("kallsyms_relative_base");
53262306a36Sopenharmony_ci		output_address(relative_base);
53362306a36Sopenharmony_ci		printf("\n");
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	if (lto_clang)
53762306a36Sopenharmony_ci		for (i = 0; i < table_cnt; i++)
53862306a36Sopenharmony_ci			cleanup_symbol_name((char *)table[i]->sym);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	sort_symbols_by_name();
54162306a36Sopenharmony_ci	output_label("kallsyms_seqs_of_names");
54262306a36Sopenharmony_ci	for (i = 0; i < table_cnt; i++)
54362306a36Sopenharmony_ci		printf("\t.byte 0x%02x, 0x%02x, 0x%02x\n",
54462306a36Sopenharmony_ci			(unsigned char)(table[i]->seq >> 16),
54562306a36Sopenharmony_ci			(unsigned char)(table[i]->seq >> 8),
54662306a36Sopenharmony_ci			(unsigned char)(table[i]->seq >> 0));
54762306a36Sopenharmony_ci	printf("\n");
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci/* table lookup compression functions */
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci/* count all the possible tokens in a symbol */
55462306a36Sopenharmony_cistatic void learn_symbol(const unsigned char *symbol, int len)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	int i;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	for (i = 0; i < len - 1; i++)
55962306a36Sopenharmony_ci		token_profit[ symbol[i] + (symbol[i + 1] << 8) ]++;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci/* decrease the count for all the possible tokens in a symbol */
56362306a36Sopenharmony_cistatic void forget_symbol(const unsigned char *symbol, int len)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	int i;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	for (i = 0; i < len - 1; i++)
56862306a36Sopenharmony_ci		token_profit[ symbol[i] + (symbol[i + 1] << 8) ]--;
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci/* do the initial token count */
57262306a36Sopenharmony_cistatic void build_initial_token_table(void)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	unsigned int i;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	for (i = 0; i < table_cnt; i++)
57762306a36Sopenharmony_ci		learn_symbol(table[i]->sym, table[i]->len);
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic unsigned char *find_token(unsigned char *str, int len,
58162306a36Sopenharmony_ci				 const unsigned char *token)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	int i;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	for (i = 0; i < len - 1; i++) {
58662306a36Sopenharmony_ci		if (str[i] == token[0] && str[i+1] == token[1])
58762306a36Sopenharmony_ci			return &str[i];
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci	return NULL;
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci/* replace a given token in all the valid symbols. Use the sampled symbols
59362306a36Sopenharmony_ci * to update the counts */
59462306a36Sopenharmony_cistatic void compress_symbols(const unsigned char *str, int idx)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	unsigned int i, len, size;
59762306a36Sopenharmony_ci	unsigned char *p1, *p2;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	for (i = 0; i < table_cnt; i++) {
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci		len = table[i]->len;
60262306a36Sopenharmony_ci		p1 = table[i]->sym;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		/* find the token on the symbol */
60562306a36Sopenharmony_ci		p2 = find_token(p1, len, str);
60662306a36Sopenharmony_ci		if (!p2) continue;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci		/* decrease the counts for this symbol's tokens */
60962306a36Sopenharmony_ci		forget_symbol(table[i]->sym, len);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		size = len;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci		do {
61462306a36Sopenharmony_ci			*p2 = idx;
61562306a36Sopenharmony_ci			p2++;
61662306a36Sopenharmony_ci			size -= (p2 - p1);
61762306a36Sopenharmony_ci			memmove(p2, p2 + 1, size);
61862306a36Sopenharmony_ci			p1 = p2;
61962306a36Sopenharmony_ci			len--;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci			if (size < 2) break;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci			/* find the token on the symbol */
62462306a36Sopenharmony_ci			p2 = find_token(p1, size, str);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci		} while (p2);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci		table[i]->len = len;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci		/* increase the counts for this symbol's new tokens */
63162306a36Sopenharmony_ci		learn_symbol(table[i]->sym, len);
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci/* search the token with the maximum profit */
63662306a36Sopenharmony_cistatic int find_best_token(void)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	int i, best, bestprofit;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	bestprofit=-10000;
64162306a36Sopenharmony_ci	best = 0;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	for (i = 0; i < 0x10000; i++) {
64462306a36Sopenharmony_ci		if (token_profit[i] > bestprofit) {
64562306a36Sopenharmony_ci			best = i;
64662306a36Sopenharmony_ci			bestprofit = token_profit[i];
64762306a36Sopenharmony_ci		}
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci	return best;
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci/* this is the core of the algorithm: calculate the "best" table */
65362306a36Sopenharmony_cistatic void optimize_result(void)
65462306a36Sopenharmony_ci{
65562306a36Sopenharmony_ci	int i, best;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	/* using the '\0' symbol last allows compress_symbols to use standard
65862306a36Sopenharmony_ci	 * fast string functions */
65962306a36Sopenharmony_ci	for (i = 255; i >= 0; i--) {
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci		/* if this table slot is empty (it is not used by an actual
66262306a36Sopenharmony_ci		 * original char code */
66362306a36Sopenharmony_ci		if (!best_table_len[i]) {
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci			/* find the token with the best profit value */
66662306a36Sopenharmony_ci			best = find_best_token();
66762306a36Sopenharmony_ci			if (token_profit[best] == 0)
66862306a36Sopenharmony_ci				break;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci			/* place it in the "best" table */
67162306a36Sopenharmony_ci			best_table_len[i] = 2;
67262306a36Sopenharmony_ci			best_table[i][0] = best & 0xFF;
67362306a36Sopenharmony_ci			best_table[i][1] = (best >> 8) & 0xFF;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci			/* replace this token in all the valid symbols */
67662306a36Sopenharmony_ci			compress_symbols(best_table[i], i);
67762306a36Sopenharmony_ci		}
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci/* start by placing the symbols that are actually used on the table */
68262306a36Sopenharmony_cistatic void insert_real_symbols_in_table(void)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci	unsigned int i, j, c;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	for (i = 0; i < table_cnt; i++) {
68762306a36Sopenharmony_ci		for (j = 0; j < table[i]->len; j++) {
68862306a36Sopenharmony_ci			c = table[i]->sym[j];
68962306a36Sopenharmony_ci			best_table[c][0]=c;
69062306a36Sopenharmony_ci			best_table_len[c]=1;
69162306a36Sopenharmony_ci		}
69262306a36Sopenharmony_ci	}
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_cistatic void optimize_token_table(void)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	build_initial_token_table();
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	insert_real_symbols_in_table();
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	optimize_result();
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci/* guess for "linker script provide" symbol */
70562306a36Sopenharmony_cistatic int may_be_linker_script_provide_symbol(const struct sym_entry *se)
70662306a36Sopenharmony_ci{
70762306a36Sopenharmony_ci	const char *symbol = sym_name(se);
70862306a36Sopenharmony_ci	int len = se->len - 1;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	if (len < 8)
71162306a36Sopenharmony_ci		return 0;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	if (symbol[0] != '_' || symbol[1] != '_')
71462306a36Sopenharmony_ci		return 0;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	/* __start_XXXXX */
71762306a36Sopenharmony_ci	if (!memcmp(symbol + 2, "start_", 6))
71862306a36Sopenharmony_ci		return 1;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	/* __stop_XXXXX */
72162306a36Sopenharmony_ci	if (!memcmp(symbol + 2, "stop_", 5))
72262306a36Sopenharmony_ci		return 1;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	/* __end_XXXXX */
72562306a36Sopenharmony_ci	if (!memcmp(symbol + 2, "end_", 4))
72662306a36Sopenharmony_ci		return 1;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	/* __XXXXX_start */
72962306a36Sopenharmony_ci	if (!memcmp(symbol + len - 6, "_start", 6))
73062306a36Sopenharmony_ci		return 1;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	/* __XXXXX_end */
73362306a36Sopenharmony_ci	if (!memcmp(symbol + len - 4, "_end", 4))
73462306a36Sopenharmony_ci		return 1;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	return 0;
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_cistatic int compare_symbols(const void *a, const void *b)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	const struct sym_entry *sa = *(const struct sym_entry **)a;
74262306a36Sopenharmony_ci	const struct sym_entry *sb = *(const struct sym_entry **)b;
74362306a36Sopenharmony_ci	int wa, wb;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	/* sort by address first */
74662306a36Sopenharmony_ci	if (sa->addr > sb->addr)
74762306a36Sopenharmony_ci		return 1;
74862306a36Sopenharmony_ci	if (sa->addr < sb->addr)
74962306a36Sopenharmony_ci		return -1;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	/* sort by "weakness" type */
75262306a36Sopenharmony_ci	wa = (sa->sym[0] == 'w') || (sa->sym[0] == 'W');
75362306a36Sopenharmony_ci	wb = (sb->sym[0] == 'w') || (sb->sym[0] == 'W');
75462306a36Sopenharmony_ci	if (wa != wb)
75562306a36Sopenharmony_ci		return wa - wb;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	/* sort by "linker script provide" type */
75862306a36Sopenharmony_ci	wa = may_be_linker_script_provide_symbol(sa);
75962306a36Sopenharmony_ci	wb = may_be_linker_script_provide_symbol(sb);
76062306a36Sopenharmony_ci	if (wa != wb)
76162306a36Sopenharmony_ci		return wa - wb;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	/* sort by the number of prefix underscores */
76462306a36Sopenharmony_ci	wa = strspn(sym_name(sa), "_");
76562306a36Sopenharmony_ci	wb = strspn(sym_name(sb), "_");
76662306a36Sopenharmony_ci	if (wa != wb)
76762306a36Sopenharmony_ci		return wa - wb;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	/* sort by initial order, so that other symbols are left undisturbed */
77062306a36Sopenharmony_ci	return sa->start_pos - sb->start_pos;
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic void sort_symbols(void)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	qsort(table, table_cnt, sizeof(table[0]), compare_symbols);
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_cistatic void make_percpus_absolute(void)
77962306a36Sopenharmony_ci{
78062306a36Sopenharmony_ci	unsigned int i;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	for (i = 0; i < table_cnt; i++)
78362306a36Sopenharmony_ci		if (symbol_in_range(table[i], &percpu_range, 1)) {
78462306a36Sopenharmony_ci			/*
78562306a36Sopenharmony_ci			 * Keep the 'A' override for percpu symbols to
78662306a36Sopenharmony_ci			 * ensure consistent behavior compared to older
78762306a36Sopenharmony_ci			 * versions of this tool.
78862306a36Sopenharmony_ci			 */
78962306a36Sopenharmony_ci			table[i]->sym[0] = 'A';
79062306a36Sopenharmony_ci			table[i]->percpu_absolute = 1;
79162306a36Sopenharmony_ci		}
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci/* find the minimum non-absolute symbol address */
79562306a36Sopenharmony_cistatic void record_relative_base(void)
79662306a36Sopenharmony_ci{
79762306a36Sopenharmony_ci	unsigned int i;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	for (i = 0; i < table_cnt; i++)
80062306a36Sopenharmony_ci		if (!symbol_absolute(table[i])) {
80162306a36Sopenharmony_ci			/*
80262306a36Sopenharmony_ci			 * The table is sorted by address.
80362306a36Sopenharmony_ci			 * Take the first non-absolute symbol value.
80462306a36Sopenharmony_ci			 */
80562306a36Sopenharmony_ci			relative_base = table[i]->addr;
80662306a36Sopenharmony_ci			return;
80762306a36Sopenharmony_ci		}
80862306a36Sopenharmony_ci}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ciint main(int argc, char **argv)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	while (1) {
81362306a36Sopenharmony_ci		static const struct option long_options[] = {
81462306a36Sopenharmony_ci			{"all-symbols",     no_argument, &all_symbols,     1},
81562306a36Sopenharmony_ci			{"absolute-percpu", no_argument, &absolute_percpu, 1},
81662306a36Sopenharmony_ci			{"base-relative",   no_argument, &base_relative,   1},
81762306a36Sopenharmony_ci			{"lto-clang",       no_argument, &lto_clang,       1},
81862306a36Sopenharmony_ci			{},
81962306a36Sopenharmony_ci		};
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci		int c = getopt_long(argc, argv, "", long_options, NULL);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci		if (c == -1)
82462306a36Sopenharmony_ci			break;
82562306a36Sopenharmony_ci		if (c != 0)
82662306a36Sopenharmony_ci			usage();
82762306a36Sopenharmony_ci	}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	if (optind >= argc)
83062306a36Sopenharmony_ci		usage();
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	read_map(argv[optind]);
83362306a36Sopenharmony_ci	shrink_table();
83462306a36Sopenharmony_ci	if (absolute_percpu)
83562306a36Sopenharmony_ci		make_percpus_absolute();
83662306a36Sopenharmony_ci	sort_symbols();
83762306a36Sopenharmony_ci	if (base_relative)
83862306a36Sopenharmony_ci		record_relative_base();
83962306a36Sopenharmony_ci	optimize_token_table();
84062306a36Sopenharmony_ci	write_src();
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	return 0;
84362306a36Sopenharmony_ci}
844