move some files, fix winelib build
[ia32rtools.git] / tools / translate.c
index 2871c42..eeea0ac 100644 (file)
@@ -126,6 +126,7 @@ struct parsed_opr {
   unsigned int type_from_var:1; // .. in header, sometimes wrong
   unsigned int size_mismatch:1; // type override differs from C
   unsigned int size_lt:1;  // type override is larger than C
+  unsigned int had_ds:1;   // had ds: prefix
   int reg;
   unsigned int val;
   char name[256];
@@ -147,7 +148,7 @@ struct parsed_op {
 };
 
 // datap:
-// OP_CALL - ptr to parsed_proto
+// OP_CALL - parser proto hint (str), ptr to struct parsed_proto
 // (OPF_CC) - point to one of (OPF_FLAGS) that affects cc op
 
 struct parsed_equ {
@@ -562,8 +563,10 @@ static int parse_operand(struct parsed_opr *opr,
 
     if (label != NULL) {
       opr->type = OPT_LABEL;
-      if (IS_START(label, "ds:"))
+      if (IS_START(label, "ds:")) {
+        opr->had_ds = 1;
         label += 3;
+      }
       strcpy(opr->name, label);
       return wordc;
     }
@@ -604,8 +607,10 @@ static int parse_operand(struct parsed_opr *opr,
   if (wordc_in != 1)
     aerr("parse_operand 1 word expected\n");
 
-  if (IS_START(words[w], "ds:"))
+  if (IS_START(words[w], "ds:")) {
+    opr->had_ds = 1;
     memmove(words[w], words[w] + 3, strlen(words[w]) - 2);
+  }
   strcpy(opr->name, words[w]);
 
   if (words[w][0] == '[') {
@@ -1445,9 +1450,13 @@ static void stack_frame_access(struct parsed_op *po,
 static void check_func_pp(struct parsed_op *po,
   const struct parsed_proto *pp, const char *pfx)
 {
+  char buf[256];
+
   if (pp->argc_reg != 0) {
-    if (!g_allow_regfunc)
-      ferr(po, "%s: reg arg in arg-call unhandled yet\n", pfx);
+    if (/*!g_allow_regfunc &&*/ !pp->is_fastcall) {
+      pp_print(buf, sizeof(buf), pp);
+      ferr(po, "%s: unexpected reg arg in icall: %s\n", pfx, buf);
+    }
     if (pp->argc_stack > 0 && pp->argc_reg != 2)
       ferr(po, "%s: %d reg arg(s) with %d stack arg(s)\n",
         pfx, pp->argc_reg, pp->argc_stack);
@@ -1569,8 +1578,11 @@ static char *out_src_opr(char *buf, size_t buf_size,
       ferr(po, "lea from const?\n");
 
     printf_number(tmp1, sizeof(tmp1), popr->val);
-    snprintf(buf, buf_size, "%s%s",
-      simplify_cast_num(cast, popr->val), tmp1);
+    if (popr->val == 0 && strchr(cast, '*'))
+      snprintf(buf, buf_size, "NULL");
+    else
+      snprintf(buf, buf_size, "%s%s",
+        simplify_cast_num(cast, popr->val), tmp1);
     break;
 
   default:
@@ -2681,13 +2693,24 @@ static void output_std_flags(FILE *fout, struct parsed_op *po,
   }
 }
 
+static void output_pp_attrs(FILE *fout, const struct parsed_proto *pp,
+  int is_noreturn)
+{
+  if (pp->is_fastcall)
+    fprintf(fout, "__fastcall ");
+  else if (pp->is_stdcall && pp->argc_reg == 0)
+    fprintf(fout, "__stdcall ");
+  if (pp->is_noreturn || is_noreturn)
+    fprintf(fout, "noreturn ");
+}
+
 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;
+  struct parsed_proto *pp, *pp_tmp;
   struct parsed_data *pd;
   const char *tmpname;
   enum parsed_flag_op pfo;
@@ -2699,6 +2722,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   int regmask_save = 0;
   int regmask_arg = 0;
   int regmask_now = 0;
+  int regmask_init = 0;
   int regmask = 0;
   int pfomask = 0;
   int found = 0;
@@ -2718,10 +2742,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
     ferr(ops, "proto_parse failed for '%s'\n", funcn);
 
   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 ");
+  output_pp_attrs(fout, g_func_pp, g_ida_func_attr & IDAFA_NORETURN);
   fprintf(fout, "%s(", funcn);
 
   for (i = 0; i < g_func_pp->argc; i++) {
@@ -2731,8 +2752,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       // 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 ");
+      output_pp_attrs(fout, pp, 0);
       fprintf(fout, "*a%d)(", i + 1);
       for (j = 0; j < pp->argc; j++) {
         if (j > 0)
@@ -2885,6 +2905,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       continue;
 
     if (po->op == OP_CALL) {
+      pp = NULL;
+
       if (po->operand[0].type == OPT_LABEL) {
         tmpname = opr_name(po, 0);
         if (IS_START(tmpname, "loc_"))
@@ -2892,13 +2914,26 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         pp_c = proto_parse(fhdr, tmpname, 0);
         if (pp_c == NULL)
           ferr(po, "proto_parse failed for call '%s'\n", tmpname);
-        if (pp_c->is_fptr)
-          check_func_pp(po, pp_c, "fptr var call");
-        if (pp_c->is_noreturn)
-          po->flags |= OPF_TAIL;
 
         pp = proto_clone(pp_c);
         my_assert_not(pp, NULL);
+      }
+      else if (po->datap != NULL) {
+        pp = calloc(1, sizeof(*pp));
+        my_assert_not(pp, NULL);
+
+        ret = parse_protostr(po->datap, pp);
+        if (ret < 0)
+          ferr(po, "bad protostr supplied: %s\n", (char *)po->datap);
+        free(po->datap);
+        po->datap = NULL;
+      }
+
+      if (pp != NULL) {
+        if (pp->is_fptr)
+          check_func_pp(po, pp, "fptr var call");
+        if (pp->is_noreturn)
+          po->flags |= OPF_TAIL;
         po->datap = pp;
       }
       continue;
@@ -3046,25 +3081,6 @@ tailcall:
         ferr(po, "missing esp_adjust for vararg func '%s'\n",
           pp->name);
 
-      for (arg = 0; arg < pp->argc; arg++) {
-        if (pp->arg[arg].reg != NULL) {
-          reg = char_array_i(regs_r32,
-                  ARRAY_SIZE(regs_r32), pp->arg[arg].reg);
-          if (reg < 0)
-            ferr(ops, "arg '%s' is not a reg?\n", pp->arg[arg].reg);
-          regmask |= 1 << reg;
-        }
-#if 0
-        if (pp->arg[arg].fptr != NULL) {
-          // can't call functions with non-__cdecl callbacks yet
-          struct parsed_proto *pp_tmp;
-          pp_tmp = pp->arg[arg].fptr;
-          if (pp_tmp->argc_reg != 0)
-            ferr(po, "'%s' has a callback with reg-args\n", tmpname);
-        }
-#endif
-      }
-
       if (!pp->is_unresolved) {
         // since we know the args, collect them
         collect_call_args(po, i, pp, &regmask, &save_arg_vars,
@@ -3200,34 +3216,85 @@ tailcall:
         ferr(po, "NULL pp\n");
 
       if (pp->is_unresolved) {
-        int regmask_push = 0;
-        collect_call_args(po, i, pp, &regmask_push, &save_arg_vars,
+        int regmask_stack = 0;
+        collect_call_args(po, i, pp, &regmask, &save_arg_vars,
           i + opcnt * 2);
 
-        if (!((regmask_push & (1 << xCX))
-          && (regmask_push & (1 << xDX))))
+        // this is pretty rough guess:
+        // see ecx and edx were pushed (and not their saved versions)
+        for (arg = 0; arg < pp->argc; arg++) {
+          if (pp->arg[arg].reg != NULL)
+            continue;
+
+          tmp_op = pp->arg[arg].datap;
+          if (tmp_op == NULL)
+            ferr(po, "parsed_op missing for arg%d\n", arg);
+          if (tmp_op->argnum == 0 && tmp_op->operand[0].type == OPT_REG)
+            regmask_stack |= 1 << tmp_op->operand[0].reg;
+        }
+
+        if (!((regmask_stack & (1 << xCX))
+          && (regmask_stack & (1 << xDX))))
         {
           if (pp->argc_stack != 0
-           || ((regmask | regmask_arg) & (1 << xCX)))
+           || ((regmask | regmask_arg) & ((1 << xCX)|(1 << xDX))))
           {
             pp_insert_reg_arg(pp, "ecx");
+            pp->is_fastcall = 1;
+            regmask_init |= 1 << xCX;
             regmask |= 1 << xCX;
           }
           if (pp->argc_stack != 0
            || ((regmask | regmask_arg) & (1 << xDX)))
           {
             pp_insert_reg_arg(pp, "edx");
+            regmask_init |= 1 << xDX;
             regmask |= 1 << xDX;
           }
         }
-        regmask |= regmask_push;
+
+        // note: __cdecl doesn't fall into is_unresolved category
+        if (pp->argc_stack > 0)
+          pp->is_stdcall = 1;
+      }
+
+      for (arg = 0; arg < pp->argc; arg++) {
+        if (pp->arg[arg].reg != NULL) {
+          reg = char_array_i(regs_r32,
+                  ARRAY_SIZE(regs_r32), pp->arg[arg].reg);
+          if (reg < 0)
+            ferr(ops, "arg '%s' is not a reg?\n", pp->arg[arg].reg);
+          if (!(regmask & (1 << reg))) {
+            regmask_init |= 1 << reg;
+            regmask |= 1 << reg;
+          }
+        }
       }
 
       if (pp->is_fptr) {
+        if (pp->name[0] != 0) {
+          memmove(pp->name + 2, pp->name, strlen(pp->name) + 1);
+          memcpy(pp->name, "i_", 2);
+
+          // might be declared already
+          found = 0;
+          for (j = 0; j < i; j++) {
+            if (ops[j].op == OP_CALL && (pp_tmp = ops[j].datap)) {
+              if (pp_tmp->is_fptr && IS(pp->name, pp_tmp->name)) {
+                found = 1;
+                break;
+              }
+            }
+          }
+          if (found)
+            continue;
+        }
+        else
+          snprintf(pp->name, sizeof(pp->name), "icall%d", i);
+
         fprintf(fout, "  %s (", pp->ret_type.name);
-        if (pp->is_stdcall && pp->argc_reg == 0)
-          fprintf(fout, "__stdcall ");
-        fprintf(fout, "*icall%d)(", i);
+        output_pp_attrs(fout, pp, 0);
+        fprintf(fout, "*%s)(", pp->name);
         for (j = 0; j < pp->argc; j++) {
           if (j > 0)
             fprintf(fout, ", ");
@@ -3311,7 +3378,10 @@ tailcall:
   if (regmask_now) {
     for (reg = 0; reg < 8; reg++) {
       if (regmask_now & (1 << reg)) {
-        fprintf(fout, "  u32 %s;\n", regs_r32[reg]);
+        fprintf(fout, "  u32 %s", regs_r32[reg]);
+        if (regmask_init & (1 << reg))
+          fprintf(fout, " = 0");
+        fprintf(fout, ";\n");
         had_decl = 1;
       }
     }
@@ -3802,7 +3872,7 @@ tailcall:
         my_assert_not(pp, NULL);
 
         if (pp->is_fptr)
-          fprintf(fout, "  icall%d = %s;\n", i,
+          fprintf(fout, "  %s = %s;\n", pp->name,
             out_src_opr(buf1, sizeof(buf1), po, &po->operand[0],
               "(void *)", 0));
 
@@ -3827,15 +3897,10 @@ tailcall:
           }
         }
 
-        if (pp->is_fptr) {
-          fprintf(fout, "icall%d(", i);
-        }
-        else {
-          if (pp->name[0] == 0)
-            ferr(po, "missing pp->name\n");
-          fprintf(fout, "%s%s(", pp->name,
-            pp->has_structarg ? "_sa" : "");
-        }
+        if (pp->name[0] == 0)
+          ferr(po, "missing pp->name\n");
+        fprintf(fout, "%s%s(", pp->name,
+          pp->has_structarg ? "_sa" : "");
 
         for (arg = 0; arg < pp->argc; arg++) {
           if (arg > 0)
@@ -3872,14 +3937,14 @@ tailcall:
         }
 
         if (pp->is_unresolved) {
-          snprintf(buf3, sizeof(buf3), "unresoved %dreg ",
+          snprintf(buf3, sizeof(buf3), " unresoved %dreg",
             pp->argc_reg);
           strcat(g_comment, buf3);
         }
 
         if (po->flags & OPF_TAIL) {
           ret = 0;
-          if (i == opcnt - 1)
+          if (i == opcnt - 1 || pp->is_noreturn)
             ret = 0;
           else if (IS(pp->ret_type.name, "void"))
             ret = 1;
@@ -3889,18 +3954,18 @@ tailcall:
 
           if (ret) {
             if (!IS(g_func_pp->ret_type.name, "void")) {
-              if (!pp->is_noreturn)
-                ferr(po, "int func -> void func tailcall?\n");
-              strcat(g_comment, "tailcall noreturn");
+              ferr(po, "int func -> void func tailcall?\n");
             }
             else {
               fprintf(fout, "\n  return;");
-              strcat(g_comment, "^ tailcall");
+              strcat(g_comment, " ^ tailcall");
             }
           }
           else
-            strcat(g_comment, "tailcall");
+            strcat(g_comment, " tailcall");
         }
+        if (pp->is_noreturn)
+          strcat(g_comment, " noreturn");
         delayed_flag_op = NULL;
         last_arith_dst = NULL;
         break;
@@ -3962,7 +4027,10 @@ tailcall:
     }
 
     if (g_comment[0] != 0) {
-      fprintf(fout, "  // %s", g_comment);
+      char *p = g_comment;
+      while (my_isblank(*p))
+        p++;
+      fprintf(fout, "  // %s", p);
       g_comment[0] = 0;
       no_output = 0;
     }
@@ -4168,6 +4236,7 @@ int main(int argc, char *argv[])
   char line[256];
   char words[20][256];
   enum opr_lenmod lmod;
+  char *sctproto = NULL;
   int in_func = 0;
   int pending_endp = 0;
   int skip_func = 0;
@@ -4392,11 +4461,16 @@ parse_words:
       aerr("too many words\n");
 
     // alow asm patches in comments
-    if (*p == ';' && IS_START(p, "; sctpatch:")) {
-      p = sskip(p + 11);
-      if (*p == 0 || *p == ';')
-        continue;
-      goto parse_words; // lame
+    if (*p == ';') {
+      if (IS_START(p, "; sctpatch:")) {
+        p = sskip(p + 11);
+        if (*p == 0 || *p == ';')
+          continue;
+        goto parse_words; // lame
+      }
+      if (IS_START(p, "; sctproto:")) {
+        sctproto = strdup(p + 11);
+      }
     }
 
     if (wordc == 0) {
@@ -4509,8 +4583,7 @@ do_pending_endp:
         aerr("proc '%s' while in_func '%s'?\n",
           words[0], g_func);
       p = words[0];
-      if ((g_ida_func_attr & IDAFA_THUNK)
-       || bsearch(&p, rlist, rlist_len, sizeof(rlist[0]), cmpstringp))
+      if (bsearch(&p, rlist, rlist_len, sizeof(rlist[0]), cmpstringp))
         skip_func = 1;
       strcpy(g_func, words[0]);
       set_label(0, words[0]);
@@ -4526,6 +4599,13 @@ do_pending_endp:
         aerr("endp '%s' while in_func '%s'?\n",
           words[0], g_func);
 
+      if ((g_ida_func_attr & IDAFA_THUNK) && pi == 1
+        && ops[0].op == OP_JMP && ops[0].operand[0].had_ds)
+      {
+        // import jump
+        skip_func = 1;
+      }
+
       if (!skip_func && func_chunks_used) {
         // start processing chunks
         struct chunk_item *ci, key = { g_func, 0 };
@@ -4576,7 +4656,8 @@ do_pending_endp:
       continue;
     }
 
-    if (wordc > 1 && IS(words[1], "=")) {
+    if (wordc > 1 && IS(words[1], "="))
+    {
       if (wordc != 5)
         aerr("unhandled equ, wc=%d\n", wordc);
       if (g_eqcnt >= eq_alloc) {
@@ -4610,6 +4691,12 @@ do_pending_endp:
       aerr("too many ops\n");
 
     parse_op(&ops[pi], words, wordc);
+
+    if (sctproto != NULL) {
+      if (ops[pi].op == OP_CALL)
+        ops[pi].datap = sctproto;
+      sctproto = NULL;
+    }
     pi++;
   }