diff options
Diffstat (limited to 'arch/arm/kernel/entry-common.S')
-rw-r--r-- | arch/arm/kernel/entry-common.S | 124 |
1 files changed, 80 insertions, 44 deletions
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 2c1db77d7848..f05a35a59694 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S | |||
@@ -92,75 +92,111 @@ ENDPROC(ret_from_fork) | |||
92 | #define CALL(x) .long x | 92 | #define CALL(x) .long x |
93 | 93 | ||
94 | #ifdef CONFIG_FUNCTION_TRACER | 94 | #ifdef CONFIG_FUNCTION_TRACER |
95 | /* | ||
96 | * When compiling with -pg, gcc inserts a call to the mcount routine at the | ||
97 | * start of every function. In mcount, apart from the function's address (in | ||
98 | * lr), we need to get hold of the function's caller's address. | ||
99 | * | ||
100 | * Older GCCs (pre-4.4) inserted a call to a routine called mcount like this: | ||
101 | * | ||
102 | * bl mcount | ||
103 | * | ||
104 | * These versions have the limitation that in order for the mcount routine to | ||
105 | * be able to determine the function's caller's address, an APCS-style frame | ||
106 | * pointer (which is set up with something like the code below) is required. | ||
107 | * | ||
108 | * mov ip, sp | ||
109 | * push {fp, ip, lr, pc} | ||
110 | * sub fp, ip, #4 | ||
111 | * | ||
112 | * With EABI, these frame pointers are not available unless -mapcs-frame is | ||
113 | * specified, and if building as Thumb-2, not even then. | ||
114 | * | ||
115 | * Newer GCCs (4.4+) solve this problem by introducing a new version of mcount, | ||
116 | * with call sites like: | ||
117 | * | ||
118 | * push {lr} | ||
119 | * bl __gnu_mcount_nc | ||
120 | * | ||
121 | * With these compilers, frame pointers are not necessary. | ||
122 | * | ||
123 | * mcount can be thought of as a function called in the middle of a subroutine | ||
124 | * call. As such, it needs to be transparent for both the caller and the | ||
125 | * callee: the original lr needs to be restored when leaving mcount, and no | ||
126 | * registers should be clobbered. (In the __gnu_mcount_nc implementation, we | ||
127 | * clobber the ip register. This is OK because the ARM calling convention | ||
128 | * allows it to be clobbered in subroutines and doesn't use it to hold | ||
129 | * parameters.) | ||
130 | */ | ||
95 | #ifdef CONFIG_DYNAMIC_FTRACE | 131 | #ifdef CONFIG_DYNAMIC_FTRACE |
96 | ENTRY(mcount) | 132 | ENTRY(mcount) |
97 | stmdb sp!, {r0-r3, lr} | 133 | stmdb sp!, {r0-r3, lr} |
98 | mov r0, lr | 134 | mov r0, lr |
99 | sub r0, r0, #MCOUNT_INSN_SIZE | 135 | sub r0, r0, #MCOUNT_INSN_SIZE |
100 | 136 | ||
101 | .globl mcount_call | 137 | .globl mcount_call |
102 | mcount_call: | 138 | mcount_call: |
103 | bl ftrace_stub | 139 | bl ftrace_stub |
104 | ldr lr, [fp, #-4] @ restore lr | 140 | ldr lr, [fp, #-4] @ restore lr |
105 | ldmia sp!, {r0-r3, pc} | 141 | ldmia sp!, {r0-r3, pc} |
106 | 142 | ||
107 | ENTRY(ftrace_caller) | 143 | ENTRY(ftrace_caller) |
108 | stmdb sp!, {r0-r3, lr} | 144 | stmdb sp!, {r0-r3, lr} |
109 | ldr r1, [fp, #-4] | 145 | ldr r1, [fp, #-4] |
110 | mov r0, lr | 146 | mov r0, lr |
111 | sub r0, r0, #MCOUNT_INSN_SIZE | 147 | sub r0, r0, #MCOUNT_INSN_SIZE |
112 | 148 | ||
113 | .globl ftrace_call | 149 | .globl ftrace_call |
114 | ftrace_call: | 150 | ftrace_call: |
115 | bl ftrace_stub | 151 | bl ftrace_stub |
116 | ldr lr, [fp, #-4] @ restore lr | 152 | ldr lr, [fp, #-4] @ restore lr |
117 | ldmia sp!, {r0-r3, pc} | 153 | ldmia sp!, {r0-r3, pc} |
118 | 154 | ||
119 | #else | 155 | #else |
120 | 156 | ||
121 | ENTRY(__gnu_mcount_nc) | 157 | ENTRY(__gnu_mcount_nc) |
122 | stmdb sp!, {r0-r3, lr} | 158 | stmdb sp!, {r0-r3, lr} |
123 | ldr r0, =ftrace_trace_function | 159 | ldr r0, =ftrace_trace_function |
124 | ldr r2, [r0] | 160 | ldr r2, [r0] |
125 | adr r0, ftrace_stub | 161 | adr r0, ftrace_stub |
126 | cmp r0, r2 | 162 | cmp r0, r2 |
127 | bne gnu_trace | 163 | bne gnu_trace |
128 | ldmia sp!, {r0-r3, ip, lr} | 164 | ldmia sp!, {r0-r3, ip, lr} |
129 | mov pc, ip | 165 | mov pc, ip |
130 | 166 | ||
131 | gnu_trace: | 167 | gnu_trace: |
132 | ldr r1, [sp, #20] @ lr of instrumented routine | 168 | ldr r1, [sp, #20] @ lr of instrumented routine |
133 | mov r0, lr | 169 | mov r0, lr |
134 | sub r0, r0, #MCOUNT_INSN_SIZE | 170 | sub r0, r0, #MCOUNT_INSN_SIZE |
135 | mov lr, pc | 171 | mov lr, pc |
136 | mov pc, r2 | 172 | mov pc, r2 |
137 | ldmia sp!, {r0-r3, ip, lr} | 173 | ldmia sp!, {r0-r3, ip, lr} |
138 | mov pc, ip | 174 | mov pc, ip |
139 | 175 | ||
140 | ENTRY(mcount) | 176 | ENTRY(mcount) |
141 | stmdb sp!, {r0-r3, lr} | 177 | stmdb sp!, {r0-r3, lr} |
142 | ldr r0, =ftrace_trace_function | 178 | ldr r0, =ftrace_trace_function |
143 | ldr r2, [r0] | 179 | ldr r2, [r0] |
144 | adr r0, ftrace_stub | 180 | adr r0, ftrace_stub |
145 | cmp r0, r2 | 181 | cmp r0, r2 |
146 | bne trace | 182 | bne trace |
147 | ldr lr, [fp, #-4] @ restore lr | 183 | ldr lr, [fp, #-4] @ restore lr |
148 | ldmia sp!, {r0-r3, pc} | 184 | ldmia sp!, {r0-r3, pc} |
149 | 185 | ||
150 | trace: | 186 | trace: |
151 | ldr r1, [fp, #-4] @ lr of instrumented routine | 187 | ldr r1, [fp, #-4] @ lr of instrumented routine |
152 | mov r0, lr | 188 | mov r0, lr |
153 | sub r0, r0, #MCOUNT_INSN_SIZE | 189 | sub r0, r0, #MCOUNT_INSN_SIZE |
154 | mov lr, pc | 190 | mov lr, pc |
155 | mov pc, r2 | 191 | mov pc, r2 |
156 | ldr lr, [fp, #-4] @ restore lr | 192 | ldr lr, [fp, #-4] @ restore lr |
157 | ldmia sp!, {r0-r3, pc} | 193 | ldmia sp!, {r0-r3, pc} |
158 | 194 | ||
159 | #endif /* CONFIG_DYNAMIC_FTRACE */ | 195 | #endif /* CONFIG_DYNAMIC_FTRACE */ |
160 | 196 | ||
161 | .globl ftrace_stub | 197 | .globl ftrace_stub |
162 | ftrace_stub: | 198 | ftrace_stub: |
163 | mov pc, lr | 199 | mov pc, lr |
164 | 200 | ||
165 | #endif /* CONFIG_FUNCTION_TRACER */ | 201 | #endif /* CONFIG_FUNCTION_TRACER */ |
166 | 202 | ||