Merge tag 'md/4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shli/md
[cascardo/linux.git] / kernel / kallsyms.c
index 5c5987f..fafd1a3 100644 (file)
@@ -38,6 +38,7 @@
  * during the second link stage.
  */
 extern const unsigned long kallsyms_addresses[] __weak;
+extern const int kallsyms_offsets[] __weak;
 extern const u8 kallsyms_names[] __weak;
 
 /*
@@ -47,6 +48,9 @@ extern const u8 kallsyms_names[] __weak;
 extern const unsigned long kallsyms_num_syms
 __attribute__((weak, section(".rodata")));
 
+extern const unsigned long kallsyms_relative_base
+__attribute__((weak, section(".rodata")));
+
 extern const u8 kallsyms_token_table[] __weak;
 extern const u16 kallsyms_token_index[] __weak;
 
@@ -176,6 +180,23 @@ static unsigned int get_symbol_offset(unsigned long pos)
        return name - kallsyms_names;
 }
 
+static unsigned long kallsyms_sym_address(int idx)
+{
+       if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))
+               return kallsyms_addresses[idx];
+
+       /* values are unsigned offsets if --absolute-percpu is not in effect */
+       if (!IS_ENABLED(CONFIG_KALLSYMS_ABSOLUTE_PERCPU))
+               return kallsyms_relative_base + (u32)kallsyms_offsets[idx];
+
+       /* ...otherwise, positive offsets are absolute values */
+       if (kallsyms_offsets[idx] >= 0)
+               return kallsyms_offsets[idx];
+
+       /* ...and negative offsets are relative to kallsyms_relative_base - 1 */
+       return kallsyms_relative_base - 1 - kallsyms_offsets[idx];
+}
+
 /* Lookup the address for this symbol. Returns 0 if not found. */
 unsigned long kallsyms_lookup_name(const char *name)
 {
@@ -187,7 +208,7 @@ unsigned long kallsyms_lookup_name(const char *name)
                off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
 
                if (strcmp(namebuf, name) == 0)
-                       return kallsyms_addresses[i];
+                       return kallsyms_sym_address(i);
        }
        return module_kallsyms_lookup_name(name);
 }
@@ -204,7 +225,7 @@ int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
 
        for (i = 0, off = 0; i < kallsyms_num_syms; i++) {
                off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
-               ret = fn(data, namebuf, NULL, kallsyms_addresses[i]);
+               ret = fn(data, namebuf, NULL, kallsyms_sym_address(i));
                if (ret != 0)
                        return ret;
        }
@@ -220,7 +241,10 @@ static unsigned long get_symbol_pos(unsigned long addr,
        unsigned long i, low, high, mid;
 
        /* This kernel should never had been booted. */
-       BUG_ON(!kallsyms_addresses);
+       if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))
+               BUG_ON(!kallsyms_addresses);
+       else
+               BUG_ON(!kallsyms_offsets);
 
        /* Do a binary search on the sorted kallsyms_addresses array. */
        low = 0;
@@ -228,7 +252,7 @@ static unsigned long get_symbol_pos(unsigned long addr,
 
        while (high - low > 1) {
                mid = low + (high - low) / 2;
-               if (kallsyms_addresses[mid] <= addr)
+               if (kallsyms_sym_address(mid) <= addr)
                        low = mid;
                else
                        high = mid;
@@ -238,15 +262,15 @@ static unsigned long get_symbol_pos(unsigned long addr,
         * Search for the first aliased symbol. Aliased
         * symbols are symbols with the same address.
         */
-       while (low && kallsyms_addresses[low-1] == kallsyms_addresses[low])
+       while (low && kallsyms_sym_address(low-1) == kallsyms_sym_address(low))
                --low;
 
-       symbol_start = kallsyms_addresses[low];
+       symbol_start = kallsyms_sym_address(low);
 
        /* Search for next non-aliased symbol. */
        for (i = low + 1; i < kallsyms_num_syms; i++) {
-               if (kallsyms_addresses[i] > symbol_start) {
-                       symbol_end = kallsyms_addresses[i];
+               if (kallsyms_sym_address(i) > symbol_start) {
+                       symbol_end = kallsyms_sym_address(i);
                        break;
                }
        }
@@ -470,7 +494,7 @@ static unsigned long get_ksymbol_core(struct kallsym_iter *iter)
        unsigned off = iter->nameoff;
 
        iter->module_name[0] = '\0';
-       iter->value = kallsyms_addresses[iter->pos];
+       iter->value = kallsyms_sym_address(iter->pos);
 
        iter->type = kallsyms_get_symbol_type(off);