diff options
Diffstat (limited to 'scripts/kallsyms.c')
-rw-r--r-- | scripts/kallsyms.c | 376 |
1 files changed, 213 insertions, 163 deletions
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index 54ad86d13784..653b92f6d4c8 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -5,7 +5,8 @@ * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * - * Usage: nm -n vmlinux | scripts/kallsyms [--all-symbols] > symbols.S + * Usage: kallsyms [--all-symbols] [--absolute-percpu] + * [--base-relative] [--lto-clang] in.map > out.S * * Table compression uses all the unused char codes on the symbols and * maps these to the most used substrings (tokens). For instance, it might @@ -18,6 +19,8 @@ * */ +#include <errno.h> +#include <getopt.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> @@ -27,11 +30,12 @@ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) -#define KSYM_NAME_LEN 128 +#define KSYM_NAME_LEN 512 struct sym_entry { unsigned long long addr; unsigned int len; + unsigned int seq; unsigned int start_pos; unsigned int percpu_absolute; unsigned char sym[]; @@ -60,6 +64,7 @@ static unsigned int table_size, table_cnt; static int all_symbols; static int absolute_percpu; static int base_relative; +static int lto_clang; static int token_profit[0x10000]; @@ -70,8 +75,8 @@ static unsigned char best_table_len[256]; static void usage(void) { - fprintf(stderr, "Usage: kallsyms [--all-symbols] " - "[--base-relative] < in.map > out.S\n"); + fprintf(stderr, "Usage: kallsyms [--all-symbols] [--absolute-percpu] " + "[--base-relative] [--lto-clang] in.map > out.S\n"); exit(1); } @@ -82,86 +87,7 @@ static char *sym_name(const struct sym_entry *s) static bool is_ignored_symbol(const char *name, char type) { - /* Symbol names that exactly match to the following are ignored.*/ - static const char * const ignored_symbols[] = { - /* - * Symbols which vary between passes. Passes 1 and 2 must have - * identical symbol lists. The kallsyms_* symbols below are - * only added after pass 1, they would be included in pass 2 - * when --all-symbols is specified so exclude them to get a - * stable symbol list. - */ - "kallsyms_addresses", - "kallsyms_offsets", - "kallsyms_relative_base", - "kallsyms_num_syms", - "kallsyms_names", - "kallsyms_markers", - "kallsyms_token_table", - "kallsyms_token_index", - /* Exclude linker generated symbols which vary between passes */ - "_SDA_BASE_", /* ppc */ - "_SDA2_BASE_", /* ppc */ - NULL - }; - - /* Symbol names that begin with the following are ignored.*/ - static const char * const ignored_prefixes[] = { - "$", /* local symbols for ARM, MIPS, etc. */ - ".LASANPC", /* s390 kasan local symbols */ - "__crc_", /* modversions */ - "__efistub_", /* arm64 EFI stub namespace */ - "__kvm_nvhe_", /* arm64 non-VHE KVM namespace */ - "__AArch64ADRPThunk_", /* arm64 lld */ - "__ARMV5PILongThunk_", /* arm lld */ - "__ARMV7PILongThunk_", - "__ThumbV7PILongThunk_", - "__LA25Thunk_", /* mips lld */ - "__microLA25Thunk_", - NULL - }; - - /* Symbol names that end with the following are ignored.*/ - static const char * const ignored_suffixes[] = { - "_from_arm", /* arm */ - "_from_thumb", /* arm */ - "_veneer", /* arm */ - NULL - }; - - /* Symbol names that contain the following are ignored.*/ - static const char * const ignored_matches[] = { - ".long_branch.", /* ppc stub */ - ".plt_branch.", /* ppc stub */ - NULL - }; - - const char * const *p; - - for (p = ignored_symbols; *p; p++) - if (!strcmp(name, *p)) - return true; - - for (p = ignored_prefixes; *p; p++) - if (!strncmp(name, *p, strlen(*p))) - return true; - - for (p = ignored_suffixes; *p; p++) { - int l = strlen(name) - strlen(*p); - - if (l >= 0 && !strcmp(name + l, *p)) - return true; - } - - for (p = ignored_matches; *p; p++) { - if (strstr(name, *p)) - return true; - } - - if (type == 'U' || type == 'u') - return true; - /* exclude debugging symbols */ - if (type == 'N' || type == 'n') + if (type == 'u' || type == 'n') return true; if (toupper(type) == 'A') { @@ -195,24 +121,41 @@ static void check_symbol_range(const char *sym, unsigned long long addr, } } -static struct sym_entry *read_symbol(FILE *in) +static struct sym_entry *read_symbol(FILE *in, char **buf, size_t *buf_len) { - char name[500], type; + char *name, type, *p; unsigned long long addr; - unsigned int len; + size_t len; + ssize_t readlen; struct sym_entry *sym; - int rc; - rc = fscanf(in, "%llx %c %499s\n", &addr, &type, name); - if (rc != 3) { - if (rc != EOF && fgets(name, 500, in) == NULL) - fprintf(stderr, "Read error or end of file.\n"); + errno = 0; + readlen = getline(buf, buf_len, in); + if (readlen < 0) { + if (errno) { + perror("read_symbol"); + exit(EXIT_FAILURE); + } return NULL; } - if (strlen(name) >= KSYM_NAME_LEN) { + + if ((*buf)[readlen - 1] == '\n') + (*buf)[readlen - 1] = 0; + + addr = strtoull(*buf, &p, 16); + + if (*buf == p || *p++ != ' ' || !isascii((type = *p++)) || *p++ != ' ') { + fprintf(stderr, "line format error\n"); + exit(EXIT_FAILURE); + } + + name = p; + len = strlen(name); + + if (len >= KSYM_NAME_LEN) { fprintf(stderr, "Symbol %s too long for kallsyms (%zu >= %d).\n" "Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n", - name, strlen(name), KSYM_NAME_LEN); + name, len, KSYM_NAME_LEN); return NULL; } @@ -228,8 +171,7 @@ static struct sym_entry *read_symbol(FILE *in) /* include the type field in the symbol name, so that it gets * compressed together */ - - len = strlen(name) + 1; + len++; sym = malloc(sizeof(*sym) + len + 1); if (!sym) { @@ -312,12 +254,21 @@ static void shrink_table(void) } } -static void read_map(FILE *in) +static void read_map(const char *in) { + FILE *fp; struct sym_entry *sym; + char *buf = NULL; + size_t buflen = 0; - while (!feof(in)) { - sym = read_symbol(in); + fp = fopen(in, "r"); + if (!fp) { + perror(in); + exit(1); + } + + while (!feof(fp)) { + sym = read_symbol(fp, &buf, &buflen); if (!sym) continue; @@ -328,12 +279,16 @@ static void read_map(FILE *in) table = realloc(table, sizeof(*table) * table_size); if (!table) { fprintf(stderr, "out of memory\n"); + fclose(fp); exit (1); } } table[table_cnt++] = sym; } + + free(buf); + fclose(fp); } static void output_label(const char *label) @@ -384,6 +339,50 @@ static int symbol_absolute(const struct sym_entry *s) return s->percpu_absolute; } +static void cleanup_symbol_name(char *s) +{ + char *p; + + /* + * ASCII[.] = 2e + * ASCII[0-9] = 30,39 + * ASCII[A-Z] = 41,5a + * ASCII[_] = 5f + * ASCII[a-z] = 61,7a + * + * As above, replacing the first '.' in ".llvm." with '\0' does not + * affect the main sorting, but it helps us with subsorting. + */ + p = strstr(s, ".llvm."); + if (p) + *p = '\0'; +} + +static int compare_names(const void *a, const void *b) +{ + int ret; + const struct sym_entry *sa = *(const struct sym_entry **)a; + const struct sym_entry *sb = *(const struct sym_entry **)b; + + ret = strcmp(sym_name(sa), sym_name(sb)); + if (!ret) { + if (sa->addr > sb->addr) + return 1; + else if (sa->addr < sb->addr) + return -1; + + /* keep old order */ + return (int)(sa->seq - sb->seq); + } + + return ret; +} + +static void sort_symbols_by_name(void) +{ + qsort(table, table_cnt, sizeof(table[0]), compare_names); +} + static void write_src(void) { unsigned int i, k, off; @@ -402,6 +401,89 @@ static void write_src(void) printf("\t.section .rodata, \"a\"\n"); + output_label("kallsyms_num_syms"); + printf("\t.long\t%u\n", table_cnt); + printf("\n"); + + /* table of offset markers, that give the offset in the compressed stream + * every 256 symbols */ + markers = malloc(sizeof(unsigned int) * ((table_cnt + 255) / 256)); + if (!markers) { + fprintf(stderr, "kallsyms failure: " + "unable to allocate required memory\n"); + exit(EXIT_FAILURE); + } + + output_label("kallsyms_names"); + off = 0; + for (i = 0; i < table_cnt; i++) { + if ((i & 0xFF) == 0) + markers[i >> 8] = off; + table[i]->seq = i; + + /* There cannot be any symbol of length zero. */ + if (table[i]->len == 0) { + fprintf(stderr, "kallsyms failure: " + "unexpected zero symbol length\n"); + exit(EXIT_FAILURE); + } + + /* Only lengths that fit in up-to-two-byte ULEB128 are supported. */ + if (table[i]->len > 0x3FFF) { + fprintf(stderr, "kallsyms failure: " + "unexpected huge symbol length\n"); + exit(EXIT_FAILURE); + } + + /* Encode length with ULEB128. */ + if (table[i]->len <= 0x7F) { + /* Most symbols use a single byte for the length. */ + printf("\t.byte 0x%02x", table[i]->len); + off += table[i]->len + 1; + } else { + /* "Big" symbols use two bytes. */ + printf("\t.byte 0x%02x, 0x%02x", + (table[i]->len & 0x7F) | 0x80, + (table[i]->len >> 7) & 0x7F); + off += table[i]->len + 2; + } + for (k = 0; k < table[i]->len; k++) + printf(", 0x%02x", table[i]->sym[k]); + printf("\n"); + } + printf("\n"); + + /* + * Now that we wrote out the compressed symbol names, restore the + * original names, which are needed in some of the later steps. + */ + for (i = 0; i < table_cnt; i++) { + expand_symbol(table[i]->sym, table[i]->len, buf); + strcpy((char *)table[i]->sym, buf); + } + + output_label("kallsyms_markers"); + for (i = 0; i < ((table_cnt + 255) >> 8); i++) + printf("\t.long\t%u\n", markers[i]); + printf("\n"); + + free(markers); + + output_label("kallsyms_token_table"); + off = 0; + for (i = 0; i < 256; i++) { + best_idx[i] = off; + expand_symbol(best_table[i], best_table_len[i], buf); + printf("\t.asciz\t\"%s\"\n", buf); + off += strlen(buf) + 1; + } + printf("\n"); + + output_label("kallsyms_token_index"); + for (i = 0; i < 256; i++) + printf("\t.short\t%d\n", best_idx[i]); + printf("\n"); + if (!base_relative) output_label("kallsyms_addresses"); else @@ -436,7 +518,7 @@ static void write_src(void) table[i]->addr); exit(EXIT_FAILURE); } - printf("\t.long\t%#x\n", (int)offset); + printf("\t.long\t%#x /* %s */\n", (int)offset, table[i]->sym); } else if (!symbol_absolute(table[i])) { output_address(table[i]->addr); } else { @@ -451,54 +533,17 @@ static void write_src(void) printf("\n"); } - output_label("kallsyms_num_syms"); - printf("\t.long\t%u\n", table_cnt); - printf("\n"); - - /* table of offset markers, that give the offset in the compressed stream - * every 256 symbols */ - markers = malloc(sizeof(unsigned int) * ((table_cnt + 255) / 256)); - if (!markers) { - fprintf(stderr, "kallsyms failure: " - "unable to allocate required memory\n"); - exit(EXIT_FAILURE); - } - - output_label("kallsyms_names"); - off = 0; - for (i = 0; i < table_cnt; i++) { - if ((i & 0xFF) == 0) - markers[i >> 8] = off; - - printf("\t.byte 0x%02x", table[i]->len); - for (k = 0; k < table[i]->len; k++) - printf(", 0x%02x", table[i]->sym[k]); - printf("\n"); - - off += table[i]->len + 1; - } - printf("\n"); - - output_label("kallsyms_markers"); - for (i = 0; i < ((table_cnt + 255) >> 8); i++) - printf("\t.long\t%u\n", markers[i]); - printf("\n"); + if (lto_clang) + for (i = 0; i < table_cnt; i++) + cleanup_symbol_name((char *)table[i]->sym); - free(markers); - - output_label("kallsyms_token_table"); - off = 0; - for (i = 0; i < 256; i++) { - best_idx[i] = off; - expand_symbol(best_table[i], best_table_len[i], buf); - printf("\t.asciz\t\"%s\"\n", buf); - off += strlen(buf) + 1; - } - printf("\n"); - - output_label("kallsyms_token_index"); - for (i = 0; i < 256; i++) - printf("\t.short\t%d\n", best_idx[i]); + sort_symbols_by_name(); + output_label("kallsyms_seqs_of_names"); + for (i = 0; i < table_cnt; i++) + printf("\t.byte 0x%02x, 0x%02x, 0x%02x\n", + (unsigned char)(table[i]->seq >> 16), + (unsigned char)(table[i]->seq >> 8), + (unsigned char)(table[i]->seq >> 0)); printf("\n"); } @@ -524,7 +569,7 @@ static void forget_symbol(const unsigned char *symbol, int len) } /* do the initial token count */ -static void build_initial_tok_table(void) +static void build_initial_token_table(void) { unsigned int i; @@ -649,7 +694,7 @@ static void insert_real_symbols_in_table(void) static void optimize_token_table(void) { - build_initial_tok_table(); + build_initial_token_table(); insert_real_symbols_in_table(); @@ -764,22 +809,27 @@ static void record_relative_base(void) int main(int argc, char **argv) { - if (argc >= 2) { - int i; - for (i = 1; i < argc; i++) { - if(strcmp(argv[i], "--all-symbols") == 0) - all_symbols = 1; - else if (strcmp(argv[i], "--absolute-percpu") == 0) - absolute_percpu = 1; - else if (strcmp(argv[i], "--base-relative") == 0) - base_relative = 1; - else - usage(); - } - } else if (argc != 1) + while (1) { + static const struct option long_options[] = { + {"all-symbols", no_argument, &all_symbols, 1}, + {"absolute-percpu", no_argument, &absolute_percpu, 1}, + {"base-relative", no_argument, &base_relative, 1}, + {"lto-clang", no_argument, <o_clang, 1}, + {}, + }; + + int c = getopt_long(argc, argv, "", long_options, NULL); + + if (c == -1) + break; + if (c != 0) + usage(); + } + + if (optind >= argc) usage(); - read_map(stdin); + read_map(argv[optind]); shrink_table(); if (absolute_percpu) make_percpus_absolute(); |