From: notaz Date: Mon, 19 Jan 2015 01:45:04 +0000 (+0200) Subject: translate: further work on header gen X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9af2d373a75efbac33d111d4a820cbcb300e0efd;p=ia32rtools.git translate: further work on header gen --- diff --git a/tools/translate.c b/tools/translate.c index 9f6edf5..c464758 100644 --- a/tools/translate.c +++ b/tools/translate.c @@ -48,13 +48,14 @@ enum op_flags { OPF_REP = (1 << 8), /* prefixed by rep */ OPF_REPZ = (1 << 9), /* rep is repe/repz */ OPF_REPNZ = (1 << 10), /* rep is repne/repnz */ - OPF_FARG = (1 << 11), /* push collected as func arg (no reuse) */ - OPF_EBP_S = (1 << 12), /* ebp used as scratch here, not BP */ - OPF_DF = (1 << 13), /* DF flag set */ - OPF_ATAIL = (1 << 14), /* tail call with reused arg frame */ - OPF_32BIT = (1 << 15), /* 32bit division */ - OPF_LOCK = (1 << 16), /* op has lock prefix */ - OPF_VAPUSH = (1 << 17), /* vararg ptr push (as call arg) */ + OPF_FARG = (1 << 11), /* push collected as func arg */ + OPF_FARGNR = (1 << 12), /* push collected as func arg (no reuse) */ + OPF_EBP_S = (1 << 13), /* ebp used as scratch here, not BP */ + OPF_DF = (1 << 14), /* DF flag set */ + OPF_ATAIL = (1 << 15), /* tail call with reused arg frame */ + OPF_32BIT = (1 << 16), /* 32bit division */ + OPF_LOCK = (1 << 17), /* op has lock prefix */ + OPF_VAPUSH = (1 << 18), /* vararg ptr push (as call arg) */ }; enum op_op { @@ -234,6 +235,7 @@ static int g_stack_fsz; static int g_ida_func_attr; static int g_allow_regfunc; static int g_quiet_pp; +static int g_header_mode; #define ferr(op_, fmt, ...) do { \ printf("%s:%d: error: [%s] '%s': " fmt, asmfn, (op_)->asmln, g_func, \ @@ -2190,7 +2192,7 @@ static int scan_for_pop(int i, int opcnt, const char *reg, && po->operand[0].type == OPT_REG && IS(po->operand[0].name, reg)) { - if (po->op == OP_PUSH && !(po->flags & OPF_FARG)) { + if (po->op == OP_PUSH && !(po->flags & OPF_FARGNR)) { depth++; if (depth > *maxdepth) *maxdepth = depth; @@ -2250,6 +2252,27 @@ static int scan_for_pop_ret(int i, int opcnt, const char *reg, return found ? 0 : -1; } +// XXX: merge with scan_for_pop? +static void scan_for_pop_const(int i, int opcnt) +{ + int j; + + 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] != NULL) + { + break; + } + + if (!(ops[j].flags & OPF_RMD) && ops[j].op == OP_POP) + { + ops[i].flags |= OPF_RMD; + ops[j].datap = &ops[i]; + break; + } + } +} + static void scan_propagate_df(int i, int opcnt) { struct parsed_op *po = &ops[i]; @@ -2492,15 +2515,15 @@ static int scan_for_reg_clear(int i, int reg) } // scan for positive, constant esp adjust -static int scan_for_esp_adjust(int i, int opcnt, int *adj, - int *multipath) +static int scan_for_esp_adjust(int i, int opcnt, + unsigned int adj_expect, int *adj, int *multipath) { struct parsed_op *po; int first_pop = -1; *adj = *multipath = 0; - for (; i < opcnt; i++) { + for (; i < opcnt && *adj < adj_expect; i++) { po = &ops[i]; if (g_labels[i] != NULL) @@ -2596,6 +2619,9 @@ static const struct parsed_proto *try_recover_pp( int stack_ra = 0; int offset = 0; + if (g_header_mode) + return NULL; + parse_stack_access(po, opr->name, ofs_reg, &offset, &stack_ra, NULL, 0); if (ofs_reg[0] != 0) @@ -2631,10 +2657,13 @@ static const struct parsed_proto *try_recover_pp( pp = proto_parse(g_fhdr, buf, g_quiet_pp); } else if (opr->type == OPT_OFFSET || opr->type == OPT_LABEL) { - pp = proto_parse(g_fhdr, opr->name, 0); - if (pp == NULL) - ferr(po, "proto_parse failed for icall from '%s'\n", opr->name); - check_func_pp(po, pp, "reg-fptr ref"); + pp = proto_parse(g_fhdr, opr->name, g_quiet_pp); + if (pp == NULL) { + if (!g_header_mode) + ferr(po, "proto_parse failed for icall to '%s'\n", opr->name); + } + else + check_func_pp(po, pp, "reg-fptr ref"); } return pp; @@ -2723,6 +2752,224 @@ static void scan_for_call_type(int i, const struct parsed_opr *opr, *pp_found = pp; } +// early check for tail call or branch back +static int is_like_tailjmp(int j) +{ + if (!(ops[j].flags & OPF_JMP)) + return 0; + + if (ops[j].op == OP_JMP && !ops[j].operand[0].had_ds) + // probably local branch back.. + return 1; + if (ops[j].op == OP_CALL) + // probably noreturn call.. + return 1; + + return 0; +} + +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; + ops[1].flags |= OPF_RMD; + 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; + 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; + 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; + i++; + ops[i].flags |= OPF_RMD; + i++; + } + } + + found = 0; + 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_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); + + return; + } + + // non-bp frame + i = 0; + while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) { + ops[i].flags |= OPF_RMD; + 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; + 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; + + if (ops[i].op == OP_CALL && ops[i].operand[0].type == OPT_LABEL) { + const struct parsed_proto *pp; + pp = proto_parse(g_fhdr, opr_name(&ops[i], 0), 1); + j = pp ? pp->argc_stack : 0; + while (i > 0 && j > 0) { + i--; + if (ops[i].op == OP_PUSH) { + ops[i].flags &= ~OPF_RMD; + j--; + } + } + if (j != 0) + ferr(&ops[i], "unhandled prologue\n"); + + // recheck + i = g_stack_fsz = ecx_push = 0; + while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) { + if (!(ops[i].flags & OPF_RMD)) + break; + g_stack_fsz += 4; + ecx_push++; + i++; + } + } + } + + found = 0; + if (ecx_push || esp_sub) + { + g_sp_frame = 1; + + 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 (ecx_push > 0) { + for (l = 0; l < ecx_push; l++) { + if (ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ecx")) + /* pop ecx */; + else if (ops[j].op == OP_ADD + && IS(opr_name(&ops[j], 0), "esp") + && ops[j].operand[1].type == OPT_CONST) + { + /* add esp, N */ + ecx_push -= ops[j].operand[1].val / 4 - 1; + } + else + ferr(&ops[j], "'pop ecx' expected\n"); + + ops[j].flags |= OPF_RMD; + j--; + } + if (l != ecx_push) + ferr(&ops[j], "epilogue scan failed\n"); + + found = 1; + } + + if (esp_sub) { + 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; + ops[j].operand[1].val = 0; // hack for stack arg scanner + found = 1; + } + + i++; + } while (i < opcnt); + } +} + static const struct parsed_proto *resolve_icall(int i, int opcnt, int *multi_src) { @@ -2820,6 +3067,116 @@ static int try_resolve_const(int i, const struct parsed_opr *opr, return -1; } +static struct parsed_proto *process_call(int i, int opcnt) +{ + struct parsed_op *po = &ops[i]; + const struct parsed_proto *pp_c; + struct parsed_proto *pp; + const char *tmpname; + int j = 0, l = 0; + int ret, arg; + + 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, ~0, &j, &l); + if (ret < 0 || j < 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, + pp->argc_stack * 4, &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) { + fnote(po, "(this call)\n"); + ferr(&ops[ret], "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) { + if (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); + + return pp; +} + static int collect_call_args_r(struct parsed_op *po, int i, struct parsed_proto *pp, int *regmask, int *save_arg_vars, int *arg_grp, int arg, int magic, int need_op_saving, int may_reuse) @@ -2913,7 +3270,9 @@ static int collect_call_args_r(struct parsed_op *po, int i, ferr(po, "arg collect %d/%d hit esp adjust of %d\n", arg, pp->argc, ops[j].operand[1].val); } - else if (ops[j].op == OP_POP) { + else if (ops[j].op == OP_POP && !(ops[j].flags & OPF_RMD) + && ops[j].datap == NULL) + { if (pp->is_unresolved) break; @@ -2926,7 +3285,7 @@ static int collect_call_args_r(struct parsed_op *po, int i, may_reuse = 1; } - else if (ops[j].op == OP_PUSH && !(ops[j].flags & OPF_FARG)) + else if (ops[j].op == OP_PUSH && !(ops[j].flags & OPF_FARGNR)) { if (pp->is_unresolved && (ops[j].flags & OPF_RMD)) break; @@ -2976,8 +3335,9 @@ static int collect_call_args_r(struct parsed_op *po, int i, // but that can't happen if we didn't branch, so they // can be removed from future searches (handles nested calls) if (!may_reuse) - ops[j].flags |= OPF_FARG; + ops[j].flags |= OPF_FARGNR; + ops[j].flags |= OPF_FARG; ops[j].flags &= ~OPF_RSAVE; // check for __VALIST @@ -3100,22 +3460,6 @@ static int collect_call_args(struct parsed_op *po, int i, return ret; } -// early check for tail call or branch back -static int is_like_tailjmp(int j) -{ - if (!(ops[j].flags & OPF_JMP)) - return 0; - - if (ops[j].op == OP_JMP && !ops[j].operand[0].had_ds) - // probably local branch back.. - return 1; - if (ops[j].op == OP_CALL) - // probably noreturn call.. - return 1; - - return 0; -} - static void pp_insert_reg_arg(struct parsed_proto *pp, const char *reg) { int i; @@ -3229,6 +3573,25 @@ static void output_pp_attrs(FILE *fout, const struct parsed_proto *pp, fprintf(fout, "noreturn "); } +static int get_pp_arg_regmask(const struct parsed_proto *pp) +{ + int regmask = 0; + int i, reg; + + for (i = 0; i < pp->argc; i++) { + if (pp->arg[i].reg != NULL) { + reg = char_array_i(regs_r32, + ARRAY_SIZE(regs_r32), pp->arg[i].reg); + if (reg < 0) + ferr(ops, "arg '%s' of func '%s' is not a reg?\n", + pp->arg[i].reg, pp->name); + regmask |= 1 << reg; + } + } + + return regmask; +} + static char *saved_arg_name(char *buf, size_t buf_size, int grp, int num) { char buf1[16]; @@ -3241,6 +3604,8 @@ static char *saved_arg_name(char *buf, size_t buf_size, int grp, int num) return buf; } +static void gen_x_cleanup(int opcnt); + static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) { struct parsed_op *po, *delayed_flag_op = NULL, *tmp_op; @@ -3278,172 +3643,11 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (g_func_pp == NULL) ferr(ops, "proto_parse failed for '%s'\n", funcn); - 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); - if (reg < 0) - ferr(ops, "arg '%s' is not a reg?\n", g_func_pp->arg[i].reg); - regmask_arg |= 1 << reg; - } - } + regmask_arg = get_pp_arg_regmask(g_func_pp); // pass1: // - handle ebp/esp frame, remove ops related to it - 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")) - { - int ecx_push = 0; - - g_bp_frame = 1; - ops[0].flags |= OPF_RMD; - ops[1].flags |= OPF_RMD; - 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; - 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; - 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; - i++; - ops[i].flags |= OPF_RMD; - i++; - } - } - - found = 0; - 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_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 { - int ecx_push = 0, esp_sub = 0; - - i = 0; - while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) { - ops[i].flags |= OPF_RMD; - 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; - esp_sub = 1; - break; - } - } - - found = 0; - if (ecx_push || esp_sub) - { - g_sp_frame = 1; - - 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 (ecx_push > 0) { - for (l = 0; l < ecx_push; l++) { - if (ops[j].op != OP_POP - || !IS(opr_name(&ops[j], 0), "ecx")) - { - ferr(&ops[j], "'pop ecx' expected\n"); - } - ops[j].flags |= OPF_RMD; - j--; - } - - found = 1; - } - - if (esp_sub) { - 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); - } - } + scan_prologue_epilogue(opcnt); // pass2: // - parse calls with labels @@ -3552,97 +3756,7 @@ tailcall: 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); + pp = process_call(i, opcnt); if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) { // since we know the args, collect them @@ -3676,7 +3790,7 @@ tailcall: regmask_save |= 1 << reg; } - if (po->op == OP_PUSH && po->p_argnum == 0 + if (po->op == OP_PUSH && !(po->flags & OPF_FARG) && !(po->flags & OPF_RSAVE) && !g_func_pp->is_userstack) { if (po->operand[0].type == OPT_REG) @@ -3711,20 +3825,7 @@ tailcall: } } 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] != NULL) - { - break; - } - - if (!(ops[j].flags & OPF_RMD) && ops[j].op == OP_POP) - { - po->flags |= OPF_RMD; - ops[j].datap = po; - break; - } - } + scan_for_pop_const(i, opcnt); } } @@ -5176,7 +5277,13 @@ tailcall: fprintf(fout, "}\n\n"); - // cleanup + gen_x_cleanup(opcnt); +} + +static void gen_x_cleanup(int opcnt) +{ + int i; + for (i = 0; i < opcnt; i++) { struct label_ref *lr, *lr_del; @@ -5221,6 +5328,9 @@ struct func_proto_dep { static struct func_prototype *hg_fp; static int hg_fp_cnt; +static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, + int count); + static struct func_proto_dep *hg_fp_find_dep(struct func_prototype *fp, const char *name) { @@ -5266,10 +5376,13 @@ static int hg_fp_cmp_id(const void *p1_, const void *p2_) static void gen_hdr(const char *funcn, int opcnt) { + const struct parsed_proto *pp_c; + struct parsed_proto *pp; struct func_prototype *fp; struct func_proto_dep *dep; struct parsed_data *pd; struct parsed_op *po; + const char *tmpname; int regmask_save = 0; int regmask_dst = 0; int regmask_dep = 0; @@ -5288,9 +5401,26 @@ static void gen_hdr(const char *funcn, int opcnt) fp = &hg_fp[hg_fp_cnt]; snprintf(fp->name, sizeof(fp->name), "%s", funcn); fp->id = hg_fp_cnt; + fp->argc_stack = -1; hg_fp_cnt++; + // perhaps already in seed header? + pp_c = proto_parse(g_fhdr, funcn, 1); + if (pp_c != NULL) { + fp->argc_stack = pp_c->argc_stack; + fp->regmask_dep = get_pp_arg_regmask(pp_c); + fp->has_ret = !IS(pp_c->ret_type.name, "void"); + return; + } + + g_bp_frame = g_sp_frame = g_stack_fsz = 0; + g_stack_frame_used = 0; + // pass1: + // - handle ebp/esp frame, remove ops related to it + scan_prologue_epilogue(opcnt); + + // pass2: // - collect calls // - resolve all branches for (i = 0; i < opcnt; i++) @@ -5303,9 +5433,30 @@ static void gen_hdr(const char *funcn, int opcnt) continue; if (po->op == OP_CALL) { - if (po->operand[0].type == OPT_LABEL) - hg_fp_add_dep(fp, opr_name(po, 0)); + tmpname = opr_name(po, 0); + pp = NULL; + if (po->operand[0].type == OPT_LABEL) { + hg_fp_add_dep(fp, tmpname); + + // perhaps a call to already known func? + pp_c = proto_parse(g_fhdr, tmpname, 1); + if (pp_c != NULL) + pp = proto_clone(pp_c); + } + 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 && pp->is_noreturn) + po->flags |= OPF_TAIL; + + po->pp = pp; continue; } @@ -5348,26 +5499,48 @@ tailcall: i--; // reprocess } - // pass2: + // pass3: // - remove dead labels + // - process calls + // - handle push /pop pairs for (i = 0; i < opcnt; i++) { if (g_labels[i] != NULL && g_label_refs[i].i == -1) { free(g_labels[i]); g_labels[i] = NULL; } + + po = &ops[i]; + if (po->flags & OPF_RMD) + continue; + + if (po->op == OP_CALL) { + pp = process_call(i, opcnt); + + if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) { + int regmask_dummy = 0, save_arg_vars[MAX_ARG_GRP] = { 0, }; + // since we know the args, collect them + collect_call_args(po, i, pp, ®mask_dummy, save_arg_vars, + i + opcnt * 2); + } + } + else if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST) { + scan_for_pop_const(i, opcnt); + } } - // pass3: + // pass4: // - track saved regs // - try to figure out arg-regs for (i = 0; i < opcnt; i++) { po = &ops[i]; - if (po->flags & OPF_RMD) - continue; - if (po->op == OP_PUSH && po->operand[0].type == OPT_REG) + if (po->flags & OPF_FARG) + /* (just calculate register deps) */; + else if (po->flags & OPF_RMD) + continue; + else if (po->op == OP_PUSH && po->operand[0].type == OPT_REG) { reg = po->operand[0].reg; if (reg < 0) @@ -5391,23 +5564,6 @@ tailcall: continue; } } - else if (po->op == OP_PUSH && 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] != NULL) - { - break; - } - - if (!(ops[j].flags & OPF_RMD) && ops[j].op == OP_POP) - { - po->flags |= OPF_RMD; - ops[j].datap = po; - break; - } - } - continue; - } else if (po->op == OP_CALL) { po->regmask_dst |= 1 << xAX; @@ -5416,8 +5572,13 @@ tailcall: dep->regmask_live = regmask_save | regmask_dst; } else if (po->op == OP_RET) { - if (po->operand_cnt > 0) + if (po->operand_cnt > 0) { fp->is_stdcall = 1; + if (fp->argc_stack >= 0 + && fp->argc_stack != po->operand[0].val / 4) + ferr(po, "ret mismatch? (%d)\n", fp->argc_stack * 4); + fp->argc_stack = po->operand[0].val / 4; + } } if (has_ret != 0 && (po->flags & OPF_TAIL)) { @@ -5451,7 +5612,11 @@ tailcall: } } - l = po->regmask_src & ~(regmask_save | regmask_dst); + l = regmask_save | regmask_dst; + if (g_bp_frame && !(po->flags & OPF_EBP_S)) + l |= 1 << xBP; + + l = po->regmask_src & ~l; #if 0 if (l) fnote(po, "dep |= %04x, dst %04x, save %04x\n", l, @@ -5464,19 +5629,22 @@ tailcall: if (has_ret == -1 && (regmask_dep & (1 << xAX))) has_ret = 1; - for (i = 0; i < g_eqcnt; i++) + for (i = 0; i < g_eqcnt; i++) { if (g_eqs[i].offset > max_bp_offset && g_eqs[i].offset < 4*32) max_bp_offset = g_eqs[i].offset; + } - if (max_bp_offset > 0) { + if (fp->argc_stack < 0) { max_bp_offset = (max_bp_offset + 3) & ~3; - fp->argc_stack = max_bp_offset / 4 - 1; - if (!(g_ida_func_attr & IDAFA_BP_FRAME)) + fp->argc_stack = max_bp_offset / 4; + if ((g_ida_func_attr & IDAFA_BP_FRAME) && fp->argc_stack > 0) fp->argc_stack--; } fp->regmask_dep = regmask_dep & ~(1 << xSP); fp->has_ret = has_ret; + + gen_x_cleanup(opcnt); } static void hg_fp_resolve_deps(struct func_prototype *fp) @@ -5504,24 +5672,16 @@ static void hg_fp_resolve_deps(struct func_prototype *fp) } } -static void output_hdr(FILE *fout) +static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, + int count) { - struct func_prototype *fp; - int had_usercall = 0; + char *p, buf[NAMELEN]; + const char *cp; int regmask_dep; int argc_stack; - int i, j, arg; + int j, arg; - // resolve deps - qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_name); - for (i = 0; i < hg_fp_cnt; i++) - hg_fp_resolve_deps(&hg_fp[i]); - - // note: messes up .proto ptr, don't use - //qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_id); - - for (i = 0; i < hg_fp_cnt; i++) { - fp = &hg_fp[i]; + for (; count > 0; count--, fp++) { if (fp->has_ret == -1) fprintf(fout, "// ret unresolved\n"); #if 0 @@ -5542,9 +5702,7 @@ static void output_hdr(FILE *fout) if (regmask_dep && (fp->is_stdcall || argc_stack == 0) && (regmask_dep & ~((1 << xCX) | (1 << xDX))) == 0) { - fprintf(fout, "__fastcall "); - if (had_usercall) - fprintf(fout, " "); // align + fprintf(fout, " __fastcall "); if (!(regmask_dep & (1 << xDX)) && argc_stack == 0) argc_stack = 1; else @@ -5553,18 +5711,26 @@ static void output_hdr(FILE *fout) } else if (regmask_dep && !fp->is_stdcall) { fprintf(fout, "/*__usercall*/ "); - had_usercall = 1; } else if (regmask_dep) { fprintf(fout, "/*__userpurge*/ "); - had_usercall = 1; } else if (fp->is_stdcall) - fprintf(fout, "__stdcall "); + fprintf(fout, " __stdcall "); else - fprintf(fout, "__cdecl "); + fprintf(fout, " __cdecl "); - fprintf(fout, "%s(", fp->name); + p = strchr(fp->name, '@'); + if (p != NULL) { + memcpy(buf, fp->name, p - fp->name); + buf[p - fp->name] = 0; + cp = buf; + } + else + cp = fp->name; + if (cp[0] == '_') + cp++; + fprintf(fout, "%s(", cp); arg = 0; for (j = 0; j < xSP; j++) { @@ -5587,6 +5753,21 @@ static void output_hdr(FILE *fout) } } +static void output_hdr(FILE *fout) +{ + int i; + + // resolve deps + qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_name); + for (i = 0; i < hg_fp_cnt; i++) + hg_fp_resolve_deps(&hg_fp[i]); + + // note: messes up .proto ptr, don't use + //qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_id); + + output_hdr_fp(fout, hg_fp, hg_fp_cnt); +} + static void set_label(int i, const char *name) { const char *p; @@ -5743,7 +5924,6 @@ int main(int argc, char *argv[]) int pending_endp = 0; int skip_func = 0; int skip_warned = 0; - int header_mode = 0; int eq_alloc; int verbose = 0; int multi_seg = 0; @@ -5764,14 +5944,14 @@ int main(int argc, char *argv[]) else if (IS(argv[arg], "-m")) multi_seg = 1; else if (IS(argv[arg], "-hdr")) - header_mode = g_quiet_pp = 1; + g_header_mode = g_quiet_pp = g_allow_regfunc = 1; else break; } if (argc < arg + 3) { printf("usage:\n%s [-v] [-rf] [-m] <.c> <.asm> [rlist]*\n" - "%s -hdr <.h> <.asm> [rlist]*\n", + "%s -hdr <.asm> [rlist]*\n", argv[0], argv[0]); return 1; } @@ -6062,7 +6242,7 @@ do_pending_endp: } if (in_func && !skip_func) { - if (header_mode) + if (g_header_mode) gen_hdr(g_func, pi); else gen_func(fout, g_fhdr, g_func, pi); @@ -6244,7 +6424,7 @@ do_pending_endp: pi++; } - if (header_mode) + if (g_header_mode) output_hdr(fout); fclose(fout);