d1091c7fa3
The BUG() macro's use of __builtin_unreachable() via the unreachable() macro tells gcc that the instruction is a dead end, and that it's safe to assume the current code path will not execute past the previous instruction. On x86, the BUG() macro is implemented with the 'ud2' instruction. When objtool's branch analysis sees that instruction, it knows the current code path has come to a dead end. Peter Zijlstra has been working on a patch to change the WARN macros to use 'ud2'. That patch will break objtool's assumption that 'ud2' is always a dead end. Generally it's best for objtool to avoid making those kinds of assumptions anyway. The more ignorant it is of kernel code internals, the better. So create a more generic way for objtool to detect dead ends by adding an annotation to the unreachable() macro. The annotation stores a pointer to the end of the unreachable code path in an '__unreachable' section. Objtool can read that section to find the dead ends. Tested-by: Peter Zijlstra (Intel) <peterz@infradead.org> Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/41a6d33971462ebd944a1c60ad4bf5be86c17b77.1487712920.git.jpoimboe@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
179 lines
3.8 KiB
C
179 lines
3.8 KiB
C
/*
|
|
* Copyright (C) 2015 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/>.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#define unlikely(cond) (cond)
|
|
#include "insn/insn.h"
|
|
#include "insn/inat.c"
|
|
#include "insn/insn.c"
|
|
|
|
#include "../../elf.h"
|
|
#include "../../arch.h"
|
|
#include "../../warn.h"
|
|
|
|
static int is_x86_64(struct elf *elf)
|
|
{
|
|
switch (elf->ehdr.e_machine) {
|
|
case EM_X86_64:
|
|
return 1;
|
|
case EM_386:
|
|
return 0;
|
|
default:
|
|
WARN("unexpected ELF machine type %d", elf->ehdr.e_machine);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct insn insn;
|
|
int x86_64;
|
|
unsigned char op1, op2, ext;
|
|
|
|
x86_64 = is_x86_64(elf);
|
|
if (x86_64 == -1)
|
|
return -1;
|
|
|
|
insn_init(&insn, (void *)(sec->data + 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);
|
|
return -1;
|
|
}
|
|
|
|
*len = insn.length;
|
|
*type = INSN_OTHER;
|
|
|
|
if (insn.vex_prefix.nbytes)
|
|
return 0;
|
|
|
|
op1 = insn.opcode.bytes[0];
|
|
op2 = insn.opcode.bytes[1];
|
|
|
|
switch (op1) {
|
|
case 0x55:
|
|
if (!insn.rex_prefix.nbytes)
|
|
/* push rbp */
|
|
*type = INSN_FP_SAVE;
|
|
break;
|
|
|
|
case 0x5d:
|
|
if (!insn.rex_prefix.nbytes)
|
|
/* pop rbp */
|
|
*type = INSN_FP_RESTORE;
|
|
break;
|
|
|
|
case 0x70 ... 0x7f:
|
|
*type = INSN_JUMP_CONDITIONAL;
|
|
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;
|
|
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;
|
|
break;
|
|
|
|
case 0x90:
|
|
*type = INSN_NOP;
|
|
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;
|
|
|
|
break;
|
|
|
|
case 0xc9: /* leave */
|
|
*type = INSN_FP_RESTORE;
|
|
break;
|
|
|
|
case 0xe3: /* jecxz/jrcxz */
|
|
*type = INSN_JUMP_CONDITIONAL;
|
|
break;
|
|
|
|
case 0xe9:
|
|
case 0xeb:
|
|
*type = INSN_JUMP_UNCONDITIONAL;
|
|
break;
|
|
|
|
case 0xc2:
|
|
case 0xc3:
|
|
*type = INSN_RETURN;
|
|
break;
|
|
|
|
case 0xca: /* retf */
|
|
case 0xcb: /* retf */
|
|
case 0xcf: /* iret */
|
|
*type = INSN_CONTEXT_SWITCH;
|
|
break;
|
|
|
|
case 0xe8:
|
|
*type = INSN_CALL;
|
|
break;
|
|
|
|
case 0xff:
|
|
ext = X86_MODRM_REG(insn.modrm.bytes[0]);
|
|
if (ext == 2 || ext == 3)
|
|
*type = INSN_CALL_DYNAMIC;
|
|
else if (ext == 4)
|
|
*type = INSN_JUMP_DYNAMIC;
|
|
else if (ext == 5) /*jmpf */
|
|
*type = INSN_CONTEXT_SWITCH;
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
*immediate = insn.immediate.nbytes ? insn.immediate.value : 0;
|
|
|
|
return 0;
|
|
}
|