+ j = i - 1;
+ if (i == opcnt && (ops[j].flags & OPF_JMP)) {
+ if (found && is_like_tailjmp(j))
+ break;
+ j--;
+ }
+
+ if ((ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ebp"))
+ || ops[j].op == OP_LEAVE)
+ {
+ ops[j].flags |= OPF_RMD;
+ }
+ else if (!(g_ida_func_attr & IDAFA_NORETURN))
+ ferr(&ops[j], "'pop ebp' expected\n");
+
+ if (g_stack_fsz != 0) {
+ if (ops[j - 1].op == OP_MOV
+ && IS(opr_name(&ops[j - 1], 0), "esp")
+ && IS(opr_name(&ops[j - 1], 1), "ebp"))
+ {
+ ops[j - 1].flags |= OPF_RMD;
+ }
+ else if (ops[j].op != OP_LEAVE
+ && !(g_ida_func_attr & IDAFA_NORETURN))
+ {
+ ferr(&ops[j - 1], "esp restore expected\n");
+ }
+
+ if (ecx_push && ops[j - 2].op == OP_POP
+ && IS(opr_name(&ops[j - 2], 0), "ecx"))
+ {
+ ferr(&ops[j - 2], "unexpected ecx pop\n");
+ }
+ }
+
+ found = 1;
+ i++;
+ } while (i < opcnt);
+ }
+ else {
+ for (i = 0; i < opcnt; i++) {
+ if (ops[i].op == OP_PUSH || (ops[i].flags & (OPF_JMP|OPF_TAIL)))
+ break;
+ if (ops[i].op == OP_SUB && ops[i].operand[0].reg == xSP
+ && ops[i].operand[1].type == OPT_CONST)
+ {
+ g_sp_frame = 1;
+ break;
+ }
+ }
+
+ found = 0;
+ if (g_sp_frame)
+ {
+ g_stack_fsz = ops[i].operand[1].val;
+ ops[i].flags |= OPF_RMD;
+
+ i++;
+ do {
+ for (; i < opcnt; i++)
+ if (ops[i].op == OP_RET)
+ break;
+ j = i - 1;
+ if (i == opcnt && (ops[j].flags & OPF_JMP)) {
+ if (found && is_like_tailjmp(j))
+ break;
+ j--;
+ }
+
+ if (ops[j].op != OP_ADD
+ || !IS(opr_name(&ops[j], 0), "esp")
+ || ops[j].operand[1].type != OPT_CONST
+ || ops[j].operand[1].val != g_stack_fsz)
+ ferr(&ops[j], "'add esp' expected\n");
+ ops[j].flags |= OPF_RMD;
+
+ found = 1;
+ i++;
+ } while (i < opcnt);
+ }
+ }
+
+ // pass2:
+ // - parse calls with labels
+ // - resolve all branches
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ po->bt_i = -1;
+ po->btj = NULL;
+
+ if (po->flags & OPF_RMD)
+ continue;
+
+ if (po->op == OP_CALL) {
+ pp = NULL;
+
+ if (po->operand[0].type == OPT_LABEL) {
+ tmpname = opr_name(po, 0);
+ if (IS_START(tmpname, "loc_"))
+ ferr(po, "call to loc_*\n");
+ pp_c = proto_parse(fhdr, tmpname, 0);
+ if (pp_c == NULL)
+ ferr(po, "proto_parse failed for call '%s'\n", tmpname);
+
+ pp = proto_clone(pp_c);
+ my_assert_not(pp, NULL);
+ }
+ else if (po->datap != NULL) {
+ pp = calloc(1, sizeof(*pp));
+ my_assert_not(pp, NULL);
+
+ ret = parse_protostr(po->datap, pp);
+ if (ret < 0)
+ ferr(po, "bad protostr supplied: %s\n", (char *)po->datap);
+ free(po->datap);
+ po->datap = NULL;
+ }
+
+ if (pp != NULL) {
+ if (pp->is_fptr)
+ check_func_pp(po, pp, "fptr var call");
+ if (pp->is_noreturn)
+ po->flags |= OPF_TAIL;
+ }
+ po->pp = pp;
+ continue;
+ }
+
+ if (!(po->flags & OPF_JMP) || po->op == OP_RET)
+ continue;
+
+ if (po->operand[0].type == OPT_REGMEM) {
+ char *p = strchr(po->operand[0].name, '[');
+ if (p == NULL)
+ goto tailcall;
+ ret = p - po->operand[0].name;
+ strncpy(buf1, po->operand[0].name, ret);
+ buf1[ret] = 0;
+
+ for (j = 0, pd = NULL; j < g_func_pd_cnt; j++) {
+ if (IS(g_func_pd[j].label, buf1)) {
+ pd = &g_func_pd[j];
+ break;
+ }
+ }
+ if (pd == NULL)
+ //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);
+
+ // find all labels, link
+ for (j = 0; j < pd->count; j++) {
+ for (l = 0; l < opcnt; l++) {
+ if (g_labels[l][0] && IS(g_labels[l], pd->d[j].u.label)) {
+ add_label_ref(&g_label_refs[l], i);
+ pd->d[j].bt_i = l;
+ break;
+ }
+ }
+ }
+
+ po->btj = pd;
+ continue;
+ }
+
+ for (l = 0; l < opcnt; l++) {
+ if (g_labels[l][0] && IS(po->operand[0].name, g_labels[l])) {
+ if (l == i + 1 && po->op == OP_JMP) {
+ // yet another alignment type..
+ po->flags |= OPF_RMD;
+ break;
+ }
+ add_label_ref(&g_label_refs[l], i);
+ po->bt_i = l;
+ break;
+ }
+ }
+
+ if (po->bt_i != -1 || (po->flags & OPF_RMD))
+ continue;
+
+ if (po->operand[0].type == OPT_LABEL)
+ // assume tail call
+ goto tailcall;
+
+ ferr(po, "unhandled branch\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
+ }
+
+ // pass3:
+ // - remove dead labels
+ // - process calls
+ for (i = 0; i < opcnt; i++)
+ {
+ if (g_labels[i][0] != 0 && g_label_refs[i].i == -1)
+ g_labels[i][0] = 0;
+
+ po = &ops[i];
+ if (po->flags & OPF_RMD)
+ continue;
+
+ if (po->op == OP_CALL)
+ {
+ tmpname = opr_name(po, 0);
+ pp = po->pp;
+ if (pp == NULL)
+ {
+ // indirect call
+ pp_c = resolve_icall(i, opcnt, &l);
+ if (pp_c != NULL) {
+ if (!pp_c->is_func && !pp_c->is_fptr)
+ ferr(po, "call to non-func: %s\n", pp_c->name);
+ pp = proto_clone(pp_c);
+ my_assert_not(pp, NULL);
+ if (l)
+ // not resolved just to single func
+ pp->is_fptr = 1;
+
+ switch (po->operand[0].type) {
+ case OPT_REG:
+ // we resolved this call and no longer need the register
+ po->regmask_src &= ~(1 << po->operand[0].reg);
+ break;
+ case OPT_REGMEM:
+ pp->is_fptr = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ if (pp == NULL) {
+ pp = calloc(1, sizeof(*pp));
+ my_assert_not(pp, NULL);
+ pp->is_fptr = 1;
+ 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");
+ pp->is_unresolved = 1;
+ j = 0;
+ }
+ j /= 4;
+ if (j > ARRAY_SIZE(pp->arg))
+ ferr(po, "esp adjust too large: %d\n", j);
+ pp->ret_type.name = strdup("int");
+ pp->argc = pp->argc_stack = j;
+ for (arg = 0; arg < pp->argc; arg++)
+ pp->arg[arg].type.name = strdup("int");
+ }
+ po->pp = pp;
+ }
+
+ // 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, &l);
+ if (ret >= 0) {
+ if (pp->is_vararg) {
+ if (j / 4 < pp->argc_stack)
+ ferr(po, "esp adjust is too small: %x < %x\n",
+ j, pp->argc_stack * 4);
+ // modify pp to make it have varargs as normal args
+ arg = pp->argc;
+ pp->argc += j / 4 - pp->argc_stack;
+ for (; arg < pp->argc; arg++) {
+ pp->arg[arg].type.name = strdup("int");
+ pp->argc_stack++;
+ }
+ if (pp->argc > ARRAY_SIZE(pp->arg))
+ ferr(po, "too many args for '%s'\n", tmpname);
+ }
+ if (pp->argc_stack != j / 4)
+ ferr(po, "stack tracking failed for '%s': %x %x\n",
+ tmpname, pp->argc_stack * 4, j);
+
+ ops[ret].flags |= OPF_RMD;
+ if (ops[ret].op == OP_POP && j > 4) {
+ // deal with multi-pop stack adjust
+ j = pp->argc_stack;
+ while (ops[ret].op == OP_POP && j > 0 && ret < opcnt) {
+ ops[ret].flags |= OPF_RMD;
+ j--;
+ ret++;
+ }
+ }
+ else if (!l) {
+ // a bit of a hack, but deals with use of
+ // single adj for multiple calls
+ ops[ret].operand[1].val -= j;
+ }
+ }
+ else if (pp->is_vararg)
+ ferr(po, "missing esp_adjust for vararg func '%s'\n",
+ pp->name);
+
+ 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_tmp64 = 1;
+ }
+ }
+
+ // pass4:
+ // - find POPs for PUSHes, rm both
+ // - scan for STD/CLD, propagate DF
+ // - scan for all used registers
+ // - find flag set ops for their users
+ // - do unreselved calls
+ // - declare indirect functions
+ for (i = 0; i < opcnt; i++) {
+ po = &ops[i];
+ if (po->flags & OPF_RMD)
+ continue;
+
+ if (po->op == OP_PUSH && (po->flags & OPF_RSAVE)) {
+ reg = po->operand[0].reg;
+ if (!(regmask & (1 << reg)))
+ // not a reg save after all, rerun scan_for_pop
+ po->flags &= ~OPF_RSAVE;
+ else
+ regmask_save |= 1 << reg;
+ }
+
+ if (po->op == OP_PUSH && po->argnum == 0
+ && !(po->flags & OPF_RSAVE) && !g_func_pp->is_userstack)
+ {
+ if (po->operand[0].type == OPT_REG)
+ {
+ reg = po->operand[0].reg;
+ if (reg < 0)
+ ferr(po, "reg not set for push?\n");
+
+ depth = 0;
+ ret = scan_for_pop(i + 1, opcnt,
+ 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 * 4, 0, &depth, 1);
+ continue;
+ }
+ ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, 0);
+ if (ret == 0) {
+ arg = OPF_RMD;
+ if (regmask & (1 << reg)) {
+ if (regmask_save & (1 << reg))
+ ferr(po, "%s already saved?\n", po->operand[0].name);
+ arg = OPF_RSAVE;
+ }
+ po->flags |= arg;
+ scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, arg);
+ continue;
+ }
+ }
+ else if (po->operand[0].type == OPT_CONST) {
+ for (j = i + 1; j < opcnt; j++) {
+ if ((ops[j].flags & (OPF_JMP|OPF_TAIL|OPF_RSAVE))
+ || ops[j].op == OP_PUSH || g_labels[i][0] != 0)
+ {
+ break;
+ }
+
+ if (!(ops[j].flags & OPF_RMD) && ops[j].op == OP_POP)
+ {
+ po->flags |= OPF_RMD;
+ ops[j].datap = po;
+ break;
+ }
+ }
+ }
+ }
+
+ if (po->op == OP_STD) {
+ po->flags |= OPF_DF | OPF_RMD;
+ scan_propagate_df(i + 1, opcnt);
+ }
+
+ 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 + 1, opcnt, i + opcnt * 5, OPF_EBP_S);
+ else
+ regmask_now &= ~(1 << xBP);
+ }
+ }
+
+ regmask |= regmask_now;
+
+ if (po->flags & OPF_CC)
+ {
+ int setters[16], cnt = 0, branched = 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");
+
+ 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 arith op, or branch, make it calculate flags explicitly
+ if (tmp_op->op == OP_TEST || tmp_op->op == OP_CMP)