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 21aa221f2eSMasahiro Yamada #include <getopt.h> 22a41333e0SMasahiro Yamada #include <stdbool.h> 231da177e4SLinus Torvalds #include <stdio.h> 241da177e4SLinus Torvalds #include <stdlib.h> 251da177e4SLinus Torvalds #include <string.h> 261da177e4SLinus Torvalds #include <ctype.h> 272213e9a6SArd Biesheuvel #include <limits.h> 281da177e4SLinus Torvalds 2917b1f0deSMike Frysinger #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) 3017b1f0deSMike Frysinger 31b471927eSBoqun Feng #define _stringify_1(x) #x 32b471927eSBoqun Feng #define _stringify(x) _stringify_1(x) 33b471927eSBoqun Feng 34b8a94bfbSMiguel Ojeda #define KSYM_NAME_LEN 512 351da177e4SLinus Torvalds 366e8c5bbdSMiguel Ojeda /* 376e8c5bbdSMiguel Ojeda * A substantially bigger size than the current maximum. 386e8c5bbdSMiguel Ojeda * 396e8c5bbdSMiguel Ojeda * It cannot be defined as an expression because it gets stringified 406e8c5bbdSMiguel Ojeda * for the fscanf() format string. Therefore, a _Static_assert() is 416e8c5bbdSMiguel Ojeda * used instead to maintain the relationship with KSYM_NAME_LEN. 426e8c5bbdSMiguel Ojeda */ 43b8a94bfbSMiguel Ojeda #define KSYM_NAME_LEN_BUFFER 2048 446e8c5bbdSMiguel Ojeda _Static_assert( 456e8c5bbdSMiguel Ojeda KSYM_NAME_LEN_BUFFER == KSYM_NAME_LEN * 4, 466e8c5bbdSMiguel Ojeda "Please keep KSYM_NAME_LEN_BUFFER in sync with KSYM_NAME_LEN" 476e8c5bbdSMiguel Ojeda ); 48b471927eSBoqun Feng 491da177e4SLinus Torvalds struct sym_entry { 501da177e4SLinus Torvalds unsigned long long addr; 51b3dbb4ecSPaulo Marques unsigned int len; 52f2df3f65SPaulo Marques unsigned int start_pos; 538c996940SArd Biesheuvel unsigned int percpu_absolute; 549d82973eSLinus Torvalds unsigned char sym[]; 551da177e4SLinus Torvalds }; 561da177e4SLinus Torvalds 5778eb7159SKees Cook struct addr_range { 5878eb7159SKees Cook const char *start_sym, *end_sym; 5917b1f0deSMike Frysinger unsigned long long start, end; 6017b1f0deSMike Frysinger }; 6117b1f0deSMike Frysinger 6217b1f0deSMike Frysinger static unsigned long long _text; 632213e9a6SArd Biesheuvel static unsigned long long relative_base; 6478eb7159SKees Cook static struct addr_range text_ranges[] = { 6517b1f0deSMike Frysinger { "_stext", "_etext" }, 6617b1f0deSMike Frysinger { "_sinittext", "_einittext" }, 6717b1f0deSMike Frysinger }; 6817b1f0deSMike Frysinger #define text_range_text (&text_ranges[0]) 6917b1f0deSMike Frysinger #define text_range_inittext (&text_ranges[1]) 7017b1f0deSMike Frysinger 71c6bda7c9SRusty Russell static struct addr_range percpu_range = { 72c6bda7c9SRusty Russell "__per_cpu_start", "__per_cpu_end", -1ULL, 0 73c6bda7c9SRusty Russell }; 74c6bda7c9SRusty Russell 758d605269SMasahiro Yamada static struct sym_entry **table; 76b3dbb4ecSPaulo Marques static unsigned int table_size, table_cnt; 77831362fcSMasahiro Yamada static int all_symbols; 78831362fcSMasahiro Yamada static int absolute_percpu; 79831362fcSMasahiro Yamada static int base_relative; 801da177e4SLinus Torvalds 81f43e9daaSMasahiro Yamada static int token_profit[0x10000]; 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds /* the table that holds the result of the compression */ 84f43e9daaSMasahiro Yamada static unsigned char best_table[256][2]; 85f43e9daaSMasahiro Yamada static unsigned char best_table_len[256]; 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds 88b3dbb4ecSPaulo Marques static void usage(void) 891da177e4SLinus Torvalds { 908d3a7507SYuntao Wang fprintf(stderr, "Usage: kallsyms [--all-symbols] [--absolute-percpu] " 91aa221f2eSMasahiro Yamada "[--base-relative] in.map > out.S\n"); 921da177e4SLinus Torvalds exit(1); 931da177e4SLinus Torvalds } 941da177e4SLinus Torvalds 9529e55ad3SMasahiro Yamada static char *sym_name(const struct sym_entry *s) 9629e55ad3SMasahiro Yamada { 9729e55ad3SMasahiro Yamada return (char *)s->sym + 1; 9829e55ad3SMasahiro Yamada } 9929e55ad3SMasahiro Yamada 100a41333e0SMasahiro Yamada static bool is_ignored_symbol(const char *name, char type) 101a41333e0SMasahiro Yamada { 102516d980fSMasahiro Yamada /* Symbol names that exactly match to the following are ignored.*/ 103a41333e0SMasahiro Yamada static const char * const ignored_symbols[] = { 104a41333e0SMasahiro Yamada /* 105a41333e0SMasahiro Yamada * Symbols which vary between passes. Passes 1 and 2 must have 106a41333e0SMasahiro Yamada * identical symbol lists. The kallsyms_* symbols below are 107a41333e0SMasahiro Yamada * only added after pass 1, they would be included in pass 2 108a41333e0SMasahiro Yamada * when --all-symbols is specified so exclude them to get a 109a41333e0SMasahiro Yamada * stable symbol list. 110a41333e0SMasahiro Yamada */ 111a41333e0SMasahiro Yamada "kallsyms_addresses", 112a41333e0SMasahiro Yamada "kallsyms_offsets", 113a41333e0SMasahiro Yamada "kallsyms_relative_base", 114a41333e0SMasahiro Yamada "kallsyms_num_syms", 115a41333e0SMasahiro Yamada "kallsyms_names", 116a41333e0SMasahiro Yamada "kallsyms_markers", 117a41333e0SMasahiro Yamada "kallsyms_token_table", 118a41333e0SMasahiro Yamada "kallsyms_token_index", 119a41333e0SMasahiro Yamada /* Exclude linker generated symbols which vary between passes */ 120a41333e0SMasahiro Yamada "_SDA_BASE_", /* ppc */ 121a41333e0SMasahiro Yamada "_SDA2_BASE_", /* ppc */ 122a41333e0SMasahiro Yamada NULL 123a41333e0SMasahiro Yamada }; 124a41333e0SMasahiro Yamada 125516d980fSMasahiro Yamada /* Symbol names that begin with the following are ignored.*/ 126a41333e0SMasahiro Yamada static const char * const ignored_prefixes[] = { 127a41333e0SMasahiro Yamada "__efistub_", /* arm64 EFI stub namespace */ 1286ccf9cb5SKalesh Singh "__kvm_nvhe_$", /* arm64 local symbols in non-VHE KVM namespace */ 1296ccf9cb5SKalesh Singh "__kvm_nvhe_.L", /* arm64 local symbols in non-VHE KVM namespace */ 130efe6e306SArnd Bergmann "__AArch64ADRPThunk_", /* arm64 lld */ 131efe6e306SArnd Bergmann "__ARMV5PILongThunk_", /* arm lld */ 132efe6e306SArnd Bergmann "__ARMV7PILongThunk_", 133efe6e306SArnd Bergmann "__ThumbV7PILongThunk_", 134efe6e306SArnd Bergmann "__LA25Thunk_", /* mips lld */ 135efe6e306SArnd Bergmann "__microLA25Thunk_", 136d0f9562eSSami Tolvanen "__kcfi_typeid_", /* CFI type identifiers */ 137a41333e0SMasahiro Yamada NULL 138a41333e0SMasahiro Yamada }; 139a41333e0SMasahiro Yamada 140516d980fSMasahiro Yamada /* Symbol names that end with the following are ignored.*/ 141a41333e0SMasahiro Yamada static const char * const ignored_suffixes[] = { 142a41333e0SMasahiro Yamada "_from_arm", /* arm */ 143a41333e0SMasahiro Yamada "_from_thumb", /* arm */ 144a41333e0SMasahiro Yamada "_veneer", /* arm */ 145a41333e0SMasahiro Yamada NULL 146a41333e0SMasahiro Yamada }; 147a41333e0SMasahiro Yamada 148516d980fSMasahiro Yamada /* Symbol names that contain the following are ignored.*/ 149516d980fSMasahiro Yamada static const char * const ignored_matches[] = { 150516d980fSMasahiro Yamada ".long_branch.", /* ppc stub */ 151516d980fSMasahiro Yamada ".plt_branch.", /* ppc stub */ 152516d980fSMasahiro Yamada NULL 153516d980fSMasahiro Yamada }; 154516d980fSMasahiro Yamada 155a41333e0SMasahiro Yamada const char * const *p; 156a41333e0SMasahiro Yamada 157a41333e0SMasahiro Yamada for (p = ignored_symbols; *p; p++) 158a41333e0SMasahiro Yamada if (!strcmp(name, *p)) 159a41333e0SMasahiro Yamada return true; 160a41333e0SMasahiro Yamada 161a41333e0SMasahiro Yamada for (p = ignored_prefixes; *p; p++) 162a41333e0SMasahiro Yamada if (!strncmp(name, *p, strlen(*p))) 163a41333e0SMasahiro Yamada return true; 164a41333e0SMasahiro Yamada 165a41333e0SMasahiro Yamada for (p = ignored_suffixes; *p; p++) { 166a41333e0SMasahiro Yamada int l = strlen(name) - strlen(*p); 167a41333e0SMasahiro Yamada 168a41333e0SMasahiro Yamada if (l >= 0 && !strcmp(name + l, *p)) 169a41333e0SMasahiro Yamada return true; 170a41333e0SMasahiro Yamada } 171a41333e0SMasahiro Yamada 172516d980fSMasahiro Yamada for (p = ignored_matches; *p; p++) { 173516d980fSMasahiro Yamada if (strstr(name, *p)) 174516d980fSMasahiro Yamada return true; 175516d980fSMasahiro Yamada } 176516d980fSMasahiro Yamada 177887df76dSMasahiro Yamada if (type == 'U' || type == 'u') 178887df76dSMasahiro Yamada return true; 179887df76dSMasahiro Yamada /* exclude debugging symbols */ 180887df76dSMasahiro Yamada if (type == 'N' || type == 'n') 181887df76dSMasahiro Yamada return true; 182887df76dSMasahiro Yamada 183887df76dSMasahiro Yamada if (toupper(type) == 'A') { 184887df76dSMasahiro Yamada /* Keep these useful absolute symbols */ 185887df76dSMasahiro Yamada if (strcmp(name, "__kernel_syscall_via_break") && 186887df76dSMasahiro Yamada strcmp(name, "__kernel_syscall_via_epc") && 187887df76dSMasahiro Yamada strcmp(name, "__kernel_sigtramp") && 188887df76dSMasahiro Yamada strcmp(name, "__gp")) 189887df76dSMasahiro Yamada return true; 190887df76dSMasahiro Yamada } 191887df76dSMasahiro Yamada 192a41333e0SMasahiro Yamada return false; 193a41333e0SMasahiro Yamada } 194a41333e0SMasahiro Yamada 195b6233d0dSMasahiro Yamada static void check_symbol_range(const char *sym, unsigned long long addr, 19678eb7159SKees Cook struct addr_range *ranges, int entries) 19717b1f0deSMike Frysinger { 19817b1f0deSMike Frysinger size_t i; 19978eb7159SKees Cook struct addr_range *ar; 20017b1f0deSMike Frysinger 20178eb7159SKees Cook for (i = 0; i < entries; ++i) { 20278eb7159SKees Cook ar = &ranges[i]; 20317b1f0deSMike Frysinger 20478eb7159SKees Cook if (strcmp(sym, ar->start_sym) == 0) { 20578eb7159SKees Cook ar->start = addr; 206b6233d0dSMasahiro Yamada return; 20778eb7159SKees Cook } else if (strcmp(sym, ar->end_sym) == 0) { 20878eb7159SKees Cook ar->end = addr; 209b6233d0dSMasahiro Yamada return; 21017b1f0deSMike Frysinger } 21117b1f0deSMike Frysinger } 21217b1f0deSMike Frysinger } 21317b1f0deSMike Frysinger 2148d605269SMasahiro Yamada static struct sym_entry *read_symbol(FILE *in) 2151da177e4SLinus Torvalds { 216b471927eSBoqun Feng char name[KSYM_NAME_LEN_BUFFER+1], type; 2178d605269SMasahiro Yamada unsigned long long addr; 2188d605269SMasahiro Yamada unsigned int len; 2198d605269SMasahiro Yamada struct sym_entry *sym; 2201da177e4SLinus Torvalds int rc; 2211da177e4SLinus Torvalds 222b471927eSBoqun Feng rc = fscanf(in, "%llx %c %" _stringify(KSYM_NAME_LEN_BUFFER) "s\n", &addr, &type, name); 2231da177e4SLinus Torvalds if (rc != 3) { 224b66c874fSBoqun Feng if (rc != EOF && fgets(name, ARRAY_SIZE(name), in) == NULL) 225ef894870SJean Sacren fprintf(stderr, "Read error or end of file.\n"); 2268d605269SMasahiro Yamada return NULL; 2271da177e4SLinus Torvalds } 228be9f6133SMasahiro Yamada if (strlen(name) >= KSYM_NAME_LEN) { 2296db2983cSEugene Loh fprintf(stderr, "Symbol %s too long for kallsyms (%zu >= %d).\n" 230f3462aa9SAndi Kleen "Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n", 231be9f6133SMasahiro Yamada name, strlen(name), KSYM_NAME_LEN); 2328d605269SMasahiro Yamada return NULL; 233f3462aa9SAndi Kleen } 2341da177e4SLinus Torvalds 235be9f6133SMasahiro Yamada if (strcmp(name, "_text") == 0) 2368d605269SMasahiro Yamada _text = addr; 237b6233d0dSMasahiro Yamada 2387883a143SMikhail Petrov /* Ignore most absolute/undefined (?) symbols. */ 2397883a143SMikhail Petrov if (is_ignored_symbol(name, type)) 2407883a143SMikhail Petrov return NULL; 2417883a143SMikhail Petrov 2428d605269SMasahiro Yamada check_symbol_range(name, addr, text_ranges, ARRAY_SIZE(text_ranges)); 2438d605269SMasahiro Yamada check_symbol_range(name, addr, &percpu_range, 1); 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds /* include the type field in the symbol name, so that it gets 2461da177e4SLinus Torvalds * compressed together */ 2478d605269SMasahiro Yamada 2488d605269SMasahiro Yamada len = strlen(name) + 1; 2498d605269SMasahiro Yamada 2509d1b3895SMasahiro Yamada sym = malloc(sizeof(*sym) + len + 1); 2518d605269SMasahiro Yamada if (!sym) { 252f1a136e0SJesper Juhl fprintf(stderr, "kallsyms failure: " 253f1a136e0SJesper Juhl "unable to allocate required amount of memory\n"); 254f1a136e0SJesper Juhl exit(EXIT_FAILURE); 255f1a136e0SJesper Juhl } 2568d605269SMasahiro Yamada sym->addr = addr; 2578d605269SMasahiro Yamada sym->len = len; 2588d605269SMasahiro Yamada sym->sym[0] = type; 2599d1b3895SMasahiro Yamada strcpy(sym_name(sym), name); 2608d605269SMasahiro Yamada sym->percpu_absolute = 0; 2611da177e4SLinus Torvalds 2628d605269SMasahiro Yamada return sym; 2631da177e4SLinus Torvalds } 2641da177e4SLinus Torvalds 2654bfe2b78SMasahiro Yamada static int symbol_in_range(const struct sym_entry *s, 2664bfe2b78SMasahiro Yamada const struct addr_range *ranges, int entries) 26717b1f0deSMike Frysinger { 26817b1f0deSMike Frysinger size_t i; 2694bfe2b78SMasahiro Yamada const struct addr_range *ar; 27017b1f0deSMike Frysinger 27178eb7159SKees Cook for (i = 0; i < entries; ++i) { 27278eb7159SKees Cook ar = &ranges[i]; 27317b1f0deSMike Frysinger 27478eb7159SKees Cook if (s->addr >= ar->start && s->addr <= ar->end) 275ac6ca5c8SMike Frysinger return 1; 27617b1f0deSMike Frysinger } 27717b1f0deSMike Frysinger 278ac6ca5c8SMike Frysinger return 0; 27917b1f0deSMike Frysinger } 28017b1f0deSMike Frysinger 2814bfe2b78SMasahiro Yamada static int symbol_valid(const struct sym_entry *s) 2821da177e4SLinus Torvalds { 28329e55ad3SMasahiro Yamada const char *name = sym_name(s); 284bd8b22d2SArd Biesheuvel 2851da177e4SLinus Torvalds /* if --all-symbols is not specified, then symbols outside the text 2861da177e4SLinus Torvalds * and inittext sections are discarded */ 2871da177e4SLinus Torvalds if (!all_symbols) { 28878eb7159SKees Cook if (symbol_in_range(s, text_ranges, 28978eb7159SKees Cook ARRAY_SIZE(text_ranges)) == 0) 2901da177e4SLinus Torvalds return 0; 2911da177e4SLinus Torvalds /* Corner case. Discard any symbols with the same value as 292a3b81113SRobin Getz * _etext _einittext; they can move between pass 1 and 2 when 293a3b81113SRobin Getz * the kallsyms data are added. If these symbols move then 294a3b81113SRobin Getz * they may get dropped in pass 2, which breaks the kallsyms 295a3b81113SRobin Getz * rules. 2961da177e4SLinus Torvalds */ 29717b1f0deSMike Frysinger if ((s->addr == text_range_text->end && 29829e55ad3SMasahiro Yamada strcmp(name, text_range_text->end_sym)) || 29917b1f0deSMike Frysinger (s->addr == text_range_inittext->end && 30029e55ad3SMasahiro Yamada strcmp(name, text_range_inittext->end_sym))) 3011da177e4SLinus Torvalds return 0; 3021da177e4SLinus Torvalds } 3031da177e4SLinus Torvalds 3041da177e4SLinus Torvalds return 1; 3051da177e4SLinus Torvalds } 3061da177e4SLinus Torvalds 3075e5c4fa7SMasahiro Yamada /* remove all the invalid symbols from the table */ 3085e5c4fa7SMasahiro Yamada static void shrink_table(void) 3095e5c4fa7SMasahiro Yamada { 3105e5c4fa7SMasahiro Yamada unsigned int i, pos; 3115e5c4fa7SMasahiro Yamada 3125e5c4fa7SMasahiro Yamada pos = 0; 3135e5c4fa7SMasahiro Yamada for (i = 0; i < table_cnt; i++) { 3148d605269SMasahiro Yamada if (symbol_valid(table[i])) { 3155e5c4fa7SMasahiro Yamada if (pos != i) 3165e5c4fa7SMasahiro Yamada table[pos] = table[i]; 3175e5c4fa7SMasahiro Yamada pos++; 3185e5c4fa7SMasahiro Yamada } else { 3198d605269SMasahiro Yamada free(table[i]); 3205e5c4fa7SMasahiro Yamada } 3215e5c4fa7SMasahiro Yamada } 3225e5c4fa7SMasahiro Yamada table_cnt = pos; 3235e5c4fa7SMasahiro Yamada 3245e5c4fa7SMasahiro Yamada /* When valid symbol is not registered, exit to error */ 3255e5c4fa7SMasahiro Yamada if (!table_cnt) { 3265e5c4fa7SMasahiro Yamada fprintf(stderr, "No valid symbol.\n"); 3275e5c4fa7SMasahiro Yamada exit(1); 3285e5c4fa7SMasahiro Yamada } 3295e5c4fa7SMasahiro Yamada } 3305e5c4fa7SMasahiro Yamada 331aa221f2eSMasahiro Yamada static void read_map(const char *in) 3321da177e4SLinus Torvalds { 333aa221f2eSMasahiro Yamada FILE *fp; 3348d605269SMasahiro Yamada struct sym_entry *sym; 3358d605269SMasahiro Yamada 336aa221f2eSMasahiro Yamada fp = fopen(in, "r"); 337aa221f2eSMasahiro Yamada if (!fp) { 338aa221f2eSMasahiro Yamada perror(in); 339aa221f2eSMasahiro Yamada exit(1); 340aa221f2eSMasahiro Yamada } 341aa221f2eSMasahiro Yamada 342aa221f2eSMasahiro Yamada while (!feof(fp)) { 343aa221f2eSMasahiro Yamada sym = read_symbol(fp); 3448d605269SMasahiro Yamada if (!sym) 3458d605269SMasahiro Yamada continue; 3468d605269SMasahiro Yamada 3478d605269SMasahiro Yamada sym->start_pos = table_cnt; 3488d605269SMasahiro Yamada 349b3dbb4ecSPaulo Marques if (table_cnt >= table_size) { 350b3dbb4ecSPaulo Marques table_size += 10000; 351b3dbb4ecSPaulo Marques table = realloc(table, sizeof(*table) * table_size); 3521da177e4SLinus Torvalds if (!table) { 3531da177e4SLinus Torvalds fprintf(stderr, "out of memory\n"); 354aa221f2eSMasahiro Yamada fclose(fp); 3551da177e4SLinus Torvalds exit (1); 3561da177e4SLinus Torvalds } 3571da177e4SLinus Torvalds } 3588d605269SMasahiro Yamada 3598d605269SMasahiro Yamada table[table_cnt++] = sym; 3601da177e4SLinus Torvalds } 361aa221f2eSMasahiro Yamada 362aa221f2eSMasahiro Yamada fclose(fp); 363f2df3f65SPaulo Marques } 3641da177e4SLinus Torvalds 3654bfe2b78SMasahiro Yamada static void output_label(const char *label) 3661da177e4SLinus Torvalds { 3671da177e4SLinus Torvalds printf(".globl %s\n", label); 3681da177e4SLinus Torvalds printf("\tALGN\n"); 3691da177e4SLinus Torvalds printf("%s:\n", label); 3701da177e4SLinus Torvalds } 3711da177e4SLinus Torvalds 372fd2ab2f6SMasahiro Yamada /* Provide proper symbols relocatability by their '_text' relativeness. */ 373fd2ab2f6SMasahiro Yamada static void output_address(unsigned long long addr) 374fd2ab2f6SMasahiro Yamada { 375fd2ab2f6SMasahiro Yamada if (_text <= addr) 376fd2ab2f6SMasahiro Yamada printf("\tPTR\t_text + %#llx\n", addr - _text); 377fd2ab2f6SMasahiro Yamada else 378fd2ab2f6SMasahiro Yamada printf("\tPTR\t_text - %#llx\n", _text - addr); 379fd2ab2f6SMasahiro Yamada } 380fd2ab2f6SMasahiro Yamada 3811da177e4SLinus Torvalds /* uncompress a compressed symbol. When this function is called, the best table 3821da177e4SLinus Torvalds * might still be compressed itself, so the function needs to be recursive */ 3834bfe2b78SMasahiro Yamada static int expand_symbol(const unsigned char *data, int len, char *result) 3841da177e4SLinus Torvalds { 3851da177e4SLinus Torvalds int c, rlen, total=0; 3861da177e4SLinus Torvalds 3871da177e4SLinus Torvalds while (len) { 3881da177e4SLinus Torvalds c = *data; 3891da177e4SLinus Torvalds /* if the table holds a single char that is the same as the one 3901da177e4SLinus Torvalds * we are looking for, then end the search */ 3911da177e4SLinus Torvalds if (best_table[c][0]==c && best_table_len[c]==1) { 3921da177e4SLinus Torvalds *result++ = c; 3931da177e4SLinus Torvalds total++; 3941da177e4SLinus Torvalds } else { 3951da177e4SLinus Torvalds /* if not, recurse and expand */ 3961da177e4SLinus Torvalds rlen = expand_symbol(best_table[c], best_table_len[c], result); 3971da177e4SLinus Torvalds total += rlen; 3981da177e4SLinus Torvalds result += rlen; 3991da177e4SLinus Torvalds } 4001da177e4SLinus Torvalds data++; 4011da177e4SLinus Torvalds len--; 4021da177e4SLinus Torvalds } 4031da177e4SLinus Torvalds *result=0; 4041da177e4SLinus Torvalds 4051da177e4SLinus Torvalds return total; 4061da177e4SLinus Torvalds } 4071da177e4SLinus Torvalds 4084bfe2b78SMasahiro Yamada static int symbol_absolute(const struct sym_entry *s) 40978eb7159SKees Cook { 4108c996940SArd Biesheuvel return s->percpu_absolute; 41178eb7159SKees Cook } 41278eb7159SKees Cook 413b3dbb4ecSPaulo Marques static void write_src(void) 4141da177e4SLinus Torvalds { 415b3dbb4ecSPaulo Marques unsigned int i, k, off; 4161da177e4SLinus Torvalds unsigned int best_idx[256]; 4171da177e4SLinus Torvalds unsigned int *markers; 4189281aceaSTejun Heo char buf[KSYM_NAME_LEN]; 4191da177e4SLinus Torvalds 420500193ecSMasahiro Yamada printf("#include <asm/bitsperlong.h>\n"); 4211da177e4SLinus Torvalds printf("#if BITS_PER_LONG == 64\n"); 4221da177e4SLinus Torvalds printf("#define PTR .quad\n"); 42372d3ebb9SMathias Krause printf("#define ALGN .balign 8\n"); 4241da177e4SLinus Torvalds printf("#else\n"); 4251da177e4SLinus Torvalds printf("#define PTR .long\n"); 42672d3ebb9SMathias Krause printf("#define ALGN .balign 4\n"); 4271da177e4SLinus Torvalds printf("#endif\n"); 4281da177e4SLinus Torvalds 429aad09470SJan Beulich printf("\t.section .rodata, \"a\"\n"); 4301da177e4SLinus Torvalds 4312213e9a6SArd Biesheuvel if (!base_relative) 4321da177e4SLinus Torvalds output_label("kallsyms_addresses"); 4332213e9a6SArd Biesheuvel else 4342213e9a6SArd Biesheuvel output_label("kallsyms_offsets"); 4352213e9a6SArd Biesheuvel 436b3dbb4ecSPaulo Marques for (i = 0; i < table_cnt; i++) { 4372213e9a6SArd Biesheuvel if (base_relative) { 438fd2ab2f6SMasahiro Yamada /* 439fd2ab2f6SMasahiro Yamada * Use the offset relative to the lowest value 440fd2ab2f6SMasahiro Yamada * encountered of all relative symbols, and emit 441fd2ab2f6SMasahiro Yamada * non-relocatable fixed offsets that will be fixed 442fd2ab2f6SMasahiro Yamada * up at runtime. 443fd2ab2f6SMasahiro Yamada */ 444fd2ab2f6SMasahiro Yamada 4452213e9a6SArd Biesheuvel long long offset; 4462213e9a6SArd Biesheuvel int overflow; 4472213e9a6SArd Biesheuvel 4482213e9a6SArd Biesheuvel if (!absolute_percpu) { 4498d605269SMasahiro Yamada offset = table[i]->addr - relative_base; 4502213e9a6SArd Biesheuvel overflow = (offset < 0 || offset > UINT_MAX); 4518d605269SMasahiro Yamada } else if (symbol_absolute(table[i])) { 4528d605269SMasahiro Yamada offset = table[i]->addr; 4532213e9a6SArd Biesheuvel overflow = (offset < 0 || offset > INT_MAX); 4542213e9a6SArd Biesheuvel } else { 4558d605269SMasahiro Yamada offset = relative_base - table[i]->addr - 1; 4562213e9a6SArd Biesheuvel overflow = (offset < INT_MIN || offset >= 0); 4572213e9a6SArd Biesheuvel } 4582213e9a6SArd Biesheuvel if (overflow) { 4592213e9a6SArd Biesheuvel fprintf(stderr, "kallsyms failure: " 4602213e9a6SArd Biesheuvel "%s symbol value %#llx out of range in relative mode\n", 4618d605269SMasahiro Yamada symbol_absolute(table[i]) ? "absolute" : "relative", 4628d605269SMasahiro Yamada table[i]->addr); 4632213e9a6SArd Biesheuvel exit(EXIT_FAILURE); 4642213e9a6SArd Biesheuvel } 4652213e9a6SArd Biesheuvel printf("\t.long\t%#x\n", (int)offset); 4668d605269SMasahiro Yamada } else if (!symbol_absolute(table[i])) { 4678d605269SMasahiro Yamada output_address(table[i]->addr); 468fd593d12SEric W. Biederman } else { 4698d605269SMasahiro Yamada printf("\tPTR\t%#llx\n", table[i]->addr); 4701da177e4SLinus Torvalds } 471fd593d12SEric W. Biederman } 4721da177e4SLinus Torvalds printf("\n"); 4731da177e4SLinus Torvalds 4742213e9a6SArd Biesheuvel if (base_relative) { 4752213e9a6SArd Biesheuvel output_label("kallsyms_relative_base"); 476fd2ab2f6SMasahiro Yamada output_address(relative_base); 4772213e9a6SArd Biesheuvel printf("\n"); 4782213e9a6SArd Biesheuvel } 4792213e9a6SArd Biesheuvel 4801da177e4SLinus Torvalds output_label("kallsyms_num_syms"); 48180ffbaa5SJan Beulich printf("\t.long\t%u\n", table_cnt); 4821da177e4SLinus Torvalds printf("\n"); 4831da177e4SLinus Torvalds 4841da177e4SLinus Torvalds /* table of offset markers, that give the offset in the compressed stream 4851da177e4SLinus Torvalds * every 256 symbols */ 486f1a136e0SJesper Juhl markers = malloc(sizeof(unsigned int) * ((table_cnt + 255) / 256)); 487f1a136e0SJesper Juhl if (!markers) { 488f1a136e0SJesper Juhl fprintf(stderr, "kallsyms failure: " 489f1a136e0SJesper Juhl "unable to allocate required memory\n"); 490f1a136e0SJesper Juhl exit(EXIT_FAILURE); 491f1a136e0SJesper Juhl } 4921da177e4SLinus Torvalds 4931da177e4SLinus Torvalds output_label("kallsyms_names"); 4941da177e4SLinus Torvalds off = 0; 495b3dbb4ecSPaulo Marques for (i = 0; i < table_cnt; i++) { 496b3dbb4ecSPaulo Marques if ((i & 0xFF) == 0) 497b3dbb4ecSPaulo Marques markers[i >> 8] = off; 4981da177e4SLinus Torvalds 49973bbb944SMiguel Ojeda /* There cannot be any symbol of length zero. */ 50073bbb944SMiguel Ojeda if (table[i]->len == 0) { 50173bbb944SMiguel Ojeda fprintf(stderr, "kallsyms failure: " 50273bbb944SMiguel Ojeda "unexpected zero symbol length\n"); 50373bbb944SMiguel Ojeda exit(EXIT_FAILURE); 50473bbb944SMiguel Ojeda } 50573bbb944SMiguel Ojeda 50673bbb944SMiguel Ojeda /* Only lengths that fit in up-to-two-byte ULEB128 are supported. */ 50773bbb944SMiguel Ojeda if (table[i]->len > 0x3FFF) { 50873bbb944SMiguel Ojeda fprintf(stderr, "kallsyms failure: " 50973bbb944SMiguel Ojeda "unexpected huge symbol length\n"); 51073bbb944SMiguel Ojeda exit(EXIT_FAILURE); 51173bbb944SMiguel Ojeda } 51273bbb944SMiguel Ojeda 51373bbb944SMiguel Ojeda /* Encode length with ULEB128. */ 51473bbb944SMiguel Ojeda if (table[i]->len <= 0x7F) { 51573bbb944SMiguel Ojeda /* Most symbols use a single byte for the length. */ 5168d605269SMasahiro Yamada printf("\t.byte 0x%02x", table[i]->len); 51773bbb944SMiguel Ojeda off += table[i]->len + 1; 51873bbb944SMiguel Ojeda } else { 51973bbb944SMiguel Ojeda /* "Big" symbols use two bytes. */ 52073bbb944SMiguel Ojeda printf("\t.byte 0x%02x, 0x%02x", 52173bbb944SMiguel Ojeda (table[i]->len & 0x7F) | 0x80, 52273bbb944SMiguel Ojeda (table[i]->len >> 7) & 0x7F); 52373bbb944SMiguel Ojeda off += table[i]->len + 2; 52473bbb944SMiguel Ojeda } 5258d605269SMasahiro Yamada for (k = 0; k < table[i]->len; k++) 5268d605269SMasahiro Yamada printf(", 0x%02x", table[i]->sym[k]); 5271da177e4SLinus Torvalds printf("\n"); 5281da177e4SLinus Torvalds } 5291da177e4SLinus Torvalds printf("\n"); 5301da177e4SLinus Torvalds 5311da177e4SLinus Torvalds output_label("kallsyms_markers"); 532b3dbb4ecSPaulo Marques for (i = 0; i < ((table_cnt + 255) >> 8); i++) 53380ffbaa5SJan Beulich printf("\t.long\t%u\n", markers[i]); 5341da177e4SLinus Torvalds printf("\n"); 5351da177e4SLinus Torvalds 5361da177e4SLinus Torvalds free(markers); 5371da177e4SLinus Torvalds 5381da177e4SLinus Torvalds output_label("kallsyms_token_table"); 5391da177e4SLinus Torvalds off = 0; 5401da177e4SLinus Torvalds for (i = 0; i < 256; i++) { 5411da177e4SLinus Torvalds best_idx[i] = off; 5421da177e4SLinus Torvalds expand_symbol(best_table[i], best_table_len[i], buf); 5431da177e4SLinus Torvalds printf("\t.asciz\t\"%s\"\n", buf); 5441da177e4SLinus Torvalds off += strlen(buf) + 1; 5451da177e4SLinus Torvalds } 5461da177e4SLinus Torvalds printf("\n"); 5471da177e4SLinus Torvalds 5481da177e4SLinus Torvalds output_label("kallsyms_token_index"); 5491da177e4SLinus Torvalds for (i = 0; i < 256; i++) 5501da177e4SLinus Torvalds printf("\t.short\t%d\n", best_idx[i]); 5511da177e4SLinus Torvalds printf("\n"); 5521da177e4SLinus Torvalds } 5531da177e4SLinus Torvalds 5541da177e4SLinus Torvalds 5551da177e4SLinus Torvalds /* table lookup compression functions */ 5561da177e4SLinus Torvalds 5571da177e4SLinus Torvalds /* count all the possible tokens in a symbol */ 5584bfe2b78SMasahiro Yamada static void learn_symbol(const unsigned char *symbol, int len) 5591da177e4SLinus Torvalds { 5601da177e4SLinus Torvalds int i; 5611da177e4SLinus Torvalds 5621da177e4SLinus Torvalds for (i = 0; i < len - 1; i++) 563b3dbb4ecSPaulo Marques token_profit[ symbol[i] + (symbol[i + 1] << 8) ]++; 5641da177e4SLinus Torvalds } 5651da177e4SLinus Torvalds 5661da177e4SLinus Torvalds /* decrease the count for all the possible tokens in a symbol */ 5674bfe2b78SMasahiro Yamada static void forget_symbol(const unsigned char *symbol, int len) 5681da177e4SLinus Torvalds { 5691da177e4SLinus Torvalds int i; 5701da177e4SLinus Torvalds 5711da177e4SLinus Torvalds for (i = 0; i < len - 1; i++) 572b3dbb4ecSPaulo Marques token_profit[ symbol[i] + (symbol[i + 1] << 8) ]--; 5731da177e4SLinus Torvalds } 5741da177e4SLinus Torvalds 5755e5c4fa7SMasahiro Yamada /* do the initial token count */ 576*fcdf7197SZhen Lei static void build_initial_token_table(void) 5771da177e4SLinus Torvalds { 5785e5c4fa7SMasahiro Yamada unsigned int i; 5791da177e4SLinus Torvalds 5805e5c4fa7SMasahiro Yamada for (i = 0; i < table_cnt; i++) 5818d605269SMasahiro Yamada learn_symbol(table[i]->sym, table[i]->len); 5821da177e4SLinus Torvalds } 5831da177e4SLinus Torvalds 5842558c138SMasahiro Yamada static unsigned char *find_token(unsigned char *str, int len, 5854bfe2b78SMasahiro Yamada const unsigned char *token) 5867c5d249aSPaulo Marques { 5877c5d249aSPaulo Marques int i; 5887c5d249aSPaulo Marques 5897c5d249aSPaulo Marques for (i = 0; i < len - 1; i++) { 5907c5d249aSPaulo Marques if (str[i] == token[0] && str[i+1] == token[1]) 5917c5d249aSPaulo Marques return &str[i]; 5927c5d249aSPaulo Marques } 5937c5d249aSPaulo Marques return NULL; 5947c5d249aSPaulo Marques } 5957c5d249aSPaulo Marques 5961da177e4SLinus Torvalds /* replace a given token in all the valid symbols. Use the sampled symbols 5971da177e4SLinus Torvalds * to update the counts */ 5984bfe2b78SMasahiro Yamada static void compress_symbols(const unsigned char *str, int idx) 5991da177e4SLinus Torvalds { 600b3dbb4ecSPaulo Marques unsigned int i, len, size; 601b3dbb4ecSPaulo Marques unsigned char *p1, *p2; 6021da177e4SLinus Torvalds 603b3dbb4ecSPaulo Marques for (i = 0; i < table_cnt; i++) { 6041da177e4SLinus Torvalds 6058d605269SMasahiro Yamada len = table[i]->len; 6068d605269SMasahiro Yamada p1 = table[i]->sym; 607b3dbb4ecSPaulo Marques 608b3dbb4ecSPaulo Marques /* find the token on the symbol */ 6097c5d249aSPaulo Marques p2 = find_token(p1, len, str); 610b3dbb4ecSPaulo Marques if (!p2) continue; 611b3dbb4ecSPaulo Marques 612b3dbb4ecSPaulo Marques /* decrease the counts for this symbol's tokens */ 6138d605269SMasahiro Yamada forget_symbol(table[i]->sym, len); 614b3dbb4ecSPaulo Marques 615b3dbb4ecSPaulo Marques size = len; 6161da177e4SLinus Torvalds 6171da177e4SLinus Torvalds do { 618b3dbb4ecSPaulo Marques *p2 = idx; 619b3dbb4ecSPaulo Marques p2++; 620b3dbb4ecSPaulo Marques size -= (p2 - p1); 621b3dbb4ecSPaulo Marques memmove(p2, p2 + 1, size); 622b3dbb4ecSPaulo Marques p1 = p2; 623b3dbb4ecSPaulo Marques len--; 624b3dbb4ecSPaulo Marques 625b3dbb4ecSPaulo Marques if (size < 2) break; 626b3dbb4ecSPaulo Marques 6271da177e4SLinus Torvalds /* find the token on the symbol */ 6287c5d249aSPaulo Marques p2 = find_token(p1, size, str); 6291da177e4SLinus Torvalds 630b3dbb4ecSPaulo Marques } while (p2); 6311da177e4SLinus Torvalds 6328d605269SMasahiro Yamada table[i]->len = len; 633b3dbb4ecSPaulo Marques 634b3dbb4ecSPaulo Marques /* increase the counts for this symbol's new tokens */ 6358d605269SMasahiro Yamada learn_symbol(table[i]->sym, len); 6361da177e4SLinus Torvalds } 6371da177e4SLinus Torvalds } 6381da177e4SLinus Torvalds 6391da177e4SLinus Torvalds /* search the token with the maximum profit */ 640b3dbb4ecSPaulo Marques static int find_best_token(void) 6411da177e4SLinus Torvalds { 642b3dbb4ecSPaulo Marques int i, best, bestprofit; 6431da177e4SLinus Torvalds 6441da177e4SLinus Torvalds bestprofit=-10000; 645b3dbb4ecSPaulo Marques best = 0; 6461da177e4SLinus Torvalds 647b3dbb4ecSPaulo Marques for (i = 0; i < 0x10000; i++) { 648b3dbb4ecSPaulo Marques if (token_profit[i] > bestprofit) { 649b3dbb4ecSPaulo Marques best = i; 650b3dbb4ecSPaulo Marques bestprofit = token_profit[i]; 6511da177e4SLinus Torvalds } 6521da177e4SLinus Torvalds } 6531da177e4SLinus Torvalds return best; 6541da177e4SLinus Torvalds } 6551da177e4SLinus Torvalds 6561da177e4SLinus Torvalds /* this is the core of the algorithm: calculate the "best" table */ 6571da177e4SLinus Torvalds static void optimize_result(void) 6581da177e4SLinus Torvalds { 659b3dbb4ecSPaulo Marques int i, best; 6601da177e4SLinus Torvalds 6611da177e4SLinus Torvalds /* using the '\0' symbol last allows compress_symbols to use standard 6621da177e4SLinus Torvalds * fast string functions */ 6631da177e4SLinus Torvalds for (i = 255; i >= 0; i--) { 6641da177e4SLinus Torvalds 6651da177e4SLinus Torvalds /* if this table slot is empty (it is not used by an actual 6661da177e4SLinus Torvalds * original char code */ 6671da177e4SLinus Torvalds if (!best_table_len[i]) { 6681da177e4SLinus Torvalds 669cbf7a90eSCao jin /* find the token with the best profit value */ 6701da177e4SLinus Torvalds best = find_best_token(); 671e0a04b11SXiaochen Wang if (token_profit[best] == 0) 672e0a04b11SXiaochen Wang break; 6731da177e4SLinus Torvalds 6741da177e4SLinus Torvalds /* place it in the "best" table */ 675b3dbb4ecSPaulo Marques best_table_len[i] = 2; 676b3dbb4ecSPaulo Marques best_table[i][0] = best & 0xFF; 677b3dbb4ecSPaulo Marques best_table[i][1] = (best >> 8) & 0xFF; 6781da177e4SLinus Torvalds 6791da177e4SLinus Torvalds /* replace this token in all the valid symbols */ 680b3dbb4ecSPaulo Marques compress_symbols(best_table[i], i); 6811da177e4SLinus Torvalds } 6821da177e4SLinus Torvalds } 6831da177e4SLinus Torvalds } 6841da177e4SLinus Torvalds 6851da177e4SLinus Torvalds /* start by placing the symbols that are actually used on the table */ 6861da177e4SLinus Torvalds static void insert_real_symbols_in_table(void) 6871da177e4SLinus Torvalds { 688b3dbb4ecSPaulo Marques unsigned int i, j, c; 6891da177e4SLinus Torvalds 690b3dbb4ecSPaulo Marques for (i = 0; i < table_cnt; i++) { 6918d605269SMasahiro Yamada for (j = 0; j < table[i]->len; j++) { 6928d605269SMasahiro Yamada c = table[i]->sym[j]; 6931da177e4SLinus Torvalds best_table[c][0]=c; 6941da177e4SLinus Torvalds best_table_len[c]=1; 6951da177e4SLinus Torvalds } 6961da177e4SLinus Torvalds } 6971da177e4SLinus Torvalds } 6981da177e4SLinus Torvalds 6991da177e4SLinus Torvalds static void optimize_token_table(void) 7001da177e4SLinus Torvalds { 701*fcdf7197SZhen Lei build_initial_token_table(); 7021da177e4SLinus Torvalds 7031da177e4SLinus Torvalds insert_real_symbols_in_table(); 7041da177e4SLinus Torvalds 7051da177e4SLinus Torvalds optimize_result(); 7061da177e4SLinus Torvalds } 7071da177e4SLinus Torvalds 708b478b782SLai Jiangshan /* guess for "linker script provide" symbol */ 709b478b782SLai Jiangshan static int may_be_linker_script_provide_symbol(const struct sym_entry *se) 710b478b782SLai Jiangshan { 71129e55ad3SMasahiro Yamada const char *symbol = sym_name(se); 712b478b782SLai Jiangshan int len = se->len - 1; 713b478b782SLai Jiangshan 714b478b782SLai Jiangshan if (len < 8) 715b478b782SLai Jiangshan return 0; 716b478b782SLai Jiangshan 717b478b782SLai Jiangshan if (symbol[0] != '_' || symbol[1] != '_') 718b478b782SLai Jiangshan return 0; 719b478b782SLai Jiangshan 720b478b782SLai Jiangshan /* __start_XXXXX */ 721b478b782SLai Jiangshan if (!memcmp(symbol + 2, "start_", 6)) 722b478b782SLai Jiangshan return 1; 723b478b782SLai Jiangshan 724b478b782SLai Jiangshan /* __stop_XXXXX */ 725b478b782SLai Jiangshan if (!memcmp(symbol + 2, "stop_", 5)) 726b478b782SLai Jiangshan return 1; 727b478b782SLai Jiangshan 728b478b782SLai Jiangshan /* __end_XXXXX */ 729b478b782SLai Jiangshan if (!memcmp(symbol + 2, "end_", 4)) 730b478b782SLai Jiangshan return 1; 731b478b782SLai Jiangshan 732b478b782SLai Jiangshan /* __XXXXX_start */ 733b478b782SLai Jiangshan if (!memcmp(symbol + len - 6, "_start", 6)) 734b478b782SLai Jiangshan return 1; 735b478b782SLai Jiangshan 736b478b782SLai Jiangshan /* __XXXXX_end */ 737b478b782SLai Jiangshan if (!memcmp(symbol + len - 4, "_end", 4)) 738b478b782SLai Jiangshan return 1; 739b478b782SLai Jiangshan 740b478b782SLai Jiangshan return 0; 741b478b782SLai Jiangshan } 742b478b782SLai Jiangshan 743f2df3f65SPaulo Marques static int compare_symbols(const void *a, const void *b) 744f2df3f65SPaulo Marques { 7458d605269SMasahiro Yamada const struct sym_entry *sa = *(const struct sym_entry **)a; 7468d605269SMasahiro Yamada const struct sym_entry *sb = *(const struct sym_entry **)b; 747f2df3f65SPaulo Marques int wa, wb; 748f2df3f65SPaulo Marques 749f2df3f65SPaulo Marques /* sort by address first */ 750f2df3f65SPaulo Marques if (sa->addr > sb->addr) 751f2df3f65SPaulo Marques return 1; 752f2df3f65SPaulo Marques if (sa->addr < sb->addr) 753f2df3f65SPaulo Marques return -1; 754f2df3f65SPaulo Marques 755f2df3f65SPaulo Marques /* sort by "weakness" type */ 756f2df3f65SPaulo Marques wa = (sa->sym[0] == 'w') || (sa->sym[0] == 'W'); 757f2df3f65SPaulo Marques wb = (sb->sym[0] == 'w') || (sb->sym[0] == 'W'); 758f2df3f65SPaulo Marques if (wa != wb) 759f2df3f65SPaulo Marques return wa - wb; 760f2df3f65SPaulo Marques 761b478b782SLai Jiangshan /* sort by "linker script provide" type */ 762b478b782SLai Jiangshan wa = may_be_linker_script_provide_symbol(sa); 763b478b782SLai Jiangshan wb = may_be_linker_script_provide_symbol(sb); 764b478b782SLai Jiangshan if (wa != wb) 765b478b782SLai Jiangshan return wa - wb; 766b478b782SLai Jiangshan 767b478b782SLai Jiangshan /* sort by the number of prefix underscores */ 768aa915245SMasahiro Yamada wa = strspn(sym_name(sa), "_"); 769aa915245SMasahiro Yamada wb = strspn(sym_name(sb), "_"); 770b478b782SLai Jiangshan if (wa != wb) 771b478b782SLai Jiangshan return wa - wb; 772b478b782SLai Jiangshan 773f2df3f65SPaulo Marques /* sort by initial order, so that other symbols are left undisturbed */ 774f2df3f65SPaulo Marques return sa->start_pos - sb->start_pos; 775f2df3f65SPaulo Marques } 776f2df3f65SPaulo Marques 777f2df3f65SPaulo Marques static void sort_symbols(void) 778f2df3f65SPaulo Marques { 7798d605269SMasahiro Yamada qsort(table, table_cnt, sizeof(table[0]), compare_symbols); 780f2df3f65SPaulo Marques } 7811da177e4SLinus Torvalds 782c6bda7c9SRusty Russell static void make_percpus_absolute(void) 783c6bda7c9SRusty Russell { 784c6bda7c9SRusty Russell unsigned int i; 785c6bda7c9SRusty Russell 786c6bda7c9SRusty Russell for (i = 0; i < table_cnt; i++) 7878d605269SMasahiro Yamada if (symbol_in_range(table[i], &percpu_range, 1)) { 7888c996940SArd Biesheuvel /* 7898c996940SArd Biesheuvel * Keep the 'A' override for percpu symbols to 7908c996940SArd Biesheuvel * ensure consistent behavior compared to older 7918c996940SArd Biesheuvel * versions of this tool. 7928c996940SArd Biesheuvel */ 7938d605269SMasahiro Yamada table[i]->sym[0] = 'A'; 7948d605269SMasahiro Yamada table[i]->percpu_absolute = 1; 7958c996940SArd Biesheuvel } 796c6bda7c9SRusty Russell } 797c6bda7c9SRusty Russell 7982213e9a6SArd Biesheuvel /* find the minimum non-absolute symbol address */ 7992213e9a6SArd Biesheuvel static void record_relative_base(void) 8002213e9a6SArd Biesheuvel { 8012213e9a6SArd Biesheuvel unsigned int i; 8022213e9a6SArd Biesheuvel 8032213e9a6SArd Biesheuvel for (i = 0; i < table_cnt; i++) 8048d605269SMasahiro Yamada if (!symbol_absolute(table[i])) { 805f34ea029SMasahiro Yamada /* 806f34ea029SMasahiro Yamada * The table is sorted by address. 807f34ea029SMasahiro Yamada * Take the first non-absolute symbol value. 808f34ea029SMasahiro Yamada */ 8098d605269SMasahiro Yamada relative_base = table[i]->addr; 810f34ea029SMasahiro Yamada return; 811f34ea029SMasahiro Yamada } 8122213e9a6SArd Biesheuvel } 8132213e9a6SArd Biesheuvel 814b3dbb4ecSPaulo Marques int main(int argc, char **argv) 8151da177e4SLinus Torvalds { 816aa221f2eSMasahiro Yamada while (1) { 817aa221f2eSMasahiro Yamada static struct option long_options[] = { 818aa221f2eSMasahiro Yamada {"all-symbols", no_argument, &all_symbols, 1}, 819aa221f2eSMasahiro Yamada {"absolute-percpu", no_argument, &absolute_percpu, 1}, 820aa221f2eSMasahiro Yamada {"base-relative", no_argument, &base_relative, 1}, 821aa221f2eSMasahiro Yamada {}, 822aa221f2eSMasahiro Yamada }; 823aa221f2eSMasahiro Yamada 824aa221f2eSMasahiro Yamada int c = getopt_long(argc, argv, "", long_options, NULL); 825aa221f2eSMasahiro Yamada 826aa221f2eSMasahiro Yamada if (c == -1) 827aa221f2eSMasahiro Yamada break; 828aa221f2eSMasahiro Yamada if (c != 0) 82941f11a4fSYoshinori Sato usage(); 83041f11a4fSYoshinori Sato } 831aa221f2eSMasahiro Yamada 832aa221f2eSMasahiro Yamada if (optind >= argc) 8331da177e4SLinus Torvalds usage(); 8341da177e4SLinus Torvalds 835aa221f2eSMasahiro Yamada read_map(argv[optind]); 8365e5c4fa7SMasahiro Yamada shrink_table(); 837c6bda7c9SRusty Russell if (absolute_percpu) 838c6bda7c9SRusty Russell make_percpus_absolute(); 839f34ea029SMasahiro Yamada sort_symbols(); 8402213e9a6SArd Biesheuvel if (base_relative) 8412213e9a6SArd Biesheuvel record_relative_base(); 8421da177e4SLinus Torvalds optimize_token_table(); 8431da177e4SLinus Torvalds write_src(); 8441da177e4SLinus Torvalds 8451da177e4SLinus Torvalds return 0; 8461da177e4SLinus Torvalds } 847