Currently update_cfi_state() is implemented for the x86_64 architecture. This function is specific to the architecture. In order to avoid powerpc build errors, define x86_64 as the default implementation and rename update_cfi_state() to arch_update_cfi_state(). LoongArch will reimplement arch_update_cfi_state(). Co-developed-by: Jinyang He <hejinyang@xxxxxxxxxxx> Signed-off-by: Jinyang He <hejinyang@xxxxxxxxxxx> Signed-off-by: Youling Tang <tangyouling@xxxxxxxxxxx> --- tools/objtool/arch/loongarch/decode.c | 76 +++++++++++++++++++++++++++ tools/objtool/check.c | 10 ++-- tools/objtool/include/objtool/arch.h | 3 ++ tools/objtool/include/objtool/check.h | 5 ++ 4 files changed, 89 insertions(+), 5 deletions(-) diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c index 90adbbeab8d4..6ca8cbad6813 100644 --- a/tools/objtool/arch/loongarch/decode.c +++ b/tools/objtool/arch/loongarch/decode.c @@ -4,6 +4,8 @@ #include <stdlib.h> #include <linux/bitops.h> +#include <linux/objtool.h> + #include <asm/inst.h> #include <asm/orc_types.h> @@ -274,3 +276,77 @@ void arch_initial_func_cfi_state(struct cfi_init_state *state) state->cfa.base = CFI_SP; state->cfa.offset = 0; } + + +int arch_update_cfi_state(struct instruction *insn, + struct instruction *next_insn, + struct cfi_state *cfi, struct stack_op *op) +{ + struct cfi_reg *cfa = &cfi->cfa; + struct cfi_reg *regs = cfi->regs; + + /* stack operations don't make sense with an undefined CFA */ + if (cfa->base == CFI_UNDEFINED) { + if (insn_func(insn)) { + WARN_FUNC("undefined stack state", insn->sec, insn->offset); + return -1; + } + return 0; + } + + if (cfi->type == UNWIND_HINT_TYPE_REGS) + return update_cfi_state_regs(insn, cfi, op); + + + switch (op->dest.type) { + case OP_DEST_REG: + switch (op->src.type) { + case OP_SRC_ADD: + if (op->dest.reg == CFI_SP && op->src.reg == CFI_SP) { + /* addi.d sp, sp, imm */ + cfi->stack_size -= op->src.offset; + if (cfa->base == CFI_SP) + cfa->offset -= op->src.offset; + } else if (op->dest.reg == CFI_FP && op->src.reg == CFI_SP) { + /* addi.d fp, sp, imm */ + if (cfa->base == CFI_SP && cfa->offset == op->src.offset) { + cfa->base = CFI_FP; + cfa->offset = 0; + } + } else if (op->dest.reg == CFI_SP && op->src.reg == CFI_FP) { + /* addi.d sp, fp, imm */ + if (cfa->base == CFI_FP && cfa->offset == 0) { + cfa->base = CFI_SP; + cfa->offset = -op->src.offset; + } + } + break; + case OP_SRC_REG_INDIRECT: + /* ld.d _reg, sp, imm */ + if (op->src.reg == CFI_SP && + op->src.offset == (regs[op->dest.reg].offset + cfi->stack_size)) { + restore_reg(cfi, op->dest.reg); + /* Gcc may not restore sp, we adjust it directly. */ + if (cfa->base == CFI_FP && cfa->offset == 0) { + cfa->base = CFI_SP; + cfa->offset = cfi->stack_size; + } + } + break; + default: + break; + } + break; + case OP_DEST_REG_INDIRECT: + if (op->src.type == OP_SRC_REG) + /* st.d _reg, sp, imm */ + if (op->dest.offset) + save_reg(cfi, op->src.reg, CFI_CFA, op->dest.offset - cfi->stack_size); + break; + default: + WARN_FUNC("unknown stack-related instruction", insn->sec, insn->offset); + return -1; + } + + return 0; +} diff --git a/tools/objtool/check.c b/tools/objtool/check.c index c637e54088f6..42f87a33f558 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -2230,7 +2230,7 @@ static bool has_valid_stack_frame(struct insn_state *state) return false; } -static int update_cfi_state_regs(struct instruction *insn, +int update_cfi_state_regs(struct instruction *insn, struct cfi_state *cfi, struct stack_op *op) { @@ -2255,7 +2255,7 @@ static int update_cfi_state_regs(struct instruction *insn, return 0; } -static void save_reg(struct cfi_state *cfi, unsigned char reg, int base, int offset) +void save_reg(struct cfi_state *cfi, unsigned char reg, int base, int offset) { if (arch_callee_saved_reg(reg) && cfi->regs[reg].base == CFI_UNDEFINED) { @@ -2264,7 +2264,7 @@ static void save_reg(struct cfi_state *cfi, unsigned char reg, int base, int off } } -static void restore_reg(struct cfi_state *cfi, unsigned char reg) +void restore_reg(struct cfi_state *cfi, unsigned char reg) { cfi->regs[reg].base = initial_func_cfi.regs[reg].base; cfi->regs[reg].offset = initial_func_cfi.regs[reg].offset; @@ -2323,7 +2323,7 @@ static void restore_reg(struct cfi_state *cfi, unsigned char reg) * 41 5d pop %r13 * c3 retq */ -static int update_cfi_state(struct instruction *insn, +int __weak arch_update_cfi_state(struct instruction *insn, struct instruction *next_insn, struct cfi_state *cfi, struct stack_op *op) { @@ -2795,7 +2795,7 @@ static int handle_insn_ops(struct instruction *insn, for (op = insn->stack_ops; op; op = op->next) { - if (update_cfi_state(insn, next_insn, &state->cfi, op)) + if (arch_update_cfi_state(insn, next_insn, &state->cfi, op)) return 1; if (!insn->alt_group) diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h index 2b6d2ce4f9a5..88570c598752 100644 --- a/tools/objtool/include/objtool/arch.h +++ b/tools/objtool/include/objtool/arch.h @@ -95,4 +95,7 @@ int arch_rewrite_retpolines(struct objtool_file *file); bool arch_pc_relative_reloc(struct reloc *reloc); +int __weak arch_update_cfi_state(struct instruction *insn, + struct instruction *next_insn, + struct cfi_state *cfi, struct stack_op *op); #endif /* _ARCH_H */ diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h index 34898364bf03..dd1b95c67620 100644 --- a/tools/objtool/include/objtool/check.h +++ b/tools/objtool/include/objtool/check.h @@ -33,4 +33,9 @@ struct alt_group { extern unsigned long nr_insns; +int update_cfi_state_regs(struct instruction *insn, + struct cfi_state *cfi, + struct stack_op *op); +void save_reg(struct cfi_state *cfi, unsigned char reg, int base, int offset); +void restore_reg(struct cfi_state *cfi, unsigned char reg); #endif /* _CHECK_H */ -- 2.39.2