various fixes
[ia32rtools.git] / tools / translate.c
index 7c7390e..7248839 100644 (file)
@@ -188,7 +188,7 @@ static struct parsed_equ *g_eqs;
 static int g_eqcnt;
 static char g_labels[MAX_OPS][32];
 static struct label_ref g_label_refs[MAX_OPS];
-static struct parsed_proto g_func_pp;
+static const struct parsed_proto *g_func_pp;
 static struct parsed_data *g_func_pd;
 static int g_func_pd_cnt;
 static char g_func[256];
@@ -469,6 +469,7 @@ static int guess_lmod_from_c_type(enum opr_lenmod *lmod,
 {
   static const char *dword_types[] = {
     "int", "_DWORD", "DWORD", "HANDLE", "HWND", "HMODULE",
+    "WPARAM", "LPARAM", "UINT",
   };
   static const char *word_types[] = {
     "uint16_t", "int16_t",
@@ -541,7 +542,7 @@ static int parse_operand(struct parsed_opr *opr,
   int *regmask, int *regmask_indirect,
   char words[16][256], int wordc, int w, unsigned int op_flags)
 {
-  struct parsed_proto pp;
+  const struct parsed_proto *pp;
   enum opr_lenmod tmplmod;
   unsigned long number;
   int ret, len;
@@ -672,16 +673,17 @@ static int parse_operand(struct parsed_opr *opr,
   // most likely var in data segment
   opr->type = OPT_LABEL;
 
-  ret = proto_parse(g_fhdr, opr->name, &pp);
-  if (ret == 0) {
-    if (pp.is_fptr) {
+  pp = proto_parse(g_fhdr, opr->name);
+  if (pp != NULL) {
+    if (pp->is_fptr) {
       opr->lmod = OPLM_DWORD;
       opr->is_ptr = 1;
     }
     else {
-      if (!guess_lmod_from_c_type(&tmplmod, &pp.type))
+      tmplmod = OPLM_UNSPEC;
+      if (!guess_lmod_from_c_type(&tmplmod, &pp->type))
         anote("unhandled C type '%s' for '%s'\n",
-          pp.type.name, opr->name);
+          pp->type.name, opr->name);
       
       if (opr->lmod == OPLM_UNSPEC)
         opr->lmod = tmplmod;
@@ -690,11 +692,10 @@ static int parse_operand(struct parsed_opr *opr,
         if (tmplmod < opr->lmod)
           opr->size_lt = 1;
       }
+      opr->is_ptr = pp->type.is_ptr;
     }
-    opr->is_ptr = pp.type.is_ptr;
-    opr->is_array = pp.type.is_array;
+    opr->is_array = pp->type.is_array;
   }
-  proto_release(&pp);
 
   if (opr->lmod == OPLM_UNSPEC)
     guess_lmod_from_name(opr);
@@ -1215,9 +1216,11 @@ static void stack_frame_access(struct parsed_op *po,
   if (offset > stack_ra)
   {
     arg_i = (offset - stack_ra - 4) / 4;
-    if (arg_i < 0 || arg_i >= g_func_pp.argc_stack)
+    if (arg_i < 0 || arg_i >= g_func_pp->argc_stack)
     {
-      if (g_func_pp.is_vararg && arg_i == g_func_pp.argc_stack && is_lea) {
+      if (g_func_pp->is_vararg
+          && arg_i == g_func_pp->argc_stack && is_lea)
+      {
         // should be va_list
         if (cast[0] == 0)
           cast = "(u32)";
@@ -1230,17 +1233,17 @@ static void stack_frame_access(struct parsed_op *po,
     if (ofs_reg[0] != 0)
       ferr(po, "offset reg on arg access?\n");
 
-    for (i = arg_s = 0; i < g_func_pp.argc; i++) {
-      if (g_func_pp.arg[i].reg != NULL)
+    for (i = arg_s = 0; i < g_func_pp->argc; i++) {
+      if (g_func_pp->arg[i].reg != NULL)
         continue;
       if (arg_s == arg_i)
         break;
       arg_s++;
     }
-    if (i == g_func_pp.argc)
+    if (i == g_func_pp->argc)
       ferr(po, "arg %d not in prototype?\n", arg_i);
 
-    popr->is_ptr = g_func_pp.arg[i].type.is_ptr;
+    popr->is_ptr = g_func_pp->arg[i].type.is_ptr;
 
     switch (popr->lmod)
     {
@@ -1290,10 +1293,10 @@ static void stack_frame_access(struct parsed_op *po,
     }
 
     // common problem
-    guess_lmod_from_c_type(&tmp_lmod, &g_func_pp.arg[i].type);
+    guess_lmod_from_c_type(&tmp_lmod, &g_func_pp->arg[i].type);
     if ((offset & 3) && tmp_lmod != OPLM_DWORD)
       ferr(po, "bp_arg arg/w offset %d and type '%s'\n",
-        offset, g_func_pp.arg[i].type.name);
+        offset, g_func_pp->arg[i].type.name);
   }
   else
   {
@@ -1351,8 +1354,15 @@ static void stack_frame_access(struct parsed_op *po,
 
 static void check_label_read_ref(struct parsed_op *po, const char *name)
 {
-  if (IS_START(name, "sub_"))
-    ferr(po, "func reference?\n");
+  const struct parsed_proto *pp;
+
+  pp = proto_parse(g_fhdr, name);
+  if (pp == NULL)
+    ferr(po, "proto_parse failed for ref '%s'\n", name);
+
+  // currently we can take __cdecl and __stdcall
+  if (pp->is_func && pp->argc_reg != 0)
+    ferr(po, "reg-arg func reference?\n");
 }
 
 static char *out_src_opr(char *buf, size_t buf_size,
@@ -2033,7 +2043,7 @@ static int scan_for_esp_adjust(int i, int opcnt, int *adj)
 
 static int collect_call_args(struct parsed_op *po, int i,
   struct parsed_proto *pp, int *save_arg_vars, int arg,
-  int need_op_saving, int branched)
+  int need_op_saving, int may_reuse)
 {
   struct parsed_proto *pp_tmp;
   struct label_ref *lr;
@@ -2051,14 +2061,18 @@ static int collect_call_args(struct parsed_op *po, int i,
   for (j = i; j >= 0 && arg < pp->argc; )
   {
     if (g_labels[j][0] != 0) {
-      branched = 1;
       lr = &g_label_refs[j];
       if (lr->next != NULL)
         need_op_saving = 1;
-      for (; lr->next; lr = lr->next)
+      for (; lr->next; lr = lr->next) {
+        if ((ops[lr->i].flags & (OPF_JMP|OPF_CC)) != OPF_JMP)
+          may_reuse = 1;
         ret |= collect_call_args(po, lr->i, pp, save_arg_vars,
-                 arg, need_op_saving, branched);
+                 arg, need_op_saving, may_reuse);
+      }
 
+      if ((ops[lr->i].flags & (OPF_JMP|OPF_CC)) != OPF_JMP)
+        may_reuse = 1;
       if (j > 0 && LAST_OP(j - 1)) {
         // follow last branch in reverse
         j = lr->i;
@@ -2066,7 +2080,7 @@ static int collect_call_args(struct parsed_op *po, int i,
       }
       need_op_saving = 1;
       ret |= collect_call_args(po, lr->i, pp, save_arg_vars,
-               arg, need_op_saving, branched);
+               arg, need_op_saving, may_reuse);
     }
     j--;
 
@@ -2075,7 +2089,7 @@ static int collect_call_args(struct parsed_op *po, int i,
       pp_tmp = ops[j].datap;
       if (pp_tmp == NULL)
         ferr(po, "arg collect hit unparsed call\n");
-      if (branched && pp_tmp->argc_stack > 0)
+      if (may_reuse && pp_tmp->argc_stack > 0)
         ferr(po, "arg collect %d/%d hit '%s' with %d stack args\n",
           arg, pp->argc, opr_name(&ops[j], 0), pp_tmp->argc_stack);
     }
@@ -2086,12 +2100,9 @@ static int collect_call_args(struct parsed_op *po, int i,
     else if (ops[j].op == OP_POP) {
       ferr(po, "arg collect %d/%d hit pop\n", arg, pp->argc);
     }
-    else if (LAST_OP(j)) {
-      break;
-    }
     else if ((ops[j].flags & (OPF_JMP|OPF_CC)) == (OPF_JMP|OPF_CC))
     {
-      branched = 1;
+      may_reuse = 1;
     }
     else if (ops[j].op == OP_PUSH && !(ops[j].flags & OPF_FARG))
     {
@@ -2115,10 +2126,10 @@ static int collect_call_args(struct parsed_op *po, int i,
       else if (ops[j].argnum == 0)
         ops[j].flags |= OPF_RMD;
 
-      // some PUSHes are reused by calls on multiple branches,
+      // some PUSHes are reused by different calls on other branches,
       // but that can't happen if we didn't branch, so they
       // can be removed from future searches (handles nested calls)
-      if (!branched)
+      if (!may_reuse)
         ops[j].flags |= OPF_FARG;
 
       // next arg
@@ -2147,6 +2158,7 @@ static void add_label_ref(struct label_ref *lr, int op_i)
 
   lr_new = calloc(1, sizeof(*lr_new));
   lr_new->i = op_i;
+  lr_new->next = lr->next;
   lr->next = lr_new;
 }
 
@@ -2155,6 +2167,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   struct parsed_op *po, *delayed_flag_op = NULL, *tmp_op;
   struct parsed_opr *last_arith_dst = NULL;
   char buf1[256], buf2[256], buf3[256], cast[64];
+  const struct parsed_proto *pp_c;
   struct parsed_proto *pp, *pp_tmp;
   struct parsed_data *pd;
   const char *tmpname;
@@ -2179,24 +2192,46 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 
   g_bp_frame = g_sp_frame = g_stack_fsz = 0;
 
-  ret = proto_parse(fhdr, funcn, &g_func_pp);
-  if (ret)
+  g_func_pp = proto_parse(fhdr, funcn);
+  if (g_func_pp == NULL)
     ferr(ops, "proto_parse failed for '%s'\n", funcn);
 
-  fprintf(fout, "%s ", g_func_pp.ret_type.name);
+  fprintf(fout, "%s ", g_func_pp->ret_type.name);
+  if (g_func_pp->is_stdcall && g_func_pp->argc_reg == 0)
+    fprintf(fout, "__stdcall ");
   if (g_ida_func_attr & IDAFA_NORETURN)
     fprintf(fout, "noreturn ");
   fprintf(fout, "%s(", funcn);
-  for (i = 0; i < g_func_pp.argc; i++) {
+
+  for (i = 0; i < g_func_pp->argc; i++) {
     if (i > 0)
       fprintf(fout, ", ");
-    fprintf(fout, "%s a%d", g_func_pp.arg[i].type.name, i + 1);
+    if (g_func_pp->arg[i].fptr != NULL) {
+      // func pointer..
+      pp = g_func_pp->arg[i].fptr;
+      fprintf(fout, "%s (", pp->ret_type.name);
+      if (pp->is_stdcall && pp->argc_reg == 0)
+        fprintf(fout, "__stdcall ");
+      fprintf(fout, "*a%d)(", i + 1);
+      for (j = 0; j < pp->argc; j++) {
+        if (j > 0)
+          fprintf(fout, ", ");
+        if (pp->arg[j].fptr)
+          ferr(ops, "nested fptr\n");
+        fprintf(fout, "%s", pp->arg[j].type.name);
+      }
+      fprintf(fout, ")");
+    }
+    else {
+      fprintf(fout, "%s a%d", g_func_pp->arg[i].type.name, i + 1);
+    }
   }
-  if (g_func_pp.is_vararg) {
+  if (g_func_pp->is_vararg) {
     if (i > 0)
       fprintf(fout, ", ");
     fprintf(fout, "...");
   }
+
   fprintf(fout, ")\n{\n");
 
   // pass1:
@@ -2336,7 +2371,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         }
       }
       if (pd == NULL)
-        ferr(po, "label '%s' not parsed?\n", buf1);
+        //ferr(po, "label '%s' not parsed?\n", buf1);
+        goto tailcall;
       if (pd->type != OPT_OFFSET)
         ferr(po, "label '%s' with non-offset data?\n", buf1);
 
@@ -2366,14 +2402,15 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
     if (po->bt_i != -1)
       continue;
 
-    if (po->operand[0].type == OPT_LABEL) {
+    if (po->operand[0].type == OPT_LABEL)
       // assume tail call
-      po->op = OP_CALL;
-      po->flags |= OPF_TAIL;
-      continue;
-    }
+      goto tailcall;
 
     ferr(po, "unhandled branch\n");
+
+tailcall:
+    po->op = OP_CALL;
+    po->flags |= OPF_TAIL;
   }
 
   // pass3:
@@ -2403,9 +2440,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
           pp->arg[arg].type.name = strdup("int");
       }
       else {
-        ret = proto_parse(fhdr, tmpname, pp);
-        if (ret)
+        pp_c = proto_parse(fhdr, tmpname);
+        if (pp_c == NULL)
           ferr(po, "proto_parse failed for call '%s'\n", tmpname);
+        pp = proto_clone(pp_c);
       }
 
       // look for and make use of esp adjust
@@ -2596,27 +2634,27 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
     fprintf(fout, "  union { u32 d[%d]; u16 w[%d]; u8 b[%d]; } sf;\n",
       (g_stack_fsz + 3) / 4, (g_stack_fsz + 1) / 2, g_stack_fsz);
 
-  if (g_func_pp.is_vararg)
+  if (g_func_pp->is_vararg)
     fprintf(fout, "  va_list ap;\n");
 
   // declare arg-registers
-  for (i = 0; i < g_func_pp.argc; i++) {
-    if (g_func_pp.arg[i].reg != NULL) {
+  for (i = 0; i < g_func_pp->argc; i++) {
+    if (g_func_pp->arg[i].reg != NULL) {
       reg = char_array_i(regs_r32,
-              ARRAY_SIZE(regs_r32), g_func_pp.arg[i].reg);
+              ARRAY_SIZE(regs_r32), g_func_pp->arg[i].reg);
       if (reg < 0)
-        ferr(ops, "arg '%s' is not a reg?\n", g_func_pp.arg[i].reg);
+        ferr(ops, "arg '%s' is not a reg?\n", g_func_pp->arg[i].reg);
 
       regmask_arg |= 1 << reg;
       fprintf(fout, "  u32 %s = (u32)a%d;\n",
-        g_func_pp.arg[i].reg, i + 1);
+        g_func_pp->arg[i].reg, i + 1);
       had_decl = 1;
     }
   }
 
   // declare other regs - special case for eax
   if (!((regmask | regmask_arg) & 1)
-   && !IS(g_func_pp.ret_type.name, "void"))
+   && !IS(g_func_pp->ret_type.name, "void"))
   {
     fprintf(fout, "  u32 eax = 0;\n");
     had_decl = 1;
@@ -2670,10 +2708,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   if (had_decl)
     fprintf(fout, "\n");
 
-  if (g_func_pp.is_vararg) {
-    if (g_func_pp.argc_stack == 0)
+  if (g_func_pp->is_vararg) {
+    if (g_func_pp->argc_stack == 0)
       ferr(ops, "vararg func without stack args?\n");
-    fprintf(fout, "  va_start(ap, a%d);\n", g_func_pp.argc);
+    fprintf(fout, "  va_start(ap, a%d);\n", g_func_pp->argc);
   }
 
   // output ops
@@ -2894,6 +2932,14 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
             op_to_c(po),
             out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+        if (pfomask & (1 << PFO_Z)) {
+          fprintf(fout, "\n  cond_z = (%s == 0);", buf1);
+          pfomask &= ~(1 << PFO_Z);
+        }
+        if (pfomask & (1 << PFO_S)) {
+          fprintf(fout, "\n  cond_s = ((s32)%s < 0);", buf1);
+          pfomask &= ~(1 << PFO_S);
+        }
         last_arith_dst = &po->operand[0];
         delayed_flag_op = NULL;
         break;
@@ -2944,10 +2990,19 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       case OP_SBB:
         assert_operand_cnt(2);
         propagate_lmod(po, &po->operand[0], &po->operand[1]);
-        fprintf(fout, "  %s %s= %s + cond_c;",
+        if (po->op == OP_SBB
+          && IS(po->operand[0].name, po->operand[1].name))
+        {
+          // avoid use of unitialized var
+          fprintf(fout, "  %s = -cond_c;",
+            out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
+        }
+        else {
+          fprintf(fout, "  %s %s= %s + cond_c;",
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
             op_to_c(po),
             out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+        }
         last_arith_dst = &po->operand[0];
         delayed_flag_op = NULL;
         break;
@@ -3087,9 +3142,11 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         }
         else if (!IS(pp->ret_type.name, "void")) {
           if (po->flags & OPF_TAIL) {
-            fprintf(fout, "return ");
-            if (g_func_pp.ret_type.is_ptr != pp->ret_type.is_ptr)
-              fprintf(fout, "(%s)", g_func_pp.ret_type.name);
+            if (!IS(g_func_pp->ret_type.name, "void")) {
+              fprintf(fout, "return ");
+              if (g_func_pp->ret_type.is_ptr != pp->ret_type.is_ptr)
+                fprintf(fout, "(%s)", g_func_pp->ret_type.name);
+            }
           }
           else {
             fprintf(fout, "eax = ");
@@ -3104,7 +3161,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         else {
           if (pp->name[0] == 0)
             ferr(po, "missing pp->name\n");
-          fprintf(fout, "%s(", pp->name);
+          fprintf(fout, "%s%s(", pp->name,
+            pp->has_structarg ? "_sa" : "");
         }
 
         for (arg = 0; arg < pp->argc; arg++) {
@@ -3143,9 +3201,16 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 
         if (po->flags & OPF_TAIL) {
           strcpy(g_comment, "tailcall");
-          if (IS(pp->ret_type.name, "void")
-           && !(g_ida_func_attr & IDAFA_NORETURN))
-          {
+          ret = 0;
+          if (i == opcnt - 1)
+            ret = 0;
+          else if (IS(pp->ret_type.name, "void"))
+            ret = 1;
+          else if (IS(g_func_pp->ret_type.name, "void"))
+            ret = 1;
+          // else already handled as 'return f()'
+
+          if (ret) {
             fprintf(fout, "\n  return;");
             strcpy(g_comment, "^ tailcall");
           }
@@ -3155,16 +3220,16 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         break;
 
       case OP_RET:
-        if (g_func_pp.is_vararg)
+        if (g_func_pp->is_vararg)
           fprintf(fout, "  va_end(ap);\n");
  
-        if (IS(g_func_pp.ret_type.name, "void")) {
+        if (IS(g_func_pp->ret_type.name, "void")) {
           if (i != opcnt - 1 || label_pending)
             fprintf(fout, "  return;");
         }
-        else if (g_func_pp.ret_type.is_ptr) {
+        else if (g_func_pp->ret_type.is_ptr) {
           fprintf(fout, "  return (%s)eax;",
-            g_func_pp.ret_type.name);
+            g_func_pp->ret_type.name);
         }
         else
           fprintf(fout, "  return eax;");
@@ -3259,13 +3324,11 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 
     if (ops[i].op == OP_CALL) {
       pp = ops[i].datap;
-      if (pp) {
+      if (pp)
         proto_release(pp);
-        free(pp);
-      }
     }
   }
-  proto_release(&g_func_pp);
+  g_func_pp = NULL;
 }
 
 static void set_label(int i, const char *name)