[tip: objtool/core] objtool/klp: Add test for jump table key relocations
"tip-bot2 for Puranjay Mohan" <[email protected]>
| Newsgroups | gmane.linux.kernel |
|---|---|
| Message-ID | <178972655168.1720534.6330971626454523212.tip-bot2@tip-bot2> |
The following commit has been merged into the objtool/core branch of tip: Commit-ID: c521de5718bedac1607a93b74bdf39240ebd4806 Gitweb: https://git.kernel.org/tip/c521de5718bedac1607a93b74bdf39240ebd4806 Author: Puranjay Mohan <[email protected]> AuthorDate: Wed, 16 Sep 2026 11:43:12 -07:00 Committer: Josh Poimboeuf <[email protected]> CommitterDate: Wed, 16 Sep 2026 17:13:27 -07:00 objtool/klp: Add test for jump table key relocations A cloned __jump_table entry must keep a relocation in its key slot. objtool parses the table again after klp diff to convert the static branch's scaffold instruction, and an empty slot leaves it unable to do so. The scaffold then stays as emitted and the module trips BUG() in __jump_label_patch() once the key's state differs from its compile-time default. Check both halves for a vmlinux-owned key: unexported gives a tombstone plus a klp relocation, exported gives an ordinary relocation and no klp machinery. The fixture defines the key as an STT_OBJECT, which is what the kernel emits. validate_special_section_klp_reloc() ignores anything else, so a fixture using an undefined extern would skip the interesting paths. Signed-off-by: Puranjay Mohan <[email protected]> Assisted-by: Claude:claude-opus-5 Signed-off-by: Song Liu <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Josh Poimboeuf <[email protected]> --- tools/objtool/tests/generic/fixtures/jump_label.c | 76 +++++++++++++- tools/objtool/tests/generic/test-jump-label-key.sh | 44 ++++++++- 2 files changed, 120 insertions(+) create mode 100644 tools/objtool/tests/generic/fixtures/jump_label.c create mode 100755 tools/objtool/tests/generic/test-jump-label-key.sh diff --git a/tools/objtool/tests/generic/fixtures/jump_label.c b/tools/objtool/tests/generic/fixtures/jump_label.c new file mode 100644 index 0000000..ecd3c06 --- /dev/null +++ b/tools/objtool/tests/generic/fixtures/jump_label.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Static branch in a patched function. The jump table entry is written out by + * hand, mirroring JUMP_TABLE_ENTRY(), so the fixture builds without kernel + * headers. The key is an STT_OBJECT; anything else is ignored by + * validate_special_section_klp_reloc(). + * + * MODNAME selects whether the key is taken to belong to vmlinux or a module. + * + * NEW_KEY puts the whole static branch behind PATCHED, so the patch introduces + * one where the original had none -- a different question from patching code + * that already has a key, because the __jump_table entry itself is new. + * + * KEY_NAME renames the key. Two names are special to + * validate_special_section_klp_reloc(): a __tracepoint_* key and the + * __UNIQUE_ID_ddebug_* one pr_debug() generates are both unsupported in a + * module, but are disabled with a warning rather than rejected, because the + * kernel is full of them and refusing outright would make ordinary functions + * unpatchable. + * + * STATIC_KEY makes the key file-local. That changes the shape of the + * relocation rather than the meaning of the code: a reference to a static lands + * on the section symbol plus an addend, so the key has to be resolved from the + * section before it can be recognised as a key at all. + */ + +#ifndef MODNAME +#define MODNAME "vmlinux" +#endif + +#ifndef KEY_NAME +#define KEY_NAME klp_test_key +#endif + +static const char __modinfo[] + __attribute__((section(".modinfo"), used, aligned(1))) = "\0name=" MODNAME; + +#ifdef STATIC_KEY +static long KEY_NAME; +#else +long KEY_NAME; +#endif + +int target(int x) +{ + int r = x; + +#if defined(NEW_KEY) && !defined(PATCHED) + /* The original has no static branch at all. */ + return r + 1; +#else + asm goto( + "1: nop\n\t" + ".pushsection __jump_table, \"aw\"\n\t" + ".balign 8\n\t" + "912:\n\t" + ".pushsection .discard.annotate_data, \"M\", @progbits, 8\n\t" + ".long 912b - ., 1\n\t" + ".popsection\n\t" + ".long 1b - ., %l[l_yes] - .\n\t" + ".quad %c0 - .\n\t" + ".popsection\n\t" + : : "i" (&KEY_NAME) : : l_yes); + + r += 1; + goto out; +l_yes: + r += 2; +out: +#endif +#ifdef PATCHED + return r + 100; +#else + return r; +#endif +} diff --git a/tools/objtool/tests/generic/test-jump-label-key.sh b/tools/objtool/tests/generic/test-jump-label-key.sh new file mode 100755 index 0000000..142f94b --- /dev/null +++ b/tools/objtool/tests/generic/test-jump-label-key.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# A cloned __jump_table entry must keep a relocation in its key slot, whether +# the key needs a klp relocation or not. + +. "$(dirname "$0")/../lib.sh" + +key=klp_test_key + +setup +build_pair jump_label.c + +has_input_section orig.o __jump_table || + probe_skip "fixture produced no __jump_table on this arch" + +key_slot_relocs() +{ + out_relocs | awk '/rela__jump_table/,/^$/' | grep -c "^0*8[[:space:]]" +} + +# Unexported: klp relocation, key slot holds a tombstone. +export_syms +run_diff + +[ "$(key_slot_relocs)" = 1 ] || + fail "unexported key: key slot has no relocation" +out_relocs | awk '/rela__jump_table/,/^$/' | grep -q "\.klp\.tombstone\.$key" || + fail "unexported key: expected a .klp.tombstone.$key relocation" +out_symbols | grep -q "\.klp\.sym\..*\.$key," || + fail "unexported key: no .klp.sym reference for the real relocation" + +# Exported: ordinary relocation, no klp machinery. +export_syms "$key" +run_diff + +[ "$(key_slot_relocs)" = 1 ] || + fail "exported key: key slot has no relocation" +out_relocs | awk '/rela__jump_table/,/^$/' | grep -q "[[:space:]]$key[[:space:]]*+" || + fail "exported key: expected a direct relocation to $key" +out_symbols | grep -q '\.klp\.tombstone\.' && + fail "exported key: tombstone emitted for an exported symbol" + +pass "key slot populated for exported and unexported vmlinux keys"