Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit baa41469 authored by Josh Poimboeuf's avatar Josh Poimboeuf Committed by Ingo Molnar
Browse files

objtool: Implement stack validation 2.0



This is a major rewrite of objtool.  Instead of only tracking frame
pointer changes, it now tracks all stack-related operations, including
all register saves/restores.

In addition to making stack validation more robust, this also paves the
way for undwarf generation.

Signed-off-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Jiri Slaby <jslaby@suse.cz>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: live-patching@vger.kernel.org
Link: http://lkml.kernel.org/r/678bd94c0566c6129bcc376cddb259c4c5633004.1498659915.git.jpoimboe@redhat.com


Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent c207aee4
Loading
Loading
Loading
Loading
+65 −88
Original line number Diff line number Diff line
@@ -127,28 +127,13 @@ b) 100% reliable stack traces for DWARF enabled kernels

c) Higher live patching compatibility rate

   (NOTE: This is not yet implemented)

   Currently with CONFIG_LIVEPATCH there's a basic live patching
   framework which is safe for roughly 85-90% of "security" fixes.  But
   patches can't have complex features like function dependency or
   prototype changes, or data structure changes.

   There's a strong need to support patches which have the more complex
   features so that the patch compatibility rate for security fixes can
   eventually approach something resembling 100%.  To achieve that, a
   "consistency model" is needed, which allows tasks to be safely
   transitioned from an unpatched state to a patched state.

   One of the key requirements of the currently proposed livepatch
   consistency model [*] is that it needs to walk the stack of each
   sleeping task to determine if it can be transitioned to the patched
   state.  If objtool can ensure that stack traces are reliable, this
   consistency model can be used and the live patching compatibility
   rate can be improved significantly.

   [*] https://lkml.kernel.org/r/cover.1423499826.git.jpoimboe@redhat.com
   Livepatch has an optional "consistency model", which is needed for
   more complex patches.  In order for the consistency model to work,
   stack traces need to be reliable (or an unreliable condition needs to
   be detectable).  Objtool makes that possible.

   For more details, see the livepatch documentation in the Linux kernel
   source tree at Documentation/livepatch/livepatch.txt.

Rules
-----
@@ -201,80 +186,84 @@ To achieve the validation, objtool enforces the following rules:
   return normally.


Errors in .S files
------------------
Objtool warnings
----------------

For asm files, if you're getting an error which doesn't make sense,
first make sure that the affected code follows the above rules.

If you're getting an error in a compiled .S file which you don't
understand, first make sure that the affected code follows the above
rules.
For C files, the common culprits are inline asm statements and calls to
"noreturn" functions.  See below for more details.

Another possible cause for errors in C code is if the Makefile removes
-fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options.

Here are some examples of common warnings reported by objtool, what
they mean, and suggestions for how to fix them.


1. asm_file.o: warning: objtool: func()+0x128: call without frame pointer save/setup
1. file.o: warning: objtool: func()+0x128: call without frame pointer save/setup

   The func() function made a function call without first saving and/or
   updating the frame pointer.

   If func() is indeed a callable function, add proper frame pointer
   logic using the FRAME_BEGIN and FRAME_END macros.  Otherwise, remove
   its ELF function annotation by changing ENDPROC to END.
   updating the frame pointer, and CONFIG_FRAME_POINTER is enabled.

   If you're getting this error in a .c file, see the "Errors in .c
   files" section.
   If the error is for an asm file, and func() is indeed a callable
   function, add proper frame pointer logic using the FRAME_BEGIN and
   FRAME_END macros.  Otherwise, if it's not a callable function, remove
   its ELF function annotation by changing ENDPROC to END, and instead
   use the manual CFI hint macros in asm/undwarf.h.

   If it's a GCC-compiled .c file, the error may be because the function
   uses an inline asm() statement which has a "call" instruction.  An
   asm() statement with a call instruction must declare the use of the
   stack pointer in its output operand.  For example, on x86_64:

2. asm_file.o: warning: objtool: .text+0x53: return instruction outside of a callable function

   A return instruction was detected, but objtool couldn't find a way
   for a callable function to reach the instruction.
     register void *__sp asm("rsp");
     asm volatile("call func" : "+r" (__sp));

   If the return instruction is inside (or reachable from) a callable
   function, the function needs to be annotated with the ENTRY/ENDPROC
   macros.
   Otherwise the stack frame may not get created before the call.

   If you _really_ need a return instruction outside of a function, and
   are 100% sure that it won't affect stack traces, you can tell
   objtool to ignore it.  See the "Adding exceptions" section below.

2. file.o: warning: objtool: .text+0x53: unreachable instruction

3. asm_file.o: warning: objtool: func()+0x9: function has unreachable instruction
   Objtool couldn't find a code path to reach the instruction.

   The instruction lives inside of a callable function, but there's no
   possible control flow path from the beginning of the function to the
   instruction.
   If the error is for an asm file, and the instruction is inside (or
   reachable from) a callable function, the function should be annotated
   with the ENTRY/ENDPROC macros (ENDPROC is the important one).
   Otherwise, the code should probably be annotated with the CFI hint
   macros in asm/undwarf.h so objtool and the unwinder can know the
   stack state associated with the code.

   If the instruction is actually needed, and it's actually in a
   callable function, ensure that its function is properly annotated
   with ENTRY/ENDPROC.
   If you're 100% sure the code won't affect stack traces, or if you're
   a just a bad person, you can tell objtool to ignore it.  See the
   "Adding exceptions" section below.

   If it's not actually in a callable function (e.g. kernel entry code),
   change ENDPROC to END.


4. asm_file.o: warning: objtool: func(): can't find starting instruction
4. file.o: warning: objtool: func(): can't find starting instruction
   or
   asm_file.o: warning: objtool: func()+0x11dd: can't decode instruction
   file.o: warning: objtool: func()+0x11dd: can't decode instruction

   Did you put data in a text section?  If so, that can confuse
   Does the file have data in a text section?  If so, that can confuse
   objtool's instruction decoder.  Move the data to a more appropriate
   section like .data or .rodata.


5. asm_file.o: warning: objtool: func()+0x6: kernel entry/exit from callable instruction

   This is a kernel entry/exit instruction like sysenter or sysret.
   Such instructions aren't allowed in a callable function, and are most
   likely part of the kernel entry code.
5. file.o: warning: objtool: func()+0x6: unsupported instruction in callable function

   If the instruction isn't actually in a callable function, change
   ENDPROC to END.
   This is a kernel entry/exit instruction like sysenter or iret.  Such
   instructions aren't allowed in a callable function, and are most
   likely part of the kernel entry code.  They should usually not have
   the callable function annotation (ENDPROC) and should always be
   annotated with the CFI hint macros in asm/undwarf.h.


6. asm_file.o: warning: objtool: func()+0x26: sibling call from callable instruction with changed frame pointer
6. file.o: warning: objtool: func()+0x26: sibling call from callable instruction with modified stack frame

   This is a dynamic jump or a jump to an undefined symbol.  Stacktool
   This is a dynamic jump or a jump to an undefined symbol.  Objtool
   assumed it's a sibling call and detected that the frame pointer
   wasn't first restored to its original state.

@@ -282,24 +271,28 @@ they mean, and suggestions for how to fix them.
   destination code to the local file.

   If the instruction is not actually in a callable function (e.g.
   kernel entry code), change ENDPROC to END.
   kernel entry code), change ENDPROC to END and annotate manually with
   the CFI hint macros in asm/undwarf.h.


7. asm_file: warning: objtool: func()+0x5c: frame pointer state mismatch
7. file: warning: objtool: func()+0x5c: stack state mismatch

   The instruction's frame pointer state is inconsistent, depending on
   which execution path was taken to reach the instruction.

   Make sure the function pushes and sets up the frame pointer (for
   x86_64, this means rbp) at the beginning of the function and pops it
   at the end of the function.  Also make sure that no other code in the
   function touches the frame pointer.
   Make sure that, when CONFIG_FRAME_POINTER is enabled, the function
   pushes and sets up the frame pointer (for x86_64, this means rbp) at
   the beginning of the function and pops it at the end of the function.
   Also make sure that no other code in the function touches the frame
   pointer.

   Another possibility is that the code has some asm or inline asm which
   does some unusual things to the stack or the frame pointer.  In such
   cases it's probably appropriate to use the CFI hint macros in
   asm/undwarf.h.

Errors in .c files
------------------

1. c_file.o: warning: objtool: funcA() falls through to next function funcB()
8. file.o: warning: objtool: funcA() falls through to next function funcB()

   This means that funcA() doesn't end with a return instruction or an
   unconditional jump, and that objtool has determined that the function
@@ -318,22 +311,6 @@ Errors in .c files
      might be corrupt due to a gcc bug.  For more details, see:
      https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646

2. If you're getting any other objtool error in a compiled .c file, it
   may be because the file uses an asm() statement which has a "call"
   instruction.  An asm() statement with a call instruction must declare
   the use of the stack pointer in its output operand.  For example, on
   x86_64:

     register void *__sp asm("rsp");
     asm volatile("call func" : "+r" (__sp));

   Otherwise the stack frame may not get created before the call.

3. Another possible cause for errors in C code is if the Makefile removes
   -fno-omit-frame-pointer or adds -fomit-frame-pointer to the gcc options.

Also see the above section for .S file errors for more information what
the individual error messages mean.

If the error doesn't seem to make sense, it could be a bug in objtool.
Feel free to ask the objtool maintainer for help.
+1 −1
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@ OBJTOOL_IN := $(OBJTOOL)-in.o
all: $(OBJTOOL)

INCLUDES := -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi
CFLAGS   += -Wall -Werror $(EXTRA_WARNINGS) -fomit-frame-pointer -O2 -g $(INCLUDES)
CFLAGS   += -Wall -Werror $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -fomit-frame-pointer -O2 -g $(INCLUDES)
LDFLAGS  += -lelf $(LIBSUBCMD)

# Allow old libelf to be used:
+51 −13
Original line number Diff line number Diff line
@@ -19,25 +19,63 @@
#define _ARCH_H

#include <stdbool.h>
#include <linux/list.h>
#include "elf.h"
#include "cfi.h"

#define INSN_FP_SAVE		1
#define INSN_FP_SETUP		2
#define INSN_FP_RESTORE		3
#define INSN_JUMP_CONDITIONAL	4
#define INSN_JUMP_UNCONDITIONAL	5
#define INSN_JUMP_DYNAMIC	6
#define INSN_CALL		7
#define INSN_CALL_DYNAMIC	8
#define INSN_RETURN		9
#define INSN_CONTEXT_SWITCH	10
#define INSN_NOP		11
#define INSN_OTHER		12
#define INSN_JUMP_CONDITIONAL	1
#define INSN_JUMP_UNCONDITIONAL	2
#define INSN_JUMP_DYNAMIC	3
#define INSN_CALL		4
#define INSN_CALL_DYNAMIC	5
#define INSN_RETURN		6
#define INSN_CONTEXT_SWITCH	7
#define INSN_STACK		8
#define INSN_NOP		9
#define INSN_OTHER		10
#define INSN_LAST		INSN_OTHER

enum op_dest_type {
	OP_DEST_REG,
	OP_DEST_REG_INDIRECT,
	OP_DEST_MEM,
	OP_DEST_PUSH,
	OP_DEST_LEAVE,
};

struct op_dest {
	enum op_dest_type type;
	unsigned char reg;
	int offset;
};

enum op_src_type {
	OP_SRC_REG,
	OP_SRC_REG_INDIRECT,
	OP_SRC_CONST,
	OP_SRC_POP,
	OP_SRC_ADD,
	OP_SRC_AND,
};

struct op_src {
	enum op_src_type type;
	unsigned char reg;
	int offset;
};

struct stack_op {
	struct op_dest dest;
	struct op_src src;
};

void arch_initial_func_cfi_state(struct cfi_state *state);

int arch_decode_instruction(struct elf *elf, struct section *sec,
			    unsigned long offset, unsigned int maxlen,
			    unsigned int *len, unsigned char *type,
			    unsigned long *displacement);
			    unsigned long *immediate, struct stack_op *op);

bool arch_callee_saved_reg(unsigned char reg);

#endif /* _ARCH_H */
+362 −38
Original line number Diff line number Diff line
@@ -27,6 +27,17 @@
#include "../../arch.h"
#include "../../warn.h"

static unsigned char op_to_cfi_reg[][2] = {
	{CFI_AX, CFI_R8},
	{CFI_CX, CFI_R9},
	{CFI_DX, CFI_R10},
	{CFI_BX, CFI_R11},
	{CFI_SP, CFI_R12},
	{CFI_BP, CFI_R13},
	{CFI_SI, CFI_R14},
	{CFI_DI, CFI_R15},
};

static int is_x86_64(struct elf *elf)
{
	switch (elf->ehdr.e_machine) {
@@ -40,24 +51,50 @@ static int is_x86_64(struct elf *elf)
	}
}

bool arch_callee_saved_reg(unsigned char reg)
{
	switch (reg) {
	case CFI_BP:
	case CFI_BX:
	case CFI_R12:
	case CFI_R13:
	case CFI_R14:
	case CFI_R15:
		return true;

	case CFI_AX:
	case CFI_CX:
	case CFI_DX:
	case CFI_SI:
	case CFI_DI:
	case CFI_SP:
	case CFI_R8:
	case CFI_R9:
	case CFI_R10:
	case CFI_R11:
	case CFI_RA:
	default:
		return false;
	}
}

int arch_decode_instruction(struct elf *elf, struct section *sec,
			    unsigned long offset, unsigned int maxlen,
			    unsigned int *len, unsigned char *type,
			    unsigned long *immediate)
			    unsigned long *immediate, struct stack_op *op)
{
	struct insn insn;
	int x86_64;
	unsigned char op1, op2, ext;
	int x86_64, sign;
	unsigned char op1, op2, rex = 0, rex_b = 0, rex_r = 0, rex_w = 0,
		      modrm = 0, modrm_mod = 0, modrm_rm = 0, modrm_reg = 0,
		      sib = 0;

	x86_64 = is_x86_64(elf);
	if (x86_64 == -1)
		return -1;

	insn_init(&insn, (void *)(sec->data + offset), maxlen, x86_64);
	insn_init(&insn, sec->data->d_buf + offset, maxlen, x86_64);
	insn_get_length(&insn);
	insn_get_opcode(&insn);
	insn_get_modrm(&insn);
	insn_get_immediate(&insn);

	if (!insn_complete(&insn)) {
		WARN_FUNC("can't decode instruction", sec, offset);
@@ -73,67 +110,323 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
	op1 = insn.opcode.bytes[0];
	op2 = insn.opcode.bytes[1];

	if (insn.rex_prefix.nbytes) {
		rex = insn.rex_prefix.bytes[0];
		rex_w = X86_REX_W(rex) >> 3;
		rex_r = X86_REX_R(rex) >> 2;
		rex_b = X86_REX_B(rex);
	}

	if (insn.modrm.nbytes) {
		modrm = insn.modrm.bytes[0];
		modrm_mod = X86_MODRM_MOD(modrm);
		modrm_reg = X86_MODRM_REG(modrm);
		modrm_rm = X86_MODRM_RM(modrm);
	}

	if (insn.sib.nbytes)
		sib = insn.sib.bytes[0];

	switch (op1) {
	case 0x55:
		if (!insn.rex_prefix.nbytes)
			/* push rbp */
			*type = INSN_FP_SAVE;

	case 0x1:
	case 0x29:
		if (rex_w && !rex_b && modrm_mod == 3 && modrm_rm == 4) {

			/* add/sub reg, %rsp */
			*type = INSN_STACK;
			op->src.type = OP_SRC_ADD;
			op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
			op->dest.type = OP_SRC_REG;
			op->dest.reg = CFI_SP;
		}
		break;

	case 0x5d:
		if (!insn.rex_prefix.nbytes)
			/* pop rbp */
			*type = INSN_FP_RESTORE;
	case 0x50 ... 0x57:

		/* push reg */
		*type = INSN_STACK;
		op->src.type = OP_SRC_REG;
		op->src.reg = op_to_cfi_reg[op1 & 0x7][rex_b];
		op->dest.type = OP_DEST_PUSH;

		break;

	case 0x58 ... 0x5f:

		/* pop reg */
		*type = INSN_STACK;
		op->src.type = OP_SRC_POP;
		op->dest.type = OP_DEST_REG;
		op->dest.reg = op_to_cfi_reg[op1 & 0x7][rex_b];

		break;

	case 0x68:
	case 0x6a:
		/* push immediate */
		*type = INSN_STACK;
		op->src.type = OP_SRC_CONST;
		op->dest.type = OP_DEST_PUSH;
		break;

	case 0x70 ... 0x7f:
		*type = INSN_JUMP_CONDITIONAL;
		break;

	case 0x81:
	case 0x83:
		if (rex != 0x48)
			break;

		if (modrm == 0xe4) {
			/* and imm, %rsp */
			*type = INSN_STACK;
			op->src.type = OP_SRC_AND;
			op->src.reg = CFI_SP;
			op->src.offset = insn.immediate.value;
			op->dest.type = OP_DEST_REG;
			op->dest.reg = CFI_SP;
			break;
		}

		if (modrm == 0xc4)
			sign = 1;
		else if (modrm == 0xec)
			sign = -1;
		else
			break;

		/* add/sub imm, %rsp */
		*type = INSN_STACK;
		op->src.type = OP_SRC_ADD;
		op->src.reg = CFI_SP;
		op->src.offset = insn.immediate.value * sign;
		op->dest.type = OP_DEST_REG;
		op->dest.reg = CFI_SP;
		break;

	case 0x89:
		if (insn.rex_prefix.nbytes == 1 &&
		    insn.rex_prefix.bytes[0] == 0x48 &&
		    insn.modrm.nbytes && insn.modrm.bytes[0] == 0xe5)
			/* mov rsp, rbp */
			*type = INSN_FP_SETUP;
		if (rex == 0x48 && modrm == 0xe5) {

			/* mov %rsp, %rbp */
			*type = INSN_STACK;
			op->src.type = OP_SRC_REG;
			op->src.reg = CFI_SP;
			op->dest.type = OP_DEST_REG;
			op->dest.reg = CFI_BP;
			break;
		}
		/* fallthrough */
	case 0x88:
		if (!rex_b &&
		    (modrm_mod == 1 || modrm_mod == 2) && modrm_rm == 5) {

			/* mov reg, disp(%rbp) */
			*type = INSN_STACK;
			op->src.type = OP_SRC_REG;
			op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
			op->dest.type = OP_DEST_REG_INDIRECT;
			op->dest.reg = CFI_BP;
			op->dest.offset = insn.displacement.value;

		} else if (rex_w && !rex_b && modrm_rm == 4 && sib == 0x24) {

			/* mov reg, disp(%rsp) */
			*type = INSN_STACK;
			op->src.type = OP_SRC_REG;
			op->src.reg = op_to_cfi_reg[modrm_reg][rex_r];
			op->dest.type = OP_DEST_REG_INDIRECT;
			op->dest.reg = CFI_SP;
			op->dest.offset = insn.displacement.value;
		}

		break;

	case 0x8b:
		if (rex_w && !rex_b && modrm_mod == 1 && modrm_rm == 5) {

			/* mov disp(%rbp), reg */
			*type = INSN_STACK;
			op->src.type = OP_SRC_REG_INDIRECT;
			op->src.reg = CFI_BP;
			op->src.offset = insn.displacement.value;
			op->dest.type = OP_DEST_REG;
			op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];

		} else if (rex_w && !rex_b && sib == 0x24 &&
			   modrm_mod != 3 && modrm_rm == 4) {

			/* mov disp(%rsp), reg */
			*type = INSN_STACK;
			op->src.type = OP_SRC_REG_INDIRECT;
			op->src.reg = CFI_SP;
			op->src.offset = insn.displacement.value;
			op->dest.type = OP_DEST_REG;
			op->dest.reg = op_to_cfi_reg[modrm_reg][rex_r];
		}

		break;

	case 0x8d:
		if (insn.rex_prefix.nbytes &&
		    insn.rex_prefix.bytes[0] == 0x48 &&
		    insn.modrm.nbytes && insn.modrm.bytes[0] == 0x2c &&
		    insn.sib.nbytes && insn.sib.bytes[0] == 0x24)
			/* lea %(rsp), %rbp */
			*type = INSN_FP_SETUP;
		if (rex == 0x48 && modrm == 0x65) {

			/* lea -disp(%rbp), %rsp */
			*type = INSN_STACK;
			op->src.type = OP_SRC_ADD;
			op->src.reg = CFI_BP;
			op->src.offset = insn.displacement.value;
			op->dest.type = OP_DEST_REG;
			op->dest.reg = CFI_SP;
			break;
		}

		if (rex == 0x4c && modrm == 0x54 && sib == 0x24 &&
		    insn.displacement.value == 8) {

			/*
			 * lea 0x8(%rsp), %r10
			 *
			 * Here r10 is the "drap" pointer, used as a stack
			 * pointer helper when the stack gets realigned.
			 */
			*type = INSN_STACK;
			op->src.type = OP_SRC_ADD;
			op->src.reg = CFI_SP;
			op->src.offset = 8;
			op->dest.type = OP_DEST_REG;
			op->dest.reg = CFI_R10;
			break;
		}

		if (rex == 0x4c && modrm == 0x6c && sib == 0x24 &&
		    insn.displacement.value == 16) {

			/*
			 * lea 0x10(%rsp), %r13
			 *
			 * Here r13 is the "drap" pointer, used as a stack
			 * pointer helper when the stack gets realigned.
			 */
			*type = INSN_STACK;
			op->src.type = OP_SRC_ADD;
			op->src.reg = CFI_SP;
			op->src.offset = 16;
			op->dest.type = OP_DEST_REG;
			op->dest.reg = CFI_R13;
			break;
		}

		if (rex == 0x49 && modrm == 0x62 &&
		    insn.displacement.value == -8) {

			/*
			 * lea -0x8(%r10), %rsp
			 *
			 * Restoring rsp back to its original value after a
			 * stack realignment.
			 */
			*type = INSN_STACK;
			op->src.type = OP_SRC_ADD;
			op->src.reg = CFI_R10;
			op->src.offset = -8;
			op->dest.type = OP_DEST_REG;
			op->dest.reg = CFI_SP;
			break;
		}

		if (rex == 0x49 && modrm == 0x65 &&
		    insn.displacement.value == -16) {

			/*
			 * lea -0x10(%r13), %rsp
			 *
			 * Restoring rsp back to its original value after a
			 * stack realignment.
			 */
			*type = INSN_STACK;
			op->src.type = OP_SRC_ADD;
			op->src.reg = CFI_R13;
			op->src.offset = -16;
			op->dest.type = OP_DEST_REG;
			op->dest.reg = CFI_SP;
			break;
		}

		break;

	case 0x8f:
		/* pop to mem */
		*type = INSN_STACK;
		op->src.type = OP_SRC_POP;
		op->dest.type = OP_DEST_MEM;
		break;

	case 0x90:
		*type = INSN_NOP;
		break;

	case 0x9c:
		/* pushf */
		*type = INSN_STACK;
		op->src.type = OP_SRC_CONST;
		op->dest.type = OP_DEST_PUSH;
		break;

	case 0x9d:
		/* popf */
		*type = INSN_STACK;
		op->src.type = OP_SRC_POP;
		op->dest.type = OP_DEST_MEM;
		break;

	case 0x0f:

		if (op2 >= 0x80 && op2 <= 0x8f)
			*type = INSN_JUMP_CONDITIONAL;
		else if (op2 == 0x05 || op2 == 0x07 || op2 == 0x34 ||
			 op2 == 0x35)

			/* sysenter, sysret */
			*type = INSN_CONTEXT_SWITCH;

		else if (op2 == 0x0d || op2 == 0x1f)

			/* nopl/nopw */
			*type = INSN_NOP;
		else if (op2 == 0x01 && insn.modrm.nbytes &&
			 (insn.modrm.bytes[0] == 0xc2 ||
			  insn.modrm.bytes[0] == 0xd8))
			/* vmlaunch, vmrun */
			*type = INSN_CONTEXT_SWITCH;

		else if (op2 == 0xa0 || op2 == 0xa8) {

			/* push fs/gs */
			*type = INSN_STACK;
			op->src.type = OP_SRC_CONST;
			op->dest.type = OP_DEST_PUSH;

		} else if (op2 == 0xa1 || op2 == 0xa9) {

			/* pop fs/gs */
			*type = INSN_STACK;
			op->src.type = OP_SRC_POP;
			op->dest.type = OP_DEST_MEM;
		}

		break;

	case 0xc9: /* leave */
		*type = INSN_FP_RESTORE;
	case 0xc9:
		/*
		 * leave
		 *
		 * equivalent to:
		 * mov bp, sp
		 * pop bp
		 */
		*type = INSN_STACK;
		op->dest.type = OP_DEST_LEAVE;

		break;

	case 0xe3: /* jecxz/jrcxz */
	case 0xe3:
		/* jecxz/jrcxz */
		*type = INSN_JUMP_CONDITIONAL;
		break;

@@ -158,14 +451,27 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
		break;

	case 0xff:
		ext = X86_MODRM_REG(insn.modrm.bytes[0]);
		if (ext == 2 || ext == 3)
		if (modrm_reg == 2 || modrm_reg == 3)

			*type = INSN_CALL_DYNAMIC;
		else if (ext == 4)

		else if (modrm_reg == 4)

			*type = INSN_JUMP_DYNAMIC;
		else if (ext == 5) /*jmpf */

		else if (modrm_reg == 5)

			/* jmpf */
			*type = INSN_CONTEXT_SWITCH;

		else if (modrm_reg == 6) {

			/* push from mem */
			*type = INSN_STACK;
			op->src.type = OP_SRC_CONST;
			op->dest.type = OP_DEST_PUSH;
		}

		break;

	default:
@@ -176,3 +482,21 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,

	return 0;
}

void arch_initial_func_cfi_state(struct cfi_state *state)
{
	int i;

	for (i = 0; i < CFI_NUM_REGS; i++) {
		state->regs[i].base = CFI_UNDEFINED;
		state->regs[i].offset = 0;
	}

	/* initial CFA (call frame address) */
	state->cfa.base = CFI_SP;
	state->cfa.offset = 8;

	/* initial RA (return address) */
	state->regs[16].base = CFI_CFA;
	state->regs[16].offset = -8;
}

tools/objtool/cfi.h

0 → 100644
+55 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _OBJTOOL_CFI_H
#define _OBJTOOL_CFI_H

#define CFI_UNDEFINED		-1
#define CFI_CFA			-2
#define CFI_SP_INDIRECT		-3
#define CFI_BP_INDIRECT		-4

#define CFI_AX			0
#define CFI_DX			1
#define CFI_CX			2
#define CFI_BX			3
#define CFI_SI			4
#define CFI_DI			5
#define CFI_BP			6
#define CFI_SP			7
#define CFI_R8			8
#define CFI_R9			9
#define CFI_R10			10
#define CFI_R11			11
#define CFI_R12			12
#define CFI_R13			13
#define CFI_R14			14
#define CFI_R15			15
#define CFI_RA			16
#define CFI_NUM_REGS	17

struct cfi_reg {
	int base;
	int offset;
};

struct cfi_state {
	struct cfi_reg cfa;
	struct cfi_reg regs[CFI_NUM_REGS];
};

#endif /* _OBJTOOL_CFI_H */
Loading