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