2eb83afa45f3f467adee8b8c641be73c4ab658ef
[ia32rtools.git] / plugin / saveasm.cpp
1 #define NO_OBSOLETE_FUNCS
2 #include <ida.hpp>
3 #include <idp.hpp>
4 #include <bytes.hpp>
5 #include <loader.hpp>
6 #include <kernwin.hpp>
7
8 #include <name.hpp>
9 #include <frame.hpp>
10 #include <struct.hpp>
11 #include <auto.hpp>
12 #include <intel.hpp>
13
14 #define IS_START(w, y) !strncmp(w, y, strlen(y))
15 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
16
17 // non-local branch targets
18 static ea_t *nonlocal_bt;
19 static int nonlocal_bt_alloc;
20 static int nonlocal_bt_cnt;
21
22 //--------------------------------------------------------------------------
23 static int idaapi init(void)
24 {
25   return PLUGIN_OK;
26 }
27
28 //--------------------------------------------------------------------------
29 static void idaapi term(void)
30 {
31   if (nonlocal_bt != NULL) {
32     free(nonlocal_bt);
33     nonlocal_bt = NULL;
34   }
35   nonlocal_bt_alloc = 0;
36 }
37
38 //--------------------------------------------------------------------------
39
40 static const char *reserved_names[] = {
41   "name",
42   "offset",
43 };
44
45 static int is_name_reserved(const char *name)
46 {
47   int i;
48   for (i = 0; i < ARRAY_SIZE(reserved_names); i++)
49     if (strcasecmp(name, reserved_names[i]) == 0)
50       return 1;
51
52   return 0;
53 }
54
55 static int nonlocal_bt_cmp(const void *p1, const void *p2)
56 {
57   const ea_t *e1 = (const ea_t *)p1, *e2 = (const ea_t *)p2;
58   return *e1 - *e2;
59 }
60
61 static void nonlocal_add(ea_t ea)
62 {
63   if (nonlocal_bt_cnt >= nonlocal_bt_alloc) {
64     nonlocal_bt_alloc += nonlocal_bt_alloc * 2 + 64;
65     nonlocal_bt = (ea_t *)realloc(nonlocal_bt,
66       nonlocal_bt_alloc * sizeof(nonlocal_bt[0]));
67     if (nonlocal_bt == NULL) {
68       msg("OOM\n");
69       return;
70     }
71   }
72   nonlocal_bt[nonlocal_bt_cnt++] = ea;
73 }
74
75 static void do_def_line(char *buf, size_t buf_size, const char *line)
76 {
77   char *endp = NULL;
78   ea_t ea, *ea_ret;
79   int len;
80
81   tag_remove(line, buf, buf_size); // remove color codes
82   len = strlen(buf);
83   if (len < 9) {
84     buf[0] = 0;
85     return;
86   }
87   memmove(buf, buf + 9, len - 9 + 1); // rm address
88
89   if (IS_START(buf, "loc_")) {
90     ea = strtoul(buf + 4, &endp, 16);
91     if (ea != 0 && *endp == ':') {
92       ea_ret = (ea_t *)bsearch(&ea, nonlocal_bt, nonlocal_bt_cnt,
93         sizeof(nonlocal_bt[0]), nonlocal_bt_cmp);
94       if (ea_ret != 0) {
95         if (endp[1] != ' ')
96           msg("no trailing blank in '%s'\n", buf);
97         else
98           endp[1] = ':';
99       }
100     }
101   }
102 }
103
104 static void idaapi run(int /*arg*/)
105 {
106   // isEnabled(ea) // address belongs to disassembly
107   // ea_t ea = get_screen_ea();
108   // foo = DecodeInstruction(ScreenEA());
109   FILE *fout = NULL;
110   int fout_line = 0;
111   char buf[MAXSTR];
112   int drop_large = 0;
113   struc_t *frame;
114   func_t *func;
115   ea_t ui_ea_block = 0, ea_size;
116   ea_t tmp_ea, target_ea;
117   ea_t ea;
118   int i, o, m, n;
119   int ret;
120   char *p;
121
122   nonlocal_bt_cnt = 0;
123
124   // 1st pass: walk through all funcs
125   func = get_func(inf.minEA);
126   while (func != NULL)
127   {
128     func_tail_iterator_t fti(func);
129     if (!fti.main()) {
130       msg("%x: func_tail_iterator_t main failed\n", ea);
131       return;
132     }
133     const area_t &f_area = fti.chunk();
134     ea = f_area.startEA;
135
136     // rename global syms which conflict with frame member names
137     frame = get_frame(func);
138     if (frame != NULL)
139     {
140       for (m = 0; m < (int)frame->memqty; m++)
141       {
142         ret = get_member_name(frame->members[m].id, buf, sizeof(buf));
143         if (ret <= 0) {
144           msg("%x: member has no name?\n", ea);
145           return;
146         }
147         if (buf[0] == ' ') // what's this?
148           continue;
149         if (IS_START(buf, "arg_") || IS_START(buf, "var_"))
150           continue;
151
152         if (is_name_reserved(buf)) {
153           msg("%x: renaming '%s'\n", ea, buf);
154           qstrncat(buf, "_", sizeof(buf));
155           ret = set_member_name(frame, frame->members[m].soff, buf);
156           if (!ret) {
157             msg("%x: renaming failed\n", ea);
158             return;
159           }
160         }
161
162         tmp_ea = get_name_ea(ea, buf);
163         if (tmp_ea == 0 || tmp_ea == ~0)
164           continue;
165
166         msg("%x: from %x: renaming '%s'\n", tmp_ea, ea, buf);
167         qstrncat(buf, "_g", sizeof(buf));
168         set_name(tmp_ea, buf);
169       }
170     }
171
172     func = get_next_func(ea);
173   }
174
175   // 2nd pass over whole .text segment
176   for (ea = inf.minEA; ea != BADADDR; ea = next_head(ea, inf.maxEA))
177   {
178     segment_t *seg = getseg(ea);
179     if (!seg || seg->type != SEG_CODE)
180       break;
181
182     flags_t ea_flags = get_flags_novalue(ea);
183     func = get_func(ea);
184     if (isCode(ea_flags))
185     {
186       if (!decode_insn(ea)) {
187         msg("%x: decode_insn() failed\n", ea);
188         continue;
189       }
190
191       // find non-local branches
192       if ((cmd.itype == NN_jmp || insn_jcc())
193         && cmd.Operands[0].type == o_near)
194       {
195         target_ea = cmd.Operands[0].addr;
196         if (func == NULL)
197           nonlocal_add(target_ea);
198         else {
199           ret = get_func_chunknum(func, target_ea);
200           if (ret != 0) {
201             // a jump to another func or chunk
202             // check if it lands on func start
203             if (!isFunc(get_flags_novalue(target_ea)))
204               nonlocal_add(target_ea);
205           }
206         }
207       }
208     }
209     else { // not code
210       if (func == NULL && isOff0(ea_flags)) {
211         ea_size = get_item_size(ea);
212         for (tmp_ea = 0; tmp_ea < ea_size; tmp_ea += 4)
213           nonlocal_add(get_long(ea + tmp_ea));
214       }
215     }
216   }
217
218   if (nonlocal_bt_cnt > 1) {
219     qsort(nonlocal_bt, nonlocal_bt_cnt,
220       sizeof(nonlocal_bt[0]), nonlocal_bt_cmp);
221   }
222
223   char *fname = askfile_c(1, NULL, "Save asm file");
224   if (fname == NULL)
225     return;
226   fout = qfopen(fname, "w");
227   if (fout == NULL) {
228     msg("couldn't open '%s'\n", fname);
229     return;
230   }
231
232   show_wait_box("Saving..");
233
234   // deal with the beginning
235   ea = inf.minEA;
236   int flags = 0; // calc_default_idaplace_flags();
237   linearray_t ln(&flags);
238   idaplace_t pl;
239   pl.ea = ea;
240   pl.lnnum = 0;
241   ln.set_place(&pl);
242   n = ln.get_linecnt();
243   for (i = 0; i < n - 1; i++) {
244     do_def_line(buf, sizeof(buf), ln.down());
245     if (strstr(buf, "include"))
246       continue;
247
248     fout_line++;
249     qfprintf(fout, "%s\n", buf);
250     p = strstr(buf, ".mmx");
251     if (p != NULL) {
252       memcpy(p, ".xmm", 4);
253       fout_line++;
254       qfprintf(fout, "%s\n", buf);
255     }
256   }
257
258   for (;;)
259   {
260     drop_large = 0;
261
262     if ((ea >> 14) != ui_ea_block) {
263       ui_ea_block = ea >> 14;
264       showAddr(ea);
265       if (wasBreak())
266         break;
267     }
268
269     segment_t *seg = getseg(ea);
270     if (!seg || seg->type != SEG_CODE)
271       goto pass;
272     if (!decode_insn(ea))
273       goto pass;
274
275     // note: decode_insn() picks up things like dd, size is then weird
276     //cmd_size = cmd.size;
277
278     for (o = 0; o < UA_MAXOP; o++) {
279       if (cmd.Operands[o].type == o_void)
280         break;
281
282       if (cmd.Operands[o].type == o_mem
283         && cmd.Operands[o].specval_shorts.high == 0x21) // correct?
284       {
285         drop_large = 1;
286       }
287 #if 0
288       if (cmd.Operands[o].type == o_displ && cmd.Operands[o].reg == 5) {
289         member_t *m;
290
291         m = get_stkvar(cmd.Operands[o], cmd.Operands[o].addr, NULL);
292         if (m == NULL) {
293           msg("%x: no stkvar for offs %x\n",
294             ea, cmd.Operands[o].addr);
295           goto out;
296         }
297         if (get_struc_name(m->id, buf, sizeof(buf)) <= 0) {
298           msg("%x: stkvar with offs %x has no name?\n",
299             ea, cmd.Operands[o].addr);
300           goto out;
301         }
302         msg("%x: name '%s'\n", ea, buf);
303       }
304 #endif
305     }
306
307 pass:
308     do_def_line(buf, sizeof(buf), ln.down());
309     if (drop_large) {
310       p = strstr(buf, "large ");
311       if (p != NULL)
312         memmove(p, p + 6, strlen(p + 6) + 1);
313     }
314
315     fout_line++;
316     qfprintf(fout, "%s\n", buf);
317
318     // note: next_head skips some undefined stuff
319     ea = next_not_tail(ea); // correct?
320     if (ea == BADADDR)
321       break;
322
323     pl.ea = ea;
324     pl.lnnum = 0;
325     ln.set_place(&pl);
326     n = ln.get_linecnt();
327     for (i = 0; i < n - 1; i++)
328     {
329       fout_line++;
330       do_def_line(buf, sizeof(buf), ln.down());
331       qfprintf(fout, "%s\n", buf);
332     }
333   }
334
335   if (fout != NULL)
336     qfclose(fout);
337   if (fname != NULL)
338     qfree(fname);
339
340   hide_wait_box();
341   msg("%d lines saved.\n", fout_line);
342 }
343
344 //--------------------------------------------------------------------------
345
346 static const char comment[] = "Generate disassembly lines for one address";
347 static const char help[] = "Generate asm file\n";
348 static const char wanted_name[] = "Save asm";
349 static const char wanted_hotkey[] = "Ctrl-F6";
350
351 //--------------------------------------------------------------------------
352 //
353 //      PLUGIN DESCRIPTION BLOCK
354 //
355 //--------------------------------------------------------------------------
356 plugin_t PLUGIN =
357 {
358   IDP_INTERFACE_VERSION,
359   0,                    // plugin flags
360   init,                 // initialize
361   term,                 // terminate. this pointer may be NULL.
362   run,                  // invoke plugin
363   comment,              // long comment about the plugin
364                         // it could appear in the status line
365                         // or as a hint
366   help,                 // multiline help about the plugin
367   wanted_name,          // the preferred short name of the plugin
368   wanted_hotkey         // the preferred hotkey to run the plugin
369 };
370
371 // vim:ts=2:shiftwidth=2:expandtab