1 /*
2  *	HT Editor
3  *	htdisasm.cc
4  *
5  *	Copyright (C) 1999-2002 Stefan Weyergraf
6  *
7  *	This program is free software; you can redistribute it and/or modify
8  *	it under the terms of the GNU General Public License version 2 as
9  *	published by the Free Software Foundation.
10  *
11  *	This program is distributed in the hope that it will be useful,
12  *	but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *	GNU General Public License for more details.
15  *
16  *	You should have received a copy of the GNU General Public License
17  *	along with this program; if not, write to the Free Software
18  *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20 
21 #include <cstring>
22 
23 #include "cmds.h"
24 #include "htctrl.h"
25 #include "htdisasm.h"
26 #include "hthist.h"
27 #include "htiobox.h"
28 #include "htmenu.h"
29 #include "httag.h"
30 #include "snprintf.h"
31 #include "x86asm.h"
32 #include "x86dis.h"
33 #include "ppcdis.h"
34 
35 extern "C" {
36 #include "evalx.h"
37 #include "regex.h"
38 }
39 
htdisasm_init(Bounds * b,File * file,ht_format_group * group)40 ht_view *htdisasm_init(Bounds *b, File *file, ht_format_group *group)
41 {
42 	int t1632;
43 #if 1
44 	Assembler *assembler=new x86asm(X86_OPSIZE32, X86_ADDRSIZE32);
45 	x86dis *disassembler=new x86dis(X86_OPSIZE32, X86_ADDRSIZE32);
46 #else
47 	Assembler *assembler = NULL;
48 	Disassembler *disassembler = new PPCDisassembler(PPC_MODE_32);
49 #endif
50 	t1632 = 0;
51 
52 	ht_disasm_viewer *v=new ht_disasm_viewer();
53 	v->init(b, DESC_DISASM, VC_EDIT | VC_GOTO | VC_SEARCH, file, group, assembler, disassembler, t1632);
54 
55 	ht_disasm_sub *d=new ht_disasm_sub();
56 	d->init(file, 0, file->getSize(),
57 		disassembler, false, X86DIS_STYLE_OPTIMIZE_ADDR);
58 
59 	v->insertsub(d);
60 	return v;
61 }
62 
63 format_viewer_if htdisasm_if = {
64 	htdisasm_init,
65 	0
66 };
67 
68 /*
69  *	dialog_assemble
70  */
71 
opcode_compare(const char * a,const char * b)72 static int opcode_compare(const char *a, const char *b)
73 {
74 	int al = strlen(a);
75 	int bl = strlen(b);
76 	if (al > bl) return 1; else if (al < bl) return -1; else return strcmp(a, b);
77 }
78 
dialog_assemble(ht_format_viewer * f,viewer_pos vaddr,CPU_ADDR cpuaddr,Assembler * a,Disassembler * disasm,const char * default_str,int want_length)79 void dialog_assemble(ht_format_viewer *f, viewer_pos vaddr, CPU_ADDR cpuaddr, Assembler *a, Disassembler *disasm, const char *default_str, int want_length)
80 {
81 	char instr[257] = "";
82 	if (default_str) strcpy(instr, default_str);
83 	asm_insn *insn = a->alloc_insn();
84 	asm_code *ac = NULL;
85 	while (inputbox(a->get_name(), "~instruction:", instr, 255, HISTATOM_ASSEMBLER)) {
86 		if ((a->translate_str(insn, instr)) && (ac = a->encode(insn, 0, cpuaddr))) {
87 			break;
88 		} else {
89 			errorbox("%s: %s", a->get_name(), a->get_error_msg());
90 		}
91 	}
92 	if (ac) {
93 		bool ok=true;
94 		asm_code *chosen_ac = ac;
95 		if (ac->next) {
96 			// choose from list if ambigous
97 			Bounds b;
98 			b.w = 60;
99 			b.h = 15;
100 			center_bounds(&b);
101 			ht_dialog *dialog = new ht_dialog();
102 			dialog->init(&b, "choose opcode", FS_KILLER | FS_TITLE | FS_MOVE);
103 			b.assign(1, 0, 56, 1);
104 			ht_listbox_title *text = new ht_listbox_title();
105 			text->init(&b);
106 			text->setText(2, "opcode", "disassembly");
107 			dialog->insert(text);
108 			b.assign(1, 1, 56, 12);
109 			ht_text_listbox *list=new ht_text_listbox();
110 			list->init(&b, 2, 0);
111 			list->attachTitle(text);
112 			asm_code *ac2 = ac;
113 			uint aci = 0;
114 			int best = 0;
115 			while (ac2) {
116 				char s[1024], *tmp = s;
117 				for (int i=0; i < ac2->size; i++) {
118 					tmp += sprintf(tmp, "%02x ", ac2->data[i]);
119 				}
120 				if (best == 0 && want_length == ac2->size) {
121 					best = aci+1;
122 				}
123 				const char *tmp2;
124 				if (disasm) {
125 					dis_insn *o = disasm->decode((byte *)ac2->data, ac2->size, cpuaddr);
126 					tmp2 = disasm->strf(o, DIS_STYLE_HEX_NOZEROPAD+DIS_STYLE_HEX_ASMSTYLE, DISASM_STRF_SMALL_FORMAT);
127 				} else {
128 					tmp2 = "<no disassembler>";
129 				}
130 				list->insert_str(aci, s, tmp2);
131 				ac2 = ac2->next;
132 				aci++;
133 			}
134 			ht_text_listbox_sort_order so;
135 			so.col = 0;
136 			so.compare_func = opcode_compare;
137 			list->update();
138 			if (best) {
139 				list->gotoItemByPosition(best-1);
140 			}
141 			list->sort(1, &so);
142 			if (!best) {
143 				list->gotoItemByPosition(0);
144 			}
145 			dialog->insert(list);
146 			int r = dialog->run(0);
147 			ok = r;
148 			if (r == button_ok) {
149 				ht_listbox_data d;
150 				ViewDataBuf vdb(list, &d, sizeof d);
151 				ht_text_listbox_item *i = (ht_text_listbox_item *)d.data->cursor_ptr;
152 				asm_code *ac3 = ac;
153 				int ac3i = 0;
154 				while (ac3) {
155 					if (ac3i == i->id) {
156 						chosen_ac = ac3;
157 						break;
158 					}
159 					ac3 = ac3->next;
160 					ac3i++;
161 				}
162 			}
163 			dialog->done();
164 			delete dialog;
165 		}
166 		if (ok) {
167 			baseview->sendmsg(cmd_edit_mode_i, f->get_file(), NULL);
168 			if (f->get_file() && (f->get_file()->getAccessMode() & IOAM_WRITE)) {
169 				f->vwrite(vaddr, chosen_ac->data, chosen_ac->size);
170 			}
171 		}
172 	}
173 	free(insn);
174 }
175 
176 /*
177  *	CLASS ht_disasm_viewer
178  */
179 
init(Bounds * b,const char * desc,int caps,File * file,ht_format_group * format_group,Assembler * a,Disassembler * d,int t)180 void ht_disasm_viewer::init(Bounds *b, const char *desc, int caps, File *file, ht_format_group *format_group, Assembler *a, Disassembler *d, int t)
181 {
182 	ht_uformat_viewer::init(b, desc, caps, file, format_group);
183 	assem = a;
184 	disasm = d;
185 	op1632 = t;
186 }
187 
done()188 void ht_disasm_viewer::done()
189 {
190 	ht_uformat_viewer::done();
191 	delete assem;
192 	delete disasm;
193 }
194 
get_pindicator_str(char * buf,int max_len)195 int ht_disasm_viewer::get_pindicator_str(char *buf, int max_len)
196 {
197 	FileOfs o;
198 	if (get_current_offset(&o)) {
199 		return ht_snprintf(buf, max_len, " %s 0x%08qx/%qu ", edit() ? "edit" : "view", o, o);
200 	} else {
201 		return ht_snprintf(buf, max_len, " ? ");
202 	}
203 }
204 
get_vscrollbar_pos(int * pstart,int * psize)205 bool ht_disasm_viewer::get_vscrollbar_pos(int *pstart, int *psize)
206 {
207 	FileOfs s=file->getSize();
208 	if (s) {
209 		int z = MIN(size.h*16, s-(int)top.line_id.id1);
210 		return scrollbar_pos(top.line_id.id1, z, s, pstart, psize);
211 	}
212 	return false;
213 }
214 
handlemsg(htmsg * msg)215 void ht_disasm_viewer::handlemsg(htmsg *msg)
216 {
217 	switch (msg->msg) {
218 		case msg_contextmenuquery: {
219 			ht_static_context_menu *m=new ht_static_context_menu();
220 			m->init("~Local-Disasm");
221 			m->insert_entry("~Assemble", "Ctrl+A", cmd_disasm_call_assembler, K_Control_A, 1);
222 			// FIXME: wrong implementation
223 			m->insert_entry("~Toggle 16/32", NULL, cmd_disasm_toggle1632, 0, 1);
224 
225 			msg->msg = msg_retval;
226 			msg->data1.ptr = m;
227 			return;
228 		}
229 		case msg_get_scrollinfo: {
230 			switch (msg->data1.integer) {
231 				case gsi_hscrollbar: {
232 					gsi_scrollbar_t *p=(gsi_scrollbar_t*)msg->data2.ptr;
233 					if (!get_hscrollbar_pos(&p->pstart, &p->psize)) {
234 						p->pstart = 0;
235 						p->psize = 100;
236 					}
237 					clearmsg(msg);
238 					return;
239 				}
240 				case gsi_vscrollbar: {
241 					gsi_scrollbar_t *p=(gsi_scrollbar_t*)msg->data2.ptr;
242 					if (!get_vscrollbar_pos(&p->pstart, &p->psize)) {
243 						p->pstart = 0;
244 						p->psize = 100;
245 					}
246 					clearmsg(msg);
247 					return;
248 				}
249 			}
250 			break;
251 		}
252 		case msg_filesize_changed: {
253 			htmsg m;
254 			m.msg=msg_filesize_changed;
255 			m.type=mt_broadcast;
256 			sendsubmsg(&m);
257 
258 			// FIXME: hack
259 			uf_initialized=false;
260 			complete_init();
261 
262 			dirtyview();
263 			return;
264 		}
265 		case cmd_disasm_call_assembler: {
266 			viewer_pos current_pos;
267 			get_current_pos(&current_pos);
268 
269 			CPU_ADDR cpuaddr;
270 			cpuaddr.addr32.seg = 0;
271 			cpuaddr.addr32.offset = current_pos.u.line_id.id1;
272 
273 			assem->set_imm_eval_proc(NULL, NULL);
274 
275 			byte data[32];
276 			int datalen = vread(current_pos, data, sizeof data);
277 			dis_insn *o = disasm->decode(data, datalen, cpuaddr);
278 			const char *curinsn = disasm->strf(o, DIS_STYLE_HEX_NOZEROPAD+DIS_STYLE_HEX_ASMSTYLE, DISASM_STRF_SMALL_FORMAT);
279 			int want_length = disasm->getSize(o);
280 
281 			dialog_assemble(this, current_pos, cpuaddr, assem, disasm, curinsn, want_length);
282 
283 			clearmsg(msg);
284 			return;
285 		}
286 		case cmd_disasm_toggle1632: {
287 			// FIXME: very beautiful...
288 			op1632 ^= 1;
289 			if (op1632) {
290 				((x86asm *)assem)->opsize = X86_OPSIZE16;
291 				((x86asm *)assem)->addrsize = X86_ADDRSIZE16;
292 				((x86dis *)disasm)->opsize = X86_OPSIZE16;
293 				((x86dis *)disasm)->addrsize = X86_ADDRSIZE16;
294 			} else {
295 				((x86asm *)assem)->opsize = X86_OPSIZE32;
296 				((x86asm *)assem)->addrsize = X86_ADDRSIZE32;
297 				((x86dis *)disasm)->opsize = X86_OPSIZE32;
298 				((x86dis *)disasm)->addrsize = X86_ADDRSIZE32;
299 			}
300 			dirtyview();
301 			clearmsg(msg);
302 			return;
303 		}
304 	}
305 	ht_uformat_viewer::handlemsg(msg);
306 }
307 
get_disasm_sub()308 ht_disasm_sub *ht_disasm_viewer::get_disasm_sub()
309 {
310 	return (ht_disasm_sub*)cursor.sub;
311 }
312 
offset_to_pos(FileOfs ofs,viewer_pos * p)313 bool ht_disasm_viewer::offset_to_pos(FileOfs ofs, viewer_pos *p)
314 {
315 	p->u.sub = get_disasm_sub();
316 	p->u.line_id.id1 = ofs;
317 	p->u.line_id.id2 = 0;
318 	p->u.tag_idx = 0;
319 	return true;
320 }
321 
pos_to_offset(viewer_pos p,FileOfs * ofs)322 bool ht_disasm_viewer::pos_to_offset(viewer_pos p, FileOfs *ofs)
323 {
324 	*ofs = p.u.line_id.id1;
325 	return true;
326 }
327 
ref_sel(LINE_ID * id)328 bool ht_disasm_viewer::ref_sel(LINE_ID *id)
329 {
330 	return goto_offset(id->id1, true);
331 }
332 
qword_to_pos(uint64 q,viewer_pos * p)333 bool ht_disasm_viewer::qword_to_pos(uint64 q, viewer_pos *p)
334 {
335 	ht_linear_sub *s = get_disasm_sub();
336 	FileOfs ofs = q;
337 	clear_viewer_pos(p);
338 	p->u.sub = s;
339 	p->u.tag_idx = 0;
340 	return s->convert_ofs_to_id(ofs, &p->u.line_id);
341 }
342 
symbol_handler(eval_scalar * result,char * name)343 bool ht_disasm_viewer::symbol_handler(eval_scalar *result, char *name)
344 {
345 	if (strcmp(name, "$") == 0) {
346 		FileOfs ofs;
347 		if (!pos_to_offset(*(viewer_pos*)&cursor, &ofs)) return 0;
348 		scalar_create_int_q(result, ofs);
349 		return true;
350 	}
351 	return ht_uformat_viewer::symbol_handler(result, name);
352 }
353 
func(uint i,bool execute)354 const char *ht_disasm_viewer::func(uint i, bool execute)
355 {
356 	switch (i) {
357 		// FIXME: wrong implementation
358 		case 8:
359 			if (execute) sendmsg(cmd_disasm_toggle1632);
360 			return op1632 ? (char*)"use32" : (char*)"use16";
361 	}
362 	return ht_uformat_viewer::func(i, execute);
363 }
364 
365 /*
366  *	CLASS ht_disasm_sub
367  */
368 
init(File * f,FileOfs ofs,int size,Disassembler * u,bool own_u,int ds)369 void ht_disasm_sub::init(File *f, FileOfs ofs, int size, Disassembler *u, bool own_u, int ds)
370 {
371 	ht_linear_sub::init(f, ofs, size);
372 	disasm = u;
373 	own_disasm = own_u;
374 	display_style = ds;
375 }
376 
done()377 void ht_disasm_sub::done()
378 {
379 	if (own_disasm) {
380 		delete disasm;
381 	}
382 	ht_linear_sub::done();
383 }
384 
convert_ofs_to_id(const FileOfs offset,LINE_ID * line_id)385 bool ht_disasm_sub::convert_ofs_to_id(const FileOfs offset, LINE_ID *line_id)
386 {
387 	if ((offset >= fofs) && (offset < fofs+fsize)) {
388 		line_id->id1=offset;
389 		line_id->id2=0;
390 		return true;
391 	}
392 	return false;
393 }
394 
convert_id_to_ofs(const LINE_ID line_id,FileOfs * offset)395 bool ht_disasm_sub::convert_id_to_ofs(const LINE_ID line_id, FileOfs *offset)
396 {
397 	*offset = line_id.id1;
398 	return true;
399 }
400 
diasm_addr_sym_func(CPU_ADDR Addr,int * symstrlen,void * context)401 static char *diasm_addr_sym_func(CPU_ADDR Addr, int *symstrlen, void *context)
402 {
403 	ht_disasm_sub *sub = (ht_disasm_sub *) context;
404 	static char buf[120];
405 	LINE_ID line_id;
406 	sub->first_line_id(&line_id);
407 	if (Addr.addr32.offset >= line_id.id1) {
408 		sub->last_line_id(&line_id);
409 		if (Addr.addr32.offset <= line_id.id1) {
410 			char buf2[60];
411 			ht_snprintf(buf2, sizeof buf2, "0x%x", Addr.addr32.offset);
412 			char *b = tag_make_ref(buf, sizeof buf-1, Addr.addr32.offset, 0, 0, 0, buf2);
413 			*b = 0;
414 			if (symstrlen) *symstrlen = b-buf;
415 			return buf;
416 		}
417 	}
418 	return NULL;
419 }
420 
getline(char * line,int maxlen,const LINE_ID line_id)421 bool ht_disasm_sub::getline(char *line, int maxlen, const LINE_ID line_id)
422 {
423 	if (line_id.id2) return false;
424 	uint64 ofs = line_id.id1;
425 	byte buf[16];
426 	int c = MIN(16, sint64(fofs+fsize-ofs));
427 	if (c <= 0) return false;
428 	file->seek(ofs);
429 	c = file->read(buf, c);
430 	CPU_ADDR caddr;
431 	caddr.addr32.seg = 0;
432 	caddr.addr32.offset = ofs;
433 	const char *s;
434 	char *l = line;
435 	if (c) {
436 		dis_insn *insn = disasm->decode(buf, c, caddr);
437 		addr_sym_func_context = this;
438 		addr_sym_func = &diasm_addr_sym_func;
439 		s = disasm->str(insn, display_style);
440 		addr_sym_func = NULL;
441 		c = disasm->getSize(insn);
442 	} else {
443 		s = "db ?";
444 		c = 0;
445 	}
446 	l += ht_snprintf(l, maxlen, "%08qx ", ofs);
447 	for (int i=0; i<15; i++) {
448 		if (i<c) {
449 			l=tag_make_edit_byte(l, maxlen, ofs+i);
450 		} else {
451 			*l++=' ';
452 			*l++=' ';
453 		}
454 	}
455 	*l++=' ';
456 	tag_strcpy(l, maxlen, s);
457 	return true;
458 }
459 
first_line_id(LINE_ID * line_id)460 void ht_disasm_sub::first_line_id(LINE_ID *line_id)
461 {
462 	clear_line_id(line_id);
463 	line_id->id1 = fofs;
464 }
465 
last_line_id(LINE_ID * line_id)466 void ht_disasm_sub::last_line_id(LINE_ID *line_id)
467 {
468 	clear_line_id(line_id);
469 	line_id->id1 = fofs+fsize-1;
470 }
471 
prev_line_id(LINE_ID * line_id,int n)472 int ht_disasm_sub::prev_line_id(LINE_ID *line_id, int n)
473 {
474 	if (line_id->id2) return 0;
475 	uint32 *ofs=&line_id->id1;
476 	int min_length;
477 	int max_length;
478 	int min_look_ahead;
479 	int avg_look_ahead;
480 	int addr_align;
481 	disasm->getOpcodeMetrics(min_length, max_length, min_look_ahead, avg_look_ahead, addr_align);
482 
483 	unsigned char buf[avg_look_ahead*50], *bufp=buf;
484 	int offsets[avg_look_ahead*50];
485 	int *of=offsets;
486 	int r=n<6 ? 6*avg_look_ahead : n*avg_look_ahead;
487 	if (r > (int)sizeof buf) r = sizeof buf;
488 	uint32 o=*ofs-r;
489 	int c=r, d;
490 	int s;
491 	if (*ofs<fofs+r) {
492 		c-=fofs-o;
493 		o=fofs;
494 	}
495 	if (o+c>fofs+fsize) {
496 		c=fofs+fsize-o;
497 	}
498 	file->seek(o);
499 	d=file->read(buf, c);
500 
501 	CPU_ADDR caddr;
502 	caddr.addr32.seg = 0;
503 	caddr.addr32.offset = 0;
504 	do {
505 		if (d>0) {
506 			dis_insn *insn=disasm->decode(bufp, d, caddr);
507 			s=disasm->getSize(insn);
508 /*			if (s!=4) {
509 				insn=disasm->decode(bufp, d, caddr);
510 			}
511 			assert(s==4);*/
512 			d-=s;
513 		} else {
514 			s=1;
515 		}
516 		*(of++)=o;
517 		o+=s;
518 		bufp+=s;
519 	} while (o<=*ofs);
520 	if (of-n-1<offsets) {
521 		*ofs=*(offsets);
522 		return of-offsets-1;
523 	} else {
524 		*ofs=*(of-n-1);
525 		return n;
526 	}
527 }
528 
next_line_id(LINE_ID * line_id,int n)529 int ht_disasm_sub::next_line_id(LINE_ID *line_id, int n)
530 {
531 	if (line_id->id2) return 0;
532 	uint32 *ofs = &line_id->id1;
533 	unsigned char buf[15];
534 	int c=0, s;
535 	uint z;
536 	CPU_ADDR caddr;
537 	caddr.addr32.seg = 0;
538 	caddr.addr32.offset = 0;
539 	while (n--) {
540 		z=MIN(15, (uint)(fofs+fsize-*ofs));
541 		file->seek(*ofs);
542 		z=file->read(buf, z);
543 		if (z) {
544 			dis_insn *insn=disasm->decode(buf, z, caddr);
545 			s=disasm->getSize(insn);
546 		} else {
547 			s=1;
548 		}
549 		if (*ofs+s>fofs+fsize-1) return c;
550 		*ofs+=s;
551 		c++;
552 	}
553 	return c;
554 }
555 
556