Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARM: memcpy: use frame pointer as unwind anchor

The memcpy template is a bit unusual in the way it manages the stack
pointer: depending on the execution path through the function, the SP
assumes different values as different subsets of the register file are
preserved and restored again. This is problematic when it comes to EHABI
unwind info, as it is not instruction accurate, and does not allow
tracking the SP value as it changes.

Commit 279f487e0b471 ("ARM: 8225/1: Add unwinding support for memory
copy functions") addressed this by carving up the function in different
chunks as far as the unwinder is concerned, and keeping a set of unwind
directives for each of them, each corresponding with the state of the
stack pointer during execution of the chunk in question. This not only
duplicates unwind info unnecessarily, but it also complicates unwinding
the stack upon overflow.

Instead, let's do what the compiler does when the SP is updated halfway
through a function, which is to use a frame pointer and emit the
appropriate unwind directives to communicate this to the unwinder.

Note that Thumb-2 uses R7 for this, while ARM uses R11 aka FP. So let's
avoid touching R7 in the body of the template, so that Thumb-2 can use
it as the frame pointer. R11 was not modified in the first place.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Tested-by: Keith Packard <keithpac@amazon.com>
Tested-by: Marc Zyngier <maz@kernel.org>
Tested-by: Vladimir Murzin <vladimir.murzin@arm.com> # ARMv7M

+38 -68
+5 -8
arch/arm/lib/copy_from_user.S
··· 91 91 strb\cond \reg, [\ptr], #1 92 92 .endm 93 93 94 - .macro enter reg1 reg2 94 + .macro enter regs:vararg 95 95 mov r3, #0 96 - stmdb sp!, {r0, r2, r3, \reg1, \reg2} 96 + UNWIND( .save {r0, r2, r3, \regs} ) 97 + stmdb sp!, {r0, r2, r3, \regs} 97 98 .endm 98 99 99 - .macro usave reg1 reg2 100 - UNWIND( .save {r0, r2, r3, \reg1, \reg2} ) 101 - .endm 102 - 103 - .macro exit reg1 reg2 100 + .macro exit regs:vararg 104 101 add sp, sp, #8 105 - ldmfd sp!, {r0, \reg1, \reg2} 102 + ldmfd sp!, {r0, \regs} 106 103 .endm 107 104 108 105 .text
+23 -44
arch/arm/lib/copy_template.S
··· 69 69 * than one 32bit instruction in Thumb-2) 70 70 */ 71 71 72 - 73 72 UNWIND( .fnstart ) 74 - enter r4, lr 75 - UNWIND( .fnend ) 76 - 77 - UNWIND( .fnstart ) 78 - usave r4, lr @ in first stmdb block 73 + enter r4, UNWIND(fpreg,) lr 74 + UNWIND( .setfp fpreg, sp ) 75 + UNWIND( mov fpreg, sp ) 79 76 80 77 subs r2, r2, #4 81 78 blt 8f ··· 83 86 bne 10f 84 87 85 88 1: subs r2, r2, #(28) 86 - stmfd sp!, {r5 - r8} 87 - UNWIND( .fnend ) 88 - 89 - UNWIND( .fnstart ) 90 - usave r4, lr 91 - UNWIND( .save {r5 - r8} ) @ in second stmfd block 89 + stmfd sp!, {r5, r6, r8, r9} 92 90 blt 5f 93 91 94 92 CALGN( ands ip, r0, #31 ) ··· 102 110 PLD( pld [r1, #92] ) 103 111 104 112 3: PLD( pld [r1, #124] ) 105 - 4: ldr8w r1, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f 113 + 4: ldr8w r1, r3, r4, r5, r6, r8, r9, ip, lr, abort=20f 106 114 subs r2, r2, #32 107 - str8w r0, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f 115 + str8w r0, r3, r4, r5, r6, r8, r9, ip, lr, abort=20f 108 116 bge 3b 109 117 PLD( cmn r2, #96 ) 110 118 PLD( bge 4b ) ··· 124 132 ldr1w r1, r4, abort=20f 125 133 ldr1w r1, r5, abort=20f 126 134 ldr1w r1, r6, abort=20f 127 - ldr1w r1, r7, abort=20f 128 135 ldr1w r1, r8, abort=20f 136 + ldr1w r1, r9, abort=20f 129 137 ldr1w r1, lr, abort=20f 130 138 131 139 #if LDR1W_SHIFT < STR1W_SHIFT ··· 142 150 str1w r0, r4, abort=20f 143 151 str1w r0, r5, abort=20f 144 152 str1w r0, r6, abort=20f 145 - str1w r0, r7, abort=20f 146 153 str1w r0, r8, abort=20f 154 + str1w r0, r9, abort=20f 147 155 str1w r0, lr, abort=20f 148 156 149 157 CALGN( bcs 2b ) 150 158 151 - 7: ldmfd sp!, {r5 - r8} 152 - UNWIND( .fnend ) @ end of second stmfd block 159 + 7: ldmfd sp!, {r5, r6, r8, r9} 153 160 154 - UNWIND( .fnstart ) 155 - usave r4, lr @ still in first stmdb block 156 161 8: movs r2, r2, lsl #31 157 162 ldr1b r1, r3, ne, abort=21f 158 163 ldr1b r1, r4, cs, abort=21f ··· 158 169 str1b r0, r4, cs, abort=21f 159 170 str1b r0, ip, cs, abort=21f 160 171 161 - exit r4, pc 172 + exit r4, UNWIND(fpreg,) pc 162 173 163 174 9: rsb ip, ip, #4 164 175 cmp ip, #2 ··· 178 189 ldr1w r1, lr, abort=21f 179 190 beq 17f 180 191 bgt 18f 181 - UNWIND( .fnend ) 182 192 183 193 184 194 .macro forward_copy_shift pull push 185 195 186 - UNWIND( .fnstart ) 187 - usave r4, lr @ still in first stmdb block 188 196 subs r2, r2, #28 189 197 blt 14f 190 198 ··· 191 205 CALGN( subcc r2, r2, ip ) 192 206 CALGN( bcc 15f ) 193 207 194 - 11: stmfd sp!, {r5 - r9} 195 - UNWIND( .fnend ) 208 + 11: stmfd sp!, {r5, r6, r8 - r10} 196 209 197 - UNWIND( .fnstart ) 198 - usave r4, lr 199 - UNWIND( .save {r5 - r9} ) @ in new second stmfd block 200 210 PLD( pld [r1, #0] ) 201 211 PLD( subs r2, r2, #96 ) 202 212 PLD( pld [r1, #28] ) ··· 201 219 PLD( pld [r1, #92] ) 202 220 203 221 12: PLD( pld [r1, #124] ) 204 - 13: ldr4w r1, r4, r5, r6, r7, abort=19f 222 + 13: ldr4w r1, r4, r5, r6, r8, abort=19f 205 223 mov r3, lr, lspull #\pull 206 224 subs r2, r2, #32 207 - ldr4w r1, r8, r9, ip, lr, abort=19f 225 + ldr4w r1, r9, r10, ip, lr, abort=19f 208 226 orr r3, r3, r4, lspush #\push 209 227 mov r4, r4, lspull #\pull 210 228 orr r4, r4, r5, lspush #\push 211 229 mov r5, r5, lspull #\pull 212 230 orr r5, r5, r6, lspush #\push 213 231 mov r6, r6, lspull #\pull 214 - orr r6, r6, r7, lspush #\push 215 - mov r7, r7, lspull #\pull 216 - orr r7, r7, r8, lspush #\push 232 + orr r6, r6, r8, lspush #\push 217 233 mov r8, r8, lspull #\pull 218 234 orr r8, r8, r9, lspush #\push 219 235 mov r9, r9, lspull #\pull 220 - orr r9, r9, ip, lspush #\push 236 + orr r9, r9, r10, lspush #\push 237 + mov r10, r10, lspull #\pull 238 + orr r10, r10, ip, lspush #\push 221 239 mov ip, ip, lspull #\pull 222 240 orr ip, ip, lr, lspush #\push 223 - str8w r0, r3, r4, r5, r6, r7, r8, r9, ip, abort=19f 241 + str8w r0, r3, r4, r5, r6, r8, r9, r10, ip, abort=19f 224 242 bge 12b 225 243 PLD( cmn r2, #96 ) 226 244 PLD( bge 13b ) 227 245 228 - ldmfd sp!, {r5 - r9} 229 - UNWIND( .fnend ) @ end of the second stmfd block 246 + ldmfd sp!, {r5, r6, r8 - r10} 230 247 231 - UNWIND( .fnstart ) 232 - usave r4, lr @ still in first stmdb block 233 248 14: ands ip, r2, #28 234 249 beq 16f 235 250 ··· 241 262 242 263 16: sub r1, r1, #(\push / 8) 243 264 b 8b 244 - UNWIND( .fnend ) 245 265 246 266 .endm 247 267 ··· 251 273 252 274 18: forward_copy_shift pull=24 push=8 253 275 276 + UNWIND( .fnend ) 254 277 255 278 /* 256 279 * Abort preamble and completion macros. ··· 261 282 */ 262 283 263 284 .macro copy_abort_preamble 264 - 19: ldmfd sp!, {r5 - r9} 285 + 19: ldmfd sp!, {r5, r6, r8 - r10} 265 286 b 21f 266 - 20: ldmfd sp!, {r5 - r8} 287 + 20: ldmfd sp!, {r5, r6, r8, r9} 267 288 21: 268 289 .endm 269 290 270 291 .macro copy_abort_end 271 - ldmfd sp!, {r4, pc} 292 + ldmfd sp!, {r4, UNWIND(fpreg,) pc} 272 293 .endm 273 294
+5 -8
arch/arm/lib/copy_to_user.S
··· 90 90 strusr \reg, \ptr, 1, \cond, abort=\abort 91 91 .endm 92 92 93 - .macro enter reg1 reg2 93 + .macro enter regs:vararg 94 94 mov r3, #0 95 - stmdb sp!, {r0, r2, r3, \reg1, \reg2} 95 + UNWIND( .save {r0, r2, r3, \regs} ) 96 + stmdb sp!, {r0, r2, r3, \regs} 96 97 .endm 97 98 98 - .macro usave reg1 reg2 99 - UNWIND( .save {r0, r2, r3, \reg1, \reg2} ) 100 - .endm 101 - 102 - .macro exit reg1 reg2 99 + .macro exit regs:vararg 103 100 add sp, sp, #8 104 - ldmfd sp!, {r0, \reg1, \reg2} 101 + ldmfd sp!, {r0, \regs} 105 102 .endm 106 103 107 104 .text
+5 -8
arch/arm/lib/memcpy.S
··· 42 42 strb\cond \reg, [\ptr], #1 43 43 .endm 44 44 45 - .macro enter reg1 reg2 46 - stmdb sp!, {r0, \reg1, \reg2} 45 + .macro enter regs:vararg 46 + UNWIND( .save {r0, \regs} ) 47 + stmdb sp!, {r0, \regs} 47 48 .endm 48 49 49 - .macro usave reg1 reg2 50 - UNWIND( .save {r0, \reg1, \reg2} ) 51 - .endm 52 - 53 - .macro exit reg1 reg2 54 - ldmfd sp!, {r0, \reg1, \reg2} 50 + .macro exit regs:vararg 51 + ldmfd sp!, {r0, \regs} 55 52 .endm 56 53 57 54 .text