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", "LPARAM", "UINT",
};
static const char *word_types[] = {
"uint16_t", "int16_t",
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 (!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;
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)
{
}
// 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
{
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,
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;
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))
{
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;
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:
}
}
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);
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:
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
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
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;
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;
}
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 = ");
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++) {
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");
}
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;");
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)