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(¤t_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