+}
+
+static void resolve_branches_parse_calls(int opcnt)
+{
+ const struct parsed_proto *pp_c;
+ struct parsed_proto *pp;
+ struct parsed_data *pd;
+ struct parsed_op *po;
+ const char *tmpname;
+ int i, l, ret;
+
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ po->bt_i = -1;
+ po->btj = NULL;
+
+ 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(g_fhdr, tmpname, g_header_mode);
+ if (!g_header_mode && pp_c == NULL)
+ ferr(po, "proto_parse failed for call '%s'\n", tmpname);
+
+ if (pp_c != NULL) {
+ 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) {
+ pd = try_resolve_jumptab(i, opcnt);
+ if (pd == NULL)
+ goto tailcall;
+
+ po->btj = pd;
+ continue;
+ }
+
+ for (l = 0; l < opcnt; l++) {
+ if (g_labels[l] != NULL
+ && IS(po->operand[0].name, g_labels[l]))
+ {
+ if (l == i + 1 && po->op == OP_JMP) {
+ // yet another alignment type..
+ po->flags |= OPF_RMD|OPF_DONE;
+ 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
+ }
+}
+
+static void scan_prologue_epilogue(int opcnt)
+{
+ int ecx_push = 0, esp_sub = 0;
+ int found;
+ int i, j, l;
+
+ if (ops[0].op == OP_PUSH && IS(opr_name(&ops[0], 0), "ebp")
+ && ops[1].op == OP_MOV
+ && IS(opr_name(&ops[1], 0), "ebp")
+ && IS(opr_name(&ops[1], 1), "esp"))
+ {
+ g_bp_frame = 1;
+ ops[0].flags |= OPF_RMD | OPF_DONE;
+ ops[1].flags |= OPF_RMD | OPF_DONE;
+ i = 2;
+
+ if (ops[2].op == OP_SUB && IS(opr_name(&ops[2], 0), "esp")) {
+ g_stack_fsz = opr_const(&ops[2], 1);
+ ops[2].flags |= OPF_RMD | OPF_DONE;
+ i++;
+ }
+ else {
+ // another way msvc builds stack frame..
+ i = 2;
+ while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
+ g_stack_fsz += 4;
+ ops[i].flags |= OPF_RMD | OPF_DONE;
+ ecx_push++;
+ i++;
+ }
+ // and another way..
+ if (i == 2 && ops[i].op == OP_MOV && ops[i].operand[0].reg == xAX
+ && ops[i].operand[1].type == OPT_CONST
+ && ops[i + 1].op == OP_CALL
+ && IS(opr_name(&ops[i + 1], 0), "__alloca_probe"))
+ {
+ g_stack_fsz += ops[i].operand[1].val;
+ ops[i].flags |= OPF_RMD | OPF_DONE;
+ i++;
+ ops[i].flags |= OPF_RMD | OPF_DONE;
+ i++;
+ }
+ }
+
+ found = 0;
+ do {
+ for (; i < opcnt; i++)
+ if (ops[i].flags & OPF_TAIL)
+ break;
+ j = i - 1;
+ if (i == opcnt && (ops[j].flags & OPF_JMP)) {
+ if (ops[j].bt_i != -1 || ops[j].btj != NULL)
+ break;
+ i--;
+ j--;
+ }
+
+ if ((ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ebp"))
+ || ops[j].op == OP_LEAVE)
+ {
+ ops[j].flags |= OPF_RMD | OPF_DONE;
+ }
+ else if (ops[i].op == OP_CALL && ops[i].pp != NULL
+ && ops[i].pp->is_noreturn)
+ {
+ // on noreturn, msvc sometimes cleans stack, sometimes not
+ i++;
+ found = 1;
+ continue;
+ }
+ else if (!(g_ida_func_attr & IDAFA_NORETURN))
+ ferr(&ops[j], "'pop ebp' expected\n");
+
+ if (g_stack_fsz != 0) {
+ if (ops[j].op == OP_LEAVE)
+ j--;
+ else if (ops[j].op == OP_POP
+ && 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 | OPF_DONE;
+ j -= 2;
+ }
+ else if (!(g_ida_func_attr & IDAFA_NORETURN))
+ {
+ ferr(&ops[j], "esp restore expected\n");
+ }
+
+ if (ecx_push && j >= 0 && ops[j].op == OP_POP
+ && IS(opr_name(&ops[j], 0), "ecx"))
+ {
+ ferr(&ops[j], "unexpected ecx pop\n");
+ }
+ }
+
+ found = 1;
+ i++;
+ } while (i < opcnt);
+
+ if (!found)
+ ferr(ops, "missing ebp epilogue\n");
+ return;
+ }
+
+ // non-bp frame
+ i = 0;
+ while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
+ ops[i].flags |= OPF_RMD | OPF_DONE;
+ g_stack_fsz += 4;
+ ecx_push++;
+ i++;
+ }
+
+ for (; 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_stack_fsz = ops[i].operand[1].val;
+ ops[i].flags |= OPF_RMD | OPF_DONE;
+ esp_sub = 1;
+ break;
+ }
+ }
+
+ if (ecx_push && !esp_sub) {
+ // could actually be args for a call..
+ for (; i < opcnt; i++)
+ if (ops[i].op != OP_PUSH)
+ break;