objtool: Split INSN_CONTEXT_SWITCH into INSN_SYSCALL and INSN_SYSRET

INSN_CONTEXT_SWITCH is ambiguous.  It can represent both call semantics
(SYSCALL, SYSENTER) and return semantics (SYSRET, IRET, RETS, RETU).
Those differ significantly: calls preserve control flow whereas returns
terminate it.

Objtool uses an arbitrary rule for INSN_CONTEXT_SWITCH that almost works
by accident: if in a function, keep going; otherwise stop.  It should
instead be based on the semantics of the underlying instruction.

In preparation for improving that, split INSN_CONTEXT_SWITCH into
INSN_SYCALL and INSN_SYSRET.

No functional change.

Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Link: https://lore.kernel.org/r/19a76c74d2c051d3bc9a775823cafc65ad267a7a.1744095216.git.jpoimboe@kernel.org
This commit is contained in:
Josh Poimboeuf 2025-04-08 00:02:14 -07:00 committed by Ingo Molnar
parent a8df7d0ef9
commit fe1042b1ef
3 changed files with 17 additions and 10 deletions

View file

@ -522,7 +522,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
case INAT_PFX_REPNE:
if (modrm == 0xca)
/* eretu/erets */
insn->type = INSN_CONTEXT_SWITCH;
insn->type = INSN_SYSRET;
break;
default:
if (modrm == 0xca)
@ -535,11 +535,15 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
insn->type = INSN_JUMP_CONDITIONAL;
} else if (op2 == 0x05 || op2 == 0x07 || op2 == 0x34 ||
op2 == 0x35) {
} else if (op2 == 0x05 || op2 == 0x34) {
/* sysenter, sysret */
insn->type = INSN_CONTEXT_SWITCH;
/* syscall, sysenter */
insn->type = INSN_SYSCALL;
} else if (op2 == 0x07 || op2 == 0x35) {
/* sysret, sysexit */
insn->type = INSN_SYSRET;
} else if (op2 == 0x0b || op2 == 0xb9) {
@ -676,7 +680,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
case 0xca: /* retf */
case 0xcb: /* retf */
insn->type = INSN_CONTEXT_SWITCH;
insn->type = INSN_SYSRET;
break;
case 0xe0: /* loopne */
@ -721,7 +725,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
} else if (modrm_reg == 5) {
/* jmpf */
insn->type = INSN_CONTEXT_SWITCH;
insn->type = INSN_SYSRET;
} else if (modrm_reg == 6) {

View file

@ -3684,7 +3684,8 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
break;
case INSN_CONTEXT_SWITCH:
case INSN_SYSCALL:
case INSN_SYSRET:
if (func) {
if (!next_insn || !next_insn->hint) {
WARN_INSN(insn, "unsupported instruction in callable function");
@ -3886,7 +3887,8 @@ static int validate_unret(struct objtool_file *file, struct instruction *insn)
WARN_INSN(insn, "RET before UNTRAIN");
return 1;
case INSN_CONTEXT_SWITCH:
case INSN_SYSCALL:
case INSN_SYSRET:
if (insn_func(insn))
break;
return 0;

View file

@ -19,7 +19,8 @@ enum insn_type {
INSN_CALL,
INSN_CALL_DYNAMIC,
INSN_RETURN,
INSN_CONTEXT_SWITCH,
INSN_SYSCALL,
INSN_SYSRET,
INSN_BUG,
INSN_NOP,
INSN_STAC,