| // 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; | |
| } |