]>
Commit | Line | Data |
---|---|---|
442f04c3 JP |
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 | ||
30 | static 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 | ||
43 | int 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 | } |