summaryrefslogtreecommitdiff
path: root/tools/objtool/tests/x86/fixtures
diff options
context:
space:
mode:
Diffstat (limited to 'tools/objtool/tests/x86/fixtures')
-rw-r--r--tools/objtool/tests/x86/fixtures/alt_annotate.c57
-rw-r--r--tools/objtool/tests/x86/fixtures/checksum_alt.c66
-rw-r--r--tools/objtool/tests/x86/fixtures/empty_alternative.c77
-rw-r--r--tools/objtool/tests/x86/fixtures/kcfi.c39
-rw-r--r--tools/objtool/tests/x86/fixtures/special_sections.c77
-rw-r--r--tools/objtool/tests/x86/fixtures/static_call_no_key.c32
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;
+}