[ARM] 5384/1: unwind: Add stack unwinding support for loadable modules
authorCatalin Marinas <catalin.marinas@arm.com>
Wed, 11 Feb 2009 12:09:54 +0000 (13:09 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Thu, 19 Feb 2009 11:27:19 +0000 (11:27 +0000)
This patch adds ELF section parsing for the unwinding tables in loadable
modules together with the PREL31 relocation symbol resolving.

Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/include/asm/elf.h
arch/arm/include/asm/module.h
arch/arm/kernel/module.c

index a58378c..def8eac 100644 (file)
@@ -50,6 +50,7 @@ typedef struct user_fp elf_fpregset_t;
 #define R_ARM_ABS32    2
 #define R_ARM_CALL     28
 #define R_ARM_JUMP24   29
+#define R_ARM_PREL31   42
 
 /*
  * These are used to set parameters in the core dumps.
index 24b168d..e4dfa69 100644 (file)
@@ -1,15 +1,27 @@
 #ifndef _ASM_ARM_MODULE_H
 #define _ASM_ARM_MODULE_H
 
-struct mod_arch_specific
-{
-       int foo;
-};
-
 #define Elf_Shdr       Elf32_Shdr
 #define Elf_Sym                Elf32_Sym
 #define Elf_Ehdr       Elf32_Ehdr
 
+struct unwind_table;
+
+struct mod_arch_specific
+{
+#ifdef CONFIG_ARM_UNWIND
+       Elf_Shdr *unw_sec_init;
+       Elf_Shdr *unw_sec_devinit;
+       Elf_Shdr *unw_sec_core;
+       Elf_Shdr *sec_init_text;
+       Elf_Shdr *sec_devinit_text;
+       Elf_Shdr *sec_core_text;
+       struct unwind_table *unwind_init;
+       struct unwind_table *unwind_devinit;
+       struct unwind_table *unwind_core;
+#endif
+};
+
 /*
  * Include the ARM architecture version.
  */
index dab48f2..13dbd5b 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <asm/pgtable.h>
 #include <asm/sections.h>
+#include <asm/unwind.h>
 
 #ifdef CONFIG_XIP_KERNEL
 /*
@@ -66,6 +67,24 @@ int module_frob_arch_sections(Elf_Ehdr *hdr,
                              char *secstrings,
                              struct module *mod)
 {
+#ifdef CONFIG_ARM_UNWIND
+       Elf_Shdr *s, *sechdrs_end = sechdrs + hdr->e_shnum;
+
+       for (s = sechdrs; s < sechdrs_end; s++) {
+               if (strcmp(".ARM.exidx.init.text", secstrings + s->sh_name) == 0)
+                       mod->arch.unw_sec_init = s;
+               else if (strcmp(".ARM.exidx.devinit.text", secstrings + s->sh_name) == 0)
+                       mod->arch.unw_sec_devinit = s;
+               else if (strcmp(".ARM.exidx", secstrings + s->sh_name) == 0)
+                       mod->arch.unw_sec_core = s;
+               else if (strcmp(".init.text", secstrings + s->sh_name) == 0)
+                       mod->arch.sec_init_text = s;
+               else if (strcmp(".devinit.text", secstrings + s->sh_name) == 0)
+                       mod->arch.sec_devinit_text = s;
+               else if (strcmp(".text", secstrings + s->sh_name) == 0)
+                       mod->arch.sec_core_text = s;
+       }
+#endif
        return 0;
 }
 
@@ -104,6 +123,10 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex,
                loc = dstsec->sh_addr + rel->r_offset;
 
                switch (ELF32_R_TYPE(rel->r_info)) {
+               case R_ARM_NONE:
+                       /* ignore */
+                       break;
+
                case R_ARM_ABS32:
                        *(u32 *)loc += sym->st_value;
                        break;
@@ -132,6 +155,11 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex,
                        *(u32 *)loc |= offset & 0x00ffffff;
                        break;
 
+               case R_ARM_PREL31:
+                       offset = *(u32 *)loc + sym->st_value - loc;
+                       *(u32 *)loc = offset & 0x7fffffff;
+                       break;
+
                default:
                        printk(KERN_ERR "%s: unknown relocation: %u\n",
                               module->name, ELF32_R_TYPE(rel->r_info));
@@ -150,14 +178,50 @@ apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab,
        return -ENOEXEC;
 }
 
+#ifdef CONFIG_ARM_UNWIND
+static void register_unwind_tables(struct module *mod)
+{
+       if (mod->arch.unw_sec_init && mod->arch.sec_init_text)
+               mod->arch.unwind_init =
+                       unwind_table_add(mod->arch.unw_sec_init->sh_addr,
+                                        mod->arch.unw_sec_init->sh_size,
+                                        mod->arch.sec_init_text->sh_addr,
+                                        mod->arch.sec_init_text->sh_size);
+       if (mod->arch.unw_sec_devinit && mod->arch.sec_devinit_text)
+               mod->arch.unwind_devinit =
+                       unwind_table_add(mod->arch.unw_sec_devinit->sh_addr,
+                                        mod->arch.unw_sec_devinit->sh_size,
+                                        mod->arch.sec_devinit_text->sh_addr,
+                                        mod->arch.sec_devinit_text->sh_size);
+       if (mod->arch.unw_sec_core && mod->arch.sec_core_text)
+               mod->arch.unwind_core =
+                       unwind_table_add(mod->arch.unw_sec_core->sh_addr,
+                                        mod->arch.unw_sec_core->sh_size,
+                                        mod->arch.sec_core_text->sh_addr,
+                                        mod->arch.sec_core_text->sh_size);
+}
+
+static void unregister_unwind_tables(struct module *mod)
+{
+       unwind_table_del(mod->arch.unwind_init);
+       unwind_table_del(mod->arch.unwind_devinit);
+       unwind_table_del(mod->arch.unwind_core);
+}
+#else
+static inline void register_unwind_tables(struct module *mod) { }
+static inline void unregister_unwind_tables(struct module *mod) { }
+#endif
+
 int
 module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,
                struct module *module)
 {
+       register_unwind_tables(module);
        return 0;
 }
 
 void
 module_arch_cleanup(struct module *mod)
 {
+       unregister_unwind_tables(mod);
 }