diff options
author | Catalin Marinas <catalin.marinas@arm.com> | 2009-02-11 15:09:54 +0300 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-02-19 14:27:19 +0300 |
commit | 2e1926e7b5d39eb31880152d636e8d8d011888cb (patch) | |
tree | 3d871e05d14db1358f065cd87e310c6f59430cb8 /arch/arm | |
parent | bff595c15c92b9c5c8f3d32edefcef6c3cbdd59f (diff) | |
download | linux-2e1926e7b5d39eb31880152d636e8d8d011888cb.tar.xz |
[ARM] 5384/1: unwind: Add stack unwinding support for loadable modules
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>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/include/asm/elf.h | 1 | ||||
-rw-r--r-- | arch/arm/include/asm/module.h | 22 | ||||
-rw-r--r-- | arch/arm/kernel/module.c | 64 |
3 files changed, 82 insertions, 5 deletions
diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h index a58378c343b9..def8eac6e89d 100644 --- a/arch/arm/include/asm/elf.h +++ b/arch/arm/include/asm/elf.h @@ -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. diff --git a/arch/arm/include/asm/module.h b/arch/arm/include/asm/module.h index 24b168dc31a3..e4dfa69abb68 100644 --- a/arch/arm/include/asm/module.h +++ b/arch/arm/include/asm/module.h @@ -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. */ diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c index dab48f27263f..13dbd5bf5cc2 100644 --- a/arch/arm/kernel/module.c +++ b/arch/arm/kernel/module.c @@ -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); } |