[PATCH 2/2] arm64: ftrace: add support for far branches to dynamic ftrace

Steven Rostedt rostedt at goodmis.org
Mon Apr 10 10:39:35 EDT 2017


On Mon, 10 Apr 2017 15:22:24 +0100
Ard Biesheuvel <ard.biesheuvel at linaro.org> wrote:

> On 10 April 2017 at 15:13, Ard Biesheuvel <ard.biesheuvel at linaro.org> wrote:
> > Currently, dynamic ftrace support in the arm64 kernel assumes that all
> > core kernel code is within range of ordinary branch instructions in
> > module code, which is usually the case, but is no longer guaranteed now
> > that we have support for module PLTs and address space randomization.
> >
> > Since all patching of branch instructions involves function calls to
> > ftrace_caller(), we can emit the modules with a trampoline that has
> > unlimited range, and patch the branch instruction to call the trampoline
> > if ftrace_caller() itself is out of range.
> >
> > Signed-off-by: Ard Biesheuvel <ard.biesheuvel at linaro.org>
> > ---
> >  arch/arm64/Kconfig              |  2 +-
> >  arch/arm64/Makefile             |  3 ++
> >  arch/arm64/include/asm/module.h |  3 ++
> >  arch/arm64/kernel/ftrace.c      | 37 ++++++++++++++++++--
> >  arch/arm64/kernel/module-plts.c | 10 ++++++
> >  arch/arm64/lib/Makefile         |  2 ++
> >  arch/arm64/lib/ftrace-mod.S     | 25 +++++++++++++
> >  7 files changed, 78 insertions(+), 4 deletions(-)
> >
> > diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> > index e7f043efff41..31af7ea72072 100644
> > --- a/arch/arm64/Kconfig
> > +++ b/arch/arm64/Kconfig
> > @@ -982,7 +982,7 @@ config RANDOMIZE_BASE
> >
> >  config RANDOMIZE_MODULE_REGION_FULL
> >         bool "Randomize the module region independently from the core kernel"
> > -       depends on RANDOMIZE_BASE && !DYNAMIC_FTRACE
> > +       depends on RANDOMIZE_BASE
> >         default y
> >         help
> >           Randomizes the location of the module region without considering the
> > diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
> > index b9a4a934ca05..01498eab5ada 100644
> > --- a/arch/arm64/Makefile
> > +++ b/arch/arm64/Makefile
> > @@ -68,6 +68,9 @@ endif
> >
> >  ifeq ($(CONFIG_ARM64_MODULE_PLTS),y)
> >  KBUILD_LDFLAGS_MODULE  += -T $(srctree)/arch/arm64/kernel/module.lds
> > +ifeq ($(CONFIG_DYNAMIC_FTRACE),y)
> > +KBUILD_LDFLAGS_MODULE  += $(objtree)/arch/arm64/lib/ftrace-mod.o
> > +endif
> >  endif
> >
> >  # Default value
> > diff --git a/arch/arm64/include/asm/module.h b/arch/arm64/include/asm/module.h
> > index e12af6754634..2ff2b7782957 100644
> > --- a/arch/arm64/include/asm/module.h
> > +++ b/arch/arm64/include/asm/module.h
> > @@ -25,6 +25,9 @@ struct mod_arch_specific {
> >         struct elf64_shdr       *plt;
> >         int                     plt_num_entries;
> >         int                     plt_max_entries;
> > +
> > +       /* for CONFIG_DYNAMIC_FTRACE */
> > +       struct elf64_shdr       *ftrace_trampoline;
> >  };
> >  #endif
> >
> > diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c
> > index a8db6857cad6..8f5a7dc36c1b 100644
> > --- a/arch/arm64/kernel/ftrace.c
> > +++ b/arch/arm64/kernel/ftrace.c
> > @@ -9,11 +9,14 @@
> >   * published by the Free Software Foundation.
> >   */
> >
> > +#include <linux/elf.h>
> >  #include <linux/ftrace.h>
> > +#include <linux/module.h>
> >  #include <linux/swab.h>
> >  #include <linux/uaccess.h>
> >
> >  #include <asm/cacheflush.h>
> > +#include <asm/debug-monitors.h>
> >  #include <asm/ftrace.h>
> >  #include <asm/insn.h>
> >
> > @@ -63,6 +66,35 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
> >         return ftrace_modify_code(pc, 0, new, false);
> >  }
> >
> > +#ifdef CONFIG_ARM64_MODULE_PLTS
> > +EXPORT_SYMBOL_GPL(ftrace_caller);
> > +#endif
> > +
> > +static u32 __ftrace_gen_branch(unsigned long pc, unsigned long addr)
> > +{
> > +       long offset = (long)pc - (long)addr;
> > +       struct module *mod;
> > +
> > +       if (IS_ENABLED(CONFIG_ARM64_MODULE_PLTS) &&
> > +           addr == (unsigned long)&ftrace_caller &&
> > +           unlikely(offset < -SZ_128M || offset >= SZ_128M)) {
> > +
> > +               /*
> > +                * On kernels that support module PLTs, the offset between the
> > +                * call and its target may legally exceed the range of an
> > +                * ordinary branch instruction. In this case, we need to branch
> > +                * via a trampoline in the module.
> > +                */
> > +               mod = __module_address(pc);
> > +               if (WARN_ON(!mod))
> > +                       return AARCH64_BREAK_FAULT;
> > +
> > +               addr = (unsigned long)mod->arch.ftrace_trampoline->sh_addr;
> > +       }
> > +
> > +       return aarch64_insn_gen_branch_imm(pc, addr, AARCH64_INSN_BRANCH_LINK);
> > +}
> > +
> >  /*
> >   * Turn on the call to ftrace_caller() in instrumented function
> >   */
> > @@ -72,7 +104,7 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
> >         u32 old, new;
> >
> >         old = aarch64_insn_gen_nop();
> > -       new = aarch64_insn_gen_branch_imm(pc, addr, AARCH64_INSN_BRANCH_LINK);
> > +       new = __ftrace_gen_branch(pc, addr);
> >
> >         return ftrace_modify_code(pc, old, new, true);
> >  }
> > @@ -87,8 +119,7 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
> >         u32 old = 0, new;
> >
> >         if (!IS_ENABLED(CONFIG_ARM64_MODULE_PLTS))
> > -               old = aarch64_insn_gen_branch_imm(pc, addr,
> > -                                                 AARCH64_INSN_BRANCH_LINK);
> > +               old = __ftrace_gen_branch(pc, addr);
> >         new = aarch64_insn_gen_nop();
> >
> >         return ftrace_modify_code(pc, old, new,
> > diff --git a/arch/arm64/kernel/module-plts.c b/arch/arm64/kernel/module-plts.c
> > index 1ce90d8450ae..859c7170e69a 100644
> > --- a/arch/arm64/kernel/module-plts.c
> > +++ b/arch/arm64/kernel/module-plts.c
> > @@ -162,6 +162,10 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
> >                         mod->arch.plt = sechdrs + i;
> >                 else if (sechdrs[i].sh_type == SHT_SYMTAB)
> >                         syms = (Elf64_Sym *)sechdrs[i].sh_addr;
> > +               else if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE) &&
> > +                        strcmp(".text.ftrace_trampoline",
> > +                               secstrings + sechdrs[i].sh_name) == 0)
> > +                       mod->arch.ftrace_trampoline = sechdrs + i;
> >         }
> >
> >         if (!mod->arch.plt) {
> > @@ -173,6 +177,12 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
> >                 return -ENOEXEC;
> >         }
> >
> > +       if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE) && !mod->arch.ftrace_trampoline) {
> > +               pr_err("%s: module ftrace trampoline section missing\n",
> > +                      mod->name);
> > +               return -ENOEXEC;
> > +       }
> > +
> >         for (i = 0; i < ehdr->e_shnum; i++) {
> >                 Elf64_Rela *rels = (void *)ehdr + sechdrs[i].sh_offset;
> >                 int numrels = sechdrs[i].sh_size / sizeof(Elf64_Rela);
> > diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile
> > index c86b7909ef31..b01dcfa9c002 100644
> > --- a/arch/arm64/lib/Makefile
> > +++ b/arch/arm64/lib/Makefile
> > @@ -4,6 +4,8 @@ lib-y           := bitops.o clear_user.o delay.o copy_from_user.o       \
> >                    memcmp.o strcmp.o strncmp.o strlen.o strnlen.o       \
> >                    strchr.o strrchr.o
> >
> > +lib-$(CONFIG_DYNAMIC_FTRACE) += ftrace-mod.o
> > +
> >  # Tell the compiler to treat all general purpose registers (with the
> >  # exception of the IP registers, which are already handled by the caller
> >  # in case of a PLT) as callee-saved, which allows for efficient runtime
> > diff --git a/arch/arm64/lib/ftrace-mod.S b/arch/arm64/lib/ftrace-mod.S
> > new file mode 100644
> > index 000000000000..ce15b9948851
> > --- /dev/null
> > +++ b/arch/arm64/lib/ftrace-mod.S
> > @@ -0,0 +1,25 @@
> > +/*
> > + * Copyright (C) 2017 Linaro Ltd <ard.biesheuvel at linaro.org>
> > + *
> > + * 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/linkage.h>
> > +#include <asm/assembler.h>
> > +
> > +       .section        ".text.ftrace_trampoline", "ax"
> > +ENTRY(__ftrace_trampoline)
> > +       stp             x29, x30, [sp, #-16]!
> > +       mov             x29, sp
> > +
> > +       movz            x30, #:abs_g3:ftrace_caller
> > +       movk            x30, #:abs_g2_nc:ftrace_caller
> > +       movk            x30, #:abs_g1_nc:ftrace_caller
> > +       movk            x30, #:abs_g0_nc:ftrace_caller
> > +       blr             x30
> > +
> > +       ldp             x29, x30, [sp], #16
> > +       ret
> > +ENDPROC(__ftrace_trampoline)  
> 
> I suppose the additional stack frame interferes with correct operation
> of ftrace() here, so we should probably do this:
> 
> ENTRY(__ftrace_trampoline)
>     movz x16, #:abs_g3:ftrace_caller
>     movk x16, #:abs_g2_nc:ftrace_caller
>     movk x16, #:abs_g1_nc:ftrace_caller
>     movk x16, #:abs_g0_nc:ftrace_caller
>     blr  x16

Then should that still be a blr or just a br?

-- Steve

> ENDPROC(__ftrace_trampoline)
> 
> instead.




More information about the linux-arm-kernel mailing list