1/*
2 * Copyright 2012, Alex Smith, alex@alex-smith.me.uk.
3 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Copyright 2008, François Revol, revol@free.fr
5 * Copyright 2016, Rene Gollent, rene@gollent.com.
6 * Distributed under the terms of the MIT License.
7 */
8
9#include "DisassemblerX8664.h"
10
11#include <new>
12
13#include "udis86.h"
14
15#include <OS.h>
16
17
18#include "CpuStateX8664.h"
19#include "InstructionInfo.h"
20
21
22static uint8 RegisterNumberFromUdisIndex(int32 udisIndex)
23{
24	switch (udisIndex) {
25		case UD_R_RIP: return X86_64_REGISTER_RIP;
26		case UD_R_RSP: return X86_64_REGISTER_RSP;
27		case UD_R_RBP: return X86_64_REGISTER_RBP;
28
29		case UD_R_RAX: return X86_64_REGISTER_RAX;
30		case UD_R_RBX: return X86_64_REGISTER_RBX;
31		case UD_R_RCX: return X86_64_REGISTER_RCX;
32		case UD_R_RDX: return X86_64_REGISTER_RDX;
33
34		case UD_R_RSI: return X86_64_REGISTER_RSI;
35		case UD_R_RDI: return X86_64_REGISTER_RDI;
36
37		case UD_R_R8: return X86_64_REGISTER_R8;
38		case UD_R_R9: return X86_64_REGISTER_R9;
39		case UD_R_R10: return X86_64_REGISTER_R10;
40		case UD_R_R11: return X86_64_REGISTER_R11;
41		case UD_R_R12: return X86_64_REGISTER_R12;
42		case UD_R_R13: return X86_64_REGISTER_R13;
43		case UD_R_R14: return X86_64_REGISTER_R14;
44		case UD_R_R15: return X86_64_REGISTER_R15;
45
46		case UD_R_CS: return X86_64_REGISTER_CS;
47		case UD_R_DS: return X86_64_REGISTER_DS;
48		case UD_R_ES: return X86_64_REGISTER_ES;
49		case UD_R_FS: return X86_64_REGISTER_FS;
50		case UD_R_GS: return X86_64_REGISTER_GS;
51		case UD_R_SS: return X86_64_REGISTER_SS;
52	}
53
54	return X86_64_INT_REGISTER_END;
55}
56
57
58struct DisassemblerX8664::UdisData : ud_t {
59};
60
61
62DisassemblerX8664::DisassemblerX8664()
63	:
64	fAddress(0),
65	fCode(NULL),
66	fCodeSize(0),
67	fUdisData(NULL)
68{
69}
70
71
72DisassemblerX8664::~DisassemblerX8664()
73{
74	delete fUdisData;
75}
76
77
78status_t
79DisassemblerX8664::Init(target_addr_t address, const void* code, size_t codeSize)
80{
81	// unset old data
82	delete fUdisData;
83	fUdisData = NULL;
84
85	// set new data
86	fUdisData = new(std::nothrow) UdisData;
87	if (fUdisData == NULL)
88		return B_NO_MEMORY;
89
90	fAddress = address;
91	fCode = (const uint8*)code;
92	fCodeSize = codeSize;
93
94	// init udis
95	ud_init(fUdisData);
96	ud_set_input_buffer(fUdisData, (unsigned char*)fCode, fCodeSize);
97	ud_set_mode(fUdisData, 64);
98	ud_set_pc(fUdisData, (uint64_t)fAddress);
99	ud_set_syntax(fUdisData, UD_SYN_ATT);
100	ud_set_vendor(fUdisData, UD_VENDOR_INTEL);
101		// TODO: Set the correct vendor!
102
103	return B_OK;
104}
105
106
107status_t
108DisassemblerX8664::GetNextInstruction(BString& line, target_addr_t& _address,
109	target_size_t& _size, bool& _breakpointAllowed)
110{
111	unsigned int size = ud_disassemble(fUdisData);
112	if (size < 1)
113		return B_ENTRY_NOT_FOUND;
114
115	target_addr_t address = ud_insn_off(fUdisData);
116
117	char buffer[256];
118	snprintf(buffer, sizeof(buffer), "0x%016" B_PRIx64 ": %16.16s  %s", address,
119		ud_insn_hex(fUdisData), ud_insn_asm(fUdisData));
120			// TODO: Resolve symbols!
121
122	line = buffer;
123	_address = address;
124	_size = size;
125	_breakpointAllowed = true;
126		// TODO: Implement (rep!)!
127
128	return B_OK;
129}
130
131
132status_t
133DisassemblerX8664::GetPreviousInstruction(target_addr_t nextAddress,
134	target_addr_t& _address, target_size_t& _size)
135{
136	if (nextAddress < fAddress || nextAddress > fAddress + fCodeSize)
137		return B_BAD_VALUE;
138
139	// loop until hitting the last instruction
140	while (true) {
141		target_size_t size = ud_disassemble(fUdisData);
142		if (size < 1)
143			return B_ENTRY_NOT_FOUND;
144
145		target_addr_t address = ud_insn_off(fUdisData);
146		if (address + size == nextAddress) {
147			_address = address;
148			_size = size;
149			return B_OK;
150		}
151	}
152}
153
154
155status_t
156DisassemblerX8664::GetNextInstructionInfo(InstructionInfo& _info,
157	CpuState* state)
158{
159	unsigned int size = ud_disassemble(fUdisData);
160	if (size < 1)
161		return B_ENTRY_NOT_FOUND;
162
163	target_addr_t address = ud_insn_off(fUdisData);
164
165	instruction_type type = INSTRUCTION_TYPE_OTHER;
166	target_addr_t targetAddress = 0;
167
168	ud_mnemonic_code mnemonic = ud_insn_mnemonic(fUdisData);
169	if (mnemonic == UD_Icall)
170		type = INSTRUCTION_TYPE_SUBROUTINE_CALL;
171	else if (mnemonic == UD_Ijmp)
172		type = INSTRUCTION_TYPE_JUMP;
173	if (state != NULL)
174		targetAddress = GetInstructionTargetAddress(state);
175
176	char buffer[256];
177	snprintf(buffer, sizeof(buffer), "0x%016" B_PRIx64 ": %16.16s  %s", address,
178		ud_insn_hex(fUdisData), ud_insn_asm(fUdisData));
179			// TODO: Resolve symbols!
180
181	if (!_info.SetTo(address, targetAddress, size, type, true, buffer))
182		return B_NO_MEMORY;
183
184	return B_OK;
185}
186
187
188target_addr_t
189DisassemblerX8664::GetInstructionTargetAddress(CpuState* state) const
190{
191	ud_mnemonic_code mnemonic = ud_insn_mnemonic(fUdisData);
192	if (mnemonic != UD_Icall && mnemonic != UD_Ijmp)
193		return 0;
194
195	CpuStateX8664* x64State = dynamic_cast<CpuStateX8664*>(state);
196	if (x64State == NULL)
197		return 0;
198
199	target_addr_t targetAddress = 0;
200	const struct ud_operand* op = ud_insn_opr(fUdisData, 0);
201	switch (op->type) {
202		case UD_OP_REG:
203		{
204			targetAddress = x64State->IntRegisterValue(
205				RegisterNumberFromUdisIndex(op->base));
206			targetAddress += op->offset;
207		}
208		break;
209		case UD_OP_MEM:
210		{
211			targetAddress = x64State->IntRegisterValue(
212				RegisterNumberFromUdisIndex(op->base));
213			targetAddress += x64State->IntRegisterValue(
214				RegisterNumberFromUdisIndex(op->index))
215				* op->scale;
216			off_t offset = 0;
217			switch (op->offset) {
218				case 8:
219					offset = op->lval.sbyte;
220					break;
221				case 16:
222					offset = op->lval.sword;
223					break;
224				case 32:
225					offset = op->lval.sdword;
226					break;
227				case 64:
228					offset = op->lval.sqword;
229					break;
230			}
231			targetAddress += offset;
232		}
233		break;
234		case UD_OP_JIMM:
235		{
236			targetAddress = ud_insn_off(fUdisData) + ud_insn_len(fUdisData);
237			if (op->size == 32)
238				targetAddress += op->lval.sdword;
239			else
240				targetAddress += op->lval.sqword;
241		}
242		break;
243
244		case UD_OP_IMM:
245		case UD_OP_CONST:
246		{
247			if (op->size == 32)
248				targetAddress = op->lval.udword;
249			else if (op->size == 64)
250				targetAddress = op->lval.uqword;
251		}
252		break;
253
254		default:
255		break;
256	}
257
258	return targetAddress;
259}
260