From 21bdd17b21b45ea48e06e23918d681afbe0622e9 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Mon, 3 Feb 2014 11:14:13 +1030 Subject: module: allow multiple calls to MODULE_DEVICE_TABLE() per module Commit 78551277e4df5: "Input: i8042 - add PNP modaliases" had a bug, where the second call to MODULE_DEVICE_TABLE() overrode the first resulting in not all the modaliases being exposed. This fixes the problem by including the name of the device_id table in the __mod_*_device_table alias, allowing us to export several device_id tables per module. Suggested-by: Kay Sievers Acked-by: Greg Kroah-Hartman Cc: Dmitry Torokhov Signed-off-by: Tom Gundersen Signed-off-by: Rusty Russell --- scripts/mod/file2alias.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 25e5cb0aaef6..ce164044f0cc 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -42,7 +42,7 @@ typedef unsigned char __u8; /* This array collects all instances that use the generic do_table */ struct devtable { - const char *device_id; /* name of table, __mod__device_table. */ + const char *device_id; /* name of table, __mod___*_device_table. */ unsigned long id_size; void *function; }; @@ -146,7 +146,8 @@ static void device_id_check(const char *modname, const char *device_id, if (size % id_size || size < id_size) { fatal("%s: sizeof(struct %s_device_id)=%lu is not a modulo " - "of the size of section __mod_%s_device_table=%lu.\n" + "of the size of " + "section __mod_%s___device_table=%lu.\n" "Fix definition of struct %s_device_id " "in mod_devicetable.h\n", modname, device_id, id_size, device_id, size, device_id); @@ -1206,7 +1207,7 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, { void *symval; char *zeros = NULL; - const char *name; + const char *name, *identifier; unsigned int namelen; /* We're looking for a section relative symbol */ @@ -1217,7 +1218,7 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, if (ELF_ST_TYPE(sym->st_info) != STT_OBJECT) return; - /* All our symbols are of form __mod_XXX_device_table. */ + /* All our symbols are of form __mod____device_table. */ name = strstr(symname, "__mod_"); if (!name) return; @@ -1227,7 +1228,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, return; if (strcmp(name + namelen - strlen("_device_table"), "_device_table")) return; - namelen -= strlen("_device_table"); + identifier = strstr(name, "__"); + if (!identifier) + return; + namelen = identifier - name; /* Handle all-NULL symbols allocated into .bss */ if (info->sechdrs[get_secindex(info, sym)].sh_type & SHT_NOBITS) { -- cgit v1.2.3 From 78eb71594b3265f3cfe871480235be158feebd0f Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 17 Mar 2014 13:18:27 +1030 Subject: kallsyms: generalize address range checking This refactors the address range checks to be generalized instead of specific to text range checks, in preparation for other range checks. Also extracts logic for "is the symbol absolute" into a function. Signed-off-by: Kees Cook Signed-off-by: Rusty Russell --- scripts/kallsyms.c | 53 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 21 deletions(-) (limited to 'scripts') diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index 10085de886fe..3df15f5e7c55 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -36,13 +36,13 @@ struct sym_entry { unsigned char *sym; }; -struct text_range { - const char *stext, *etext; +struct addr_range { + const char *start_sym, *end_sym; unsigned long long start, end; }; static unsigned long long _text; -static struct text_range text_ranges[] = { +static struct addr_range text_ranges[] = { { "_stext", "_etext" }, { "_sinittext", "_einittext" }, { "_stext_l1", "_etext_l1" }, /* Blackfin on-chip L1 inst SRAM */ @@ -83,19 +83,20 @@ static inline int is_arm_mapping_symbol(const char *str) && (str[2] == '\0' || str[2] == '.'); } -static int read_symbol_tr(const char *sym, unsigned long long addr) +static int check_symbol_range(const char *sym, unsigned long long addr, + struct addr_range *ranges, int entries) { size_t i; - struct text_range *tr; + struct addr_range *ar; - for (i = 0; i < ARRAY_SIZE(text_ranges); ++i) { - tr = &text_ranges[i]; + for (i = 0; i < entries; ++i) { + ar = &ranges[i]; - if (strcmp(sym, tr->stext) == 0) { - tr->start = addr; + if (strcmp(sym, ar->start_sym) == 0) { + ar->start = addr; return 0; - } else if (strcmp(sym, tr->etext) == 0) { - tr->end = addr; + } else if (strcmp(sym, ar->end_sym) == 0) { + ar->end = addr; return 0; } } @@ -130,7 +131,8 @@ 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 (read_symbol_tr(sym, s->addr) == 0) + else if (check_symbol_range(sym, s->addr, text_ranges, + ARRAY_SIZE(text_ranges)) == 0) /* nothing to do */; else if (toupper(stype) == 'A') { @@ -167,15 +169,16 @@ static int read_symbol(FILE *in, struct sym_entry *s) return 0; } -static int symbol_valid_tr(struct sym_entry *s) +static int symbol_in_range(struct sym_entry *s, struct addr_range *ranges, + int entries) { size_t i; - struct text_range *tr; + struct addr_range *ar; - for (i = 0; i < ARRAY_SIZE(text_ranges); ++i) { - tr = &text_ranges[i]; + for (i = 0; i < entries; ++i) { + ar = &ranges[i]; - if (s->addr >= tr->start && s->addr <= tr->end) + if (s->addr >= ar->start && s->addr <= ar->end) return 1; } @@ -214,7 +217,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 (symbol_valid_tr(s) == 0) + 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 @@ -223,9 +227,11 @@ static int symbol_valid(struct sym_entry *s) * rules. */ if ((s->addr == text_range_text->end && - strcmp((char *)s->sym + offset, text_range_text->etext)) || + strcmp((char *)s->sym + offset, + text_range_text->end_sym)) || (s->addr == text_range_inittext->end && - strcmp((char *)s->sym + offset, text_range_inittext->etext))) + strcmp((char *)s->sym + offset, + text_range_inittext->end_sym))) return 0; } @@ -298,6 +304,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; @@ -325,7 +336,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); -- cgit v1.2.3 From c6bda7c988a57958108741cde9b1f12e9727a938 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 17 Mar 2014 14:05:46 +1030 Subject: kallsyms: fix percpu vars on x86-64 with relocation. x86-64 has a problem: per-cpu variables are actually represented by their absolute offsets within the per-cpu area, but the symbols are not emitted as absolute. Thus kallsyms naively creates them as offsets from _text, meaning their values change if the kernel is relocated (especially noticeable with CONFIG_RANDOMIZE_BASE): $ egrep ' (gdt_|_(stext|_per_cpu_))' /root/kallsyms.nokaslr 0000000000000000 D __per_cpu_start 0000000000004000 D gdt_page 0000000000014280 D __per_cpu_end ffffffff810001c8 T _stext ffffffff81ee53c0 D __per_cpu_offset $ egrep ' (gdt_|_(stext|_per_cpu_))' /root/kallsyms.kaslr1 000000001f200000 D __per_cpu_start 000000001f204000 D gdt_page 000000001f214280 D __per_cpu_end ffffffffa02001c8 T _stext ffffffffa10e53c0 D __per_cpu_offset Making them absolute symbols is the Right Thing, but requires fixes to the relocs tool. So for the moment, we add a --absolute-percpu option which makes them absolute from a kallsyms perspective: $ egrep ' (gdt_|_(stext|_per_cpu_))' /proc/kallsyms # no KASLR 0000000000000000 A __per_cpu_start 000000000000a000 A gdt_page 0000000000013040 A __per_cpu_end ffffffff802001c8 T _stext ffffffff8099b180 D __per_cpu_offset ffffffff809a3000 D __per_cpu_load $ egrep ' (gdt_|_(stext|_per_cpu_))' /proc/kallsyms # With KASLR 0000000000000000 A __per_cpu_start 000000000000a000 A gdt_page 0000000000013040 A __per_cpu_end ffffffff89c001c8 T _stext ffffffff8a39d180 D __per_cpu_offset ffffffff8a3a5000 D __per_cpu_load Based-on-the-original-screenplay-by: Andy Honig Signed-off-by: Rusty Russell Acked-by: Kees Cook --- scripts/kallsyms.c | 21 +++++++++++++++++++++ scripts/link-vmlinux.sh | 4 ++++ 2 files changed, 25 insertions(+) (limited to 'scripts') diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index 3df15f5e7c55..1237dd7fb4ca 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -51,9 +51,14 @@ 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 = 0; +static int absolute_percpu = 0; static char symbol_prefix_char = '\0'; static unsigned long long kernel_start_addr = 0; @@ -166,6 +171,9 @@ 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; } @@ -657,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) { @@ -664,6 +681,8 @@ 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 */ @@ -680,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(); diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 2dcb37736d84..86a4fe75f453 100644 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -86,6 +86,10 @@ kallsyms() kallsymopt="${kallsymopt} --page-offset=$CONFIG_PAGE_OFFSET" fi + if [ -n "${CONFIG_X86_64}" ]; then + kallsymopt="${kallsymopt} --absolute-percpu" + fi + local aflags="${KBUILD_AFLAGS} ${KBUILD_AFLAGS_KERNEL} \ ${NOSTDINC_FLAGS} ${LINUXINCLUDE} ${KBUILD_CPPFLAGS}" -- cgit v1.2.3