diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-20 12:55:04 -1000 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-20 12:55:04 -1000 |
| commit | 5b6dca02b3e2254d2fd5706d76d7ab4610a67296 (patch) | |
| tree | 443bf1de39c37775248ae83c2f287c09fe15f749 /tools/objtool/tests/x86 | |
| parent | 776bb6df3f16ab68c40a97dbb8af26af4185d7c0 (diff) | |
| parent | 945537271df1e3680be9b32af7041ac20f1184fb (diff) | |
Merge tag 'objtool-core-2026-10-18' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull objtool updates from Ingo Molnar:
- Add unit test suite for objtool klp functionality and
known failure modes (by Song Liu and Puranjay Mohan,
with fixes by Joe Lawrence)
* tag 'objtool-core-2026-10-18' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (58 commits)
objtool/klp: Add test for ThinLTO symbols sharing a demangled name
objtool/klp: Add test for Clang switch jump tables
objtool/klp: Add test for UBSAN metadata in an unchanged function
objtool/klp: Add test for absolute and __ADDRESSABLE symbols
objtool/klp: Add test for a patch which strips a data annotation
objtool/klp: Add test for the alignment of cloned data sections
objtool/klp: Add test for alternative replacement code in checksums
objtool/klp: Add test for instruction operand checksums
objtool/klp: Add test for a static call introduced by the patch
objtool/klp: Add test for tracepoint and pr_debug static branch keys
objtool/klp: Add test for a static branch introduced by the patch
objtool/klp: Add test for symbols with no checksum entry of their own
objtool/klp: Add test for data object checksums
objtool/klp: Test text annotations on alternative replacements
objtool/klp: Test a hand-built livepatch module's static call keys
objtool/klp: Test rejection of a file-local static branch key
objtool/klp: Add test for symbols whose linkage the patch changes
objtool/klp: Add test for kCFI prefix symbols and traps
objtool/klp: Add test for __bug_table, __ex_table and __mcount_loc extraction
objtool/klp: Add test for static locals which must not be correlated
...
Diffstat (limited to 'tools/objtool/tests/x86')
| -rw-r--r-- | tools/objtool/tests/x86/fixtures/alt_annotate.c | 57 | ||||
| -rw-r--r-- | tools/objtool/tests/x86/fixtures/checksum_alt.c | 66 | ||||
| -rw-r--r-- | tools/objtool/tests/x86/fixtures/empty_alternative.c | 77 | ||||
| -rw-r--r-- | tools/objtool/tests/x86/fixtures/kcfi.c | 39 | ||||
| -rw-r--r-- | tools/objtool/tests/x86/fixtures/special_sections.c | 77 | ||||
| -rw-r--r-- | tools/objtool/tests/x86/fixtures/static_call_no_key.c | 32 | ||||
| -rwxr-xr-x | tools/objtool/tests/x86/test-alt-annotation.sh | 38 | ||||
| -rwxr-xr-x | tools/objtool/tests/x86/test-checksum-alt.sh | 45 | ||||
| -rwxr-xr-x | tools/objtool/tests/x86/test-empty-alternative.sh | 31 | ||||
| -rwxr-xr-x | tools/objtool/tests/x86/test-kcfi.sh | 39 | ||||
| -rwxr-xr-x | tools/objtool/tests/x86/test-manual-klp-static-call.sh | 40 | ||||
| -rwxr-xr-x | tools/objtool/tests/x86/test-special-sections.sh | 42 |
12 files changed, 583 insertions, 0 deletions
diff --git a/tools/objtool/tests/x86/fixtures/alt_annotate.c b/tools/objtool/tests/x86/fixtures/alt_annotate.c new file mode 100644 index 000000000000..af44d320dcb0 --- /dev/null +++ b/tools/objtool/tests/x86/fixtures/alt_annotate.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * An x86 alternative whose replacement instruction carries a text annotation. + * + * The kernel does this wherever an ALTERNATIVE contains something objtool has + * to be told about -- a retpoline-safe indirect branch, an intentionally + * missing ENDBR -- so the .discard.annotate_insn entry references an address + * inside .altinstr_replacement rather than inside a function. + * + * Two things make that awkward for klp diff, and both are why this fixture + * exists. Replacement code has no real symbol: objtool invents a NOTYPE fake + * symbol for it, so an annotation pointing there does not reference a FUNC. + * And .discard.annotate_insn has to be cloned after .altinstructions, or the + * replacement it names has no clone to point at yet. + * + * struct alt_instr is written out by hand as in empty_alternative.c: s32 + * instr_offset, s32 repl_offset, u32 ft_flags, u8 instrlen, u8 replacementlen, + * with an entsize so klp diff can find the entry boundaries. + * .discard.annotate_insn entries are s32 offset, s32 type; type 2 is + * ANNOTYPE_RETPOLINE_SAFE. + * + * The replacement label is global so the relocations name it rather than + * .altinstr_replacement plus an addend, which klp diff cannot convert. It is + * still NOTYPE, which is the shape that matters here. + */ + +static const char __modinfo[] + __attribute__((section(".modinfo"), used, aligned(1))) = "\0name=vmlinux"; + +int target(int x) +{ + asm volatile( + "661: nop\n\t" + ".pushsection .altinstr_replacement, \"ax\"\n\t" + ".globl target_repl\n\t" + "target_repl:\n\t" + " nop\n\t" + /* The annotation lands inside the replacement. */ + ".pushsection .discard.annotate_insn, \"M\", @progbits, 8\n\t" + ".long target_repl - .\n\t" + ".long 2\n\t" + ".popsection\n\t" + "target_repl_end:\n\t" + ".popsection\n\t" + ".pushsection .altinstructions, \"aM\", @progbits, 14\n\t" + ".long 661b - .\n\t" + ".long target_repl - .\n\t" + ".long 0\n\t" + ".byte 1\n\t" + ".byte target_repl_end - target_repl\n\t" + ".popsection\n\t"); +#ifdef PATCHED + return x + 2; +#else + return x + 1; +#endif +} diff --git a/tools/objtool/tests/x86/fixtures/checksum_alt.c b/tools/objtool/tests/x86/fixtures/checksum_alt.c new file mode 100644 index 000000000000..ed342d94eb9e --- /dev/null +++ b/tools/objtool/tests/x86/fixtures/checksum_alt.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * An x86 alternative whose replacement code is part of the patched function's + * checksum. + * + * checksum_update_insn() walks insn->alts after hashing the instruction + * itself, hashing the alternative's type and, when the replacement forms a + * group, its feature number and every instruction in it. So editing only the + * replacement -- code the CPU may or may not ever run -- has to move the + * function's checksum. + * + * It is reached through objtool's own alternative handling, so the object has + * to go through the check pass first: insn->alts is built there, not by the + * compiler. + * + * struct alt_instr is written out by hand as in empty_alternative.c: s32 + * instr_offset, s32 repl_offset, u32 ft_flags, u8 instrlen, u8 replacementlen. + * + * Variants, applied to the patched build only: + * + * ALT_REPL the replacement instruction changes; the original does not + * ALT_FEATURE the feature number changes; no code changes at all + */ + +static const char __modinfo[] + __attribute__((section(".modinfo"), used, aligned(1))) = "\0name=vmlinux"; + +/* + * Both spellings are two bytes, because a replacement may not be longer than + * the instruction it replaces: "xchg %ax, %ax" is 66 90 and two nops are + * 90 90. The original below is padded to match. + */ +#if defined(PATCHED) && defined(ALT_REPL) +#define REPL_INSN " nop\n\t nop\n\t" +#else +#define REPL_INSN " xchg %ax, %ax\n\t" +#endif + +#if defined(PATCHED) && defined(ALT_FEATURE) +#define FEATURE "7" +#else +#define FEATURE "3" +#endif + +int target(int x) +{ + asm volatile( + "661: nop\n\t" + " nop\n\t" + "662:\n\t" + ".pushsection .altinstr_replacement, \"ax\"\n\t" + ".globl target_repl\n\t" + "target_repl:\n\t" + REPL_INSN + "target_repl_end:\n\t" + ".popsection\n\t" + ".pushsection .altinstructions, \"aM\", @progbits, 14\n\t" + ".long 661b - .\n\t" + ".long target_repl - .\n\t" + ".long " FEATURE "\n\t" + ".byte 662b - 661b\n\t" + ".byte target_repl_end - target_repl\n\t" + ".popsection\n\t"); + + return x + 1; +} diff --git a/tools/objtool/tests/x86/fixtures/empty_alternative.c b/tools/objtool/tests/x86/fixtures/empty_alternative.c new file mode 100644 index 000000000000..9336d74bfa92 --- /dev/null +++ b/tools/objtool/tests/x86/fixtures/empty_alternative.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * An x86 alternative with an empty replacement, as the second entry of + * ALTERNATIVE_2("orig", "repl", ft1, "", ft2) produces. Its replacement + * offset still gets a relocation, but the label it points at is the end of the + * previous replacement, which is also where the *next* one begins -- here, + * neighbor()'s. The value is meaningless; it is only ever used with a length + * of zero. + * + * struct alt_instr is written out by hand so the fixture builds without kernel + * headers: s32 instr_offset, s32 repl_offset, u32 ft_flags, u8 instrlen, + * u8 replacementlen. The section carries an entsize because klp diff needs + * either that or an ANNOTATE_DATA_SPECIAL annotation to find entry boundaries. + * + * The replacement labels are global so the relocations name them rather than + * .altinstr_replacement plus an addend. + */ + +static const char __modinfo[] + __attribute__((section(".modinfo"), used, aligned(1))) = "\0name=vmlinux"; + +extern int neighbor_only(int x); + +int target(int x) +{ + asm volatile( + "661: nop\n\t" + ".pushsection .altinstr_replacement, \"ax\"\n\t" + ".globl target_repl\n\t" + "target_repl:\n\t" + " nop\n\t" + "target_repl_end:\n\t" + ".popsection\n\t" + ".pushsection .altinstructions, \"aM\", @progbits, 14\n\t" + /* a real replacement */ + ".long 661b - .\n\t" + ".long target_repl - .\n\t" + ".long 0\n\t" + ".byte 1\n\t" + ".byte target_repl_end - target_repl\n\t" + /* an empty one, pointing at neighbor()'s replacement */ + ".long 661b - .\n\t" + ".long neighbor_repl - .\n\t" + ".long 0\n\t" + ".byte 1\n\t" + ".byte 0\n\t" + ".popsection\n\t"); +#ifdef PATCHED + return x + 2; +#else + return x + 1; +#endif +} + +/* + * Unrelated, unpatched, and referencing a symbol nothing else does, so that + * dragging its replacement in is visible. + */ +int neighbor(int x) +{ + asm volatile( + "771: nop\n\t" + ".pushsection .altinstr_replacement, \"ax\"\n\t" + ".globl neighbor_repl\n\t" + "neighbor_repl:\n\t" + " call neighbor_only\n\t" + "neighbor_repl_end:\n\t" + ".popsection\n\t" + ".pushsection .altinstructions, \"aM\", @progbits, 14\n\t" + ".long 771b - .\n\t" + ".long neighbor_repl - .\n\t" + ".long 0\n\t" + ".byte 1\n\t" + ".byte neighbor_repl_end - neighbor_repl\n\t" + ".popsection\n\t"); + return x; +} diff --git a/tools/objtool/tests/x86/fixtures/kcfi.c b/tools/objtool/tests/x86/fixtures/kcfi.c new file mode 100644 index 000000000000..b62fc60634cf --- /dev/null +++ b/tools/objtool/tests/x86/fixtures/kcfi.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * An indirect call, which under kCFI is preceded by a type check and a trap. + * + * Clang emits a __cfi_<func> prefix symbol carrying the type hash ahead of + * every address-taken function, and records the trap site in .kcfi_traps. + * Both belong to the function and both have to come with it into a patch. + * + * Needs -fsanitize=kcfi, which only Clang has. + */ + +static const char __modinfo[] + __attribute__((section(".modinfo"), used, aligned(1))) = "\0name=vmlinux"; + +static int impl_a(int x) +{ + return x + 1; +} + +static int impl_b(int x) +{ + return x * 2; +} + +__attribute__((noinline)) int (*pick(int x))(int) +{ + return (x & 1) ? impl_a : impl_b; +} + +int target(int x) +{ + int (*fn)(int arg) = pick(x); + +#ifdef PATCHED + return fn(x) + 2; +#else + return fn(x) + 1; +#endif +} diff --git a/tools/objtool/tests/x86/fixtures/special_sections.c b/tools/objtool/tests/x86/fixtures/special_sections.c new file mode 100644 index 000000000000..d42798848fda --- /dev/null +++ b/tools/objtool/tests/x86/fixtures/special_sections.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A special section entry belonging to a patched function. SPECIAL_SEC picks + * which section, since klp diff treats eight of them alike and each needs + * extracting for the patched function and no other. + * + * The entry is written out by hand so the fixture builds without kernel + * headers. Only the leading relocation matters to klp diff; the rest is + * padded to the section's real entry size, because the entries have to be the + * right length for the boundaries between them to fall in the right places. + * + * SPECIAL_RELOCS covers __ex_table, whose entries relocate both the faulting + * instruction and its fixup; objtool rejects one with only the first. + */ + +#ifndef SPECIAL_SEC +#define SPECIAL_SEC "__bug_table" +#endif +#ifndef SPECIAL_ENTSIZE +#define SPECIAL_ENTSIZE 12 +#endif +#ifndef SPECIAL_RELOCS +#define SPECIAL_RELOCS 1 +#endif + +#define STR_(x) #x +#define STR(x) STR_(x) + +static const char __modinfo[] + __attribute__((section(".modinfo"), used, aligned(1))) = "\0name=vmlinux"; + +/* + * other() gets an entry of its own, so that "the untouched function's entry + * was not dragged in" is a question the section can actually answer. With + * only target() contributing, there is nothing for klp diff to leave behind + * and the negative assertion holds however the extraction behaves. + */ +int other(int x) +{ + asm volatile( + "3: nop\n\t" + "4:\n\t" + ".pushsection " SPECIAL_SEC ", \"aM\", @progbits, " + STR(SPECIAL_ENTSIZE) "\n\t" + ".long 3b - .\n\t" +#if SPECIAL_RELOCS > 1 + ".long 4b - .\n\t" + ".fill " STR(SPECIAL_ENTSIZE) " - 8, 1, 0\n\t" +#else + ".fill " STR(SPECIAL_ENTSIZE) " - 4, 1, 0\n\t" +#endif + ".popsection\n\t"); + + return x + 9; +} + +int target(int x) +{ + asm volatile( + "1: nop\n\t" + "2:\n\t" + ".pushsection " SPECIAL_SEC ", \"aM\", @progbits, " + STR(SPECIAL_ENTSIZE) "\n\t" + ".long 1b - .\n\t" +#if SPECIAL_RELOCS > 1 + ".long 2b - .\n\t" + ".fill " STR(SPECIAL_ENTSIZE) " - 8, 1, 0\n\t" +#else + ".fill " STR(SPECIAL_ENTSIZE) " - 4, 1, 0\n\t" +#endif + ".popsection\n\t"); +#ifdef PATCHED + return x + 2; +#else + return x + 1; +#endif +} diff --git a/tools/objtool/tests/x86/fixtures/static_call_no_key.c b/tools/objtool/tests/x86/fixtures/static_call_no_key.c new file mode 100644 index 000000000000..748ba7c0f86d --- /dev/null +++ b/tools/objtool/tests/x86/fixtures/static_call_no_key.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * A static call to a trampoline whose key symbol this object cannot see. + * + * That is the normal situation for a module: __SCK__* keys are not exported, + * and read-only access is granted at load time instead. objtool's static call + * handling has to accept it for any module, including a livepatch module built + * by hand rather than by klp-build. + * + * LIVEPATCH adds the .modinfo tag which makes objtool treat this as a + * livepatch module. + */ + +static const char __modinfo[] + __attribute__((section(".modinfo"), used, aligned(1))) = +#ifdef LIVEPATCH + "\0livepatch=Y" +#endif + "\0name=klp_testmod"; + +/* + * The trampoline is undefined here, exactly as it is for a module calling a + * static call defined in vmlinux. No __SCK__klp_test_call accompanies it. + */ +extern void __SCT__klp_test_call(void); + +int target(int x) +{ + __asm__ volatile("call __SCT__klp_test_call\n\t" ::: "memory"); + + return x + 1; +} diff --git a/tools/objtool/tests/x86/test-alt-annotation.sh b/tools/objtool/tests/x86/test-alt-annotation.sh new file mode 100755 index 000000000000..96760e6df9e5 --- /dev/null +++ b/tools/objtool/tests/x86/test-alt-annotation.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# A text annotation on an instruction inside an alternative's replacement must +# be carried into the patch. +# +# The kernel annotates replacement code wherever objtool has to be told +# something about it -- a retpoline-safe indirect branch, a deliberately absent +# ENDBR. Two things made klp diff drop those annotations: +# +# - replacement code has no real symbol, so objtool invents a NOTYPE fake +# one, and the extraction only kept references to FUNC symbols; +# - .discard.annotate_insn was processed before .altinstructions, so the +# replacement it referenced had no clone to point at yet. +# +# Nothing fails at build time when the annotation goes missing. It surfaces +# later as objtool warning about, or rejecting, the patched code it was there +# to explain. +# +# Fixed by 62a7a01fde87 ("objtool/klp: Fix extraction of text annotations for +# alternatives"). + +. "$(dirname "$0")/../lib.sh" + +setup +build_pair alt_annotate.c + +assert_input_section .altinstructions +assert_input_section .discard.annotate_insn + +run_diff + +# The annotation has to survive, and to still name the replacement. Checking +# only the section would pass on an entry whose relocation was dropped. +assert_section .discard.annotate_insn +assert_reloc_sym .discard.annotate_insn target_repl + +pass "text annotation on an alternative replacement carried into the patch" diff --git a/tools/objtool/tests/x86/test-checksum-alt.sh b/tools/objtool/tests/x86/test-checksum-alt.sh new file mode 100755 index 000000000000..74ebfca2b3ff --- /dev/null +++ b/tools/objtool/tests/x86/test-checksum-alt.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# An alternative's replacement code counts towards the checksum of the function +# it belongs to. +# +# checksum_update_insn() walks insn->alts after hashing the instruction itself: +# the alternative's type, and where the replacement forms a group, its feature +# number and every instruction in it. So a patch which edits only the +# replacement -- code that runs on some CPUs and not others -- still has to +# move the function's checksum. +# +# If it does not, klp diff decides the function is unchanged and leaves it out. +# The patch then ships the old replacement, and the bug is fixed only on +# machines whose CPU takes the other arm. Which machines those are depends on +# the feature bit, so the failure looks like a machine-specific bug rather than +# a missing patch. +# +# insn->alts exists only after objtool's check pass, so the pair goes through +# that first -- the compiler emits none of this structure itself. +# +# Covers the same ground as corpus/x86_64/checksum-alt-group, +# checksum-alt-no-group and checksum-alt-recursion-guard in Joe Lawrence's +# klp-build unit test corpus. + +. "$(dirname "$0")/../lib.sh" + +setup + +check() +{ + build_pair checksum_alt.c "-D$1" + assert_input_section .altinstructions + run_objtool_check --mcount + run_checksum + + assert_checksum_differs target +} + +# The replacement instruction itself. +check ALT_REPL +# The feature number, with no instruction anywhere changed. +check ALT_FEATURE + +pass "alternative replacement code counts towards the checksum" diff --git a/tools/objtool/tests/x86/test-empty-alternative.sh b/tools/objtool/tests/x86/test-empty-alternative.sh new file mode 100755 index 000000000000..9d40c3a405af --- /dev/null +++ b/tools/objtool/tests/x86/test-empty-alternative.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# An x86 alternative with an empty replacement still gets a relocation for its +# replacement offset, but the label it points at is the end of the previous +# replacement -- which is also the start of the next one. The value is +# meaningless, and get_alt_entry() already ignores it. +# +# Cloning it drags in an unrelated neighboring replacement and everything that +# replacement references. In the reported case an empty alternative in +# meminfo_proc_show() pulled in one from proc_kcore_init(), emitting a klp +# relocation against init text which is long freed by the time the patch is +# applied. + +. "$(dirname "$0")/../lib.sh" + +setup +build_pair empty_alternative.c + +assert_input_section .altinstructions +assert_input_section .altinstr_replacement + +run_diff + +# target's own replacement comes along ... +assert_symbol target_repl +# ... neighbor's does not, nor what it references. +assert_no_symbol neighbor_repl +assert_no_symbol neighbor_only + +pass "empty alternative's replacement offset ignored when cloning" diff --git a/tools/objtool/tests/x86/test-kcfi.sh b/tools/objtool/tests/x86/test-kcfi.sh new file mode 100755 index 000000000000..b583ef608747 --- /dev/null +++ b/tools/objtool/tests/x86/test-kcfi.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Under kCFI an indirect call checks a type hash before jumping, and traps on a +# mismatch. Two things belong to the calling function and must come with it +# into a patch: +# +# - the __cfi_<func> prefix symbol holding the hash. Lose it and the patched +# function has no type identity, so indirect calls to it trap. +# - its .kcfi_traps entry. Lose that and the trap is not recognised as a +# CFI failure, so what should be a clean report becomes an oops. +# +# Neither shows up at build time. + +. "$(dirname "$0")/../lib.sh" + +clang_only "kCFI is a Clang feature" + +setup + +# Declared above that this is Clang's; a given Clang may still be too old. +cc_supports -fsanitize=kcfi || + probe_skip "this clang does not support -fsanitize=kcfi" + +build_pair kcfi.c -fsanitize=kcfi + +assert_input_section .kcfi_traps +assert_input_symbol __cfi_target + +run_diff + +assert_patched target + +# The prefix symbol comes with its function ... +assert_symbol __cfi_target +# ... and so does the trap entry. +assert_section .kcfi_traps + +pass "kCFI prefix symbol and trap entry carried with the patched function" diff --git a/tools/objtool/tests/x86/test-manual-klp-static-call.sh b/tools/objtool/tests/x86/test-manual-klp-static-call.sh new file mode 100755 index 000000000000..6c4d275e3549 --- /dev/null +++ b/tools/objtool/tests/x86/test-manual-klp-static-call.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# objtool's static call handling must accept a livepatch module which cannot +# see a static call's key symbol. +# +# __SCK__* keys are not exported; modules get read-only access at load time +# instead. Livepatch modules built by klp-build do have full access to their +# keys, and a check was added on the strength of that -- but a livepatch module +# can also be written by hand, and samples/livepatch is full of them. One of +# those needs a key it cannot see as soon as it does anything that expands to a +# static call, which with CONFIG_MEM_ALLOC_PROFILING_DEBUG includes allocating +# memory: +# +# samples/livepatch/livepatch-shadow-fix1.o: error: objtool: static_call: +# can't find static_call_key symbol: __SCK__WARN_trap +# +# The module built without the livepatch tag is the control: it takes the same +# path and has always been accepted, so a test which only built the livepatch +# one could not tell this fix from the check being removed altogether. +# +# Fixed by f495054bd12e ("objtool/klp: Fix unexported static call key access +# for manually built livepatch modules"). + +. "$(dirname "$0")/../lib.sh" + +setup + +# Not a klp subcommand: this is objtool's ordinary check pass, which is what +# runs over a hand-built livepatch module during a normal kernel build. +for tag in "" -DLIVEPATCH; do + build_one static_call_no_key.c mod.o $tag + + "$OBJTOOL" --module --static-call "$workdir/mod.o" \ + > "$workdir/objtool.log" 2>&1 || + fail "objtool rejected a ${tag:+livepatch }module which cannot" \ + "see its static call key: $(tail -1 "$workdir/objtool.log")" +done + +pass "livepatch module accepted without access to its static call key" diff --git a/tools/objtool/tests/x86/test-special-sections.sh b/tools/objtool/tests/x86/test-special-sections.sh new file mode 100755 index 000000000000..8dfaa4fc9a36 --- /dev/null +++ b/tools/objtool/tests/x86/test-special-sections.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# klp diff extracts entries from eight special sections. Between them the +# existing tests reach .kcfi_traps, __jump_table, .static_call_sites and +# .altinstructions; __bug_table, __ex_table and __mcount_loc are covered by +# nothing, though the same extraction code serves all of them. +# +# Losing an entry is quiet in every case and wrong in a different way for each: +# a WARN() in patched code that no longer reports where it came from, an +# exception fixup that is simply not there when the faulting instruction traps, +# a function ftrace can no longer see. + +. "$(dirname "$0")/../lib.sh" + + +# section, entry size, relocations per entry +for spec in "__bug_table 12 1" "__ex_table 12 2" "__mcount_loc 8 1"; do + set -- $spec + sec=$1 + + # A fresh workdir per section: run_diff caches its checksums. + setup + build_pair special_sections.c \ + -DSPECIAL_SEC="\"$1\"" -DSPECIAL_ENTSIZE="$2" -DSPECIAL_RELOCS="$3" + + assert_input_section "$sec" + run_diff + + # Extracted, and pointing at the function that was patched. + assert_section "$sec" + assert_reloc_sym "$sec" target + assert_patched target + + # Nothing belonging to the function that was not. + assert_not_patched other + assert_no_reloc_sym "$sec" other + + cleanup +done + +pass "__bug_table, __ex_table and __mcount_loc entries extracted" |
