OPF_FARG = (1 << 11), /* push collected as func arg (no reuse) */
OPF_EBP_S = (1 << 12), /* ebp used as scratch here, not BP */
OPF_DF = (1 << 13), /* DF flag set */
+ OPF_ATAIL = (1 << 14), /* tail call with reused arg frame */
};
enum op_op {
{
static const char *dword_types[] = {
"int", "_DWORD", "UINT_PTR", "DWORD",
- "WPARAM", "LPARAM", "UINT",
- "HIMC",
+ "WPARAM", "LPARAM", "UINT", "__int32",
+ "LONG", "HIMC",
};
static const char *word_types[] = {
"uint16_t", "int16_t", "_WORD",
static const char *byte_types[] = {
"uint8_t", "int8_t", "char",
"unsigned __int8", "__int8", "BYTE", "_BYTE",
- "_UNKNOWN",
+ "CHAR", "_UNKNOWN",
};
const char *n;
int i;
ferr(po, "bp_arg arg%d/w offset %d and type '%s' is too small\n",
i + 1, offset, g_func_pp->arg[i].type.name);
}
- if (popr->is_ptr && popr->lmod != OPLM_DWORD)
- ferr(po, "bp_arg arg%d: non-dword ptr access\n", i + 1);
+ // can't check this because msvc likes to reuse
+ // arg space for scratch..
+ //if (popr->is_ptr && popr->lmod != OPLM_DWORD)
+ // ferr(po, "bp_arg arg%d: non-dword ptr access\n", i + 1);
}
else
{
}
// scan for positive, constant esp adjust
-static int scan_for_esp_adjust(int i, int opcnt, int *adj)
+static int scan_for_esp_adjust(int i, int opcnt, int *adj,
+ int *multipath)
{
const struct parsed_proto *pp;
struct parsed_op *po;
int first_pop = -1;
- *adj = 0;
+
+ *adj = *multipath = 0;
for (; i < opcnt; i++) {
po = &ops[i];
if (g_labels[i][0] != 0)
- break;
+ *multipath = 1;
if (po->op == OP_ADD && po->operand[0].reg == xSP) {
if (po->operand[1].type != OPT_CONST)
*adj += lmod_bytes(po, po->operand[0].lmod);
}
else if (po->flags & (OPF_JMP|OPF_TAIL)) {
+ if (po->op == OP_JMP && po->btj == NULL) {
+ i = po->bt_i - 1;
+ continue;
+ }
if (po->op != OP_CALL)
break;
if (po->operand[0].type != OPT_LABEL)
int save_arg_vars = 0;
int cmp_result_vars = 0;
int need_tmp_var = 0;
- int need_mul_var = 0;
+ int need_tmp64 = 0;
int had_decl = 0;
int label_pending = 0;
int regmask_save = 0;
for (; i < opcnt; i++)
if (ops[i].op == OP_RET)
break;
- if (i == opcnt && (ops[i - 1].flags & OPF_JMP) && found)
- break;
+ j = i - 1;
+ if (i == opcnt && (ops[j].flags & OPF_JMP)) {
+ if (found) {
+ if (ops[j].op == OP_JMP && !ops[j].operand[0].had_ds)
+ // probably local branch back..
+ break;
+ if (ops[j].op == OP_CALL)
+ // probably noreturn call..
+ break;
+ }
+ j--;
+ }
- if ((ops[i - 1].op == OP_POP && IS(opr_name(&ops[i - 1], 0), "ebp"))
- || ops[i - 1].op == OP_LEAVE)
+ if ((ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ebp"))
+ || ops[j].op == OP_LEAVE)
{
- ops[i - 1].flags |= OPF_RMD;
+ ops[j].flags |= OPF_RMD;
}
else if (!(g_ida_func_attr & IDAFA_NORETURN))
- ferr(&ops[i - 1], "'pop ebp' expected\n");
+ ferr(&ops[j], "'pop ebp' expected\n");
if (g_stack_fsz != 0) {
- if (ops[i - 2].op == OP_MOV
- && IS(opr_name(&ops[i - 2], 0), "esp")
- && IS(opr_name(&ops[i - 2], 1), "ebp"))
+ if (ops[j - 1].op == OP_MOV
+ && IS(opr_name(&ops[j - 1], 0), "esp")
+ && IS(opr_name(&ops[j - 1], 1), "ebp"))
{
- ops[i - 2].flags |= OPF_RMD;
+ ops[j - 1].flags |= OPF_RMD;
}
- else if (ops[i - 1].op != OP_LEAVE
+ else if (ops[j].op != OP_LEAVE
&& !(g_ida_func_attr & IDAFA_NORETURN))
{
- ferr(&ops[i - 2], "esp restore expected\n");
+ ferr(&ops[j - 1], "esp restore expected\n");
}
- if (ecx_push && ops[i - 3].op == OP_POP
- && IS(opr_name(&ops[i - 3], 0), "ecx"))
+ if (ecx_push && ops[j - 2].op == OP_POP
+ && IS(opr_name(&ops[j - 2], 0), "ecx"))
{
- ferr(&ops[i - 3], "unexpected ecx pop\n");
+ ferr(&ops[j - 2], "unexpected ecx pop\n");
}
}
tailcall:
po->op = OP_CALL;
po->flags |= OPF_TAIL;
+ if (i > 0 && ops[i - 1].op == OP_POP)
+ po->flags |= OPF_ATAIL;
i--; // reprocess
}
pp = calloc(1, sizeof(*pp));
my_assert_not(pp, NULL);
pp->is_fptr = 1;
- ret = scan_for_esp_adjust(i + 1, opcnt, &j);
+ ret = scan_for_esp_adjust(i + 1, opcnt, &j, &l);
if (ret < 0) {
if (!g_allow_regfunc)
ferr(po, "non-__cdecl indirect call unhandled yet\n");
// look for and make use of esp adjust
ret = -1;
if (!pp->is_stdcall && pp->argc_stack > 0)
- ret = scan_for_esp_adjust(i + 1, opcnt, &j);
+ ret = scan_for_esp_adjust(i + 1, opcnt, &j, &l);
if (ret >= 0) {
if (pp->is_vararg) {
if (j / 4 < pp->argc_stack)
ret++;
}
}
- else {
+ else if (!l) {
// a bit of a hack, but deals with use of
// single adj for multiple calls
ops[ret].operand[1].val -= j;
ferr(po, "missing esp_adjust for vararg func '%s'\n",
pp->name);
- if (!pp->is_unresolved) {
+ if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
// since we know the args, collect them
collect_call_args(po, i, pp, ®mask, &save_arg_vars,
i + opcnt * 2);
}
if (strstr(pp->ret_type.name, "int64"))
- need_mul_var = 1;
+ need_tmp64 = 1;
}
}
if (branched || scan_for_mod(tmp_op, setters[j] + 1, i, 0) >= 0)
pfomask = 1 << pfo;
}
- else if (tmp_op->op == OP_SCAS) {
+ else if (tmp_op->op == OP_CMPS || tmp_op->op == OP_SCAS) {
pfomask = 1 << pfo;
}
- else if (tmp_op->op == OP_CMPS) {
- pfomask = 1 << PFO_Z;
- }
else {
// see if we'll be able to handle based on op result
if ((tmp_op->op != OP_AND && tmp_op->op != OP_OR
|| branched
|| scan_for_mod_opr0(tmp_op, setters[j] + 1, i) >= 0)
pfomask = 1 << pfo;
+
+ if (tmp_op->op == OP_ADD && pfo == PFO_C)
+ need_tmp64 = 1;
}
if (pfomask) {
tmp_op->pfomask |= pfomask;
else if (po->op == OP_MUL
|| (po->op == OP_IMUL && po->operand_cnt == 1))
{
- need_mul_var = 1;
+ need_tmp64 = 1;
}
else if (po->op == OP_XCHG) {
need_tmp_var = 1;
had_decl = 1;
}
- if (need_mul_var) {
- fprintf(fout, " u64 mul_tmp;\n");
+ if (need_tmp64) {
+ fprintf(fout, " u64 tmp64;\n");
had_decl = 1;
}
pfomask = po->pfomask;
+ if (po->flags & (OPF_REPZ|OPF_REPNZ)) {
+ // we need initial flags for ecx=0 case..
+ if (i > 0 && ops[i - 1].op == OP_XOR
+ && IS(ops[i - 1].operand[0].name,
+ ops[i - 1].operand[1].name))
+ {
+ fprintf(fout, " cond_z = ");
+ if (pfomask & (1 << PFO_C))
+ fprintf(fout, "cond_c = ");
+ fprintf(fout, "0;\n");
+ }
+ else if (last_arith_dst != NULL) {
+ out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
+ out_test_for_cc(buf1, sizeof(buf1), po, PFO_Z, 0,
+ last_arith_dst->lmod, buf3);
+ fprintf(fout, " cond_z = %s;\n", buf1);
+ }
+ else
+ ferr(po, "missing initial ZF\n");
+ }
+
switch (po->op)
{
case OP_MOV:
l = (po->flags & OPF_DF) ? '-' : '+';
if (po->flags & OPF_REP) {
fprintf(fout,
- " for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d)\n",
+ " for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d) {\n",
l, j, l, j);
+ if (pfomask & (1 << PFO_C)) {
+ // ugh..
+ fprintf(fout,
+ " cond_c = %sedi < %sesi;\n", buf1, buf1);
+ pfomask &= ~(1 << PFO_C);
+ }
fprintf(fout,
" if ((cond_z = (%sedi == %sesi)) %s 0)\n",
buf1, buf1, (po->flags & OPF_REPZ) ? "==" : "!=");
fprintf(fout,
- " break;");
+ " break;\n"
+ " }");
snprintf(g_comment, sizeof(g_comment), "rep%s cmps",
(po->flags & OPF_REPZ) ? "e" : "ne");
}
break;
// arithmetic w/flags
- case OP_ADD:
- case OP_SUB:
case OP_AND:
case OP_OR:
propagate_lmod(po, &po->operand[0], &po->operand[1]);
propagate_lmod(po, &po->operand[0], &po->operand[1]);
if (IS(opr_name(po, 0), opr_name(po, 1))) {
// special case for XOR
+ if (pfomask & (1 << PFO_BE)) { // weird, but it happens..
+ fprintf(fout, " cond_be = 1;\n");
+ pfomask &= ~(1 << PFO_BE);
+ }
fprintf(fout, " %s = 0;",
out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
last_arith_dst = &po->operand[0];
}
goto dualop_arith;
+ case OP_ADD:
+ assert_operand_cnt(2);
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ if (pfomask & (1 << PFO_C)) {
+ fprintf(fout, " tmp64 = (u64)%s + %s;\n",
+ out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]),
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ fprintf(fout, " cond_c = tmp64 >> 32;\n");
+ fprintf(fout, " %s = (u32)tmp64;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
+ strcat(g_comment, "add64");
+ pfomask &= ~(1 << PFO_C);
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+ }
+ goto dualop_arith;
+
+ case OP_SUB:
+ assert_operand_cnt(2);
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ if (pfomask & (1 << PFO_C)) {
+ fprintf(fout, " cond_c = %s < %s;\n",
+ out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]),
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ pfomask &= ~(1 << PFO_C);
+ }
+ goto dualop_arith;
+
case OP_ADC:
case OP_SBB:
assert_operand_cnt(2);
case OP_MUL:
assert_operand_cnt(1);
strcpy(buf1, po->op == OP_IMUL ? "(s64)(s32)" : "(u64)");
- fprintf(fout, " mul_tmp = %seax * %s%s;\n", buf1, buf1,
+ fprintf(fout, " tmp64 = %seax * %s%s;\n", buf1, buf1,
out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]));
- fprintf(fout, " edx = mul_tmp >> 32;\n");
- fprintf(fout, " eax = mul_tmp;");
+ fprintf(fout, " edx = tmp64 >> 32;\n");
+ fprintf(fout, " eax = tmp64;");
last_arith_dst = NULL;
delayed_flag_op = NULL;
break;
if (strstr(pp->ret_type.name, "int64")) {
if (po->flags & OPF_TAIL)
ferr(po, "int64 and tail?\n");
- fprintf(fout, "mul_tmp = ");
+ fprintf(fout, "tmp64 = ");
}
else if (!IS(pp->ret_type.name, "void")) {
if (po->flags & OPF_TAIL) {
fprintf(fout, "%s%s(", pp->name,
pp->has_structarg ? "_sa" : "");
- for (arg = 0; arg < pp->argc; arg++) {
- if (arg > 0)
- fprintf(fout, ", ");
+ if (po->flags & OPF_ATAIL) {
+ if (pp->argc_stack != g_func_pp->argc_stack
+ || (pp->argc_stack > 0
+ && pp->is_stdcall != g_func_pp->is_stdcall))
+ ferr(po, "incompatible tailcall\n");
- cast[0] = 0;
- if (pp->arg[arg].type.is_ptr)
- snprintf(cast, sizeof(cast), "(%s)", pp->arg[arg].type.name);
+ for (arg = j = 0; arg < pp->argc; arg++) {
+ if (arg > 0)
+ fprintf(fout, ", ");
- if (pp->arg[arg].reg != NULL) {
- fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
- continue;
- }
+ cast[0] = 0;
+ if (pp->arg[arg].type.is_ptr)
+ snprintf(cast, sizeof(cast), "(%s)",
+ pp->arg[arg].type.name);
- // stack arg
- 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) {
- fprintf(fout, "%ss_a%d", cast, tmp_op->argnum);
+ if (pp->arg[arg].reg != NULL) {
+ fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
+ continue;
+ }
+ // stack arg
+ for (; j < g_func_pp->argc; j++)
+ if (g_func_pp->arg[j].reg == NULL)
+ break;
+ fprintf(fout, "%sa%d", cast, j + 1);
+ j++;
}
- else {
- fprintf(fout, "%s",
- out_src_opr(buf1, sizeof(buf1),
- tmp_op, &tmp_op->operand[0], cast, 0));
+ }
+ else {
+ for (arg = 0; arg < pp->argc; arg++) {
+ if (arg > 0)
+ fprintf(fout, ", ");
+
+ cast[0] = 0;
+ if (pp->arg[arg].type.is_ptr)
+ snprintf(cast, sizeof(cast), "(%s)",
+ pp->arg[arg].type.name);
+
+ if (pp->arg[arg].reg != NULL) {
+ fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
+ continue;
+ }
+
+ // stack arg
+ 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) {
+ fprintf(fout, "%ss_a%d", cast, tmp_op->argnum);
+ }
+ else {
+ fprintf(fout, "%s",
+ out_src_opr(buf1, sizeof(buf1),
+ tmp_op, &tmp_op->operand[0], cast, 0));
+ }
}
}
fprintf(fout, ");");
if (strstr(pp->ret_type.name, "int64")) {
fprintf(fout, "\n");
- fprintf(fout, " edx = mul_tmp >> 32;\n");
- fprintf(fout, " eax = mul_tmp;");
+ fprintf(fout, " edx = tmp64 >> 32;\n");
+ fprintf(fout, " eax = tmp64;");
}
if (pp->is_unresolved) {
}
if (pp->is_noreturn)
strcat(g_comment, " noreturn");
+ if ((po->flags & OPF_ATAIL) && pp->argc_stack > 0)
+ strcat(g_comment, " argframe");
delayed_flag_op = NULL;
last_arith_dst = NULL;
break;
fprintf(fout, " return (%s)eax;",
g_func_pp->ret_type.name);
}
+ else if (IS(g_func_pp->ret_type.name, "__int64"))
+ fprintf(fout, " return ((u64)edx << 32) | eax;");
else
fprintf(fout, " return eax;");