diff options
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/boot/compressed/vmlinux.lds.in | 5 | ||||
-rw-r--r-- | arch/arm/include/asm/unwind.h | 69 | ||||
-rw-r--r-- | arch/arm/kernel/setup.c | 3 | ||||
-rw-r--r-- | arch/arm/kernel/traps.c | 10 | ||||
-rw-r--r-- | arch/arm/kernel/unwind.c | 434 | ||||
-rw-r--r-- | arch/arm/kernel/vmlinux.lds.S | 19 |
6 files changed, 540 insertions, 0 deletions
diff --git a/arch/arm/boot/compressed/vmlinux.lds.in b/arch/arm/boot/compressed/vmlinux.lds.in index 153a07e7222b..a5924b9b88bd 100644 --- a/arch/arm/boot/compressed/vmlinux.lds.in +++ b/arch/arm/boot/compressed/vmlinux.lds.in | |||
@@ -11,6 +11,11 @@ OUTPUT_ARCH(arm) | |||
11 | ENTRY(_start) | 11 | ENTRY(_start) |
12 | SECTIONS | 12 | SECTIONS |
13 | { | 13 | { |
14 | /DISCARD/ : { | ||
15 | *(.ARM.exidx*) | ||
16 | *(.ARM.extab*) | ||
17 | } | ||
18 | |||
14 | . = TEXT_START; | 19 | . = TEXT_START; |
15 | _text = .; | 20 | _text = .; |
16 | 21 | ||
diff --git a/arch/arm/include/asm/unwind.h b/arch/arm/include/asm/unwind.h new file mode 100644 index 000000000000..a5edf421005c --- /dev/null +++ b/arch/arm/include/asm/unwind.h | |||
@@ -0,0 +1,69 @@ | |||
1 | /* | ||
2 | * arch/arm/include/asm/unwind.h | ||
3 | * | ||
4 | * Copyright (C) 2008 ARM Limited | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | #ifndef __ASM_UNWIND_H | ||
21 | #define __ASM_UNWIND_H | ||
22 | |||
23 | #ifndef __ASSEMBLY__ | ||
24 | |||
25 | /* Unwind reason code according the the ARM EABI documents */ | ||
26 | enum unwind_reason_code { | ||
27 | URC_OK = 0, /* operation completed successfully */ | ||
28 | URC_CONTINUE_UNWIND = 8, | ||
29 | URC_FAILURE = 9 /* unspecified failure of some kind */ | ||
30 | }; | ||
31 | |||
32 | struct unwind_idx { | ||
33 | unsigned long addr; | ||
34 | unsigned long insn; | ||
35 | }; | ||
36 | |||
37 | struct unwind_table { | ||
38 | struct list_head list; | ||
39 | struct unwind_idx *start; | ||
40 | struct unwind_idx *stop; | ||
41 | unsigned long begin_addr; | ||
42 | unsigned long end_addr; | ||
43 | }; | ||
44 | |||
45 | extern struct unwind_table *unwind_table_add(unsigned long start, | ||
46 | unsigned long size, | ||
47 | unsigned long text_addr, | ||
48 | unsigned long text_size); | ||
49 | extern void unwind_table_del(struct unwind_table *tab); | ||
50 | extern void unwind_backtrace(struct pt_regs *regs, struct task_struct *tsk); | ||
51 | |||
52 | #ifdef CONFIG_ARM_UNWIND | ||
53 | extern int __init unwind_init(void); | ||
54 | #else | ||
55 | static inline int __init unwind_init(void) | ||
56 | { | ||
57 | return 0; | ||
58 | } | ||
59 | #endif | ||
60 | |||
61 | #endif /* !__ASSEMBLY__ */ | ||
62 | |||
63 | #ifdef CONFIG_ARM_UNWIND | ||
64 | #define UNWIND(code...) code | ||
65 | #else | ||
66 | #define UNWIND(code...) | ||
67 | #endif | ||
68 | |||
69 | #endif /* __ASM_UNWIND_H */ | ||
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 645ec7436681..8d21427bb679 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <asm/mach/irq.h> | 40 | #include <asm/mach/irq.h> |
41 | #include <asm/mach/time.h> | 41 | #include <asm/mach/time.h> |
42 | #include <asm/traps.h> | 42 | #include <asm/traps.h> |
43 | #include <asm/unwind.h> | ||
43 | 44 | ||
44 | #include "compat.h" | 45 | #include "compat.h" |
45 | #include "atags.h" | 46 | #include "atags.h" |
@@ -684,6 +685,8 @@ void __init setup_arch(char **cmdline_p) | |||
684 | struct machine_desc *mdesc; | 685 | struct machine_desc *mdesc; |
685 | char *from = default_command_line; | 686 | char *from = default_command_line; |
686 | 687 | ||
688 | unwind_init(); | ||
689 | |||
687 | setup_processor(); | 690 | setup_processor(); |
688 | mdesc = setup_machine(machine_arch_type); | 691 | mdesc = setup_machine(machine_arch_type); |
689 | machine_name = mdesc->name; | 692 | machine_name = mdesc->name; |
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index f68ca0fb0ed7..57eb0f6f6005 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <asm/system.h> | 27 | #include <asm/system.h> |
28 | #include <asm/unistd.h> | 28 | #include <asm/unistd.h> |
29 | #include <asm/traps.h> | 29 | #include <asm/traps.h> |
30 | #include <asm/unwind.h> | ||
30 | 31 | ||
31 | #include "ptrace.h" | 32 | #include "ptrace.h" |
32 | #include "signal.h" | 33 | #include "signal.h" |
@@ -61,6 +62,7 @@ void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long | |||
61 | dump_mem("Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs)); | 62 | dump_mem("Exception stack", frame + 4, frame + 4 + sizeof(struct pt_regs)); |
62 | } | 63 | } |
63 | 64 | ||
65 | #ifndef CONFIG_ARM_UNWIND | ||
64 | /* | 66 | /* |
65 | * Stack pointers should always be within the kernels view of | 67 | * Stack pointers should always be within the kernels view of |
66 | * physical memory. If it is not there, then we can't dump | 68 | * physical memory. If it is not there, then we can't dump |
@@ -74,6 +76,7 @@ static int verify_stack(unsigned long sp) | |||
74 | 76 | ||
75 | return 0; | 77 | return 0; |
76 | } | 78 | } |
79 | #endif | ||
77 | 80 | ||
78 | /* | 81 | /* |
79 | * Dump out the contents of some memory nicely... | 82 | * Dump out the contents of some memory nicely... |
@@ -150,6 +153,12 @@ static void dump_instr(struct pt_regs *regs) | |||
150 | set_fs(fs); | 153 | set_fs(fs); |
151 | } | 154 | } |
152 | 155 | ||
156 | #ifdef CONFIG_ARM_UNWIND | ||
157 | static inline void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) | ||
158 | { | ||
159 | unwind_backtrace(regs, tsk); | ||
160 | } | ||
161 | #else | ||
153 | static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) | 162 | static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) |
154 | { | 163 | { |
155 | unsigned int fp, mode; | 164 | unsigned int fp, mode; |
@@ -184,6 +193,7 @@ static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) | |||
184 | if (ok) | 193 | if (ok) |
185 | c_backtrace(fp, mode); | 194 | c_backtrace(fp, mode); |
186 | } | 195 | } |
196 | #endif | ||
187 | 197 | ||
188 | void dump_stack(void) | 198 | void dump_stack(void) |
189 | { | 199 | { |
diff --git a/arch/arm/kernel/unwind.c b/arch/arm/kernel/unwind.c new file mode 100644 index 000000000000..1dedc2c7ff49 --- /dev/null +++ b/arch/arm/kernel/unwind.c | |||
@@ -0,0 +1,434 @@ | |||
1 | /* | ||
2 | * arch/arm/kernel/unwind.c | ||
3 | * | ||
4 | * Copyright (C) 2008 ARM Limited | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | * | ||
20 | * Stack unwinding support for ARM | ||
21 | * | ||
22 | * An ARM EABI version of gcc is required to generate the unwind | ||
23 | * tables. For information about the structure of the unwind tables, | ||
24 | * see "Exception Handling ABI for the ARM Architecture" at: | ||
25 | * | ||
26 | * http://infocenter.arm.com/help/topic/com.arm.doc.subset.swdev.abi/index.html | ||
27 | */ | ||
28 | |||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/sched.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/spinlock.h> | ||
35 | #include <linux/list.h> | ||
36 | |||
37 | #include <asm/stacktrace.h> | ||
38 | #include <asm/traps.h> | ||
39 | #include <asm/unwind.h> | ||
40 | |||
41 | /* Dummy functions to avoid linker complaints */ | ||
42 | void __aeabi_unwind_cpp_pr0(void) | ||
43 | { | ||
44 | }; | ||
45 | EXPORT_SYMBOL(__aeabi_unwind_cpp_pr0); | ||
46 | |||
47 | void __aeabi_unwind_cpp_pr1(void) | ||
48 | { | ||
49 | }; | ||
50 | EXPORT_SYMBOL(__aeabi_unwind_cpp_pr1); | ||
51 | |||
52 | void __aeabi_unwind_cpp_pr2(void) | ||
53 | { | ||
54 | }; | ||
55 | EXPORT_SYMBOL(__aeabi_unwind_cpp_pr2); | ||
56 | |||
57 | struct unwind_ctrl_block { | ||
58 | unsigned long vrs[16]; /* virtual register set */ | ||
59 | unsigned long *insn; /* pointer to the current instructions word */ | ||
60 | int entries; /* number of entries left to interpret */ | ||
61 | int byte; /* current byte number in the instructions word */ | ||
62 | }; | ||
63 | |||
64 | enum regs { | ||
65 | FP = 11, | ||
66 | SP = 13, | ||
67 | LR = 14, | ||
68 | PC = 15 | ||
69 | }; | ||
70 | |||
71 | extern struct unwind_idx __start_unwind_idx[]; | ||
72 | extern struct unwind_idx __stop_unwind_idx[]; | ||
73 | |||
74 | static DEFINE_SPINLOCK(unwind_lock); | ||
75 | static LIST_HEAD(unwind_tables); | ||
76 | |||
77 | /* Convert a prel31 symbol to an absolute address */ | ||
78 | #define prel31_to_addr(ptr) \ | ||
79 | ({ \ | ||
80 | /* sign-extend to 32 bits */ \ | ||
81 | long offset = (((long)*(ptr)) << 1) >> 1; \ | ||
82 | (unsigned long)(ptr) + offset; \ | ||
83 | }) | ||
84 | |||
85 | /* | ||
86 | * Binary search in the unwind index. The entries entries are | ||
87 | * guaranteed to be sorted in ascending order by the linker. | ||
88 | */ | ||
89 | static struct unwind_idx *search_index(unsigned long addr, | ||
90 | struct unwind_idx *first, | ||
91 | struct unwind_idx *last) | ||
92 | { | ||
93 | pr_debug("%s(%08lx, %p, %p)\n", __func__, addr, first, last); | ||
94 | |||
95 | if (addr < first->addr) { | ||
96 | pr_warning("unwind: Unknown symbol address %08lx\n", addr); | ||
97 | return NULL; | ||
98 | } else if (addr >= last->addr) | ||
99 | return last; | ||
100 | |||
101 | while (first < last - 1) { | ||
102 | struct unwind_idx *mid = first + ((last - first + 1) >> 1); | ||
103 | |||
104 | if (addr < mid->addr) | ||
105 | last = mid; | ||
106 | else | ||
107 | first = mid; | ||
108 | } | ||
109 | |||
110 | return first; | ||
111 | } | ||
112 | |||
113 | static struct unwind_idx *unwind_find_idx(unsigned long addr) | ||
114 | { | ||
115 | struct unwind_idx *idx = NULL; | ||
116 | unsigned long flags; | ||
117 | |||
118 | pr_debug("%s(%08lx)\n", __func__, addr); | ||
119 | |||
120 | if (core_kernel_text(addr)) | ||
121 | /* main unwind table */ | ||
122 | idx = search_index(addr, __start_unwind_idx, | ||
123 | __stop_unwind_idx - 1); | ||
124 | else { | ||
125 | /* module unwind tables */ | ||
126 | struct unwind_table *table; | ||
127 | |||
128 | spin_lock_irqsave(&unwind_lock, flags); | ||
129 | list_for_each_entry(table, &unwind_tables, list) { | ||
130 | if (addr >= table->begin_addr && | ||
131 | addr < table->end_addr) { | ||
132 | idx = search_index(addr, table->start, | ||
133 | table->stop - 1); | ||
134 | break; | ||
135 | } | ||
136 | } | ||
137 | spin_unlock_irqrestore(&unwind_lock, flags); | ||
138 | } | ||
139 | |||
140 | pr_debug("%s: idx = %p\n", __func__, idx); | ||
141 | return idx; | ||
142 | } | ||
143 | |||
144 | static unsigned long unwind_get_byte(struct unwind_ctrl_block *ctrl) | ||
145 | { | ||
146 | unsigned long ret; | ||
147 | |||
148 | if (ctrl->entries <= 0) { | ||
149 | pr_warning("unwind: Corrupt unwind table\n"); | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | ret = (*ctrl->insn >> (ctrl->byte * 8)) & 0xff; | ||
154 | |||
155 | if (ctrl->byte == 0) { | ||
156 | ctrl->insn++; | ||
157 | ctrl->entries--; | ||
158 | ctrl->byte = 3; | ||
159 | } else | ||
160 | ctrl->byte--; | ||
161 | |||
162 | return ret; | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | * Execute the current unwind instruction. | ||
167 | */ | ||
168 | static int unwind_exec_insn(struct unwind_ctrl_block *ctrl) | ||
169 | { | ||
170 | unsigned long insn = unwind_get_byte(ctrl); | ||
171 | |||
172 | pr_debug("%s: insn = %08lx\n", __func__, insn); | ||
173 | |||
174 | if ((insn & 0xc0) == 0x00) | ||
175 | ctrl->vrs[SP] += ((insn & 0x3f) << 2) + 4; | ||
176 | else if ((insn & 0xc0) == 0x40) | ||
177 | ctrl->vrs[SP] -= ((insn & 0x3f) << 2) + 4; | ||
178 | else if ((insn & 0xf0) == 0x80) { | ||
179 | unsigned long mask; | ||
180 | unsigned long *vsp = (unsigned long *)ctrl->vrs[SP]; | ||
181 | int load_sp, reg = 4; | ||
182 | |||
183 | insn = (insn << 8) | unwind_get_byte(ctrl); | ||
184 | mask = insn & 0x0fff; | ||
185 | if (mask == 0) { | ||
186 | pr_warning("unwind: 'Refuse to unwind' instruction %04lx\n", | ||
187 | insn); | ||
188 | return -URC_FAILURE; | ||
189 | } | ||
190 | |||
191 | /* pop R4-R15 according to mask */ | ||
192 | load_sp = mask & (1 << (13 - 4)); | ||
193 | while (mask) { | ||
194 | if (mask & 1) | ||
195 | ctrl->vrs[reg] = *vsp++; | ||
196 | mask >>= 1; | ||
197 | reg++; | ||
198 | } | ||
199 | if (!load_sp) | ||
200 | ctrl->vrs[SP] = (unsigned long)vsp; | ||
201 | } else if ((insn & 0xf0) == 0x90 && | ||
202 | (insn & 0x0d) != 0x0d) | ||
203 | ctrl->vrs[SP] = ctrl->vrs[insn & 0x0f]; | ||
204 | else if ((insn & 0xf0) == 0xa0) { | ||
205 | unsigned long *vsp = (unsigned long *)ctrl->vrs[SP]; | ||
206 | int reg; | ||
207 | |||
208 | /* pop R4-R[4+bbb] */ | ||
209 | for (reg = 4; reg <= 4 + (insn & 7); reg++) | ||
210 | ctrl->vrs[reg] = *vsp++; | ||
211 | if (insn & 0x80) | ||
212 | ctrl->vrs[14] = *vsp++; | ||
213 | ctrl->vrs[SP] = (unsigned long)vsp; | ||
214 | } else if (insn == 0xb0) { | ||
215 | ctrl->vrs[PC] = ctrl->vrs[LR]; | ||
216 | /* no further processing */ | ||
217 | ctrl->entries = 0; | ||
218 | } else if (insn == 0xb1) { | ||
219 | unsigned long mask = unwind_get_byte(ctrl); | ||
220 | unsigned long *vsp = (unsigned long *)ctrl->vrs[SP]; | ||
221 | int reg = 0; | ||
222 | |||
223 | if (mask == 0 || mask & 0xf0) { | ||
224 | pr_warning("unwind: Spare encoding %04lx\n", | ||
225 | (insn << 8) | mask); | ||
226 | return -URC_FAILURE; | ||
227 | } | ||
228 | |||
229 | /* pop R0-R3 according to mask */ | ||
230 | while (mask) { | ||
231 | if (mask & 1) | ||
232 | ctrl->vrs[reg] = *vsp++; | ||
233 | mask >>= 1; | ||
234 | reg++; | ||
235 | } | ||
236 | ctrl->vrs[SP] = (unsigned long)vsp; | ||
237 | } else if (insn == 0xb2) { | ||
238 | unsigned long uleb128 = unwind_get_byte(ctrl); | ||
239 | |||
240 | ctrl->vrs[SP] += 0x204 + (uleb128 << 2); | ||
241 | } else { | ||
242 | pr_warning("unwind: Unhandled instruction %02lx\n", insn); | ||
243 | return -URC_FAILURE; | ||
244 | } | ||
245 | |||
246 | pr_debug("%s: fp = %08lx sp = %08lx lr = %08lx pc = %08lx\n", __func__, | ||
247 | ctrl->vrs[FP], ctrl->vrs[SP], ctrl->vrs[LR], ctrl->vrs[PC]); | ||
248 | |||
249 | return URC_OK; | ||
250 | } | ||
251 | |||
252 | /* | ||
253 | * Unwind a single frame starting with *sp for the symbol at *pc. It | ||
254 | * updates the *pc and *sp with the new values. | ||
255 | */ | ||
256 | int unwind_frame(struct stackframe *frame) | ||
257 | { | ||
258 | unsigned long high, low; | ||
259 | struct unwind_idx *idx; | ||
260 | struct unwind_ctrl_block ctrl; | ||
261 | |||
262 | /* only go to a higher address on the stack */ | ||
263 | low = frame->sp; | ||
264 | high = ALIGN(low, THREAD_SIZE) + THREAD_SIZE; | ||
265 | |||
266 | pr_debug("%s(pc = %08lx lr = %08lx sp = %08lx)\n", __func__, | ||
267 | frame->pc, frame->lr, frame->sp); | ||
268 | |||
269 | if (!kernel_text_address(frame->pc)) | ||
270 | return -URC_FAILURE; | ||
271 | |||
272 | idx = unwind_find_idx(frame->pc); | ||
273 | if (!idx) { | ||
274 | pr_warning("unwind: Index not found %08lx\n", frame->pc); | ||
275 | return -URC_FAILURE; | ||
276 | } | ||
277 | |||
278 | ctrl.vrs[FP] = frame->fp; | ||
279 | ctrl.vrs[SP] = frame->sp; | ||
280 | ctrl.vrs[LR] = frame->lr; | ||
281 | ctrl.vrs[PC] = 0; | ||
282 | |||
283 | if (idx->insn == 1) | ||
284 | /* can't unwind */ | ||
285 | return -URC_FAILURE; | ||
286 | else if ((idx->insn & 0x80000000) == 0) | ||
287 | /* prel31 to the unwind table */ | ||
288 | ctrl.insn = (unsigned long *)prel31_to_addr(&idx->insn); | ||
289 | else if ((idx->insn & 0xff000000) == 0x80000000) | ||
290 | /* only personality routine 0 supported in the index */ | ||
291 | ctrl.insn = &idx->insn; | ||
292 | else { | ||
293 | pr_warning("unwind: Unsupported personality routine %08lx in the index at %p\n", | ||
294 | idx->insn, idx); | ||
295 | return -URC_FAILURE; | ||
296 | } | ||
297 | |||
298 | /* check the personality routine */ | ||
299 | if ((*ctrl.insn & 0xff000000) == 0x80000000) { | ||
300 | ctrl.byte = 2; | ||
301 | ctrl.entries = 1; | ||
302 | } else if ((*ctrl.insn & 0xff000000) == 0x81000000) { | ||
303 | ctrl.byte = 1; | ||
304 | ctrl.entries = 1 + ((*ctrl.insn & 0x00ff0000) >> 16); | ||
305 | } else { | ||
306 | pr_warning("unwind: Unsupported personality routine %08lx at %p\n", | ||
307 | *ctrl.insn, ctrl.insn); | ||
308 | return -URC_FAILURE; | ||
309 | } | ||
310 | |||
311 | while (ctrl.entries > 0) { | ||
312 | int urc; | ||
313 | |||
314 | if (ctrl.vrs[SP] < low || ctrl.vrs[SP] >= high) | ||
315 | return -URC_FAILURE; | ||
316 | urc = unwind_exec_insn(&ctrl); | ||
317 | if (urc < 0) | ||
318 | return urc; | ||
319 | } | ||
320 | |||
321 | if (ctrl.vrs[PC] == 0) | ||
322 | ctrl.vrs[PC] = ctrl.vrs[LR]; | ||
323 | |||
324 | frame->fp = ctrl.vrs[FP]; | ||
325 | frame->sp = ctrl.vrs[SP]; | ||
326 | frame->lr = ctrl.vrs[LR]; | ||
327 | frame->pc = ctrl.vrs[PC]; | ||
328 | |||
329 | return URC_OK; | ||
330 | } | ||
331 | |||
332 | void unwind_backtrace(struct pt_regs *regs, struct task_struct *tsk) | ||
333 | { | ||
334 | struct stackframe frame; | ||
335 | unsigned long high, low; | ||
336 | register unsigned long current_sp asm ("sp"); | ||
337 | |||
338 | pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk); | ||
339 | |||
340 | if (!tsk) | ||
341 | tsk = current; | ||
342 | |||
343 | if (regs) { | ||
344 | frame.fp = regs->ARM_fp; | ||
345 | frame.sp = regs->ARM_sp; | ||
346 | frame.lr = regs->ARM_lr; | ||
347 | frame.pc = regs->ARM_pc; | ||
348 | } else if (tsk == current) { | ||
349 | frame.fp = (unsigned long)__builtin_frame_address(0); | ||
350 | frame.sp = current_sp; | ||
351 | frame.lr = (unsigned long)__builtin_return_address(0); | ||
352 | frame.pc = (unsigned long)unwind_backtrace; | ||
353 | } else { | ||
354 | /* task blocked in __switch_to */ | ||
355 | frame.fp = thread_saved_fp(tsk); | ||
356 | frame.sp = thread_saved_sp(tsk); | ||
357 | /* | ||
358 | * The function calling __switch_to cannot be a leaf function | ||
359 | * so LR is recovered from the stack. | ||
360 | */ | ||
361 | frame.lr = 0; | ||
362 | frame.pc = thread_saved_pc(tsk); | ||
363 | } | ||
364 | |||
365 | low = frame.sp & ~(THREAD_SIZE - 1); | ||
366 | high = low + THREAD_SIZE; | ||
367 | |||
368 | while (1) { | ||
369 | int urc; | ||
370 | unsigned long where = frame.pc; | ||
371 | |||
372 | urc = unwind_frame(&frame); | ||
373 | if (urc < 0) | ||
374 | break; | ||
375 | dump_backtrace_entry(where, frame.pc, frame.sp - 4); | ||
376 | } | ||
377 | } | ||
378 | |||
379 | struct unwind_table *unwind_table_add(unsigned long start, unsigned long size, | ||
380 | unsigned long text_addr, | ||
381 | unsigned long text_size) | ||
382 | { | ||
383 | unsigned long flags; | ||
384 | struct unwind_idx *idx; | ||
385 | struct unwind_table *tab = kmalloc(sizeof(*tab), GFP_KERNEL); | ||
386 | |||
387 | pr_debug("%s(%08lx, %08lx, %08lx, %08lx)\n", __func__, start, size, | ||
388 | text_addr, text_size); | ||
389 | |||
390 | if (!tab) | ||
391 | return tab; | ||
392 | |||
393 | tab->start = (struct unwind_idx *)start; | ||
394 | tab->stop = (struct unwind_idx *)(start + size); | ||
395 | tab->begin_addr = text_addr; | ||
396 | tab->end_addr = text_addr + text_size; | ||
397 | |||
398 | /* Convert the symbol addresses to absolute values */ | ||
399 | for (idx = tab->start; idx < tab->stop; idx++) | ||
400 | idx->addr = prel31_to_addr(&idx->addr); | ||
401 | |||
402 | spin_lock_irqsave(&unwind_lock, flags); | ||
403 | list_add_tail(&tab->list, &unwind_tables); | ||
404 | spin_unlock_irqrestore(&unwind_lock, flags); | ||
405 | |||
406 | return tab; | ||
407 | } | ||
408 | |||
409 | void unwind_table_del(struct unwind_table *tab) | ||
410 | { | ||
411 | unsigned long flags; | ||
412 | |||
413 | if (!tab) | ||
414 | return; | ||
415 | |||
416 | spin_lock_irqsave(&unwind_lock, flags); | ||
417 | list_del(&tab->list); | ||
418 | spin_unlock_irqrestore(&unwind_lock, flags); | ||
419 | |||
420 | kfree(tab); | ||
421 | } | ||
422 | |||
423 | int __init unwind_init(void) | ||
424 | { | ||
425 | struct unwind_idx *idx; | ||
426 | |||
427 | /* Convert the symbol addresses to absolute values */ | ||
428 | for (idx = __start_unwind_idx; idx < __stop_unwind_idx; idx++) | ||
429 | idx->addr = prel31_to_addr(&idx->addr); | ||
430 | |||
431 | pr_debug("unwind: ARM stack unwinding initialised\n"); | ||
432 | |||
433 | return 0; | ||
434 | } | ||
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 00216071eaf7..5f664599c945 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S | |||
@@ -80,6 +80,8 @@ SECTIONS | |||
80 | EXIT_TEXT | 80 | EXIT_TEXT |
81 | EXIT_DATA | 81 | EXIT_DATA |
82 | *(.exitcall.exit) | 82 | *(.exitcall.exit) |
83 | *(.ARM.exidx.exit.text) | ||
84 | *(.ARM.extab.exit.text) | ||
83 | #ifndef CONFIG_MMU | 85 | #ifndef CONFIG_MMU |
84 | *(.fixup) | 86 | *(.fixup) |
85 | *(__ex_table) | 87 | *(__ex_table) |
@@ -110,6 +112,23 @@ SECTIONS | |||
110 | 112 | ||
111 | _etext = .; /* End of text and rodata section */ | 113 | _etext = .; /* End of text and rodata section */ |
112 | 114 | ||
115 | #ifdef CONFIG_ARM_UNWIND | ||
116 | /* | ||
117 | * Stack unwinding tables | ||
118 | */ | ||
119 | . = ALIGN(8); | ||
120 | .ARM.unwind_idx : { | ||
121 | __start_unwind_idx = .; | ||
122 | *(.ARM.exidx*) | ||
123 | __stop_unwind_idx = .; | ||
124 | } | ||
125 | .ARM.unwind_tab : { | ||
126 | __start_unwind_tab = .; | ||
127 | *(.ARM.extab*) | ||
128 | __stop_unwind_tab = .; | ||
129 | } | ||
130 | #endif | ||
131 | |||
113 | #ifdef CONFIG_XIP_KERNEL | 132 | #ifdef CONFIG_XIP_KERNEL |
114 | __data_loc = ALIGN(4); /* location in binary */ | 133 | __data_loc = ALIGN(4); /* location in binary */ |
115 | . = PAGE_OFFSET + TEXT_OFFSET; | 134 | . = PAGE_OFFSET + TEXT_OFFSET; |