diff options
Diffstat (limited to 'scripts/kallsyms.c')
| -rw-r--r-- | scripts/kallsyms.c | 202 |
1 files changed, 175 insertions, 27 deletions
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index c912137f80e..dc7aa45e80c 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -7,12 +7,6 @@ * * Usage: nm -n vmlinux | scripts/kallsyms [--all-symbols] > symbols.S * - * ChangeLog: - * - * (25/Aug/2004) Paulo Marques <pmarques@grupopie.com> - * Changed the compression method from stem compression to "table lookup" - * compression - * * Table compression uses all the unused char codes on the symbols and * maps these to the most used substrings (tokens). For instance, it might * map char code 0xF7 to represent "write_" and then in every symbol where @@ -29,6 +23,10 @@ #include <string.h> #include <ctype.h> +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) +#endif + #define KSYM_NAME_LEN 128 struct sym_entry { @@ -38,11 +36,31 @@ struct sym_entry { unsigned char *sym; }; +struct addr_range { + const char *start_sym, *end_sym; + unsigned long long start, end; +}; + +static unsigned long long _text; +static struct addr_range text_ranges[] = { + { "_stext", "_etext" }, + { "_sinittext", "_einittext" }, + { "_stext_l1", "_etext_l1" }, /* Blackfin on-chip L1 inst SRAM */ + { "_stext_l2", "_etext_l2" }, /* Blackfin on-chip L2 SRAM */ +}; +#define text_range_text (&text_ranges[0]) +#define text_range_inittext (&text_ranges[1]) + +static struct addr_range percpu_range = { + "__per_cpu_start", "__per_cpu_end", -1ULL, 0 +}; + static struct sym_entry *table; static unsigned int table_size, table_cnt; -static unsigned long long _text, _stext, _etext, _sinittext, _einittext; static int all_symbols = 0; +static int absolute_percpu = 0; static char symbol_prefix_char = '\0'; +static unsigned long long kernel_start_addr = 0; int token_profit[0x10000]; @@ -53,7 +71,10 @@ unsigned char best_table_len[256]; static void usage(void) { - fprintf(stderr, "Usage: kallsyms [--all-symbols] [--symbol-prefix=<prefix char>] < in.map > out.S\n"); + fprintf(stderr, "Usage: kallsyms [--all-symbols] " + "[--symbol-prefix=<prefix char>] " + "[--page-offset=<CONFIG_PAGE_OFFSET>] " + "< in.map > out.S\n"); exit(1); } @@ -67,6 +88,27 @@ static inline int is_arm_mapping_symbol(const char *str) && (str[2] == '\0' || str[2] == '.'); } +static int check_symbol_range(const char *sym, unsigned long long addr, + struct addr_range *ranges, int entries) +{ + size_t i; + struct addr_range *ar; + + for (i = 0; i < entries; ++i) { + ar = &ranges[i]; + + if (strcmp(sym, ar->start_sym) == 0) { + ar->start = addr; + return 0; + } else if (strcmp(sym, ar->end_sym) == 0) { + ar->end = addr; + return 0; + } + } + + return 1; +} + static int read_symbol(FILE *in, struct sym_entry *s) { char str[500]; @@ -75,10 +117,14 @@ static int read_symbol(FILE *in, struct sym_entry *s) rc = fscanf(in, "%llx %c %499s\n", &s->addr, &stype, str); if (rc != 3) { - if (rc != EOF) { - /* skip line */ - fgets(str, 500, in); - } + if (rc != EOF && fgets(str, 500, in) == NULL) + fprintf(stderr, "Read error or end of file.\n"); + return -1; + } + if (strlen(str) > KSYM_NAME_LEN) { + fprintf(stderr, "Symbol %s too long for kallsyms (%zu vs %d).\n" + "Please increase KSYM_NAME_LEN both in kernel and kallsyms.c\n", + str, strlen(str), KSYM_NAME_LEN); return -1; } @@ -90,14 +136,9 @@ static int read_symbol(FILE *in, struct sym_entry *s) /* Ignore most absolute/undefined (?) symbols. */ if (strcmp(sym, "_text") == 0) _text = s->addr; - else if (strcmp(sym, "_stext") == 0) - _stext = s->addr; - else if (strcmp(sym, "_etext") == 0) - _etext = s->addr; - else if (strcmp(sym, "_sinittext") == 0) - _sinittext = s->addr; - else if (strcmp(sym, "_einittext") == 0) - _einittext = s->addr; + else if (check_symbol_range(sym, s->addr, text_ranges, + ARRAY_SIZE(text_ranges)) == 0) + /* nothing to do */; else if (toupper(stype) == 'A') { /* Keep these useful absolute symbols */ @@ -114,6 +155,9 @@ static int read_symbol(FILE *in, struct sym_entry *s) /* exclude also MIPS ELF local symbols ($L123 instead of .L123) */ else if (str[0] == '$') return -1; + /* exclude debugging symbols */ + else if (stype == 'N') + return -1; /* include the type field in the symbol name, so that it gets * compressed together */ @@ -127,6 +171,25 @@ static int read_symbol(FILE *in, struct sym_entry *s) strcpy((char *)s->sym + 1, str); s->sym[0] = stype; + /* Record if we've found __per_cpu_start/end. */ + check_symbol_range(sym, s->addr, &percpu_range, 1); + + return 0; +} + +static int symbol_in_range(struct sym_entry *s, struct addr_range *ranges, + int entries) +{ + size_t i; + struct addr_range *ar; + + for (i = 0; i < entries; ++i) { + ar = &ranges[i]; + + if (s->addr >= ar->start && s->addr <= ar->end) + return 1; + } + return 0; } @@ -152,6 +215,9 @@ static int symbol_valid(struct sym_entry *s) int i; int offset = 1; + if (s->addr < kernel_start_addr) + return 0; + /* skip prefix char */ if (symbol_prefix_char && *(s->sym + 1) == symbol_prefix_char) offset++; @@ -159,8 +225,8 @@ static int symbol_valid(struct sym_entry *s) /* if --all-symbols is not specified, then symbols outside the text * and inittext sections are discarded */ if (!all_symbols) { - if ((s->addr < _stext || s->addr > _etext) - && (s->addr < _sinittext || s->addr > _einittext)) + if (symbol_in_range(s, text_ranges, + ARRAY_SIZE(text_ranges)) == 0) return 0; /* Corner case. Discard any symbols with the same value as * _etext _einittext; they can move between pass 1 and 2 when @@ -168,10 +234,12 @@ static int symbol_valid(struct sym_entry *s) * they may get dropped in pass 2, which breaks the kallsyms * rules. */ - if ((s->addr == _etext && - strcmp((char *)s->sym + offset, "_etext")) || - (s->addr == _einittext && - strcmp((char *)s->sym + offset, "_einittext"))) + if ((s->addr == text_range_text->end && + strcmp((char *)s->sym + offset, + text_range_text->end_sym)) || + (s->addr == text_range_inittext->end && + strcmp((char *)s->sym + offset, + text_range_inittext->end_sym))) return 0; } @@ -244,6 +312,11 @@ static int expand_symbol(unsigned char *data, int len, char *result) return total; } +static int symbol_absolute(struct sym_entry *s) +{ + return toupper(s->sym[0]) == 'A'; +} + static void write_src(void) { unsigned int i, k, off; @@ -271,7 +344,7 @@ static void write_src(void) */ output_label("kallsyms_addresses"); for (i = 0; i < table_cnt; i++) { - if (toupper(table[i].sym[0]) != 'A') { + if (!symbol_absolute(&table[i])) { if (_text <= table[i].addr) printf("\tPTR\t_text + %#llx\n", table[i].addr - _text); @@ -459,6 +532,8 @@ static void optimize_result(void) /* find the token with the breates profit value */ best = find_best_token(); + if (token_profit[best] == 0) + break; /* place it in the "best" table */ best_table_len[i] = 2; @@ -503,6 +578,51 @@ static void optimize_token_table(void) optimize_result(); } +/* guess for "linker script provide" symbol */ +static int may_be_linker_script_provide_symbol(const struct sym_entry *se) +{ + const char *symbol = (char *)se->sym + 1; + int len = se->len - 1; + + if (len < 8) + return 0; + + if (symbol[0] != '_' || symbol[1] != '_') + return 0; + + /* __start_XXXXX */ + if (!memcmp(symbol + 2, "start_", 6)) + return 1; + + /* __stop_XXXXX */ + if (!memcmp(symbol + 2, "stop_", 5)) + return 1; + + /* __end_XXXXX */ + if (!memcmp(symbol + 2, "end_", 4)) + return 1; + + /* __XXXXX_start */ + if (!memcmp(symbol + len - 6, "_start", 6)) + return 1; + + /* __XXXXX_end */ + if (!memcmp(symbol + len - 4, "_end", 4)) + return 1; + + return 0; +} + +static int prefix_underscores_count(const char *str) +{ + const char *tail = str; + + while (*tail == '_') + tail++; + + return tail - str; +} + static int compare_symbols(const void *a, const void *b) { const struct sym_entry *sa; @@ -524,6 +644,18 @@ static int compare_symbols(const void *a, const void *b) if (wa != wb) return wa - wb; + /* sort by "linker script provide" type */ + wa = may_be_linker_script_provide_symbol(sa); + wb = may_be_linker_script_provide_symbol(sb); + if (wa != wb) + return wa - wb; + + /* sort by the number of prefix underscores */ + wa = prefix_underscores_count((const char *)sa->sym + 1); + wb = prefix_underscores_count((const char *)sb->sym + 1); + if (wa != wb) + return wa - wb; + /* sort by initial order, so that other symbols are left undisturbed */ return sa->start_pos - sb->start_pos; } @@ -533,6 +665,15 @@ static void sort_symbols(void) qsort(table, table_cnt, sizeof(struct sym_entry), compare_symbols); } +static void make_percpus_absolute(void) +{ + unsigned int i; + + for (i = 0; i < table_cnt; i++) + if (symbol_in_range(&table[i], &percpu_range, 1)) + table[i].sym[0] = 'A'; +} + int main(int argc, char **argv) { if (argc >= 2) { @@ -540,12 +681,17 @@ int main(int argc, char **argv) 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 (strncmp(argv[i], "--symbol-prefix=", 16) == 0) { char *p = &argv[i][16]; /* skip quote */ if ((*p == '"' && *(p+2) == '"') || (*p == '\'' && *(p+2) == '\'')) p++; symbol_prefix_char = *p; + } else if (strncmp(argv[i], "--page-offset=", 14) == 0) { + const char *p = &argv[i][14]; + kernel_start_addr = strtoull(p, NULL, 16); } else usage(); } @@ -553,6 +699,8 @@ int main(int argc, char **argv) usage(); read_map(stdin); + if (absolute_percpu) + make_percpus_absolute(); sort_symbols(); optimize_token_table(); write_src(); |
