aboutsummaryrefslogtreecommitdiff
path: root/scripts/kallsyms.c
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/kallsyms.c')
-rw-r--r--scripts/kallsyms.c202
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();