1*dff07c58SSong Liu // SPDX-License-Identifier: GPL-2.0
2*dff07c58SSong Liu /*
3*dff07c58SSong Liu * An x86 alternative with an empty replacement, as the second entry of
4*dff07c58SSong Liu * ALTERNATIVE_2("orig", "repl", ft1, "", ft2) produces. Its replacement
5*dff07c58SSong Liu * offset still gets a relocation, but the label it points at is the end of the
6*dff07c58SSong Liu * previous replacement, which is also where the *next* one begins -- here,
7*dff07c58SSong Liu * neighbor()'s. The value is meaningless; it is only ever used with a length
8*dff07c58SSong Liu * of zero.
9*dff07c58SSong Liu *
10*dff07c58SSong Liu * struct alt_instr is written out by hand so the fixture builds without kernel
11*dff07c58SSong Liu * headers: s32 instr_offset, s32 repl_offset, u32 ft_flags, u8 instrlen,
12*dff07c58SSong Liu * u8 replacementlen. The section carries an entsize because klp diff needs
13*dff07c58SSong Liu * either that or an ANNOTATE_DATA_SPECIAL annotation to find entry boundaries.
14*dff07c58SSong Liu *
15*dff07c58SSong Liu * The replacement labels are global so the relocations name them rather than
16*dff07c58SSong Liu * .altinstr_replacement plus an addend.
17*dff07c58SSong Liu */
18*dff07c58SSong Liu
19*dff07c58SSong Liu static const char __modinfo[]
20*dff07c58SSong Liu __attribute__((section(".modinfo"), used, aligned(1))) = "\0name=vmlinux";
21*dff07c58SSong Liu
22*dff07c58SSong Liu extern int neighbor_only(int x);
23*dff07c58SSong Liu
target(int x)24*dff07c58SSong Liu int target(int x)
25*dff07c58SSong Liu {
26*dff07c58SSong Liu asm volatile(
27*dff07c58SSong Liu "661: nop\n\t"
28*dff07c58SSong Liu ".pushsection .altinstr_replacement, \"ax\"\n\t"
29*dff07c58SSong Liu ".globl target_repl\n\t"
30*dff07c58SSong Liu "target_repl:\n\t"
31*dff07c58SSong Liu " nop\n\t"
32*dff07c58SSong Liu "target_repl_end:\n\t"
33*dff07c58SSong Liu ".popsection\n\t"
34*dff07c58SSong Liu ".pushsection .altinstructions, \"aM\", @progbits, 14\n\t"
35*dff07c58SSong Liu /* a real replacement */
36*dff07c58SSong Liu ".long 661b - .\n\t"
37*dff07c58SSong Liu ".long target_repl - .\n\t"
38*dff07c58SSong Liu ".long 0\n\t"
39*dff07c58SSong Liu ".byte 1\n\t"
40*dff07c58SSong Liu ".byte target_repl_end - target_repl\n\t"
41*dff07c58SSong Liu /* an empty one, pointing at neighbor()'s replacement */
42*dff07c58SSong Liu ".long 661b - .\n\t"
43*dff07c58SSong Liu ".long neighbor_repl - .\n\t"
44*dff07c58SSong Liu ".long 0\n\t"
45*dff07c58SSong Liu ".byte 1\n\t"
46*dff07c58SSong Liu ".byte 0\n\t"
47*dff07c58SSong Liu ".popsection\n\t");
48*dff07c58SSong Liu #ifdef PATCHED
49*dff07c58SSong Liu return x + 2;
50*dff07c58SSong Liu #else
51*dff07c58SSong Liu return x + 1;
52*dff07c58SSong Liu #endif
53*dff07c58SSong Liu }
54*dff07c58SSong Liu
55*dff07c58SSong Liu /*
56*dff07c58SSong Liu * Unrelated, unpatched, and referencing a symbol nothing else does, so that
57*dff07c58SSong Liu * dragging its replacement in is visible.
58*dff07c58SSong Liu */
neighbor(int x)59*dff07c58SSong Liu int neighbor(int x)
60*dff07c58SSong Liu {
61*dff07c58SSong Liu asm volatile(
62*dff07c58SSong Liu "771: nop\n\t"
63*dff07c58SSong Liu ".pushsection .altinstr_replacement, \"ax\"\n\t"
64*dff07c58SSong Liu ".globl neighbor_repl\n\t"
65*dff07c58SSong Liu "neighbor_repl:\n\t"
66*dff07c58SSong Liu " call neighbor_only\n\t"
67*dff07c58SSong Liu "neighbor_repl_end:\n\t"
68*dff07c58SSong Liu ".popsection\n\t"
69*dff07c58SSong Liu ".pushsection .altinstructions, \"aM\", @progbits, 14\n\t"
70*dff07c58SSong Liu ".long 771b - .\n\t"
71*dff07c58SSong Liu ".long neighbor_repl - .\n\t"
72*dff07c58SSong Liu ".long 0\n\t"
73*dff07c58SSong Liu ".byte 1\n\t"
74*dff07c58SSong Liu ".byte neighbor_repl_end - neighbor_repl\n\t"
75*dff07c58SSong Liu ".popsection\n\t");
76*dff07c58SSong Liu return x;
77*dff07c58SSong Liu }
78