diff options
author | Catalin Marinas <catalin.marinas@arm.com> | 2009-02-11 07:09:54 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-02-19 06:27:19 -0500 |
commit | 2e1926e7b5d39eb31880152d636e8d8d011888cb (patch) | |
tree | 3d871e05d14db1358f065cd87e310c6f59430cb8 | |
parent | bff595c15c92b9c5c8f3d32edefcef6c3cbdd59f (diff) |
[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>
-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; | |||
50 | #define R_ARM_ABS32 2 | 50 | #define R_ARM_ABS32 2 |
51 | #define R_ARM_CALL 28 | 51 | #define R_ARM_CALL 28 |
52 | #define R_ARM_JUMP24 29 | 52 | #define R_ARM_JUMP24 29 |
53 | #define R_ARM_PREL31 42 | ||
53 | 54 | ||
54 | /* | 55 | /* |
55 | * These are used to set parameters in the core dumps. | 56 | * 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 @@ | |||
1 | #ifndef _ASM_ARM_MODULE_H | 1 | #ifndef _ASM_ARM_MODULE_H |
2 | #define _ASM_ARM_MODULE_H | 2 | #define _ASM_ARM_MODULE_H |
3 | 3 | ||
4 | struct mod_arch_specific | ||
5 | { | ||
6 | int foo; | ||
7 | }; | ||
8 | |||
9 | #define Elf_Shdr Elf32_Shdr | 4 | #define Elf_Shdr Elf32_Shdr |
10 | #define Elf_Sym Elf32_Sym | 5 | #define Elf_Sym Elf32_Sym |
11 | #define Elf_Ehdr Elf32_Ehdr | 6 | #define Elf_Ehdr Elf32_Ehdr |
12 | 7 | ||
8 | struct unwind_table; | ||
9 | |||
10 | struct mod_arch_specific | ||
11 | { | ||
12 | #ifdef CONFIG_ARM_UNWIND | ||
13 | Elf_Shdr *unw_sec_init; | ||
14 | Elf_Shdr *unw_sec_devinit; | ||
15 | Elf_Shdr *unw_sec_core; | ||
16 | Elf_Shdr *sec_init_text; | ||
17 | Elf_Shdr *sec_devinit_text; | ||
18 | Elf_Shdr *sec_core_text; | ||
19 | struct unwind_table *unwind_init; | ||
20 | struct unwind_table *unwind_devinit; | ||
21 | struct unwind_table *unwind_core; | ||
22 | #endif | ||
23 | }; | ||
24 | |||
13 | /* | 25 | /* |
14 | * Include the ARM architecture version. | 26 | * Include the ARM architecture version. |
15 | */ | 27 | */ |
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 @@ | |||
22 | 22 | ||
23 | #include <asm/pgtable.h> | 23 | #include <asm/pgtable.h> |
24 | #include <asm/sections.h> | 24 | #include <asm/sections.h> |
25 | #include <asm/unwind.h> | ||
25 | 26 | ||
26 | #ifdef CONFIG_XIP_KERNEL | 27 | #ifdef CONFIG_XIP_KERNEL |
27 | /* | 28 | /* |
@@ -66,6 +67,24 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, | |||
66 | char *secstrings, | 67 | char *secstrings, |
67 | struct module *mod) | 68 | struct module *mod) |
68 | { | 69 | { |
70 | #ifdef CONFIG_ARM_UNWIND | ||
71 | Elf_Shdr *s, *sechdrs_end = sechdrs + hdr->e_shnum; | ||
72 | |||
73 | for (s = sechdrs; s < sechdrs_end; s++) { | ||
74 | if (strcmp(".ARM.exidx.init.text", secstrings + s->sh_name) == 0) | ||
75 | mod->arch.unw_sec_init = s; | ||
76 | else if (strcmp(".ARM.exidx.devinit.text", secstrings + s->sh_name) == 0) | ||
77 | mod->arch.unw_sec_devinit = s; | ||
78 | else if (strcmp(".ARM.exidx", secstrings + s->sh_name) == 0) | ||
79 | mod->arch.unw_sec_core = s; | ||
80 | else if (strcmp(".init.text", secstrings + s->sh_name) == 0) | ||
81 | mod->arch.sec_init_text = s; | ||
82 | else if (strcmp(".devinit.text", secstrings + s->sh_name) == 0) | ||
83 | mod->arch.sec_devinit_text = s; | ||
84 | else if (strcmp(".text", secstrings + s->sh_name) == 0) | ||
85 | mod->arch.sec_core_text = s; | ||
86 | } | ||
87 | #endif | ||
69 | return 0; | 88 | return 0; |
70 | } | 89 | } |
71 | 90 | ||
@@ -104,6 +123,10 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, | |||
104 | loc = dstsec->sh_addr + rel->r_offset; | 123 | loc = dstsec->sh_addr + rel->r_offset; |
105 | 124 | ||
106 | switch (ELF32_R_TYPE(rel->r_info)) { | 125 | switch (ELF32_R_TYPE(rel->r_info)) { |
126 | case R_ARM_NONE: | ||
127 | /* ignore */ | ||
128 | break; | ||
129 | |||
107 | case R_ARM_ABS32: | 130 | case R_ARM_ABS32: |
108 | *(u32 *)loc += sym->st_value; | 131 | *(u32 *)loc += sym->st_value; |
109 | break; | 132 | break; |
@@ -132,6 +155,11 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, | |||
132 | *(u32 *)loc |= offset & 0x00ffffff; | 155 | *(u32 *)loc |= offset & 0x00ffffff; |
133 | break; | 156 | break; |
134 | 157 | ||
158 | case R_ARM_PREL31: | ||
159 | offset = *(u32 *)loc + sym->st_value - loc; | ||
160 | *(u32 *)loc = offset & 0x7fffffff; | ||
161 | break; | ||
162 | |||
135 | default: | 163 | default: |
136 | printk(KERN_ERR "%s: unknown relocation: %u\n", | 164 | printk(KERN_ERR "%s: unknown relocation: %u\n", |
137 | module->name, ELF32_R_TYPE(rel->r_info)); | 165 | module->name, ELF32_R_TYPE(rel->r_info)); |
@@ -150,14 +178,50 @@ apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab, | |||
150 | return -ENOEXEC; | 178 | return -ENOEXEC; |
151 | } | 179 | } |
152 | 180 | ||
181 | #ifdef CONFIG_ARM_UNWIND | ||
182 | static void register_unwind_tables(struct module *mod) | ||
183 | { | ||
184 | if (mod->arch.unw_sec_init && mod->arch.sec_init_text) | ||
185 | mod->arch.unwind_init = | ||
186 | unwind_table_add(mod->arch.unw_sec_init->sh_addr, | ||
187 | mod->arch.unw_sec_init->sh_size, | ||
188 | mod->arch.sec_init_text->sh_addr, | ||
189 | mod->arch.sec_init_text->sh_size); | ||
190 | if (mod->arch.unw_sec_devinit && mod->arch.sec_devinit_text) | ||
191 | mod->arch.unwind_devinit = | ||
192 | unwind_table_add(mod->arch.unw_sec_devinit->sh_addr, | ||
193 | mod->arch.unw_sec_devinit->sh_size, | ||
194 | mod->arch.sec_devinit_text->sh_addr, | ||
195 | mod->arch.sec_devinit_text->sh_size); | ||
196 | if (mod->arch.unw_sec_core && mod->arch.sec_core_text) | ||
197 | mod->arch.unwind_core = | ||
198 | unwind_table_add(mod->arch.unw_sec_core->sh_addr, | ||
199 | mod->arch.unw_sec_core->sh_size, | ||
200 | mod->arch.sec_core_text->sh_addr, | ||
201 | mod->arch.sec_core_text->sh_size); | ||
202 | } | ||
203 | |||
204 | static void unregister_unwind_tables(struct module *mod) | ||
205 | { | ||
206 | unwind_table_del(mod->arch.unwind_init); | ||
207 | unwind_table_del(mod->arch.unwind_devinit); | ||
208 | unwind_table_del(mod->arch.unwind_core); | ||
209 | } | ||
210 | #else | ||
211 | static inline void register_unwind_tables(struct module *mod) { } | ||
212 | static inline void unregister_unwind_tables(struct module *mod) { } | ||
213 | #endif | ||
214 | |||
153 | int | 215 | int |
154 | module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs, | 216 | module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs, |
155 | struct module *module) | 217 | struct module *module) |
156 | { | 218 | { |
219 | register_unwind_tables(module); | ||
157 | return 0; | 220 | return 0; |
158 | } | 221 | } |
159 | 222 | ||
160 | void | 223 | void |
161 | module_arch_cleanup(struct module *mod) | 224 | module_arch_cleanup(struct module *mod) |
162 | { | 225 | { |
226 | unregister_unwind_tables(mod); | ||
163 | } | 227 | } |