Skip to content

Commit 923510c

Browse files
Peter ZijlstraIngo Molnar
authored andcommitted
x86/static_call: Add support for Jcc tail-calls
Clang likes to create conditional tail calls like: 0000000000000350 <amd_pmu_add_event>: 350: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 351: R_X86_64_NONE __fentry__-0x4 355: 48 83 bf 20 01 00 00 00 cmpq $0x0,0x120(%rdi) 35d: 0f 85 00 00 00 00 jne 363 <amd_pmu_add_event+0x13> 35f: R_X86_64_PLT32 __SCT__amd_pmu_branch_add-0x4 363: e9 00 00 00 00 jmp 368 <amd_pmu_add_event+0x18> 364: R_X86_64_PLT32 __x86_return_thunk-0x4 Where 0x35d is a static call site that's turned into a conditional tail-call using the Jcc class of instructions. Teach the in-line static call text patching about this. Notably, since there is no conditional-ret, in that case patch the Jcc to point at an empty stub function that does the ret -- or the return thunk when needed. Reported-by: "Erhard F." <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Signed-off-by: Ingo Molnar <[email protected]> Reviewed-by: Masami Hiramatsu (Google) <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent ac0ee0a commit 923510c

File tree

1 file changed

+47
-3
lines changed

1 file changed

+47
-3
lines changed

arch/x86/kernel/static_call.c

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ enum insn_type {
99
NOP = 1, /* site cond-call */
1010
JMP = 2, /* tramp / site tail-call */
1111
RET = 3, /* tramp / site cond-tail-call */
12+
JCC = 4,
1213
};
1314

1415
/*
@@ -25,12 +26,40 @@ static const u8 xor5rax[] = { 0x2e, 0x2e, 0x2e, 0x31, 0xc0 };
2526

2627
static const u8 retinsn[] = { RET_INSN_OPCODE, 0xcc, 0xcc, 0xcc, 0xcc };
2728

29+
static u8 __is_Jcc(u8 *insn) /* Jcc.d32 */
30+
{
31+
u8 ret = 0;
32+
33+
if (insn[0] == 0x0f) {
34+
u8 tmp = insn[1];
35+
if ((tmp & 0xf0) == 0x80)
36+
ret = tmp;
37+
}
38+
39+
return ret;
40+
}
41+
42+
extern void __static_call_return(void);
43+
44+
asm (".global __static_call_return\n\t"
45+
".type __static_call_return, @function\n\t"
46+
ASM_FUNC_ALIGN "\n\t"
47+
"__static_call_return:\n\t"
48+
ANNOTATE_NOENDBR
49+
ANNOTATE_RETPOLINE_SAFE
50+
"ret; int3\n\t"
51+
".size __static_call_return, . - __static_call_return \n\t");
52+
2853
static void __ref __static_call_transform(void *insn, enum insn_type type,
2954
void *func, bool modinit)
3055
{
3156
const void *emulate = NULL;
3257
int size = CALL_INSN_SIZE;
3358
const void *code;
59+
u8 op, buf[6];
60+
61+
if ((type == JMP || type == RET) && (op = __is_Jcc(insn)))
62+
type = JCC;
3463

3564
switch (type) {
3665
case CALL:
@@ -57,6 +86,20 @@ static void __ref __static_call_transform(void *insn, enum insn_type type,
5786
else
5887
code = &retinsn;
5988
break;
89+
90+
case JCC:
91+
if (!func) {
92+
func = __static_call_return;
93+
if (cpu_feature_enabled(X86_FEATURE_RETHUNK))
94+
func = x86_return_thunk;
95+
}
96+
97+
buf[0] = 0x0f;
98+
__text_gen_insn(buf+1, op, insn+1, func, 5);
99+
code = buf;
100+
size = 6;
101+
102+
break;
60103
}
61104

62105
if (memcmp(insn, code, size) == 0)
@@ -68,9 +111,9 @@ static void __ref __static_call_transform(void *insn, enum insn_type type,
68111
text_poke_bp(insn, code, size, emulate);
69112
}
70113

71-
static void __static_call_validate(void *insn, bool tail, bool tramp)
114+
static void __static_call_validate(u8 *insn, bool tail, bool tramp)
72115
{
73-
u8 opcode = *(u8 *)insn;
116+
u8 opcode = insn[0];
74117

75118
if (tramp && memcmp(insn+5, tramp_ud, 3)) {
76119
pr_err("trampoline signature fail");
@@ -79,7 +122,8 @@ static void __static_call_validate(void *insn, bool tail, bool tramp)
79122

80123
if (tail) {
81124
if (opcode == JMP32_INSN_OPCODE ||
82-
opcode == RET_INSN_OPCODE)
125+
opcode == RET_INSN_OPCODE ||
126+
__is_Jcc(insn))
83127
return;
84128
} else {
85129
if (opcode == CALL_INSN_OPCODE ||

0 commit comments

Comments
 (0)