objtool: Make unwind hint definitions available to other architectures
authorJulien Thierry <jthierry@redhat.com>
Fri, 4 Sep 2020 15:30:27 +0000 (16:30 +0100)
committerJosh Poimboeuf <jpoimboe@redhat.com>
Thu, 10 Sep 2020 15:43:13 +0000 (10:43 -0500)
Unwind hints are useful to provide objtool with information about stack
states in non-standard functions/code.

While the type of information being provided might be very arch
specific, the mechanism to provide the information can be useful for
other architectures.

Move the relevant unwint hint definitions for all architectures to
see.

[ jpoimboe: REGS_IRET -> REGS_PARTIAL ]

Signed-off-by: Julien Thierry <jthierry@redhat.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
arch/x86/include/asm/orc_types.h
arch/x86/include/asm/unwind_hints.h
arch/x86/kernel/unwind_orc.c
include/linux/objtool.h
tools/arch/x86/include/asm/orc_types.h
tools/include/linux/objtool.h [new file with mode: 0644]
tools/objtool/check.c
tools/objtool/orc_dump.c
tools/objtool/orc_gen.c
tools/objtool/sync-check.sh

index d25534940bde04f38796e8e36a57626b81f02d03..fdbffec4cfdeaba82e7fe6c1c76b169316cbb308 100644 (file)
 #define ORC_REG_SP_INDIRECT            9
 #define ORC_REG_MAX                    15
 
-/*
- * ORC_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP (the
- * caller's SP right before it made the call).  Used for all callable
- * functions, i.e. all C code and all callable asm functions.
- *
- * ORC_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset points
- * to a fully populated pt_regs from a syscall, interrupt, or exception.
- *
- * ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset
- * points to the iret return frame.
- *
- * The UNWIND_HINT macros are used only for the unwind_hint struct.  They
- * aren't used in struct orc_entry due to size and complexity constraints.
- * Objtool converts them to real types when it converts the hints to orc
- * entries.
- */
-#define ORC_TYPE_CALL                  0
-#define ORC_TYPE_REGS                  1
-#define ORC_TYPE_REGS_IRET             2
-#define UNWIND_HINT_TYPE_RET_OFFSET    3
-
 #ifndef __ASSEMBLY__
 /*
  * This struct is more or less a vastly simplified version of the DWARF Call
@@ -78,19 +57,6 @@ struct orc_entry {
        unsigned        end:1;
 } __packed;
 
-/*
- * This struct is used by asm and inline asm code to manually annotate the
- * location of registers on the stack for the ORC unwinder.
- *
- * Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*.
- */
-struct unwind_hint {
-       u32             ip;
-       s16             sp_offset;
-       u8              sp_reg;
-       u8              type;
-       u8              end;
-};
 #endif /* __ASSEMBLY__ */
 
 #endif /* _ORC_TYPES_H */
index 7d903fdb3f4392e1ec8d04db40ba54daa3ddf1db..664d4610d700e35f4b5cd81823333656e6260380 100644 (file)
@@ -1,51 +1,17 @@
 #ifndef _ASM_X86_UNWIND_HINTS_H
 #define _ASM_X86_UNWIND_HINTS_H
 
+#include <linux/objtool.h>
+
 #include "orc_types.h"
 
 #ifdef __ASSEMBLY__
 
-/*
- * In asm, there are two kinds of code: normal C-type callable functions and
- * the rest.  The normal callable functions can be called by other code, and
- * don't do anything unusual with the stack.  Such normal callable functions
- * are annotated with the ENTRY/ENDPROC macros.  Most asm code falls in this
- * category.  In this case, no special debugging annotations are needed because
- * objtool can automatically generate the ORC data for the ORC unwinder to read
- * at runtime.
- *
- * Anything which doesn't fall into the above category, such as syscall and
- * interrupt handlers, tends to not be called directly by other functions, and
- * often does unusual non-C-function-type things with the stack pointer.  Such
- * code needs to be annotated such that objtool can understand it.  The
- * following CFI hint macros are for this type of code.
- *
- * These macros provide hints to objtool about the state of the stack at each
- * instruction.  Objtool starts from the hints and follows the code flow,
- * making automatic CFI adjustments when it sees pushes and pops, filling out
- * the debuginfo as necessary.  It will also warn if it sees any
- * inconsistencies.
- */
-.macro UNWIND_HINT sp_reg=ORC_REG_SP sp_offset=0 type=ORC_TYPE_CALL end=0
-#ifdef CONFIG_STACK_VALIDATION
-.Lunwind_hint_ip_\@:
-       .pushsection .discard.unwind_hints
-               /* struct unwind_hint */
-               .long .Lunwind_hint_ip_\@ - .
-               .short \sp_offset
-               .byte \sp_reg
-               .byte \type
-               .byte \end
-               .balign 4
-       .popsection
-#endif
-.endm
-
 .macro UNWIND_HINT_EMPTY
-       UNWIND_HINT sp_reg=ORC_REG_UNDEFINED end=1
+       UNWIND_HINT sp_reg=ORC_REG_UNDEFINED type=UNWIND_HINT_TYPE_CALL end=1
 .endm
 
-.macro UNWIND_HINT_REGS base=%rsp offset=0 indirect=0 extra=1 iret=0
+.macro UNWIND_HINT_REGS base=%rsp offset=0 indirect=0 extra=1 partial=0
        .if \base == %rsp
                .if \indirect
                        .set sp_reg, ORC_REG_SP_INDIRECT
 
        .set sp_offset, \offset
 
-       .if \iret
-               .set type, ORC_TYPE_REGS_IRET
+       .if \partial
+               .set type, UNWIND_HINT_TYPE_REGS_PARTIAL
        .elseif \extra == 0
-               .set type, ORC_TYPE_REGS_IRET
+               .set type, UNWIND_HINT_TYPE_REGS_PARTIAL
                .set sp_offset, \offset + (16*8)
        .else
-               .set type, ORC_TYPE_REGS
+               .set type, UNWIND_HINT_TYPE_REGS
        .endif
 
        UNWIND_HINT sp_reg=sp_reg sp_offset=sp_offset type=type
 .endm
 
 .macro UNWIND_HINT_IRET_REGS base=%rsp offset=0
-       UNWIND_HINT_REGS base=\base offset=\offset iret=1
+       UNWIND_HINT_REGS base=\base offset=\offset partial=1
 .endm
 
 .macro UNWIND_HINT_FUNC sp_offset=8
-       UNWIND_HINT sp_offset=\sp_offset
+       UNWIND_HINT sp_reg=ORC_REG_SP sp_offset=\sp_offset type=UNWIND_HINT_TYPE_CALL
 .endm
 
 /*
@@ -92,7 +58,7 @@
  * initial_func_cfi.
  */
 .macro UNWIND_HINT_RET_OFFSET sp_offset=8
-       UNWIND_HINT type=UNWIND_HINT_TYPE_RET_OFFSET sp_offset=\sp_offset
+       UNWIND_HINT sp_reg=ORC_REG_SP type=UNWIND_HINT_TYPE_RET_OFFSET sp_offset=\sp_offset
 .endm
 
 #endif /* __ASSEMBLY__ */
index ec88bbe08a3281f0eea48d79df4f2f7d99aa4e9b..6a339ce328e0a7eb2546ae1f5f8517b293100226 100644 (file)
@@ -1,4 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0-only
+#include <linux/objtool.h>
 #include <linux/module.h>
 #include <linux/sort.h>
 #include <asm/ptrace.h>
@@ -127,12 +128,12 @@ static struct orc_entry null_orc_entry = {
        .sp_offset = sizeof(long),
        .sp_reg = ORC_REG_SP,
        .bp_reg = ORC_REG_UNDEFINED,
-       .type = ORC_TYPE_CALL
+       .type = UNWIND_HINT_TYPE_CALL
 };
 
 /* Fake frame pointer entry -- used as a fallback for generated code */
 static struct orc_entry orc_fp_entry = {
-       .type           = ORC_TYPE_CALL,
+       .type           = UNWIND_HINT_TYPE_CALL,
        .sp_reg         = ORC_REG_BP,
        .sp_offset      = 16,
        .bp_reg         = ORC_REG_PREV_SP,
@@ -531,7 +532,7 @@ bool unwind_next_frame(struct unwind_state *state)
 
        /* Find IP, SP and possibly regs: */
        switch (orc->type) {
-       case ORC_TYPE_CALL:
+       case UNWIND_HINT_TYPE_CALL:
                ip_p = sp - sizeof(long);
 
                if (!deref_stack_reg(state, ip_p, &state->ip))
@@ -546,7 +547,7 @@ bool unwind_next_frame(struct unwind_state *state)
                state->signal = false;
                break;
 
-       case ORC_TYPE_REGS:
+       case UNWIND_HINT_TYPE_REGS:
                if (!deref_stack_regs(state, sp, &state->ip, &state->sp)) {
                        orc_warn_current("can't access registers at %pB\n",
                                         (void *)orig_ip);
@@ -559,7 +560,7 @@ bool unwind_next_frame(struct unwind_state *state)
                state->signal = true;
                break;
 
-       case ORC_TYPE_REGS_IRET:
+       case UNWIND_HINT_TYPE_REGS_PARTIAL:
                if (!deref_stack_iret_regs(state, sp, &state->ip, &state->sp)) {
                        orc_warn_current("can't access iret registers at %pB\n",
                                         (void *)orig_ip);
index 15e9997a9fb41e9d436776e1cdfcd4a154c2433d..ab82c793c897a1553eecb424ba7535373b7df710 100644 (file)
@@ -2,9 +2,55 @@
 #ifndef _LINUX_OBJTOOL_H
 #define _LINUX_OBJTOOL_H
 
+#ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+
+/*
+ * This struct is used by asm and inline asm code to manually annotate the
+ * location of registers on the stack.
+ */
+struct unwind_hint {
+       u32             ip;
+       s16             sp_offset;
+       u8              sp_reg;
+       u8              type;
+       u8              end;
+};
+#endif
+
+/*
+ * UNWIND_HINT_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP
+ * (the caller's SP right before it made the call).  Used for all callable
+ * functions, i.e. all C code and all callable asm functions.
+ *
+ * UNWIND_HINT_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset
+ * points to a fully populated pt_regs from a syscall, interrupt, or exception.
+ *
+ * UNWIND_HINT_TYPE_REGS_PARTIAL: Used in entry code to indicate that
+ * sp_reg+sp_offset points to the iret return frame.
+ */
+#define UNWIND_HINT_TYPE_CALL          0
+#define UNWIND_HINT_TYPE_REGS          1
+#define UNWIND_HINT_TYPE_REGS_PARTIAL  2
+#define UNWIND_HINT_TYPE_RET_OFFSET    3
+
 #ifdef CONFIG_STACK_VALIDATION
 
 #ifndef __ASSEMBLY__
+
+#define UNWIND_HINT(sp_reg, sp_offset, type, end)              \
+       "987: \n\t"                                             \
+       ".pushsection .discard.unwind_hints\n\t"                \
+       /* struct unwind_hint */                                \
+       ".long 987b - .\n\t"                                    \
+       ".short " __stringify(sp_offset) "\n\t"                 \
+       ".byte " __stringify(sp_reg) "\n\t"                     \
+       ".byte " __stringify(type) "\n\t"                       \
+       ".byte " __stringify(end) "\n\t"                        \
+       ".balign 4 \n\t"                                        \
+       ".popsection\n\t"
+
 /*
  * This macro marks the given function's stack frame as "non-standard", which
  * tells objtool to ignore the function when doing stack metadata validation.
        .long 999b;                                             \
        .popsection;
 
+/*
+ * In asm, there are two kinds of code: normal C-type callable functions and
+ * the rest.  The normal callable functions can be called by other code, and
+ * don't do anything unusual with the stack.  Such normal callable functions
+ * are annotated with the ENTRY/ENDPROC macros.  Most asm code falls in this
+ * category.  In this case, no special debugging annotations are needed because
+ * objtool can automatically generate the ORC data for the ORC unwinder to read
+ * at runtime.
+ *
+ * Anything which doesn't fall into the above category, such as syscall and
+ * interrupt handlers, tends to not be called directly by other functions, and
+ * often does unusual non-C-function-type things with the stack pointer.  Such
+ * code needs to be annotated such that objtool can understand it.  The
+ * following CFI hint macros are for this type of code.
+ *
+ * These macros provide hints to objtool about the state of the stack at each
+ * instruction.  Objtool starts from the hints and follows the code flow,
+ * making automatic CFI adjustments when it sees pushes and pops, filling out
+ * the debuginfo as necessary.  It will also warn if it sees any
+ * inconsistencies.
+ */
+.macro UNWIND_HINT sp_reg:req sp_offset=0 type:req end=0
+.Lunwind_hint_ip_\@:
+       .pushsection .discard.unwind_hints
+               /* struct unwind_hint */
+               .long .Lunwind_hint_ip_\@ - .
+               .short \sp_offset
+               .byte \sp_reg
+               .byte \type
+               .byte \end
+               .balign 4
+       .popsection
+.endm
+
 #endif /* __ASSEMBLY__ */
 
 #else /* !CONFIG_STACK_VALIDATION */
 
+#ifndef __ASSEMBLY__
+
+#define UNWIND_HINT(sp_reg, sp_offset, type, end)      \
+       "\n\t"
 #define STACK_FRAME_NON_STANDARD(func)
+#else
 #define ANNOTATE_INTRA_FUNCTION_CALL
+.macro UNWIND_HINT sp_reg:req sp_offset=0 type:req end=0
+.endm
+#endif
 
 #endif /* CONFIG_STACK_VALIDATION */
 
index d25534940bde04f38796e8e36a57626b81f02d03..fdbffec4cfdeaba82e7fe6c1c76b169316cbb308 100644 (file)
 #define ORC_REG_SP_INDIRECT            9
 #define ORC_REG_MAX                    15
 
-/*
- * ORC_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP (the
- * caller's SP right before it made the call).  Used for all callable
- * functions, i.e. all C code and all callable asm functions.
- *
- * ORC_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset points
- * to a fully populated pt_regs from a syscall, interrupt, or exception.
- *
- * ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset
- * points to the iret return frame.
- *
- * The UNWIND_HINT macros are used only for the unwind_hint struct.  They
- * aren't used in struct orc_entry due to size and complexity constraints.
- * Objtool converts them to real types when it converts the hints to orc
- * entries.
- */
-#define ORC_TYPE_CALL                  0
-#define ORC_TYPE_REGS                  1
-#define ORC_TYPE_REGS_IRET             2
-#define UNWIND_HINT_TYPE_RET_OFFSET    3
-
 #ifndef __ASSEMBLY__
 /*
  * This struct is more or less a vastly simplified version of the DWARF Call
@@ -78,19 +57,6 @@ struct orc_entry {
        unsigned        end:1;
 } __packed;
 
-/*
- * This struct is used by asm and inline asm code to manually annotate the
- * location of registers on the stack for the ORC unwinder.
- *
- * Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*.
- */
-struct unwind_hint {
-       u32             ip;
-       s16             sp_offset;
-       u8              sp_reg;
-       u8              type;
-       u8              end;
-};
 #endif /* __ASSEMBLY__ */
 
 #endif /* _ORC_TYPES_H */
diff --git a/tools/include/linux/objtool.h b/tools/include/linux/objtool.h
new file mode 100644 (file)
index 0000000..ab82c79
--- /dev/null
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_OBJTOOL_H
+#define _LINUX_OBJTOOL_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+
+/*
+ * This struct is used by asm and inline asm code to manually annotate the
+ * location of registers on the stack.
+ */
+struct unwind_hint {
+       u32             ip;
+       s16             sp_offset;
+       u8              sp_reg;
+       u8              type;
+       u8              end;
+};
+#endif
+
+/*
+ * UNWIND_HINT_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP
+ * (the caller's SP right before it made the call).  Used for all callable
+ * functions, i.e. all C code and all callable asm functions.
+ *
+ * UNWIND_HINT_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset
+ * points to a fully populated pt_regs from a syscall, interrupt, or exception.
+ *
+ * UNWIND_HINT_TYPE_REGS_PARTIAL: Used in entry code to indicate that
+ * sp_reg+sp_offset points to the iret return frame.
+ */
+#define UNWIND_HINT_TYPE_CALL          0
+#define UNWIND_HINT_TYPE_REGS          1
+#define UNWIND_HINT_TYPE_REGS_PARTIAL  2
+#define UNWIND_HINT_TYPE_RET_OFFSET    3
+
+#ifdef CONFIG_STACK_VALIDATION
+
+#ifndef __ASSEMBLY__
+
+#define UNWIND_HINT(sp_reg, sp_offset, type, end)              \
+       "987: \n\t"                                             \
+       ".pushsection .discard.unwind_hints\n\t"                \
+       /* struct unwind_hint */                                \
+       ".long 987b - .\n\t"                                    \
+       ".short " __stringify(sp_offset) "\n\t"                 \
+       ".byte " __stringify(sp_reg) "\n\t"                     \
+       ".byte " __stringify(type) "\n\t"                       \
+       ".byte " __stringify(end) "\n\t"                        \
+       ".balign 4 \n\t"                                        \
+       ".popsection\n\t"
+
+/*
+ * This macro marks the given function's stack frame as "non-standard", which
+ * tells objtool to ignore the function when doing stack metadata validation.
+ * It should only be used in special cases where you're 100% sure it won't
+ * affect the reliability of frame pointers and kernel stack traces.
+ *
+ * For more information, see tools/objtool/Documentation/stack-validation.txt.
+ */
+#define STACK_FRAME_NON_STANDARD(func) \
+       static void __used __section(.discard.func_stack_frame_non_standard) \
+               *__func_stack_frame_non_standard_##func = func
+
+#else /* __ASSEMBLY__ */
+
+/*
+ * This macro indicates that the following intra-function call is valid.
+ * Any non-annotated intra-function call will cause objtool to issue a warning.
+ */
+#define ANNOTATE_INTRA_FUNCTION_CALL                           \
+       999:                                                    \
+       .pushsection .discard.intra_function_calls;             \
+       .long 999b;                                             \
+       .popsection;
+
+/*
+ * In asm, there are two kinds of code: normal C-type callable functions and
+ * the rest.  The normal callable functions can be called by other code, and
+ * don't do anything unusual with the stack.  Such normal callable functions
+ * are annotated with the ENTRY/ENDPROC macros.  Most asm code falls in this
+ * category.  In this case, no special debugging annotations are needed because
+ * objtool can automatically generate the ORC data for the ORC unwinder to read
+ * at runtime.
+ *
+ * Anything which doesn't fall into the above category, such as syscall and
+ * interrupt handlers, tends to not be called directly by other functions, and
+ * often does unusual non-C-function-type things with the stack pointer.  Such
+ * code needs to be annotated such that objtool can understand it.  The
+ * following CFI hint macros are for this type of code.
+ *
+ * These macros provide hints to objtool about the state of the stack at each
+ * instruction.  Objtool starts from the hints and follows the code flow,
+ * making automatic CFI adjustments when it sees pushes and pops, filling out
+ * the debuginfo as necessary.  It will also warn if it sees any
+ * inconsistencies.
+ */
+.macro UNWIND_HINT sp_reg:req sp_offset=0 type:req end=0
+.Lunwind_hint_ip_\@:
+       .pushsection .discard.unwind_hints
+               /* struct unwind_hint */
+               .long .Lunwind_hint_ip_\@ - .
+               .short \sp_offset
+               .byte \sp_reg
+               .byte \type
+               .byte \end
+               .balign 4
+       .popsection
+.endm
+
+#endif /* __ASSEMBLY__ */
+
+#else /* !CONFIG_STACK_VALIDATION */
+
+#ifndef __ASSEMBLY__
+
+#define UNWIND_HINT(sp_reg, sp_offset, type, end)      \
+       "\n\t"
+#define STACK_FRAME_NON_STANDARD(func)
+#else
+#define ANNOTATE_INTRA_FUNCTION_CALL
+.macro UNWIND_HINT sp_reg:req sp_offset=0 type:req end=0
+.endm
+#endif
+
+#endif /* CONFIG_STACK_VALIDATION */
+
+#endif /* _LINUX_OBJTOOL_H */
index a94ad88d036c130dc076d5e4712a5fbd3c752a7b..95c6e0d31c0ad73a642d4e527abe5b23d9fb4625 100644 (file)
@@ -14,6 +14,7 @@
 #include "warn.h"
 #include "arch_elf.h"
 
+#include <linux/objtool.h>
 #include <linux/hashtable.h>
 #include <linux/kernel.h>
 #include <linux/static_call_types.h>
@@ -1805,7 +1806,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)
+       if (cfi->type == UNWIND_HINT_TYPE_REGS ||
+           cfi->type == UNWIND_HINT_TYPE_REGS_PARTIAL)
                return update_cfi_state_regs(insn, cfi, op);
 
        switch (op->dest.type) {
index fca46e006fc2efd1f7284a9b79e60b07f0da232c..5e6a95368d351a181d0bdfd35927602b9b5ae076 100644 (file)
@@ -4,6 +4,7 @@
  */
 
 #include <unistd.h>
+#include <linux/objtool.h>
 #include <asm/orc_types.h>
 #include "objtool.h"
 #include "warn.h"
@@ -37,12 +38,12 @@ static const char *reg_name(unsigned int reg)
 static const char *orc_type_name(unsigned int type)
 {
        switch (type) {
-       case ORC_TYPE_CALL:
+       case UNWIND_HINT_TYPE_CALL:
                return "call";
-       case ORC_TYPE_REGS:
+       case UNWIND_HINT_TYPE_REGS:
                return "regs";
-       case ORC_TYPE_REGS_IRET:
-               return "iret";
+       case UNWIND_HINT_TYPE_REGS_PARTIAL:
+               return "regs (partial)";
        default:
                return "?";
        }
index 22fe4398197f2d31966cc9186ce147a94546bfbd..235663b96adc7dbd73eb71ea268879d87072cf6f 100644 (file)
@@ -6,6 +6,9 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <linux/objtool.h>
+#include <asm/orc_types.h>
+
 #include "check.h"
 #include "warn.h"
 
@@ -146,7 +149,7 @@ int create_orc_sections(struct objtool_file *file)
        struct orc_entry empty = {
                .sp_reg = ORC_REG_UNDEFINED,
                .bp_reg  = ORC_REG_UNDEFINED,
-               .type    = ORC_TYPE_CALL,
+               .type    = UNWIND_HINT_TYPE_CALL,
        };
 
        sec = find_section_by_name(file->elf, ".orc_unwind");
index cea1c12607b923b2163feef50f307808c50cb248..606a4b5e929f29311d379b818dc6b1b60929dc1b 100755 (executable)
@@ -6,8 +6,10 @@ if [ -z "$SRCARCH" ]; then
        exit 1
 fi
 
+FILES="include/linux/objtool.h"
+
 if [ "$SRCARCH" = "x86" ]; then
-FILES="
+FILES="$FILES
 arch/x86/include/asm/inat_types.h
 arch/x86/include/asm/orc_types.h
 arch/x86/include/asm/emulate_prefix.h