X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=tools%2Ftranslate.c;h=cca300f1759fde203fe33e48d798e9402aece34d;hb=7a3c55553ae875cfc821e347867e08266a03f13c;hp=8ebda8289749b881ef54db6744b280bde8f4e001;hpb=236c23eb625fc3d412b3105ab6e67723d661348d;p=ia32rtools.git diff --git a/tools/translate.c b/tools/translate.c index 8ebda82..cca300f 100644 --- a/tools/translate.c +++ b/tools/translate.c @@ -96,6 +96,8 @@ enum op_op { OP_MOVS, OP_CMPS, OP_SCAS, + OP_RDTSC, + OP_CPUID, OP_STD, OP_CLD, OP_RET, @@ -278,9 +280,13 @@ 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 { @@ -332,6 +338,7 @@ 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; @@ -349,6 +356,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 @@ -370,6 +380,7 @@ enum x86_regs { }; #define mxAX (1 << xAX) +#define mxBX (1 << xBX) #define mxCX (1 << xCX) #define mxDX (1 << xDX) #define mxSP (1 << xSP) @@ -591,24 +602,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; @@ -945,7 +959,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) @@ -989,6 +1003,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 }, @@ -1316,6 +1332,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 @@ -1413,7 +1439,7 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) case OP_CALL: // needed because of OPF_DATA - op->regmask_src = op->regmask_dst; + op->regmask_src |= op->regmask_dst; // trashed regs must be explicitly detected later op->regmask_dst = 0; break; @@ -1917,6 +1943,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; @@ -1947,7 +1974,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) @@ -1966,17 +1993,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: @@ -1987,18 +2017,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: @@ -2010,19 +2040,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; @@ -2030,8 +2060,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: @@ -2678,8 +2708,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; @@ -2693,18 +2724,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) { @@ -2712,7 +2753,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 } @@ -2722,7 +2763,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 } @@ -2744,6 +2785,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; } @@ -3516,6 +3564,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)) { @@ -3908,8 +3961,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); @@ -4137,9 +4192,72 @@ static void eliminate_seh_calls(int 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 pushes 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)) @@ -4155,7 +4273,11 @@ static int scan_prologue(int i, int opcnt, int *ecx_push, int *esp_sub) for (; i < opcnt; i++) { if (i > 0 && g_labels[i] != NULL) break; - if (ops[i].op == OP_PUSH || (ops[i].flags & (OPF_JMP|OPF_TAIL))) + 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) @@ -4166,6 +4288,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) { @@ -4180,8 +4315,8 @@ static int scan_prologue(int i, int opcnt, int *ecx_push, int *esp_sub) ops[j].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; i = j + 1; *esp_sub = 1; + break; } - break; } } @@ -4192,7 +4327,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) { @@ -4316,6 +4452,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) { @@ -4373,13 +4510,21 @@ static void scan_prologue_epilogue(int opcnt, int *stack_align) for (; j >= 0; j--) { if (ops[j].op != OP_MOV) break; - if (ops[j].operand[0].type != OPT_REGMEM) - break; - if (strstr(ops[j].operand[0].name, "arg_") == NULL) - 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 && j >= 0; l++) { if (ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ecx")) @@ -4413,28 +4558,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++; @@ -4443,6 +4597,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 @@ -4641,6 +4799,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 @@ -5512,6 +5679,14 @@ static int collect_call_args(struct parsed_op *po, int i, if (ret < 0) return ret; + if (pp->is_unresolved) { + pp->argc += ret; + pp->argc_stack += ret; + for (a = 0; a < pp->argc; a++) + if (pp->arg[a].type.name == NULL) + pp->arg[a].type.name = strdup("int"); + } + if (arg_grp != 0) { // propagate arg_grp for (a = 0; a < pp->argc; a++) { @@ -5526,14 +5701,6 @@ static int collect_call_args(struct parsed_op *po, int i, } } - if (pp->is_unresolved) { - pp->argc += ret; - pp->argc_stack += ret; - for (a = 0; a < pp->argc; a++) - if (pp->arg[a].type.name == NULL) - pp->arg[a].type.name = strdup("int"); - } - return ret; } @@ -5584,6 +5751,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); @@ -5592,12 +5761,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); @@ -5635,9 +5806,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); @@ -5885,6 +6055,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 @@ -6186,11 +6357,16 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) 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; @@ -6198,7 +6374,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; @@ -6210,6 +6386,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: @@ -6265,25 +6479,31 @@ 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: 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); @@ -6405,6 +6625,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"); @@ -6592,6 +6834,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_) \ @@ -6769,9 +7012,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); @@ -6793,8 +7037,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); @@ -6877,6 +7123,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) @@ -7180,9 +7436,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); @@ -8042,6 +8307,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)) @@ -8095,11 +8363,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 @@ -8110,6 +8381,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; @@ -8161,10 +8435,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) { @@ -8175,6 +8453,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++; } @@ -8211,7 +8490,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; @@ -8237,7 +8516,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; } @@ -8245,7 +8525,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; @@ -8267,11 +8548,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; } } @@ -8297,9 +8580,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; @@ -8318,11 +8599,23 @@ static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits, } 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; @@ -8340,10 +8633,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; + } } } @@ -8353,9 +8650,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; @@ -8384,6 +8684,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) { @@ -8397,9 +8698,19 @@ 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); } } @@ -8477,15 +8788,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++) @@ -8520,6 +8847,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", @@ -8534,28 +8862,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; } } } @@ -8570,11 +8910,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; } } @@ -8624,9 +8961,17 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, regmask_dep = fp->regmask_dep; argc_normal = fp->argc_stack; + 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_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) { @@ -9166,6 +9511,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")) @@ -9175,13 +9522,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]); @@ -9325,6 +9673,8 @@ int main(int argc, char *argv[]) "clear_sf", "clear_regmask", "rm_regmask", + "nowarn", + "argframe", }; // parse manual attribute-list comment @@ -9448,16 +9798,18 @@ parse_words: 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;