X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=tools%2Ftranslate.c;h=eacb0e0c3cf254d6082e9056b896207aba1569ab;hb=f9d7b4610c82104c07202a6557bae7228e45c6ca;hp=b57c8128987e2eb6acba3845af3b53f4f7541131;hpb=88eed95d8e5111264869e388468d33b20384cdac;p=ia32rtools.git diff --git a/tools/translate.c b/tools/translate.c index b57c812..eacb0e0 100644 --- a/tools/translate.c +++ b/tools/translate.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "my_assert.h" @@ -60,7 +61,7 @@ enum op_flags { 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_32BIT = (1 << 16), /* enough to do 32bit for this op */ OPF_LOCK = (1 << 17), /* op has lock prefix */ OPF_VAPUSH = (1 << 18), /* vararg ptr push (as call arg) */ OPF_DONE = (1 << 19), /* already fully handled by analysis */ @@ -277,15 +278,18 @@ enum ida_func_attr { IDAFA_FPD = (1 << 5), }; +// sctattr enum sct_func_attr { SCTFA_CLEAR_SF = (1 << 0), // clear stack frame SCTFA_CLEAR_REGS = (1 << 1), // clear registers (mask) + SCTFA_RM_REGS = (1 << 2), // don't emit regs + SCTFA_NOWARN = (1 << 3), // don't try to detect problems }; enum x87_const { X87_CONST_1 = 1, - X87_CONST_2T, - X87_CONST_2E, + X87_CONST_L2T, + X87_CONST_L2E, X87_CONST_PI, X87_CONST_LG2, X87_CONST_LN2, @@ -327,6 +331,7 @@ static int g_sct_func_attr; static int g_stack_clear_start; // in dwords static int g_stack_clear_len; static int g_regmask_init; +static int g_regmask_rm; static int g_skip_func; static int g_allow_regfunc; static int g_allow_user_icall; @@ -347,6 +352,9 @@ static int g_header_mode; if (!(cond)) ferr(op_, "assertion '%s' failed\n", #cond); \ } while (0) +#define IS_OP_INDIRECT_CALL(op_) \ + ((op_)->op == OP_CALL && (op_)->operand[0].type != OPT_LABEL) + const char *regs_r32[] = { "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp", // not r32, but list here for easy parsing and printing @@ -589,24 +597,27 @@ static const char *parse_stack_el(const char *name, char *extra_reg, if (p == NULL) aerr("%s IDA stackvar not set?\n", __func__); } - if (!('0' <= *s && *s <= '9')) { - aerr("%s IDA stackvar offset not set?\n", __func__); - return NULL; - } - if (s[0] == '0' && s[1] == 'x') - s += 2; - len = p - s; - if (len < sizeof(buf) - 1) { - strncpy(buf, s, len); - buf[len] = 0; - errno = 0; - val = strtol(buf, &endp, 16); - if (val == 0 || *endp != 0 || errno != 0) { - aerr("%s num parse fail for '%s'\n", __func__, buf); - return NULL; + if ('0' <= *s && *s <= '9') { + if (s[0] == '0' && s[1] == 'x') + s += 2; + len = p - s; + if (len < sizeof(buf) - 1) { + strncpy(buf, s, len); + buf[len] = 0; + errno = 0; + val = strtol(buf, &endp, 16); + if (val == 0 || *endp != 0 || errno != 0) { + aerr("%s num parse fail for '%s'\n", __func__, buf); + return NULL; + } } + p++; + } + else { + // probably something like [esp+arg_4+2] + p = s; + val = 0; } - p++; } else p = name + 4; @@ -1084,6 +1095,10 @@ static const struct { { "fld", OP_FLD, 1, 1, OPF_FPUSH }, { "fild", OP_FILD, 1, 1, OPF_FPUSH|OPF_FINT }, { "fld1", OP_FLDc, 0, 0, OPF_FPUSH }, + { "fldl2t", OP_FLDc, 0, 0, OPF_FPUSH }, + { "fldl2e", OP_FLDc, 0, 0, OPF_FPUSH }, + { "fldpi", OP_FLDc, 0, 0, OPF_FPUSH }, + { "fldlg2", OP_FLDc, 0, 0, OPF_FPUSH }, { "fldln2", OP_FLDc, 0, 0, OPF_FPUSH }, { "fldz", OP_FLDc, 0, 0, OPF_FPUSH }, { "fst", OP_FST, 1, 1, 0 }, @@ -1332,20 +1347,26 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) // fallthrough case OP_MUL: // singleop mul - op->regmask_src |= op->regmask_dst; - op->regmask_dst = (1 << xDX) | (1 << xAX); if (op->operand[0].lmod == OPLM_UNSPEC) op->operand[0].lmod = OPLM_DWORD; + op->regmask_src = mxAX | op->regmask_dst; + op->regmask_dst = mxAX; + if (op->operand[0].lmod != OPLM_BYTE) + op->regmask_dst |= mxDX; break; case OP_DIV: case OP_IDIV: // we could set up operands for edx:eax, but there is no real need to // (see is_opr_modified()) - op->regmask_src |= op->regmask_dst; - op->regmask_dst = (1 << xDX) | (1 << xAX); if (op->operand[0].lmod == OPLM_UNSPEC) op->operand[0].lmod = OPLM_DWORD; + op->regmask_src = mxAX | op->regmask_dst; + op->regmask_dst = mxAX; + if (op->operand[0].lmod != OPLM_BYTE) { + op->regmask_src |= mxDX; + op->regmask_dst |= mxDX; + } break; case OP_SHL: @@ -1400,6 +1421,8 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) break; case OP_CALL: + // needed because of OPF_DATA + op->regmask_src |= op->regmask_dst; // trashed regs must be explicitly detected later op->regmask_dst = 0; break; @@ -1418,12 +1441,20 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) op->regmask_dst |= mxST0; if (IS(words[op_w] + 3, "1")) op->operand[0].val = X87_CONST_1; + else if (IS(words[op_w] + 3, "l2t")) + op->operand[0].val = X87_CONST_L2T; + else if (IS(words[op_w] + 3, "l2e")) + op->operand[0].val = X87_CONST_L2E; + else if (IS(words[op_w] + 3, "pi")) + op->operand[0].val = X87_CONST_PI; + else if (IS(words[op_w] + 3, "lg2")) + op->operand[0].val = X87_CONST_LG2; else if (IS(words[op_w] + 3, "ln2")) op->operand[0].val = X87_CONST_LN2; else if (IS(words[op_w] + 3, "z")) op->operand[0].val = X87_CONST_Z; else - aerr("TODO\n"); + aerr("fld what?\n"); break; case OP_FST: @@ -1925,7 +1956,7 @@ static int stack_frame_access(struct parsed_op *po, snprintf(buf, buf_size, "%sap", cast); return -1; } - ferr(po, "offset %d (%s,%d) doesn't map to any arg\n", + ferr(po, "offset 0x%x (%s,%d) doesn't map to any arg\n", offset, bp_arg, arg_i); } if (ofs_reg[0] != 0) @@ -2129,7 +2160,7 @@ static void check_func_pp(struct parsed_op *po, } static const char *check_label_read_ref(struct parsed_op *po, - const char *name) + const char *name, int *is_import) { const struct parsed_proto *pp; @@ -2140,6 +2171,9 @@ static const char *check_label_read_ref(struct parsed_op *po, if (pp->is_func) check_func_pp(po, pp, "ref"); + if (is_import != NULL) + *is_import = pp->is_import; + return pp->name; } @@ -2158,6 +2192,7 @@ static char *out_src_opr(char *buf, size_t buf_size, char tmp1[256], tmp2[256]; char expr[256]; const char *name; + int is_import = 0; char *p; int ret; @@ -2230,7 +2265,11 @@ static char *out_src_opr(char *buf, size_t buf_size, break; case OPT_LABEL: - name = check_label_read_ref(po, popr->name); + name = check_label_read_ref(po, popr->name, &is_import); + if (is_import) + // for imported data, asm is loading the offset + goto do_offset; + if (cast[0] == 0 && popr->is_ptr) cast = "(u32)"; @@ -2246,7 +2285,8 @@ static char *out_src_opr(char *buf, size_t buf_size, break; case OPT_OFFSET: - name = check_label_read_ref(po, popr->name); + do_offset: + name = check_label_read_ref(po, popr->name, NULL); if (cast[0] == 0) cast = "(u32)"; if (is_lea) @@ -2342,11 +2382,20 @@ static char *out_opr_float(char *buf, size_t buf_size, { const char *cast = NULL; char tmp[256]; + union { + float f; + int i; + } u; switch (popr->type) { case OPT_REG: - if (popr->reg < xST0 || popr->reg > xST7) - ferr(po, "bad reg: %d\n", popr->reg); + if (popr->reg < xST0 || popr->reg > xST7) { + // func arg + ferr_assert(po, po->op == OP_PUSH); + ferr_assert(po, popr->lmod == OPLM_DWORD); + snprintf(buf, buf_size, "*(float *)&%s", opr_reg_p(po, popr)); + break; + } if (need_float_stack) { if (popr->reg == xST0) @@ -2383,6 +2432,16 @@ static char *out_opr_float(char *buf, size_t buf_size, snprintf(buf, buf_size, "*(%s *)(%s)", cast, tmp); break; + case OPT_CONST: + // only for func float args pushes + ferr_assert(po, po->op == OP_PUSH); + u.i = po->operand[0].val; + if (ceilf(u.f) == u.f) + snprintf(buf, buf_size, "%.1ff", u.f); + else + snprintf(buf, buf_size, "%.8ff", u.f); + break; + default: ferr(po, "invalid float type: %d\n", popr->type); } @@ -3787,8 +3846,12 @@ static void resolve_branches_parse_calls(int opcnt) else if (po->operand[0].type == OPT_LABEL) { tmpname = opr_name(po, 0); - if (IS_START(tmpname, "loc_")) - ferr(po, "call to loc_*\n"); + if (IS_START(tmpname, "loc_")) { + if (!g_seh_found) + ferr(po, "call to loc_*\n"); + // eliminate_seh() must take care of it + continue; + } if (IS(tmpname, "__alloca_probe")) continue; if (IS(tmpname, "__SEH_prolog")) { @@ -3890,6 +3953,7 @@ tailcall: static int resolve_origin(int i, const struct parsed_opr *opr, int magic, int *op_i, int *is_caller); +static void set_label(int i, const char *name); static void eliminate_seh_writes(int opcnt) { @@ -3917,6 +3981,87 @@ static void eliminate_seh_writes(int opcnt) } } +static void eliminate_seh_finally(int opcnt) +{ + const char *target_name = NULL; + const char *return_name = NULL; + int exits[MAX_EXITS]; + int exit_count = 0; + int call_i = -1; + int target_i = -1; + int return_i = -1; + int tgend_i = -1; + int i; + + for (i = 0; i < opcnt; i++) { + if (ops[i].op != OP_CALL) + continue; + if (!IS_START(opr_name(&ops[i], 0), "loc_")) + continue; + if (target_name != NULL) + ferr(&ops[i], "multiple finally calls? (last was %s)\n", + target_name); + target_name = opr_name(&ops[i], 0); + call_i = i; + + if (g_labels[i + 1] == NULL) + set_label(i + 1, "seh_fin_done"); + return_name = g_labels[i + 1]; + return_i = i + 1; + } + + if (call_i == -1) + // no finally block + return; + + // find finally code (bt_i is not set because it's call) + for (i = 0; i < opcnt; i++) { + if (g_labels[i] == NULL) + continue; + if (!IS(g_labels[i], target_name)) + continue; + + ferr_assert(&ops[i], target_i == -1); + target_i = i; + } + ferr_assert(&ops[0], target_i != -1); + + find_reachable_exits(target_i, opcnt, target_i + opcnt * 24, + exits, &exit_count); + ferr_assert(&ops[target_i], exit_count == 1); + ferr_assert(&ops[target_i], ops[exits[0]].op == OP_RET); + tgend_i = exits[0]; + + // convert to jumps, link + ops[call_i].op = OP_JMP; + ops[call_i].bt_i = target_i; + add_label_ref(&g_label_refs[target_i], call_i); + + ops[tgend_i].op = OP_JMP; + ops[tgend_i].flags &= ~OPF_TAIL; + ops[tgend_i].flags |= OPF_JMP; + ops[tgend_i].bt_i = return_i; + ops[tgend_i].operand_cnt = 1; + ops[tgend_i].operand[0].type = OPT_LABEL; + snprintf(ops[tgend_i].operand[0].name, NAMELEN, "%s", return_name); + add_label_ref(&g_label_refs[return_i], tgend_i); + + // rm seh finally entry code + for (i = target_i - 1; i >= 0; i--) { + if (g_labels[i] != NULL && g_label_refs[i].i != -1) + return; + if (ops[i].flags & OPF_CJMP) + return; + if (ops[i].flags & (OPF_JMP | OPF_TAIL)) + break; + } + for (i = target_i - 1; i >= 0; i--) { + if (ops[i].flags & (OPF_JMP | OPF_TAIL)) + break; + ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + } +} + static void eliminate_seh(int opcnt) { int i, j, k, ret; @@ -3959,6 +4104,7 @@ static void eliminate_seh(int opcnt) } eliminate_seh_writes(opcnt); + eliminate_seh_finally(opcnt); } static void eliminate_seh_calls(int opcnt) @@ -3997,11 +4143,75 @@ static void eliminate_seh_calls(int opcnt) ferr_assert(ops, epilog_found); eliminate_seh_writes(opcnt); + eliminate_seh_finally(opcnt); +} + +// check for prologue of many pushes and epilogue with pops +static void check_simple_sequence(int opcnt, int *fsz) +{ + int found = 0; + int seq_len; + int seq_p; + int seq[4]; + int reg; + int i, j; + + for (i = 0; i < opcnt && i < ARRAY_SIZE(seq); i++) { + if (ops[i].op != OP_PUSH || ops[i].operand[0].type != OPT_REG) + break; + reg = ops[i].operand[0].reg; + if (reg != xBX && reg != xSI && reg != xDI && reg != xBP) + break; + for (j = 0; j < i; j++) + if (seq[j] == reg) + break; + if (j != i) + // probably something else is going on here + break; + seq[i] = reg; + } + seq_len = i; + if (seq_len == 0) + return; + + for (; i < opcnt && seq_len > 0; i++) { + if (!(ops[i].flags & OPF_TAIL)) + continue; + + for (j = i - 1, seq_p = 0; j >= 0 && seq_p < seq_len; j--) { + if (ops[j].op != OP_POP || ops[j].operand[0].type != OPT_REG) + break; + if (ops[j].operand[0].reg != seq[seq_p]) + break; + seq_p++; + } + found = seq_len = seq_p; + } + if (!found) + return; + + for (i = 0; i < seq_len; i++) + ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + + for (; i < opcnt && seq_len > 0; i++) { + if (!(ops[i].flags & OPF_TAIL)) + continue; + + for (j = i - 1, seq_p = 0; j >= 0 && seq_p < seq_len; j--) { + ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + seq_p++; + } + } + + // unlike pushes after sub esp, + // IDA treats pushed like this as part of var area + *fsz += seq_len * 4; } static int scan_prologue(int i, int opcnt, int *ecx_push, int *esp_sub) { - int j; + const char *name; + int j, len, ret; for (; i < opcnt; i++) if (!(ops[i].flags & OPF_DONE)) @@ -4028,6 +4238,19 @@ static int scan_prologue(int i, int opcnt, int *ecx_push, int *esp_sub) *esp_sub = 1; break; } + if (ops[i].op == OP_LEA && ops[i].operand[0].reg == xSP + && ops[i].operand[1].type == OPT_REGMEM + && IS_START(ops[i].operand[1].name, "esp-")) + { + name = ops[i].operand[1].name; + ret = sscanf(name, "esp-%x%n", &j, &len); + ferr_assert(&ops[i], ret == 1 && len == strlen(name)); + g_stack_fsz += j; + ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + i++; + *esp_sub = 1; + break; + } if (ops[i].op == OP_MOV && ops[i].operand[0].reg == xAX && ops[i].operand[1].type == OPT_CONST) { @@ -4054,7 +4277,8 @@ static void scan_prologue_epilogue(int opcnt, int *stack_align) { int ecx_push = 0, esp_sub = 0, pusha = 0; int sandard_epilogue; - int found; + int found, ret, len; + int push_fsz = 0; int i, j, l; if (g_seh_found == 2) { @@ -4178,6 +4402,7 @@ static void scan_prologue_epilogue(int opcnt, int *stack_align) } // non-bp frame + check_simple_sequence(opcnt, &push_fsz); i = scan_prologue(0, opcnt, &ecx_push, &esp_sub); if (ecx_push && !esp_sub) { @@ -4242,6 +4467,12 @@ static void scan_prologue_epilogue(int opcnt, int *stack_align) } } + for (; j >= 0; j--) { + if ((ops[j].flags & (OPF_RMD | OPF_DONE | OPF_NOREGS)) != + (OPF_RMD | OPF_DONE | OPF_NOREGS)) + break; + } + if (ecx_push > 0 && !esp_sub) { for (l = 0; l < ecx_push && j >= 0; l++) { if (ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ecx")) @@ -4275,28 +4506,37 @@ static void scan_prologue_epilogue(int opcnt, int *stack_align) } if (esp_sub) { - if (ops[j].op != OP_ADD - || !IS(opr_name(&ops[j], 0), "esp") - || ops[j].operand[1].type != OPT_CONST) + if (ops[j].op == OP_ADD + && IS(opr_name(&ops[j], 0), "esp") + && ops[j].operand[1].type == OPT_CONST) { - if (i < opcnt && ops[i].op == OP_CALL - && ops[i].pp != NULL && ops[i].pp->is_noreturn) - { - // noreturn tailcall with no epilogue - i++; - found = 1; - continue; - } - ferr(&ops[j], "'add esp' expected\n"); - } - - if (ops[j].operand[1].val < g_stack_fsz) - ferr(&ops[j], "esp adj is too low (need %d)\n", g_stack_fsz); + if (ops[j].operand[1].val < g_stack_fsz) + ferr(&ops[j], "esp adj is too low (need %d)\n", g_stack_fsz); - ops[j].operand[1].val -= g_stack_fsz; // for stack arg scanner - if (ops[j].operand[1].val == 0) + ops[j].operand[1].val -= g_stack_fsz; // for stack arg scanner + if (ops[j].operand[1].val == 0) + ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + found = 1; + } + else if (ops[j].op == OP_LEA && ops[j].operand[0].reg == xSP + && ops[j].operand[1].type == OPT_REGMEM + && IS_START(ops[j].operand[1].name, "esp+")) + { + const char *name = ops[j].operand[1].name; + ret = sscanf(name, "esp+%x%n", &l, &len); + ferr_assert(&ops[j], ret == 1 && len == strlen(name)); + ferr_assert(&ops[j], l <= g_stack_fsz); ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; - found = 1; + found = 1; + } + else if (i < opcnt && ops[i].op == OP_CALL + && ops[i].pp != NULL && ops[i].pp->is_noreturn) + { + // noreturn tailcall with no epilogue + found = 1; + } + else + ferr(&ops[j], "'add esp' expected\n"); } i++; @@ -4305,6 +4545,10 @@ static void scan_prologue_epilogue(int opcnt, int *stack_align) if (!found) ferr(ops, "missing esp epilogue\n"); } + + if (g_stack_fsz != 0) + // see check_simple_sequence + g_stack_fsz += push_fsz; } // find an instruction that changed opr before i op @@ -4503,6 +4747,15 @@ static int find_next_read(int i, int opcnt, return 0; } +static int find_next_read_reg(int i, int opcnt, int reg, + enum opr_lenmod lmod, int magic, int *op_i) +{ + struct parsed_opr opr = OPR_INIT(OPT_REG, lmod, reg); + + *op_i = -1; + return find_next_read(i, opcnt, &opr, magic, op_i); +} + // find next instruction that reads opr // *op_i must be set to -1 by the caller // on return, *op_i is set to first flag user insn @@ -4906,6 +5159,37 @@ out: return pp; } +static void mark_float_arg(struct parsed_op *po, + struct parsed_proto *pp, int arg, int *regmask_ffca) +{ + po->p_argnext = -1; + po->p_argnum = arg + 1; + ferr_assert(po, pp->arg[arg].datap == NULL); + pp->arg[arg].datap = po; + po->flags |= OPF_DONE | OPF_FARGNR | OPF_FARG; + if (regmask_ffca != NULL) + *regmask_ffca |= 1 << arg; +} + +static int check_for_stp(int i, int i_to) +{ + struct parsed_op *po; + + for (; i < i_to; i++) { + po = &ops[i]; + if (po->op == OP_FST) + return i; + if (g_labels[i] != NULL || (po->flags & OPF_JMP)) + return -1; + if (po->op == OP_CALL || po->op == OP_PUSH || po->op == OP_POP) + return -1; + if (po->op == OP_ADD && po->operand[0].reg == xSP) + return -1; + } + + return -1; +} + static int collect_call_args_no_push(int i, struct parsed_proto *pp, int *regmask_ffca) { @@ -4939,13 +5223,7 @@ static int collect_call_args_no_push(int i, struct parsed_proto *pp, } arg = base_arg + offset / 4; - po->p_argnext = -1; - po->p_argnum = arg + 1; - ferr_assert(po, pp->arg[arg].datap == NULL); - pp->arg[arg].datap = po; - po->flags |= OPF_DONE | OPF_FARGNR | OPF_FARG; - if (regmask_ffca != NULL) - *regmask_ffca |= 1 << arg; + mark_float_arg(po, pp, arg, regmask_ffca); } else if (po->op == OP_SUB && po->operand[0].reg == xSP && po->operand[1].type == OPT_CONST) @@ -4972,7 +5250,8 @@ static int collect_call_args_early(int i, struct parsed_proto *pp, { struct parsed_op *po; int arg, ret; - int j; + int offset; + int j, k; for (arg = 0; arg < pp->argc; arg++) if (pp->arg[arg].reg == NULL) @@ -5040,10 +5319,24 @@ static int collect_call_args_early(int i, struct parsed_proto *pp, { ops[j].p_argnext = -1; ferr_assert(&ops[j], pp->arg[arg].datap == NULL); - pp->arg[arg].datap = &ops[j]; - if (regmask != NULL && ops[j].operand[0].type == OPT_REG) - *regmask |= 1 << ops[j].operand[0].reg; + k = check_for_stp(j + 1, i); + if (k != -1) { + // push ecx; fstp dword ptr [esp] + ret = parse_stack_esp_offset(&ops[k], + ops[k].operand[0].name, &offset); + if (ret == 0 && offset == 0) { + if (!pp->arg[arg].type.is_float) + ferr(&ops[i], "arg %d should be float\n", arg + 1); + mark_float_arg(&ops[k], pp, arg, regmask_ffca); + } + } + + if (pp->arg[arg].datap == NULL) { + pp->arg[arg].datap = &ops[j]; + if (regmask != NULL && ops[j].operand[0].type == OPT_REG) + *regmask |= 1 << ops[j].operand[0].reg; + } ops[j].flags |= OPF_RMD | OPF_DONE | OPF_FARGNR | OPF_FARG; ops[j].flags &= ~OPF_RSAVE; @@ -5457,9 +5750,8 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits, // don't need eax, will do "return f();" or "f(); return;" po->regmask_dst &= ~(1 << xAX); else { - struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xAX); - j = -1; - find_next_read(i + 1, opcnt, &opr, i + opcnt * 17, &j); + find_next_read_reg(i + 1, opcnt, xAX, OPLM_DWORD, + i + opcnt * 17, &j); if (j == -1) // not used po->regmask_dst &= ~(1 << xAX); @@ -6032,6 +6324,44 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (pp->argc_stack > 0) pp->is_stdcall = 1; } + if (!(po->flags & OPF_TAIL) + && !(g_sct_func_attr & SCTFA_NOWARN)) + { + // treat al write as overwrite to avoid many false positives + if (IS(pp->ret_type.name, "void") || pp->ret_type.is_float) { + find_next_read_reg(i + 1, opcnt, xAX, OPLM_BYTE, + i + opcnt * 25, &j); + if (j != -1) { + fnote(po, "eax used after void/float ret call\n"); + fnote(&ops[j], "(used here)\n"); + } + } + if (!strstr(pp->ret_type.name, "int64")) { + find_next_read_reg(i + 1, opcnt, xDX, OPLM_BYTE, + i + opcnt * 26, &j); + // indirect calls are often guessed, don't warn + if (j != -1 && !IS_OP_INDIRECT_CALL(&ops[j])) { + fnote(po, "edx used after 32bit ret call\n"); + fnote(&ops[j], "(used here)\n"); + } + } + j = 1; + // msvc often relies on callee not modifying 'this' + for (arg = 0; arg < pp->argc; arg++) { + if (pp->arg[arg].reg && IS(pp->arg[arg].reg, "ecx")) { + j = 0; + break; + } + } + if (j != 0) { + find_next_read_reg(i + 1, opcnt, xCX, OPLM_BYTE, + i + opcnt * 27, &j); + if (j != -1 && !IS_OP_INDIRECT_CALL(&ops[j])) { + fnote(po, "ecx used after call\n"); + fnote(&ops[j], "(used here)\n"); + } + } + } break; case OP_MOV: @@ -6092,14 +6422,12 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) need_tmp64 = 1; break; - case OPP_FTOL: { - struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xDX); - j = -1; - find_next_read(i + 1, opcnt, &opr, i + opcnt * 18, &j); + case OPP_FTOL: + find_next_read_reg(i + 1, opcnt, xDX, OPLM_DWORD, + i + opcnt * 18, &j); if (j == -1) po->flags |= OPF_32BIT; break; - } default: break; @@ -6150,6 +6478,9 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (pp->is_fptr && !(pp->name[0] != 0 && pp->is_arg)) { if (pp->name[0] != 0) { + if (IS_START(pp->name, "guess")) + pp->is_guessed = 1; + memmove(pp->name + 2, pp->name, strlen(pp->name) + 1); memcpy(pp->name, "i_", 2); @@ -6260,7 +6591,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) } // declare normal registers - regmask_now = regmask & ~regmask_arg; + regmask_now = regmask & ~regmask_arg & ~g_regmask_rm; regmask_now &= ~(1 << xSP); if (regmask_now & 0x00ff) { for (reg = 0; reg < 8; reg++) { @@ -7187,9 +7518,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) fprintf(fout, "%s%s = %s;\n", buf3, pp->name, out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], "(void *)", 0)); - if (pp->is_unresolved || IS_START(pp->name, "i_guess")) - fprintf(fout, "%sunresolved_call(\"%s:%d\", %s);\n", - buf3, asmfn, po->asmln, pp->name); + } + if (pp->is_fptr && (pp->is_unresolved || pp->is_guessed)) { + fprintf(fout, "%sunresolved_call(\"%s:%d\", %s);\n", + buf3, asmfn, po->asmln, pp->name); } fprintf(fout, "%s", buf3); @@ -7297,15 +7629,37 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (tmp_op->operand[0].lmod == OPLM_QWORD) arg++; } + else if (pp->arg[arg].type.is_64bit) { + ferr_assert(po, tmp_op->p_argpass == 0); + ferr_assert(po, !pp->arg[arg].is_saved); + ferr_assert(po, !pp->arg[arg].type.is_float); + ferr_assert(po, cast[0] == 0); + out_src_opr(buf1, sizeof(buf1), + tmp_op, &tmp_op->operand[0], cast, 0); + tmp_op = pp->arg[++arg].datap; + ferr_assert(po, tmp_op != NULL); + out_src_opr(buf2, sizeof(buf2), + tmp_op, &tmp_op->operand[0], cast, 0); + fprintf(fout, "((u64)(%s) << 32) | (%s)", + buf2, buf1); + } else if (tmp_op->p_argpass != 0) { + ferr_assert(po, !pp->arg[arg].type.is_float); fprintf(fout, "a%d", tmp_op->p_argpass); } else if (pp->arg[arg].is_saved) { ferr_assert(po, tmp_op->p_argnum > 0); + ferr_assert(po, !pp->arg[arg].type.is_float); fprintf(fout, "%s%s", cast, saved_arg_name(buf1, sizeof(buf1), tmp_op->p_arggrp, tmp_op->p_argnum)); } + else if (pp->arg[arg].type.is_float) { + ferr_assert(po, !pp->arg[arg].type.is_64bit); + fprintf(fout, "%s", + out_src_opr_float(buf1, sizeof(buf1), + tmp_op, &tmp_op->operand[0], need_float_stack)); + } else { fprintf(fout, "%s", out_src_opr(buf1, sizeof(buf1), @@ -7520,9 +7874,13 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) } switch (po->operand[0].val) { case X87_CONST_1: fprintf(fout, "1.0;"); break; - case X87_CONST_LN2: fprintf(fout, "0.693147180559945;"); break; + case X87_CONST_L2T: fprintf(fout, "3.321928094887362;"); break; + case X87_CONST_L2E: fprintf(fout, "M_LOG2E;"); break; + case X87_CONST_PI: fprintf(fout, "M_PI;"); break; + case X87_CONST_LG2: fprintf(fout, "0.301029995663981;"); break; + case X87_CONST_LN2: fprintf(fout, "M_LN2;"); break; case X87_CONST_Z: fprintf(fout, "0.0;"); break; - default: ferr(po, "TODO\n"); break; + default: ferr_assert(po, 0); break; } break; @@ -7887,10 +8245,13 @@ struct func_prototype { char name[NAMELEN]; int id; int argc_stack; - int regmask_dep; + int regmask_dep; // likely register args + int regmask_use; // used registers int has_ret:3; // -1, 0, 1: unresolved, no, yes + unsigned int has_ret64:1; unsigned int dep_resolved:1; unsigned int is_stdcall:1; + unsigned int eax_pass:1; // returns without touching eax struct func_proto_dep *dep_func; int dep_func_cnt; const struct parsed_proto *pp; // seed pp, if any @@ -7901,6 +8262,8 @@ struct func_proto_dep { struct func_prototype *proto; int regmask_live; // .. at the time of call unsigned int ret_dep:1; // return from this is caller's return + unsigned int has_ret:1; // found from eax use after return + unsigned int has_ret64:1; }; static struct func_prototype *hg_fp; @@ -8002,7 +8365,7 @@ static void hg_ref_add(const char *name) // - 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) + int *regmask_dep, int *regmask_use, int *has_ret) { struct func_proto_dep *dep; struct parsed_op *po; @@ -8028,7 +8391,8 @@ static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits, for (j = 0; j < po->btj->count; j++) { check_i(po, po->btj->d[j].bt_i); gen_hdr_dep_pass(po->btj->d[j].bt_i, opcnt, cbits, fp, - regmask_save, regmask_dst, regmask_dep, has_ret); + regmask_save, regmask_dst, regmask_dep, regmask_use, + has_ret); } return; } @@ -8036,7 +8400,8 @@ static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits, check_i(po, po->bt_i); if (po->flags & OPF_CJMP) { gen_hdr_dep_pass(po->bt_i, opcnt, cbits, fp, - regmask_save, regmask_dst, regmask_dep, has_ret); + regmask_save, regmask_dst, regmask_dep, regmask_use, + has_ret); } else { i = po->bt_i - 1; @@ -8088,9 +8453,7 @@ static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits, } } - // if has_ret is 0, there is uninitialized eax path, - // which means it's most likely void func - if (*has_ret != 0 && (po->flags & OPF_TAIL)) { + if (!fp->eax_pass && (po->flags & OPF_TAIL)) { if (po->op == OP_CALL) { j = i; ret = 1; @@ -8105,14 +8468,27 @@ static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits, if (ret != 1 && from_caller) { // unresolved eax - probably void func *has_ret = 0; + fp->eax_pass = 1; } else { if (j >= 0 && ops[j].op == OP_CALL) { - dep = hg_fp_find_dep(fp, ops[j].operand[0].name); - if (dep != NULL) - dep->ret_dep = 1; - else - *has_ret = 1; + if (ops[j].pp != NULL && !ops[j].pp->is_unresolved) { + int call_has_ret = !IS(ops[j].pp->ret_type.name, "void"); + if (ops[j].pp->is_noreturn) { + // could be some fail path + if (*has_ret == -1) + *has_ret = call_has_ret; + } + else + *has_ret = call_has_ret; + } + else { + dep = hg_fp_find_dep(fp, ops[j].operand[0].name); + if (dep != NULL) + dep->ret_dep = 1; + else + *has_ret = 1; + } } else *has_ret = 1; @@ -8130,10 +8506,14 @@ static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits, l, regmask_dst, regmask_save, po->flags); #endif *regmask_dep |= l; + *regmask_use |= (po->regmask_src | po->regmask_dst) + & ~regmask_save; regmask_dst |= po->regmask_dst; - if (po->flags & OPF_TAIL) - return; + if (po->flags & OPF_TAIL) { + if (!(po->flags & OPF_CC)) // not cond. tailcall + return; + } } } @@ -8143,9 +8523,11 @@ 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_op *po; int regmask_dummy = 0; int regmask_dep; + int regmask_use; int max_bp_offset = 0; int has_ret; int i, j, l; @@ -8194,15 +8576,9 @@ static void gen_hdr(const char *funcn, int opcnt) } // pass4: - // - remove dead labels // - 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|OPF_DONE)) continue; @@ -8273,15 +8649,31 @@ static void gen_hdr(const char *funcn, int opcnt) ret = collect_call_args(po, i, pp, ®mask_dummy, i + opcnt * 1); } + if (!(po->flags & OPF_TAIL) + && po->operand[0].type == OPT_LABEL) + { + dep = hg_fp_find_dep(fp, opr_name(po, 0)); + ferr_assert(po, dep != NULL); + // treat al write as overwrite to avoid many false positives + find_next_read_reg(i + 1, opcnt, xAX, OPLM_BYTE, + i + opcnt * 25, &j); + if (j != -1) + dep->has_ret = 1; + find_next_read_reg(i + 1, opcnt, xDX, OPLM_BYTE, + i + opcnt * 26, &j); + if (j != -1 && !IS_OP_INDIRECT_CALL(&ops[j])) + dep->has_ret64 = 1; + } } } // pass7 - memset(cbits, 0, sizeof(cbits)); - regmask_dep = 0; + memset(cbits, 0, (opcnt + 7) / 8); + regmask_dep = regmask_use = 0; has_ret = -1; - gen_hdr_dep_pass(0, opcnt, cbits, fp, 0, 0, ®mask_dep, &has_ret); + gen_hdr_dep_pass(0, opcnt, cbits, fp, 0, 0, + ®mask_dep, ®mask_use, &has_ret); // find unreachable code - must be fixed in IDA for (i = 0; i < opcnt; i++) @@ -8296,8 +8688,11 @@ static void gen_hdr(const char *funcn, int opcnt) // noreturn OS functions break; } - if (ops[i].op != OP_NOP && ops[i].op != OPP_ABORT) + if (!(ops[i].flags & OPF_RMD) + && ops[i].op != OP_NOP && ops[i].op != OPP_ABORT) + { ferr(&ops[i], "unreachable code\n"); + } } for (i = 0; i < g_eqcnt; i++) { @@ -8313,6 +8708,7 @@ static void gen_hdr(const char *funcn, int opcnt) } fp->regmask_dep = regmask_dep & ~((1 << xSP) | mxSTa); + fp->regmask_use = regmask_use; fp->has_ret = has_ret; #if 0 printf("// has_ret %d, regmask_dep %x\n", @@ -8327,28 +8723,35 @@ static void gen_hdr(const char *funcn, int opcnt) static void hg_fp_resolve_deps(struct func_prototype *fp) { struct func_prototype fp_s; - int dep; + struct func_proto_dep *dep; + int regmask_dep; int i; // this thing is recursive, so mark first.. fp->dep_resolved = 1; for (i = 0; i < fp->dep_func_cnt; i++) { - strcpy(fp_s.name, fp->dep_func[i].name); - fp->dep_func[i].proto = bsearch(&fp_s, hg_fp, hg_fp_cnt, + dep = &fp->dep_func[i]; + + strcpy(fp_s.name, dep->name); + dep->proto = bsearch(&fp_s, hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_name); - if (fp->dep_func[i].proto != NULL) { - if (!fp->dep_func[i].proto->dep_resolved) - hg_fp_resolve_deps(fp->dep_func[i].proto); + if (dep->proto != NULL) { + if (!dep->proto->dep_resolved) + hg_fp_resolve_deps(dep->proto); - dep = ~fp->dep_func[i].regmask_live - & fp->dep_func[i].proto->regmask_dep; - fp->regmask_dep |= dep; + regmask_dep = ~dep->regmask_live + & dep->proto->regmask_dep; + fp->regmask_dep |= regmask_dep; // printf("dep %s %s |= %x\n", fp->name, - // fp->dep_func[i].name, dep); + // fp->dep_func[i].name, regmask_dep); - if (fp->has_ret == -1 && fp->dep_func[i].ret_dep) - fp->has_ret = fp->dep_func[i].proto->has_ret; + if (dep->has_ret && (dep->proto->regmask_use & mxAX)) + dep->proto->has_ret = 1; + if (dep->has_ret64 && (dep->proto->regmask_use & mxDX)) + dep->proto->has_ret64 = 1; + if (fp->has_ret == -1 && dep->ret_dep) + fp->has_ret = dep->proto->has_ret; } } } @@ -8418,8 +8821,10 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, regmask_dep = fp->regmask_dep; argc_normal = fp->argc_stack; - fprintf(fout, "%-5s", fp->pp ? fp->pp->ret_type.name : - (fp->has_ret ? "int" : "void")); + fprintf(fout, "%-5s", + fp->pp ? fp->pp->ret_type.name : + fp->has_ret64 ? "__int64" : + fp->has_ret ? "int" : "void"); if (regmask_dep && (fp->is_stdcall || fp->argc_stack > 0) && (regmask_dep & ~mxCX) == 0) { @@ -8427,8 +8832,9 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, argc_normal++; regmask_dep = 0; } - else if (regmask_dep && (fp->is_stdcall || fp->argc_stack == 0) - && (regmask_dep & ~(mxCX | mxDX)) == 0) + else if ((regmask_dep == (mxCX | mxDX) + && (fp->is_stdcall || fp->argc_stack == 0)) + || (regmask_dep == mxCX && fp->argc_stack == 0)) { fprintf(fout, " __fastcall "); if (!(regmask_dep & (1 << xDX)) && fp->argc_stack == 0) @@ -8519,6 +8925,12 @@ static void output_hdr(FILE *fout) // adjust functions referenced from data segment do_func_refs_from_data(); + // final adjustments + for (i = 0; i < hg_fp_cnt; i++) { + if (hg_fp[i].eax_pass && (hg_fp[i].regmask_dep & mxAX)) + hg_fp[i].has_ret = 1; + } + // note: messes up .proto ptr, don't use //qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_id); @@ -8942,7 +9354,7 @@ int main(int argc, char *argv[]) int pi = 0; int i, j; int ret, len; - char *p; + char *p, *p2; int wordc; for (arg = 1; arg < argc; arg++) { @@ -9110,6 +9522,8 @@ int main(int argc, char *argv[]) static const char *attrs[] = { "clear_sf", "clear_regmask", + "rm_regmask", + "nowarn", }; // parse manual attribute-list comment @@ -9133,6 +9547,9 @@ int main(int argc, char *argv[]) else if (i == 1) // clear_regmask= ret = sscanf(p, "=%x%n", &g_regmask_init, &j) + 1; + else if (i == 2) + // rm_regmask= + ret = sscanf(p, "=%x%n", &g_regmask_rm, &j) + 1; if (ret < 2) { anote("unparsed attr value: %s\n", p); break; @@ -9219,6 +9636,11 @@ parse_words: // allow asm patches in comments if (*p == ';') { + // skip IDA's forced non-removable comment + if (!IS_START(p, "; sct") && (p2 = strchr(p + 1, ';'))) + p = p2; + } + if (*p == ';' && IS_START(p, "; sct")) { if (IS_START(p, "; sctpatch:")) { p = sskip(p + 11); if (*p == 0 || *p == ';') @@ -9336,6 +9758,7 @@ do_pending_endp: g_stack_clear_start = 0; g_stack_clear_len = 0; g_regmask_init = 0; + g_regmask_rm = 0; skip_warned = 0; g_skip_func = 0; g_func[0] = 0;