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 * 8adc40221SYuma Ueda * Usage: kallsyms [--all-symbols] [--absolute-percpu] 979549da6SMasahiro Yamada * [--base-relative] [--lto-clang] in.map > out.S 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * Table compression uses all the unused char codes on the symbols and 121da177e4SLinus Torvalds * maps these to the most used substrings (tokens). For instance, it might 131da177e4SLinus Torvalds * map char code 0xF7 to represent "write_" and then in every symbol where 141da177e4SLinus Torvalds * "write_" appears it can be replaced by 0xF7, saving 5 bytes. 151da177e4SLinus Torvalds * The used codes themselves are also placed in the table so that the 161da177e4SLinus Torvalds * decompresion can work without "special cases". 171da177e4SLinus Torvalds * Applied to kernel symbols, this usually produces a compression ratio 181da177e4SLinus Torvalds * of about 50%. 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds */ 211da177e4SLinus Torvalds 22aa221f2eSMasahiro Yamada #include <getopt.h> 23a41333e0SMasahiro Yamada #include <stdbool.h> 241da177e4SLinus Torvalds #include <stdio.h> 251da177e4SLinus Torvalds #include <stdlib.h> 261da177e4SLinus Torvalds #include <string.h> 271da177e4SLinus Torvalds #include <ctype.h> 282213e9a6SArd Biesheuvel #include <limits.h> 291da177e4SLinus Torvalds 3017b1f0deSMike Frysinger #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) 3117b1f0deSMike Frysinger 32b471927eSBoqun Feng #define _stringify_1(x) #x 33b471927eSBoqun Feng #define _stringify(x) _stringify_1(x) 34b471927eSBoqun Feng 35b8a94bfbSMiguel Ojeda #define KSYM_NAME_LEN 512 361da177e4SLinus Torvalds 376e8c5bbdSMiguel Ojeda /* 386e8c5bbdSMiguel Ojeda * A substantially bigger size than the current maximum. 396e8c5bbdSMiguel Ojeda * 406e8c5bbdSMiguel Ojeda * It cannot be defined as an expression because it gets stringified 416e8c5bbdSMiguel Ojeda * for the fscanf() format string. Therefore, a _Static_assert() is 426e8c5bbdSMiguel Ojeda * used instead to maintain the relationship with KSYM_NAME_LEN. 436e8c5bbdSMiguel Ojeda */ 44b8a94bfbSMiguel Ojeda #define KSYM_NAME_LEN_BUFFER 2048 456e8c5bbdSMiguel Ojeda _Static_assert( 466e8c5bbdSMiguel Ojeda KSYM_NAME_LEN_BUFFER == KSYM_NAME_LEN * 4, 476e8c5bbdSMiguel Ojeda "Please keep KSYM_NAME_LEN_BUFFER in sync with KSYM_NAME_LEN" 486e8c5bbdSMiguel Ojeda ); 49b471927eSBoqun Feng 501da177e4SLinus Torvalds struct sym_entry { 511da177e4SLinus Torvalds unsigned long long addr; 52b3dbb4ecSPaulo Marques unsigned int len; 5360443c88SZhen Lei unsigned int seq; 54f2df3f65SPaulo Marques unsigned int start_pos; 558c996940SArd Biesheuvel unsigned int percpu_absolute; 569d82973eSLinus Torvalds unsigned char sym[]; 571da177e4SLinus Torvalds }; 581da177e4SLinus Torvalds 5978eb7159SKees Cook struct addr_range { 6078eb7159SKees Cook const char *start_sym, *end_sym; 6117b1f0deSMike Frysinger unsigned long long start, end; 6217b1f0deSMike Frysinger }; 6317b1f0deSMike Frysinger 6417b1f0deSMike Frysinger static unsigned long long _text; 652213e9a6SArd Biesheuvel static unsigned long long relative_base; 6678eb7159SKees Cook static struct addr_range text_ranges[] = { 6717b1f0deSMike Frysinger { "_stext", "_etext" }, 6817b1f0deSMike Frysinger { "_sinittext", "_einittext" }, 6917b1f0deSMike Frysinger }; 7017b1f0deSMike Frysinger #define text_range_text (&text_ranges[0]) 7117b1f0deSMike Frysinger #define text_range_inittext (&text_ranges[1]) 7217b1f0deSMike Frysinger 73c6bda7c9SRusty Russell static struct addr_range percpu_range = { 74c6bda7c9SRusty Russell "__per_cpu_start", "__per_cpu_end", -1ULL, 0 75c6bda7c9SRusty Russell }; 76c6bda7c9SRusty Russell 778d605269SMasahiro Yamada static struct sym_entry **table; 78b3dbb4ecSPaulo Marques static unsigned int table_size, table_cnt; 79831362fcSMasahiro Yamada static int all_symbols; 80831362fcSMasahiro Yamada static int absolute_percpu; 81831362fcSMasahiro Yamada static int base_relative; 82010a0aadSZhen Lei static int lto_clang; 831da177e4SLinus Torvalds 84f43e9daaSMasahiro Yamada static int token_profit[0x10000]; 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds /* the table that holds the result of the compression */ 87f43e9daaSMasahiro Yamada static unsigned char best_table[256][2]; 88f43e9daaSMasahiro Yamada static unsigned char best_table_len[256]; 891da177e4SLinus Torvalds 901da177e4SLinus Torvalds 91b3dbb4ecSPaulo Marques static void usage(void) 921da177e4SLinus Torvalds { 938d3a7507SYuntao Wang fprintf(stderr, "Usage: kallsyms [--all-symbols] [--absolute-percpu] " 94010a0aadSZhen Lei "[--base-relative] [--lto-clang] in.map > out.S\n"); 951da177e4SLinus Torvalds exit(1); 961da177e4SLinus Torvalds } 971da177e4SLinus Torvalds 9829e55ad3SMasahiro Yamada static char *sym_name(const struct sym_entry *s) 9929e55ad3SMasahiro Yamada { 10029e55ad3SMasahiro Yamada return (char *)s->sym + 1; 10129e55ad3SMasahiro Yamada } 10229e55ad3SMasahiro Yamada 103a41333e0SMasahiro Yamada static bool is_ignored_symbol(const char *name, char type) 104a41333e0SMasahiro Yamada { 105a7b00a18SMasahiro Yamada if (type == 'u' || type == 'n') 106887df76dSMasahiro Yamada return true; 107887df76dSMasahiro Yamada 108887df76dSMasahiro Yamada if (toupper(type) == 'A') { 109887df76dSMasahiro Yamada /* Keep these useful absolute symbols */ 110887df76dSMasahiro Yamada if (strcmp(name, "__kernel_syscall_via_break") && 111887df76dSMasahiro Yamada strcmp(name, "__kernel_syscall_via_epc") && 112887df76dSMasahiro Yamada strcmp(name, "__kernel_sigtramp") && 113887df76dSMasahiro Yamada strcmp(name, "__gp")) 114887df76dSMasahiro Yamada return true; 115887df76dSMasahiro Yamada } 116887df76dSMasahiro Yamada 117a41333e0SMasahiro Yamada return false; 118a41333e0SMasahiro Yamada } 119a41333e0SMasahiro Yamada 120b6233d0dSMasahiro Yamada static void check_symbol_range(const char *sym, unsigned long long addr, 12178eb7159SKees Cook struct addr_range *ranges, int entries) 12217b1f0deSMike Frysinger { 12317b1f0deSMike Frysinger size_t i; 12478eb7159SKees Cook struct addr_range *ar; 12517b1f0deSMike Frysinger 12678eb7159SKees Cook for (i = 0; i < entries; ++i) { 12778eb7159SKees Cook ar = &ranges[i]; 12817b1f0deSMike Frysinger 12978eb7159SKees Cook if (strcmp(sym, ar->start_sym) == 0) { 13078eb7159SKees Cook ar->start = addr; 131b6233d0dSMasahiro Yamada return; 13278eb7159SKees Cook } else if (strcmp(sym, ar->end_sym) == 0) { 13378eb7159SKees Cook ar->end = addr; 134b6233d0dSMasahiro Yamada return; 13517b1f0deSMike Frysinger } 13617b1f0deSMike Frysinger } 13717b1f0deSMike Frysinger } 13817b1f0deSMike Frysinger 1398d605269SMasahiro Yamada static struct sym_entry *read_symbol(FILE *in) 1401da177e4SLinus Torvalds { 141b471927eSBoqun Feng char name[KSYM_NAME_LEN_BUFFER+1], type; 1428d605269SMasahiro Yamada unsigned long long addr; 1438d605269SMasahiro Yamada unsigned int len; 1448d605269SMasahiro Yamada struct sym_entry *sym; 1451da177e4SLinus Torvalds int rc; 1461da177e4SLinus Torvalds 147b471927eSBoqun Feng rc = fscanf(in, "%llx %c %" _stringify(KSYM_NAME_LEN_BUFFER) "s\n", &addr, &type, name); 1481da177e4SLinus Torvalds if (rc != 3) { 149b66c874fSBoqun Feng if (rc != EOF && fgets(name, ARRAY_SIZE(name), in) == NULL) 150ef894870SJean Sacren fprintf(stderr, "Read error or end of file.\n"); 1518d605269SMasahiro Yamada return NULL; 1521da177e4SLinus Torvalds } 153be9f6133SMasahiro Yamada if (strlen(name) >= KSYM_NAME_LEN) { 1546db2983cSEugene Loh fprintf(stderr, "Symbol %s too long for kallsyms (%zu >= %d).\n" 155f3462aa9SAndi Kleen "Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n", 156be9f6133SMasahiro Yamada name, strlen(name), KSYM_NAME_LEN); 1578d605269SMasahiro Yamada return NULL; 158f3462aa9SAndi Kleen } 1591da177e4SLinus Torvalds 160be9f6133SMasahiro Yamada if (strcmp(name, "_text") == 0) 1618d605269SMasahiro Yamada _text = addr; 162b6233d0dSMasahiro Yamada 1637883a143SMikhail Petrov /* Ignore most absolute/undefined (?) symbols. */ 1647883a143SMikhail Petrov if (is_ignored_symbol(name, type)) 1657883a143SMikhail Petrov return NULL; 1667883a143SMikhail Petrov 1678d605269SMasahiro Yamada check_symbol_range(name, addr, text_ranges, ARRAY_SIZE(text_ranges)); 1688d605269SMasahiro Yamada check_symbol_range(name, addr, &percpu_range, 1); 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds /* include the type field in the symbol name, so that it gets 1711da177e4SLinus Torvalds * compressed together */ 1728d605269SMasahiro Yamada 1738d605269SMasahiro Yamada len = strlen(name) + 1; 1748d605269SMasahiro Yamada 1759d1b3895SMasahiro Yamada sym = malloc(sizeof(*sym) + len + 1); 1768d605269SMasahiro Yamada if (!sym) { 177f1a136e0SJesper Juhl fprintf(stderr, "kallsyms failure: " 178f1a136e0SJesper Juhl "unable to allocate required amount of memory\n"); 179f1a136e0SJesper Juhl exit(EXIT_FAILURE); 180f1a136e0SJesper Juhl } 1818d605269SMasahiro Yamada sym->addr = addr; 1828d605269SMasahiro Yamada sym->len = len; 1838d605269SMasahiro Yamada sym->sym[0] = type; 1849d1b3895SMasahiro Yamada strcpy(sym_name(sym), name); 1858d605269SMasahiro Yamada sym->percpu_absolute = 0; 1861da177e4SLinus Torvalds 1878d605269SMasahiro Yamada return sym; 1881da177e4SLinus Torvalds } 1891da177e4SLinus Torvalds 1904bfe2b78SMasahiro Yamada static int symbol_in_range(const struct sym_entry *s, 1914bfe2b78SMasahiro Yamada const struct addr_range *ranges, int entries) 19217b1f0deSMike Frysinger { 19317b1f0deSMike Frysinger size_t i; 1944bfe2b78SMasahiro Yamada const struct addr_range *ar; 19517b1f0deSMike Frysinger 19678eb7159SKees Cook for (i = 0; i < entries; ++i) { 19778eb7159SKees Cook ar = &ranges[i]; 19817b1f0deSMike Frysinger 19978eb7159SKees Cook if (s->addr >= ar->start && s->addr <= ar->end) 200ac6ca5c8SMike Frysinger return 1; 20117b1f0deSMike Frysinger } 20217b1f0deSMike Frysinger 203ac6ca5c8SMike Frysinger return 0; 20417b1f0deSMike Frysinger } 20517b1f0deSMike Frysinger 2064bfe2b78SMasahiro Yamada static int symbol_valid(const struct sym_entry *s) 2071da177e4SLinus Torvalds { 20829e55ad3SMasahiro Yamada const char *name = sym_name(s); 209bd8b22d2SArd Biesheuvel 2101da177e4SLinus Torvalds /* if --all-symbols is not specified, then symbols outside the text 2111da177e4SLinus Torvalds * and inittext sections are discarded */ 2121da177e4SLinus Torvalds if (!all_symbols) { 21378eb7159SKees Cook if (symbol_in_range(s, text_ranges, 21478eb7159SKees Cook ARRAY_SIZE(text_ranges)) == 0) 2151da177e4SLinus Torvalds return 0; 2161da177e4SLinus Torvalds /* Corner case. Discard any symbols with the same value as 217a3b81113SRobin Getz * _etext _einittext; they can move between pass 1 and 2 when 218a3b81113SRobin Getz * the kallsyms data are added. If these symbols move then 219a3b81113SRobin Getz * they may get dropped in pass 2, which breaks the kallsyms 220a3b81113SRobin Getz * rules. 2211da177e4SLinus Torvalds */ 22217b1f0deSMike Frysinger if ((s->addr == text_range_text->end && 22329e55ad3SMasahiro Yamada strcmp(name, text_range_text->end_sym)) || 22417b1f0deSMike Frysinger (s->addr == text_range_inittext->end && 22529e55ad3SMasahiro Yamada strcmp(name, text_range_inittext->end_sym))) 2261da177e4SLinus Torvalds return 0; 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds return 1; 2301da177e4SLinus Torvalds } 2311da177e4SLinus Torvalds 2325e5c4fa7SMasahiro Yamada /* remove all the invalid symbols from the table */ 2335e5c4fa7SMasahiro Yamada static void shrink_table(void) 2345e5c4fa7SMasahiro Yamada { 2355e5c4fa7SMasahiro Yamada unsigned int i, pos; 2365e5c4fa7SMasahiro Yamada 2375e5c4fa7SMasahiro Yamada pos = 0; 2385e5c4fa7SMasahiro Yamada for (i = 0; i < table_cnt; i++) { 2398d605269SMasahiro Yamada if (symbol_valid(table[i])) { 2405e5c4fa7SMasahiro Yamada if (pos != i) 2415e5c4fa7SMasahiro Yamada table[pos] = table[i]; 2425e5c4fa7SMasahiro Yamada pos++; 2435e5c4fa7SMasahiro Yamada } else { 2448d605269SMasahiro Yamada free(table[i]); 2455e5c4fa7SMasahiro Yamada } 2465e5c4fa7SMasahiro Yamada } 2475e5c4fa7SMasahiro Yamada table_cnt = pos; 2485e5c4fa7SMasahiro Yamada 2495e5c4fa7SMasahiro Yamada /* When valid symbol is not registered, exit to error */ 2505e5c4fa7SMasahiro Yamada if (!table_cnt) { 2515e5c4fa7SMasahiro Yamada fprintf(stderr, "No valid symbol.\n"); 2525e5c4fa7SMasahiro Yamada exit(1); 2535e5c4fa7SMasahiro Yamada } 2545e5c4fa7SMasahiro Yamada } 2555e5c4fa7SMasahiro Yamada 256aa221f2eSMasahiro Yamada static void read_map(const char *in) 2571da177e4SLinus Torvalds { 258aa221f2eSMasahiro Yamada FILE *fp; 2598d605269SMasahiro Yamada struct sym_entry *sym; 2608d605269SMasahiro Yamada 261aa221f2eSMasahiro Yamada fp = fopen(in, "r"); 262aa221f2eSMasahiro Yamada if (!fp) { 263aa221f2eSMasahiro Yamada perror(in); 264aa221f2eSMasahiro Yamada exit(1); 265aa221f2eSMasahiro Yamada } 266aa221f2eSMasahiro Yamada 267aa221f2eSMasahiro Yamada while (!feof(fp)) { 268aa221f2eSMasahiro Yamada sym = read_symbol(fp); 2698d605269SMasahiro Yamada if (!sym) 2708d605269SMasahiro Yamada continue; 2718d605269SMasahiro Yamada 2728d605269SMasahiro Yamada sym->start_pos = table_cnt; 2738d605269SMasahiro Yamada 274b3dbb4ecSPaulo Marques if (table_cnt >= table_size) { 275b3dbb4ecSPaulo Marques table_size += 10000; 276b3dbb4ecSPaulo Marques table = realloc(table, sizeof(*table) * table_size); 2771da177e4SLinus Torvalds if (!table) { 2781da177e4SLinus Torvalds fprintf(stderr, "out of memory\n"); 279aa221f2eSMasahiro Yamada fclose(fp); 2801da177e4SLinus Torvalds exit (1); 2811da177e4SLinus Torvalds } 2821da177e4SLinus Torvalds } 2838d605269SMasahiro Yamada 2848d605269SMasahiro Yamada table[table_cnt++] = sym; 2851da177e4SLinus Torvalds } 286aa221f2eSMasahiro Yamada 287aa221f2eSMasahiro Yamada fclose(fp); 288f2df3f65SPaulo Marques } 2891da177e4SLinus Torvalds 2904bfe2b78SMasahiro Yamada static void output_label(const char *label) 2911da177e4SLinus Torvalds { 2921da177e4SLinus Torvalds printf(".globl %s\n", label); 2931da177e4SLinus Torvalds printf("\tALGN\n"); 2941da177e4SLinus Torvalds printf("%s:\n", label); 2951da177e4SLinus Torvalds } 2961da177e4SLinus Torvalds 297fd2ab2f6SMasahiro Yamada /* Provide proper symbols relocatability by their '_text' relativeness. */ 298fd2ab2f6SMasahiro Yamada static void output_address(unsigned long long addr) 299fd2ab2f6SMasahiro Yamada { 300fd2ab2f6SMasahiro Yamada if (_text <= addr) 301fd2ab2f6SMasahiro Yamada printf("\tPTR\t_text + %#llx\n", addr - _text); 302fd2ab2f6SMasahiro Yamada else 303fd2ab2f6SMasahiro Yamada printf("\tPTR\t_text - %#llx\n", _text - addr); 304fd2ab2f6SMasahiro Yamada } 305fd2ab2f6SMasahiro Yamada 3061da177e4SLinus Torvalds /* uncompress a compressed symbol. When this function is called, the best table 3071da177e4SLinus Torvalds * might still be compressed itself, so the function needs to be recursive */ 3084bfe2b78SMasahiro Yamada static int expand_symbol(const unsigned char *data, int len, char *result) 3091da177e4SLinus Torvalds { 3101da177e4SLinus Torvalds int c, rlen, total=0; 3111da177e4SLinus Torvalds 3121da177e4SLinus Torvalds while (len) { 3131da177e4SLinus Torvalds c = *data; 3141da177e4SLinus Torvalds /* if the table holds a single char that is the same as the one 3151da177e4SLinus Torvalds * we are looking for, then end the search */ 3161da177e4SLinus Torvalds if (best_table[c][0]==c && best_table_len[c]==1) { 3171da177e4SLinus Torvalds *result++ = c; 3181da177e4SLinus Torvalds total++; 3191da177e4SLinus Torvalds } else { 3201da177e4SLinus Torvalds /* if not, recurse and expand */ 3211da177e4SLinus Torvalds rlen = expand_symbol(best_table[c], best_table_len[c], result); 3221da177e4SLinus Torvalds total += rlen; 3231da177e4SLinus Torvalds result += rlen; 3241da177e4SLinus Torvalds } 3251da177e4SLinus Torvalds data++; 3261da177e4SLinus Torvalds len--; 3271da177e4SLinus Torvalds } 3281da177e4SLinus Torvalds *result=0; 3291da177e4SLinus Torvalds 3301da177e4SLinus Torvalds return total; 3311da177e4SLinus Torvalds } 3321da177e4SLinus Torvalds 3334bfe2b78SMasahiro Yamada static int symbol_absolute(const struct sym_entry *s) 33478eb7159SKees Cook { 3358c996940SArd Biesheuvel return s->percpu_absolute; 33678eb7159SKees Cook } 33778eb7159SKees Cook 338010a0aadSZhen Lei static void cleanup_symbol_name(char *s) 339010a0aadSZhen Lei { 340010a0aadSZhen Lei char *p; 341010a0aadSZhen Lei 342010a0aadSZhen Lei /* 343010a0aadSZhen Lei * ASCII[.] = 2e 344010a0aadSZhen Lei * ASCII[0-9] = 30,39 345010a0aadSZhen Lei * ASCII[A-Z] = 41,5a 346010a0aadSZhen Lei * ASCII[_] = 5f 347010a0aadSZhen Lei * ASCII[a-z] = 61,7a 348010a0aadSZhen Lei * 349010a0aadSZhen Lei * As above, replacing '.' with '\0' does not affect the main sorting, 350010a0aadSZhen Lei * but it helps us with subsorting. 351010a0aadSZhen Lei */ 352010a0aadSZhen Lei p = strchr(s, '.'); 353010a0aadSZhen Lei if (p) 354010a0aadSZhen Lei *p = '\0'; 355010a0aadSZhen Lei } 356010a0aadSZhen Lei 35760443c88SZhen Lei static int compare_names(const void *a, const void *b) 35860443c88SZhen Lei { 35960443c88SZhen Lei int ret; 36060443c88SZhen Lei const struct sym_entry *sa = *(const struct sym_entry **)a; 36160443c88SZhen Lei const struct sym_entry *sb = *(const struct sym_entry **)b; 36260443c88SZhen Lei 363dd1553b8SMasahiro Yamada ret = strcmp(sym_name(sa), sym_name(sb)); 36460443c88SZhen Lei if (!ret) { 36560443c88SZhen Lei if (sa->addr > sb->addr) 36660443c88SZhen Lei return 1; 36760443c88SZhen Lei else if (sa->addr < sb->addr) 36860443c88SZhen Lei return -1; 36960443c88SZhen Lei 37060443c88SZhen Lei /* keep old order */ 37160443c88SZhen Lei return (int)(sa->seq - sb->seq); 37260443c88SZhen Lei } 37360443c88SZhen Lei 37460443c88SZhen Lei return ret; 37560443c88SZhen Lei } 37660443c88SZhen Lei 37760443c88SZhen Lei static void sort_symbols_by_name(void) 37860443c88SZhen Lei { 37960443c88SZhen Lei qsort(table, table_cnt, sizeof(table[0]), compare_names); 38060443c88SZhen Lei } 38160443c88SZhen Lei 382b3dbb4ecSPaulo Marques static void write_src(void) 3831da177e4SLinus Torvalds { 384b3dbb4ecSPaulo Marques unsigned int i, k, off; 3851da177e4SLinus Torvalds unsigned int best_idx[256]; 3861da177e4SLinus Torvalds unsigned int *markers; 3879281aceaSTejun Heo char buf[KSYM_NAME_LEN]; 3881da177e4SLinus Torvalds 389500193ecSMasahiro Yamada printf("#include <asm/bitsperlong.h>\n"); 3901da177e4SLinus Torvalds printf("#if BITS_PER_LONG == 64\n"); 3911da177e4SLinus Torvalds printf("#define PTR .quad\n"); 39272d3ebb9SMathias Krause printf("#define ALGN .balign 8\n"); 3931da177e4SLinus Torvalds printf("#else\n"); 3941da177e4SLinus Torvalds printf("#define PTR .long\n"); 39572d3ebb9SMathias Krause printf("#define ALGN .balign 4\n"); 3961da177e4SLinus Torvalds printf("#endif\n"); 3971da177e4SLinus Torvalds 398aad09470SJan Beulich printf("\t.section .rodata, \"a\"\n"); 3991da177e4SLinus Torvalds 4001da177e4SLinus Torvalds output_label("kallsyms_num_syms"); 40180ffbaa5SJan Beulich printf("\t.long\t%u\n", table_cnt); 4021da177e4SLinus Torvalds printf("\n"); 4031da177e4SLinus Torvalds 4041da177e4SLinus Torvalds /* table of offset markers, that give the offset in the compressed stream 4051da177e4SLinus Torvalds * every 256 symbols */ 406f1a136e0SJesper Juhl markers = malloc(sizeof(unsigned int) * ((table_cnt + 255) / 256)); 407f1a136e0SJesper Juhl if (!markers) { 408f1a136e0SJesper Juhl fprintf(stderr, "kallsyms failure: " 409f1a136e0SJesper Juhl "unable to allocate required memory\n"); 410f1a136e0SJesper Juhl exit(EXIT_FAILURE); 411f1a136e0SJesper Juhl } 4121da177e4SLinus Torvalds 4131da177e4SLinus Torvalds output_label("kallsyms_names"); 4141da177e4SLinus Torvalds off = 0; 415b3dbb4ecSPaulo Marques for (i = 0; i < table_cnt; i++) { 416b3dbb4ecSPaulo Marques if ((i & 0xFF) == 0) 417b3dbb4ecSPaulo Marques markers[i >> 8] = off; 41860443c88SZhen Lei table[i]->seq = i; 4191da177e4SLinus Torvalds 42073bbb944SMiguel Ojeda /* There cannot be any symbol of length zero. */ 42173bbb944SMiguel Ojeda if (table[i]->len == 0) { 42273bbb944SMiguel Ojeda fprintf(stderr, "kallsyms failure: " 42373bbb944SMiguel Ojeda "unexpected zero symbol length\n"); 42473bbb944SMiguel Ojeda exit(EXIT_FAILURE); 42573bbb944SMiguel Ojeda } 42673bbb944SMiguel Ojeda 42773bbb944SMiguel Ojeda /* Only lengths that fit in up-to-two-byte ULEB128 are supported. */ 42873bbb944SMiguel Ojeda if (table[i]->len > 0x3FFF) { 42973bbb944SMiguel Ojeda fprintf(stderr, "kallsyms failure: " 43073bbb944SMiguel Ojeda "unexpected huge symbol length\n"); 43173bbb944SMiguel Ojeda exit(EXIT_FAILURE); 43273bbb944SMiguel Ojeda } 43373bbb944SMiguel Ojeda 43473bbb944SMiguel Ojeda /* Encode length with ULEB128. */ 43573bbb944SMiguel Ojeda if (table[i]->len <= 0x7F) { 43673bbb944SMiguel Ojeda /* Most symbols use a single byte for the length. */ 4378d605269SMasahiro Yamada printf("\t.byte 0x%02x", table[i]->len); 43873bbb944SMiguel Ojeda off += table[i]->len + 1; 43973bbb944SMiguel Ojeda } else { 44073bbb944SMiguel Ojeda /* "Big" symbols use two bytes. */ 44173bbb944SMiguel Ojeda printf("\t.byte 0x%02x, 0x%02x", 44273bbb944SMiguel Ojeda (table[i]->len & 0x7F) | 0x80, 44373bbb944SMiguel Ojeda (table[i]->len >> 7) & 0x7F); 44473bbb944SMiguel Ojeda off += table[i]->len + 2; 44573bbb944SMiguel Ojeda } 4468d605269SMasahiro Yamada for (k = 0; k < table[i]->len; k++) 4478d605269SMasahiro Yamada printf(", 0x%02x", table[i]->sym[k]); 4481da177e4SLinus Torvalds printf("\n"); 4491da177e4SLinus Torvalds } 4501da177e4SLinus Torvalds printf("\n"); 4511da177e4SLinus Torvalds 452dd1553b8SMasahiro Yamada /* 453dd1553b8SMasahiro Yamada * Now that we wrote out the compressed symbol names, restore the 454dd1553b8SMasahiro Yamada * original names, which are needed in some of the later steps. 455dd1553b8SMasahiro Yamada */ 456dd1553b8SMasahiro Yamada for (i = 0; i < table_cnt; i++) { 457dd1553b8SMasahiro Yamada expand_symbol(table[i]->sym, table[i]->len, buf); 458dd1553b8SMasahiro Yamada strcpy((char *)table[i]->sym, buf); 459dd1553b8SMasahiro Yamada } 460dd1553b8SMasahiro Yamada 4611da177e4SLinus Torvalds output_label("kallsyms_markers"); 462b3dbb4ecSPaulo Marques for (i = 0; i < ((table_cnt + 255) >> 8); i++) 46380ffbaa5SJan Beulich printf("\t.long\t%u\n", markers[i]); 4641da177e4SLinus Torvalds printf("\n"); 4651da177e4SLinus Torvalds 4661da177e4SLinus Torvalds free(markers); 4671da177e4SLinus Torvalds 4681da177e4SLinus Torvalds output_label("kallsyms_token_table"); 4691da177e4SLinus Torvalds off = 0; 4701da177e4SLinus Torvalds for (i = 0; i < 256; i++) { 4711da177e4SLinus Torvalds best_idx[i] = off; 4721da177e4SLinus Torvalds expand_symbol(best_table[i], best_table_len[i], buf); 4731da177e4SLinus Torvalds printf("\t.asciz\t\"%s\"\n", buf); 4741da177e4SLinus Torvalds off += strlen(buf) + 1; 4751da177e4SLinus Torvalds } 4761da177e4SLinus Torvalds printf("\n"); 4771da177e4SLinus Torvalds 4781da177e4SLinus Torvalds output_label("kallsyms_token_index"); 4791da177e4SLinus Torvalds for (i = 0; i < 256; i++) 4801da177e4SLinus Torvalds printf("\t.short\t%d\n", best_idx[i]); 4811da177e4SLinus Torvalds printf("\n"); 482404bad70SMasahiro Yamada 483404bad70SMasahiro Yamada if (!base_relative) 484404bad70SMasahiro Yamada output_label("kallsyms_addresses"); 485404bad70SMasahiro Yamada else 486404bad70SMasahiro Yamada output_label("kallsyms_offsets"); 487404bad70SMasahiro Yamada 488404bad70SMasahiro Yamada for (i = 0; i < table_cnt; i++) { 489404bad70SMasahiro Yamada if (base_relative) { 490404bad70SMasahiro Yamada /* 491404bad70SMasahiro Yamada * Use the offset relative to the lowest value 492404bad70SMasahiro Yamada * encountered of all relative symbols, and emit 493404bad70SMasahiro Yamada * non-relocatable fixed offsets that will be fixed 494404bad70SMasahiro Yamada * up at runtime. 495404bad70SMasahiro Yamada */ 496404bad70SMasahiro Yamada 497404bad70SMasahiro Yamada long long offset; 498404bad70SMasahiro Yamada int overflow; 499404bad70SMasahiro Yamada 500404bad70SMasahiro Yamada if (!absolute_percpu) { 501404bad70SMasahiro Yamada offset = table[i]->addr - relative_base; 502404bad70SMasahiro Yamada overflow = (offset < 0 || offset > UINT_MAX); 503404bad70SMasahiro Yamada } else if (symbol_absolute(table[i])) { 504404bad70SMasahiro Yamada offset = table[i]->addr; 505404bad70SMasahiro Yamada overflow = (offset < 0 || offset > INT_MAX); 506404bad70SMasahiro Yamada } else { 507404bad70SMasahiro Yamada offset = relative_base - table[i]->addr - 1; 508404bad70SMasahiro Yamada overflow = (offset < INT_MIN || offset >= 0); 509404bad70SMasahiro Yamada } 510404bad70SMasahiro Yamada if (overflow) { 511404bad70SMasahiro Yamada fprintf(stderr, "kallsyms failure: " 512404bad70SMasahiro Yamada "%s symbol value %#llx out of range in relative mode\n", 513404bad70SMasahiro Yamada symbol_absolute(table[i]) ? "absolute" : "relative", 514404bad70SMasahiro Yamada table[i]->addr); 515404bad70SMasahiro Yamada exit(EXIT_FAILURE); 516404bad70SMasahiro Yamada } 517dd1553b8SMasahiro Yamada printf("\t.long\t%#x /* %s */\n", (int)offset, table[i]->sym); 518404bad70SMasahiro Yamada } else if (!symbol_absolute(table[i])) { 519404bad70SMasahiro Yamada output_address(table[i]->addr); 520404bad70SMasahiro Yamada } else { 521404bad70SMasahiro Yamada printf("\tPTR\t%#llx\n", table[i]->addr); 522404bad70SMasahiro Yamada } 523404bad70SMasahiro Yamada } 524404bad70SMasahiro Yamada printf("\n"); 525404bad70SMasahiro Yamada 526404bad70SMasahiro Yamada if (base_relative) { 527404bad70SMasahiro Yamada output_label("kallsyms_relative_base"); 528404bad70SMasahiro Yamada output_address(relative_base); 529404bad70SMasahiro Yamada printf("\n"); 530404bad70SMasahiro Yamada } 531404bad70SMasahiro Yamada 532dd1553b8SMasahiro Yamada if (lto_clang) 533dd1553b8SMasahiro Yamada for (i = 0; i < table_cnt; i++) 534dd1553b8SMasahiro Yamada cleanup_symbol_name((char *)table[i]->sym); 535dd1553b8SMasahiro Yamada 536404bad70SMasahiro Yamada sort_symbols_by_name(); 537404bad70SMasahiro Yamada output_label("kallsyms_seqs_of_names"); 538404bad70SMasahiro Yamada for (i = 0; i < table_cnt; i++) 539404bad70SMasahiro Yamada printf("\t.byte 0x%02x, 0x%02x, 0x%02x\n", 540404bad70SMasahiro Yamada (unsigned char)(table[i]->seq >> 16), 541404bad70SMasahiro Yamada (unsigned char)(table[i]->seq >> 8), 542404bad70SMasahiro Yamada (unsigned char)(table[i]->seq >> 0)); 543404bad70SMasahiro Yamada printf("\n"); 5441da177e4SLinus Torvalds } 5451da177e4SLinus Torvalds 5461da177e4SLinus Torvalds 5471da177e4SLinus Torvalds /* table lookup compression functions */ 5481da177e4SLinus Torvalds 5491da177e4SLinus Torvalds /* count all the possible tokens in a symbol */ 5504bfe2b78SMasahiro Yamada static void learn_symbol(const unsigned char *symbol, int len) 5511da177e4SLinus Torvalds { 5521da177e4SLinus Torvalds int i; 5531da177e4SLinus Torvalds 5541da177e4SLinus Torvalds for (i = 0; i < len - 1; i++) 555b3dbb4ecSPaulo Marques token_profit[ symbol[i] + (symbol[i + 1] << 8) ]++; 5561da177e4SLinus Torvalds } 5571da177e4SLinus Torvalds 5581da177e4SLinus Torvalds /* decrease the count for all the possible tokens in a symbol */ 5594bfe2b78SMasahiro Yamada static void forget_symbol(const unsigned char *symbol, int len) 5601da177e4SLinus Torvalds { 5611da177e4SLinus Torvalds int i; 5621da177e4SLinus Torvalds 5631da177e4SLinus Torvalds for (i = 0; i < len - 1; i++) 564b3dbb4ecSPaulo Marques token_profit[ symbol[i] + (symbol[i + 1] << 8) ]--; 5651da177e4SLinus Torvalds } 5661da177e4SLinus Torvalds 5675e5c4fa7SMasahiro Yamada /* do the initial token count */ 568fcdf7197SZhen Lei static void build_initial_token_table(void) 5691da177e4SLinus Torvalds { 5705e5c4fa7SMasahiro Yamada unsigned int i; 5711da177e4SLinus Torvalds 5725e5c4fa7SMasahiro Yamada for (i = 0; i < table_cnt; i++) 5738d605269SMasahiro Yamada learn_symbol(table[i]->sym, table[i]->len); 5741da177e4SLinus Torvalds } 5751da177e4SLinus Torvalds 5762558c138SMasahiro Yamada static unsigned char *find_token(unsigned char *str, int len, 5774bfe2b78SMasahiro Yamada const unsigned char *token) 5787c5d249aSPaulo Marques { 5797c5d249aSPaulo Marques int i; 5807c5d249aSPaulo Marques 5817c5d249aSPaulo Marques for (i = 0; i < len - 1; i++) { 5827c5d249aSPaulo Marques if (str[i] == token[0] && str[i+1] == token[1]) 5837c5d249aSPaulo Marques return &str[i]; 5847c5d249aSPaulo Marques } 5857c5d249aSPaulo Marques return NULL; 5867c5d249aSPaulo Marques } 5877c5d249aSPaulo Marques 5881da177e4SLinus Torvalds /* replace a given token in all the valid symbols. Use the sampled symbols 5891da177e4SLinus Torvalds * to update the counts */ 5904bfe2b78SMasahiro Yamada static void compress_symbols(const unsigned char *str, int idx) 5911da177e4SLinus Torvalds { 592b3dbb4ecSPaulo Marques unsigned int i, len, size; 593b3dbb4ecSPaulo Marques unsigned char *p1, *p2; 5941da177e4SLinus Torvalds 595b3dbb4ecSPaulo Marques for (i = 0; i < table_cnt; i++) { 5961da177e4SLinus Torvalds 5978d605269SMasahiro Yamada len = table[i]->len; 5988d605269SMasahiro Yamada p1 = table[i]->sym; 599b3dbb4ecSPaulo Marques 600b3dbb4ecSPaulo Marques /* find the token on the symbol */ 6017c5d249aSPaulo Marques p2 = find_token(p1, len, str); 602b3dbb4ecSPaulo Marques if (!p2) continue; 603b3dbb4ecSPaulo Marques 604b3dbb4ecSPaulo Marques /* decrease the counts for this symbol's tokens */ 6058d605269SMasahiro Yamada forget_symbol(table[i]->sym, len); 606b3dbb4ecSPaulo Marques 607b3dbb4ecSPaulo Marques size = len; 6081da177e4SLinus Torvalds 6091da177e4SLinus Torvalds do { 610b3dbb4ecSPaulo Marques *p2 = idx; 611b3dbb4ecSPaulo Marques p2++; 612b3dbb4ecSPaulo Marques size -= (p2 - p1); 613b3dbb4ecSPaulo Marques memmove(p2, p2 + 1, size); 614b3dbb4ecSPaulo Marques p1 = p2; 615b3dbb4ecSPaulo Marques len--; 616b3dbb4ecSPaulo Marques 617b3dbb4ecSPaulo Marques if (size < 2) break; 618b3dbb4ecSPaulo Marques 6191da177e4SLinus Torvalds /* find the token on the symbol */ 6207c5d249aSPaulo Marques p2 = find_token(p1, size, str); 6211da177e4SLinus Torvalds 622b3dbb4ecSPaulo Marques } while (p2); 6231da177e4SLinus Torvalds 6248d605269SMasahiro Yamada table[i]->len = len; 625b3dbb4ecSPaulo Marques 626b3dbb4ecSPaulo Marques /* increase the counts for this symbol's new tokens */ 6278d605269SMasahiro Yamada learn_symbol(table[i]->sym, len); 6281da177e4SLinus Torvalds } 6291da177e4SLinus Torvalds } 6301da177e4SLinus Torvalds 6311da177e4SLinus Torvalds /* search the token with the maximum profit */ 632b3dbb4ecSPaulo Marques static int find_best_token(void) 6331da177e4SLinus Torvalds { 634b3dbb4ecSPaulo Marques int i, best, bestprofit; 6351da177e4SLinus Torvalds 6361da177e4SLinus Torvalds bestprofit=-10000; 637b3dbb4ecSPaulo Marques best = 0; 6381da177e4SLinus Torvalds 639b3dbb4ecSPaulo Marques for (i = 0; i < 0x10000; i++) { 640b3dbb4ecSPaulo Marques if (token_profit[i] > bestprofit) { 641b3dbb4ecSPaulo Marques best = i; 642b3dbb4ecSPaulo Marques bestprofit = token_profit[i]; 6431da177e4SLinus Torvalds } 6441da177e4SLinus Torvalds } 6451da177e4SLinus Torvalds return best; 6461da177e4SLinus Torvalds } 6471da177e4SLinus Torvalds 6481da177e4SLinus Torvalds /* this is the core of the algorithm: calculate the "best" table */ 6491da177e4SLinus Torvalds static void optimize_result(void) 6501da177e4SLinus Torvalds { 651b3dbb4ecSPaulo Marques int i, best; 6521da177e4SLinus Torvalds 6531da177e4SLinus Torvalds /* using the '\0' symbol last allows compress_symbols to use standard 6541da177e4SLinus Torvalds * fast string functions */ 6551da177e4SLinus Torvalds for (i = 255; i >= 0; i--) { 6561da177e4SLinus Torvalds 6571da177e4SLinus Torvalds /* if this table slot is empty (it is not used by an actual 6581da177e4SLinus Torvalds * original char code */ 6591da177e4SLinus Torvalds if (!best_table_len[i]) { 6601da177e4SLinus Torvalds 661cbf7a90eSCao jin /* find the token with the best profit value */ 6621da177e4SLinus Torvalds best = find_best_token(); 663e0a04b11SXiaochen Wang if (token_profit[best] == 0) 664e0a04b11SXiaochen Wang break; 6651da177e4SLinus Torvalds 6661da177e4SLinus Torvalds /* place it in the "best" table */ 667b3dbb4ecSPaulo Marques best_table_len[i] = 2; 668b3dbb4ecSPaulo Marques best_table[i][0] = best & 0xFF; 669b3dbb4ecSPaulo Marques best_table[i][1] = (best >> 8) & 0xFF; 6701da177e4SLinus Torvalds 6711da177e4SLinus Torvalds /* replace this token in all the valid symbols */ 672b3dbb4ecSPaulo Marques compress_symbols(best_table[i], i); 6731da177e4SLinus Torvalds } 6741da177e4SLinus Torvalds } 6751da177e4SLinus Torvalds } 6761da177e4SLinus Torvalds 6771da177e4SLinus Torvalds /* start by placing the symbols that are actually used on the table */ 6781da177e4SLinus Torvalds static void insert_real_symbols_in_table(void) 6791da177e4SLinus Torvalds { 680b3dbb4ecSPaulo Marques unsigned int i, j, c; 6811da177e4SLinus Torvalds 682b3dbb4ecSPaulo Marques for (i = 0; i < table_cnt; i++) { 6838d605269SMasahiro Yamada for (j = 0; j < table[i]->len; j++) { 6848d605269SMasahiro Yamada c = table[i]->sym[j]; 6851da177e4SLinus Torvalds best_table[c][0]=c; 6861da177e4SLinus Torvalds best_table_len[c]=1; 6871da177e4SLinus Torvalds } 6881da177e4SLinus Torvalds } 6891da177e4SLinus Torvalds } 6901da177e4SLinus Torvalds 6911da177e4SLinus Torvalds static void optimize_token_table(void) 6921da177e4SLinus Torvalds { 693fcdf7197SZhen Lei build_initial_token_table(); 6941da177e4SLinus Torvalds 6951da177e4SLinus Torvalds insert_real_symbols_in_table(); 6961da177e4SLinus Torvalds 6971da177e4SLinus Torvalds optimize_result(); 6981da177e4SLinus Torvalds } 6991da177e4SLinus Torvalds 700b478b782SLai Jiangshan /* guess for "linker script provide" symbol */ 701b478b782SLai Jiangshan static int may_be_linker_script_provide_symbol(const struct sym_entry *se) 702b478b782SLai Jiangshan { 70329e55ad3SMasahiro Yamada const char *symbol = sym_name(se); 704b478b782SLai Jiangshan int len = se->len - 1; 705b478b782SLai Jiangshan 706b478b782SLai Jiangshan if (len < 8) 707b478b782SLai Jiangshan return 0; 708b478b782SLai Jiangshan 709b478b782SLai Jiangshan if (symbol[0] != '_' || symbol[1] != '_') 710b478b782SLai Jiangshan return 0; 711b478b782SLai Jiangshan 712b478b782SLai Jiangshan /* __start_XXXXX */ 713b478b782SLai Jiangshan if (!memcmp(symbol + 2, "start_", 6)) 714b478b782SLai Jiangshan return 1; 715b478b782SLai Jiangshan 716b478b782SLai Jiangshan /* __stop_XXXXX */ 717b478b782SLai Jiangshan if (!memcmp(symbol + 2, "stop_", 5)) 718b478b782SLai Jiangshan return 1; 719b478b782SLai Jiangshan 720b478b782SLai Jiangshan /* __end_XXXXX */ 721b478b782SLai Jiangshan if (!memcmp(symbol + 2, "end_", 4)) 722b478b782SLai Jiangshan return 1; 723b478b782SLai Jiangshan 724b478b782SLai Jiangshan /* __XXXXX_start */ 725b478b782SLai Jiangshan if (!memcmp(symbol + len - 6, "_start", 6)) 726b478b782SLai Jiangshan return 1; 727b478b782SLai Jiangshan 728b478b782SLai Jiangshan /* __XXXXX_end */ 729b478b782SLai Jiangshan if (!memcmp(symbol + len - 4, "_end", 4)) 730b478b782SLai Jiangshan return 1; 731b478b782SLai Jiangshan 732b478b782SLai Jiangshan return 0; 733b478b782SLai Jiangshan } 734b478b782SLai Jiangshan 735f2df3f65SPaulo Marques static int compare_symbols(const void *a, const void *b) 736f2df3f65SPaulo Marques { 7378d605269SMasahiro Yamada const struct sym_entry *sa = *(const struct sym_entry **)a; 7388d605269SMasahiro Yamada const struct sym_entry *sb = *(const struct sym_entry **)b; 739f2df3f65SPaulo Marques int wa, wb; 740f2df3f65SPaulo Marques 741f2df3f65SPaulo Marques /* sort by address first */ 742f2df3f65SPaulo Marques if (sa->addr > sb->addr) 743f2df3f65SPaulo Marques return 1; 744f2df3f65SPaulo Marques if (sa->addr < sb->addr) 745f2df3f65SPaulo Marques return -1; 746f2df3f65SPaulo Marques 747f2df3f65SPaulo Marques /* sort by "weakness" type */ 748f2df3f65SPaulo Marques wa = (sa->sym[0] == 'w') || (sa->sym[0] == 'W'); 749f2df3f65SPaulo Marques wb = (sb->sym[0] == 'w') || (sb->sym[0] == 'W'); 750f2df3f65SPaulo Marques if (wa != wb) 751f2df3f65SPaulo Marques return wa - wb; 752f2df3f65SPaulo Marques 753b478b782SLai Jiangshan /* sort by "linker script provide" type */ 754b478b782SLai Jiangshan wa = may_be_linker_script_provide_symbol(sa); 755b478b782SLai Jiangshan wb = may_be_linker_script_provide_symbol(sb); 756b478b782SLai Jiangshan if (wa != wb) 757b478b782SLai Jiangshan return wa - wb; 758b478b782SLai Jiangshan 759b478b782SLai Jiangshan /* sort by the number of prefix underscores */ 760aa915245SMasahiro Yamada wa = strspn(sym_name(sa), "_"); 761aa915245SMasahiro Yamada wb = strspn(sym_name(sb), "_"); 762b478b782SLai Jiangshan if (wa != wb) 763b478b782SLai Jiangshan return wa - wb; 764b478b782SLai Jiangshan 765f2df3f65SPaulo Marques /* sort by initial order, so that other symbols are left undisturbed */ 766f2df3f65SPaulo Marques return sa->start_pos - sb->start_pos; 767f2df3f65SPaulo Marques } 768f2df3f65SPaulo Marques 769f2df3f65SPaulo Marques static void sort_symbols(void) 770f2df3f65SPaulo Marques { 7718d605269SMasahiro Yamada qsort(table, table_cnt, sizeof(table[0]), compare_symbols); 772f2df3f65SPaulo Marques } 7731da177e4SLinus Torvalds 774c6bda7c9SRusty Russell static void make_percpus_absolute(void) 775c6bda7c9SRusty Russell { 776c6bda7c9SRusty Russell unsigned int i; 777c6bda7c9SRusty Russell 778c6bda7c9SRusty Russell for (i = 0; i < table_cnt; i++) 7798d605269SMasahiro Yamada if (symbol_in_range(table[i], &percpu_range, 1)) { 7808c996940SArd Biesheuvel /* 7818c996940SArd Biesheuvel * Keep the 'A' override for percpu symbols to 7828c996940SArd Biesheuvel * ensure consistent behavior compared to older 7838c996940SArd Biesheuvel * versions of this tool. 7848c996940SArd Biesheuvel */ 7858d605269SMasahiro Yamada table[i]->sym[0] = 'A'; 7868d605269SMasahiro Yamada table[i]->percpu_absolute = 1; 7878c996940SArd Biesheuvel } 788c6bda7c9SRusty Russell } 789c6bda7c9SRusty Russell 7902213e9a6SArd Biesheuvel /* find the minimum non-absolute symbol address */ 7912213e9a6SArd Biesheuvel static void record_relative_base(void) 7922213e9a6SArd Biesheuvel { 7932213e9a6SArd Biesheuvel unsigned int i; 7942213e9a6SArd Biesheuvel 7952213e9a6SArd Biesheuvel for (i = 0; i < table_cnt; i++) 7968d605269SMasahiro Yamada if (!symbol_absolute(table[i])) { 797f34ea029SMasahiro Yamada /* 798f34ea029SMasahiro Yamada * The table is sorted by address. 799f34ea029SMasahiro Yamada * Take the first non-absolute symbol value. 800f34ea029SMasahiro Yamada */ 8018d605269SMasahiro Yamada relative_base = table[i]->addr; 802f34ea029SMasahiro Yamada return; 803f34ea029SMasahiro Yamada } 8042213e9a6SArd Biesheuvel } 8052213e9a6SArd Biesheuvel 806b3dbb4ecSPaulo Marques int main(int argc, char **argv) 8071da177e4SLinus Torvalds { 808aa221f2eSMasahiro Yamada while (1) { 809*92e74fb6SMasahiro Yamada static const struct option long_options[] = { 810aa221f2eSMasahiro Yamada {"all-symbols", no_argument, &all_symbols, 1}, 811aa221f2eSMasahiro Yamada {"absolute-percpu", no_argument, &absolute_percpu, 1}, 812aa221f2eSMasahiro Yamada {"base-relative", no_argument, &base_relative, 1}, 813010a0aadSZhen Lei {"lto-clang", no_argument, <o_clang, 1}, 814aa221f2eSMasahiro Yamada {}, 815aa221f2eSMasahiro Yamada }; 816aa221f2eSMasahiro Yamada 817aa221f2eSMasahiro Yamada int c = getopt_long(argc, argv, "", long_options, NULL); 818aa221f2eSMasahiro Yamada 819aa221f2eSMasahiro Yamada if (c == -1) 820aa221f2eSMasahiro Yamada break; 821aa221f2eSMasahiro Yamada if (c != 0) 82241f11a4fSYoshinori Sato usage(); 82341f11a4fSYoshinori Sato } 824aa221f2eSMasahiro Yamada 825aa221f2eSMasahiro Yamada if (optind >= argc) 8261da177e4SLinus Torvalds usage(); 8271da177e4SLinus Torvalds 828aa221f2eSMasahiro Yamada read_map(argv[optind]); 8295e5c4fa7SMasahiro Yamada shrink_table(); 830c6bda7c9SRusty Russell if (absolute_percpu) 831c6bda7c9SRusty Russell make_percpus_absolute(); 832f34ea029SMasahiro Yamada sort_symbols(); 8332213e9a6SArd Biesheuvel if (base_relative) 8342213e9a6SArd Biesheuvel record_relative_base(); 8351da177e4SLinus Torvalds optimize_token_table(); 8361da177e4SLinus Torvalds write_src(); 8371da177e4SLinus Torvalds 8381da177e4SLinus Torvalds return 0; 8391da177e4SLinus Torvalds } 840