blob: 7c22c12618cc66aee585d0be5b5205512048fc18 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
/*
* Arm specific backtracing code for oprofile
*
* Copyright 2005 Openedhand Ltd.
*
* Author: Richard Purdie <rpurdie@openedhand.com>
*
* Based on i386 oprofile backtrace code by John Levon, David Smith
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/oprofile.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <asm/ptrace.h>
#include <asm/uaccess.h>
/*
* The registers we're interested in are at the end of the variable
* length saved register structure. The fp points at the end of this
* structure so the address of this struct is:
* (struct frame_tail *)(xxx->fp)-1
*/
struct frame_tail {
struct frame_tail *fp;
unsigned long sp;
unsigned long lr;
} __attribute__((packed));
#ifdef CONFIG_FRAME_POINTER
static struct frame_tail* kernel_backtrace(struct frame_tail *tail)
{
oprofile_add_trace(tail->lr);
/* frame pointers should strictly progress back up the stack
* (towards higher addresses) */
if (tail >= tail->fp)
return NULL;
return tail->fp-1;
}
#endif
static struct frame_tail* user_backtrace(struct frame_tail *tail)
{
struct frame_tail buftail[2];
/* Also check accessibility of one struct frame_tail beyond */
if (!access_ok(VERIFY_READ, tail, sizeof(buftail)))
return NULL;
if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail)))
return NULL;
oprofile_add_trace(buftail[0].lr);
/* frame pointers should strictly progress back up the stack
* (towards higher addresses) */
if (tail >= buftail[0].fp)
return NULL;
return buftail[0].fp-1;
}
/*
* | | /\ Higher addresses
* | |
* --------------- stack base (address of current_thread_info)
* | thread info |
* . .
* | stack |
* --------------- saved regs->ARM_fp value if valid (frame_tail address)
* . .
* --------------- struct pt_regs stored on stack (struct pt_regs *)
* | |
* . .
* | |
* --------------- %esp
* | |
* | | \/ Lower addresses
*
* Thus, &pt_regs <-> stack base restricts the valid(ish) fp values
*/
static int valid_kernel_stack(struct frame_tail *tail, struct pt_regs *regs)
{
unsigned long tailaddr = (unsigned long)tail;
unsigned long stack = (unsigned long)regs;
unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE;
return (tailaddr > stack) && (tailaddr < stack_base);
}
void arm_backtrace(struct pt_regs * const regs, unsigned int depth)
{
struct frame_tail *tail;
tail = ((struct frame_tail *) regs->ARM_fp) - 1;
if (!user_mode(regs)) {
#ifdef CONFIG_FRAME_POINTER
while (depth-- && tail && valid_kernel_stack(tail, regs)) {
tail = kernel_backtrace(tail);
}
#endif
return;
}
while (depth-- && tail && !((unsigned long) tail & 3))
tail = user_backtrace(tail);
}
|