Hi NIck, On Mon, 26 Sept 2022 at 20:37, Nick Desaulniers <ndesaulniers@xxxxxxxxxx> wrote: > > commit 1069c1dd20a3 ("ARM: 9231/1: Recover kretprobes return address for > EABI stack unwinder") > tickled a bug in clang's integrated assembler where the .save and .pad > directives must have corresponding .fnstart directives. The integrated > assembler is unaware that the compiler will be generating the .fnstart > directive. > > arch/arm/probes/kprobes/core.c:409:30: error: .fnstart must precede > .save or .vsave directives > <inline asm>:3:2: note: instantiated into assembly here > .save {sp, lr, pc} > ^ > arch/arm/probes/kprobes/core.c:412:29: error: .fnstart must precede > .pad directive > <inline asm>:6:2: note: instantiated into assembly here > .pad #52 > ^ > > __kretprobe_trampoline's definition is already entirely inline asm. Move > it to out-of-line asm to avoid breaking the build. > I think this is the right approach. I don't think is is even specified what exactly attribute((naked)) entails in the context of unwind information Some nits below, but regardless: Acked-by: Ard Biesheuvel <ardb@xxxxxxxxxx> > Link: https://github.com/llvm/llvm-project/issues/57993 > Link: https://github.com/ClangBuiltLinux/linux/issues/1718 > Reported-by: Nathan Chancellor <nathan@xxxxxxxxxx> > Reported-by: Linux Kernel Functional Testing <lkft@xxxxxxxxxx> > Suggested-by: Logan Chien <loganchien@xxxxxxxxxx> > Signed-off-by: Nick Desaulniers <ndesaulniers@xxxxxxxxxx> > --- > Note: I wasn't quite sure if a Fixes tag against 1069c1dd20a3 was > appropriate here? Either way, if 1069c1dd20a3 gets picked up for stable > without this, it will break clang builds. > > arch/arm/probes/kprobes/Makefile | 1 + > arch/arm/probes/kprobes/core.c | 54 ++---------------- > .../arm/probes/kprobes/kretprobe-trampoline.S | 55 +++++++++++++++++++ > include/asm-generic/kprobes.h | 13 +++-- > 4 files changed, 69 insertions(+), 54 deletions(-) > create mode 100644 arch/arm/probes/kprobes/kretprobe-trampoline.S > > diff --git a/arch/arm/probes/kprobes/Makefile b/arch/arm/probes/kprobes/Makefile > index 6159010dac4a..cdbe9dd99e28 100644 > --- a/arch/arm/probes/kprobes/Makefile > +++ b/arch/arm/probes/kprobes/Makefile > @@ -3,6 +3,7 @@ KASAN_SANITIZE_actions-common.o := n > KASAN_SANITIZE_actions-arm.o := n > KASAN_SANITIZE_actions-thumb.o := n > obj-$(CONFIG_KPROBES) += core.o actions-common.o checkers-common.o > +obj-$(CONFIG_KPROBES) += kretprobe-trampoline.o > obj-$(CONFIG_ARM_KPROBES_TEST) += test-kprobes.o > test-kprobes-objs := test-core.o > > diff --git a/arch/arm/probes/kprobes/core.c b/arch/arm/probes/kprobes/core.c > index 1435b508aa36..17d7e0259e63 100644 > --- a/arch/arm/probes/kprobes/core.c > +++ b/arch/arm/probes/kprobes/core.c > @@ -375,58 +375,10 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self, > return NOTIFY_DONE; > } > > -/* > - * When a retprobed function returns, trampoline_handler() is called, > - * calling the kretprobe's handler. We construct a struct pt_regs to > - * give a view of registers r0-r11, sp, lr, and pc to the user > - * return-handler. This is not a complete pt_regs structure, but that > - * should be enough for stacktrace from the return handler with or > - * without pt_regs. > - */ > -void __naked __kprobes __kretprobe_trampoline(void) > -{ > - __asm__ __volatile__ ( > - "ldr lr, =__kretprobe_trampoline \n\t" > -#ifdef CONFIG_FRAME_POINTER > - /* __kretprobe_trampoline makes a framepointer on pt_regs. */ > -#ifdef CONFIG_CC_IS_CLANG > - "stmdb sp, {sp, lr, pc} \n\t" > - "sub sp, sp, #12 \n\t" > - /* In clang case, pt_regs->ip = lr. */ > - "stmdb sp!, {r0 - r11, lr} \n\t" > - /* fp points regs->r11 (fp) */ > - "add fp, sp, #44 \n\t" > -#else /* !CONFIG_CC_IS_CLANG */ > - /* In gcc case, pt_regs->ip = fp. */ > - "stmdb sp, {fp, sp, lr, pc} \n\t" > - "sub sp, sp, #16 \n\t" > - "stmdb sp!, {r0 - r11} \n\t" > - /* fp points regs->r15 (pc) */ > - "add fp, sp, #60 \n\t" > -#endif /* CONFIG_CC_IS_CLANG */ > -#else /* !CONFIG_FRAME_POINTER */ > - /* store SP, LR on stack and add EABI unwind hint */ > - "stmdb sp, {sp, lr, pc} \n\t" > - ".save {sp, lr, pc} \n\t" > - "sub sp, sp, #16 \n\t" > - "stmdb sp!, {r0 - r11} \n\t" > - ".pad #52 \n\t" > -#endif /* CONFIG_FRAME_POINTER */ > - "mov r0, sp \n\t" > - "bl trampoline_handler \n\t" > - "mov lr, r0 \n\t" > - "ldmia sp!, {r0 - r11} \n\t" > - "add sp, sp, #16 \n\t" > -#ifdef CONFIG_THUMB2_KERNEL > - "bx lr \n\t" > -#else > - "mov pc, lr \n\t" > -#endif > - : : : "memory"); > -} > +/*void __kretprobe_trampoline(void);*/ > > /* Called from __kretprobe_trampoline */ > -static __used __kprobes void *trampoline_handler(struct pt_regs *regs) > +__kprobes void *trampoline_handler(struct pt_regs *regs) > { > return (void *)kretprobe_trampoline_handler(regs, (void *)regs->TRAMP_FP); > } > @@ -434,6 +386,8 @@ static __used __kprobes void *trampoline_handler(struct pt_regs *regs) > void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, > struct pt_regs *regs) > { > + extern void __kretprobe_trampoline(void); > + > ri->ret_addr = (kprobe_opcode_t *)regs->ARM_lr; > ri->fp = (void *)regs->TRAMP_FP; > > diff --git a/arch/arm/probes/kprobes/kretprobe-trampoline.S b/arch/arm/probes/kprobes/kretprobe-trampoline.S > new file mode 100644 > index 000000000000..261c99b8c17f > --- /dev/null > +++ b/arch/arm/probes/kprobes/kretprobe-trampoline.S > @@ -0,0 +1,55 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > + > +#include <linux/linkage.h> > +#include <asm/unwind.h> > +#include <asm-generic/kprobes.h> > + > +/* > + * When a retprobed function returns, trampoline_handler() is called, > + * calling the kretprobe's handler. We construct a struct pt_regs to > + * give a view of registers r0-r11, sp, lr, and pc to the user > + * return-handler. This is not a complete pt_regs structure, but that > + * should be enough for stacktrace from the return handler with or > + * without pt_regs. > + */ > +__KPROBE > +SYM_FUNC_START(__kretprobe_trampoline) > +UNWIND(.fnstart) > + ldr lr, =__kretprobe_trampoline Nit: adr lr, __kretprobe_trampoline Now that you made a clean spot, might as well clean this up a bit (although perhaps not in the same patch). I'd merge the !CONFIG_FRAME_POINTER and CC_IS_CLANG cases, and just put the UNWIND() directives in there. > +#ifdef CONFIG_FRAME_POINTER Drop this > + /* __kretprobe_trampoline makes a framepointer on pt_regs. */ > +#ifdef CONFIG_CC_IS_CLANG || !defined(CONFIG_FRAME_POINTER) > + stmdb sp, {sp, lr, pc} > + sub sp, sp, #12 UNWIND(.save {sp, lr, pc}) > + /* In clang case, pt_regs->ip = lr. */ For .S, it is more idiomatic to put the comments in a column on the right. This reduces visual clutter a lot, so I think it would be worth the effort. > + stmdb sp!, {r0 - r11, lr} UNWIND(.pad #52) > + /* fp points regs->r11 (fp) */ > + add fp, sp, #44 > +#else /* !CONFIG_CC_IS_CLANG */ > + /* In gcc case, pt_regs->ip = fp. */ > + stmdb sp, {fp, sp, lr, pc} > + sub sp, sp, #16 > + stmdb sp!, {r0 - r11} > + /* fp points regs->r15 (pc) */ > + add fp, sp, #60 > +#endif /* CONFIG_CC_IS_CLANG */ > +#else /* !CONFIG_FRAME_POINTER */ > + /* store SP, LR on stack and add EABI unwind hint */ > + stmdb sp, {sp, lr, pc} > +UNWIND(.save {sp, lr, pc}) > + sub sp, sp, #16 > + stmdb sp!, {r0 - r11} > +UNWIND(.pad #52) > +#endif /* CONFIG_FRAME_POINTER */ > + mov r0, sp > + bl trampoline_handler > + mov lr, r0 > + ldmia sp!, {r0 - r11} > + add sp, sp, #16 > +#ifdef CONFIG_THUMB2_KERNEL > + bx lr > +#else > + mov pc, lr > +#endif Please use 'ret lr' here. Note that this code does not even compile for Thumb2, but 'bx lr' should be used at least on v6+ in any case. > +UNWIND(.fnend) > +SYM_FUNC_END(__kretprobe_trampoline) > diff --git a/include/asm-generic/kprobes.h b/include/asm-generic/kprobes.h > index 060eab094e5a..1509daa281b8 100644 > --- a/include/asm-generic/kprobes.h > +++ b/include/asm-generic/kprobes.h > @@ -2,7 +2,11 @@ > #ifndef _ASM_GENERIC_KPROBES_H > #define _ASM_GENERIC_KPROBES_H > > -#if defined(__KERNEL__) && !defined(__ASSEMBLY__) > +#ifdef __KERNEL__ > + > +#ifdef __ASSEMBLY__ > +# define __KPROBE .section ".kprobes.text", "ax" > +#else > #ifdef CONFIG_KPROBES > /* > * Blacklist ganerating macro. Specify functions which is not probed > @@ -16,11 +20,12 @@ static unsigned long __used \ > /* Use this to forbid a kprobes attach on very low level functions */ > # define __kprobes __section(".kprobes.text") > # define nokprobe_inline __always_inline > -#else > +#else /* !defined(CONFIG_KPROBES) */ > # define NOKPROBE_SYMBOL(fname) > # define __kprobes > # define nokprobe_inline inline > -#endif > -#endif /* defined(__KERNEL__) && !defined(__ASSEMBLY__) */ > +#endif /* defined(CONFIG_KPROBES) */ > +#endif /* defined(__ASSEMBLY__) */ > +#endif /* defined(__KERNEL__) */ > > #endif /* _ASM_GENERIC_KPROBES_H */ > -- > 2.37.3.998.g577e59143f-goog >