translate: ignore attrs from skipped funcs
[ia32rtools.git] / tools / translate.c
index 79413a2..1ae5c27 100644 (file)
@@ -96,6 +96,8 @@ enum op_op {
        OP_MOVS,
        OP_CMPS,
        OP_SCAS,
+       OP_RDTSC,
+       OP_CPUID,
        OP_STD,
        OP_CLD,
        OP_RET,
@@ -282,8 +284,9 @@ enum ida_func_attr {
 enum sct_func_attr {
   SCTFA_CLEAR_SF   = (1 << 0), // clear stack frame
   SCTFA_CLEAR_REGS = (1 << 1), // clear registers (mask)
-  SCTFA_RM_REGS    = (1 << 2), // don't emit regs
+  SCTFA_RM_REGS    = (1 << 2), // don't emit regs (mask)
   SCTFA_NOWARN     = (1 << 3), // don't try to detect problems
+  SCTFA_ARGFRAME   = (1 << 4), // copy all args to a struct, in order
 };
 
 enum x87_const {
@@ -376,6 +379,7 @@ enum x86_regs {
 };
 
 #define mxAX     (1 << xAX)
+#define mxBX     (1 << xBX)
 #define mxCX     (1 << xCX)
 #define mxDX     (1 << xDX)
 #define mxSP     (1 << xSP)
@@ -954,7 +958,7 @@ static const struct {
   { "repz",   OPF_REP|OPF_REPZ },
   { "repne",  OPF_REP|OPF_REPNZ },
   { "repnz",  OPF_REP|OPF_REPNZ },
-  { "lock",   OPF_LOCK }, // ignored for now..
+  { "lock",   OPF_LOCK },
 };
 
 #define OPF_CJMP_CC (OPF_JMP|OPF_CJMP|OPF_CC)
@@ -998,6 +1002,8 @@ static const struct {
   { "scasb",OP_SCAS,   0, 0, OPF_DATA|OPF_FLAGS },
   { "scasw",OP_SCAS,   0, 0, OPF_DATA|OPF_FLAGS },
   { "scasd",OP_SCAS,   0, 0, OPF_DATA|OPF_FLAGS },
+  { "rdtsc",OP_RDTSC,  0, 0, OPF_DATA },
+  { "cpuid",OP_CPUID,  0, 0, OPF_DATA },
   { "std",  OP_STD,    0, 0, OPF_DATA }, // special flag
   { "cld",  OP_CLD,    0, 0, OPF_DATA },
   { "add",  OP_ADD,    2, 2, OPF_DATA|OPF_FLAGS },
@@ -1325,6 +1331,16 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc)
     op->regmask_dst = op->regmask_src;
     break;
 
+  case OP_RDTSC:
+    op->regmask_dst = mxAX | mxDX;
+    break;
+
+  case OP_CPUID:
+    // for now, ignore ecx dep for eax={4,7,b,d}
+    op->regmask_src = mxAX;
+    op->regmask_dst = mxAX | mxBX | mxCX | mxDX;
+    break;
+
   case OP_LOOP:
     op->regmask_dst = 1 << xCX;
     // fallthrough
@@ -1926,6 +1942,7 @@ static int stack_frame_access(struct parsed_op *po,
   const char *prefix = "";
   const char *bp_arg = NULL;
   char ofs_reg[16] = { 0, };
+  char argname[8];
   int i, arg_i, arg_s;
   int unaligned = 0;
   int stack_ra = 0;
@@ -1956,7 +1973,7 @@ static int stack_frame_access(struct parsed_op *po,
         snprintf(buf, buf_size, "%sap", cast);
         return -1;
       }
-      ferr(po, "offset %d (%s,%d) doesn't map to any arg\n",
+      ferr(po, "offset 0x%x (%s,%d) doesn't map to any arg\n",
         offset, bp_arg, arg_i);
     }
     if (ofs_reg[0] != 0)
@@ -1975,17 +1992,20 @@ static int stack_frame_access(struct parsed_op *po,
     popr->is_ptr = g_func_pp->arg[i].type.is_ptr;
     retval = i;
 
+    snprintf(argname, sizeof(argname), "%sa%d",
+      g_sct_func_attr & SCTFA_ARGFRAME ? "af." : "", i + 1);
+
     switch (popr->lmod)
     {
     case OPLM_BYTE:
       if (is_lea)
         ferr(po, "lea/byte to arg?\n");
       if (is_src && (offset & 3) == 0)
-        snprintf(buf, buf_size, "%sa%d",
-          simplify_cast(cast, "(u8)"), i + 1);
+        snprintf(buf, buf_size, "%s%s",
+          simplify_cast(cast, "(u8)"), argname);
       else
-        snprintf(buf, buf_size, "%sBYTE%d(a%d)",
-          cast, offset & 3, i + 1);
+        snprintf(buf, buf_size, "%sBYTE%d(%s)",
+          cast, offset & 3, argname);
       break;
 
     case OPLM_WORD:
@@ -1996,18 +2016,18 @@ static int stack_frame_access(struct parsed_op *po,
         if (!is_src) {
           if (offset & 2)
             ferr(po, "problematic arg store\n");
-          snprintf(buf, buf_size, "%s((char *)&a%d + 1)",
-            simplify_cast(cast, "*(u16 *)"), i + 1);
+          snprintf(buf, buf_size, "%s((char *)&%s + 1)",
+            simplify_cast(cast, "*(u16 *)"), argname);
         }
         else
           ferr(po, "unaligned arg word load\n");
       }
       else if (is_src && (offset & 2) == 0)
-        snprintf(buf, buf_size, "%sa%d",
-          simplify_cast(cast, "(u16)"), i + 1);
+        snprintf(buf, buf_size, "%s%s",
+          simplify_cast(cast, "(u16)"), argname);
       else
-        snprintf(buf, buf_size, "%s%sWORD(a%d)",
-          cast, (offset & 2) ? "HI" : "LO", i + 1);
+        snprintf(buf, buf_size, "%s%sWORD(%s)",
+          cast, (offset & 2) ? "HI" : "LO", argname);
       break;
 
     case OPLM_DWORD:
@@ -2019,19 +2039,19 @@ static int stack_frame_access(struct parsed_op *po,
       if (offset & 3) {
         unaligned = 1;
         if (is_lea)
-          snprintf(buf, buf_size, "(u32)&a%d + %d",
-            i + 1, offset & 3);
+          snprintf(buf, buf_size, "(u32)&%s + %d",
+            argname, offset & 3);
         else if (!is_src)
           ferr(po, "unaligned arg store\n");
         else {
           // mov edx, [ebp+arg_4+2]; movsx ecx, dx
-          snprintf(buf, buf_size, "%s(a%d >> %d)",
-            prefix, i + 1, (offset & 3) * 8);
+          snprintf(buf, buf_size, "%s(%s >> %d)",
+            prefix, argname, (offset & 3) * 8);
         }
       }
       else {
-        snprintf(buf, buf_size, "%s%sa%d",
-          prefix, is_lea ? "&" : "", i + 1);
+        snprintf(buf, buf_size, "%s%s%s",
+          prefix, is_lea ? "&" : "", argname);
       }
       break;
 
@@ -2039,8 +2059,8 @@ static int stack_frame_access(struct parsed_op *po,
       ferr_assert(po, !(offset & 7));
       if (cast[0])
         prefix = cast;
-      snprintf(buf, buf_size, "%s%sa%d",
-        prefix, is_lea ? "&" : "", i + 1);
+      snprintf(buf, buf_size, "%s%s%s",
+        prefix, is_lea ? "&" : "", argname);
       break;
 
     default:
@@ -3525,6 +3545,11 @@ static const struct parsed_proto *try_recover_pp(
   char buf[256];
   char *p;
 
+  if (po->pp != NULL && (po->flags & OPF_DATA)) {
+    // hint given in asm
+    return po->pp;
+  }
+
   // maybe an arg of g_func?
   if (opr->type == OPT_REGMEM && is_stack_access(po, opr))
   {
@@ -3917,8 +3942,10 @@ static void resolve_branches_parse_calls(int opcnt)
           && IS(po->operand[0].name, g_labels[l]))
       {
         if (l == i + 1 && po->op == OP_JMP) {
-          // yet another alignment type..
-          po->flags |= OPF_RMD|OPF_DONE;
+          // yet another alignment type...
+          po->flags |= OPF_RMD | OPF_DONE;
+          po->flags &= ~OPF_JMP;
+          po->op = OP_NOP;
           break;
         }
         add_label_ref(&g_label_refs[l], i);
@@ -4146,9 +4173,72 @@ static void eliminate_seh_calls(int opcnt)
   eliminate_seh_finally(opcnt);
 }
 
+// check for prologue of many pushes and epilogue with pops
+static void check_simple_sequence(int opcnt, int *fsz)
+{
+  int found = 0;
+  int seq_len;
+  int seq_p;
+  int seq[4];
+  int reg;
+  int i, j;
+
+  for (i = 0; i < opcnt && i < ARRAY_SIZE(seq); i++) {
+    if (ops[i].op != OP_PUSH || ops[i].operand[0].type != OPT_REG)
+      break;
+    reg = ops[i].operand[0].reg;
+    if (reg != xBX && reg != xSI && reg != xDI && reg != xBP)
+      break;
+    for (j = 0; j < i; j++)
+      if (seq[j] == reg)
+        break;
+    if (j != i)
+      // probably something else is going on here
+      break;
+    seq[i] = reg;
+  }
+  seq_len = i;
+  if (seq_len == 0)
+    return;
+
+  for (; i < opcnt && seq_len > 0; i++) {
+    if (!(ops[i].flags & OPF_TAIL))
+      continue;
+
+    for (j = i - 1, seq_p = 0; j >= 0 && seq_p < seq_len; j--) {
+      if (ops[j].op != OP_POP || ops[j].operand[0].type != OPT_REG)
+        break;
+      if (ops[j].operand[0].reg != seq[seq_p])
+        break;
+      seq_p++;
+    }
+    found = seq_len = seq_p;
+  }
+  if (!found)
+    return;
+
+  for (i = 0; i < seq_len; i++)
+    ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+
+  for (; i < opcnt && seq_len > 0; i++) {
+    if (!(ops[i].flags & OPF_TAIL))
+      continue;
+
+    for (j = i - 1, seq_p = 0; j >= 0 && seq_p < seq_len; j--) {
+      ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+      seq_p++;
+    }
+  }
+
+  // unlike pushes after sub esp,
+  // IDA treats pushed like this as part of var area
+  *fsz += seq_len * 4;
+}
+
 static int scan_prologue(int i, int opcnt, int *ecx_push, int *esp_sub)
 {
-  int j;
+  const char *name;
+  int j, len, ret;
 
   for (; i < opcnt; i++)
     if (!(ops[i].flags & OPF_DONE))
@@ -4175,6 +4265,19 @@ static int scan_prologue(int i, int opcnt, int *ecx_push, int *esp_sub)
       *esp_sub = 1;
       break;
     }
+    if (ops[i].op == OP_LEA && ops[i].operand[0].reg == xSP
+      && ops[i].operand[1].type == OPT_REGMEM
+      && IS_START(ops[i].operand[1].name, "esp-"))
+    {
+      name = ops[i].operand[1].name;
+      ret = sscanf(name, "esp-%x%n", &j, &len);
+      ferr_assert(&ops[i], ret == 1 && len == strlen(name));
+      g_stack_fsz += j;
+      ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+      i++;
+      *esp_sub = 1;
+      break;
+    }
     if (ops[i].op == OP_MOV && ops[i].operand[0].reg == xAX
         && ops[i].operand[1].type == OPT_CONST)
     {
@@ -4201,7 +4304,8 @@ static void scan_prologue_epilogue(int opcnt, int *stack_align)
 {
   int ecx_push = 0, esp_sub = 0, pusha = 0;
   int sandard_epilogue;
-  int found;
+  int found, ret, len;
+  int push_fsz = 0;
   int i, j, l;
 
   if (g_seh_found == 2) {
@@ -4325,6 +4429,7 @@ static void scan_prologue_epilogue(int opcnt, int *stack_align)
   }
 
   // non-bp frame
+  check_simple_sequence(opcnt, &push_fsz);
   i = scan_prologue(0, opcnt, &ecx_push, &esp_sub);
 
   if (ecx_push && !esp_sub) {
@@ -4389,6 +4494,12 @@ static void scan_prologue_epilogue(int opcnt, int *stack_align)
         }
       }
 
+      for (; j >= 0; j--) {
+        if ((ops[j].flags & (OPF_RMD | OPF_DONE | OPF_NOREGS)) !=
+            (OPF_RMD | OPF_DONE | OPF_NOREGS))
+          break;
+      }
+
       if (ecx_push > 0 && !esp_sub) {
         for (l = 0; l < ecx_push && j >= 0; l++) {
           if (ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ecx"))
@@ -4422,28 +4533,37 @@ static void scan_prologue_epilogue(int opcnt, int *stack_align)
       }
 
       if (esp_sub) {
-        if (ops[j].op != OP_ADD
-            || !IS(opr_name(&ops[j], 0), "esp")
-            || ops[j].operand[1].type != OPT_CONST)
+        if (ops[j].op == OP_ADD
+            && IS(opr_name(&ops[j], 0), "esp")
+            && ops[j].operand[1].type == OPT_CONST)
         {
-          if (i < opcnt && ops[i].op == OP_CALL
-            && ops[i].pp != NULL && ops[i].pp->is_noreturn)
-          {
-            // noreturn tailcall with no epilogue
-            i++;
-            found = 1;
-            continue;
-          }
-          ferr(&ops[j], "'add esp' expected\n");
-        }
+          if (ops[j].operand[1].val < g_stack_fsz)
+            ferr(&ops[j], "esp adj is too low (need %d)\n", g_stack_fsz);
 
-        if (ops[j].operand[1].val < g_stack_fsz)
-          ferr(&ops[j], "esp adj is too low (need %d)\n", g_stack_fsz);
-
-        ops[j].operand[1].val -= g_stack_fsz; // for stack arg scanner
-        if (ops[j].operand[1].val == 0)
+          ops[j].operand[1].val -= g_stack_fsz; // for stack arg scanner
+          if (ops[j].operand[1].val == 0)
+            ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
+          found = 1;
+        }
+        else if (ops[j].op == OP_LEA && ops[j].operand[0].reg == xSP
+          && ops[j].operand[1].type == OPT_REGMEM
+          && IS_START(ops[j].operand[1].name, "esp+"))
+        {
+          const char *name = ops[j].operand[1].name;
+          ret = sscanf(name, "esp+%x%n", &l, &len);
+          ferr_assert(&ops[j], ret == 1 && len == strlen(name));
+          ferr_assert(&ops[j], l <= g_stack_fsz);
           ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS;
-        found = 1;
+          found = 1;
+        }
+        else if (i < opcnt && ops[i].op == OP_CALL
+          && ops[i].pp != NULL && ops[i].pp->is_noreturn)
+        {
+          // noreturn tailcall with no epilogue
+          found = 1;
+        }
+        else
+          ferr(&ops[j], "'add esp' expected\n");
       }
 
       i++;
@@ -4452,6 +4572,10 @@ static void scan_prologue_epilogue(int opcnt, int *stack_align)
     if (!found)
       ferr(ops, "missing esp epilogue\n");
   }
+
+  if (g_stack_fsz != 0)
+    // see check_simple_sequence
+    g_stack_fsz += push_fsz;
 }
 
 // find an instruction that changed opr before i op
@@ -5902,6 +6026,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   int need_double = 0;
   int stack_align = 0;
   int stack_fsz_adj = 0;
+  int lock_handled = 0;
   int regmask_save = 0; // used regs saved/restored in this func
   int regmask_arg;      // regs from this function args (fastcall, etc)
   int regmask_ret;      // regs needed on ret
@@ -6320,6 +6445,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         need_double = 1;
       break;
 
+    case OP_RDTSC:
     case OPP_ALLSHL:
     case OPP_ALLSHR:
       need_tmp64 = 1;
@@ -6458,6 +6584,28 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
     had_decl = 1;
   }
 
+  if ((g_sct_func_attr & SCTFA_ARGFRAME) && g_func_pp->argc_stack) {
+    fprintf(fout, "  struct { u32 ");
+    for (i = j = 0; i < g_func_pp->argc; i++) {
+      if (g_func_pp->arg[i].reg != NULL)
+        continue;
+      if (j++ != 0)
+        fprintf(fout, ", ");
+      fprintf(fout, "a%d", i + 1);
+    }
+    fprintf(fout, "; } af = {\n    ");
+    for (i = j = 0; i < g_func_pp->argc; i++) {
+      if (g_func_pp->arg[i].reg != NULL)
+        continue;
+      if (j++ != 0)
+        fprintf(fout, ", ");
+        if (g_func_pp->arg[i].type.is_ptr)
+          fprintf(fout, "(u32)");
+      fprintf(fout, "a%d", i + 1);
+    }
+    fprintf(fout, "\n  };\n");
+  }
+
   if (g_func_pp->is_userstack) {
     fprintf(fout, "  u32 fake_sf[US_SZ_%s / 4];\n", g_func_pp->name);
     fprintf(fout, "  u32 *esp = &fake_sf[sizeof(fake_sf) / 4];\n");
@@ -6645,6 +6793,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
     if (po->flags & OPF_RMD)
       continue;
 
+    lock_handled = 0;
     no_output = 0;
 
     #define assert_operand_cnt(n_) \
@@ -6822,9 +6971,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
           fprintf(fout, "  for (; ecx != 0; ecx--, edi %c= %d)\n",
             (po->flags & OPF_DF) ? '-' : '+',
             lmod_bytes(po, po->operand[1].lmod));
-          fprintf(fout, "    %sedi = eax;",
+          fprintf(fout, "    %sedi = eax;\n",
             lmod_cast_u_ptr(po, po->operand[1].lmod));
-          strcpy(g_comment, "rep stos");
+          fprintf(fout, "  barrier();");
+          strcpy(g_comment, "^ rep stos");
         }
         else {
           assert_operand_cnt(2);
@@ -6846,8 +6996,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
             "  for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d)\n",
             l, j, l, j);
           fprintf(fout,
-            "    %sedi = %sesi;", buf1, buf1);
-          strcpy(g_comment, "rep movs");
+            "    %sedi = %sesi;\n", buf1, buf1);
+          // this can overwrite many variables
+          fprintf(fout, "  barrier();");
+          strcpy(g_comment, "^ rep movs");
         }
         else {
           assert_operand_cnt(2);
@@ -6930,6 +7082,16 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         delayed_flag_op = NULL;
         break;
 
+      case OP_RDTSC:
+        fprintf(fout, "  tmp64 = ext_rdtsc();\n");
+        fprintf(fout, "  edx = tmp64 >> 32;\n");
+        fprintf(fout, "  eax = tmp64;");
+        break;
+
+      case OP_CPUID:
+        fprintf(fout, "  ext_cpuid(&eax, &ebx, &ecx, &edx);");
+        break;
+
       // arithmetic w/flags
       case OP_AND:
         if (po->operand[1].type == OPT_CONST && !po->operand[1].val)
@@ -7233,9 +7395,18 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 
         out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
         if (po->operand[0].type == OPT_REG) {
+          ferr_assert(po, !(po->flags & OPF_LOCK));
           strcpy(buf2, po->op == OP_INC ? "++" : "--");
           fprintf(fout, "  %s%s;", buf1, buf2);
         }
+        else if (po->flags & OPF_LOCK) {
+          out_src_opr(buf2, sizeof(buf2), po, &po->operand[0], "", 1);
+          fprintf(fout, "  __sync_fetch_and_%s((%s *)(%s), 1);",
+            po->op == OP_INC ? "add" : "sub",
+            lmod_type_u(po, po->operand[0].lmod), buf2);
+          strcat(g_comment, " lock");
+          lock_handled = 1;
+        }
         else {
           strcpy(buf2, po->op == OP_INC ? "+" : "-");
           fprintf(fout, "  %s %s= 1;", buf1, buf2);
@@ -8095,6 +8266,9 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
     if (pfomask != 0)
       ferr(po, "missed flag calc, pfomask=%x\n", pfomask);
 
+    if ((po->flags & OPF_LOCK) && !lock_handled)
+      ferr(po, "unhandled lock\n");
+
     // see is delayed flag stuff is still valid
     if (delayed_flag_op != NULL && delayed_flag_op != po) {
       if (is_any_opr_modified(delayed_flag_op, po, 0))
@@ -9427,6 +9601,7 @@ int main(int argc, char *argv[])
           "clear_regmask",
           "rm_regmask",
           "nowarn",
+          "argframe",
         };
 
         // parse manual attribute-list comment
@@ -9550,16 +9725,18 @@ parse_words:
           continue;
         goto parse_words; // lame
       }
-      if (IS_START(p, "; sctproto:")) {
-        sctproto = strdup(p + 11);
-      }
       else if (IS_START(p, "; sctend")) {
         end = 1;
         if (!pending_endp)
           break;
       }
+      else if (g_skip_func)
+        /* ignore remaining attrs */;
+      else if (IS_START(p, "; sctproto:")) {
+        sctproto = strdup(p + 11);
+      }
       else if (IS_START(p, "; sctskip_start")) {
-        if (in_func && !g_skip_func) {
+        if (in_func) {
           if (!skip_code) {
             ops[pi].op = OPP_ABORT;
             ops[pi].asmln = asmln;