static int g_header_mode;
#define ferr(op_, fmt, ...) do { \
- printf("%s:%d: error: [%s] '%s': " fmt, asmfn, (op_)->asmln, g_func, \
- dump_op(op_), ##__VA_ARGS__); \
+ printf("%s:%d: error %u: [%s] '%s': " fmt, asmfn, (op_)->asmln, \
+ __LINE__, g_func, dump_op(op_), ##__VA_ARGS__); \
fcloseall(); \
exit(1); \
} while (0)
static int scan_for_esp_adjust(int i, int opcnt,
int adj_expect, int *adj, int *is_multipath, int do_update)
{
+ int adj_expect_unknown = 0;
struct parsed_op *po;
int first_pop = -1;
+ int adj_best = 0;
*adj = *is_multipath = 0;
+ if (adj_expect < 0) {
+ adj_expect_unknown = 1;
+ adj_expect = 32 * 4; // enough?
+ }
for (; i < opcnt && *adj < adj_expect; i++) {
if (g_labels[i] != NULL)
}
*adj += lmod_bytes(po, po->operand[0].lmod);
+ if (*adj > adj_best)
+ adj_best = *adj;
}
else if (po->flags & (OPF_JMP|OPF_TAIL)) {
if (po->op == OP_JMP && po->btj == NULL) {
break;
if (po->pp != NULL && po->pp->is_stdcall)
break;
+ if (adj_expect_unknown && first_pop >= 0)
+ break;
// assume it's another cdecl call
}
}
if (first_pop >= 0) {
- // probably 'pop ecx' was used..
+ // probably only 'pop ecx' was used
+ *adj = adj_best;
return first_pop;
}
ferr(&ops[j], "'pop ebp' expected\n");
if (g_stack_fsz != 0) {
- if (ops[j - 1].op == OP_MOV
+ 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 (ops[j].op != OP_LEAVE
- && !(g_ida_func_attr & IDAFA_NORETURN))
+ else if (!(g_ida_func_attr & IDAFA_NORETURN))
{
- ferr(&ops[j - 1], "esp restore expected\n");
+ ferr(&ops[j], "esp restore expected\n");
}
- if (ecx_push && ops[j - 2].op == OP_POP
- && IS(opr_name(&ops[j - 2], 0), "ecx"))
+ if (ecx_push && j >= 0 && ops[j].op == OP_POP
+ && IS(opr_name(&ops[j], 0), "ecx"))
{
- ferr(&ops[j - 2], "unexpected ecx pop\n");
+ ferr(&ops[j], "unexpected ecx pop\n");
}
}
struct parsed_proto *pp;
int multipath = 0;
int adj = 0;
- int ret;
+ int j, ret;
pp = po->pp;
if (pp == NULL || pp->is_vararg || pp->argc_reg != 0)
return NULL;
if (multipath)
return NULL;
- if (ops[ret].op == OP_POP && adj != 4)
- return NULL;
+ if (ops[ret].op == OP_POP) {
+ for (j = 1; j < adj / 4; j++) {
+ if (ops[ret + j].op != OP_POP
+ || ops[ret + j].operand[0].reg != xCX)
+ {
+ return NULL;
+ }
+ }
+ }
}
*adj_i = ret;
pp->is_fptr = 1;
ret = scan_for_esp_adjust(i + 1, opcnt,
- 32*4, &adj, &multipath, 0);
+ -1, &adj, &multipath, 0);
if (ret < 0 || adj < 0) {
if (!g_allow_regfunc)
ferr(po, "non-__cdecl indirect call unhandled yet\n");
// look for and make use of esp adjust
multipath = 0;
ret = -1;
- if (!pp->is_stdcall && pp->argc_stack > 0)
+ if (!pp->is_stdcall && pp->argc_stack > 0) {
+ int adj_expect = pp->is_vararg ? -1 : pp->argc_stack * 4;
ret = scan_for_esp_adjust(i + 1, opcnt,
- pp->argc_stack * 4, &adj, &multipath, 0);
+ adj_expect, &adj, &multipath, 0);
+ }
if (ret >= 0) {
if (pp->is_vararg) {
if (adj / 4 < pp->argc_stack) {
if (pp != NULL) {
if (j >= 0) {
// commit esp adjust
- ops[j].flags |= OPF_RMD;
if (ops[j].op != OP_POP)
patch_esp_adjust(&ops[j], pp->argc_stack * 4);
- else
- ops[j].flags |= OPF_DONE;
+ else {
+ for (l = 0; l < pp->argc_stack; l++)
+ ops[j + l].flags |= OPF_DONE | OPF_RMD;
+ }
}
if (strstr(pp->ret_type.name, "int64"))
int regmask_dep;
int max_bp_offset = 0;
int has_ret;
- int i, j, ret;
+ int i, j, l;
+ int ret;
pp_c = proto_parse(g_fhdr, funcn, 1);
if (pp_c != NULL)
if (pp != NULL) {
if (j >= 0) {
// commit esp adjust
- ops[j].flags |= OPF_RMD;
if (ops[j].op != OP_POP)
patch_esp_adjust(&ops[j], pp->argc_stack * 4);
- else
- ops[j].flags |= OPF_DONE;
+ else {
+ for (l = 0; l < pp->argc_stack; l++)
+ ops[j + l].flags |= OPF_DONE | OPF_RMD;
+ }
}
po->flags |= OPF_DONE;