X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=tools%2Ftranslate.c;h=704d4bc85c83e4ecbcd676dd44442ac44e6ba1ae;hb=4e81a3a2e191ccf8498da6d23e96c03fc8b36c83;hp=81c555be627a981d2bb4aa4d5bdcdb948250a96f;hpb=266771390e5ccfaa1f8153de5b878f1c7226b2a0;p=ia32rtools.git diff --git a/tools/translate.c b/tools/translate.c index 81c555b..704d4bc 100644 --- a/tools/translate.c +++ b/tools/translate.c @@ -37,7 +37,7 @@ static FILE *g_fhdr; #include "masm_tools.h" enum op_flags { - OPF_RMD = (1 << 0), /* removed or optimized out */ + OPF_RMD = (1 << 0), /* removed from code generation */ OPF_DATA = (1 << 1), /* data processing - writes to dst opr */ OPF_FLAGS = (1 << 2), /* sets flags */ OPF_JMP = (1 << 3), /* branch, call */ @@ -113,7 +113,7 @@ enum op_op { // x87 // mmx OP_EMMS, - // mmx + // undefined OP_UD2, }; @@ -234,6 +234,7 @@ static int g_sp_frame; static int g_stack_frame_used; static int g_stack_fsz; static int g_ida_func_attr; +static int g_skip_func; static int g_allow_regfunc; static int g_quiet_pp; static int g_header_mode; @@ -528,7 +529,7 @@ static int guess_lmod_from_c_type(enum opr_lenmod *lmod, const struct parsed_type *c_type) { static const char *dword_types[] = { - "int", "_DWORD", "UINT_PTR", "DWORD", + "uint32_t", "int", "_DWORD", "UINT_PTR", "DWORD", "WPARAM", "LPARAM", "UINT", "__int32", "LONG", "HIMC", "BOOL", "size_t", "float", @@ -984,7 +985,8 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) } if (i == ARRAY_SIZE(op_table)) { - anote("unhandled op: '%s'\n", words[0]); + if (!g_skip_func) + aerr("unhandled op: '%s'\n", words[0]); i--; // OP_UD2 } w++; @@ -1174,7 +1176,7 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) && op->operand[0].reg == op->operand[1].reg && IS(op->operand[0].name, op->operand[1].name)) // ! ah, al.. { - op->flags |= OPF_RMD; + op->flags |= OPF_RMD | OPF_DONE; op->regmask_src = op->regmask_dst = 0; } break; @@ -1186,7 +1188,7 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) char buf[16]; snprintf(buf, sizeof(buf), "%s+0", op->operand[0].name); if (IS(buf, op->operand[1].name)) - op->flags |= OPF_RMD; + op->flags |= OPF_RMD | OPF_DONE; } break; @@ -1720,7 +1722,7 @@ static void check_func_pp(struct parsed_op *po, // fptrs must use 32bit args, callsite might have no information and // lack a cast to smaller types, which results in incorrectly masked // args passed (callee may assume masked args, it does on ARM) - if (!pp->is_oslib) { + if (!pp->is_osinc) { for (i = 0; i < pp->argc; i++) { ret = guess_lmod_from_c_type(&tmp_lmod, &pp->arg[i].type); if (ret && tmp_lmod != OPLM_DWORD) @@ -2161,7 +2163,7 @@ static int scan_for_pop(int i, int opcnt, const char *reg, return -1; // deadend } - if ((po->flags & OPF_RMD) + if ((po->flags & (OPF_RMD|OPF_DONE)) || (po->op == OP_PUSH && po->p_argnum != 0)) // arg push continue; @@ -2237,12 +2239,13 @@ static int scan_for_pop_ret(int i, int opcnt, const char *reg, continue; for (j = i - 1; j >= 0; j--) { - if (ops[j].flags & OPF_RMD) + if (ops[j].flags & (OPF_RMD|OPF_DONE)) continue; if (ops[j].flags & OPF_JMP) return -1; - if (ops[j].op == OP_POP && ops[j].operand[0].type == OPT_REG + if (ops[j].op == OP_POP && ops[j].datap == NULL + && ops[j].operand[0].type == OPT_REG && IS(ops[j].operand[0].name, reg)) { found = 1; @@ -2258,7 +2261,6 @@ 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; @@ -2270,9 +2272,10 @@ static void scan_for_pop_const(int i, int opcnt) break; } - if (!(ops[j].flags & OPF_RMD) && ops[j].op == OP_POP) + if (ops[j].op == OP_POP && !(ops[j].flags & (OPF_RMD|OPF_DONE))) { - ops[i].flags |= OPF_RMD; + ops[i].flags |= OPF_RMD | OPF_DONE; + ops[j].flags |= OPF_DONE; ops[j].datap = &ops[i]; break; } @@ -2317,7 +2320,7 @@ static void scan_propagate_df(int i, int opcnt) break; if (po->op == OP_CLD) { - po->flags |= OPF_RMD; + po->flags |= OPF_RMD | OPF_DONE; return; } } @@ -2520,20 +2523,38 @@ static int scan_for_reg_clear(int i, int reg) return -1; } +static void patch_esp_adjust(struct parsed_op *po, int adj) +{ + ferr_assert(po, po->op == OP_ADD); + ferr_assert(po, IS(opr_name(po, 0), "esp")); + ferr_assert(po, po->operand[1].type == OPT_CONST); + + // this is a bit of a hack, but deals with use of + // single adj for multiple calls + po->operand[1].val -= adj; + po->flags |= OPF_RMD; + if (po->operand[1].val == 0) + po->flags |= OPF_DONE; + ferr_assert(po, (int)po->operand[1].val >= 0); +} + // scan for positive, constant esp adjust +// multipath case is preliminary static int scan_for_esp_adjust(int i, int opcnt, - unsigned int adj_expect, int *adj, int *multipath) + int adj_expect, int *adj, int *is_multipath, int do_update) { struct parsed_op *po; int first_pop = -1; - *adj = *multipath = 0; + *adj = *is_multipath = 0; for (; i < opcnt && *adj < adj_expect; i++) { - po = &ops[i]; - if (g_labels[i] != NULL) - *multipath = 1; + *is_multipath = 1; + + po = &ops[i]; + if (po->flags & OPF_DONE) + continue; if (po->op == OP_ADD && po->operand[0].reg == xSP) { if (po->operand[1].type != OPT_CONST) @@ -2541,25 +2562,39 @@ static int scan_for_esp_adjust(int i, int opcnt, *adj += po->operand[1].val; if (*adj & 3) ferr(&ops[i], "unaligned esp adjust: %x\n", *adj); + if (do_update) { + if (!*is_multipath) + patch_esp_adjust(po, adj_expect); + else + po->flags |= OPF_RMD; + } return i; } - else if (po->op == OP_PUSH && !(po->flags & OPF_RMD)) { + else if (po->op == OP_PUSH) { //if (first_pop == -1) // first_pop = -2; // none *adj -= lmod_bytes(po, po->operand[0].lmod); } - else if (po->op == OP_POP && !(po->flags & OPF_RMD)) { - if (po->datap != NULL) // in push/pop pair? - break; - // seems like msvc only uses 'pop ecx' for stack realignment.. - if (po->operand[0].type != OPT_REG || po->operand[0].reg != xCX) - break; - if (first_pop == -1 && *adj >= 0) - first_pop = i; + else if (po->op == OP_POP) { + if (!(po->flags & OPF_DONE)) { + // seems like msvc only uses 'pop ecx' for stack realignment.. + if (po->operand[0].type != OPT_REG || po->operand[0].reg != xCX) + break; + if (first_pop == -1 && *adj >= 0) + first_pop = i; + } + if (do_update && *adj >= 0) { + po->flags |= OPF_RMD; + if (!*is_multipath) + po->flags |= OPF_DONE; + } + *adj += lmod_bytes(po, po->operand[0].lmod); } else if (po->flags & (OPF_JMP|OPF_TAIL)) { if (po->op == OP_JMP && po->btj == NULL) { + if (po->bt_i <= i) + break; i = po->bt_i - 1; continue; } @@ -2569,6 +2604,7 @@ static int scan_for_esp_adjust(int i, int opcnt, break; if (po->pp != NULL && po->pp->is_stdcall) break; + // assume it's another cdecl call } } @@ -2788,13 +2824,13 @@ static void scan_prologue_epilogue(int opcnt) && IS(opr_name(&ops[1], 1), "esp")) { g_bp_frame = 1; - ops[0].flags |= OPF_RMD; - ops[1].flags |= OPF_RMD; + 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; + ops[2].flags |= OPF_RMD | OPF_DONE; i++; } else { @@ -2802,7 +2838,7 @@ static void scan_prologue_epilogue(int opcnt) i = 2; while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) { g_stack_fsz += 4; - ops[i].flags |= OPF_RMD; + ops[i].flags |= OPF_RMD | OPF_DONE; ecx_push++; i++; } @@ -2813,9 +2849,9 @@ static void scan_prologue_epilogue(int opcnt) && IS(opr_name(&ops[i + 1], 0), "__alloca_probe")) { g_stack_fsz += ops[i].operand[1].val; - ops[i].flags |= OPF_RMD; + ops[i].flags |= OPF_RMD | OPF_DONE; i++; - ops[i].flags |= OPF_RMD; + ops[i].flags |= OPF_RMD | OPF_DONE; i++; } } @@ -2835,7 +2871,7 @@ static void scan_prologue_epilogue(int opcnt) if ((ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ebp")) || ops[j].op == OP_LEAVE) { - ops[j].flags |= OPF_RMD; + ops[j].flags |= OPF_RMD | OPF_DONE; } else if (!(g_ida_func_attr & IDAFA_NORETURN)) ferr(&ops[j], "'pop ebp' expected\n"); @@ -2845,7 +2881,7 @@ static void scan_prologue_epilogue(int opcnt) && IS(opr_name(&ops[j - 1], 0), "esp") && IS(opr_name(&ops[j - 1], 1), "ebp")) { - ops[j - 1].flags |= OPF_RMD; + ops[j - 1].flags |= OPF_RMD | OPF_DONE; } else if (ops[j].op != OP_LEAVE && !(g_ida_func_attr & IDAFA_NORETURN)) @@ -2870,7 +2906,7 @@ static void scan_prologue_epilogue(int opcnt) // non-bp frame i = 0; while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) { - ops[i].flags |= OPF_RMD; + ops[i].flags |= OPF_RMD | OPF_DONE; g_stack_fsz += 4; ecx_push++; i++; @@ -2883,7 +2919,7 @@ static void scan_prologue_epilogue(int opcnt) && ops[i].operand[1].type == OPT_CONST) { g_stack_fsz = ops[i].operand[1].val; - ops[i].flags |= OPF_RMD; + ops[i].flags |= OPF_RMD | OPF_DONE; esp_sub = 1; break; } @@ -2902,7 +2938,7 @@ static void scan_prologue_epilogue(int opcnt) while (i > 0 && j > 0) { i--; if (ops[i].op == OP_PUSH) { - ops[i].flags &= ~OPF_RMD; + ops[i].flags &= ~(OPF_RMD | OPF_DONE); j--; } } @@ -2947,12 +2983,12 @@ static void scan_prologue_epilogue(int opcnt) && ops[j].operand[1].type == OPT_CONST) { /* add esp, N */ - ecx_push -= ops[j].operand[1].val / 4 - 1; + l += ops[j].operand[1].val / 4 - 1; } else ferr(&ops[j], "'pop ecx' expected\n"); - ops[j].flags |= OPF_RMD; + ops[j].flags |= OPF_RMD | OPF_DONE; j--; } if (l != ecx_push) @@ -2968,7 +3004,7 @@ static void scan_prologue_epilogue(int opcnt) || ops[j].operand[1].val != g_stack_fsz) ferr(&ops[j], "'add esp' expected\n"); - ops[j].flags |= OPF_RMD; + ops[j].flags |= OPF_RMD | OPF_DONE; ops[j].operand[1].val = 0; // hack for stack arg scanner found = 1; } @@ -3093,7 +3129,7 @@ static struct parsed_proto *process_call_early(int i, int opcnt, *adj_i = ret = -1; if (!pp->is_stdcall && pp->argc_stack > 0) ret = scan_for_esp_adjust(i + 1, opcnt, - pp->argc_stack * 4, &adj, &multipath); + pp->argc_stack * 4, &adj, &multipath, 0); if (ret >= 0) { if (pp->argc_stack > adj / 4) return NULL; @@ -3148,7 +3184,8 @@ static struct parsed_proto *process_call(int i, int opcnt) my_assert_not(pp, NULL); pp->is_fptr = 1; - ret = scan_for_esp_adjust(i + 1, opcnt, ~0, &adj, &multipath); + ret = scan_for_esp_adjust(i + 1, opcnt, + 32*4, &adj, &multipath, 0); if (ret < 0 || adj < 0) { if (!g_allow_regfunc) ferr(po, "non-__cdecl indirect call unhandled yet\n"); @@ -3167,10 +3204,11 @@ static struct parsed_proto *process_call(int i, int opcnt) } // look for and make use of esp adjust + multipath = 0; ret = -1; if (!pp->is_stdcall && pp->argc_stack > 0) ret = scan_for_esp_adjust(i + 1, opcnt, - pp->argc_stack * 4, &adj, &multipath); + pp->argc_stack * 4, &adj, &multipath, 0); if (ret >= 0) { if (pp->is_vararg) { if (adj / 4 < pp->argc_stack) { @@ -3194,23 +3232,8 @@ static struct parsed_proto *process_call(int i, int opcnt) tmpname, pp->argc_stack * 4, adj); } - ops[ret].flags |= OPF_RMD; - if (ops[ret].op == OP_POP) { - if (adj > 4) { - // deal with multi-pop stack adjust - adj = pp->argc_stack; - while (ops[ret].op == OP_POP && adj > 0 && ret < opcnt) { - ops[ret].flags |= OPF_RMD; - adj--; - ret++; - } - } - } - else if (!multipath) { - // a bit of a hack, but deals with use of - // single adj for multiple calls - ops[ret].operand[1].val -= pp->argc_stack * 4; - } + scan_for_esp_adjust(i + 1, opcnt, + pp->argc_stack * 4, &adj, &multipath, 1); } else if (pp->is_vararg) ferr(po, "missing esp_adjust for vararg func '%s'\n", @@ -3282,7 +3305,7 @@ static int collect_call_args_early(struct parsed_op *po, int i, if (ops[j].operand[0].type == OPT_REG) *regmask |= 1 << ops[j].operand[0].reg; - ops[j].flags |= OPF_RMD | OPF_FARGNR | OPF_FARG; + ops[j].flags |= OPF_RMD | OPF_DONE | OPF_FARGNR | OPF_FARG; ops[j].flags &= ~OPF_RSAVE; // next arg @@ -3388,8 +3411,7 @@ 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 && !(ops[j].flags & OPF_RMD) - && ops[j].datap == NULL) + else if (ops[j].op == OP_POP && !(ops[j].flags & OPF_DONE)) { if (pp->is_unresolved) break; @@ -3403,7 +3425,8 @@ 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_FARGNR)) + else if (ops[j].op == OP_PUSH + && !(ops[j].flags & (OPF_FARGNR|OPF_DONE))) { if (pp->is_unresolved && (ops[j].flags & OPF_RMD)) break; @@ -3471,7 +3494,7 @@ static int collect_call_args_r(struct parsed_op *po, int i, if (!g_func_pp->is_vararg || strstr(ops[k].operand[1].name, buf)) { - ops[k].flags |= OPF_RMD; + ops[k].flags |= OPF_RMD | OPF_DONE; ops[j].flags |= OPF_RMD | OPF_VAPUSH; save_args &= ~(1 << arg); reg = -1; @@ -3486,7 +3509,7 @@ static int collect_call_args_r(struct parsed_op *po, int i, ret = stack_frame_access(&ops[k], &ops[k].operand[1], buf, sizeof(buf), ops[k].operand[1].name, "", 1, 0); if (ret >= 0) { - ops[k].flags |= OPF_RMD; + ops[k].flags |= OPF_RMD | OPF_DONE; ops[j].flags |= OPF_RMD; ops[j].p_argpass = ret + 1; save_args &= ~(1 << arg); @@ -3680,17 +3703,69 @@ static void output_std_flags(FILE *fout, struct parsed_op *po, } } +enum { + OPP_FORCE_NORETURN = (1 << 0), + OPP_SIMPLE_ARGS = (1 << 1), + OPP_ALIGN = (1 << 2), +}; + static void output_pp_attrs(FILE *fout, const struct parsed_proto *pp, - int is_noreturn) + int flags) { + const char *cconv = ""; + if (pp->is_fastcall) - fprintf(fout, "__fastcall "); + cconv = "__fastcall "; else if (pp->is_stdcall && pp->argc_reg == 0) - fprintf(fout, "__stdcall "); - if (pp->is_noreturn || is_noreturn) + cconv = "__stdcall "; + + fprintf(fout, (flags & OPP_ALIGN) ? "%-16s" : "%s", cconv); + + if (pp->is_noreturn || (flags & OPP_FORCE_NORETURN)) fprintf(fout, "noreturn "); } +static void output_pp(FILE *fout, const struct parsed_proto *pp, + int flags) +{ + int i; + + fprintf(fout, (flags & OPP_ALIGN) ? "%-5s" : "%s ", + pp->ret_type.name); + if (pp->is_fptr) + fprintf(fout, "("); + output_pp_attrs(fout, pp, flags); + if (pp->is_fptr) + fprintf(fout, "*"); + fprintf(fout, "%s", pp->name); + if (pp->is_fptr) + fprintf(fout, ")"); + + fprintf(fout, "("); + for (i = 0; i < pp->argc; i++) { + if (i > 0) + fprintf(fout, ", "); + if (pp->arg[i].fptr != NULL && !(flags & OPP_SIMPLE_ARGS)) { + // func pointer + output_pp(fout, pp->arg[i].fptr, 0); + } + else if (pp->arg[i].type.is_retreg) { + fprintf(fout, "u32 *r_%s", pp->arg[i].reg); + } + else { + fprintf(fout, "%s", pp->arg[i].type.name); + if (!pp->is_fptr) + fprintf(fout, " a%d", i + 1); + } + } + if (pp->is_vararg) { + if (i > 0) + fprintf(fout, ", "); + fprintf(fout, "..."); + } + fprintf(fout, ")"); +} + static int get_pp_arg_regmask(const struct parsed_proto *pp) { int regmask = 0; @@ -3776,7 +3851,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) po->bt_i = -1; po->btj = NULL; - if (po->flags & OPF_RMD) + if (po->flags & (OPF_RMD|OPF_DONE)) continue; if (po->op == OP_CALL) { @@ -3832,7 +3907,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) { if (l == i + 1 && po->op == OP_JMP) { // yet another alignment type.. - po->flags |= OPF_RMD; + po->flags |= OPF_RMD|OPF_DONE; break; } add_label_ref(&g_label_refs[l], i); @@ -3869,7 +3944,7 @@ tailcall: } po = &ops[i]; - if (po->flags & OPF_RMD) + if (po->flags & (OPF_RMD|OPF_DONE)) continue; if (po->op == OP_CALL) @@ -3886,10 +3961,10 @@ tailcall: if (j >= 0) { // commit esp adjust ops[j].flags |= OPF_RMD; - if (ops[j].op != OP_POP) { - ferr_assert(&ops[j], ops[j].op == OP_ADD); - ops[j].operand[1].val -= pp->argc_stack * 4; - } + if (ops[j].op != OP_POP) + patch_esp_adjust(&ops[j], pp->argc_stack * 4); + else + ops[j].flags |= OPF_DONE; } if (strstr(pp->ret_type.name, "int64")) @@ -3902,10 +3977,11 @@ tailcall: // pass4: // - process calls + // - handle push /pop pairs for (i = 0; i < opcnt; i++) { po = &ops[i]; - if (po->flags & OPF_RMD) + if (po->flags & (OPF_RMD|OPF_DONE)) continue; if (po->op == OP_CALL && !(po->flags & OPF_DONE)) @@ -3921,6 +3997,9 @@ tailcall: if (strstr(pp->ret_type.name, "int64")) need_tmp64 = 1; } + else if (po->op == OP_PUSH && !(po->flags & OPF_FARG) + && !(po->flags & OPF_RSAVE) && po->operand[0].type == OPT_CONST) + scan_for_pop_const(i, opcnt); } // pass5: @@ -3933,7 +4012,7 @@ tailcall: for (i = 0; i < opcnt; i++) { po = &ops[i]; - if (po->flags & OPF_RMD) + if (po->flags & (OPF_RMD|OPF_DONE)) continue; if (po->op == OP_PUSH && (po->flags & OPF_RSAVE)) { @@ -3979,13 +4058,10 @@ tailcall: continue; } } - else if (po->operand[0].type == OPT_CONST) { - scan_for_pop_const(i, opcnt); - } } if (po->op == OP_STD) { - po->flags |= OPF_DF | OPF_RMD; + po->flags |= OPF_DF | OPF_RMD | OPF_DONE; scan_propagate_df(i + 1, opcnt); } @@ -4164,7 +4240,7 @@ tailcall: need_tmp64 = 1; } else if (po->op == OP_CLD) - po->flags |= OPF_RMD; + po->flags |= OPF_RMD | OPF_DONE; if (po->op == OP_RCL || po->op == OP_RCR || po->op == OP_XCHG) { need_tmp_var = 1; @@ -4196,47 +4272,10 @@ tailcall: } // the function itself - fprintf(fout, "%s ", g_func_pp->ret_type.name); - output_pp_attrs(fout, g_func_pp, g_ida_func_attr & IDAFA_NORETURN); - fprintf(fout, "%s(", g_func_pp->name); - - for (i = 0; i < g_func_pp->argc; i++) { - if (i > 0) - fprintf(fout, ", "); - if (g_func_pp->arg[i].fptr != NULL) { - // func pointer.. - pp = g_func_pp->arg[i].fptr; - fprintf(fout, "%s (", pp->ret_type.name); - output_pp_attrs(fout, pp, 0); - 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); - } - if (pp->is_vararg) { - if (j > 0) - fprintf(fout, ", "); - fprintf(fout, "..."); - } - fprintf(fout, ")"); - } - else if (g_func_pp->arg[i].type.is_retreg) { - fprintf(fout, "u32 *r_%s", g_func_pp->arg[i].reg); - } - else { - fprintf(fout, "%s a%d", g_func_pp->arg[i].type.name, i + 1); - } - } - if (g_func_pp->is_vararg) { - if (i > 0) - fprintf(fout, ", "); - fprintf(fout, "..."); - } - - fprintf(fout, ")\n{\n"); + ferr_assert(ops, !g_func_pp->is_fptr); + output_pp(fout, g_func_pp, + (g_ida_func_attr & IDAFA_NORETURN) ? OPP_FORCE_NORETURN : 0); + fprintf(fout, "\n{\n"); // declare indirect functions for (i = 0; i < opcnt; i++) { @@ -4270,15 +4309,9 @@ tailcall: else snprintf(pp->name, sizeof(pp->name), "icall%d", i); - fprintf(fout, " %s (", pp->ret_type.name); - output_pp_attrs(fout, pp, 0); - fprintf(fout, "*%s)(", pp->name); - for (j = 0; j < pp->argc; j++) { - if (j > 0) - fprintf(fout, ", "); - fprintf(fout, "%s a%d", pp->arg[j].type.name, j + 1); - } - fprintf(fout, ");\n"); + fprintf(fout, " "); + output_pp(fout, pp, OPP_SIMPLE_ARGS); + fprintf(fout, ";\n"); } } } @@ -5467,11 +5500,12 @@ struct func_prototype { int id; int argc_stack; int regmask_dep; - int has_ret:3; // -1, 0, 1: unresolved, no, yes + int has_ret:3; // -1, 0, 1: unresolved, no, yes unsigned int dep_resolved:1; unsigned int is_stdcall:1; struct func_proto_dep *dep_func; int dep_func_cnt; + const struct parsed_proto *pp; // seed pp, if any }; struct func_proto_dep { @@ -5488,6 +5522,8 @@ static struct scanned_var { char name[NAMELEN]; enum opr_lenmod lmod; unsigned int is_seeded:1; + unsigned int is_c_str:1; + const struct parsed_proto *pp; // seed pp, if any } *hg_vars; static int hg_var_cnt; @@ -5537,25 +5573,164 @@ static int hg_fp_cmp_id(const void *p1_, const void *p2_) } #endif +// recursive register dep pass +// - track saved regs (part 2) +// - try to figure out arg-regs +// - calculate reg deps +static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits, + struct func_prototype *fp, int regmask_save, int regmask_dst, + int *regmask_dep, int *has_ret) +{ + struct func_proto_dep *dep; + struct parsed_op *po; + int from_caller = 0; + int depth; + int j, l; + int reg; + int ret; + + for (; i < opcnt; i++) + { + if (cbits[i >> 3] & (1 << (i & 7))) + return; + cbits[i >> 3] |= (1 << (i & 7)); + + po = &ops[i]; + + if ((po->flags & OPF_JMP) && po->op != OP_CALL) { + if (po->btj != NULL) { + // jumptable + for (j = 0; j < po->btj->count; j++) { + gen_hdr_dep_pass(po->btj->d[j].bt_i, opcnt, cbits, fp, + regmask_save, regmask_dst, regmask_dep, has_ret); + } + return; + } + + if (po->bt_i < 0) { + ferr(po, "dead branch\n"); + return; + } + + if (po->flags & OPF_CJMP) { + gen_hdr_dep_pass(po->bt_i, opcnt, cbits, fp, + regmask_save, regmask_dst, regmask_dep, has_ret); + } + else { + i = po->bt_i - 1; + } + continue; + } + + if (po->flags & OPF_FARG) + /* (just calculate register deps) */; + else if (po->op == OP_PUSH && po->operand[0].type == OPT_REG) + { + reg = po->operand[0].reg; + if (reg < 0) + ferr(po, "reg not set for push?\n"); + + if (po->flags & OPF_RSAVE) { + regmask_save |= 1 << reg; + continue; + } + if (po->flags & OPF_DONE) + continue; + + depth = 0; + ret = scan_for_pop(i + 1, opcnt, + po->operand[0].name, i + opcnt * 2, 0, &depth, 0); + if (ret == 1) { + regmask_save |= 1 << reg; + po->flags |= OPF_RMD; + scan_for_pop(i + 1, opcnt, + po->operand[0].name, i + opcnt * 3, 0, &depth, 1); + continue; + } + } + else if (po->flags & OPF_RMD) + continue; + else if (po->op == OP_CALL) { + po->regmask_dst |= 1 << xAX; + + dep = hg_fp_find_dep(fp, po->operand[0].name); + if (dep != NULL) + dep->regmask_live = regmask_save | regmask_dst; + } + else if (po->op == OP_RET) { + 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)) { + if (po->op == OP_CALL) { + j = i; + ret = 1; + } + else { + struct parsed_opr opr = { 0, }; + opr.type = OPT_REG; + opr.reg = xAX; + j = -1; + from_caller = 0; + ret = resolve_origin(i, &opr, i + opcnt * 4, &j, &from_caller); + } + + if (ret == -1 && from_caller) { + // unresolved eax - probably void func + *has_ret = 0; + } + else { + if (ops[j].op == OP_CALL) { + dep = hg_fp_find_dep(fp, po->operand[0].name); + if (dep != NULL) + dep->ret_dep = 1; + else + *has_ret = 1; + } + else + *has_ret = 1; + } + } + + 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 (f %x)\n", + l, regmask_dst, regmask_save, po->flags); +#endif + *regmask_dep |= l; + regmask_dst |= po->regmask_dst; + + if (po->flags & OPF_TAIL) + return; + } +} + static void gen_hdr(const char *funcn, int opcnt) { int save_arg_vars[MAX_ARG_GRP] = { 0, }; + unsigned char cbits[MAX_OPS / 8]; 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_dummy = 0; - int regmask_save = 0; - int regmask_dst = 0; - int regmask_dep = 0; + int regmask_dep; int max_bp_offset = 0; - int has_ret = -1; - int from_caller = 0; + int has_ret; int i, j, l, ret; - int depth, reg; if ((hg_fp_cnt & 0xff) == 0) { hg_fp = realloc(hg_fp, sizeof(hg_fp[0]) * (hg_fp_cnt + 0x100)); @@ -5570,11 +5745,12 @@ static void gen_hdr(const char *funcn, int opcnt) 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"); + fp->pp = proto_parse(g_fhdr, funcn, 1); + if (fp->pp != NULL) { + fp->argc_stack = fp->pp->argc_stack; + fp->is_stdcall = fp->pp->is_stdcall; + fp->regmask_dep = get_pp_arg_regmask(fp->pp); + fp->has_ret = !IS(fp->pp->ret_type.name, "void"); return; } @@ -5594,7 +5770,7 @@ static void gen_hdr(const char *funcn, int opcnt) po->bt_i = -1; po->btj = NULL; - if (po->flags & OPF_RMD) + if (po->flags & (OPF_RMD|OPF_DONE)) continue; if (po->op == OP_CALL) { @@ -5666,7 +5842,6 @@ tailcall: // pass3: // - remove dead labels - // - process trivial calls // - handle push /pop pairs for (i = 0; i < opcnt; i++) { @@ -5676,7 +5851,19 @@ tailcall: } po = &ops[i]; - if (po->flags & OPF_RMD) + if (po->flags & (OPF_RMD|OPF_DONE)) + continue; + + if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST) + scan_for_pop_const(i, opcnt); + } + + // pass4: + // - process trivial calls + for (i = 0; i < opcnt; i++) + { + po = &ops[i]; + if (po->flags & (OPF_RMD|OPF_DONE)) continue; if (po->op == OP_CALL) @@ -5693,135 +5880,62 @@ tailcall: if (j >= 0) { // commit esp adjust ops[j].flags |= OPF_RMD; - if (ops[j].op != OP_POP) { - ferr_assert(&ops[j], ops[j].op == OP_ADD); - ops[j].operand[1].val -= pp->argc_stack * 4; - } + if (ops[j].op != OP_POP) + patch_esp_adjust(&ops[j], pp->argc_stack * 4); + else + ops[j].flags |= OPF_DONE; } po->flags |= OPF_DONE; } } - else if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST) { - scan_for_pop_const(i, opcnt); - } } - // pass4: + // pass5: + // - track saved regs (simple) // - process calls for (i = 0; i < opcnt; i++) { po = &ops[i]; - if (po->flags & OPF_RMD) + if (po->flags & (OPF_RMD|OPF_DONE)) continue; - if (po->op == OP_CALL && !(po->flags & OPF_DONE)) - { - pp = process_call(i, opcnt); - - if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) { - // since we know the args, collect them - collect_call_args(po, i, pp, ®mask_dummy, save_arg_vars, - i + opcnt * 2); - } - } - } - - // pass5: - // - track saved regs - // - try to figure out arg-regs - for (i = 0; i < opcnt; i++) - { - po = &ops[i]; - - 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) + if (po->op == OP_PUSH && 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 * 1, 0, &depth, 0); - if (ret == 1) { - regmask_save |= 1 << reg; - po->flags |= OPF_RMD; - scan_for_pop(i + 1, opcnt, - po->operand[0].name, i + opcnt * 2, 0, &depth, 1); - continue; - } ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, 0); if (ret == 0) { - regmask_save |= 1 << reg; - po->flags |= OPF_RMD; + // regmask_save |= 1 << po->operand[0].reg; // do it later + po->flags |= OPF_RSAVE | OPF_RMD | OPF_DONE; scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, OPF_RMD); - continue; } } - else if (po->op == OP_CALL) { - po->regmask_dst |= 1 << xAX; + else if (po->op == OP_CALL && !(po->flags & OPF_DONE)) + { + pp = process_call(i, opcnt); - dep = hg_fp_find_dep(fp, po->operand[0].name); - if (dep != NULL) - dep->regmask_live = regmask_save | regmask_dst; - } - else if (po->op == OP_RET) { - 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 (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) { + // since we know the args, collect them + ret = collect_call_args(po, i, pp, ®mask_dummy, save_arg_vars, + i + opcnt * 1); } } + } - if (has_ret != 0 && (po->flags & OPF_TAIL)) { - if (po->op == OP_CALL) { - j = i; - ret = 1; - } - else { - struct parsed_opr opr = { 0, }; - opr.type = OPT_REG; - opr.reg = xAX; - j = -1; - from_caller = 0; - ret = resolve_origin(i, &opr, i + opcnt * 3, &j, &from_caller); - } + // pass6 + memset(cbits, 0, sizeof(cbits)); + regmask_dep = 0; + has_ret = -1; - if (ret == -1 && from_caller) { - // unresolved eax - probably void func - has_ret = 0; - } - else { - if (ops[j].op == OP_CALL) { - dep = hg_fp_find_dep(fp, po->operand[0].name); - if (dep != NULL) - dep->ret_dep = 1; - else - has_ret = 1; - } - else - has_ret = 1; - } - } + gen_hdr_dep_pass(0, opcnt, cbits, fp, 0, 0, ®mask_dep, &has_ret); - l = regmask_save | regmask_dst; - if (g_bp_frame && !(po->flags & OPF_EBP_S)) - l |= 1 << xBP; + // find unreachable code - must be fixed in IDA + for (i = 0; i < opcnt; i++) + { + if (cbits[i >> 3] & (1 << (i & 7))) + continue; - l = po->regmask_src & ~l; -#if 0 - if (l) - fnote(po, "dep |= %04x, dst %04x, save %04x\n", l, - regmask_dst, regmask_save); -#endif - regmask_dep |= l; - regmask_dst |= po->regmask_dst; + if (ops[i].op != OP_NOP) + ferr(&ops[i], "unreachable code\n"); } if (has_ret == -1 && (regmask_dep & (1 << xAX))) @@ -5841,7 +5955,12 @@ tailcall: fp->regmask_dep = regmask_dep & ~(1 << xSP); fp->has_ret = has_ret; - // output_hdr_fp(stdout, fp, 1); +#if 0 + printf("// has_ret %d, regmask_dep %x\n", + fp->has_ret, fp->regmask_dep); + output_hdr_fp(stdout, fp, 1); + if (IS(funcn, "sub_100073FD")) exit(1); +#endif gen_x_cleanup(opcnt); } @@ -5849,6 +5968,7 @@ tailcall: static void hg_fp_resolve_deps(struct func_prototype *fp) { struct func_prototype fp_s; + int dep; int i; // this thing is recursive, so mark first.. @@ -5862,8 +5982,11 @@ static void hg_fp_resolve_deps(struct func_prototype *fp) if (!fp->dep_func[i].proto->dep_resolved) hg_fp_resolve_deps(fp->dep_func[i].proto); - fp->regmask_dep |= ~fp->dep_func[i].regmask_live - & fp->dep_func[i].proto->regmask_dep; + dep = ~fp->dep_func[i].regmask_live + & fp->dep_func[i].proto->regmask_dep; + fp->regmask_dep |= dep; + // printf("dep %s %s |= %x\n", fp->name, + // fp->dep_func[i].name, dep); if (fp->has_ret == -1) fp->has_ret = fp->dep_func[i].proto->has_ret; @@ -5874,8 +5997,9 @@ static void hg_fp_resolve_deps(struct func_prototype *fp) static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, int count) { - char *p, buf[NAMELEN]; - const char *cp; + const struct parsed_proto *pp; + char *p, namebuf[NAMELEN]; + const char *name; int regmask_dep; int argc_stack; int j, arg; @@ -5894,10 +6018,31 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, fprintf(fout, "\n"); #endif + p = strchr(fp->name, '@'); + if (p != NULL) { + memcpy(namebuf, fp->name, p - fp->name); + namebuf[p - fp->name] = 0; + name = namebuf; + } + else + name = fp->name; + if (name[0] == '_') + name++; + + pp = proto_parse(g_fhdr, name, 1); + if (pp != NULL && pp->is_include) + continue; + + if (fp->pp != NULL) { + // part of seed, output later + continue; + } + regmask_dep = fp->regmask_dep; argc_stack = fp->argc_stack; - fprintf(fout, fp->has_ret ? "int " : "void "); + fprintf(fout, "%-5s", fp->pp ? fp->pp->ret_type.name : + (fp->has_ret ? "int" : "void")); if (regmask_dep && (fp->is_stdcall || argc_stack == 0) && (regmask_dep & ~((1 << xCX) | (1 << xDX))) == 0) { @@ -5919,17 +6064,7 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, else fprintf(fout, " __cdecl "); - 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); + fprintf(fout, "%s(", name); arg = 0; for (j = 0; j < xSP; j++) { @@ -5937,7 +6072,11 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, arg++; if (arg != 1) fprintf(fout, ", "); - fprintf(fout, "int a%d/*<%s>*/", arg, regs_r32[j]); + if (fp->pp != NULL) + fprintf(fout, "%s", fp->pp->arg[arg - 1].type.name); + else + fprintf(fout, "int"); + fprintf(fout, " a%d/*<%s>*/", arg, regs_r32[j]); } } @@ -5945,7 +6084,14 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, arg++; if (arg != 1) fprintf(fout, ", "); - fprintf(fout, "int a%d", arg); + if (fp->pp != NULL) { + fprintf(fout, "%s", fp->pp->arg[arg - 1].type.name); + if (!fp->pp->arg[arg - 1].type.is_ptr) + fprintf(fout, " "); + } + else + fprintf(fout, "int "); + fprintf(fout, "a%d", arg); } fprintf(fout, ");\n"); @@ -5962,6 +6108,7 @@ static void output_hdr(FILE *fout) [OPLM_QWORD] = "uint64_t", }; const struct scanned_var *var; + char line[256] = { 0, }; int i; // resolve deps @@ -5976,8 +6123,14 @@ static void output_hdr(FILE *fout) for (i = 0; i < hg_var_cnt; i++) { var = &hg_vars[i]; - fprintf(fout, "extern %-8s %s;", - lmod_c_names[var->lmod], var->name); + if (var->pp != NULL) + // part of seed + continue; + else if (var->is_c_str) + fprintf(fout, "extern %-8s %s[];", "char", var->name); + else + fprintf(fout, "extern %-8s %s;", + lmod_c_names[var->lmod], var->name); if (var->is_seeded) fprintf(fout, " // seeded"); @@ -5988,6 +6141,13 @@ static void output_hdr(FILE *fout) // output function prototypes output_hdr_fp(fout, hg_fp, hg_fp_cnt); + + // seed passthrough + fprintf(fout, "\n// - seed -\n"); + + rewind(g_fhdr); + while (fgets(line, sizeof(line), g_fhdr)) + fwrite(line, 1, strlen(line), fout); } // read a line, truncating it if it doesn't fit @@ -6014,34 +6174,49 @@ static char *my_fgets(char *s, size_t size, FILE *stream) return ret; } -// '=' needs special treatment.. +// '=' needs special treatment +// also ' quote static char *next_word_s(char *w, size_t wsize, char *s) { - size_t i; + size_t i; - s = sskip(s); + s = sskip(s); + + i = 0; + if (*s == '\'') { + w[0] = s[0]; + for (i = 1; i < wsize - 1; i++) { + if (s[i] == 0) { + printf("warning: missing closing quote: \"%s\"\n", s); + break; + } + if (s[i] == '\'') + break; + w[i] = s[i]; + } + } - for (i = 0; i < wsize - 1; i++) { - if (s[i] == 0 || my_isblank(s[i]) || (s[i] == '=' && i > 0)) - break; - w[i] = s[i]; - } - w[i] = 0; + for (; i < wsize - 1; i++) { + if (s[i] == 0 || my_isblank(s[i]) || (s[i] == '=' && i > 0)) + break; + w[i] = s[i]; + } + w[i] = 0; - if (s[i] != 0 && !my_isblank(s[i]) && s[i] != '=') - printf("warning: '%s' truncated\n", w); + if (s[i] != 0 && !my_isblank(s[i]) && s[i] != '=') + printf("warning: '%s' truncated\n", w); - return s + i; + return s + i; } static void scan_variables(FILE *fasm) { - const struct parsed_proto *pp_c; struct scanned_var *var; char line[256] = { 0, }; - char words[2][256]; + char words[3][256]; char *p = NULL; int wordc; + int l; while (!feof(fasm)) { @@ -6095,6 +6270,8 @@ static void scan_variables(FILE *fasm) if (wordc == 2 && IS(words[1], "ends")) break; + if (wordc < 2) + continue; if ((hg_var_cnt & 0xff) == 0) { hg_vars = realloc(hg_vars, sizeof(hg_vars[0]) @@ -6107,17 +6284,17 @@ static void scan_variables(FILE *fasm) snprintf(var->name, sizeof(var->name), "%s", words[0]); // maybe already in seed header? - pp_c = proto_parse(g_fhdr, var->name, 1); - if (pp_c != NULL) { - if (pp_c->is_func) - aerr("func?\n"); - else if (pp_c->is_fptr) { + var->pp = proto_parse(g_fhdr, var->name, 1); + if (var->pp != NULL) { + if (var->pp->is_fptr) { var->lmod = OPLM_DWORD; //var->is_ptr = 1; } - else if (!guess_lmod_from_c_type(&var->lmod, &pp_c->type)) + else if (var->pp->is_func) + aerr("func?\n"); + else if (!guess_lmod_from_c_type(&var->lmod, &var->pp->type)) aerr("unhandled C type '%s' for '%s'\n", - pp_c->type.name, var->name); + var->pp->type.name, var->name); var->is_seeded = 1; continue; @@ -6127,8 +6304,13 @@ static void scan_variables(FILE *fasm) var->lmod = OPLM_DWORD; else if (IS(words[1], "dw")) var->lmod = OPLM_WORD; - else if (IS(words[1], "db")) + else if (IS(words[1], "db")) { var->lmod = OPLM_BYTE; + if (wordc >= 3 && (l = strlen(words[2])) > 4) { + if (words[2][0] == '\'' && IS(words[2] + l - 2, ",0")) + var->is_c_str = 1; + } + } else if (IS(words[1], "dq")) var->lmod = OPLM_QWORD; //else if (IS(words[1], "dt")) @@ -6303,8 +6485,14 @@ int main(int argc, char *argv[]) } if (argc < arg + 3) { - printf("usage:\n%s [-v] [-rf] [-m] <.c> <.asm> [rlist]*\n" - "%s -hdr <.asm> [rlist]*\n", + printf("usage:\n%s [-v] [-rf] [-m] <.c> <.asm> [rlist]*\n" + "%s -hdr <.asm> [rlist]*\n" + "options:\n" + " -hdr - header generation mode\n" + " -rf - allow unannotated indirect calls\n" + " -m - allow multiple .text sections\n" + "[rlist] is a file with function names to skip," + " one per line\n", argv[0], argv[0]); return 1; } @@ -6542,7 +6730,7 @@ parse_words: do_pending_endp: // do delayed endp processing to collect switch jumptables if (pending_endp) { - if (in_func && !skip_func && !end && wordc >= 2 + if (in_func && !g_skip_func && !end && wordc >= 2 && ((words[0][0] == 'd' && words[0][2] == 0) || (words[1][0] == 'd' && words[1][2] == 0))) { @@ -6597,7 +6785,7 @@ do_pending_endp: continue; } - if (in_func && !skip_func) { + if (in_func && !g_skip_func) { if (g_header_mode) gen_hdr(g_func, pi); else @@ -6608,7 +6796,7 @@ do_pending_endp: in_func = 0; g_ida_func_attr = 0; skip_warned = 0; - skip_func = 0; + g_skip_func = 0; g_func[0] = 0; func_chunks_used = 0; func_chunk_i = -1; @@ -6642,7 +6830,7 @@ do_pending_endp: words[0], g_func); p = words[0]; if (bsearch(&p, rlist, rlist_len, sizeof(rlist[0]), cmpstringp)) - skip_func = 1; + g_skip_func = 1; strcpy(g_func, words[0]); set_label(0, words[0]); in_func = 1; @@ -6661,10 +6849,10 @@ do_pending_endp: && ops[0].op == OP_JMP && ops[0].operand[0].had_ds) { // import jump - skip_func = 1; + g_skip_func = 1; } - if (!skip_func && func_chunks_used) { + if (!g_skip_func && func_chunks_used) { // start processing chunks struct chunk_item *ci, key = { g_func, 0 }; @@ -6723,8 +6911,8 @@ do_pending_endp: continue; } - if (!in_func || skip_func) { - if (!skip_warned && !skip_func && g_labels[pi] != NULL) { + if (!in_func || g_skip_func) { + if (!skip_warned && !g_skip_func && g_labels[pi] != NULL) { if (verbose) anote("skipping from '%s'\n", g_labels[pi]); skip_warned = 1;