[PATCH 2/6] objtool: Add instrumentation_weak()

From: Peter Zijlstra

Date: Mon Nov 02 2026 - 06:41:24 EST


The strict pairing of instrumentation_begin()/instrumentation_end() gets in the
way of tail-call optimizations, since it requires writing code like:

int bar(void);

noinstr int foo(void)
{
int ret;
instrumentation_begin();
ret = bar();
instrumentation_end();
return ret;
}

Introduce instrumentation_weak(), which is like instrumentation_begin() but
does not nest and does not pair. This way one can write:

noinstr int foo(void)
{
instrumentation_weak();
return bar();
}

And get tail-calls without objtool getting upset.

Reported-by: kernel test robot <lkp@xxxxxxxxx>
Signed-off-by: Peter Zijlstra (Intel) <peterz@xxxxxxxxxxxxx>
---
include/linux/annotate.h | 1 +
include/linux/instrumentation.h | 17 +++++++++++++++++
include/linux/objtool_types.h | 1 +
tools/include/linux/objtool_types.h | 1 +
tools/objtool/check.c | 17 ++++++++++++-----
5 files changed, 32 insertions(+), 5 deletions(-)

--- a/include/linux/annotate.h
+++ b/include/linux/annotate.h
@@ -71,6 +71,7 @@
*/
#define ANNOTATE_INSTR_BEGIN(label) ASM_ANNOTATE_LABEL(label, ANNOTYPE_INSTR_BEGIN)
#define ANNOTATE_INSTR_END(label) ASM_ANNOTATE_LABEL(label, ANNOTYPE_INSTR_END)
+#define ANNOTATE_INSTR_WEAK(label) ASM_ANNOTATE_LABEL(label, ANNOTYPE_INSTR_WEAK)
/*
* objtool annotation to ignore the alternatives and only consider the original
* instruction(s).
--- a/include/linux/instrumentation.h
+++ b/include/linux/instrumentation.h
@@ -52,9 +52,26 @@
: : "i" (c)); \
})
#define instrumentation_end() __instrumentation_end(__COUNTER__)
+
+/*
+ * A very much weaker variant of instrumentation_begin(). It does not nest and
+ * it does not pair with instrumentation_end().
+ *
+ * Its purpose is to allow calling instrumented code while preserving
+ * tail-call optimization, something which is otherwise impossible with the
+ * need to call instrumentation_end().
+ */
+#define __instrumentation_weak(c) ({ \
+ asm volatile(__stringify(c) ": nop\n\t" \
+ ANNOTATE_INSTR_WEAK(__ASM_BREF(c)) \
+ : : "i" (c)); \
+})
+#define instrumentation_weak() __instrumentation_weak(__COUNTER__)
+
#else /* !CONFIG_NOINSTR_VALIDATION */
# define instrumentation_begin() do { } while(0)
# define instrumentation_end() do { } while(0)
+# define instrumentation_weak() do { } while(0)
#endif /* CONFIG_NOINSTR_VALIDATION */

#endif /* __LINUX_INSTRUMENTATION_H */
--- a/include/linux/objtool_types.h
+++ b/include/linux/objtool_types.h
@@ -66,6 +66,7 @@ struct unwind_hint {
#define ANNOTYPE_INTRA_FUNCTION_CALL 7
#define ANNOTYPE_REACHABLE 8
#define ANNOTYPE_NOCFI 9
+#define ANNOTYPE_INSTR_WEAK 10

#define ANNOTYPE_DATA_SPECIAL 1

--- a/tools/include/linux/objtool_types.h
+++ b/tools/include/linux/objtool_types.h
@@ -66,6 +66,7 @@ struct unwind_hint {
#define ANNOTYPE_INTRA_FUNCTION_CALL 7
#define ANNOTYPE_REACHABLE 8
#define ANNOTYPE_NOCFI 9
+#define ANNOTYPE_INSTR_WEAK 10

#define ANNOTYPE_DATA_SPECIAL 1

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2435,11 +2435,15 @@ static int __annotate_late(struct objtoo
break;

case ANNOTYPE_INSTR_BEGIN:
- insn->instr++;
+ insn->instr += 2;
break;

case ANNOTYPE_INSTR_END:
- insn->instr--;
+ insn->instr -= 2;
+ break;
+
+ case ANNOTYPE_INSTR_WEAK:
+ insn->instr += 1;
break;

case ANNOTYPE_UNRET_BEGIN:
@@ -3535,6 +3539,11 @@ static int validate_sibling_call(struct
struct instruction *insn,
struct insn_state *state)
{
+ if (state->noinstr && state->instr > 1) {
+ WARN_INSN(insn, "sibling call with instrumentation enabled");
+ return 1;
+ }
+
if (insn_func(insn) && has_modified_stack_frame(insn, state)) {
WARN_INSN(insn, "sibling call from callable instruction with modified stack frame");
return 1;
@@ -3545,7 +3554,7 @@ static int validate_sibling_call(struct

static int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state)
{
- if (state->noinstr && state->instr > 0) {
+ if (state->noinstr && state->instr > 1) {
WARN_INSN(insn, "return with instrumentation enabled");
return 1;
}
@@ -3646,8 +3655,6 @@ static bool skip_alt_group(struct instru

static int validate_branch(struct objtool_file *file, struct symbol *func,
struct instruction *insn, struct insn_state state);
-static int do_validate_branch(struct objtool_file *file, struct symbol *func,
- struct instruction *insn, struct insn_state *state);

static int validate_insn(struct objtool_file *file, struct symbol *func,
struct instruction *insn, struct insn_state *statep,