[PATCH v2 9/9] objtool: Abstract unwind hint reading

From: Julien Thierry
Date: Thu Jul 30 2020 - 05:47:31 EST


The type of unwind hints and the semantics associated with them depend
on the architecture. Let arch specific code convert unwind hints into
objtool stack state descriptions.

Signed-off-by: Julien Thierry <jthierry@xxxxxxxxxx>
---
tools/objtool/arch.h | 5 +--
tools/objtool/arch/x86/decode.c | 54 ++++++++++++++++++++++++++++++
tools/objtool/cfi.h | 3 +-
tools/objtool/check.c | 58 +++++----------------------------
tools/objtool/orc_gen.c | 4 ++-
5 files changed, 71 insertions(+), 53 deletions(-)

diff --git a/tools/objtool/arch.h b/tools/objtool/arch.h
index 2e2ce089b0e9..44107e9aab71 100644
--- a/tools/objtool/arch.h
+++ b/tools/objtool/arch.h
@@ -7,12 +7,11 @@
#define _ARCH_H

#include <stdbool.h>
+#include <linux/frame.h>
#include <linux/list.h>
#include "objtool.h"
#include "cfi.h"

-#include <asm/orc_types.h>
-
enum insn_type {
INSN_JUMP_CONDITIONAL,
INSN_JUMP_UNCONDITIONAL,
@@ -86,4 +85,6 @@ unsigned long arch_dest_reloc_offset(int addend);

const char *arch_nop_insn(int len);

+int arch_decode_insn_hint(struct instruction *insn, struct unwind_hint *hint);
+
#endif /* _ARCH_H */
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
index 1967370440b3..2099809925af 100644
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -6,6 +6,8 @@
#include <stdio.h>
#include <stdlib.h>

+#include <linux/frame.h>
+
#define unlikely(cond) (cond)
#include <asm/insn.h>
#include "../../../arch/x86/lib/inat.c"
@@ -15,6 +17,7 @@
#include "../../elf.h"
#include "../../arch.h"
#include "../../warn.h"
+#include <asm/orc_types.h>

static unsigned char op_to_cfi_reg[][2] = {
{CFI_AX, CFI_R8},
@@ -583,3 +586,54 @@ const char *arch_nop_insn(int len)

return nops[len-1];
}
+
+int arch_decode_insn_hint(struct instruction *insn, struct unwind_hint *hint)
+{
+ struct cfi_reg *cfa = &insn->cfi.cfa;
+
+ if (hint->type == UNWIND_HINT_TYPE_RET_OFFSET) {
+ insn->ret_offset = hint->sp_offset;
+ return 0;
+ }
+
+ insn->hint = true;
+
+ switch (hint->sp_reg) {
+ case ORC_REG_UNDEFINED:
+ cfa->base = CFI_UNDEFINED;
+ break;
+ case ORC_REG_SP:
+ cfa->base = CFI_SP;
+ break;
+ case ORC_REG_BP:
+ cfa->base = CFI_BP;
+ break;
+ case ORC_REG_SP_INDIRECT:
+ cfa->base = CFI_SP_INDIRECT;
+ break;
+ case ORC_REG_R10:
+ cfa->base = CFI_R10;
+ break;
+ case ORC_REG_R13:
+ cfa->base = CFI_R13;
+ break;
+ case ORC_REG_DI:
+ cfa->base = CFI_DI;
+ break;
+ case ORC_REG_DX:
+ cfa->base = CFI_DX;
+ break;
+ default:
+ WARN_FUNC("unsupported unwind_hint sp base reg %d",
+ insn->sec, insn->offset, hint->sp_reg);
+ return -1;
+ }
+
+ cfa->offset = hint->sp_offset;
+ insn->cfi.hint_type = hint->type;
+ insn->cfi.end = hint->end;
+
+ insn->cfi.sp_only = hint->type == ORC_TYPE_REGS || hint->type == ORC_TYPE_REGS_IRET;
+
+ return 0;
+}
diff --git a/tools/objtool/cfi.h b/tools/objtool/cfi.h
index c7c59c6a44ee..f5aeca023133 100644
--- a/tools/objtool/cfi.h
+++ b/tools/objtool/cfi.h
@@ -29,10 +29,11 @@ struct cfi_state {
struct cfi_reg cfa;
int stack_size;
int drap_reg, drap_offset;
- unsigned char type;
+ unsigned char hint_type;
bool bp_scratch;
bool drap;
bool end;
+ bool sp_only;
};

#endif /* _OBJTOOL_CFI_H */
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index d6731e88259d..d856580369dd 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1217,7 +1217,6 @@ static int read_unwind_hints(struct objtool_file *file)
struct reloc *reloc;
struct unwind_hint *hint;
struct instruction *insn;
- struct cfi_reg *cfa;
int i;

sec = find_section_by_name(file->elf, ".discard.unwind_hints");
@@ -1252,49 +1251,10 @@ static int read_unwind_hints(struct objtool_file *file)
return -1;
}

- cfa = &insn->cfi.cfa;
-
- if (hint->type == UNWIND_HINT_TYPE_RET_OFFSET) {
- insn->ret_offset = hint->sp_offset;
- continue;
- }
-
- insn->hint = true;
-
- switch (hint->sp_reg) {
- case ORC_REG_UNDEFINED:
- cfa->base = CFI_UNDEFINED;
- break;
- case ORC_REG_SP:
- cfa->base = CFI_SP;
- break;
- case ORC_REG_BP:
- cfa->base = CFI_BP;
- break;
- case ORC_REG_SP_INDIRECT:
- cfa->base = CFI_SP_INDIRECT;
- break;
- case ORC_REG_R10:
- cfa->base = CFI_R10;
- break;
- case ORC_REG_R13:
- cfa->base = CFI_R13;
- break;
- case ORC_REG_DI:
- cfa->base = CFI_DI;
- break;
- case ORC_REG_DX:
- cfa->base = CFI_DX;
- break;
- default:
- WARN_FUNC("unsupported unwind_hint sp base reg %d",
- insn->sec, insn->offset, hint->sp_reg);
+ if (arch_decode_insn_hint(insn, hint)) {
+ WARN_FUNC("Bad unwind hint", insn->sec, insn->offset);
return -1;
}
-
- cfa->offset = hint->sp_offset;
- insn->cfi.type = hint->type;
- insn->cfi.end = hint->end;
}

return 0;
@@ -1571,9 +1531,9 @@ static bool has_valid_stack_frame(struct insn_state *state)
return false;
}

-static int update_cfi_state_regs(struct instruction *insn,
- struct cfi_state *cfi,
- struct stack_op *op)
+static int update_sp_only_cfi_state(struct instruction *insn,
+ struct cfi_state *cfi,
+ struct stack_op *op)
{
struct cfi_reg *cfa = &cfi->cfa;

@@ -1679,8 +1639,8 @@ static int update_cfi_state(struct instruction *insn, struct cfi_state *cfi,
return 0;
}

- if (cfi->type == ORC_TYPE_REGS || cfi->type == ORC_TYPE_REGS_IRET)
- return update_cfi_state_regs(insn, cfi, op);
+ if (cfi->sp_only)
+ return update_sp_only_cfi_state(insn, cfi, op);

switch (op->dest.type) {

@@ -2084,10 +2044,10 @@ static bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2)
break;
}

- } else if (cfi1->type != cfi2->type) {
+ } else if (cfi1->hint_type != cfi2->hint_type) {

WARN_FUNC("stack state mismatch: type1=%d type2=%d",
- insn->sec, insn->offset, cfi1->type, cfi2->type);
+ insn->sec, insn->offset, cfi1->hint_type, cfi2->hint_type);

} else if (cfi1->drap != cfi2->drap ||
(cfi1->drap && cfi1->drap_reg != cfi2->drap_reg) ||
diff --git a/tools/objtool/orc_gen.c b/tools/objtool/orc_gen.c
index 00f1efd05653..a36aee98bed0 100644
--- a/tools/objtool/orc_gen.c
+++ b/tools/objtool/orc_gen.c
@@ -9,6 +9,8 @@
#include "check.h"
#include "warn.h"

+#include <asm/orc_types.h>
+
struct orc_data {
struct list_head list;
struct instruction *insn;
@@ -92,7 +94,7 @@ int create_orc(struct objtool_file *file)

orc->sp_offset = cfa->offset;
orc->bp_offset = bp->offset;
- orc->type = insn->cfi.type;
+ orc->type = insn->cfi.hint_type;
}

return 0;
--
2.21.3