linux/tools/objtool/arch/x86/decode.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2015 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#include <stdio.h>
  19#include <stdlib.h>
  20
  21#define unlikely(cond) (cond)
  22#include "insn/insn.h"
  23#include "insn/inat.c"
  24#include "insn/insn.c"
  25
  26#include "../../elf.h"
  27#include "../../arch.h"
  28#include "../../warn.h"
  29
  30static int is_x86_64(struct elf *elf)
  31{
  32        switch (elf->ehdr.e_machine) {
  33        case EM_X86_64:
  34                return 1;
  35        case EM_386:
  36                return 0;
  37        default:
  38                WARN("unexpected ELF machine type %d", elf->ehdr.e_machine);
  39                return -1;
  40        }
  41}
  42
  43int arch_decode_instruction(struct elf *elf, struct section *sec,
  44                            unsigned long offset, unsigned int maxlen,
  45                            unsigned int *len, unsigned char *type,
  46                            unsigned long *immediate)
  47{
  48        struct insn insn;
  49        int x86_64;
  50        unsigned char op1, op2, ext;
  51
  52        x86_64 = is_x86_64(elf);
  53        if (x86_64 == -1)
  54                return -1;
  55
  56        insn_init(&insn, (void *)(sec->data + offset), maxlen, x86_64);
  57        insn_get_length(&insn);
  58        insn_get_opcode(&insn);
  59        insn_get_modrm(&insn);
  60        insn_get_immediate(&insn);
  61
  62        if (!insn_complete(&insn)) {
  63                WARN_FUNC("can't decode instruction", sec, offset);
  64                return -1;
  65        }
  66
  67        *len = insn.length;
  68        *type = INSN_OTHER;
  69
  70        if (insn.vex_prefix.nbytes)
  71                return 0;
  72
  73        op1 = insn.opcode.bytes[0];
  74        op2 = insn.opcode.bytes[1];
  75
  76        switch (op1) {
  77        case 0x55:
  78                if (!insn.rex_prefix.nbytes)
  79                        /* push rbp */
  80                        *type = INSN_FP_SAVE;
  81                break;
  82
  83        case 0x5d:
  84                if (!insn.rex_prefix.nbytes)
  85                        /* pop rbp */
  86                        *type = INSN_FP_RESTORE;
  87                break;
  88
  89        case 0x70 ... 0x7f:
  90                *type = INSN_JUMP_CONDITIONAL;
  91                break;
  92
  93        case 0x89:
  94                if (insn.rex_prefix.nbytes == 1 &&
  95                    insn.rex_prefix.bytes[0] == 0x48 &&
  96                    insn.modrm.nbytes && insn.modrm.bytes[0] == 0xe5)
  97                        /* mov rsp, rbp */
  98                        *type = INSN_FP_SETUP;
  99                break;
 100
 101        case 0x90:
 102                *type = INSN_NOP;
 103                break;
 104
 105        case 0x0f:
 106                if (op2 >= 0x80 && op2 <= 0x8f)
 107                        *type = INSN_JUMP_CONDITIONAL;
 108                else if (op2 == 0x05 || op2 == 0x07 || op2 == 0x34 ||
 109                         op2 == 0x35)
 110                        /* sysenter, sysret */
 111                        *type = INSN_CONTEXT_SWITCH;
 112                else if (op2 == 0x0b || op2 == 0xb9)
 113                        /* ud2 */
 114                        *type = INSN_BUG;
 115                else if (op2 == 0x0d || op2 == 0x1f)
 116                        /* nopl/nopw */
 117                        *type = INSN_NOP;
 118                else if (op2 == 0x01 && insn.modrm.nbytes &&
 119                         (insn.modrm.bytes[0] == 0xc2 ||
 120                          insn.modrm.bytes[0] == 0xd8))
 121                        /* vmlaunch, vmrun */
 122                        *type = INSN_CONTEXT_SWITCH;
 123
 124                break;
 125
 126        case 0xc9: /* leave */
 127                *type = INSN_FP_RESTORE;
 128                break;
 129
 130        case 0xe3: /* jecxz/jrcxz */
 131                *type = INSN_JUMP_CONDITIONAL;
 132                break;
 133
 134        case 0xe9:
 135        case 0xeb:
 136                *type = INSN_JUMP_UNCONDITIONAL;
 137                break;
 138
 139        case 0xc2:
 140        case 0xc3:
 141                *type = INSN_RETURN;
 142                break;
 143
 144        case 0xc5: /* iret */
 145        case 0xca: /* retf */
 146        case 0xcb: /* retf */
 147                *type = INSN_CONTEXT_SWITCH;
 148                break;
 149
 150        case 0xe8:
 151                *type = INSN_CALL;
 152                break;
 153
 154        case 0xff:
 155                ext = X86_MODRM_REG(insn.modrm.bytes[0]);
 156                if (ext == 2 || ext == 3)
 157                        *type = INSN_CALL_DYNAMIC;
 158                else if (ext == 4)
 159                        *type = INSN_JUMP_DYNAMIC;
 160                else if (ext == 5) /*jmpf */
 161                        *type = INSN_CONTEXT_SWITCH;
 162
 163                break;
 164
 165        default:
 166                break;
 167        }
 168
 169        *immediate = insn.immediate.nbytes ? insn.immediate.value : 0;
 170
 171        return 0;
 172}
 173