OPF_REPZ = (1 << 8), /* rep is repe/repz */
OPF_REPNZ = (1 << 9), /* rep is repne/repnz */
OPF_FARG = (1 << 10), /* push collected as func arg (no reuse) */
+ OPF_EBP_S = (1 << 11), /* ebp used as scratch, not BP */
};
enum op_op {
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 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
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];
const struct parsed_type *c_type)
{
static const char *dword_types[] = {
- "int", "_DWORD", "DWORD", "HANDLE", "HWND", "HMODULE",
+ "int", "_DWORD", "UINT_PTR",
+ "DWORD", "HANDLE", "HWND", "HMODULE",
+ "WPARAM", "LPARAM", "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",
+ "_UNKNOWN",
};
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 {
+ 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);
+
+ if (opr->lmod == OPLM_UNSPEC) {
+ opr->lmod = tmplmod;
+ opr->type_from_var = 1;
+ }
+ 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);
{ "idiv", OP_IDIV, 1, 1, OPF_DATA|OPF_FLAGS },
{ "test", OP_TEST, 2, 2, OPF_FLAGS },
{ "cmp", OP_CMP, 2, 2, OPF_FLAGS },
- { "retn", OP_RET, 0, 1, OPF_JMP|OPF_TAIL },
+ { "retn", OP_RET, 0, 1, OPF_TAIL },
{ "call", OP_CALL, 1, 1, OPF_JMP|OPF_DATA|OPF_FLAGS },
{ "jmp", OP_JMP, 1, 1, OPF_JMP },
{ "jo", OP_JO, 1, 1, OPF_JMP|OPF_CC }, // 70 OF=1
const char *p;
char *endp = NULL;
int i, arg_i, arg_s;
+ int unaligned = 0;
int stack_ra = 0;
int offset = 0;
int sf_ofs;
int lim;
- if (!IS_START(name, "ebp-")) {
+ if (po->flags & OPF_EBP_S)
+ ferr(po, "stack_frame_access while ebp is scratch\n");
+
+ if (IS_START(name, "ebp-")
+ || (IS_START(name, "ebp+") && '0' <= name[4] && name[4] <= '9'))
+ {
+ p = name + 4;
+ if (IS_START(p, "0x"))
+ p += 2;
+ offset = strtoul(p, &endp, 16);
+ if (name[3] == '-')
+ offset = -offset;
+ if (*endp != 0)
+ ferr(po, "ebp- parse of '%s' failed\n", name);
+ }
+ else {
bp_arg = parse_stack_el(name, ofs_reg);
snprintf(g_comment, sizeof(g_comment), "%s", bp_arg);
eq = equ_find(po, bp_arg, &offset);
ferr(po, "detected but missing eq\n");
offset += eq->offset;
}
- else {
- p = name + 4;
- if (IS_START(p, "0x"))
- p += 2;
- offset = -strtoul(p, &endp, 16);
- if (*endp != 0)
- ferr(po, "ebp- parse of '%s' failed\n", name);
- }
if (!strncmp(name, "ebp", 3))
stack_ra = 4;
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)
{
case OPLM_WORD:
if (is_lea)
ferr(po, "lea/word to arg?\n");
- if (offset & 1)
- ferr(po, "unaligned arg access\n");
- if (is_src && (offset & 2) == 0)
+ if (offset & 1) {
+ unaligned = 1;
+ if (!is_src) {
+ if (offset & 2)
+ ferr(po, "problematic arg store\n");
+ snprintf(buf, buf_size, "*(u16 *)((char *)&a%d + 1)", i + 1);
+ }
+ else
+ ferr(po, "unaligned arg word load\n");
+ }
+ else if (is_src && (offset & 2) == 0)
snprintf(buf, buf_size, "(u16)a%d", i + 1);
else
snprintf(buf, buf_size, "%sWORD(a%d)",
prefix = cast;
else if (is_src)
prefix = "(u32)";
+
if (offset & 3) {
+ unaligned = 1;
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);
+ snprintf(buf, buf_size, "(u32)&a%d + %d",
+ i + 1, 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);
+ }
}
else {
snprintf(buf, buf_size, "%s%sa%d",
ferr(po, "bp_arg bad lmod: %d\n", popr->lmod);
}
+ if (unaligned)
+ snprintf(g_comment, sizeof(g_comment), "%s unaligned", bp_arg);
+
// common problem
- guess_lmod_from_c_type(&tmp_lmod, &g_func_pp.arg[i].type);
- if ((offset & 3) && tmp_lmod != OPLM_DWORD)
+ guess_lmod_from_c_type(&tmp_lmod, &g_func_pp->arg[i].type);
+ if (unaligned && 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,
case OPT_REGMEM:
if (parse_stack_el(popr->name, NULL)
- || (g_bp_frame && IS_START(popr->name, "ebp-")))
+ || (g_bp_frame && !(po->flags & OPF_EBP_S)
+ && IS_START(popr->name, "ebp")))
{
stack_frame_access(po, popr, buf, buf_size,
popr->name, cast, 1, is_lea);
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]" : "");
case OPT_REGMEM:
if (parse_stack_el(popr->name, NULL)
- || (g_bp_frame && IS_START(popr->name, "ebp-")))
+ || (g_bp_frame && !(po->flags & OPF_EBP_S)
+ && IS_START(popr->name, "ebp")))
{
stack_frame_access(po, popr, buf, buf_size,
popr->name, "", 0, 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:
popr1->lmod = popr2->lmod;
else if (popr2->lmod == OPLM_UNSPEC)
popr2->lmod = popr1->lmod;
- else if (popr1->lmod != popr2->lmod)
- ferr(po, "conflicting lmods: %d vs %d\n", popr1->lmod, popr2->lmod);
+ else if (popr1->lmod != popr2->lmod) {
+ if (popr1->type_from_var) {
+ popr1->size_mismatch = 1;
+ if (popr1->lmod < popr2->lmod)
+ popr1->size_lt = 1;
+ popr1->lmod = popr2->lmod;
+ }
+ else if (popr2->type_from_var) {
+ popr2->size_mismatch = 1;
+ if (popr2->lmod < popr1->lmod)
+ popr2->size_lt = 1;
+ popr2->lmod = popr1->lmod;
+ }
+ else
+ ferr(po, "conflicting lmods: %d vs %d\n",
+ popr1->lmod, popr2->lmod);
+ }
}
static const char *op_to_c(struct parsed_op *po)
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 magic, int *branched,
+ int *setters, int *setter_cnt)
{
- *branched = 0;
+ struct label_ref *lr;
+ int ret;
while (i >= 0) {
+ if (ops[i].cc_scratch == magic) {
+ ferr(&ops[i], "%s looped\n", __func__);
+ return -1;
+ }
+ ops[i].cc_scratch = magic;
+
if (g_labels[i][0] != 0) {
- if (g_label_refs[i].next != NULL)
- return -1;
+ *branched = 1;
+
+ lr = &g_label_refs[i];
+ for (; lr->next; lr = lr->next) {
+ ret = scan_for_flag_set(lr->i, magic,
+ branched, setters, setter_cnt);
+ if (ret < 0)
+ return ret;
+ }
+
if (i > 0 && LAST_OP(i - 1)) {
i = g_label_refs[i].i;
- *branched = 1;
continue;
}
+ ret = scan_for_flag_set(lr->i, magic,
+ 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;
return -1;
}
+static void scan_fwd_set_flags(int i, int opcnt, int magic, int flags)
+{
+ struct parsed_op *po;
+ int j;
+
+ if (i < 0)
+ ferr(ops, "%s: followed bad branch?\n", __func__);
+
+ for (; i < opcnt; i++) {
+ po = &ops[i];
+ if (po->cc_scratch == magic)
+ return;
+ po->cc_scratch = magic;
+ po->flags |= flags;
+
+ if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
+ if (po->btj != NULL) {
+ // jumptable
+ for (j = 0; j < po->btj->count; j++)
+ scan_fwd_set_flags(po->btj->d[j].bt_i, opcnt, magic, flags);
+ return;
+ }
+
+ scan_fwd_set_flags(po->bt_i, opcnt, magic, flags);
+ if (!(po->flags & OPF_CC))
+ return;
+ }
+ if (po->flags & OPF_TAIL)
+ return;
+ }
+}
+
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 magic, 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;
- if (i < 0)
+ if (i < 0) {
ferr(po, "no refs for '%s'?\n", g_labels[i]);
+ return -1;
+ }
for (; arg < pp->argc; arg++)
if (pp->arg[arg].reg == NULL)
break;
+ magic = (magic & 0xffffff) | (arg << 24);
for (j = i; j >= 0 && arg < pp->argc; )
{
+ if (((ops[j].cc_scratch ^ magic) & 0xffffff) == 0) {
+ if (ops[j].cc_scratch != magic) {
+ ferr(&ops[j], "arg collect hit same path with diff args for %s\n",
+ pp->name);
+ return -1;
+ }
+ // ok: have already been here
+ return 0;
+ }
+ ops[j].cc_scratch = magic;
+
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)
- ret |= collect_call_args(po, lr->i, pp, save_arg_vars,
- arg, need_op_saving, branched);
+ 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, magic, need_op_saving, may_reuse);
+ if (ret < 0)
+ return ret;
+ }
+ 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;
continue;
}
need_op_saving = 1;
- ret |= collect_call_args(po, lr->i, pp, save_arg_vars,
- arg, need_op_saving, branched);
+ ret = collect_call_args(po, lr->i, pp, save_arg_vars,
+ arg, magic, need_op_saving, may_reuse);
+ if (ret < 0)
+ return ret;
}
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
for (arg++; arg < pp->argc; arg++)
if (pp->arg[arg].reg == NULL)
break;
+ magic = (magic & 0xffffff) | (arg << 24);
}
}
lr_new = calloc(1, sizeof(*lr_new));
lr_new->i = op_i;
+ lr_new->next = lr->next;
lr->next = lr_new;
}
+static void output_std_flags(FILE *fout, struct parsed_op *po,
+ int *pfomask, const char *dst_opr_text)
+{
+ if (*pfomask & (1 << PFO_Z)) {
+ fprintf(fout, "\n cond_z = (%s%s == 0);",
+ lmod_cast_u(po, po->operand[0].lmod), dst_opr_text);
+ *pfomask &= ~(1 << PFO_Z);
+ }
+ if (*pfomask & (1 << PFO_S)) {
+ fprintf(fout, "\n cond_s = (%s%s < 0);",
+ lmod_cast_s(po, po->operand[0].lmod), dst_opr_text);
+ *pfomask &= ~(1 << PFO_S);
+ }
+}
+
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, *pp_tmp;
struct parsed_data *pd;
const char *tmpname;
int save_arg_vars = 0;
int cmp_result_vars = 0;
int need_mul_var = 0;
+ int have_func_ret = 0;
int had_decl = 0;
- int branched = 0;
int label_pending = 0;
int regmask_save = 0;
int regmask_arg = 0;
+ int regmask_now = 0;
int regmask = 0;
int pfomask = 0;
int found = 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:
}
}
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
}
}
- collect_call_args(po, i, pp, &save_arg_vars, 0, 0, 0);
+ collect_call_args(po, i, pp, &save_arg_vars,
+ 0, i + opcnt * 2, 0, 0);
+
+ if (strstr(pp->ret_type.name, "int64"))
+ need_mul_var = 1;
+ if (!(po->flags & OPF_TAIL) && !IS(pp->ret_type.name, "void"))
+ have_func_ret = 1;
po->datap = pp;
}
}
depth = 0;
ret = scan_for_pop(i + 1, opcnt,
- po->operand[0].name, i + opcnt, 0, &depth, 0);
+ po->operand[0].name, i + opcnt * 3, 0, &depth, 0);
if (ret == 1) {
if (depth > 1)
ferr(po, "too much depth: %d\n", depth);
po->flags |= OPF_RMD;
scan_for_pop(i + 1, opcnt, po->operand[0].name,
- i + opcnt * 2, 0, &depth, 1);
+ i + opcnt * 4, 0, &depth, 1);
continue;
}
ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, 0);
}
}
- regmask |= po->regmask_src | po->regmask_dst;
+ regmask_now = po->regmask_src | po->regmask_dst;
+ if (regmask_now & (1 << xBP)) {
+ if (g_bp_frame && !(po->flags & OPF_EBP_S)) {
+ if (po->regmask_dst & (1 << xBP))
+ // compiler decided to drop bp frame and use ebp as scratch
+ scan_fwd_set_flags(i, opcnt, i + opcnt * 5, OPF_EBP_S);
+ else
+ regmask_now &= ~(1 << xBP);
+ }
+ }
+
+ regmask |= regmask_now;
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;
- tmp_op = &ops[ret]; // flag setter
- pfo = split_cond(po, po->op, &dummy);
- pfomask = 0;
+ ret = scan_for_flag_set(i, i + opcnt * 6,
+ &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");
- // 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;
+ pfo = split_cond(po, po->op, &dummy);
+ 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 {
+ // see if we'll be able to handle based on op result
+ if ((tmp_op->op != OP_AND && tmp_op->op != OP_OR
+ && pfo != PFO_Z && pfo != PFO_S && pfo != PFO_P)
+ || branched
+ || 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;
}
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"))
+ if (!((regmask | regmask_arg) & 1) && have_func_ret
+ && !IS(g_func_pp->ret_type.name, "void"))
{
fprintf(fout, " u32 eax = 0;\n");
had_decl = 1;
regmask &= ~regmask_arg;
regmask &= ~(1 << xSP);
- if (g_bp_frame)
- regmask &= ~(1 << xBP);
if (regmask) {
for (reg = 0; reg < 8; reg++) {
if (regmask & (1 << reg)) {
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];
int is_inv = 0;
pfo = split_cond(po, po->op, &is_inv);
+ tmp_op = po->datap;
// we go through all this trouble to avoid using parsed_flag_op,
// which makes generated code much nicer
is_delayed = 1;
}
else if (last_arith_dst != NULL
- && (pfo == PFO_Z || pfo == PFO_S || pfo == PFO_P))
+ && (pfo == PFO_Z || pfo == PFO_S || pfo == PFO_P
+ || (tmp_op && (tmp_op->op == OP_AND || tmp_op->op == OP_OR))
+ ))
{
out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
out_test_for_cc(buf1, sizeof(buf1), po, pfo, is_inv,
last_arith_dst->lmod, buf3);
is_delayed = 1;
}
- else if (po->datap != NULL) {
+ else if (tmp_op != NULL) {
// use preprocessed flag calc results
- tmp_op = po->datap;
- if (!tmp_op || !(tmp_op->pfomask & (1 << pfo)))
+ if (!(tmp_op->pfomask & (1 << pfo)))
ferr(po, "not prepared for pfo %d\n", pfo);
// note: is_inv was not yet applied
case OP_OR:
propagate_lmod(po, &po->operand[0], &po->operand[1]);
// fallthrough
- case OP_SHL:
- case OP_SHR:
dualop_arith:
assert_operand_cnt(2);
fprintf(fout, " %s %s= %s;",
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]));
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_SHL:
+ case OP_SHR:
+ assert_operand_cnt(2);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ if (pfomask & (1 << PFO_C)) {
+ if (po->operand[1].type == OPT_CONST) {
+ l = lmod_bytes(po, po->operand[0].lmod) * 8;
+ j = po->operand[1].val;
+ j %= l;
+ if (j != 0) {
+ if (po->op == OP_SHL)
+ j = l - j;
+ else
+ j -= 1;
+ fprintf(fout, " cond_c = (%s & 0x%02x) ? 1 : 0;\n",
+ buf1, 1 << j);
+ }
+ else
+ ferr(po, "zero shift?\n");
+ }
+ else
+ ferr(po, "TODO\n");
+ pfomask &= ~(1 << PFO_C);
+ }
+ fprintf(fout, " %s %s= %s;", buf1, op_to_c(po),
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ output_std_flags(fout, po, &pfomask, buf1);
last_arith_dst = &po->operand[0];
delayed_flag_op = NULL;
break;
fprintf(fout, " %s = %s%s >> %s;", buf1,
lmod_cast_s(po, po->operand[0].lmod), buf1,
out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ output_std_flags(fout, po, &pfomask, buf1);
last_arith_dst = &po->operand[0];
delayed_flag_op = NULL;
break;
}
else
ferr(po, "TODO\n");
+ output_std_flags(fout, po, &pfomask, buf1);
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;
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)
- fprintf(fout, "return ");
- else
+ ferr(po, "int64 and tail?\n");
+ fprintf(fout, "mul_tmp = ");
+ }
+ else if (!IS(pp->ret_type.name, "void")) {
+ if (po->flags & OPF_TAIL) {
+ 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 = ");
- 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")
- && !(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;");
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)