summaryrefslogtreecommitdiff
path: root/scripts/kallsyms.c
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/kallsyms.c')
-rw-r--r--scripts/kallsyms.c441
1 files changed, 195 insertions, 246 deletions
diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c
index 54ad86d13784..4b0234e4b12f 100644
--- a/scripts/kallsyms.c
+++ b/scripts/kallsyms.c
@@ -5,7 +5,7 @@
* 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] 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 +18,8 @@
*
*/
+#include <errno.h>
+#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -25,15 +27,16 @@
#include <ctype.h>
#include <limits.h>
+#include <xalloc.h>
+
#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 start_pos;
- unsigned int percpu_absolute;
+ unsigned int seq;
unsigned char sym[];
};
@@ -51,15 +54,9 @@ static struct addr_range text_ranges[] = {
#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 int all_symbols;
-static int absolute_percpu;
-static int base_relative;
static int token_profit[0x10000];
@@ -70,8 +67,7 @@ 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] in.map > out.S\n");
exit(1);
}
@@ -82,86 +78,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 +112,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;
}
@@ -224,24 +158,16 @@ static struct sym_entry *read_symbol(FILE *in)
return NULL;
check_symbol_range(name, addr, text_ranges, ARRAY_SIZE(text_ranges));
- check_symbol_range(name, addr, &percpu_range, 1);
/* include the type field in the symbol name, so that it gets
* compressed together */
+ len++;
- len = strlen(name) + 1;
-
- sym = malloc(sizeof(*sym) + len + 1);
- if (!sym) {
- fprintf(stderr, "kallsyms failure: "
- "unable to allocate required amount of memory\n");
- exit(EXIT_FAILURE);
- }
+ sym = xmalloc(sizeof(*sym) + len + 1);
sym->addr = addr;
sym->len = len;
sym->sym[0] = type;
strcpy(sym_name(sym), name);
- sym->percpu_absolute = 0;
return sym;
}
@@ -262,6 +188,11 @@ static int symbol_in_range(const struct sym_entry *s,
return 0;
}
+static bool string_starts_with(const char *s, const char *prefix)
+{
+ return strncmp(s, prefix, strlen(prefix)) == 0;
+}
+
static int symbol_valid(const struct sym_entry *s)
{
const char *name = sym_name(s);
@@ -269,6 +200,14 @@ static int symbol_valid(const struct sym_entry *s)
/* if --all-symbols is not specified, then symbols outside the text
* and inittext sections are discarded */
if (!all_symbols) {
+ /*
+ * Symbols starting with __start and __stop are used to denote
+ * section boundaries, and should always be included:
+ */
+ if (string_starts_with(name, "__start_") ||
+ string_starts_with(name, "__stop_"))
+ return 1;
+
if (symbol_in_range(s, text_ranges,
ARRAY_SIZE(text_ranges)) == 0)
return 0;
@@ -304,36 +243,38 @@ static void shrink_table(void)
}
}
table_cnt = pos;
-
- /* When valid symbol is not registered, exit to error */
- if (!table_cnt) {
- fprintf(stderr, "No valid symbol.\n");
- exit(1);
- }
}
-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;
- sym->start_pos = table_cnt;
+ sym->seq = table_cnt;
if (table_cnt >= table_size) {
table_size += 10000;
- table = realloc(table, sizeof(*table) * table_size);
- if (!table) {
- fprintf(stderr, "out of memory\n");
- exit (1);
- }
+ table = xrealloc(table, sizeof(*table) * table_size);
}
table[table_cnt++] = sym;
}
+
+ free(buf);
+ fclose(fp);
}
static void output_label(const char *label)
@@ -343,15 +284,6 @@ static void output_label(const char *label)
printf("%s:\n", label);
}
-/* Provide proper symbols relocatability by their '_text' relativeness. */
-static void output_address(unsigned long long addr)
-{
- if (_text <= addr)
- printf("\tPTR\t_text + %#llx\n", addr - _text);
- else
- printf("\tPTR\t_text - %#llx\n", _text - addr);
-}
-
/* uncompress a compressed symbol. When this function is called, the best table
* might still be compressed itself, so the function needs to be recursive */
static int expand_symbol(const unsigned char *data, int len, char *result)
@@ -379,16 +311,36 @@ static int expand_symbol(const unsigned char *data, int len, char *result)
return total;
}
-static int symbol_absolute(const struct sym_entry *s)
+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)
{
- return s->percpu_absolute;
+ qsort(table, table_cnt, sizeof(table[0]), compare_names);
}
static void write_src(void)
{
unsigned int i, k, off;
unsigned int best_idx[256];
- unsigned int *markers;
+ unsigned int *markers, markers_cnt;
char buf[KSYM_NAME_LEN];
printf("#include <asm/bitsperlong.h>\n");
@@ -402,85 +354,63 @@ static void write_src(void)
printf("\t.section .rodata, \"a\"\n");
- if (!base_relative)
- output_label("kallsyms_addresses");
- else
- output_label("kallsyms_offsets");
-
- for (i = 0; i < table_cnt; i++) {
- if (base_relative) {
- /*
- * Use the offset relative to the lowest value
- * encountered of all relative symbols, and emit
- * non-relocatable fixed offsets that will be fixed
- * up at runtime.
- */
-
- long long offset;
- int overflow;
-
- if (!absolute_percpu) {
- offset = table[i]->addr - relative_base;
- overflow = (offset < 0 || offset > UINT_MAX);
- } else if (symbol_absolute(table[i])) {
- offset = table[i]->addr;
- overflow = (offset < 0 || offset > INT_MAX);
- } else {
- offset = relative_base - table[i]->addr - 1;
- overflow = (offset < INT_MIN || offset >= 0);
- }
- if (overflow) {
- fprintf(stderr, "kallsyms failure: "
- "%s symbol value %#llx out of range in relative mode\n",
- symbol_absolute(table[i]) ? "absolute" : "relative",
- table[i]->addr);
- exit(EXIT_FAILURE);
- }
- printf("\t.long\t%#x\n", (int)offset);
- } else if (!symbol_absolute(table[i])) {
- output_address(table[i]->addr);
- } else {
- printf("\tPTR\t%#llx\n", table[i]->addr);
- }
- }
- printf("\n");
-
- if (base_relative) {
- output_label("kallsyms_relative_base");
- output_address(relative_base);
- 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);
- }
+ markers_cnt = (table_cnt + 255) / 256;
+ markers = xmalloc(sizeof(*markers) * markers_cnt);
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);
+ }
- printf("\t.byte 0x%02x", table[i]->len);
+ /* 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");
- off += table[i]->len + 1;
+ /*
+ * Now that we wrote out the compressed symbol name, restore the
+ * original name and print it in the comment.
+ */
+ expand_symbol(table[i]->sym, table[i]->len, buf);
+ strcpy((char *)table[i]->sym, buf);
+ printf("\t/* %s */\n", table[i]->sym);
}
printf("\n");
output_label("kallsyms_markers");
- for (i = 0; i < ((table_cnt + 255) >> 8); i++)
+ for (i = 0; i < markers_cnt; i++)
printf("\t.long\t%u\n", markers[i]);
printf("\n");
@@ -500,6 +430,47 @@ static void write_src(void)
for (i = 0; i < 256; i++)
printf("\t.short\t%d\n", best_idx[i]);
printf("\n");
+
+ output_label("kallsyms_offsets");
+
+ for (i = 0; i < table_cnt; i++) {
+ /*
+ * Use the offset relative to the lowest value
+ * encountered of all relative symbols, and emit
+ * non-relocatable fixed offsets that will be fixed
+ * up at runtime.
+ */
+
+ long long offset;
+
+ offset = table[i]->addr - relative_base;
+ if (offset < 0 || offset > UINT_MAX) {
+ fprintf(stderr, "kallsyms failure: "
+ "relative symbol value %#llx out of range\n",
+ table[i]->addr);
+ exit(EXIT_FAILURE);
+ }
+ printf("\t.long\t%#x\t/* %s */\n", (int)offset, table[i]->sym);
+ }
+ printf("\n");
+
+ output_label("kallsyms_relative_base");
+ /* Provide proper symbols relocatability by their '_text' relativeness. */
+ if (_text <= relative_base)
+ printf("\tPTR\t_text + %#llx\n", relative_base - _text);
+ else
+ printf("\tPTR\t_text - %#llx\n", _text - relative_base);
+ printf("\n");
+
+ 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\t/* %s */\n",
+ (unsigned char)(table[i]->seq >> 16),
+ (unsigned char)(table[i]->seq >> 8),
+ (unsigned char)(table[i]->seq >> 0),
+ table[i]->sym);
+ printf("\n");
}
@@ -524,7 +495,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 +620,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();
@@ -722,7 +693,7 @@ static int compare_symbols(const void *a, const void *b)
return wa - wb;
/* sort by initial order, so that other symbols are left undisturbed */
- return sa->start_pos - sb->start_pos;
+ return sa->seq - sb->seq;
}
static void sort_symbols(void)
@@ -730,62 +701,40 @@ static void sort_symbols(void)
qsort(table, table_cnt, sizeof(table[0]), 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)) {
- /*
- * Keep the 'A' override for percpu symbols to
- * ensure consistent behavior compared to older
- * versions of this tool.
- */
- table[i]->sym[0] = 'A';
- table[i]->percpu_absolute = 1;
- }
-}
-
/* find the minimum non-absolute symbol address */
static void record_relative_base(void)
{
- unsigned int i;
-
- for (i = 0; i < table_cnt; i++)
- if (!symbol_absolute(table[i])) {
- /*
- * The table is sorted by address.
- * Take the first non-absolute symbol value.
- */
- relative_base = table[i]->addr;
- return;
- }
+ /*
+ * The table is sorted by address.
+ * Take the first symbol value.
+ */
+ if (table_cnt)
+ relative_base = table[0]->addr;
}
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},
+ {},
+ };
+
+ 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();
sort_symbols();
- if (base_relative)
- record_relative_base();
+ record_relative_base();
optimize_token_table();
write_src();