Commit dc438db5 authored by Ard Biesheuvel's avatar Ard Biesheuvel
Browse files

ARM: ftrace: use trampolines to keep .init.text in branching range



Kernel images that are large in comparison to the range of a direct
branch may fail to work as expected with ftrace, as patching a direct
branch to one of the core ftrace routines may not be possible from the
.init.text section, if it is emitted too far away from the normal .text
section.

This is more likely to affect Thumb2 builds, given that its range is
only -/+ 16 MiB (as opposed to ARM which has -/+ 32 MiB), but may occur
in either ISA.

To work around this, add a couple of trampolines to .init.text and
swap these in when the ftrace patching code is operating on callers in
.init.text.

Signed-off-by: default avatarArd Biesheuvel <ardb@kernel.org>
Reviewed-by: default avatarNick Desaulniers <ndesaulniers@google.com>
Reviewed-by: default avatarLinus Walleij <linus.walleij@linaro.org>
Reviewed-by: default avatarSteven Rostedt (Google) <rostedt@goodmis.org>
parent ad1c2f39
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -270,3 +270,19 @@ ENTRY(ftrace_stub)
.Lftrace_stub:
	ret	lr
ENDPROC(ftrace_stub)

#ifdef CONFIG_DYNAMIC_FTRACE

	__INIT

	.macro	init_tramp, dst:req
ENTRY(\dst\()_from_init)
	ldr	pc, =\dst
ENDPROC(\dst\()_from_init)
	.endm

	init_tramp	ftrace_caller
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
	init_tramp	ftrace_regs_caller
#endif
#endif
+20 −3
Original line number Diff line number Diff line
@@ -62,9 +62,20 @@ static unsigned long ftrace_nop_replace(struct dyn_ftrace *rec)
	return NOP;
}

static unsigned long adjust_address(struct dyn_ftrace *rec, unsigned long addr)
void ftrace_caller_from_init(void);
void ftrace_regs_caller_from_init(void);

static unsigned long __ref adjust_address(struct dyn_ftrace *rec,
					  unsigned long addr)
{
	if (!IS_ENABLED(CONFIG_DYNAMIC_FTRACE) ||
	    system_state >= SYSTEM_FREEING_INITMEM ||
	    likely(!is_kernel_inittext(rec->ip)))
		return addr;
	if (!IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS) ||
	    addr == (unsigned long)&ftrace_caller)
		return (unsigned long)&ftrace_caller_from_init;
	return (unsigned long)&ftrace_regs_caller_from_init;
}

int ftrace_arch_code_modify_prepare(void)
@@ -200,7 +211,13 @@ int ftrace_make_nop(struct module *mod,
#endif

	new = ftrace_nop_replace(rec);
	ret = ftrace_modify_code(ip, old, new, true);
	/*
	 * Locations in .init.text may call __gnu_mcount_mc via a linker
	 * emitted veneer if they are too far away from its implementation, and
	 * so validation may fail spuriously in such cases. Let's work around
	 * this by omitting those from validation.
	 */
	ret = ftrace_modify_code(ip, old, new, !is_kernel_inittext(ip));

	return ret;
}