Commit b735bd3e authored by Josh Poimboeuf's avatar Josh Poimboeuf

objtool: Combine UNWIND_HINT_RET_OFFSET and UNWIND_HINT_FUNC

The ORC metadata generated for UNWIND_HINT_FUNC isn't actually very
func-like.  With certain usages it can cause stack state mismatches
because it doesn't set the return address (CFI_RA).

Also, users of UNWIND_HINT_RET_OFFSET no longer need to set a custom
return stack offset.  Instead they just need to specify a func-like
situation, so the current ret_offset code is hacky for no good reason.

Solve both problems by simplifying the RET_OFFSET handling and
converting it into a more useful UNWIND_HINT_FUNC.

If we end up needing the old 'ret_offset' functionality again in the
future, we should be able to support it pretty easily with the addition
of a custom 'sp_offset' in UNWIND_HINT_FUNC.
Signed-off-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
Link: https://lore.kernel.org/r/db9d1f5d79dddfbb3725ef6d8ec3477ad199948d.1611263462.git.jpoimboe@redhat.com
parent 081df943
...@@ -48,17 +48,8 @@ ...@@ -48,17 +48,8 @@
UNWIND_HINT_REGS base=\base offset=\offset partial=1 UNWIND_HINT_REGS base=\base offset=\offset partial=1
.endm .endm
.macro UNWIND_HINT_FUNC sp_offset=8 .macro UNWIND_HINT_FUNC
UNWIND_HINT sp_reg=ORC_REG_SP sp_offset=\sp_offset type=UNWIND_HINT_TYPE_CALL UNWIND_HINT sp_reg=ORC_REG_SP sp_offset=8 type=UNWIND_HINT_TYPE_FUNC
.endm
/*
* RET_OFFSET: Used on instructions that terminate a function; mostly RETURN
* and sibling calls. On these, sp_offset denotes the expected offset from
* initial_func_cfi.
*/
.macro UNWIND_HINT_RET_OFFSET sp_offset=8
UNWIND_HINT sp_reg=ORC_REG_SP type=UNWIND_HINT_TYPE_RET_OFFSET sp_offset=\sp_offset
.endm .endm
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
......
...@@ -277,7 +277,7 @@ SYM_INNER_LABEL(ftrace_regs_caller_end, SYM_L_GLOBAL) ...@@ -277,7 +277,7 @@ SYM_INNER_LABEL(ftrace_regs_caller_end, SYM_L_GLOBAL)
restore_mcount_regs 8 restore_mcount_regs 8
/* Restore flags */ /* Restore flags */
popfq popfq
UNWIND_HINT_RET_OFFSET UNWIND_HINT_FUNC
jmp ftrace_epilogue jmp ftrace_epilogue
SYM_FUNC_END(ftrace_regs_caller) SYM_FUNC_END(ftrace_regs_caller)
......
...@@ -28,7 +28,7 @@ SYM_FUNC_START_NOALIGN(__x86_retpoline_\reg) ...@@ -28,7 +28,7 @@ SYM_FUNC_START_NOALIGN(__x86_retpoline_\reg)
jmp .Lspec_trap_\@ jmp .Lspec_trap_\@
.Ldo_rop_\@: .Ldo_rop_\@:
mov %\reg, (%_ASM_SP) mov %\reg, (%_ASM_SP)
UNWIND_HINT_RET_OFFSET UNWIND_HINT_FUNC
ret ret
SYM_FUNC_END(__x86_retpoline_\reg) SYM_FUNC_END(__x86_retpoline_\reg)
......
...@@ -29,11 +29,14 @@ struct unwind_hint { ...@@ -29,11 +29,14 @@ struct unwind_hint {
* *
* UNWIND_HINT_TYPE_REGS_PARTIAL: Used in entry code to indicate that * UNWIND_HINT_TYPE_REGS_PARTIAL: Used in entry code to indicate that
* sp_reg+sp_offset points to the iret return frame. * sp_reg+sp_offset points to the iret return frame.
*
* UNWIND_HINT_FUNC: Generate the unwind metadata of a callable function.
* Useful for code which doesn't have an ELF function annotation.
*/ */
#define UNWIND_HINT_TYPE_CALL 0 #define UNWIND_HINT_TYPE_CALL 0
#define UNWIND_HINT_TYPE_REGS 1 #define UNWIND_HINT_TYPE_REGS 1
#define UNWIND_HINT_TYPE_REGS_PARTIAL 2 #define UNWIND_HINT_TYPE_REGS_PARTIAL 2
#define UNWIND_HINT_TYPE_RET_OFFSET 3 #define UNWIND_HINT_TYPE_FUNC 3
#ifdef CONFIG_STACK_VALIDATION #ifdef CONFIG_STACK_VALIDATION
......
...@@ -29,11 +29,14 @@ struct unwind_hint { ...@@ -29,11 +29,14 @@ struct unwind_hint {
* *
* UNWIND_HINT_TYPE_REGS_PARTIAL: Used in entry code to indicate that * UNWIND_HINT_TYPE_REGS_PARTIAL: Used in entry code to indicate that
* sp_reg+sp_offset points to the iret return frame. * sp_reg+sp_offset points to the iret return frame.
*
* UNWIND_HINT_FUNC: Generate the unwind metadata of a callable function.
* Useful for code which doesn't have an ELF function annotation.
*/ */
#define UNWIND_HINT_TYPE_CALL 0 #define UNWIND_HINT_TYPE_CALL 0
#define UNWIND_HINT_TYPE_REGS 1 #define UNWIND_HINT_TYPE_REGS 1
#define UNWIND_HINT_TYPE_REGS_PARTIAL 2 #define UNWIND_HINT_TYPE_REGS_PARTIAL 2
#define UNWIND_HINT_TYPE_RET_OFFSET 3 #define UNWIND_HINT_TYPE_FUNC 3
#ifdef CONFIG_STACK_VALIDATION #ifdef CONFIG_STACK_VALIDATION
......
...@@ -563,8 +563,8 @@ void arch_initial_func_cfi_state(struct cfi_init_state *state) ...@@ -563,8 +563,8 @@ void arch_initial_func_cfi_state(struct cfi_init_state *state)
state->cfa.offset = 8; state->cfa.offset = 8;
/* initial RA (return address) */ /* initial RA (return address) */
state->regs[16].base = CFI_CFA; state->regs[CFI_RA].base = CFI_CFA;
state->regs[16].offset = -8; state->regs[CFI_RA].offset = -8;
} }
const char *arch_nop_insn(int len) const char *arch_nop_insn(int len)
......
...@@ -1404,13 +1404,20 @@ static int add_jump_table_alts(struct objtool_file *file) ...@@ -1404,13 +1404,20 @@ static int add_jump_table_alts(struct objtool_file *file)
return 0; return 0;
} }
static void set_func_state(struct cfi_state *state)
{
state->cfa = initial_func_cfi.cfa;
memcpy(&state->regs, &initial_func_cfi.regs,
CFI_NUM_REGS * sizeof(struct cfi_reg));
state->stack_size = initial_func_cfi.cfa.offset;
}
static int read_unwind_hints(struct objtool_file *file) static int read_unwind_hints(struct objtool_file *file)
{ {
struct section *sec, *relocsec; struct section *sec, *relocsec;
struct reloc *reloc; struct reloc *reloc;
struct unwind_hint *hint; struct unwind_hint *hint;
struct instruction *insn; struct instruction *insn;
struct cfi_reg *cfa;
int i; int i;
sec = find_section_by_name(file->elf, ".discard.unwind_hints"); sec = find_section_by_name(file->elf, ".discard.unwind_hints");
...@@ -1445,22 +1452,20 @@ static int read_unwind_hints(struct objtool_file *file) ...@@ -1445,22 +1452,20 @@ static int read_unwind_hints(struct objtool_file *file)
return -1; return -1;
} }
cfa = &insn->cfi.cfa; insn->hint = true;
if (hint->type == UNWIND_HINT_TYPE_RET_OFFSET) { if (hint->type == UNWIND_HINT_TYPE_FUNC) {
insn->ret_offset = bswap_if_needed(hint->sp_offset); set_func_state(&insn->cfi);
continue; continue;
} }
insn->hint = true;
if (arch_decode_hint_reg(insn, hint->sp_reg)) { if (arch_decode_hint_reg(insn, hint->sp_reg)) {
WARN_FUNC("unsupported unwind_hint sp base reg %d", WARN_FUNC("unsupported unwind_hint sp base reg %d",
insn->sec, insn->offset, hint->sp_reg); insn->sec, insn->offset, hint->sp_reg);
return -1; return -1;
} }
cfa->offset = bswap_if_needed(hint->sp_offset); insn->cfi.cfa.offset = bswap_if_needed(hint->sp_offset);
insn->cfi.type = hint->type; insn->cfi.type = hint->type;
insn->cfi.end = hint->end; insn->cfi.end = hint->end;
} }
...@@ -1716,27 +1721,18 @@ static bool is_fentry_call(struct instruction *insn) ...@@ -1716,27 +1721,18 @@ static bool is_fentry_call(struct instruction *insn)
static bool has_modified_stack_frame(struct instruction *insn, struct insn_state *state) static bool has_modified_stack_frame(struct instruction *insn, struct insn_state *state)
{ {
u8 ret_offset = insn->ret_offset;
struct cfi_state *cfi = &state->cfi; struct cfi_state *cfi = &state->cfi;
int i; int i;
if (cfi->cfa.base != initial_func_cfi.cfa.base || cfi->drap) if (cfi->cfa.base != initial_func_cfi.cfa.base || cfi->drap)
return true; return true;
if (cfi->cfa.offset != initial_func_cfi.cfa.offset + ret_offset) if (cfi->cfa.offset != initial_func_cfi.cfa.offset)
return true; return true;
if (cfi->stack_size != initial_func_cfi.cfa.offset + ret_offset) if (cfi->stack_size != initial_func_cfi.cfa.offset)
return true; return true;
/*
* If there is a ret offset hint then don't check registers
* because a callee-saved register might have been pushed on
* the stack.
*/
if (ret_offset)
return false;
for (i = 0; i < CFI_NUM_REGS; i++) { for (i = 0; i < CFI_NUM_REGS; i++) {
if (cfi->regs[i].base != initial_func_cfi.regs[i].base || if (cfi->regs[i].base != initial_func_cfi.regs[i].base ||
cfi->regs[i].offset != initial_func_cfi.regs[i].offset) cfi->regs[i].offset != initial_func_cfi.regs[i].offset)
...@@ -2880,10 +2876,7 @@ static int validate_section(struct objtool_file *file, struct section *sec) ...@@ -2880,10 +2876,7 @@ static int validate_section(struct objtool_file *file, struct section *sec)
continue; continue;
init_insn_state(&state, sec); init_insn_state(&state, sec);
state.cfi.cfa = initial_func_cfi.cfa; set_func_state(&state.cfi);
memcpy(&state.cfi.regs, &initial_func_cfi.regs,
CFI_NUM_REGS * sizeof(struct cfi_reg));
state.cfi.stack_size = initial_func_cfi.cfa.offset;
warnings += validate_symbol(file, sec, func, &state); warnings += validate_symbol(file, sec, func, &state);
} }
......
...@@ -50,7 +50,6 @@ struct instruction { ...@@ -50,7 +50,6 @@ struct instruction {
bool retpoline_safe; bool retpoline_safe;
s8 instr; s8 instr;
u8 visited; u8 visited;
u8 ret_offset;
struct alt_group *alt_group; struct alt_group *alt_group;
struct symbol *call_dest; struct symbol *call_dest;
struct instruction *jump_dest; struct instruction *jump_dest;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment