diff options
Diffstat (limited to 'arch/arm/kernel/module.c')
-rw-r--r-- | arch/arm/kernel/module.c | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c index dab48f27263f..d1731e39b496 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,20 @@ 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_V4BX: | ||
159 | /* Preserve Rm and the condition code. Alter | ||
160 | * other bits to re-code instruction as | ||
161 | * MOV PC,Rm. | ||
162 | */ | ||
163 | *(u32 *)loc &= 0xf000000f; | ||
164 | *(u32 *)loc |= 0x01a0f000; | ||
165 | break; | ||
166 | |||
167 | case R_ARM_PREL31: | ||
168 | offset = *(u32 *)loc + sym->st_value - loc; | ||
169 | *(u32 *)loc = offset & 0x7fffffff; | ||
170 | break; | ||
171 | |||
135 | default: | 172 | default: |
136 | printk(KERN_ERR "%s: unknown relocation: %u\n", | 173 | printk(KERN_ERR "%s: unknown relocation: %u\n", |
137 | module->name, ELF32_R_TYPE(rel->r_info)); | 174 | module->name, ELF32_R_TYPE(rel->r_info)); |
@@ -150,14 +187,50 @@ apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab, | |||
150 | return -ENOEXEC; | 187 | return -ENOEXEC; |
151 | } | 188 | } |
152 | 189 | ||
190 | #ifdef CONFIG_ARM_UNWIND | ||
191 | static void register_unwind_tables(struct module *mod) | ||
192 | { | ||
193 | if (mod->arch.unw_sec_init && mod->arch.sec_init_text) | ||
194 | mod->arch.unwind_init = | ||
195 | unwind_table_add(mod->arch.unw_sec_init->sh_addr, | ||
196 | mod->arch.unw_sec_init->sh_size, | ||
197 | mod->arch.sec_init_text->sh_addr, | ||
198 | mod->arch.sec_init_text->sh_size); | ||
199 | if (mod->arch.unw_sec_devinit && mod->arch.sec_devinit_text) | ||
200 | mod->arch.unwind_devinit = | ||
201 | unwind_table_add(mod->arch.unw_sec_devinit->sh_addr, | ||
202 | mod->arch.unw_sec_devinit->sh_size, | ||
203 | mod->arch.sec_devinit_text->sh_addr, | ||
204 | mod->arch.sec_devinit_text->sh_size); | ||
205 | if (mod->arch.unw_sec_core && mod->arch.sec_core_text) | ||
206 | mod->arch.unwind_core = | ||
207 | unwind_table_add(mod->arch.unw_sec_core->sh_addr, | ||
208 | mod->arch.unw_sec_core->sh_size, | ||
209 | mod->arch.sec_core_text->sh_addr, | ||
210 | mod->arch.sec_core_text->sh_size); | ||
211 | } | ||
212 | |||
213 | static void unregister_unwind_tables(struct module *mod) | ||
214 | { | ||
215 | unwind_table_del(mod->arch.unwind_init); | ||
216 | unwind_table_del(mod->arch.unwind_devinit); | ||
217 | unwind_table_del(mod->arch.unwind_core); | ||
218 | } | ||
219 | #else | ||
220 | static inline void register_unwind_tables(struct module *mod) { } | ||
221 | static inline void unregister_unwind_tables(struct module *mod) { } | ||
222 | #endif | ||
223 | |||
153 | int | 224 | int |
154 | module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs, | 225 | module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs, |
155 | struct module *module) | 226 | struct module *module) |
156 | { | 227 | { |
228 | register_unwind_tables(module); | ||
157 | return 0; | 229 | return 0; |
158 | } | 230 | } |
159 | 231 | ||
160 | void | 232 | void |
161 | module_arch_cleanup(struct module *mod) | 233 | module_arch_cleanup(struct module *mod) |
162 | { | 234 | { |
235 | unregister_unwind_tables(mod); | ||
163 | } | 236 | } |