To Implement ftrace trampiones through plt entry.
Tested by forcing ftrace_make_call() to use the module PLT, and then
loading up a module after setting up ftrace with:
| echo ":mod:<module-name>" > set_ftrace_filter;
| echo function > current_tracer;
| modprobe <module-name>
Since FTRACE_ADDR/FTRACE_REGS_ADDR is only defined when CONFIG_DYNAMIC_FTRACE
is selected, we wrap its use along with most of module_init_ftrace_plt() with
ifdeffery rather than using IS_ENABLED().
Signed-off-by: Qing Zhang <zhangqing@loongson.cn>
---
arch/loongarch/include/asm/ftrace.h | 4 ++
arch/loongarch/include/asm/inst.h | 3 +
arch/loongarch/include/asm/module.h | 5 +-
arch/loongarch/include/asm/module.lds.h | 1 +
arch/loongarch/kernel/ftrace_dyn.c | 79 +++++++++++++++++++++++++
arch/loongarch/kernel/inst.c | 11 ++++
arch/loongarch/kernel/module-sections.c | 11 ++++
arch/loongarch/kernel/module.c | 47 +++++++++++++++
8 files changed, 160 insertions(+), 1 deletion(-)
diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h
index 5cc13ae48164..807226aad1c0 100644
--- a/arch/loongarch/include/asm/ftrace.h
+++ b/arch/loongarch/include/asm/ftrace.h
@@ -6,6 +6,10 @@
#ifndef _ASM_LOONGARCH_FTRACE_H
#define _ASM_LOONGARCH_FTRACE_H
+#define FTRACE_PLT_IDX 0
+#define FTRACE_REGS_PLT_IDX 1
+#define NR_FTRACE_PLTS 2
+
#ifdef CONFIG_FUNCTION_TRACER
#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h
index f4a12f872a71..5a1aa977ae20 100644
--- a/arch/loongarch/include/asm/inst.h
+++ b/arch/loongarch/include/asm/inst.h
@@ -15,10 +15,12 @@
#define ADDR_IMMMASK_LU52ID 0xFFF0000000000000
#define ADDR_IMMMASK_LU32ID 0x000FFFFF00000000
+#define ADDR_IMMMASK_LU12IW 0x00000000FFFFF000
#define ADDR_IMMMASK_ADDU16ID 0x00000000FFFF0000
#define ADDR_IMMSHIFT_LU52ID 52
#define ADDR_IMMSHIFT_LU32ID 32
+#define ADDR_IMMSHIFT_LU12IW 12
#define ADDR_IMMSHIFT_ADDU16ID 16
#define ADDR_IMM(addr, INSN) ((addr & ADDR_IMMMASK_##INSN) >> ADDR_IMMSHIFT_##INSN)
@@ -346,6 +348,7 @@ u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj,
enum loongarch_gpr rk);
u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj);
+u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm);
u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm);
u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm);
u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned long pc, unsigned long dest);
diff --git a/arch/loongarch/include/asm/module.h b/arch/loongarch/include/asm/module.h
index b29b19a46f42..a311cfec2b23 100644
--- a/arch/loongarch/include/asm/module.h
+++ b/arch/loongarch/include/asm/module.h
@@ -20,6 +20,9 @@ struct mod_arch_specific {
struct mod_section got;
struct mod_section plt;
struct mod_section plt_idx;
+
+ /* for CONFIG_DYNAMIC_FTRACE */
+ struct plt_entry *ftrace_trampolines;
};
struct got_entry {
@@ -49,7 +52,7 @@ static inline struct plt_entry emit_plt_entry(unsigned long val)
{
u32 lu12iw, lu32id, lu52id, jirl;
- lu12iw = (lu12iw_op << 25 | (((val >> 12) & 0xfffff) << 5) | LOONGARCH_GPR_T1);
+ lu12iw = larch_insn_gen_lu12iw(LOONGARCH_GPR_T1, ADDR_IMM(val, LU12IW));
lu32id = larch_insn_gen_lu32id(LOONGARCH_GPR_T1, ADDR_IMM(val, LU32ID));
lu52id = larch_insn_gen_lu52id(LOONGARCH_GPR_T1, LOONGARCH_GPR_T1, ADDR_IMM(val, LU52ID));
jirl = larch_insn_gen_jirl(0, LOONGARCH_GPR_T1, 0, (val & 0xfff));
diff --git a/arch/loongarch/include/asm/module.lds.h b/arch/loongarch/include/asm/module.lds.h
index a3d1bc0fcc72..438f09d4ccf4 100644
--- a/arch/loongarch/include/asm/module.lds.h
+++ b/arch/loongarch/include/asm/module.lds.h
@@ -5,4 +5,5 @@ SECTIONS {
.got : { BYTE(0) }
.plt : { BYTE(0) }
.plt.idx : { BYTE(0) }
+ .ftrace_trampoline : { BYTE(0) }
}
diff --git a/arch/loongarch/kernel/ftrace_dyn.c b/arch/loongarch/kernel/ftrace_dyn.c
index f538829312d7..dd37185d446e 100644
--- a/arch/loongarch/kernel/ftrace_dyn.c
+++ b/arch/loongarch/kernel/ftrace_dyn.c
@@ -9,6 +9,7 @@
#include <linux/uaccess.h>
#include <asm/inst.h>
+#include <asm/module.h>
static int ftrace_modify_code(unsigned long pc, u32 old, u32 new,
bool validate)
@@ -72,12 +73,63 @@ int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec)
return ftrace_modify_code(pc, old, new, true);
}
+static inline int __get_mod(struct module **mod, unsigned long addr)
+{
+ preempt_disable();
+ *mod = __module_text_address(addr);
+ preempt_enable();
+
+ if (WARN_ON(!(*mod)))
+ return -EINVAL;
+
+ return 0;
+}
+
+static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr)
+{
+ struct plt_entry *plt = mod->arch.ftrace_trampolines;
+
+ if (addr == FTRACE_ADDR)
+ return &plt[FTRACE_PLT_IDX];
+ if (addr == FTRACE_REGS_ADDR &&
+ IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS))
+ return &plt[FTRACE_REGS_PLT_IDX];
+
+ return NULL;
+}
+
+static unsigned long get_plt_addr(struct module *mod, unsigned long addr)
+{
+ struct plt_entry *plt;
+
+ plt = get_ftrace_plt(mod, addr);
+ if (!plt) {
+ pr_err("ftrace: no module PLT for %ps\n", (void *)addr);
+ return -EINVAL;
+ }
+
+ return (unsigned long)plt;
+}
+
int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
{
unsigned long pc;
+ long offset;
u32 old, new;
pc = rec->ip + LOONGARCH_INSN_SIZE;
+ offset = (long)pc - (long)addr;
+
+ if (offset < -SZ_128M || offset >= SZ_128M) {
+ int ret;
+ struct module *mod;
+
+ ret = __get_mod(&mod, pc);
+ if (ret)
+ return ret;
+
+ addr = get_plt_addr(mod, addr);
+ }
old = larch_insn_gen_nop();
new = larch_insn_gen_bl(pc, addr);
@@ -89,9 +141,22 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
unsigned long addr)
{
unsigned long pc;
+ long offset;
u32 old, new;
pc = rec->ip + LOONGARCH_INSN_SIZE;
+ offset = (long)pc - (long)addr;
+
+ if (offset < -SZ_128M || offset >= SZ_128M) {
+ int ret;
+ struct module *mod;
+
+ ret = __get_mod(&mod, pc);
+ if (ret)
+ return ret;
+
+ addr = get_plt_addr(mod, addr);
+ }
new = larch_insn_gen_nop();
old = larch_insn_gen_bl(pc, addr);
@@ -108,6 +173,20 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
u32 old, new;
pc = rec->ip + LOONGARCH_INSN_SIZE;
+ offset = (long)pc - (long)addr;
+
+ if (offset < -SZ_128M || offset >= SZ_128M) {
+ int ret;
+ struct module *mod;
+
+ ret = __get_mod(&mod, pc);
+ if (ret)
+ return ret;
+
+ addr = get_plt_addr(mod, addr);
+
+ old_addr = get_plt_addr(mod, old_addr);
+ }
old = larch_insn_gen_bl(pc, old_addr);
new = larch_insn_gen_bl(pc, addr);
diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c
index 2d2e942eb06a..0d6bd7000ba6 100644
--- a/arch/loongarch/kernel/inst.c
+++ b/arch/loongarch/kernel/inst.c
@@ -103,6 +103,17 @@ u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest)
return insn.word;
}
+u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm)
+{
+ union loongarch_instruction insn;
+
+ insn.reg1i20_format.opcode = lu12iw_op;
+ insn.reg1i20_format.rd = rd;
+ insn.reg1i20_format.immediate = imm;
+
+ return insn.word;
+}
+
u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm)
{
union loongarch_instruction insn;
diff --git a/arch/loongarch/kernel/module-sections.c b/arch/loongarch/kernel/module-sections.c
index d296a70b758f..bd1a96691c98 100644
--- a/arch/loongarch/kernel/module-sections.c
+++ b/arch/loongarch/kernel/module-sections.c
@@ -4,6 +4,7 @@
*/
#include <linux/elf.h>
+#include <linux/ftrace.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -103,6 +104,7 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
char *secstrings, struct module *mod)
{
unsigned int i, num_plts = 0, num_gots = 0;
+ Elf_Shdr *tramp = NULL;
/*
* Find the empty .plt sections.
@@ -114,6 +116,8 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
mod->arch.plt.shdr = sechdrs + i;
else if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt.idx"))
mod->arch.plt_idx.shdr = sechdrs + i;
+ else if (!strcmp(secstrings + sechdrs[i].sh_name, ".ftrace_trampoline"))
+ tramp = sechdrs + i;
}
if (!mod->arch.got.shdr) {
@@ -166,5 +170,12 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
mod->arch.plt_idx.num_entries = 0;
mod->arch.plt_idx.max_entries = num_plts;
+ if (tramp) {
+ tramp->sh_type = SHT_NOBITS;
+ tramp->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
+ tramp->sh_addralign = __alignof__(struct plt_entry);
+ tramp->sh_size = NR_FTRACE_PLTS * sizeof(struct plt_entry);
+ }
+
return 0;
}
diff --git a/arch/loongarch/kernel/module.c b/arch/loongarch/kernel/module.c
index 097595b2fc14..e036b1ebf98e 100644
--- a/arch/loongarch/kernel/module.c
+++ b/arch/loongarch/kernel/module.c
@@ -10,6 +10,7 @@
#include <linux/moduleloader.h>
#include <linux/elf.h>
+#include <linux/ftrace.h>
#include <linux/mm.h>
#include <linux/numa.h>
#include <linux/vmalloc.h>
@@ -17,6 +18,7 @@
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/kernel.h>
+#include <asm/inst.h>
static int rela_stack_push(s64 stack_value, s64 *rela_stack, size_t *rela_stack_top)
{
@@ -456,3 +458,48 @@ void *module_alloc(unsigned long size)
return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE, __builtin_return_address(0));
}
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+static const Elf_Shdr *find_section(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
+ const char *name)
+{
+ const Elf_Shdr *s, *se;
+ const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+
+ for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) {
+ if (strcmp(name, secstrs + s->sh_name) == 0)
+ return s;
+ }
+
+ return NULL;
+}
+#endif
+
+static int module_init_ftrace_plt(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
+ struct module *mod)
+{
+#ifdef CONFIG_DYNAMIC_FTRACE
+ const Elf_Shdr *s;
+ struct plt_entry *ftrace_plts;
+
+ s = find_section(hdr, sechdrs, ".ftrace_trampoline");
+ if (!s)
+ return -ENOEXEC;
+
+ ftrace_plts = (void *)s->sh_addr;
+
+ ftrace_plts[FTRACE_PLT_IDX] = emit_plt_entry(FTRACE_ADDR);
+
+ if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS))
+ ftrace_plts[FTRACE_REGS_PLT_IDX] = emit_plt_entry(FTRACE_REGS_ADDR);
+
+ mod->arch.ftrace_trampolines = ftrace_plts;
+#endif
+ return 0;
+}
+
+int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, struct module *mod)
+{
+ return module_init_ftrace_plt(hdr, sechdrs, mod);
+
+}
--
2.36.1
Hi, Qing,
Maybe this patch can be squashed to another one? If not, please move
it earlier because it looks like some preparation work.
Huacai
On Mon, Sep 19, 2022 at 11:16 AM Qing Zhang <zhangqing@loongson.cn> wrote:
>
> To Implement ftrace trampiones through plt entry.
>
> Tested by forcing ftrace_make_call() to use the module PLT, and then
> loading up a module after setting up ftrace with:
>
> | echo ":mod:<module-name>" > set_ftrace_filter;
> | echo function > current_tracer;
> | modprobe <module-name>
>
> Since FTRACE_ADDR/FTRACE_REGS_ADDR is only defined when CONFIG_DYNAMIC_FTRACE
> is selected, we wrap its use along with most of module_init_ftrace_plt() with
> ifdeffery rather than using IS_ENABLED().
>
> Signed-off-by: Qing Zhang <zhangqing@loongson.cn>
> ---
> arch/loongarch/include/asm/ftrace.h | 4 ++
> arch/loongarch/include/asm/inst.h | 3 +
> arch/loongarch/include/asm/module.h | 5 +-
> arch/loongarch/include/asm/module.lds.h | 1 +
> arch/loongarch/kernel/ftrace_dyn.c | 79 +++++++++++++++++++++++++
> arch/loongarch/kernel/inst.c | 11 ++++
> arch/loongarch/kernel/module-sections.c | 11 ++++
> arch/loongarch/kernel/module.c | 47 +++++++++++++++
> 8 files changed, 160 insertions(+), 1 deletion(-)
>
> diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h
> index 5cc13ae48164..807226aad1c0 100644
> --- a/arch/loongarch/include/asm/ftrace.h
> +++ b/arch/loongarch/include/asm/ftrace.h
> @@ -6,6 +6,10 @@
> #ifndef _ASM_LOONGARCH_FTRACE_H
> #define _ASM_LOONGARCH_FTRACE_H
>
> +#define FTRACE_PLT_IDX 0
> +#define FTRACE_REGS_PLT_IDX 1
> +#define NR_FTRACE_PLTS 2
> +
> #ifdef CONFIG_FUNCTION_TRACER
> #define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
>
> diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h
> index f4a12f872a71..5a1aa977ae20 100644
> --- a/arch/loongarch/include/asm/inst.h
> +++ b/arch/loongarch/include/asm/inst.h
> @@ -15,10 +15,12 @@
>
> #define ADDR_IMMMASK_LU52ID 0xFFF0000000000000
> #define ADDR_IMMMASK_LU32ID 0x000FFFFF00000000
> +#define ADDR_IMMMASK_LU12IW 0x00000000FFFFF000
> #define ADDR_IMMMASK_ADDU16ID 0x00000000FFFF0000
>
> #define ADDR_IMMSHIFT_LU52ID 52
> #define ADDR_IMMSHIFT_LU32ID 32
> +#define ADDR_IMMSHIFT_LU12IW 12
> #define ADDR_IMMSHIFT_ADDU16ID 16
>
> #define ADDR_IMM(addr, INSN) ((addr & ADDR_IMMMASK_##INSN) >> ADDR_IMMSHIFT_##INSN)
> @@ -346,6 +348,7 @@ u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj,
> enum loongarch_gpr rk);
> u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj);
>
> +u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm);
> u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm);
> u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm);
> u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned long pc, unsigned long dest);
> diff --git a/arch/loongarch/include/asm/module.h b/arch/loongarch/include/asm/module.h
> index b29b19a46f42..a311cfec2b23 100644
> --- a/arch/loongarch/include/asm/module.h
> +++ b/arch/loongarch/include/asm/module.h
> @@ -20,6 +20,9 @@ struct mod_arch_specific {
> struct mod_section got;
> struct mod_section plt;
> struct mod_section plt_idx;
> +
> + /* for CONFIG_DYNAMIC_FTRACE */
> + struct plt_entry *ftrace_trampolines;
> };
>
> struct got_entry {
> @@ -49,7 +52,7 @@ static inline struct plt_entry emit_plt_entry(unsigned long val)
> {
> u32 lu12iw, lu32id, lu52id, jirl;
>
> - lu12iw = (lu12iw_op << 25 | (((val >> 12) & 0xfffff) << 5) | LOONGARCH_GPR_T1);
> + lu12iw = larch_insn_gen_lu12iw(LOONGARCH_GPR_T1, ADDR_IMM(val, LU12IW));
> lu32id = larch_insn_gen_lu32id(LOONGARCH_GPR_T1, ADDR_IMM(val, LU32ID));
> lu52id = larch_insn_gen_lu52id(LOONGARCH_GPR_T1, LOONGARCH_GPR_T1, ADDR_IMM(val, LU52ID));
> jirl = larch_insn_gen_jirl(0, LOONGARCH_GPR_T1, 0, (val & 0xfff));
> diff --git a/arch/loongarch/include/asm/module.lds.h b/arch/loongarch/include/asm/module.lds.h
> index a3d1bc0fcc72..438f09d4ccf4 100644
> --- a/arch/loongarch/include/asm/module.lds.h
> +++ b/arch/loongarch/include/asm/module.lds.h
> @@ -5,4 +5,5 @@ SECTIONS {
> .got : { BYTE(0) }
> .plt : { BYTE(0) }
> .plt.idx : { BYTE(0) }
> + .ftrace_trampoline : { BYTE(0) }
> }
> diff --git a/arch/loongarch/kernel/ftrace_dyn.c b/arch/loongarch/kernel/ftrace_dyn.c
> index f538829312d7..dd37185d446e 100644
> --- a/arch/loongarch/kernel/ftrace_dyn.c
> +++ b/arch/loongarch/kernel/ftrace_dyn.c
> @@ -9,6 +9,7 @@
> #include <linux/uaccess.h>
>
> #include <asm/inst.h>
> +#include <asm/module.h>
>
> static int ftrace_modify_code(unsigned long pc, u32 old, u32 new,
> bool validate)
> @@ -72,12 +73,63 @@ int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec)
> return ftrace_modify_code(pc, old, new, true);
> }
>
> +static inline int __get_mod(struct module **mod, unsigned long addr)
> +{
> + preempt_disable();
> + *mod = __module_text_address(addr);
> + preempt_enable();
> +
> + if (WARN_ON(!(*mod)))
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr)
> +{
> + struct plt_entry *plt = mod->arch.ftrace_trampolines;
> +
> + if (addr == FTRACE_ADDR)
> + return &plt[FTRACE_PLT_IDX];
> + if (addr == FTRACE_REGS_ADDR &&
> + IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS))
> + return &plt[FTRACE_REGS_PLT_IDX];
> +
> + return NULL;
> +}
> +
> +static unsigned long get_plt_addr(struct module *mod, unsigned long addr)
> +{
> + struct plt_entry *plt;
> +
> + plt = get_ftrace_plt(mod, addr);
> + if (!plt) {
> + pr_err("ftrace: no module PLT for %ps\n", (void *)addr);
> + return -EINVAL;
> + }
> +
> + return (unsigned long)plt;
> +}
> +
> int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
> {
> unsigned long pc;
> + long offset;
> u32 old, new;
>
> pc = rec->ip + LOONGARCH_INSN_SIZE;
> + offset = (long)pc - (long)addr;
> +
> + if (offset < -SZ_128M || offset >= SZ_128M) {
> + int ret;
> + struct module *mod;
> +
> + ret = __get_mod(&mod, pc);
> + if (ret)
> + return ret;
> +
> + addr = get_plt_addr(mod, addr);
> + }
>
> old = larch_insn_gen_nop();
> new = larch_insn_gen_bl(pc, addr);
> @@ -89,9 +141,22 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
> unsigned long addr)
> {
> unsigned long pc;
> + long offset;
> u32 old, new;
>
> pc = rec->ip + LOONGARCH_INSN_SIZE;
> + offset = (long)pc - (long)addr;
> +
> + if (offset < -SZ_128M || offset >= SZ_128M) {
> + int ret;
> + struct module *mod;
> +
> + ret = __get_mod(&mod, pc);
> + if (ret)
> + return ret;
> +
> + addr = get_plt_addr(mod, addr);
> + }
>
> new = larch_insn_gen_nop();
> old = larch_insn_gen_bl(pc, addr);
> @@ -108,6 +173,20 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
> u32 old, new;
>
> pc = rec->ip + LOONGARCH_INSN_SIZE;
> + offset = (long)pc - (long)addr;
> +
> + if (offset < -SZ_128M || offset >= SZ_128M) {
> + int ret;
> + struct module *mod;
> +
> + ret = __get_mod(&mod, pc);
> + if (ret)
> + return ret;
> +
> + addr = get_plt_addr(mod, addr);
> +
> + old_addr = get_plt_addr(mod, old_addr);
> + }
>
> old = larch_insn_gen_bl(pc, old_addr);
> new = larch_insn_gen_bl(pc, addr);
> diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c
> index 2d2e942eb06a..0d6bd7000ba6 100644
> --- a/arch/loongarch/kernel/inst.c
> +++ b/arch/loongarch/kernel/inst.c
> @@ -103,6 +103,17 @@ u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest)
> return insn.word;
> }
>
> +u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm)
> +{
> + union loongarch_instruction insn;
> +
> + insn.reg1i20_format.opcode = lu12iw_op;
> + insn.reg1i20_format.rd = rd;
> + insn.reg1i20_format.immediate = imm;
> +
> + return insn.word;
> +}
> +
> u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm)
> {
> union loongarch_instruction insn;
> diff --git a/arch/loongarch/kernel/module-sections.c b/arch/loongarch/kernel/module-sections.c
> index d296a70b758f..bd1a96691c98 100644
> --- a/arch/loongarch/kernel/module-sections.c
> +++ b/arch/loongarch/kernel/module-sections.c
> @@ -4,6 +4,7 @@
> */
>
> #include <linux/elf.h>
> +#include <linux/ftrace.h>
> #include <linux/kernel.h>
> #include <linux/module.h>
>
> @@ -103,6 +104,7 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
> char *secstrings, struct module *mod)
> {
> unsigned int i, num_plts = 0, num_gots = 0;
> + Elf_Shdr *tramp = NULL;
>
> /*
> * Find the empty .plt sections.
> @@ -114,6 +116,8 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
> mod->arch.plt.shdr = sechdrs + i;
> else if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt.idx"))
> mod->arch.plt_idx.shdr = sechdrs + i;
> + else if (!strcmp(secstrings + sechdrs[i].sh_name, ".ftrace_trampoline"))
> + tramp = sechdrs + i;
> }
>
> if (!mod->arch.got.shdr) {
> @@ -166,5 +170,12 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
> mod->arch.plt_idx.num_entries = 0;
> mod->arch.plt_idx.max_entries = num_plts;
>
> + if (tramp) {
> + tramp->sh_type = SHT_NOBITS;
> + tramp->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
> + tramp->sh_addralign = __alignof__(struct plt_entry);
> + tramp->sh_size = NR_FTRACE_PLTS * sizeof(struct plt_entry);
> + }
> +
> return 0;
> }
> diff --git a/arch/loongarch/kernel/module.c b/arch/loongarch/kernel/module.c
> index 097595b2fc14..e036b1ebf98e 100644
> --- a/arch/loongarch/kernel/module.c
> +++ b/arch/loongarch/kernel/module.c
> @@ -10,6 +10,7 @@
>
> #include <linux/moduleloader.h>
> #include <linux/elf.h>
> +#include <linux/ftrace.h>
> #include <linux/mm.h>
> #include <linux/numa.h>
> #include <linux/vmalloc.h>
> @@ -17,6 +18,7 @@
> #include <linux/fs.h>
> #include <linux/string.h>
> #include <linux/kernel.h>
> +#include <asm/inst.h>
>
> static int rela_stack_push(s64 stack_value, s64 *rela_stack, size_t *rela_stack_top)
> {
> @@ -456,3 +458,48 @@ void *module_alloc(unsigned long size)
> return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
> GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE, __builtin_return_address(0));
> }
> +
> +#ifdef CONFIG_DYNAMIC_FTRACE
> +static const Elf_Shdr *find_section(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
> + const char *name)
> +{
> + const Elf_Shdr *s, *se;
> + const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
> +
> + for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) {
> + if (strcmp(name, secstrs + s->sh_name) == 0)
> + return s;
> + }
> +
> + return NULL;
> +}
> +#endif
> +
> +static int module_init_ftrace_plt(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
> + struct module *mod)
> +{
> +#ifdef CONFIG_DYNAMIC_FTRACE
> + const Elf_Shdr *s;
> + struct plt_entry *ftrace_plts;
> +
> + s = find_section(hdr, sechdrs, ".ftrace_trampoline");
> + if (!s)
> + return -ENOEXEC;
> +
> + ftrace_plts = (void *)s->sh_addr;
> +
> + ftrace_plts[FTRACE_PLT_IDX] = emit_plt_entry(FTRACE_ADDR);
> +
> + if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS))
> + ftrace_plts[FTRACE_REGS_PLT_IDX] = emit_plt_entry(FTRACE_REGS_ADDR);
> +
> + mod->arch.ftrace_trampolines = ftrace_plts;
> +#endif
> + return 0;
> +}
> +
> +int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, struct module *mod)
> +{
> + return module_init_ftrace_plt(hdr, sechdrs, mod);
> +
> +}
> --
> 2.36.1
>
Hi, Hucai
On 2022/10/28 上午11:06, Huacai Chen wrote:
> Hi, Qing,
>
> Maybe this patch can be squashed to another one? If not, please move
> it earlier because it looks like some preparation work.
>
ok, I'll move this patch sequence and send it later.
- Qing
> Huacai
>
> On Mon, Sep 19, 2022 at 11:16 AM Qing Zhang <zhangqing@loongson.cn> wrote:
>>
>> To Implement ftrace trampiones through plt entry.
>>
>> Tested by forcing ftrace_make_call() to use the module PLT, and then
>> loading up a module after setting up ftrace with:
>>
>> | echo ":mod:<module-name>" > set_ftrace_filter;
>> | echo function > current_tracer;
>> | modprobe <module-name>
>>
>> Since FTRACE_ADDR/FTRACE_REGS_ADDR is only defined when CONFIG_DYNAMIC_FTRACE
>> is selected, we wrap its use along with most of module_init_ftrace_plt() with
>> ifdeffery rather than using IS_ENABLED().
>>
>> Signed-off-by: Qing Zhang <zhangqing@loongson.cn>
>> ---
>> arch/loongarch/include/asm/ftrace.h | 4 ++
>> arch/loongarch/include/asm/inst.h | 3 +
>> arch/loongarch/include/asm/module.h | 5 +-
>> arch/loongarch/include/asm/module.lds.h | 1 +
>> arch/loongarch/kernel/ftrace_dyn.c | 79 +++++++++++++++++++++++++
>> arch/loongarch/kernel/inst.c | 11 ++++
>> arch/loongarch/kernel/module-sections.c | 11 ++++
>> arch/loongarch/kernel/module.c | 47 +++++++++++++++
>> 8 files changed, 160 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h
>> index 5cc13ae48164..807226aad1c0 100644
>> --- a/arch/loongarch/include/asm/ftrace.h
>> +++ b/arch/loongarch/include/asm/ftrace.h
>> @@ -6,6 +6,10 @@
>> #ifndef _ASM_LOONGARCH_FTRACE_H
>> #define _ASM_LOONGARCH_FTRACE_H
>>
>> +#define FTRACE_PLT_IDX 0
>> +#define FTRACE_REGS_PLT_IDX 1
>> +#define NR_FTRACE_PLTS 2
>> +
>> #ifdef CONFIG_FUNCTION_TRACER
>> #define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
>>
>> diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h
>> index f4a12f872a71..5a1aa977ae20 100644
>> --- a/arch/loongarch/include/asm/inst.h
>> +++ b/arch/loongarch/include/asm/inst.h
>> @@ -15,10 +15,12 @@
>>
>> #define ADDR_IMMMASK_LU52ID 0xFFF0000000000000
>> #define ADDR_IMMMASK_LU32ID 0x000FFFFF00000000
>> +#define ADDR_IMMMASK_LU12IW 0x00000000FFFFF000
>> #define ADDR_IMMMASK_ADDU16ID 0x00000000FFFF0000
>>
>> #define ADDR_IMMSHIFT_LU52ID 52
>> #define ADDR_IMMSHIFT_LU32ID 32
>> +#define ADDR_IMMSHIFT_LU12IW 12
>> #define ADDR_IMMSHIFT_ADDU16ID 16
>>
>> #define ADDR_IMM(addr, INSN) ((addr & ADDR_IMMMASK_##INSN) >> ADDR_IMMSHIFT_##INSN)
>> @@ -346,6 +348,7 @@ u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj,
>> enum loongarch_gpr rk);
>> u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj);
>>
>> +u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm);
>> u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm);
>> u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm);
>> u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned long pc, unsigned long dest);
>> diff --git a/arch/loongarch/include/asm/module.h b/arch/loongarch/include/asm/module.h
>> index b29b19a46f42..a311cfec2b23 100644
>> --- a/arch/loongarch/include/asm/module.h
>> +++ b/arch/loongarch/include/asm/module.h
>> @@ -20,6 +20,9 @@ struct mod_arch_specific {
>> struct mod_section got;
>> struct mod_section plt;
>> struct mod_section plt_idx;
>> +
>> + /* for CONFIG_DYNAMIC_FTRACE */
>> + struct plt_entry *ftrace_trampolines;
>> };
>>
>> struct got_entry {
>> @@ -49,7 +52,7 @@ static inline struct plt_entry emit_plt_entry(unsigned long val)
>> {
>> u32 lu12iw, lu32id, lu52id, jirl;
>>
>> - lu12iw = (lu12iw_op << 25 | (((val >> 12) & 0xfffff) << 5) | LOONGARCH_GPR_T1);
>> + lu12iw = larch_insn_gen_lu12iw(LOONGARCH_GPR_T1, ADDR_IMM(val, LU12IW));
>> lu32id = larch_insn_gen_lu32id(LOONGARCH_GPR_T1, ADDR_IMM(val, LU32ID));
>> lu52id = larch_insn_gen_lu52id(LOONGARCH_GPR_T1, LOONGARCH_GPR_T1, ADDR_IMM(val, LU52ID));
>> jirl = larch_insn_gen_jirl(0, LOONGARCH_GPR_T1, 0, (val & 0xfff));
>> diff --git a/arch/loongarch/include/asm/module.lds.h b/arch/loongarch/include/asm/module.lds.h
>> index a3d1bc0fcc72..438f09d4ccf4 100644
>> --- a/arch/loongarch/include/asm/module.lds.h
>> +++ b/arch/loongarch/include/asm/module.lds.h
>> @@ -5,4 +5,5 @@ SECTIONS {
>> .got : { BYTE(0) }
>> .plt : { BYTE(0) }
>> .plt.idx : { BYTE(0) }
>> + .ftrace_trampoline : { BYTE(0) }
>> }
>> diff --git a/arch/loongarch/kernel/ftrace_dyn.c b/arch/loongarch/kernel/ftrace_dyn.c
>> index f538829312d7..dd37185d446e 100644
>> --- a/arch/loongarch/kernel/ftrace_dyn.c
>> +++ b/arch/loongarch/kernel/ftrace_dyn.c
>> @@ -9,6 +9,7 @@
>> #include <linux/uaccess.h>
>>
>> #include <asm/inst.h>
>> +#include <asm/module.h>
>>
>> static int ftrace_modify_code(unsigned long pc, u32 old, u32 new,
>> bool validate)
>> @@ -72,12 +73,63 @@ int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec)
>> return ftrace_modify_code(pc, old, new, true);
>> }
>>
>> +static inline int __get_mod(struct module **mod, unsigned long addr)
>> +{
>> + preempt_disable();
>> + *mod = __module_text_address(addr);
>> + preempt_enable();
>> +
>> + if (WARN_ON(!(*mod)))
>> + return -EINVAL;
>> +
>> + return 0;
>> +}
>> +
>> +static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr)
>> +{
>> + struct plt_entry *plt = mod->arch.ftrace_trampolines;
>> +
>> + if (addr == FTRACE_ADDR)
>> + return &plt[FTRACE_PLT_IDX];
>> + if (addr == FTRACE_REGS_ADDR &&
>> + IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS))
>> + return &plt[FTRACE_REGS_PLT_IDX];
>> +
>> + return NULL;
>> +}
>> +
>> +static unsigned long get_plt_addr(struct module *mod, unsigned long addr)
>> +{
>> + struct plt_entry *plt;
>> +
>> + plt = get_ftrace_plt(mod, addr);
>> + if (!plt) {
>> + pr_err("ftrace: no module PLT for %ps\n", (void *)addr);
>> + return -EINVAL;
>> + }
>> +
>> + return (unsigned long)plt;
>> +}
>> +
>> int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
>> {
>> unsigned long pc;
>> + long offset;
>> u32 old, new;
>>
>> pc = rec->ip + LOONGARCH_INSN_SIZE;
>> + offset = (long)pc - (long)addr;
>> +
>> + if (offset < -SZ_128M || offset >= SZ_128M) {
>> + int ret;
>> + struct module *mod;
>> +
>> + ret = __get_mod(&mod, pc);
>> + if (ret)
>> + return ret;
>> +
>> + addr = get_plt_addr(mod, addr);
>> + }
>>
>> old = larch_insn_gen_nop();
>> new = larch_insn_gen_bl(pc, addr);
>> @@ -89,9 +141,22 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
>> unsigned long addr)
>> {
>> unsigned long pc;
>> + long offset;
>> u32 old, new;
>>
>> pc = rec->ip + LOONGARCH_INSN_SIZE;
>> + offset = (long)pc - (long)addr;
>> +
>> + if (offset < -SZ_128M || offset >= SZ_128M) {
>> + int ret;
>> + struct module *mod;
>> +
>> + ret = __get_mod(&mod, pc);
>> + if (ret)
>> + return ret;
>> +
>> + addr = get_plt_addr(mod, addr);
>> + }
>>
>> new = larch_insn_gen_nop();
>> old = larch_insn_gen_bl(pc, addr);
>> @@ -108,6 +173,20 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
>> u32 old, new;
>>
>> pc = rec->ip + LOONGARCH_INSN_SIZE;
>> + offset = (long)pc - (long)addr;
>> +
>> + if (offset < -SZ_128M || offset >= SZ_128M) {
>> + int ret;
>> + struct module *mod;
>> +
>> + ret = __get_mod(&mod, pc);
>> + if (ret)
>> + return ret;
>> +
>> + addr = get_plt_addr(mod, addr);
>> +
>> + old_addr = get_plt_addr(mod, old_addr);
>> + }
>>
>> old = larch_insn_gen_bl(pc, old_addr);
>> new = larch_insn_gen_bl(pc, addr);
>> diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c
>> index 2d2e942eb06a..0d6bd7000ba6 100644
>> --- a/arch/loongarch/kernel/inst.c
>> +++ b/arch/loongarch/kernel/inst.c
>> @@ -103,6 +103,17 @@ u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest)
>> return insn.word;
>> }
>>
>> +u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm)
>> +{
>> + union loongarch_instruction insn;
>> +
>> + insn.reg1i20_format.opcode = lu12iw_op;
>> + insn.reg1i20_format.rd = rd;
>> + insn.reg1i20_format.immediate = imm;
>> +
>> + return insn.word;
>> +}
>> +
>> u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm)
>> {
>> union loongarch_instruction insn;
>> diff --git a/arch/loongarch/kernel/module-sections.c b/arch/loongarch/kernel/module-sections.c
>> index d296a70b758f..bd1a96691c98 100644
>> --- a/arch/loongarch/kernel/module-sections.c
>> +++ b/arch/loongarch/kernel/module-sections.c
>> @@ -4,6 +4,7 @@
>> */
>>
>> #include <linux/elf.h>
>> +#include <linux/ftrace.h>
>> #include <linux/kernel.h>
>> #include <linux/module.h>
>>
>> @@ -103,6 +104,7 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
>> char *secstrings, struct module *mod)
>> {
>> unsigned int i, num_plts = 0, num_gots = 0;
>> + Elf_Shdr *tramp = NULL;
>>
>> /*
>> * Find the empty .plt sections.
>> @@ -114,6 +116,8 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
>> mod->arch.plt.shdr = sechdrs + i;
>> else if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt.idx"))
>> mod->arch.plt_idx.shdr = sechdrs + i;
>> + else if (!strcmp(secstrings + sechdrs[i].sh_name, ".ftrace_trampoline"))
>> + tramp = sechdrs + i;
>> }
>>
>> if (!mod->arch.got.shdr) {
>> @@ -166,5 +170,12 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
>> mod->arch.plt_idx.num_entries = 0;
>> mod->arch.plt_idx.max_entries = num_plts;
>>
>> + if (tramp) {
>> + tramp->sh_type = SHT_NOBITS;
>> + tramp->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
>> + tramp->sh_addralign = __alignof__(struct plt_entry);
>> + tramp->sh_size = NR_FTRACE_PLTS * sizeof(struct plt_entry);
>> + }
>> +
>> return 0;
>> }
>> diff --git a/arch/loongarch/kernel/module.c b/arch/loongarch/kernel/module.c
>> index 097595b2fc14..e036b1ebf98e 100644
>> --- a/arch/loongarch/kernel/module.c
>> +++ b/arch/loongarch/kernel/module.c
>> @@ -10,6 +10,7 @@
>>
>> #include <linux/moduleloader.h>
>> #include <linux/elf.h>
>> +#include <linux/ftrace.h>
>> #include <linux/mm.h>
>> #include <linux/numa.h>
>> #include <linux/vmalloc.h>
>> @@ -17,6 +18,7 @@
>> #include <linux/fs.h>
>> #include <linux/string.h>
>> #include <linux/kernel.h>
>> +#include <asm/inst.h>
>>
>> static int rela_stack_push(s64 stack_value, s64 *rela_stack, size_t *rela_stack_top)
>> {
>> @@ -456,3 +458,48 @@ void *module_alloc(unsigned long size)
>> return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
>> GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE, __builtin_return_address(0));
>> }
>> +
>> +#ifdef CONFIG_DYNAMIC_FTRACE
>> +static const Elf_Shdr *find_section(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
>> + const char *name)
>> +{
>> + const Elf_Shdr *s, *se;
>> + const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
>> +
>> + for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) {
>> + if (strcmp(name, secstrs + s->sh_name) == 0)
>> + return s;
>> + }
>> +
>> + return NULL;
>> +}
>> +#endif
>> +
>> +static int module_init_ftrace_plt(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
>> + struct module *mod)
>> +{
>> +#ifdef CONFIG_DYNAMIC_FTRACE
>> + const Elf_Shdr *s;
>> + struct plt_entry *ftrace_plts;
>> +
>> + s = find_section(hdr, sechdrs, ".ftrace_trampoline");
>> + if (!s)
>> + return -ENOEXEC;
>> +
>> + ftrace_plts = (void *)s->sh_addr;
>> +
>> + ftrace_plts[FTRACE_PLT_IDX] = emit_plt_entry(FTRACE_ADDR);
>> +
>> + if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS))
>> + ftrace_plts[FTRACE_REGS_PLT_IDX] = emit_plt_entry(FTRACE_REGS_ADDR);
>> +
>> + mod->arch.ftrace_trampolines = ftrace_plts;
>> +#endif
>> + return 0;
>> +}
>> +
>> +int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, struct module *mod)
>> +{
>> + return module_init_ftrace_plt(hdr, sechdrs, mod);
>> +
>> +}
>> --
>> 2.36.1
>>
© 2016 - 2026 Red Hat, Inc.