Skip to content

Commit 39358a0

Browse files
jpoimboeIngo Molnar
authored andcommitted
objtool, x86: Add facility for asm code to provide unwind hints
Some asm (and inline asm) code does special things to the stack which objtool can't understand. (Nor can GCC or GNU assembler, for that matter.) In such cases we need a facility for the code to provide annotations, so the unwinder can unwind through it. This provides such a facility, in the form of unwind hints. They're similar to the GNU assembler .cfi* directives, but they give more information, and are needed in far fewer places, because objtool can fill in the blanks by following branches and adjusting the stack pointer for pushes and pops. Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Jiri Slaby <jslaby@suse.cz> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: live-patching@vger.kernel.org Link: http://lkml.kernel.org/r/0f5f3c9104fca559ff4088bece1d14ae3bca52d5.1499786555.git.jpoimboe@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
1 parent 627fce1 commit 39358a0

File tree

6 files changed

+417
-13
lines changed

6 files changed

+417
-13
lines changed

arch/x86/include/asm/orc_types.h

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com>
3+
*
4+
* This program is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU General Public License
6+
* as published by the Free Software Foundation; either version 2
7+
* of the License, or (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
#ifndef _ORC_TYPES_H
19+
#define _ORC_TYPES_H
20+
21+
#include <linux/types.h>
22+
#include <linux/compiler.h>
23+
24+
/*
25+
* The ORC_REG_* registers are base registers which are used to find other
26+
* registers on the stack.
27+
*
28+
* ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the
29+
* address of the previous frame: the caller's SP before it called the current
30+
* function.
31+
*
32+
* ORC_REG_UNDEFINED means the corresponding register's value didn't change in
33+
* the current frame.
34+
*
35+
* The most commonly used base registers are SP and BP -- which the previous SP
36+
* is usually based on -- and PREV_SP and UNDEFINED -- which the previous BP is
37+
* usually based on.
38+
*
39+
* The rest of the base registers are needed for special cases like entry code
40+
* and GCC realigned stacks.
41+
*/
42+
#define ORC_REG_UNDEFINED 0
43+
#define ORC_REG_PREV_SP 1
44+
#define ORC_REG_DX 2
45+
#define ORC_REG_DI 3
46+
#define ORC_REG_BP 4
47+
#define ORC_REG_SP 5
48+
#define ORC_REG_R10 6
49+
#define ORC_REG_R13 7
50+
#define ORC_REG_BP_INDIRECT 8
51+
#define ORC_REG_SP_INDIRECT 9
52+
#define ORC_REG_MAX 15
53+
54+
/*
55+
* ORC_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP (the
56+
* caller's SP right before it made the call). Used for all callable
57+
* functions, i.e. all C code and all callable asm functions.
58+
*
59+
* ORC_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset points
60+
* to a fully populated pt_regs from a syscall, interrupt, or exception.
61+
*
62+
* ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset
63+
* points to the iret return frame.
64+
*
65+
* The UNWIND_HINT macros are used only for the unwind_hint struct. They
66+
* aren't used in struct orc_entry due to size and complexity constraints.
67+
* Objtool converts them to real types when it converts the hints to orc
68+
* entries.
69+
*/
70+
#define ORC_TYPE_CALL 0
71+
#define ORC_TYPE_REGS 1
72+
#define ORC_TYPE_REGS_IRET 2
73+
#define UNWIND_HINT_TYPE_SAVE 3
74+
#define UNWIND_HINT_TYPE_RESTORE 4
75+
76+
#ifndef __ASSEMBLY__
77+
/*
78+
* This struct is more or less a vastly simplified version of the DWARF Call
79+
* Frame Information standard. It contains only the necessary parts of DWARF
80+
* CFI, simplified for ease of access by the in-kernel unwinder. It tells the
81+
* unwinder how to find the previous SP and BP (and sometimes entry regs) on
82+
* the stack for a given code address. Each instance of the struct corresponds
83+
* to one or more code locations.
84+
*/
85+
struct orc_entry {
86+
s16 sp_offset;
87+
s16 bp_offset;
88+
unsigned sp_reg:4;
89+
unsigned bp_reg:4;
90+
unsigned type:2;
91+
};
92+
93+
/*
94+
* This struct is used by asm and inline asm code to manually annotate the
95+
* location of registers on the stack for the ORC unwinder.
96+
*
97+
* Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*.
98+
*/
99+
struct unwind_hint {
100+
u32 ip;
101+
s16 sp_offset;
102+
u8 sp_reg;
103+
u8 type;
104+
};
105+
#endif /* __ASSEMBLY__ */
106+
107+
#endif /* _ORC_TYPES_H */

arch/x86/include/asm/unwind_hints.h

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#ifndef _ASM_X86_UNWIND_HINTS_H
2+
#define _ASM_X86_UNWIND_HINTS_H
3+
4+
#include "orc_types.h"
5+
6+
#ifdef __ASSEMBLY__
7+
8+
/*
9+
* In asm, there are two kinds of code: normal C-type callable functions and
10+
* the rest. The normal callable functions can be called by other code, and
11+
* don't do anything unusual with the stack. Such normal callable functions
12+
* are annotated with the ENTRY/ENDPROC macros. Most asm code falls in this
13+
* category. In this case, no special debugging annotations are needed because
14+
* objtool can automatically generate the ORC data for the ORC unwinder to read
15+
* at runtime.
16+
*
17+
* Anything which doesn't fall into the above category, such as syscall and
18+
* interrupt handlers, tends to not be called directly by other functions, and
19+
* often does unusual non-C-function-type things with the stack pointer. Such
20+
* code needs to be annotated such that objtool can understand it. The
21+
* following CFI hint macros are for this type of code.
22+
*
23+
* These macros provide hints to objtool about the state of the stack at each
24+
* instruction. Objtool starts from the hints and follows the code flow,
25+
* making automatic CFI adjustments when it sees pushes and pops, filling out
26+
* the debuginfo as necessary. It will also warn if it sees any
27+
* inconsistencies.
28+
*/
29+
.macro UNWIND_HINT sp_reg=ORC_REG_SP sp_offset=0 type=ORC_TYPE_CALL
30+
#ifdef CONFIG_STACK_VALIDATION
31+
.Lunwind_hint_ip_\@:
32+
.pushsection .discard.unwind_hints
33+
/* struct unwind_hint */
34+
.long .Lunwind_hint_ip_\@ - .
35+
.short \sp_offset
36+
.byte \sp_reg
37+
.byte \type
38+
.popsection
39+
#endif
40+
.endm
41+
42+
.macro UNWIND_HINT_EMPTY
43+
UNWIND_HINT sp_reg=ORC_REG_UNDEFINED
44+
.endm
45+
46+
.macro UNWIND_HINT_REGS base=%rsp offset=0 indirect=0 extra=1 iret=0
47+
.if \base == %rsp && \indirect
48+
.set sp_reg, ORC_REG_SP_INDIRECT
49+
.elseif \base == %rsp
50+
.set sp_reg, ORC_REG_SP
51+
.elseif \base == %rbp
52+
.set sp_reg, ORC_REG_BP
53+
.elseif \base == %rdi
54+
.set sp_reg, ORC_REG_DI
55+
.elseif \base == %rdx
56+
.set sp_reg, ORC_REG_DX
57+
.elseif \base == %r10
58+
.set sp_reg, ORC_REG_R10
59+
.else
60+
.error "UNWIND_HINT_REGS: bad base register"
61+
.endif
62+
63+
.set sp_offset, \offset
64+
65+
.if \iret
66+
.set type, ORC_TYPE_REGS_IRET
67+
.elseif \extra == 0
68+
.set type, ORC_TYPE_REGS_IRET
69+
.set sp_offset, \offset + (16*8)
70+
.else
71+
.set type, ORC_TYPE_REGS
72+
.endif
73+
74+
UNWIND_HINT sp_reg=sp_reg sp_offset=sp_offset type=type
75+
.endm
76+
77+
.macro UNWIND_HINT_IRET_REGS base=%rsp offset=0
78+
UNWIND_HINT_REGS base=\base offset=\offset iret=1
79+
.endm
80+
81+
.macro UNWIND_HINT_FUNC sp_offset=8
82+
UNWIND_HINT sp_offset=\sp_offset
83+
.endm
84+
85+
#else /* !__ASSEMBLY__ */
86+
87+
#define UNWIND_HINT(sp_reg, sp_offset, type) \
88+
"987: \n\t" \
89+
".pushsection .discard.unwind_hints\n\t" \
90+
/* struct unwind_hint */ \
91+
".long 987b - .\n\t" \
92+
".short " __stringify(sp_offset) "\n\t" \
93+
".byte " __stringify(sp_reg) "\n\t" \
94+
".byte " __stringify(type) "\n\t" \
95+
".popsection\n\t"
96+
97+
#define UNWIND_HINT_SAVE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_SAVE)
98+
99+
#define UNWIND_HINT_RESTORE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_RESTORE)
100+
101+
#endif /* __ASSEMBLY__ */
102+
103+
#endif /* _ASM_X86_UNWIND_HINTS_H */

tools/objtool/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ $(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN)
5252
diff -I'^#include' arch/x86/insn/inat.h ../../arch/x86/include/asm/inat.h >/dev/null && \
5353
diff -I'^#include' arch/x86/insn/inat_types.h ../../arch/x86/include/asm/inat_types.h >/dev/null) \
5454
|| echo "warning: objtool: x86 instruction decoder differs from kernel" >&2 )) || true
55+
@(test -d ../../kernel -a -d ../../tools -a -d ../objtool && (( \
56+
diff ../../arch/x86/include/asm/orc_types.h orc_types.h >/dev/null) \
57+
|| echo "warning: objtool: orc_types.h differs from kernel" >&2 )) || true
5558
$(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@
5659

5760

0 commit comments

Comments
 (0)