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