diff options
Diffstat (limited to 'tools/objtool/tests/x86/fixtures')
| -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 |
6 files changed, 348 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; +} |
