OPT_CONST,
};
+// must be sorted (larger len must be further in enum)
enum opr_lenmod {
OPLM_UNSPEC,
OPLM_BYTE,
enum opr_lenmod lmod;
unsigned int is_ptr:1; // pointer in C
unsigned int is_array:1; // array in C
+ unsigned int size_mismatch:1; // type override differs from C
+ unsigned int size_lt:1; // type override is larger than C
int reg;
unsigned int val;
char name[256];
// datap:
// OP_CALL - ptr to parsed_proto
-// (OPF_CC) - point to corresponding (OPF_FLAGS)
+// (OPF_CC) - point to one of (OPF_FLAGS) that affects cc op
struct parsed_equ {
char name[64];
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];
{
static const char *dword_types[] = {
"int", "_DWORD", "DWORD", "HANDLE", "HWND", "HMODULE",
+ "WPARAM", "UINT",
};
static const char *word_types[] = {
- "__int16", "unsigned __int16",
+ "uint16_t", "int16_t",
+ "unsigned __int16", "__int16",
};
static const char *byte_types[] = {
- "char", "__int8", "unsigned __int8", "BYTE",
+ "uint8_t", "int8_t", "char",
+ "unsigned __int8", "__int8", "BYTE",
};
const char *n;
int i;
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;
// 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 (opr->lmod == OPLM_UNSPEC) {
- if (!guess_lmod_from_c_type(&opr->lmod, &pp.type))
- anote("unhandled C type '%s' for '%s'\n", pp.type.name, opr->name);
+ else {
+ if (!guess_lmod_from_c_type(&tmplmod, &pp->type))
+ anote("unhandled C type '%s' for '%s'\n",
+ pp->type.name, opr->name);
+
+ if (opr->lmod == OPLM_UNSPEC)
+ opr->lmod = tmplmod;
+ else if (opr->lmod != tmplmod) {
+ opr->size_mismatch = 1;
+ 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);
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)";
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)
{
else if (is_src)
prefix = "(u32)";
if (offset & 3) {
- if (is_lea)
- ferr(po, "unaligned lea?\n");
snprintf(g_comment, sizeof(g_comment), "%s unaligned", bp_arg);
- snprintf(buf, buf_size, "%s(a%d >> %d)",
- prefix, i + 1, (offset & 3) * 8);
+ if (is_lea)
+ snprintf(buf, buf_size, "(u32)&a%d + %d",
+ i + 1, offset & 3);
+ else
+ snprintf(buf, buf_size, "%s(a%d >> %d)",
+ prefix, i + 1, (offset & 3) * 8);
}
else {
snprintf(buf, buf_size, "%s%sa%d",
}
// 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
{
check_label_read_ref(po, popr->name);
if (cast[0] == 0 && popr->is_ptr)
cast = "(u32)";
+
if (is_lea)
snprintf(buf, buf_size, "(u32)&%s", popr->name);
+ else if (popr->size_lt)
+ snprintf(buf, buf_size, "%s%s%s%s", cast,
+ lmod_cast_u_ptr(po, popr->lmod),
+ popr->is_array ? "" : "&",
+ popr->name);
else
snprintf(buf, buf_size, "%s%s%s", cast, popr->name,
popr->is_array ? "[0]" : "");
return out_src_opr(buf, buf_size, po, popr, NULL, 0);
case OPT_LABEL:
- snprintf(buf, buf_size, "%s%s", popr->name,
- popr->is_array ? "[0]" : "");
+ if (popr->size_mismatch)
+ snprintf(buf, buf_size, "%s%s%s",
+ lmod_cast_u_ptr(po, popr->lmod),
+ popr->is_array ? "" : "&", popr->name);
+ else
+ snprintf(buf, buf_size, "%s%s", popr->name,
+ popr->is_array ? "[0]" : "");
break;
default:
return 1;
// in reality, it can wreck any register, but in decompiled C
- // version it can only overwrite eax
+ // version it can only overwrite eax or edx:eax
if (po->op == OP_CALL
- && ((po_test->regmask_src | po_test->regmask_dst) & (1 << xAX)))
+ && ((po_test->regmask_src | po_test->regmask_dst)
+ & ((1 << xAX)|(1 << xDX))))
return 1;
for (i = 0; i < po_test->operand_cnt; i++)
// scan for any po_test operand modification in range given
static int scan_for_mod(struct parsed_op *po_test, int i, int opcnt)
{
+ if (po_test->operand_cnt == 1 && po_test->operand[0].type == OPT_CONST)
+ return -1;
+
for (; i < opcnt; i++) {
if (is_any_opr_modified(po_test, &ops[i]))
return i;
return -1;
}
-static int scan_for_flag_set(int i, int *branched)
+static int scan_for_flag_set(int i, int *branched, int *setters,
+ int *setter_cnt)
{
- *branched = 0;
+ int ret;
while (i >= 0) {
if (g_labels[i][0] != 0) {
+ *branched = 1;
if (g_label_refs[i].next != NULL)
return -1;
if (i > 0 && LAST_OP(i - 1)) {
i = g_label_refs[i].i;
- *branched = 1;
continue;
}
+ ret = scan_for_flag_set(g_label_refs[i].i, branched,
+ setters, setter_cnt);
+ if (ret < 0)
+ return ret;
}
i--;
- if (ops[i].flags & OPF_FLAGS)
- return i;
+ if (ops[i].flags & OPF_FLAGS) {
+ setters[*setter_cnt] = i;
+ (*setter_cnt)++;
+ return 0;
+ }
if ((ops[i].flags & OPF_JMP) && !(ops[i].flags & OPF_CC))
return -1;
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;
+ int need_to_save_current;
int ret = 0;
int j;
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;
}
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--;
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);
}
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))
{
pp->arg[arg].datap = &ops[j];
+ need_to_save_current = 0;
if (!need_op_saving) {
ret = scan_for_mod(&ops[j], j + 1, i);
- need_op_saving = (ret >= 0);
+ need_to_save_current = (ret >= 0);
}
- if (need_op_saving) {
+ if (need_op_saving || need_to_save_current) {
// mark this push as one that needs operand saving
ops[j].flags &= ~OPF_RMD;
if (ops[j].argnum == 0) {
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
lr_new = calloc(1, sizeof(*lr_new));
lr_new->i = op_i;
+ lr_new->next = lr->next;
lr->next = lr_new;
}
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;
int cmp_result_vars = 0;
int need_mul_var = 0;
int had_decl = 0;
- int branched = 0;
int label_pending = 0;
int regmask_save = 0;
int regmask_arg = 0;
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:
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
}
collect_call_args(po, i, pp, &save_arg_vars, 0, 0, 0);
+
+ if (strstr(pp->ret_type.name, "int64"))
+ need_mul_var = 1;
po->datap = pp;
}
}
if (po->flags & OPF_CC)
{
- ret = scan_for_flag_set(i, &branched);
- if (ret < 0)
- ferr(po, "unable to trace flag setter\n");
+ int setters[16], cnt = 0, branched = 0;
+
+ ret = scan_for_flag_set(i, &branched, setters, &cnt);
+ if (ret < 0 || cnt <= 0)
+ ferr(po, "unable to trace flag setter(s)\n");
+ if (cnt > ARRAY_SIZE(setters))
+ ferr(po, "too many flag setters\n");
- tmp_op = &ops[ret]; // flag setter
pfo = split_cond(po, po->op, &dummy);
- pfomask = 0;
-
- // to get nicer code, we try to delay test and cmp;
- // if we can't because of operand modification, or if we
- // have math op, or branch, make it calculate flags explicitly
- if (tmp_op->op == OP_TEST || tmp_op->op == OP_CMP) {
- if (branched || scan_for_mod(tmp_op, ret + 1, i) >= 0)
- pfomask = 1 << pfo;
- }
- else if (tmp_op->op == OP_CMPS) {
- pfomask = 1 << PFO_Z;
- }
- else {
- if ((pfo != PFO_Z && pfo != PFO_S && pfo != PFO_P)
- || scan_for_mod_opr0(tmp_op, ret + 1, i) >= 0)
- pfomask = 1 << pfo;
- }
- if (pfomask) {
- tmp_op->pfomask |= pfomask;
- cmp_result_vars |= pfomask;
- po->datap = tmp_op;
+ for (j = 0; j < cnt; j++)
+ {
+ tmp_op = &ops[setters[j]]; // flag setter
+ pfomask = 0;
+
+ // to get nicer code, we try to delay test and cmp;
+ // if we can't because of operand modification, or if we
+ // have math op, or branch, make it calculate flags explicitly
+ if (tmp_op->op == OP_TEST || tmp_op->op == OP_CMP) {
+ if (branched || scan_for_mod(tmp_op, setters[j] + 1, i) >= 0)
+ pfomask = 1 << pfo;
+ }
+ else if (tmp_op->op == OP_CMPS) {
+ pfomask = 1 << PFO_Z;
+ }
+ else {
+ if ((pfo != PFO_Z && pfo != PFO_S && pfo != PFO_P)
+ || scan_for_mod_opr0(tmp_op, setters[j] + 1, i) >= 0)
+ pfomask = 1 << pfo;
+ }
+ if (pfomask) {
+ tmp_op->pfomask |= pfomask;
+ cmp_result_vars |= pfomask;
+ // note: may overwrite, currently not a problem
+ po->datap = tmp_op;
+ }
}
if (po->op == OP_ADC || po->op == OP_SBB)
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;
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
if (g_labels[i][0] != 0 && g_label_refs[i].i != -1) {
fprintf(fout, "\n%s:\n", g_labels[i]);
label_pending = 1;
+
+ delayed_flag_op = NULL;
+ last_arith_dst = NULL;
}
po = &ops[i];
out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]));
fprintf(fout, " ");
- if (!IS(pp->ret_type.name, "void")) {
+ if (strstr(pp->ret_type.name, "int64")) {
if (po->flags & OPF_TAIL)
+ ferr(po, "int64 and tail?\n");
+ fprintf(fout, "mul_tmp = ");
+ }
+ else if (!IS(pp->ret_type.name, "void")) {
+ if (po->flags & OPF_TAIL) {
fprintf(fout, "return ");
- else
+ 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 = ");
- if (pp->ret_type.is_ptr)
- fprintf(fout, "(u32)");
+ if (pp->ret_type.is_ptr)
+ fprintf(fout, "(u32)");
+ }
}
if (po->operand[0].type != OPT_LABEL) {
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++) {
}
fprintf(fout, ");");
+ if (strstr(pp->ret_type.name, "int64")) {
+ fprintf(fout, "\n");
+ fprintf(fout, " edx = mul_tmp >> 32;\n");
+ fprintf(fout, " eax = mul_tmp;");
+ }
+
if (po->flags & OPF_TAIL) {
strcpy(g_comment, "tailcall");
if (IS(pp->ret_type.name, "void")
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;");
break;
case OP_NOP:
+ no_output = 1;
break;
default:
break;
}
- // some sanity checking
- if ((po->flags & OPF_REP) && po->op != OP_STOS
- && po->op != OP_MOVS && po->op != OP_CMPS)
- ferr(po, "unexpected rep\n");
- if ((po->flags & (OPF_REPZ|OPF_REPNZ)) && po->op != OP_CMPS)
- ferr(po, "unexpected repz/repnz\n");
-
if (g_comment[0] != 0) {
fprintf(fout, " // %s", g_comment);
g_comment[0] = 0;
if (!no_output)
fprintf(fout, "\n");
+ // some sanity checking
+ if ((po->flags & OPF_REP) && po->op != OP_STOS
+ && po->op != OP_MOVS && po->op != OP_CMPS)
+ ferr(po, "unexpected rep\n");
+ if ((po->flags & (OPF_REPZ|OPF_REPNZ)) && po->op != OP_CMPS)
+ ferr(po, "unexpected repz/repnz\n");
+
if (pfomask != 0)
ferr(po, "missed flag calc, pfomask=%x\n", pfomask);
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)