X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=tools%2Ftranslate.c;h=510478f85bf54293d387cc2c38f45330932e7730;hb=77d7983adfe3ff5944d26b3af274a15e8af05556;hp=12f01f9fae33c9de96c4c2cf2f4f473940f4fbb8;hpb=ba93cc12011777a68120c6c5cd488b8b149483f2;p=ia32rtools.git diff --git a/tools/translate.c b/tools/translate.c index 12f01f9..510478f 100644 --- a/tools/translate.c +++ b/tools/translate.c @@ -18,7 +18,9 @@ #define _GNU_SOURCE #include #include +#include #include +#include #include #include "my_assert.h" @@ -60,7 +62,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 */ @@ -68,8 +70,9 @@ enum op_flags { OPF_NOREGS = (1 << 21), /* don't track regs of this op */ OPF_FPUSH = (1 << 22), /* pushes x87 stack */ OPF_FPOP = (1 << 23), /* pops x87 stack */ - OPF_FSHIFT = (1 << 24), /* x87 stack shift is actually needed */ - OPF_FINT = (1 << 25), /* integer float op arg */ + OPF_FPOPP = (1 << 24), /* pops x87 stack twice */ + OPF_FSHIFT = (1 << 25), /* x87 stack shift is actually needed */ + OPF_FINT = (1 << 26), /* integer float op arg */ }; enum op_op { @@ -94,6 +97,8 @@ enum op_op { OP_MOVS, OP_CMPS, OP_SCAS, + OP_RDTSC, + OP_CPUID, OP_STD, OP_CLD, OP_RET, @@ -136,6 +141,7 @@ enum op_op { OP_FLDc, OP_FST, OP_FIST, + OP_FABS, OP_FADD, OP_FDIV, OP_FMUL, @@ -218,10 +224,10 @@ struct parsed_op { unsigned char pfo; unsigned char pfo_inv; unsigned char operand_cnt; - unsigned char p_argnum; // arg push: altered before call arg # + unsigned char p_argnum; // arg push: call's saved arg # unsigned char p_arggrp; // arg push: arg group # for above unsigned char p_argpass;// arg push: arg of host func - short p_argnext;// arg push: same arg pushed elsewhere or -1 + short pad; int regmask_src; // all referensed regs int regmask_dst; int pfomask; // flagop: parsed_flag_op that can't be delayed @@ -276,15 +282,19 @@ 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 (mask) + SCTFA_NOWARN = (1 << 3), // don't try to detect problems + SCTFA_ARGFRAME = (1 << 4), // copy all args to a struct, in order }; 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, @@ -300,7 +310,6 @@ enum segment { SEG_GS, }; -// note: limited to 32k due to p_argnext #define MAX_OPS 4096 #define MAX_ARG_GRP 2 @@ -326,9 +335,11 @@ 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; +static int g_nowarn_reguse; static int g_quiet_pp; static int g_header_mode; @@ -346,6 +357,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 @@ -367,6 +381,7 @@ enum x86_regs { }; #define mxAX (1 << xAX) +#define mxBX (1 << xBX) #define mxCX (1 << xCX) #define mxDX (1 << xDX) #define mxSP (1 << xSP) @@ -588,24 +603,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; @@ -942,7 +960,7 @@ static const struct { { "repz", OPF_REP|OPF_REPZ }, { "repne", OPF_REP|OPF_REPNZ }, { "repnz", OPF_REP|OPF_REPNZ }, - { "lock", OPF_LOCK }, // ignored for now.. + { "lock", OPF_LOCK }, }; #define OPF_CJMP_CC (OPF_JMP|OPF_CJMP|OPF_CC) @@ -986,6 +1004,8 @@ static const struct { { "scasb",OP_SCAS, 0, 0, OPF_DATA|OPF_FLAGS }, { "scasw",OP_SCAS, 0, 0, OPF_DATA|OPF_FLAGS }, { "scasd",OP_SCAS, 0, 0, OPF_DATA|OPF_FLAGS }, + { "rdtsc",OP_RDTSC, 0, 0, OPF_DATA }, + { "cpuid",OP_CPUID, 0, 0, OPF_DATA }, { "std", OP_STD, 0, 0, OPF_DATA }, // special flag { "cld", OP_CLD, 0, 0, OPF_DATA }, { "add", OP_ADD, 2, 2, OPF_DATA|OPF_FLAGS }, @@ -1083,12 +1103,17 @@ 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 }, { "fstp", OP_FST, 1, 1, OPF_FPOP }, { "fist", OP_FIST, 1, 1, OPF_FINT }, { "fistp", OP_FIST, 1, 1, OPF_FPOP|OPF_FINT }, + { "fabs", OP_FABS, 0, 0, 0 }, { "fadd", OP_FADD, 0, 2, 0 }, { "faddp", OP_FADD, 0, 2, OPF_FPOP }, { "fdiv", OP_FDIV, 0, 2, 0 }, @@ -1109,6 +1134,10 @@ static const struct { { "fisubr", OP_FISUBR, 1, 1, OPF_FINT }, { "fcom", OP_FCOM, 0, 1, 0 }, { "fcomp", OP_FCOM, 0, 1, OPF_FPOP }, + { "fcompp", OP_FCOM, 0, 0, OPF_FPOPP }, + { "fucom", OP_FCOM, 0, 1, 0 }, + { "fucomp", OP_FCOM, 0, 1, OPF_FPOP }, + { "fucompp",OP_FCOM, 0, 0, OPF_FPOPP }, { "fnstsw", OP_FNSTSW, 1, 1, OPF_DATA }, { "fchs", OP_FCHS, 0, 0, 0 }, { "fcos", OP_FCOS, 0, 0, 0 }, @@ -1305,6 +1334,16 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) op->regmask_dst = op->regmask_src; break; + case OP_RDTSC: + op->regmask_dst = mxAX | mxDX; + break; + + case OP_CPUID: + // for now, ignore ecx dep for eax={4,7,b,d} + op->regmask_src = mxAX; + op->regmask_dst = mxAX | mxBX | mxCX | mxDX; + break; + case OP_LOOP: op->regmask_dst = 1 << xCX; // fallthrough @@ -1327,20 +1366,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: @@ -1395,6 +1440,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; @@ -1413,12 +1460,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: @@ -1453,6 +1508,7 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) case OP_FISUB: case OP_FIDIVR: case OP_FISUBR: + case OP_FABS: case OP_FCHS: case OP_FCOS: case OP_FSIN: @@ -1474,6 +1530,13 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) case OP_FCOM: op->regmask_src |= mxST0; + if (op->operand_cnt == 0) { + op->operand_cnt = 1; + op->operand[0].type = OPT_REG; + op->operand[0].lmod = OPLM_QWORD; + op->operand[0].reg = xST1; + op->regmask_src |= mxST1; + } break; default: @@ -1875,6 +1938,9 @@ static int parse_stack_esp_offset(struct parsed_op *po, return 0; } +// returns g_func_pp arg number if arg is accessed +// -1 otherwise (stack vars, va_list) +// note: 'popr' must be from 'po', not some other op static int stack_frame_access(struct parsed_op *po, struct parsed_opr *popr, char *buf, size_t buf_size, const char *name, const char *cast, int is_src, int is_lea) @@ -1883,6 +1949,7 @@ static int stack_frame_access(struct parsed_op *po, const char *prefix = ""; const char *bp_arg = NULL; char ofs_reg[16] = { 0, }; + char argname[8]; int i, arg_i, arg_s; int unaligned = 0; int stack_ra = 0; @@ -1904,16 +1971,18 @@ static int stack_frame_access(struct parsed_op *po, arg_i = (offset - stack_ra - 4) / 4; if (arg_i < 0 || arg_i >= g_func_pp->argc_stack) { - if (g_func_pp->is_vararg - && arg_i == g_func_pp->argc_stack && is_lea) - { - // should be va_list - if (cast[0] == 0) - cast = "(u32)"; - snprintf(buf, buf_size, "%sap", cast); + if (g_func_pp->is_vararg && arg_i == g_func_pp->argc_stack) { + if (is_lea) { + // should be va_list + if (cast[0] == 0) + cast = "(u32)"; + snprintf(buf, buf_size, "%sap", cast); + } + else + snprintf(buf, buf_size, "%sva_arg(ap, u32)", 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) @@ -1932,17 +2001,20 @@ static int stack_frame_access(struct parsed_op *po, popr->is_ptr = g_func_pp->arg[i].type.is_ptr; retval = i; + snprintf(argname, sizeof(argname), "%sa%d", + g_sct_func_attr & SCTFA_ARGFRAME ? "af." : "", i + 1); + switch (popr->lmod) { case OPLM_BYTE: if (is_lea) ferr(po, "lea/byte to arg?\n"); if (is_src && (offset & 3) == 0) - snprintf(buf, buf_size, "%sa%d", - simplify_cast(cast, "(u8)"), i + 1); + snprintf(buf, buf_size, "%s%s", + simplify_cast(cast, "(u8)"), argname); else - snprintf(buf, buf_size, "%sBYTE%d(a%d)", - cast, offset & 3, i + 1); + snprintf(buf, buf_size, "%sBYTE%d(%s)", + cast, offset & 3, argname); break; case OPLM_WORD: @@ -1953,18 +2025,18 @@ static int stack_frame_access(struct parsed_op *po, if (!is_src) { if (offset & 2) ferr(po, "problematic arg store\n"); - snprintf(buf, buf_size, "%s((char *)&a%d + 1)", - simplify_cast(cast, "*(u16 *)"), i + 1); + snprintf(buf, buf_size, "%s((char *)&%s + 1)", + simplify_cast(cast, "*(u16 *)"), argname); } else ferr(po, "unaligned arg word load\n"); } else if (is_src && (offset & 2) == 0) - snprintf(buf, buf_size, "%sa%d", - simplify_cast(cast, "(u16)"), i + 1); + snprintf(buf, buf_size, "%s%s", + simplify_cast(cast, "(u16)"), argname); else - snprintf(buf, buf_size, "%s%sWORD(a%d)", - cast, (offset & 2) ? "HI" : "LO", i + 1); + snprintf(buf, buf_size, "%s%sWORD(%s)", + cast, (offset & 2) ? "HI" : "LO", argname); break; case OPLM_DWORD: @@ -1976,19 +2048,19 @@ static int stack_frame_access(struct parsed_op *po, if (offset & 3) { unaligned = 1; if (is_lea) - snprintf(buf, buf_size, "(u32)&a%d + %d", - i + 1, offset & 3); + snprintf(buf, buf_size, "(u32)&%s + %d", + argname, offset & 3); else if (!is_src) ferr(po, "unaligned arg store\n"); else { // mov edx, [ebp+arg_4+2]; movsx ecx, dx - snprintf(buf, buf_size, "%s(a%d >> %d)", - prefix, i + 1, (offset & 3) * 8); + snprintf(buf, buf_size, "%s(%s >> %d)", + prefix, argname, (offset & 3) * 8); } } else { - snprintf(buf, buf_size, "%s%sa%d", - prefix, is_lea ? "&" : "", i + 1); + snprintf(buf, buf_size, "%s%s%s", + prefix, is_lea ? "&" : "", argname); } break; @@ -1996,8 +2068,8 @@ static int stack_frame_access(struct parsed_op *po, ferr_assert(po, !(offset & 7)); if (cast[0]) prefix = cast; - snprintf(buf, buf_size, "%s%sa%d", - prefix, is_lea ? "&" : "", i + 1); + snprintf(buf, buf_size, "%s%s%s", + prefix, is_lea ? "&" : "", argname); break; default: @@ -2117,7 +2189,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; @@ -2128,6 +2200,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; } @@ -2146,6 +2221,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; @@ -2218,7 +2294,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)"; @@ -2234,7 +2314,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) @@ -2330,11 +2411,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) @@ -2371,6 +2461,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); } @@ -2616,8 +2716,9 @@ static const char *op_to_c(struct parsed_op *po) // note: this skips over calls and rm'd stuff assuming they're handled // so it's intended to use at one of final passes +// exception: doesn't skip OPF_RSAVE stuff static int scan_for_pop(int i, int opcnt, int magic, int reg, - int depth, int seen_noreturn, int flags_set) + int depth, int seen_noreturn, int save_level, int flags_set) { struct parsed_op *po; int relevant; @@ -2631,18 +2732,28 @@ static int scan_for_pop(int i, int opcnt, int magic, int reg, po->cc_scratch = magic; if (po->flags & OPF_TAIL) { - if (po->op == OP_CALL) { - if (po->pp != NULL && po->pp->is_noreturn) - seen_noreturn = 1; - else + if (po->op == OP_CALL && po->pp != NULL && po->pp->is_noreturn) { + // msvc sometimes generates stack cleanup code after + // noreturn, set a flag and continue + seen_noreturn = 1; + + // ... but stop if there is another path to next insn - + // if msvc skipped something stack tracking may mess up + if (i + 1 < opcnt && g_labels[i + 1] != NULL) goto out; } else goto out; } - if (po->flags & (OPF_RMD|OPF_DONE|OPF_FARG)) + if (po->flags & OPF_FARG) continue; + if (po->flags & (OPF_RMD|OPF_DONE)) { + if (!(po->flags & OPF_RSAVE)) + continue; + // reprocess, there might be another push in some "parallel" + // path that took a pop what we should also take + } if ((po->flags & OPF_JMP) && po->op != OP_CALL) { if (po->btj != NULL) { @@ -2650,7 +2761,7 @@ static int scan_for_pop(int i, int opcnt, int magic, int reg, for (j = 0; j < po->btj->count; j++) { check_i(po, po->btj->d[j].bt_i); ret |= scan_for_pop(po->btj->d[j].bt_i, opcnt, magic, reg, - depth, seen_noreturn, flags_set); + depth, seen_noreturn, save_level, flags_set); if (ret < 0) return ret; // dead end } @@ -2660,7 +2771,7 @@ static int scan_for_pop(int i, int opcnt, int magic, int reg, check_i(po, po->bt_i); if (po->flags & OPF_CJMP) { ret |= scan_for_pop(po->bt_i, opcnt, magic, reg, - depth, seen_noreturn, flags_set); + depth, seen_noreturn, save_level, flags_set); if (ret < 0) return ret; // dead end } @@ -2682,6 +2793,13 @@ static int scan_for_pop(int i, int opcnt, int magic, int reg, } else if (po->op == OP_POP) { if (relevant && depth == 0) { + if (flags_set == 0 && save_level > 0) { + ret = scan_for_pop(i + 1, opcnt, magic, reg, + depth, seen_noreturn, save_level - 1, flags_set); + if (ret != 1) + // no pop for other levels, current one must be false + return -1; + } po->flags |= flags_set; return 1; } @@ -3454,6 +3572,11 @@ static const struct parsed_proto *try_recover_pp( char buf[256]; char *p; + if (po->pp != NULL && (po->flags & OPF_DATA)) { + // hint given in asm + return po->pp; + } + // maybe an arg of g_func? if (opr->type == OPT_REGMEM && is_stack_access(po, opr)) { @@ -3515,8 +3638,8 @@ static const struct parsed_proto *try_recover_pp( } static void scan_for_call_type(int i, const struct parsed_opr *opr, - int magic, const struct parsed_proto **pp_found, int *pp_i, - int *multi) + int magic, int is_call_op, const struct parsed_proto **pp_found, + int *pp_i, int *multi) { const struct parsed_proto *pp = NULL; struct parsed_op *po; @@ -3529,7 +3652,8 @@ static void scan_for_call_type(int i, const struct parsed_opr *opr, lr = &g_label_refs[i]; for (; lr != NULL; lr = lr->next) { check_i(&ops[i], lr->i); - scan_for_call_type(lr->i, opr, magic, pp_found, pp_i, multi); + scan_for_call_type(lr->i, opr, magic, is_call_op, + pp_found, pp_i, multi); } if (i > 0 && LAST_OP(i - 1)) return; @@ -3574,29 +3698,30 @@ static void scan_for_call_type(int i, const struct parsed_opr *opr, if (i == g_func_pp->argc) return; pp = g_func_pp->arg[i].pp; - if (pp == NULL) - ferr(po, "icall: arg%d (%s) is not a fptr?\n", - i + 1, g_func_pp->arg[i].reg); + if (pp == NULL) { + if (is_call_op) + ferr(po, "icall: arg%d (%s) is not a fptr?\n", + i + 1, g_func_pp->arg[i].reg); + return; + } check_func_pp(po, pp, "icall reg-arg"); } else - pp = try_recover_pp(po, opr, 1, NULL); + pp = try_recover_pp(po, opr, is_call_op, NULL); if (*pp_found != NULL && pp != NULL && *pp_found != pp) { - if (!IS((*pp_found)->ret_type.name, pp->ret_type.name) - || (*pp_found)->is_stdcall != pp->is_stdcall - //|| (*pp_found)->is_fptr != pp->is_fptr - || (*pp_found)->argc != pp->argc - || (*pp_found)->argc_reg != pp->argc_reg - || (*pp_found)->argc_stack != pp->argc_stack) - { + if (pp_cmp_func(*pp_found, pp)) { + if (pp_i != NULL && *pp_i != -1) + fnote(&ops[*pp_i], "(other ref)\n"); ferr(po, "icall: parsed_proto mismatch\n"); } - *multi = 1; + if (multi != NULL) + *multi = 1; } if (pp != NULL) { *pp_found = pp; - *pp_i = po - ops; + if (pp_i != NULL) + *pp_i = po - ops; } } @@ -3775,10 +3900,21 @@ 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")) { + ferr_assert(po, g_seh_found == 0); + g_seh_found = 2; + continue; + } + if (IS(tmpname, "__SEH_epilog")) + continue; // convert some calls to pseudo-ops for (l = 0; l < ARRAY_SIZE(pseudo_ops); l++) { @@ -3789,7 +3925,8 @@ static void resolve_branches_parse_calls(int opcnt) po->operand_cnt = 0; po->regmask_src = pseudo_ops[l].regmask_src; po->regmask_dst = pseudo_ops[l].regmask_dst; - po->flags = pseudo_ops[l].flags; + po->flags &= OPF_TAIL; + po->flags |= pseudo_ops[l].flags; po->flags |= po->regmask_dst ? OPF_DATA : 0; break; } @@ -3835,8 +3972,10 @@ static void resolve_branches_parse_calls(int opcnt) && IS(po->operand[0].name, g_labels[l])) { if (l == i + 1 && po->op == OP_JMP) { - // yet another alignment type.. - po->flags |= OPF_RMD|OPF_DONE; + // yet another alignment type... + po->flags |= OPF_RMD | OPF_DONE; + po->flags &= ~OPF_JMP; + po->op = OP_NOP; break; } add_label_ref(&g_label_refs[l], i); @@ -3848,7 +3987,8 @@ static void resolve_branches_parse_calls(int opcnt) if (po->bt_i != -1 || (po->flags & OPF_RMD)) continue; - if (po->operand[0].type == OPT_LABEL) + if (po->operand[0].type == OPT_LABEL + || po->operand[0].type == OPT_REG) // assume tail call goto tailcall; @@ -3871,6 +4011,114 @@ 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) +{ + const struct parsed_opr *opr; + char ofs_reg[16]; + int offset; + int i; + + // assume all sf writes above g_seh_size to be seh related + // (probably unsafe but oh well) + for (i = 0; i < opcnt; i++) { + if (ops[i].op != OP_MOV) + continue; + opr = &ops[i].operand[0]; + if (opr->type != OPT_REGMEM) + continue; + if (!is_stack_access(&ops[i], opr)) + continue; + + offset = 0; + parse_stack_access(&ops[i], opr->name, ofs_reg, &offset, + NULL, NULL, 0); + if (offset < 0 && offset >= -g_seh_size) + ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + } +} + +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) { @@ -3913,35 +4161,244 @@ static void eliminate_seh(int opcnt) } } - // assume all sf writes above g_seh_size to be seh related - // (probably unsafe but oh well) - for (i = 0; i < opcnt; i++) { - const struct parsed_opr *opr; - char ofs_reg[16]; - int offset = 0; + eliminate_seh_writes(opcnt); + eliminate_seh_finally(opcnt); +} - if (ops[i].op != OP_MOV) +static void eliminate_seh_calls(int opcnt) +{ + int epilog_found = 0; + int i; + + g_bp_frame = 1; + g_seh_size = 0x10; + + i = 0; + ferr_assert(&ops[i], ops[i].op == OP_PUSH + && ops[i].operand[0].type == OPT_CONST); + g_stack_fsz = g_seh_size + ops[i].operand[0].val; + ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + + i++; + ferr_assert(&ops[i], ops[i].op == OP_PUSH + && ops[i].operand[0].type == OPT_OFFSET); + ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + + i++; + ferr_assert(&ops[i], ops[i].op == OP_CALL + && IS(opr_name(&ops[i], 0), "__SEH_prolog")); + ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + + for (i++; i < opcnt; i++) { + if (ops[i].op != OP_CALL) continue; - opr = &ops[i].operand[0]; - if (opr->type != OPT_REGMEM) + if (!IS(opr_name(&ops[i], 0), "__SEH_epilog")) continue; - if (!is_stack_access(&ops[i], opr)) + + ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + epilog_found = 1; + } + 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; - parse_stack_access(&ops[i], opr->name, ofs_reg, &offset, - NULL, NULL, 0); - if (offset < 0 && offset >= -g_seh_size) + 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 pushes like this as part of var area + *fsz += seq_len * 4; +} + +static int scan_prologue_ecx(int i, int opcnt, int flags_set, + int limit, int *ecx_push_out) +{ + const struct parsed_proto *pp; + int ecx_push = 0, other_push = 0; + int ret; + + while (limit > 0 && ops[i].op == OP_PUSH + && IS(opr_name(&ops[i], 0), "ecx")) + { + ops[i].flags |= flags_set; + ecx_push++; + i++; + limit--; + } + + ret = i; + if (ecx_push == 0 || flags_set != 0) + goto out; + + // check if some of the pushes aren't really call args + for (; i < opcnt; i++) { + if (i > 0 && g_labels[i] != NULL) + break; + if (ops[i].flags & (OPF_JMP|OPF_TAIL)) + break; + if (ops[i].op == OP_PUSH) + other_push++; + } + + if (ops[i].op != OP_CALL) + goto out; + + pp = ops[i].pp; + if (pp == NULL && ops[i].operand[0].type == OPT_LABEL) + pp = proto_parse(g_fhdr, opr_name(&ops[i], 0), 1); + if (pp == NULL) + goto out; + + ferr_assert(&ops[i], ecx_push + other_push >= pp->argc_stack); + if (other_push < pp->argc_stack) + ecx_push -= pp->argc_stack - other_push; + +out: + if (ecx_push_out != NULL) + *ecx_push_out = ecx_push; + return ret; +} + +static int scan_prologue(int i, int opcnt, int *ecx_push, int *esp_sub) +{ + const char *name; + int j, len, ret; + int ecx_tmp = 0; + + for (; i < opcnt; i++) + if (!(ops[i].flags & OPF_DONE)) + break; + + ret = scan_prologue_ecx(i, opcnt, 0, 4, &ecx_tmp); + if (ecx_tmp > 0) { + scan_prologue_ecx(i, opcnt, OPF_RMD | OPF_DONE | OPF_NOREGS, + ecx_tmp, NULL); + g_stack_fsz += 4 * ecx_tmp; + *ecx_push += ecx_tmp; + i = ret; + } + + for (; i < opcnt; i++) { + if (i > 0 && g_labels[i] != NULL) + break; + if (ops[i].flags & (OPF_JMP|OPF_TAIL)) + break; + if (ops[i].flags & OPF_DONE) + continue; + if (ops[i].op == OP_PUSH) + break; + if (ops[i].op == OP_SUB && ops[i].operand[0].reg == xSP + && ops[i].operand[1].type == OPT_CONST) + { + g_stack_fsz += opr_const(&ops[i], 1); + ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + i++; + *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) + { + for (j = i + 1; j < opcnt; j++) + if (!(ops[j].flags & OPF_DONE)) + break; + if (ops[j].op == OP_CALL + && IS(opr_name(&ops[j], 0), "__alloca_probe")) + { + g_stack_fsz += opr_const(&ops[i], 1); + ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + i = j + 1; + *esp_sub = 1; + break; + } + } } + + return i; } 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) { + eliminate_seh_calls(opcnt); + return; + } if (g_seh_found) { eliminate_seh(opcnt); // ida treats seh as part of sf @@ -3981,32 +4438,7 @@ static void scan_prologue_epilogue(int opcnt, int *stack_align) i++; } - if (ops[i].op == OP_SUB && IS(opr_name(&ops[i], 0), "esp")) { - g_stack_fsz += opr_const(&ops[i], 1); - ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; - i++; - } - else { - // another way msvc builds stack frame.. - while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) { - g_stack_fsz += 4; - ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; - ecx_push++; - i++; - } - // and another way.. - if (ops[i].op == OP_MOV && ops[i].operand[0].reg == xAX - && ops[i].operand[1].type == OPT_CONST - && ops[i + 1].op == OP_CALL - && IS(opr_name(&ops[i + 1], 0), "__alloca_probe")) - { - g_stack_fsz += ops[i].operand[1].val; - ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; - i++; - ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; - i++; - } - } + i = scan_prologue(i, opcnt, &ecx_push, &esp_sub); found = 0; do { @@ -4084,76 +4516,8 @@ static void scan_prologue_epilogue(int opcnt, int *stack_align) } // non-bp frame - for (i = 0; i < opcnt; i++) - if (!(ops[i].flags & OPF_DONE)) - break; - - while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) { - ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; - g_stack_fsz += 4; - ecx_push++; - i++; - } - - for (; i < opcnt; i++) { - if (ops[i].op == OP_PUSH || (ops[i].flags & (OPF_JMP|OPF_TAIL))) - break; - if (ops[i].op == OP_SUB && ops[i].operand[0].reg == xSP - && ops[i].operand[1].type == OPT_CONST) - { - g_stack_fsz += ops[i].operand[1].val; - ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; - i++; - esp_sub = 1; - break; - } - else if (ops[i].op == OP_MOV && ops[i].operand[0].reg == xAX - && ops[i].operand[1].type == OPT_CONST - && ops[i + 1].op == OP_CALL - && IS(opr_name(&ops[i + 1], 0), "__alloca_probe")) - { - g_stack_fsz += ops[i].operand[1].val; - ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; - i++; - ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; - i++; - 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 | OPF_DONE | OPF_NOREGS); - j--; - } - } - if (j != 0) - ferr(&ops[i], "unhandled prologue\n"); - - // recheck - i = ecx_push = 0; - g_stack_fsz = g_seh_size; - 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++; - } - } - } + check_simple_sequence(opcnt, &push_fsz); + i = scan_prologue(0, opcnt, &ecx_push, &esp_sub); found = 0; if (ecx_push || esp_sub) @@ -4172,9 +4536,28 @@ static void scan_prologue_epilogue(int opcnt, int *stack_align) i--; j--; } + else if (i < opcnt && (ops[i].flags & OPF_ATAIL)) { + // skip arg updates for arg-reuse tailcall + for (; j >= 0; j--) { + if (ops[j].op != OP_MOV) + break; + if (ops[j].operand[0].type == OPT_REGMEM + && strstr(ops[j].operand[0].name, "arg_") != NULL) + continue; + if (ops[j].operand[0].type == OPT_REG) + continue; // assume arg-reg mov + break; + } + } + + 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; l++) { + for (l = 0; l < ecx_push && j >= 0; l++) { if (ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ecx")) /* pop ecx */; else if (ops[j].op == OP_ADD @@ -4206,28 +4589,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++; @@ -4236,6 +4628,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 @@ -4288,6 +4684,17 @@ static int resolve_origin(int i, const struct parsed_opr *opr, } } +static int resolve_origin_reg(int i, int reg, int magic, int *op_i, + int *is_caller) +{ + struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, reg); + + *op_i = -1; + if (is_caller != NULL) + *is_caller = 0; + return resolve_origin(i, &opr, magic, op_i, is_caller); +} + // find an instruction that previously referenced opr // if multiple results are found - fail // *op_i must be set to -1 by the caller @@ -4434,6 +4841,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 @@ -4552,9 +4968,8 @@ static int resolve_used_bits(int i, int opcnt, int reg, } static const struct parsed_proto *resolve_deref(int i, int magic, - struct parsed_opr *opr, int level) + const struct parsed_opr *opr, int level) { - struct parsed_opr opr_s = OPR_INIT(OPT_REG, OPLM_DWORD, 0); const struct parsed_proto *pp = NULL; int from_caller = 0; char s_reg[4]; @@ -4576,8 +4991,7 @@ static const struct parsed_proto *resolve_deref(int i, int magic, if (reg < 0) return NULL; - opr_s.reg = reg; - ret = resolve_origin(i, &opr_s, i + magic, &j, NULL); + ret = resolve_origin_reg(i, reg, i + magic, &j, NULL); if (ret != 1) return NULL; @@ -4592,8 +5006,7 @@ static const struct parsed_proto *resolve_deref(int i, int magic, ops[j].operand[1].name); if (reg < 0) return NULL; - opr_s.reg = reg; - ret = resolve_origin(j, &opr_s, j + magic, &k, NULL); + ret = resolve_origin_reg(j, reg, j + magic, &k, NULL); if (ret != 1) return NULL; j = k; @@ -4643,32 +5056,34 @@ static const struct parsed_proto *resolve_deref(int i, int magic, return proto_lookup_struct(g_fhdr, pp->type.name, offset); } -static const struct parsed_proto *resolve_icall(int i, int opcnt, +static const struct parsed_proto *resolve_func_ptr(int i, int opcnt, + int is_call_op, const struct parsed_opr *opr, int *pp_i, int *multi_src) { const struct parsed_proto *pp = NULL; int search_advice = 0; - *multi_src = 0; - *pp_i = -1; + if (multi_src != NULL) + *multi_src = 0; + if (pp_i != NULL) + *pp_i = -1; - switch (ops[i].operand[0].type) { + switch (opr->type) { case OPT_REGMEM: // try to resolve struct member calls - pp = resolve_deref(i, i + opcnt * 19, &ops[i].operand[0], 0); + pp = resolve_deref(i, i + opcnt * 19, opr, 0); if (pp != NULL) break; // fallthrough case OPT_LABEL: case OPT_OFFSET: - pp = try_recover_pp(&ops[i], &ops[i].operand[0], - 1, &search_advice); + pp = try_recover_pp(&ops[i], opr, is_call_op, &search_advice); if (!search_advice) break; // fallthrough default: - scan_for_call_type(i, &ops[i].operand[0], i + opcnt * 9, &pp, - pp_i, multi_src); + scan_for_call_type(i, opr, i + opcnt * 9, is_call_op, + &pp, pp_i, multi_src); break; } @@ -4729,7 +5144,8 @@ static struct parsed_proto *process_call(int i, int opcnt) if (pp == NULL) { // indirect call - pp_c = resolve_icall(i, opcnt, &call_i, &multipath); + pp_c = resolve_func_ptr(i, opcnt, 1, &ops[i].operand[0], + &call_i, &multipath); if (pp_c != NULL) { if (!pp_c->is_func && !pp_c->is_fptr) ferr(po, "call to non-func: %s\n", pp_c->name); @@ -4837,6 +5253,101 @@ out: return pp; } +static void check_fptr_args(int i, int opcnt, struct parsed_proto *pp) +{ + struct parsed_opr s_opr = OPR_INIT(OPT_REG, OPLM_DWORD, 0); + const struct parsed_proto *pp_arg, *pp_cmp; + const struct parsed_op *po_a; + const char *s_reg; + int pp_cmp_i; + int arg, reg; + int bad = 0; + int j; + + for (arg = 0; arg < pp->argc; arg++) { + pp_cmp = NULL; + pp_cmp_i = -1; + + pp_arg = pp->arg[arg].pp; + if (pp_arg == NULL || !pp_arg->is_func) + continue; + + s_reg = pp->arg[arg].reg; + if (s_reg != NULL) { + reg = char_array_i(regs_r32, ARRAY_SIZE(regs_r32), s_reg); + ferr_assert(&ops[i], reg >= 0); + s_opr.reg = reg; + scan_for_call_type(i, &s_opr, i + arg + opcnt * 28, 0, + &pp_cmp, &pp_cmp_i, NULL); + if (pp_cmp != NULL && !pp_compatible_func(pp_arg, pp_cmp)) { + bad = 1; + if (pp_cmp_i >= 0) + fnote(&ops[pp_cmp_i], "(referenced here)\n"); + } + } + else { + for (j = 0; j < pp->arg[arg].push_ref_cnt; j++) { + po_a = pp->arg[arg].push_refs[j]; + if (po_a == NULL || po_a->op != OP_PUSH) + continue; + pp_cmp = resolve_func_ptr(po_a - ops, opcnt, 0, + &po_a->operand[0], &pp_cmp_i, NULL); + if (pp_cmp != NULL && !pp_compatible_func(pp_arg, pp_cmp)) { + bad = 1; + if (pp_cmp_i < 0) + pp_cmp_i = po_a - ops; + if (pp_cmp_i >= 0) + fnote(&ops[pp_cmp_i], "(referenced here)\n"); + } + } + } + + if (bad) + ferr(&ops[i], "incompatible fptr arg %d\n", arg + 1); + } +} + +static void pp_add_push_ref(struct parsed_proto *pp, + int arg, struct parsed_op *po) +{ + pp->arg[arg].push_refs = realloc(pp->arg[arg].push_refs, + (pp->arg[arg].push_ref_cnt + 1) + * sizeof(pp->arg[arg].push_refs[0])); + ferr_assert(po, pp->arg[arg].push_refs != NULL); + pp->arg[arg].push_refs[pp->arg[arg].push_ref_cnt++] = po; +} + +static void mark_float_arg(struct parsed_op *po, + struct parsed_proto *pp, int arg, int *regmask_ffca) +{ + ferr_assert(po, pp->arg[arg].push_ref_cnt == 0); + pp_add_push_ref(pp, arg, po); + + po->p_argnum = arg + 1; + 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) { @@ -4870,13 +5381,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) @@ -4888,9 +5393,9 @@ static int collect_call_args_no_push(int i, struct parsed_proto *pp, for (arg = base_arg; arg < pp->argc; arg++) { ferr_assert(&ops[i], pp->arg[arg].reg == NULL); - po = pp->arg[arg].datap; - if (po == NULL) - ferr(&ops[i], "arg %d/%d not found\n", arg, pp->argc); + if (pp->arg[arg].push_ref_cnt != 1) + ferr(&ops[i], "arg %d/%d not found or bad\n", arg, pp->argc); + po = pp->arg[arg].push_refs[0]; if (po->operand[0].lmod == OPLM_QWORD) arg++; } @@ -4898,12 +5403,13 @@ static int collect_call_args_no_push(int i, struct parsed_proto *pp, return 0; } -static int collect_call_args_early(int i, struct parsed_proto *pp, - int *regmask, int *regmask_ffca) +static int collect_call_args_early(int i, int opcnt, + struct parsed_proto *pp, int *regmask, int *regmask_ffca) { 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) @@ -4969,9 +5475,25 @@ static int collect_call_args_early(int i, struct parsed_proto *pp, if (ops[j].op == OP_PUSH) { - ops[j].p_argnext = -1; - ferr_assert(&ops[j], pp->arg[arg].datap == NULL); - pp->arg[arg].datap = &ops[j]; + int ref_handled = 0; + + 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); + ref_handled = 1; + } + } + + if (!ref_handled) { + ferr_assert(&ops[j], pp->arg[arg].push_ref_cnt == 0); + pp_add_push_ref(pp, arg, &ops[j]); + } if (regmask != NULL && ops[j].operand[0].type == OPT_REG) *regmask |= 1 << ops[j].operand[0].reg; @@ -4986,41 +5508,56 @@ static int collect_call_args_early(int i, struct parsed_proto *pp, } } + if (!g_header_mode) + check_fptr_args(i, opcnt, pp); + return 0; } -static int sync_argnum(struct parsed_op *po, int argnum) +// ensure all s_a* numbers match for a given func arg in all branches +// returns 1 if any changes were made, 0 if not +static int sync_argnum(struct parsed_proto *pp, int arg, + int *argnum, int *arggrp) { struct parsed_op *po_tmp; + int changed = 0; + int i; // see if other branches don't have higher argnum - for (po_tmp = po; po_tmp != NULL; ) { - if (argnum < po_tmp->p_argnum) - argnum = po_tmp->p_argnum; - // note: p_argnext is active on current collect_call_args only - po_tmp = po_tmp->p_argnext >= 0 ? &ops[po_tmp->p_argnext] : NULL; + for (i = 0; i < pp->arg[arg].push_ref_cnt; i++) { + po_tmp = pp->arg[arg].push_refs[i]; + if (*argnum < po_tmp->p_argnum) + *argnum = po_tmp->p_argnum; + if (*arggrp < po_tmp->p_arggrp) + *arggrp = po_tmp->p_arggrp; } // make all argnums consistent - for (po_tmp = po; po_tmp != NULL; ) { - if (po_tmp->p_argnum != 0) - po_tmp->p_argnum = argnum; - po_tmp = po_tmp->p_argnext >= 0 ? &ops[po_tmp->p_argnext] : NULL; + for (i = 0; i < pp->arg[arg].push_ref_cnt; i++) { + po_tmp = pp->arg[arg].push_refs[i]; + if (po_tmp->p_argnum == 0) + continue; + if (po_tmp->p_argnum != *argnum || po_tmp->p_arggrp != *arggrp) { + po_tmp->p_argnum = *argnum; + po_tmp->p_arggrp = *arggrp; + changed = 1; + } } - return argnum; + return changed; } static int collect_call_args_r(struct parsed_op *po, int i, - struct parsed_proto *pp, int *regmask, int *arg_grp, - int arg, int argnum, int magic, int need_op_saving, int may_reuse) + struct parsed_proto *pp, int *regmask, + int arg, int argnum, int magic, + int skip, int need_op_saving, int may_reuse) { struct parsed_proto *pp_tmp; - struct parsed_op *po_tmp; struct label_ref *lr; int need_to_save_current; int arg_grp_current = 0; int save_args_seen = 0; + int dummy = 0; int ret = 0; int reg; char buf[32]; @@ -5057,8 +5594,8 @@ static int collect_call_args_r(struct parsed_op *po, int i, check_i(&ops[j], lr->i); if ((ops[lr->i].flags & (OPF_JMP|OPF_CJMP)) != OPF_JMP) may_reuse = 1; - ret = collect_call_args_r(po, lr->i, pp, regmask, arg_grp, - arg, argnum, magic, need_op_saving, may_reuse); + ret = collect_call_args_r(po, lr->i, pp, regmask, + arg, argnum, magic, skip, need_op_saving, may_reuse); if (ret < 0) return ret; } @@ -5072,8 +5609,8 @@ static int collect_call_args_r(struct parsed_op *po, int i, continue; } need_op_saving = 1; - ret = collect_call_args_r(po, lr->i, pp, regmask, arg_grp, - arg, argnum, magic, need_op_saving, may_reuse); + ret = collect_call_args_r(po, lr->i, pp, regmask, + arg, argnum, magic, skip, need_op_saving, may_reuse); if (ret < 0) return ret; } @@ -5091,6 +5628,8 @@ static int collect_call_args_r(struct parsed_op *po, int i, if (may_reuse && pp_tmp->argc_stack > 0) ferr(po, "arg collect %d/%d hit '%s' with %d stack args\n", arg, pp->argc, opr_name(&ops[j], 0), pp_tmp->argc_stack); + if (!pp_tmp->is_unresolved) + skip = pp_tmp->argc_stack; } // esp adjust of 0 means we collected it before else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP @@ -5119,19 +5658,19 @@ static int collect_call_args_r(struct parsed_op *po, int i, may_reuse = 1; } + else if (ops[j].op == OP_PUSH && skip > 0) { + // XXX: might want to rm OPF_FARGNR and only use this + skip--; + } else if (ops[j].op == OP_PUSH && !(ops[j].flags & (OPF_FARGNR|OPF_DONE))) { if (pp->is_unresolved && (ops[j].flags & OPF_RMD)) break; - ops[j].p_argnext = -1; - po_tmp = pp->arg[arg].datap; - if (po_tmp != NULL) - ops[j].p_argnext = po_tmp - ops; - pp->arg[arg].datap = &ops[j]; + pp_add_push_ref(pp, arg, &ops[j]); - argnum = sync_argnum(&ops[j], argnum); + sync_argnum(pp, arg, &argnum, &dummy); need_to_save_current = 0; reg = -1; @@ -5213,6 +5752,7 @@ static int collect_call_args_r(struct parsed_op *po, int i, if (pp->arg[arg].is_saved) { ops[j].flags &= ~OPF_RMD; ops[j].p_argnum = argnum; + ops[j].p_arggrp = arg_grp_current; } // tracking reg usage @@ -5244,41 +5784,19 @@ static int collect_call_args_r(struct parsed_op *po, int i, return -1; } - if (arg_grp_current > *arg_grp) - *arg_grp = arg_grp_current; - return arg; } -static int collect_call_args(struct parsed_op *po, int i, +static int collect_call_args(struct parsed_op *po, int i, int opcnt, struct parsed_proto *pp, int *regmask, int magic) { - // arg group is for cases when pushes for - // multiple funcs are going on - struct parsed_op *po_tmp; - int arg_grp = 0; - int ret; - int a; + int a, ret; - ret = collect_call_args_r(po, i, pp, regmask, &arg_grp, - 0, 1, magic, 0, 0); + ret = collect_call_args_r(po, i, pp, regmask, 0, 1, magic, + 0, 0, 0); if (ret < 0) return ret; - if (arg_grp != 0) { - // propagate arg_grp - for (a = 0; a < pp->argc; a++) { - if (pp->arg[a].reg != NULL) - continue; - - po_tmp = pp->arg[a].datap; - while (po_tmp != NULL) { - po_tmp->p_arggrp = arg_grp; - po_tmp = po_tmp->p_argnext >= 0 ? &ops[po_tmp->p_argnext] : NULL; - } - } - } - if (pp->is_unresolved) { pp->argc += ret; pp->argc_stack += ret; @@ -5287,6 +5805,13 @@ static int collect_call_args(struct parsed_op *po, int i, pp->arg[a].type.name = strdup("int"); } + // note: p_argnum, p_arggrp will be propagated in a later pass, + // look for sync_argnum() (p_arggrp is for cases when mixed pushes + // for multiple funcs are going on) + + if (!g_header_mode) + check_fptr_args(i, opcnt, pp); + return ret; } @@ -5337,6 +5862,8 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits, && !g_func_pp->is_userstack && po->operand[0].type == OPT_REG) { + int save_level = 0; + reg = po->operand[0].reg; ferr_assert(po, reg >= 0); @@ -5345,12 +5872,14 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits, if (regmask_now & (1 << reg)) { already_saved = regmask_save_now & (1 << reg); flags_set = OPF_RSAVE | OPF_DONE; + save_level++; } - ret = scan_for_pop(i + 1, opcnt, i + opcnt * 3, reg, 0, 0, 0); + ret = scan_for_pop(i + 1, opcnt, i + opcnt * 3, + reg, 0, 0, save_level, 0); if (ret == 1) { scan_for_pop(i + 1, opcnt, i + opcnt * 4, - reg, 0, 0, flags_set); + reg, 0, 0, save_level, flags_set); } else { ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, 0); @@ -5388,9 +5917,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); @@ -5452,7 +5980,15 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits, *regmask |= regmask_now; // released regs - if (po->flags & OPF_FPOP) { + if (po->flags & OPF_FPOPP) { + if ((regmask_now & mxSTa) == 0) + ferr(po, "float pop on empty stack?\n"); + if (regmask_now & mxST7_2) + po->flags |= OPF_FSHIFT; + if (!(regmask_now & mxST7_2)) + regmask_now &= ~mxST1_0; + } + else if (po->flags & OPF_FPOP) { if ((regmask_now & mxSTa) == 0) ferr(po, "float pop on empty stack?\n"); if (regmask_now & (mxST7_2 | mxST1)) @@ -5630,6 +6166,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) int need_double = 0; int stack_align = 0; int stack_fsz_adj = 0; + int lock_handled = 0; int regmask_save = 0; // used regs saved/restored in this func int regmask_arg; // regs from this function args (fastcall, etc) int regmask_ret; // regs needed on ret @@ -5711,7 +6248,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (pp != NULL) { if (!(po->flags & OPF_ATAIL)) { // since we know the args, try to collect them - ret = collect_call_args_early(i, pp, ®mask, ®mask_ffca); + ret = collect_call_args_early(i, opcnt, pp, + ®mask, ®mask_ffca); if (ret != 0) pp = NULL; } @@ -5756,7 +6294,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) { // since we know the args, collect them - collect_call_args(po, i, pp, ®mask, i + opcnt * 2); + collect_call_args(po, i, opcnt, pp, ®mask, i + opcnt * 2); } // for unresolved, collect after other passes } @@ -5916,7 +6454,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (pp->is_unresolved) { int regmask_stack = 0; - collect_call_args(po, i, pp, ®mask, i + opcnt * 2); + collect_call_args(po, i, opcnt, pp, ®mask, i + opcnt * 2); // this is pretty rough guess: // see ecx and edx were pushed (and not their saved versions) @@ -5924,18 +6462,23 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (pp->arg[arg].reg != NULL && !pp->arg[arg].is_saved) continue; - tmp_op = pp->arg[arg].datap; - if (tmp_op == NULL) + if (pp->arg[arg].push_ref_cnt == 0) ferr(po, "parsed_op missing for arg%d\n", arg); + tmp_op = pp->arg[arg].push_refs[0]; if (tmp_op->operand[0].type == OPT_REG) regmask_stack |= 1 << tmp_op->operand[0].reg; } - if (!((regmask_stack & (1 << xCX)) - && (regmask_stack & (1 << xDX)))) + // quick dumb check for potential reg-args + for (j = i - 1; j >= 0 && ops[j].op == OP_MOV; j--) + if (ops[j].operand[0].type == OPT_REG) + regmask_stack &= ~(1 << ops[j].operand[0].reg); + + if ((regmask_stack & (mxCX|mxDX)) != (mxCX|mxDX) + && ((regmask | regmask_arg) & (mxCX|mxDX))) { if (pp->argc_stack != 0 - || ((regmask | regmask_arg) & ((1 << xCX)|(1 << xDX)))) + || ((regmask | regmask_arg) & (mxCX|mxDX))) { pp_insert_reg_arg(pp, "ecx"); pp->is_fastcall = 1; @@ -5943,7 +6486,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) regmask |= 1 << xCX; } if (pp->argc_stack != 0 - || ((regmask | regmask_arg) & (1 << xDX))) + || ((regmask | regmask_arg) & mxDX)) { pp_insert_reg_arg(pp, "edx"); regmask_init |= 1 << xDX; @@ -5955,6 +6498,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) && !g_nowarn_reguse) + { + // 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: @@ -6010,30 +6591,71 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) need_double = 1; break; + case OP_RDTSC: case OPP_ALLSHL: case OPP_ALLSHR: 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; } + } + + // pass8: sync all push arg numbers + // some calls share args and not all of them + // (there's only partial intersection) + do { + int changed, argnum, arggrp; + + found = 0; + for (i = 0; i < opcnt; i++) + { + po = &ops[i]; + if ((po->flags & (OPF_RMD|OPF_DONE)) || po->op != OP_CALL) + continue; + + pp = po->pp; + arggrp = 0; + do { + changed = 0; + for (arg = argnum = 0; arg < pp->argc; arg++) { + if (pp->arg[arg].reg != NULL) + continue; + if (pp->arg[arg].is_saved) + changed |= sync_argnum(pp, arg, &argnum, &arggrp); + argnum++; + } + found |= changed; + } + while (changed); + + if (argnum > 32) + ferr(po, "too many args or looping in graph\n"); + } + } + while (found); + + // pass9: final adjustments + for (i = 0; i < opcnt; i++) + { + po = &ops[i]; + if (po->flags & (OPF_RMD|OPF_DONE)) + continue; - // this might need it's own pass... if (po->op != OP_FST && po->p_argnum > 0) save_arg_vars[po->p_arggrp] |= 1 << (po->p_argnum - 1); // correct for "full stack" mode late enable - if ((po->flags & (OPF_PPUSH|OPF_FPOP)) && need_float_stack) + if ((po->flags & (OPF_PPUSH|OPF_FPOP|OPF_FPOPP)) + && need_float_stack) po->flags |= OPF_FSHIFT; } @@ -6072,6 +6694,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); @@ -6146,6 +6771,28 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) had_decl = 1; } + if ((g_sct_func_attr & SCTFA_ARGFRAME) && g_func_pp->argc_stack) { + fprintf(fout, " struct { u32 "); + for (i = j = 0; i < g_func_pp->argc; i++) { + if (g_func_pp->arg[i].reg != NULL) + continue; + if (j++ != 0) + fprintf(fout, ", "); + fprintf(fout, "a%d", i + 1); + } + fprintf(fout, "; } af = {\n "); + for (i = j = 0; i < g_func_pp->argc; i++) { + if (g_func_pp->arg[i].reg != NULL) + continue; + if (j++ != 0) + fprintf(fout, ", "); + if (g_func_pp->arg[i].type.is_ptr) + fprintf(fout, "(u32)"); + fprintf(fout, "a%d", i + 1); + } + fprintf(fout, "\n };\n"); + } + if (g_func_pp->is_userstack) { fprintf(fout, " u32 fake_sf[US_SZ_%s / 4];\n", g_func_pp->name); fprintf(fout, " u32 *esp = &fake_sf[sizeof(fake_sf) / 4];\n"); @@ -6182,7 +6829,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++) { @@ -6333,6 +6980,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (po->flags & OPF_RMD) continue; + lock_handled = 0; no_output = 0; #define assert_operand_cnt(n_) \ @@ -6359,7 +7007,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) || (tmp_op && (tmp_op->op == OP_AND || tmp_op->op == OP_OR)) )) { - out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst); + struct parsed_op *po_arith = (void *)((char *)last_arith_dst + - offsetof(struct parsed_op, operand[0])); + ferr_assert(po, &ops[po_arith - ops] == po_arith); + out_src_opr_u32(buf3, sizeof(buf3), po_arith, last_arith_dst); out_test_for_cc(buf1, sizeof(buf1), po, po->pfo, po->pfo_inv, last_arith_dst->lmod, buf3); is_delayed = 1; @@ -6510,9 +7161,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) fprintf(fout, " for (; ecx != 0; ecx--, edi %c= %d)\n", (po->flags & OPF_DF) ? '-' : '+', lmod_bytes(po, po->operand[1].lmod)); - fprintf(fout, " %sedi = eax;", + fprintf(fout, " %sedi = eax;\n", lmod_cast_u_ptr(po, po->operand[1].lmod)); - strcpy(g_comment, "rep stos"); + fprintf(fout, " barrier();"); + strcpy(g_comment, "^ rep stos"); } else { assert_operand_cnt(2); @@ -6534,8 +7186,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) " for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d)\n", l, j, l, j); fprintf(fout, - " %sedi = %sesi;", buf1, buf1); - strcpy(g_comment, "rep movs"); + " %sedi = %sesi;\n", buf1, buf1); + // this can overwrite many variables + fprintf(fout, " barrier();"); + strcpy(g_comment, "^ rep movs"); } else { assert_operand_cnt(2); @@ -6618,6 +7272,16 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) delayed_flag_op = NULL; break; + case OP_RDTSC: + fprintf(fout, " tmp64 = ext_rdtsc();\n"); + fprintf(fout, " edx = tmp64 >> 32;\n"); + fprintf(fout, " eax = tmp64;"); + break; + + case OP_CPUID: + fprintf(fout, " ext_cpuid(&eax, &ebx, &ecx, &edx);"); + break; + // arithmetic w/flags case OP_AND: if (po->operand[1].type == OPT_CONST && !po->operand[1].val) @@ -6921,9 +7585,18 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]); if (po->operand[0].type == OPT_REG) { + ferr_assert(po, !(po->flags & OPF_LOCK)); strcpy(buf2, po->op == OP_INC ? "++" : "--"); fprintf(fout, " %s%s;", buf1, buf2); } + else if (po->flags & OPF_LOCK) { + out_src_opr(buf2, sizeof(buf2), po, &po->operand[0], "", 1); + fprintf(fout, " __sync_fetch_and_%s((%s *)(%s), 1);", + po->op == OP_INC ? "add" : "sub", + lmod_type_u(po, po->operand[0].lmod), buf2); + strcat(g_comment, " lock"); + lock_handled = 1; + } else { strcpy(buf2, po->op == OP_INC ? "+" : "-"); fprintf(fout, " %s %s= 1;", buf1, buf2); @@ -7109,9 +7782,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) - 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); @@ -7207,9 +7881,12 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) } // stack arg - tmp_op = pp->arg[arg].datap; - if (tmp_op == NULL) + if (pp->arg[arg].push_ref_cnt == 0) ferr(po, "parsed_op missing for arg%d\n", arg); + if (pp->arg[arg].push_ref_cnt > 1) + ferr_assert(po, pp->arg[arg].is_saved); + tmp_op = pp->arg[arg].push_refs[0]; + ferr_assert(po, tmp_op != NULL); if (tmp_op->flags & OPF_VAPUSH) { fprintf(fout, "ap"); @@ -7219,15 +7896,39 @@ 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); + arg++; + ferr_assert(po, pp->arg[arg].push_ref_cnt == 1); + tmp_op = pp->arg[arg].push_refs[0]; + 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), @@ -7287,6 +7988,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) break; case OP_RET: + do_tail: if (g_func_pp->is_vararg) fprintf(fout, " va_end(ap);\n"); if (g_func_pp->has_retreg) { @@ -7442,9 +8144,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; @@ -7487,6 +8193,11 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) strcat(g_comment, " fist"); break; + case OP_FABS: + fprintf(fout, " %s = fabs%s(%s);", float_st0, + need_double ? "" : "f", float_st0); + break; + case OP_FADD: case OP_FDIV: case OP_FMUL: @@ -7592,7 +8303,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) fprintf(fout, " f_sw = %s < %s ? 0x0100 : 0;", float_st0, buf1); } - else if (mask == 0x4000) { // C3 -> = + else if (mask == 0x4000 || mask == 0x4400) { // C3 -> = fprintf(fout, " f_sw = %s == %s ? 0x4000 : 0;", float_st0, buf1); } @@ -7611,10 +8322,16 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) else ferr(po, "unhandled sw mask: %x\n", mask); if (po->flags & OPF_FSHIFT) { - if (need_float_stack) - fprintf(fout, " f_stp++;"); - else + if (need_float_stack) { + if (po->flags & OPF_FPOPP) + fprintf(fout, " f_stp += 2;"); + else + fprintf(fout, " f_stp++;"); + } + else { + ferr_assert(po, !(po->flags & OPF_FPOPP)); fprintf(fout, " f_st0 = f_st1;"); + } } break; } @@ -7692,7 +8409,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) fprintf(fout, " f_st0 = f_st1;"); } strcat(g_comment, " ftol"); - break; + goto tail_check; case OPP_CIPOW: if (need_float_stack) { @@ -7705,7 +8422,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) need_double ? "" : "f"); } strcat(g_comment, " CIpow"); - break; + goto tail_check; case OPP_ABORT: fprintf(fout, " do_skip_code_abort();"); @@ -7716,6 +8433,14 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) fprintf(fout, " do_emms();"); break; + tail_check: + if (po->flags & OPF_TAIL) { + fprintf(fout, "\n"); + strcat(g_comment, " tail"); + goto do_tail; + } + break; + default: no_output = 1; ferr(po, "unhandled op type %d, flags %x\n", @@ -7750,6 +8475,9 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (pfomask != 0) ferr(po, "missed flag calc, pfomask=%x\n", pfomask); + if ((po->flags & OPF_LOCK) && !lock_handled) + ferr(po, "unhandled lock\n"); + // see is delayed flag stuff is still valid if (delayed_flag_op != NULL && delayed_flag_op != po) { if (is_any_opr_modified(delayed_flag_op, po, 0)) @@ -7803,10 +8531,14 @@ 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 + unsigned int ptr_taken:1; // pointer taken of this func struct func_proto_dep *dep_func; int dep_func_cnt; const struct parsed_proto *pp; // seed pp, if any @@ -7817,6 +8549,9 @@ 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; + unsigned int ptr_taken:1; // pointer taken, not a call }; static struct func_prototype *hg_fp; @@ -7868,10 +8603,14 @@ static struct func_proto_dep *hg_fp_find_dep(struct func_prototype *fp, return NULL; } -static void hg_fp_add_dep(struct func_prototype *fp, const char *name) +static void hg_fp_add_dep(struct func_prototype *fp, const char *name, + unsigned int ptr_taken) { + struct func_proto_dep * dep; + // is it a dupe? - if (hg_fp_find_dep(fp, name)) + dep = hg_fp_find_dep(fp, name); + if (dep != NULL && dep->ptr_taken == ptr_taken) return; if ((fp->dep_func_cnt & 0xff) == 0) { @@ -7882,6 +8621,7 @@ static void hg_fp_add_dep(struct func_prototype *fp, const char *name) sizeof(fp->dep_func[0]) * 0x100); } fp->dep_func[fp->dep_func_cnt].name = strdup(name); + fp->dep_func[fp->dep_func_cnt].ptr_taken = ptr_taken; fp->dep_func_cnt++; } @@ -7918,7 +8658,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; @@ -7944,7 +8684,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; } @@ -7952,7 +8693,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; @@ -7974,11 +8716,13 @@ static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits, if (po->flags & OPF_DONE) continue; - ret = scan_for_pop(i + 1, opcnt, i + opcnt * 2, reg, 0, 0, 0); + ret = scan_for_pop(i + 1, opcnt, i + opcnt * 2, + reg, 0, 0, 0, 0); if (ret == 1) { regmask_save |= 1 << reg; po->flags |= OPF_RMD; - scan_for_pop(i + 1, opcnt, i + opcnt * 3, reg, 0, 0, OPF_RMD); + scan_for_pop(i + 1, opcnt, i + opcnt * 3, + reg, 0, 0, 0, OPF_RMD); continue; } } @@ -7993,6 +8737,9 @@ static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits, if (g_bp_frame && !(po->flags & OPF_EBP_S)) dep->regmask_live |= 1 << xBP; } + if ((po->flags & OPF_TAIL) && po->pp != NULL + && po->pp->is_stdcall) + fp->is_stdcall = 1; } else if (po->op == OP_RET) { if (po->operand_cnt > 0) { @@ -8004,31 +8751,41 @@ 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; } else { - struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xAX); j = -1; from_caller = 0; - ret = resolve_origin(i, &opr, i + opcnt * 4, &j, &from_caller); + ret = resolve_origin_reg(i, xAX, i + opcnt * 4, &j, &from_caller); } 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; @@ -8046,10 +8803,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; + } } } @@ -8059,9 +8820,12 @@ 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; + const char *tmpname; int regmask_dummy = 0; int regmask_dep; + int regmask_use; int max_bp_offset = 0; int has_ret; int i, j, l; @@ -8090,6 +8854,7 @@ static void gen_hdr(const char *funcn, int opcnt) // pass3: // - remove dead labels // - collect calls + // - collect function ptr refs for (i = 0; i < opcnt; i++) { if (g_labels[i] != NULL && g_label_refs[i].i == -1) { @@ -8103,22 +8868,26 @@ static void gen_hdr(const char *funcn, int opcnt) if (po->op == OP_CALL) { if (po->operand[0].type == OPT_LABEL) - hg_fp_add_dep(fp, opr_name(po, 0)); + hg_fp_add_dep(fp, opr_name(po, 0), 0); else if (po->pp != NULL) - hg_fp_add_dep(fp, po->pp->name); + hg_fp_add_dep(fp, po->pp->name, 0); + } + else if (po->op == OP_MOV && po->operand[1].type == OPT_OFFSET) { + tmpname = opr_name(po, 1); + if (IS_START(tmpname, "p_") || IS_START(tmpname, "sub_")) + hg_fp_add_dep(fp, tmpname, 1); + } + else if (po->op == OP_PUSH && po->operand[0].type == OPT_OFFSET) { + tmpname = opr_name(po, 0); + if (IS_START(tmpname, "p_") || IS_START(tmpname, "sub_")) + hg_fp_add_dep(fp, tmpname, 1); } } // 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; @@ -8141,7 +8910,7 @@ static void gen_hdr(const char *funcn, int opcnt) if (pp != NULL) { if (!(po->flags & OPF_ATAIL)) // since we know the args, try to collect them - if (collect_call_args_early(i, pp, NULL, NULL) != 0) + if (collect_call_args_early(i, opcnt, pp, NULL, NULL) != 0) pp = NULL; } @@ -8186,18 +8955,34 @@ static void gen_hdr(const char *funcn, int opcnt) if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) { // since we know the args, collect them - ret = collect_call_args(po, i, pp, ®mask_dummy, + ret = collect_call_args(po, i, opcnt, 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++) @@ -8212,8 +8997,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++) { @@ -8229,6 +9017,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", @@ -8243,28 +9032,40 @@ 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->ptr_taken) { + dep->proto->ptr_taken = 1; + continue; + } + + 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; } } } @@ -8279,11 +9080,8 @@ static void do_func_refs_from_data(void) strcpy(fp_s.name, hg_refs[i]); fp = bsearch(&fp_s, hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_name); - if (fp == NULL) - continue; - - if (fp->argc_stack != 0 && (fp->regmask_dep & (mxCX | mxDX))) - fp->regmask_dep |= mxCX | mxDX; + if (fp != NULL) + fp->ptr_taken = 1; } } @@ -8333,18 +9131,25 @@ 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")); - if (regmask_dep && (fp->is_stdcall || fp->argc_stack > 0) - && (regmask_dep & ~mxCX) == 0) + if (fp->ptr_taken && regmask_dep + && (regmask_dep & ~(mxCX|mxDX)) == 0) { + if ((regmask_dep & mxDX) || fp->argc_stack > 0) + regmask_dep |= mxCX | mxDX; + } + + fprintf(fout, "%-5s", + fp->pp ? fp->pp->ret_type.name : + fp->has_ret64 ? "__int64" : + fp->has_ret ? "int" : "void"); + if (regmask_dep == mxCX && fp->is_stdcall && fp->argc_stack > 0) { fprintf(fout, "/*__thiscall*/ "); 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) @@ -8435,6 +9240,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); @@ -8858,7 +9669,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++) { @@ -8868,6 +9679,8 @@ int main(int argc, char *argv[]) g_allow_regfunc = 1; else if (IS(argv[arg], "-uc")) g_allow_user_icall = 1; + else if (IS(argv[arg], "-wu")) + g_nowarn_reguse = 1; else if (IS(argv[arg], "-m")) multi_seg = 1; else if (IS(argv[arg], "-hdr")) @@ -8877,13 +9690,14 @@ int main(int argc, char *argv[]) } if (argc < arg + 3) { - printf("usage:\n%s [-v] [-rf] [-m] <.c> <.asm> [rlist]*\n" + printf("usage:\n%s [options] <.c> <.asm> [rlist]*\n" "%s -hdr <.asm> [rlist]*\n" "options:\n" " -hdr - header generation mode\n" " -rf - allow unannotated indirect calls\n" " -uc - allow ind. calls/refs to __usercall\n" " -m - allow multiple .text sections\n" + " -wu - don't warn about bad reg use\n" "[rlist] is a file with function names to skip," " one per line\n", argv[0], argv[0]); @@ -9026,6 +9840,9 @@ int main(int argc, char *argv[]) static const char *attrs[] = { "clear_sf", "clear_regmask", + "rm_regmask", + "nowarn", + "argframe", }; // parse manual attribute-list comment @@ -9049,6 +9866,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; @@ -9135,22 +9955,29 @@ 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 == ';') continue; goto parse_words; // lame } - if (IS_START(p, "; sctproto:")) { - sctproto = strdup(p + 11); - } else if (IS_START(p, "; sctend")) { end = 1; if (!pending_endp) break; } + else if (g_skip_func) + /* ignore remaining attrs */; + else if (IS_START(p, "; sctproto:")) { + sctproto = strdup(p + 11); + } else if (IS_START(p, "; sctskip_start")) { - if (in_func && !g_skip_func) { + if (in_func) { if (!skip_code) { ops[pi].op = OPP_ABORT; ops[pi].asmln = asmln; @@ -9252,6 +10079,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;