[RFC PATCH v1 22/23] objtool: Make update_cfi_state() arch-specific function

From: Youling Tang
Date: Tue Jun 20 2023 - 04:18:59 EST


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