kallsyms: fix percpu vars on x86-64 with relocation.
authorRusty Russell <rusty@rustcorp.com.au>
Mon, 17 Mar 2014 03:35:46 +0000 (14:05 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Mon, 17 Mar 2014 04:25:27 +0000 (14:55 +1030)
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 <ahonig@google.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Acked-by: Kees Cook <keescook@chromium.org>
scripts/kallsyms.c
scripts/link-vmlinux.sh

index 3df15f5e7c55384868448a9cdd7493a5ce4aa316..1237dd7fb4cac7c2f863b413399df17a9e0c8beb 100644 (file)
@@ -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();
index 2dcb37736d8469a0e18e4c1ba262281e3fd8cf37..86a4fe75f453735936e3b218f885dcd887216659 100644 (file)
@@ -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}"