1/*
2 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2008, François Revol, revol@free.fr
4 * Copyright 2016, Rene Gollent, rene@gollent.com.
5 * Distributed under the terms of the MIT License.
6 */
7
8#include "DisassemblerX86.h"
9
10#include <new>
11
12#include "udis86.h"
13
14#include <OS.h>
15
16#include "CpuStateX86.h"
17#include "InstructionInfo.h"
18
19
20static uint8 RegisterNumberFromUdisIndex(int32 udisIndex)
21{
22	switch (udisIndex) {
23		case UD_R_RIP: return X86_REGISTER_EIP;
24		case UD_R_ESP: return X86_REGISTER_ESP;
25		case UD_R_EBP: return X86_REGISTER_EBP;
26
27		case UD_R_EAX: return X86_REGISTER_EAX;
28		case UD_R_EBX: return X86_REGISTER_EBX;
29		case UD_R_ECX: return X86_REGISTER_ECX;
30		case UD_R_EDX: return X86_REGISTER_EDX;
31
32		case UD_R_ESI: return X86_REGISTER_ESI;
33		case UD_R_EDI: return X86_REGISTER_EDI;
34
35		case UD_R_CS: return X86_REGISTER_CS;
36		case UD_R_DS: return X86_REGISTER_DS;
37		case UD_R_ES: return X86_REGISTER_ES;
38		case UD_R_FS: return X86_REGISTER_FS;
39		case UD_R_GS: return X86_REGISTER_GS;
40		case UD_R_SS: return X86_REGISTER_SS;
41	}
42
43	return X86_INT_REGISTER_END;
44}
45
46
47struct DisassemblerX86::UdisData : ud_t {
48};
49
50
51DisassemblerX86::DisassemblerX86()
52	:
53	fAddress(0),
54	fCode(NULL),
55	fCodeSize(0),
56	fUdisData(NULL)
57{
58}
59
60
61DisassemblerX86::~DisassemblerX86()
62{
63	delete fUdisData;
64}
65
66
67status_t
68DisassemblerX86::Init(target_addr_t address, const void* code, size_t codeSize)
69{
70	// unset old data
71	delete fUdisData;
72	fUdisData = NULL;
73
74	// set new data
75	fUdisData = new(std::nothrow) UdisData;
76	if (fUdisData == NULL)
77		return B_NO_MEMORY;
78
79	fAddress = address;
80	fCode = (const uint8*)code;
81	fCodeSize = codeSize;
82
83	// init udis
84	ud_init(fUdisData);
85	ud_set_input_buffer(fUdisData, (unsigned char*)fCode, fCodeSize);
86	ud_set_mode(fUdisData, 32);
87	ud_set_pc(fUdisData, (uint64_t)fAddress);
88	ud_set_syntax(fUdisData, UD_SYN_ATT);
89	ud_set_vendor(fUdisData, UD_VENDOR_INTEL);
90		// TODO: Set the correct vendor!
91
92	return B_OK;
93}
94
95
96status_t
97DisassemblerX86::GetNextInstruction(BString& line, target_addr_t& _address,
98	target_size_t& _size, bool& _breakpointAllowed)
99{
100	unsigned int size = ud_disassemble(fUdisData);
101	if (size < 1)
102		return B_ENTRY_NOT_FOUND;
103
104	uint32 address = (uint32)ud_insn_off(fUdisData);
105
106	char buffer[256];
107	snprintf(buffer, sizeof(buffer), "0x%08" B_PRIx32 ": %16.16s  %s", address,
108		ud_insn_hex(fUdisData), ud_insn_asm(fUdisData));
109			// TODO: Resolve symbols!
110
111	line = buffer;
112	_address = address;
113	_size = size;
114	_breakpointAllowed = true;
115		// TODO: Implement (rep!)!
116
117	return B_OK;
118}
119
120
121status_t
122DisassemblerX86::GetPreviousInstruction(target_addr_t nextAddress,
123	target_addr_t& _address, target_size_t& _size)
124{
125	if (nextAddress < fAddress || nextAddress > fAddress + fCodeSize)
126		return B_BAD_VALUE;
127
128	// loop until hitting the last instruction
129	while (true) {
130		unsigned int size = ud_disassemble(fUdisData);
131		if (size < 1)
132			return B_ENTRY_NOT_FOUND;
133
134		uint32 address = (uint32)ud_insn_off(fUdisData);
135		if (address + size == nextAddress) {
136			_address = address;
137			_size = size;
138			return B_OK;
139		}
140	}
141}
142
143
144status_t
145DisassemblerX86::GetNextInstructionInfo(InstructionInfo& _info,
146	CpuState* state)
147{
148	unsigned int size = ud_disassemble(fUdisData);
149	if (size < 1)
150		return B_ENTRY_NOT_FOUND;
151
152	uint32 address = (uint32)ud_insn_off(fUdisData);
153
154	instruction_type type = INSTRUCTION_TYPE_OTHER;
155	target_addr_t targetAddress = 0;
156
157	ud_mnemonic_code mnemonic = ud_insn_mnemonic(fUdisData);
158	if (mnemonic == UD_Icall)
159		type = INSTRUCTION_TYPE_SUBROUTINE_CALL;
160	else if (mnemonic == UD_Ijmp)
161		type = INSTRUCTION_TYPE_JUMP;
162	if (state != NULL)
163		targetAddress = GetInstructionTargetAddress(state);
164
165	char buffer[256];
166	snprintf(buffer, sizeof(buffer), "0x%08" B_PRIx32 ": %16.16s  %s", address,
167		ud_insn_hex(fUdisData), ud_insn_asm(fUdisData));
168			// TODO: Resolve symbols!
169
170	if (!_info.SetTo(address, targetAddress, size, type, true, buffer))
171		return B_NO_MEMORY;
172
173	return B_OK;
174}
175
176
177target_addr_t
178DisassemblerX86::GetInstructionTargetAddress(CpuState* state) const
179{
180	ud_mnemonic_code mnemonic = ud_insn_mnemonic(fUdisData);
181	if (mnemonic != UD_Icall && mnemonic != UD_Ijmp)
182		return 0;
183
184	CpuStateX86* x86State = dynamic_cast<CpuStateX86*>(state);
185	if (x86State == NULL)
186		return 0;
187
188	target_addr_t targetAddress = 0;
189	const struct ud_operand* op = ud_insn_opr(fUdisData, 0);
190	switch (op->type) {
191		case UD_OP_REG:
192		{
193			targetAddress = x86State->IntRegisterValue(
194				RegisterNumberFromUdisIndex(op->base));
195			targetAddress += op->offset;
196		}
197		break;
198		case UD_OP_MEM:
199		{
200			targetAddress = x86State->IntRegisterValue(
201				RegisterNumberFromUdisIndex(op->base));
202			targetAddress += x86State->IntRegisterValue(
203				RegisterNumberFromUdisIndex(op->index))
204				* op->scale;
205			if (op->offset != 0)
206				targetAddress += op->lval.sdword;
207		}
208		break;
209		case UD_OP_JIMM:
210		{
211			targetAddress = ud_insn_off(fUdisData)
212				+ op->lval.sdword + ud_insn_len(fUdisData);
213		}
214		break;
215
216		case UD_OP_IMM:
217		case UD_OP_CONST:
218		{
219			targetAddress = op->lval.udword;
220		}
221		break;
222
223		default:
224		break;
225	}
226
227	return targetAddress;
228}
229