xref: /linux/tools/objtool/tests/x86/fixtures/empty_alternative.c (revision 5b6dca02b3e2254d2fd5706d76d7ab4610a67296)
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