Skip to content

Commit 28ac0a9

Browse files
zhangqingmychenhuacai
authored andcommitted
LoongArch: modules/ftrace: Initialize PLT at load time
This patch implements ftrace trampolines 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 their usage in module_init_ftrace_plt() with ifdeffery rather than using IS_ENABLED(). Signed-off-by: Qing Zhang <[email protected]> Signed-off-by: Huacai Chen <[email protected]>
1 parent a51ac52 commit 28ac0a9

File tree

8 files changed

+148
-2
lines changed

8 files changed

+148
-2
lines changed

arch/loongarch/include/asm/ftrace.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
#ifndef _ASM_LOONGARCH_FTRACE_H
77
#define _ASM_LOONGARCH_FTRACE_H
88

9+
#define FTRACE_PLT_IDX 0
10+
#define FTRACE_REGS_PLT_IDX 1
11+
#define NR_FTRACE_PLTS 2
12+
913
#define GRAPH_FAKE_OFFSET (sizeof(struct pt_regs) - offsetof(struct pt_regs, regs[1]))
1014

1115
#ifdef CONFIG_FUNCTION_TRACER

arch/loongarch/include/asm/inst.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313

1414
#define ADDR_IMMMASK_LU52ID 0xFFF0000000000000
1515
#define ADDR_IMMMASK_LU32ID 0x000FFFFF00000000
16+
#define ADDR_IMMMASK_LU12IW 0x00000000FFFFF000
1617
#define ADDR_IMMMASK_ADDU16ID 0x00000000FFFF0000
1718

1819
#define ADDR_IMMSHIFT_LU52ID 52
1920
#define ADDR_IMMSHIFT_LU32ID 32
21+
#define ADDR_IMMSHIFT_LU12IW 12
2022
#define ADDR_IMMSHIFT_ADDU16ID 16
2123

2224
#define ADDR_IMM(addr, INSN) ((addr & ADDR_IMMMASK_##INSN) >> ADDR_IMMSHIFT_##INSN)
@@ -360,6 +362,7 @@ u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest);
360362
u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj, enum loongarch_gpr rk);
361363
u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj);
362364

365+
u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm);
363366
u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm);
364367
u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm);
365368
u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned long pc, unsigned long dest);

arch/loongarch/include/asm/module.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ struct mod_arch_specific {
2020
struct mod_section got;
2121
struct mod_section plt;
2222
struct mod_section plt_idx;
23+
24+
/* For CONFIG_DYNAMIC_FTRACE */
25+
struct plt_entry *ftrace_trampolines;
2326
};
2427

2528
struct got_entry {
@@ -49,7 +52,7 @@ static inline struct plt_entry emit_plt_entry(unsigned long val)
4952
{
5053
u32 lu12iw, lu32id, lu52id, jirl;
5154

52-
lu12iw = (lu12iw_op << 25 | (((val >> 12) & 0xfffff) << 5) | LOONGARCH_GPR_T1);
55+
lu12iw = larch_insn_gen_lu12iw(LOONGARCH_GPR_T1, ADDR_IMM(val, LU12IW));
5356
lu32id = larch_insn_gen_lu32id(LOONGARCH_GPR_T1, ADDR_IMM(val, LU32ID));
5457
lu52id = larch_insn_gen_lu52id(LOONGARCH_GPR_T1, LOONGARCH_GPR_T1, ADDR_IMM(val, LU52ID));
5558
jirl = larch_insn_gen_jirl(0, LOONGARCH_GPR_T1, 0, (val & 0xfff));

arch/loongarch/include/asm/module.lds.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ SECTIONS {
55
.got : { BYTE(0) }
66
.plt : { BYTE(0) }
77
.plt.idx : { BYTE(0) }
8+
.ftrace_trampoline : { BYTE(0) }
89
}

arch/loongarch/kernel/ftrace_dyn.c

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/uaccess.h>
1010

1111
#include <asm/inst.h>
12+
#include <asm/module.h>
1213

1314
static int ftrace_modify_code(unsigned long pc, u32 old, u32 new, bool validate)
1415
{
@@ -29,18 +30,78 @@ static int ftrace_modify_code(unsigned long pc, u32 old, u32 new, bool validate)
2930
}
3031

3132
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
33+
34+
#ifdef CONFIG_MODULES
35+
static inline int __get_mod(struct module **mod, unsigned long addr)
36+
{
37+
preempt_disable();
38+
*mod = __module_text_address(addr);
39+
preempt_enable();
40+
41+
if (WARN_ON(!(*mod)))
42+
return -EINVAL;
43+
44+
return 0;
45+
}
46+
47+
static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr)
48+
{
49+
struct plt_entry *plt = mod->arch.ftrace_trampolines;
50+
51+
if (addr == FTRACE_ADDR)
52+
return &plt[FTRACE_PLT_IDX];
53+
if (addr == FTRACE_REGS_ADDR &&
54+
IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS))
55+
return &plt[FTRACE_REGS_PLT_IDX];
56+
57+
return NULL;
58+
}
59+
60+
static unsigned long get_plt_addr(struct module *mod, unsigned long addr)
61+
{
62+
struct plt_entry *plt;
63+
64+
plt = get_ftrace_plt(mod, addr);
65+
if (!plt) {
66+
pr_err("ftrace: no module PLT for %ps\n", (void *)addr);
67+
return -EINVAL;
68+
}
69+
70+
return (unsigned long)plt;
71+
}
72+
#endif
73+
3274
int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, unsigned long addr)
3375
{
3476
u32 old, new;
3577
unsigned long pc;
78+
long offset __maybe_unused;
3679

3780
pc = rec->ip + LOONGARCH_INSN_SIZE;
3881

82+
#ifdef CONFIG_MODULES
83+
offset = (long)pc - (long)addr;
84+
85+
if (offset < -SZ_128M || offset >= SZ_128M) {
86+
int ret;
87+
struct module *mod;
88+
89+
ret = __get_mod(&mod, pc);
90+
if (ret)
91+
return ret;
92+
93+
addr = get_plt_addr(mod, addr);
94+
95+
old_addr = get_plt_addr(mod, old_addr);
96+
}
97+
#endif
98+
3999
new = larch_insn_gen_bl(pc, addr);
40100
old = larch_insn_gen_bl(pc, old_addr);
41101

42102
return ftrace_modify_code(pc, old, new, true);
43103
}
104+
44105
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_REGS */
45106

46107
int ftrace_update_ftrace_func(ftrace_func_t func)
@@ -91,9 +152,25 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
91152
{
92153
u32 old, new;
93154
unsigned long pc;
155+
long offset __maybe_unused;
94156

95157
pc = rec->ip + LOONGARCH_INSN_SIZE;
96158

159+
#ifdef CONFIG_MODULES
160+
offset = (long)pc - (long)addr;
161+
162+
if (offset < -SZ_128M || offset >= SZ_128M) {
163+
int ret;
164+
struct module *mod;
165+
166+
ret = __get_mod(&mod, pc);
167+
if (ret)
168+
return ret;
169+
170+
addr = get_plt_addr(mod, addr);
171+
}
172+
#endif
173+
97174
old = larch_insn_gen_nop();
98175
new = larch_insn_gen_bl(pc, addr);
99176

@@ -104,9 +181,25 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long ad
104181
{
105182
u32 old, new;
106183
unsigned long pc;
184+
long offset __maybe_unused;
107185

108186
pc = rec->ip + LOONGARCH_INSN_SIZE;
109187

188+
#ifdef CONFIG_MODULES
189+
offset = (long)pc - (long)addr;
190+
191+
if (offset < -SZ_128M || offset >= SZ_128M) {
192+
int ret;
193+
struct module *mod;
194+
195+
ret = __get_mod(&mod, pc);
196+
if (ret)
197+
return ret;
198+
199+
addr = get_plt_addr(mod, addr);
200+
}
201+
#endif
202+
110203
new = larch_insn_gen_nop();
111204
old = larch_insn_gen_bl(pc, addr);
112205

arch/loongarch/kernel/inst.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,17 @@ u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj)
120120
return larch_insn_gen_or(rd, rj, 0);
121121
}
122122

123+
u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm)
124+
{
125+
union loongarch_instruction insn;
126+
127+
insn.reg1i20_format.opcode = lu12iw_op;
128+
insn.reg1i20_format.rd = rd;
129+
insn.reg1i20_format.immediate = imm;
130+
131+
return insn.word;
132+
}
133+
123134
u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm)
124135
{
125136
union loongarch_instruction insn;

arch/loongarch/kernel/module-sections.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <linux/elf.h>
77
#include <linux/kernel.h>
88
#include <linux/module.h>
9+
#include <linux/ftrace.h>
910

1011
Elf_Addr module_emit_got_entry(struct module *mod, Elf_Shdr *sechdrs, Elf_Addr val)
1112
{
@@ -103,7 +104,7 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
103104
char *secstrings, struct module *mod)
104105
{
105106
unsigned int i, num_plts = 0, num_gots = 0;
106-
Elf_Shdr *got_sec, *plt_sec, *plt_idx_sec;
107+
Elf_Shdr *got_sec, *plt_sec, *plt_idx_sec, *tramp = NULL;
107108

108109
/*
109110
* Find the empty .plt sections.
@@ -115,6 +116,8 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
115116
mod->arch.plt.shndx = i;
116117
else if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt.idx"))
117118
mod->arch.plt_idx.shndx = i;
119+
else if (!strcmp(secstrings + sechdrs[i].sh_name, ".ftrace_trampoline"))
120+
tramp = sechdrs + i;
118121
}
119122

120123
if (!mod->arch.got.shndx) {
@@ -170,5 +173,12 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
170173
mod->arch.plt_idx.num_entries = 0;
171174
mod->arch.plt_idx.max_entries = num_plts;
172175

176+
if (tramp) {
177+
tramp->sh_type = SHT_NOBITS;
178+
tramp->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
179+
tramp->sh_addralign = __alignof__(struct plt_entry);
180+
tramp->sh_size = NR_FTRACE_PLTS * sizeof(struct plt_entry);
181+
}
182+
173183
return 0;
174184
}

arch/loongarch/kernel/module.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
#include <linux/vmalloc.h>
1616
#include <linux/slab.h>
1717
#include <linux/fs.h>
18+
#include <linux/ftrace.h>
1819
#include <linux/string.h>
1920
#include <linux/kernel.h>
2021
#include <asm/alternative.h>
22+
#include <asm/inst.h>
2123

2224
static int rela_stack_push(s64 stack_value, s64 *rela_stack, size_t *rela_stack_top)
2325
{
@@ -473,6 +475,23 @@ void *module_alloc(unsigned long size)
473475
GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE, __builtin_return_address(0));
474476
}
475477

478+
static void module_init_ftrace_plt(const Elf_Ehdr *hdr,
479+
const Elf_Shdr *sechdrs, struct module *mod)
480+
{
481+
#ifdef CONFIG_DYNAMIC_FTRACE
482+
struct plt_entry *ftrace_plts;
483+
484+
ftrace_plts = (void *)sechdrs->sh_addr;
485+
486+
ftrace_plts[FTRACE_PLT_IDX] = emit_plt_entry(FTRACE_ADDR);
487+
488+
if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS))
489+
ftrace_plts[FTRACE_REGS_PLT_IDX] = emit_plt_entry(FTRACE_REGS_ADDR);
490+
491+
mod->arch.ftrace_trampolines = ftrace_plts;
492+
#endif
493+
}
494+
476495
int module_finalize(const Elf_Ehdr *hdr,
477496
const Elf_Shdr *sechdrs, struct module *mod)
478497
{
@@ -482,6 +501,8 @@ int module_finalize(const Elf_Ehdr *hdr,
482501
for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) {
483502
if (!strcmp(".altinstructions", secstrs + s->sh_name))
484503
apply_alternatives((void *)s->sh_addr, (void *)s->sh_addr + s->sh_size);
504+
if (!strcmp(".ftrace_trampoline", secstrs + s->sh_name))
505+
module_init_ftrace_plt(hdr, s, mod);
485506
}
486507

487508
return 0;

0 commit comments

Comments
 (0)