X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=tools%2Ftranslate.c;h=a7f629ee4dccea9daee3ec74244d93a334112aba;hb=497a6d6b4c2992fc9cbd2591985d108bc8859f72;hp=1bdc8841a06d59309140dcfa55355484ee592f8d;hpb=acd03176c7934f7b876cfadbeb9e1060d6535b94;p=ia32rtools.git diff --git a/tools/translate.c b/tools/translate.c index 1bdc884..a7f629e 100644 --- a/tools/translate.c +++ b/tools/translate.c @@ -60,6 +60,9 @@ enum op_flags { OPF_DONE = (1 << 19), /* already fully handled by analysis */ OPF_PPUSH = (1 << 20), /* part of complex push-pop graph */ 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 */ }; enum op_op { @@ -116,11 +119,34 @@ enum op_op { OP_LOOP, OP_JCC, OP_SCC, - // x87 - // mmx - OP_EMMS, - // undefined - OP_UD2, + // x87 + OP_FLD, + OP_FILD, + OP_FLDc, + OP_FST, + OP_FADD, + OP_FDIV, + OP_FMUL, + OP_FSUB, + OP_FDIVR, + OP_FSUBR, + OP_FIADD, + OP_FIDIV, + OP_FIMUL, + OP_FISUB, + OP_FIDIVR, + OP_FISUBR, + OP_FCOS, + OP_FPATAN, + OP_FPTAN, + OP_FSIN, + OP_FSQRT, + // mmx + OP_EMMS, + // pseudo-ops for lib calls + OPP_FTOL, + // undefined + OP_UD2, }; enum opr_type { @@ -187,7 +213,8 @@ struct parsed_op { }; // datap: -// OP_CALL - parser proto hint (str) +// on start: function/data type hint (sctproto) +// after analysis: // (OPF_CC) - points to one of (OPF_FLAGS) that affects cc op // OP_PUSH - points to OP_POP in complex push/pop graph // OP_POP - points to OP_PUSH in simple push/pop pair @@ -227,6 +254,21 @@ enum ida_func_attr { IDAFA_FPD = (1 << 5), }; +enum sct_func_attr { + SCTFA_CLEAR_SF = (1 << 0), // clear stack frame + SCTFA_CLEAR_REGS = (1 << 1), // clear registers (mask) +}; + +enum x87_const { + X87_CONST_1 = 1, + X87_CONST_2T, + X87_CONST_2E, + X87_CONST_PI, + X87_CONST_LG2, + X87_CONST_LN2, + X87_CONST_Z, +}; + // note: limited to 32k due to p_argnext #define MAX_OPS 4096 #define MAX_ARG_GRP 2 @@ -239,6 +281,7 @@ static struct label_ref g_label_refs[MAX_OPS]; static const struct parsed_proto *g_func_pp; static struct parsed_data *g_func_pd; static int g_func_pd_cnt; +static int g_func_lmods; static char g_func[256]; static char g_comment[256]; static int g_bp_frame; @@ -246,6 +289,10 @@ static int g_sp_frame; static int g_stack_frame_used; static int g_stack_fsz; static int g_ida_func_attr; +static int g_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_skip_func; static int g_allow_regfunc; static int g_quiet_pp; @@ -269,12 +316,27 @@ const char *regs_r32[] = { "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp", // not r32, but list here for easy parsing and printing "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", + "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)" }; const char *regs_r16[] = { "ax", "bx", "cx", "dx", "si", "di", "bp", "sp" }; const char *regs_r8l[] = { "al", "bl", "cl", "dl" }; const char *regs_r8h[] = { "ah", "bh", "ch", "dh" }; -enum x86_regs { xUNSPEC = -1, xAX, xBX, xCX, xDX, xSI, xDI, xBP, xSP }; +enum x86_regs { + xUNSPEC = -1, + xAX, xBX, xCX, xDX, + xSI, xDI, xBP, xSP, + xMM0, xMM1, xMM2, xMM3, // mmx + xMM4, xMM5, xMM6, xMM7, + xST0, xST1, xST2, xST3, // x87 + xST4, xST5, xST6, xST7, +}; + +#define mxAX (1 << xAX) +#define mxCX (1 << xCX) +#define mxDX (1 << xDX) +#define mxST0 (1 << xST0) +#define mxST1 (1 << xST1) // possible basic comparison types (without inversion) enum parsed_flag_op { @@ -595,7 +657,7 @@ static char *default_cast_to(char *buf, size_t buf_size, { buf[0] = 0; - if (!opr->is_ptr) + if (!opr->is_ptr || strchr(opr->name, '[')) return buf; if (opr->pp == NULL || opr->pp->type.name == NULL || opr->pp->is_fptr) @@ -753,6 +815,9 @@ static int parse_operand(struct parsed_opr *opr, equ_find(NULL, parse_stack_el(opr->name, NULL, 1), &i); if (eq) opr->lmod = eq->lmod; + + // might be unaligned access + g_func_lmods |= 1 << OPLM_BYTE; } return wordc; } @@ -961,9 +1026,40 @@ static const struct { { "setg", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_LE, 1 }, { "setnle", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_LE, 1 }, // x87 + { "fld", OP_FLD, 1, 1, OPF_FPUSH }, + { "fild", OP_FILD, 1, 1, OPF_FPUSH }, + { "fld1", OP_FLDc, 0, 0, OPF_FPUSH }, + { "fldz", OP_FLDc, 0, 0, OPF_FPUSH }, + { "fstp", OP_FST, 1, 1, OPF_FPOP }, + { "fst", OP_FST, 1, 1, 0 }, + { "fadd", OP_FADD, 0, 2, 0 }, + { "faddp", OP_FADD, 0, 2, OPF_FPOP }, + { "fdiv", OP_FDIV, 0, 2, 0 }, + { "fdivp", OP_FDIV, 0, 2, OPF_FPOP }, + { "fmul", OP_FMUL, 0, 2, 0 }, + { "fmulp", OP_FMUL, 0, 2, OPF_FPOP }, + { "fsub", OP_FSUB, 0, 2, 0 }, + { "fsubp", OP_FSUB, 0, 2, OPF_FPOP }, + { "fdivr", OP_FDIVR, 0, 2, 0 }, + { "fdivrp", OP_FDIVR, 0, 2, OPF_FPOP }, + { "fsubr", OP_FSUBR, 0, 2, 0 }, + { "fsubrp", OP_FSUBR, 0, 2, OPF_FPOP }, + { "fiadd", OP_FIADD, 1, 1, 0 }, + { "fidiv", OP_FIDIV, 1, 1, 0 }, + { "fimul", OP_FIMUL, 1, 1, 0 }, + { "fisub", OP_FISUB, 1, 1, 0 }, + { "fidivr", OP_FIDIVR, 1, 1, 0 }, + { "fisubr", OP_FISUBR, 1, 1, 0 }, + { "fcos", OP_FCOS, 0, 0, 0 }, + { "fpatan", OP_FPATAN, 0, 0, OPF_FPOP }, + { "fptan", OP_FPTAN, 0, 0, OPF_FPUSH }, + { "fsin", OP_FSIN, 0, 0, 0 }, + { "fsqrt", OP_FSQRT, 0, 0, 0 }, // mmx - { "emms", OP_EMMS, 0, 0, OPF_DATA }, - { "movq", OP_MOV, 2, 2, OPF_DATA }, + { "emms", OP_EMMS, 0, 0, OPF_DATA }, + { "movq", OP_MOV, 2, 2, OPF_DATA }, + // pseudo-ops for lib calls + { "_ftol", OPP_FTOL }, // must be last { "ud2", OP_UD2 }, }; @@ -1028,6 +1124,9 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) else op->regmask_src |= regmask; op->regmask_src |= regmask_ind; + + if (op->operand[opr].lmod != OPLM_UNSPEC) + g_func_lmods |= 1 << op->operand[opr].lmod; } if (w < wordc) @@ -1087,7 +1186,7 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) op->operand_cnt = 2; setup_reg_opr(&op->operand[0], xAX, OPLM_BYTE, &op->regmask_src); op->regmask_dst = op->regmask_src; - setup_reg_opr(&op->operand[1], xDX, OPLM_DWORD, &op->regmask_src); + setup_reg_opr(&op->operand[1], xBX, OPLM_DWORD, &op->regmask_src); break; case OP_CDQ: @@ -1099,8 +1198,6 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) case OP_LODS: case OP_STOS: case OP_SCAS: - if (op->operand_cnt != 0) - break; if (words[op_w][4] == 'b') lmod = OPLM_BYTE; else if (words[op_w][4] == 'w') @@ -1108,20 +1205,21 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) else if (words[op_w][4] == 'd') lmod = OPLM_DWORD; j = 0; + op->regmask_src = 0; setup_reg_opr(&op->operand[j++], op->op == OP_LODS ? xSI : xDI, - lmod, &op->regmask_src); - if (op->flags & OPF_REP) - setup_reg_opr(&op->operand[j++], xCX, OPLM_DWORD, &op->regmask_src); + OPLM_DWORD, &op->regmask_src); op->regmask_dst = op->regmask_src; - setup_reg_opr(&op->operand[j++], xAX, OPLM_DWORD, + setup_reg_opr(&op->operand[j++], xAX, lmod, op->op == OP_LODS ? &op->regmask_dst : &op->regmask_src); + if (op->flags & OPF_REP) { + setup_reg_opr(&op->operand[j++], xCX, OPLM_DWORD, &op->regmask_src); + op->regmask_dst |= 1 << xCX; + } op->operand_cnt = j; break; case OP_MOVS: case OP_CMPS: - if (op->operand_cnt != 0) - break; if (words[op_w][4] == 'b') lmod = OPLM_BYTE; else if (words[op_w][4] == 'w') @@ -1129,6 +1227,8 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) else if (words[op_w][4] == 'd') lmod = OPLM_DWORD; j = 0; + op->regmask_src = 0; + // note: lmod is not correct, don't have where to place it setup_reg_opr(&op->operand[j++], xDI, lmod, &op->regmask_src); setup_reg_opr(&op->operand[j++], xSI, OPLM_DWORD, &op->regmask_src); if (op->flags & OPF_REP) @@ -1236,6 +1336,68 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) op->regmask_src = 1 << xBP; break; + case OP_FLD: + case OP_FILD: + op->regmask_dst |= mxST0; + break; + + case OP_FLDc: + 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, "z")) + op->operand[0].val = X87_CONST_Z; + else + aerr("TODO\n"); + break; + + case OP_FST: + op->regmask_src |= mxST0; + break; + + case OP_FADD: + case OP_FDIV: + case OP_FMUL: + case OP_FSUB: + case OP_FDIVR: + case OP_FSUBR: + op->regmask_src |= mxST0; + if (op->operand_cnt == 2) + op->regmask_src |= op->regmask_dst; + else if (op->operand_cnt == 1) { + memcpy(&op->operand[1], &op->operand[0], sizeof(op->operand[1])); + op->operand[0].type = OPT_REG; + op->operand[0].lmod = OPLM_QWORD; + op->operand[0].reg = xST0; + op->regmask_dst |= mxST0; + } + else + // IDA doesn't use this + aerr("no operands?\n"); + break; + + case OP_FIADD: + case OP_FIDIV: + case OP_FIMUL: + case OP_FISUB: + case OP_FIDIVR: + case OP_FISUBR: + case OP_FCOS: + case OP_FSIN: + case OP_FSQRT: + op->regmask_src |= mxST0; + op->regmask_dst |= mxST0; + break; + + case OP_FPATAN: + op->regmask_src |= mxST0 | mxST1; + op->regmask_dst |= mxST0; + break; + + case OP_FPTAN: + aerr("TODO\n"); + break; + default: break; } @@ -1420,10 +1582,45 @@ static const char *opr_reg_p(struct parsed_op *po, struct parsed_opr *popr) return regs_r32[popr->reg]; } +static int check_simple_cast(const char *cast, int *bits, int *is_signed) +{ + if (IS_START(cast, "(s8)") || IS_START(cast, "(u8)")) + *bits = 8; + else if (IS_START(cast, "(s16)") || IS_START(cast, "(u16)")) + *bits = 16; + else if (IS_START(cast, "(s32)") || IS_START(cast, "(u32)")) + *bits = 32; + else if (IS_START(cast, "(s64)") || IS_START(cast, "(u64)")) + *bits = 64; + else + return -1; + + *is_signed = cast[1] == 's' ? 1 : 0; + return 0; +} + +static int check_deref_cast(const char *cast, int *bits) +{ + if (IS_START(cast, "*(u8 *)")) + *bits = 8; + else if (IS_START(cast, "*(u16 *)")) + *bits = 16; + else if (IS_START(cast, "*(u32 *)")) + *bits = 32; + else if (IS_START(cast, "*(u64 *)")) + *bits = 64; + else + return -1; + + return 0; +} + // cast1 is the "final" cast static const char *simplify_cast(const char *cast1, const char *cast2) { static char buf[256]; + int bits1, bits2; + int s1, s2; if (cast1[0] == 0) return cast2; @@ -1431,14 +1628,22 @@ static const char *simplify_cast(const char *cast1, const char *cast2) return cast1; if (IS(cast1, cast2)) return cast1; - if (IS(cast1, "(s8)") && IS(cast2, "(u8)")) - return cast1; - if (IS(cast1, "(s16)") && IS(cast2, "(u16)")) - return cast1; - if (IS(cast1, "(u8)") && IS_START(cast2, "*(u8 *)")) - return cast2; - if (IS(cast1, "(u16)") && IS_START(cast2, "*(u16 *)")) - return cast2; + + if (check_simple_cast(cast1, &bits1, &s1) == 0 + && check_simple_cast(cast2, &bits2, &s2) == 0) + { + if (bits1 <= bits2) + return cast1; + } + if (check_simple_cast(cast1, &bits1, &s1) == 0 + && check_deref_cast(cast2, &bits2) == 0) + { + if (bits1 == bits2) { + snprintf(buf, sizeof(buf), "*(%c%d *)", s1 ? 's' : 'u', bits1); + return buf; + } + } + if (strchr(cast1, '*') && IS_START(cast2, "(u32)")) return cast1; @@ -1742,6 +1947,14 @@ static int stack_frame_access(struct parsed_op *po, snprintf(buf, buf_size, "%ssf.d[%d]", prefix, sf_ofs / 4); break; + case OPLM_QWORD: + ferr_assert(po, !(sf_ofs & 7)); + ferr_assert(po, ofs_reg[0] == 0); + // float callers set is_lea + ferr_assert(po, is_lea); + snprintf(buf, buf_size, "%ssf.q[%d]", prefix, sf_ofs / 8); + break; + default: ferr(po, "bp_stack bad lmod: %d\n", popr->lmod); } @@ -1976,6 +2189,52 @@ static char *out_src_opr_u32(char *buf, size_t buf_size, return out_src_opr(buf, buf_size, po, popr, NULL, 0); } +static char *out_src_opr_float(char *buf, size_t buf_size, + struct parsed_op *po, struct parsed_opr *popr) +{ + const char *cast = NULL; + char tmp[256]; + + switch (popr->type) { + case OPT_REG: + if (popr->reg < xST0 || popr->reg > xST7) + ferr(po, "bad reg: %d\n", popr->reg); + + snprintf(buf, buf_size, "f_st%d", popr->reg - xST0); + break; + + case OPT_REGMEM: + case OPT_LABEL: + case OPT_OFFSET: + switch (popr->lmod) { + case OPLM_QWORD: + cast = "double"; + break; + case OPLM_DWORD: + cast = "float"; + break; + default: + ferr(po, "unhandled lmod: %d\n", popr->lmod); + break; + } + out_src_opr(tmp, sizeof(tmp), po, popr, "", 1); + snprintf(buf, buf_size, "*((%s *)%s)", cast, tmp); + break; + + default: + ferr(po, "invalid float type: %d\n", popr->type); + } + + return buf; +} + +static char *out_dst_opr_float(char *buf, size_t buf_size, + struct parsed_op *po, struct parsed_opr *popr) +{ + // same? + return out_src_opr_float(buf, buf_size, po, popr); +} + static void out_test_for_cc(char *buf, size_t buf_size, struct parsed_op *po, enum parsed_flag_op pfo, int is_inv, enum opr_lenmod lmod, const char *expr) @@ -3224,16 +3483,44 @@ static int get_pp_arg_regmask_src(const struct parsed_proto *pp) static int get_pp_arg_regmask_dst(const struct parsed_proto *pp) { + int regmask = 0; + int i, reg; + + if (pp->has_retreg) { + for (i = 0; i < pp->argc; i++) { + if (pp->arg[i].type.is_retreg) { + reg = char_array_i(regs_r32, + ARRAY_SIZE(regs_r32), pp->arg[i].reg); + ferr_assert(ops, reg >= 0); + regmask |= 1 << reg; + } + } + } + if (strstr(pp->ret_type.name, "int64")) - return (1 << xAX) | (1 << xDX); + return regmask | (1 << xAX) | (1 << xDX); + if (IS(pp->ret_type.name, "float") + || IS(pp->ret_type.name, "double")) + { + return regmask | mxST0; + } if (strcasecmp(pp->ret_type.name, "void") == 0) - return 0; + return regmask; - return (1 << xAX); + return regmask | mxAX; } static void resolve_branches_parse_calls(int opcnt) { + static const struct { + const char *name; + enum op_op op; + unsigned int flags; + unsigned int regmask_src; + unsigned int regmask_dst; + } pseudo_ops[] = { + { "__ftol", OPP_FTOL, OPF_FPOP, mxST0, mxAX | mxDX }, + }; const struct parsed_proto *pp_c; struct parsed_proto *pp; struct parsed_data *pd; @@ -3248,13 +3535,45 @@ static void resolve_branches_parse_calls(int opcnt) po->bt_i = -1; po->btj = NULL; + if (po->datap != NULL) { + pp = calloc(1, sizeof(*pp)); + my_assert_not(pp, NULL); + + ret = parse_protostr(po->datap, pp); + if (ret < 0) + ferr(po, "bad protostr supplied: %s\n", (char *)po->datap); + free(po->datap); + po->datap = NULL; + po->pp = pp; + } + if (po->op == OP_CALL) { pp = NULL; - if (po->operand[0].type == OPT_LABEL) { + if (po->pp != NULL) + pp = po->pp; + else if (po->operand[0].type == OPT_LABEL) + { tmpname = opr_name(po, 0); if (IS_START(tmpname, "loc_")) ferr(po, "call to loc_*\n"); + + // convert some calls to pseudo-ops + for (l = 0; l < ARRAY_SIZE(pseudo_ops); l++) { + if (!IS(tmpname, pseudo_ops[l].name)) + continue; + + po->op = pseudo_ops[l].op; + 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 |= po->regmask_dst ? OPF_DATA : 0; + break; + } + if (l < ARRAY_SIZE(pseudo_ops)) + continue; + pp_c = proto_parse(g_fhdr, tmpname, g_header_mode); if (!g_header_mode && pp_c == NULL) ferr(po, "proto_parse failed for call '%s'\n", tmpname); @@ -3264,16 +3583,6 @@ static void resolve_branches_parse_calls(int opcnt) my_assert_not(pp, NULL); } } - else if (po->datap != NULL) { - pp = calloc(1, sizeof(*pp)); - my_assert_not(pp, NULL); - - ret = parse_protostr(po->datap, pp); - if (ret < 0) - ferr(po, "bad protostr supplied: %s\n", (char *)po->datap); - free(po->datap); - po->datap = NULL; - } if (pp != NULL) { if (pp->is_fptr) @@ -3550,36 +3859,11 @@ static void scan_prologue_epilogue(int opcnt) } } -static const struct parsed_proto *resolve_icall(int i, int opcnt, - int *pp_i, int *multi_src) -{ - const struct parsed_proto *pp = NULL; - int search_advice = 0; - - *multi_src = 0; - *pp_i = -1; - - switch (ops[i].operand[0].type) { - case OPT_REGMEM: - case OPT_LABEL: - case OPT_OFFSET: - pp = try_recover_pp(&ops[i], &ops[i].operand[0], &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); - break; - } - - return pp; -} - // find an instruction that changed opr before i op // *op_i must be set to -1 by the caller -// *entry is set to 1 if one source is determined to be the caller +// *is_caller is set to 1 if one source is determined to be g_func arg // returns 1 if found, *op_i is then set to origin +// returns -1 if multiple origins are found static int resolve_origin(int i, const struct parsed_opr *opr, int magic, int *op_i, int *is_caller) { @@ -3758,6 +4042,96 @@ static int try_resolve_const(int i, const struct parsed_opr *opr, return -1; } +static const struct parsed_proto *resolve_icall(int i, int opcnt, + int *pp_i, int *multi_src) +{ + const struct parsed_proto *pp = NULL; + int search_advice = 0; + int offset = -1; + char name[256]; + char s_reg[4]; + int reg, len; + int ret; + + *multi_src = 0; + *pp_i = -1; + + switch (ops[i].operand[0].type) { + case OPT_REGMEM: + // try to resolve struct member calls + ret = sscanf(ops[i].operand[0].name, "%3s+%x%n", + s_reg, &offset, &len); + if (ret == 2 && len == strlen(ops[i].operand[0].name)) + { + reg = char_array_i(regs_r32, ARRAY_SIZE(regs_r32), s_reg); + if (reg >= 0) { + struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, reg); + int j = -1; + ret = resolve_origin(i, &opr, i + opcnt * 19, &j, NULL); + if (ret != 1) + break; + if (ops[j].op == OP_MOV && ops[j].operand[1].type == OPT_REGMEM + && ops[j].operand[0].lmod == OPLM_DWORD + && ops[j].pp == NULL) // no hint + { + // allow one simple dereference (directx) + reg = char_array_i(regs_r32, ARRAY_SIZE(regs_r32), + ops[j].operand[1].name); + if (reg < 0) + break; + struct parsed_opr opr2 = OPR_INIT(OPT_REG, OPLM_DWORD, reg); + int k = -1; + ret = resolve_origin(j, &opr2, j + opcnt * 19, &k, NULL); + if (ret != 1) + break; + j = k; + } + if (ops[j].op != OP_MOV) + break; + if (ops[j].operand[0].lmod != OPLM_DWORD) + break; + if (ops[j].pp != NULL) { + // type hint in asm + pp = ops[j].pp; + } + else if (ops[j].operand[1].type == OPT_REGMEM) { + // allow 'hello[ecx]' - assume array of same type items + ret = sscanf(ops[j].operand[1].name, "%[^[][e%2s]", + name, s_reg); + if (ret != 2) + break; + pp = proto_parse(g_fhdr, name, g_quiet_pp); + } + else if (ops[j].operand[1].type == OPT_LABEL) + pp = proto_parse(g_fhdr, ops[j].operand[1].name, g_quiet_pp); + else + break; + if (pp == NULL) + break; + if (pp->is_func || pp->is_fptr || !pp->type.is_struct) { + pp = NULL; + break; + } + pp = proto_lookup_struct(g_fhdr, pp->type.name, offset); + } + break; + } + // fallthrough + case OPT_LABEL: + case OPT_OFFSET: + pp = try_recover_pp(&ops[i], &ops[i].operand[0], &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); + break; + } + + return pp; +} + static struct parsed_proto *process_call_early(int i, int opcnt, int *adj_i) { @@ -4289,6 +4663,7 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits, int *regmask_init, int regmask_arg) { struct parsed_op *po; + unsigned int mask; int already_saved; int regmask_new; int regmask_op; @@ -4393,6 +4768,15 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits, if (po->flags & OPF_NOREGS) continue; + if (po->flags & OPF_FPUSH) { + if (regmask_now & mxST1) + ferr(po, "TODO: FPUSH on active ST1\n"); + if (regmask_now & mxST0) + po->flags |= OPF_FSHIFT; + mask = mxST0 | mxST1; + regmask_now = (regmask_now & ~mask) | ((regmask_now & mxST0) << 1); + } + // if incomplete register is used, clear it on init to avoid // later use of uninitialized upper part in some situations if ((po->flags & OPF_DATA) && po->operand[0].type == OPT_REG @@ -4412,21 +4796,6 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits, if (g_bp_frame && !(po->flags & OPF_EBP_S)) regmask_new &= ~(1 << xBP); - if (po->op == OP_CALL) { - // allow fastcall calls from anywhere, calee may be also sitting - // in some fastcall table even when it's not using reg args - if (regmask_new & po->regmask_src & (1 << xCX)) { - *regmask_init |= (1 << xCX); - regmask_now |= (1 << xCX); - regmask_new &= ~(1 << xCX); - } - if (regmask_new & po->regmask_src & (1 << xDX)) { - *regmask_init |= (1 << xDX); - regmask_now |= (1 << xDX); - regmask_new &= ~(1 << xDX); - } - } - if (regmask_new != 0) fnote(po, "uninitialized reg mask: %x\n", regmask_new); @@ -4443,8 +4812,24 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits, regmask_now |= regmask_op; *regmask |= regmask_now; - if (po->flags & OPF_TAIL) - return; + // released regs + if (po->flags & OPF_FPOP) { + mask = mxST0 | mxST1; + if (!(regmask_now & mask)) + ferr(po, "float pop on empty stack?\n"); + if (regmask_now & mxST1) + po->flags |= OPF_FSHIFT; + regmask_now = (regmask_now & ~mask) | ((regmask_now & mxST1) >> 1); + } + + if (po->flags & OPF_TAIL) { + if (regmask_now & (mxST0 | mxST1)) + ferr(po, "float regs on tail: %x\n", regmask_now); + + // there is support for "conditional tailcall", sort of + if (!(po->flags & OPF_CC)) + return; + } } } @@ -4568,11 +4953,13 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) unsigned int uval; int save_arg_vars[MAX_ARG_GRP] = { 0, }; unsigned char cbits[MAX_OPS / 8]; + const char *float_type; int cond_vars = 0; int need_tmp_var = 0; int need_tmp64 = 0; int had_decl = 0; int label_pending = 0; + int need_double = 0; int regmask_save = 0; // regs saved/restored in this func int regmask_arg; // regs from this function args (fastcall, etc) int regmask_ret; // regs needed on ret @@ -4590,6 +4977,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) g_bp_frame = g_sp_frame = g_stack_fsz = 0; g_stack_frame_used = 0; + if (g_sct_func_attr & SCTFA_CLEAR_REGS) + regmask_init = g_regmask_init; g_func_pp = proto_parse(fhdr, funcn, 0); if (g_func_pp == NULL) @@ -4598,17 +4987,6 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) regmask_arg = get_pp_arg_regmask_src(g_func_pp); regmask_ret = get_pp_arg_regmask_dst(g_func_pp); - if (g_func_pp->has_retreg) { - for (arg = 0; arg < g_func_pp->argc; arg++) { - if (g_func_pp->arg[arg].type.is_retreg) { - reg = char_array_i(regs_r32, - ARRAY_SIZE(regs_r32), g_func_pp->arg[arg].reg); - ferr_assert(ops, reg >= 0); - regmask_ret |= 1 << reg; - } - } - } - // pass1: // - resolve all branches // - parse calls with labels @@ -4698,6 +5076,9 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) po->regmask_src |= get_pp_arg_regmask_src(pp); po->regmask_dst |= get_pp_arg_regmask_dst(pp); + if (po->regmask_dst & mxST0) + po->flags |= OPF_FPUSH; + if (strstr(pp->ret_type.name, "int64")) need_tmp64 = 1; @@ -4724,7 +5105,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) // - find POPs for PUSHes, rm both // - scan for all used registers memset(cbits, 0, sizeof(cbits)); - reg_use_pass(0, opcnt, cbits, 0, ®mask, + reg_use_pass(0, opcnt, cbits, regmask_init, ®mask, 0, ®mask_save, ®mask_init, regmask_arg); // pass7: @@ -4888,11 +5269,22 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) } else if (po->op == OP_CLD) po->flags |= OPF_RMD | OPF_DONE; + else if (po->op == 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); + if (j == -1) + po->flags |= OPF_32BIT; + } + else if (po->op == OP_FLD && po->operand[0].lmod == OPLM_QWORD) + need_double = 1; if (po->op == OP_RCL || po->op == OP_RCR || po->op == OP_XCHG) need_tmp_var = 1; } + float_type = need_double ? "double" : "float"; + // output starts here // define userstack size @@ -4976,8 +5368,14 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) // declare stack frame, va_arg if (g_stack_fsz) { - fprintf(fout, " union { u32 d[%d]; u16 w[%d]; u8 b[%d]; } sf;\n", - (g_stack_fsz + 3) / 4, (g_stack_fsz + 1) / 2, g_stack_fsz); + fprintf(fout, " union { u32 d[%d];", (g_stack_fsz + 3) / 4); + if (g_func_lmods & (1 << OPLM_WORD)) + fprintf(fout, " u16 w[%d];", (g_stack_fsz + 1) / 2); + if (g_func_lmods & (1 << OPLM_BYTE)) + fprintf(fout, " u8 b[%d];", g_stack_fsz); + if (g_func_lmods & (1 << OPLM_QWORD)) + fprintf(fout, " double q[%d];", (g_stack_fsz + 7) / 8); + fprintf(fout, " } sf;\n"); had_decl = 1; } @@ -5030,6 +5428,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) } } } + // ... mmx if (regmask_now & 0xff00) { for (reg = 8; reg < 16; reg++) { if (regmask_now & (1 << reg)) { @@ -5041,6 +5440,18 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) } } } + // ... x87 + if (regmask_now & 0xff0000) { + for (reg = 16; reg < 24; reg++) { + if (regmask_now & (1 << reg)) { + fprintf(fout, " %s f_st%d", float_type, reg - 16); + if (regmask_init & (1 << reg)) + fprintf(fout, " = 0"); + fprintf(fout, ";\n"); + had_decl = 1; + } + } + } if (regmask_save) { for (reg = 0; reg < 8; reg++) { @@ -5095,6 +5506,24 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (had_decl) fprintf(fout, "\n"); + // do stack clear, if needed + if (g_sct_func_attr & SCTFA_CLEAR_SF) { + fprintf(fout, " "); + if (g_stack_clear_len != 0) { + if (g_stack_clear_len <= 4) { + for (i = 0; i < g_stack_clear_len; i++) + fprintf(fout, "sf.d[%d] = ", g_stack_clear_start + i); + fprintf(fout, "0;\n"); + } + else { + fprintf(fout, "memset(&sf[%d], 0, %d);\n", + g_stack_clear_start, g_stack_clear_len * 4); + } + } + else + fprintf(fout, "memset(&sf, 0, sizeof(sf));\n"); + } + if (g_func_pp->is_vararg) { if (g_func_pp->argc_stack == 0) ferr(ops, "vararg func without stack args?\n"); @@ -5298,10 +5727,11 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) } else { assert_operand_cnt(2); - fprintf(fout, " eax = %sesi; esi %c= %d;", - lmod_cast_u_ptr(po, po->operand[0].lmod), + fprintf(fout, " %s = %sesi; esi %c= %d;", + out_dst_opr(buf1, sizeof(buf1), po, &po->operand[1]), + lmod_cast_u_ptr(po, po->operand[1].lmod), (po->flags & OPF_DF) ? '-' : '+', - lmod_bytes(po, po->operand[0].lmod)); + lmod_bytes(po, po->operand[1].lmod)); strcpy(g_comment, "lods"); } break; @@ -5311,17 +5741,17 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) assert_operand_cnt(3); fprintf(fout, " for (; ecx != 0; ecx--, edi %c= %d)\n", (po->flags & OPF_DF) ? '-' : '+', - lmod_bytes(po, po->operand[0].lmod)); + lmod_bytes(po, po->operand[1].lmod)); fprintf(fout, " %sedi = eax;", - lmod_cast_u_ptr(po, po->operand[0].lmod)); + lmod_cast_u_ptr(po, po->operand[1].lmod)); strcpy(g_comment, "rep stos"); } else { assert_operand_cnt(2); fprintf(fout, " %sedi = eax; edi %c= %d;", - lmod_cast_u_ptr(po, po->operand[0].lmod), + lmod_cast_u_ptr(po, po->operand[1].lmod), (po->flags & OPF_DF) ? '-' : '+', - lmod_bytes(po, po->operand[0].lmod)); + lmod_bytes(po, po->operand[1].lmod)); strcpy(g_comment, "stos"); } break; @@ -5388,7 +5818,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) case OP_SCAS: // only does ZF (for now) // repe ~ repeat while ZF=1 - j = lmod_bytes(po, po->operand[0].lmod); + j = lmod_bytes(po, po->operand[1].lmod); l = (po->flags & OPF_DF) ? '-' : '+'; if (po->flags & OPF_REP) { assert_operand_cnt(3); @@ -5396,8 +5826,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) " for (; ecx != 0; ecx--) {\n"); fprintf(fout, " cond_z = (%seax == %sedi); edi %c= %d;\n", - lmod_cast_u(po, po->operand[0].lmod), - lmod_cast_u_ptr(po, po->operand[0].lmod), l, j); + lmod_cast_u(po, po->operand[1].lmod), + lmod_cast_u_ptr(po, po->operand[1].lmod), l, j); fprintf(fout, " if (cond_z %s 0) break;\n", (po->flags & OPF_REPZ) ? "==" : "!="); @@ -5409,8 +5839,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) else { assert_operand_cnt(2); fprintf(fout, " cond_z = (%seax == %sedi); edi %c= %d;", - lmod_cast_u(po, po->operand[0].lmod), - lmod_cast_u_ptr(po, po->operand[0].lmod), l, j); + lmod_cast_u(po, po->operand[1].lmod), + lmod_cast_u_ptr(po, po->operand[1].lmod), l, j); strcpy(g_comment, "scas"); } pfomask &= ~(1 << PFO_Z); @@ -5614,7 +6044,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) fprintf(fout, " cond_c = tmp64 >> 32;\n"); fprintf(fout, " %s = (u32)tmp64;", out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0])); - strcat(g_comment, "add64"); + strcat(g_comment, " add64"); } else { fprintf(fout, " cond_c = ((u32)%s + %s) >> %d;\n", @@ -5680,7 +6110,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) output_std_flags(fout, po, &pfomask, buf1); last_arith_dst = &po->operand[0]; delayed_flag_op = NULL; - strcat(g_comment, "bsf"); + strcat(g_comment, " bsf"); break; case OP_DEC: @@ -5807,7 +6237,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) fprintf(fout, " LOWORD(eax) = %s / %s%s;", buf2, cast, buf1); } - strcat(g_comment, "div16"); + strcat(g_comment, " div16"); break; default: ferr(po, "unhandled div lmod %d\n", po->operand[0].lmod); @@ -5847,13 +6277,13 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) case OP_JECXZ: fprintf(fout, " if (ecx == 0)\n"); fprintf(fout, " goto %s;", po->operand[0].name); - strcat(g_comment, "jecxz"); + strcat(g_comment, " jecxz"); break; case OP_LOOP: - fprintf(fout, " if (--ecx == 0)\n"); + fprintf(fout, " if (--ecx != 0)\n"); fprintf(fout, " goto %s;", po->operand[0].name); - strcat(g_comment, "loop"); + strcat(g_comment, " loop"); break; case OP_JMP: @@ -5906,17 +6336,22 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) } else if (!IS(pp->ret_type.name, "void")) { if (po->flags & OPF_TAIL) { - if (regmask_ret & (1 << xAX)) { + if (regmask_ret & mxAX) { fprintf(fout, "return "); if (g_func_pp->ret_type.is_ptr != pp->ret_type.is_ptr) fprintf(fout, "(%s)", g_func_pp->ret_type.name); } + else if (regmask_ret & mxST0) + ferr(po, "float tailcall\n"); } - else if (po->regmask_dst & (1 << xAX)) { + else if (po->regmask_dst & mxAX) { fprintf(fout, "eax = "); if (pp->ret_type.is_ptr) fprintf(fout, "(u32)"); } + else if (po->regmask_dst & mxST0) { + fprintf(fout, "f_st0 = "); + } } if (pp->name[0] == 0) @@ -6134,9 +6569,143 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) no_output = 1; break; + // x87 + case OP_FLD: + if (po->flags & OPF_FSHIFT) + fprintf(fout, " f_st1 = f_st0;\n"); + if (po->operand[0].type == OPT_REG + && po->operand[0].reg == xST0) + { + strcat(g_comment, " fld st"); + break; + } + fprintf(fout, " f_st0 = %s;", + out_src_opr_float(buf1, sizeof(buf1), po, &po->operand[0])); + strcat(g_comment, " fld"); + break; + + case OP_FILD: + if (po->flags & OPF_FSHIFT) + fprintf(fout, " f_st1 = f_st0;\n"); + fprintf(fout, " f_st0 = (%s)%s;", float_type, + out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], + lmod_cast(po, po->operand[0].lmod, 1), 0)); + strcat(g_comment, " fild"); + break; + + case OP_FLDc: + if (po->flags & OPF_FSHIFT) + fprintf(fout, " f_st1 = f_st0;\n"); + fprintf(fout, " f_st0 = "); + switch (po->operand[0].val) { + case X87_CONST_1: fprintf(fout, "1.0;"); break; + case X87_CONST_Z: fprintf(fout, "0.0;"); break; + default: ferr(po, "TODO\n"); break; + } + break; + + case OP_FST: + if ((po->flags & OPF_FPOP) && po->operand[0].type == OPT_REG + && po->operand[0].reg == xST0) + { + no_output = 1; + break; + } + fprintf(fout, " %s = f_st0;", + out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0])); + if (po->flags & OPF_FSHIFT) + fprintf(fout, "\n f_st0 = f_st1;"); + strcat(g_comment, " fst"); + break; + + case OP_FADD: + case OP_FDIV: + case OP_FMUL: + case OP_FSUB: + switch (po->op) { + case OP_FADD: j = '+'; break; + case OP_FDIV: j = '/'; break; + case OP_FMUL: j = '*'; break; + case OP_FSUB: j = '-'; break; + default: j = 'x'; break; + } + if (po->flags & OPF_FSHIFT) { + fprintf(fout, " f_st0 = f_st1 %c f_st0;", j); + } + else { + fprintf(fout, " %s %c= %s;", + out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0]), + j, + out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1])); + } + break; + + case OP_FDIVR: + case OP_FSUBR: + if (po->flags & OPF_FSHIFT) + snprintf(buf1, sizeof(buf1), "f_st0"); + else + out_dst_opr_float(buf1, sizeof(buf1), po, &po->operand[0]); + fprintf(fout, " %s = %s %c %s;", buf1, + out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1]), + po->op == OP_FDIVR ? '/' : '-', + out_src_opr_float(buf3, sizeof(buf3), po, &po->operand[0])); + break; + + case OP_FIADD: + case OP_FIDIV: + case OP_FIMUL: + case OP_FISUB: + switch (po->op) { + case OP_FIADD: j = '+'; break; + case OP_FIDIV: j = '/'; break; + case OP_FIMUL: j = '*'; break; + case OP_FISUB: j = '-'; break; + default: j = 'x'; break; + } + fprintf(fout, " f_st0 %c= (%s)%s;", j, float_type, + out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], + lmod_cast(po, po->operand[0].lmod, 1), 0)); + break; + + case OP_FIDIVR: + case OP_FISUBR: + fprintf(fout, " f_st0 = %s %c f_st0;", + out_src_opr_float(buf2, sizeof(buf2), po, &po->operand[1]), + po->op == OP_FIDIVR ? '/' : '-'); + break; + + case OP_FCOS: + fprintf(fout, " f_st0 = cos%s(f_st0);", + need_double ? "" : "f"); + break; + + case OP_FPATAN: + fprintf(fout, " f_st0 = atan%s(f_st1 / f_st0);", + need_double ? "" : "f"); + break; + + case OP_FSIN: + fprintf(fout, " f_st0 = sin%s(f_st0);", + need_double ? "" : "f"); + break; + + case OP_FSQRT: + fprintf(fout, " f_st0 = sqrt%s(f_st0);", + need_double ? "" : "f"); + break; + + case OPP_FTOL: + ferr_assert(po, po->flags & OPF_32BIT); + fprintf(fout, " eax = (s32)f_st0;"); + if (po->flags & OPF_FSHIFT) + fprintf(fout, "\n f_st0 = f_st1;"); + strcat(g_comment, " ftol"); + break; + // mmx case OP_EMMS: - strcpy(g_comment, "(emms)"); + strcpy(g_comment, " (emms)"); break; default: @@ -6253,10 +6822,13 @@ static struct scanned_var { } *hg_vars; static int hg_var_cnt; +static char **hg_refs; +static int hg_ref_cnt; + static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, int count); -struct func_prototype *hg_fp_add(const char *funcn) +static struct func_prototype *hg_fp_add(const char *funcn) { struct func_prototype *fp; @@ -6318,6 +6890,19 @@ static int hg_fp_cmp_id(const void *p1_, const void *p2_) } #endif +static void hg_ref_add(const char *name) +{ + if ((hg_ref_cnt & 0xff) == 0) { + hg_refs = realloc(hg_refs, sizeof(hg_refs[0]) * (hg_ref_cnt + 0x100)); + my_assert_not(hg_refs, NULL); + memset(hg_refs + hg_ref_cnt, 0, sizeof(hg_refs[0]) * 0x100); + } + + hg_refs[hg_ref_cnt] = strdup(name); + my_assert_not(hg_refs[hg_ref_cnt], NULL); + hg_ref_cnt++; +} + // recursive register dep pass // - track saved regs (part 2) // - try to figure out arg-regs @@ -6672,6 +7257,24 @@ static void hg_fp_resolve_deps(struct func_prototype *fp) } } +// make all thiscall/edx arg functions referenced from .data fastcall +static void do_func_refs_from_data(void) +{ + struct func_prototype *fp, fp_s; + int i; + + for (i = 0; i < hg_ref_cnt; i++) { + 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; + } +} + static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, int count) { @@ -6679,7 +7282,7 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, char *p, namebuf[NAMELEN]; const char *name; int regmask_dep; - int argc_stack; + int argc_normal; int j, arg; for (; count > 0; count--, fp++) { @@ -6717,18 +7320,25 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, } regmask_dep = fp->regmask_dep; - argc_stack = fp->argc_stack; + 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 || argc_stack == 0) - && (regmask_dep & ~((1 << xCX) | (1 << xDX))) == 0) + if (regmask_dep && (fp->is_stdcall || fp->argc_stack > 0) + && (regmask_dep & ~mxCX) == 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) { fprintf(fout, " __fastcall "); - if (!(regmask_dep & (1 << xDX)) && argc_stack == 0) - argc_stack = 1; + if (!(regmask_dep & (1 << xDX)) && fp->argc_stack == 0) + argc_normal = 1; else - argc_stack += 2; + argc_normal += 2; regmask_dep = 0; } else if (regmask_dep && !fp->is_stdcall) { @@ -6758,7 +7368,7 @@ static void output_hdr_fp(FILE *fout, const struct func_prototype *fp, } } - for (j = 0; j < argc_stack; j++) { + for (j = 0; j < argc_normal; j++) { arg++; if (arg != 1) fprintf(fout, ", "); @@ -6810,6 +7420,9 @@ static void output_hdr(FILE *fout) for (i = 0; i < hg_fp_cnt; i++) hg_fp_resolve_deps(&hg_fp[i]); + // adjust functions referenced from data segment + do_func_refs_from_data(); + // note: messes up .proto ptr, don't use //qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_id); @@ -6853,7 +7466,7 @@ static char *next_word_s(char *w, size_t wsize, char *s) s = sskip(s); i = 0; - if (*s == '\'') { + if (*s == '\'' && s[1] != '\r' && s[1] != '\n') { w[0] = s[0]; for (i = 1; i < wsize - 1; i++) { if (s[i] == 0) { @@ -6879,11 +7492,83 @@ static char *next_word_s(char *w, size_t wsize, char *s) return s + i; } -static void scan_variables(FILE *fasm) +static int cmpstringp(const void *p1, const void *p2) +{ + return strcmp(*(char * const *)p1, *(char * const *)p2); +} + +static int is_xref_needed(char *p, char **rlist, int rlist_len) +{ + char *p2; + + p = sskip(p); + if (strstr(p, "...")) + // unable to determine, assume needed + return 1; + + if (*p == '.') // .text, .data, ... + // ref from other data or non-function -> no + return 0; + + p2 = strpbrk(p, "+:\r\n\x18"); + if (p2 != NULL) + *p2 = 0; + if (bsearch(&p, rlist, rlist_len, sizeof(rlist[0]), cmpstringp)) + // referenced from removed code + return 0; + + return 1; +} + +static int ida_xrefs_show_need(FILE *fasm, char *p, + char **rlist, int rlist_len) +{ + int found_need = 0; + char line[256]; + long pos; + + p = strrchr(p, ';'); + if (p != NULL && *p == ';' && IS_START(p + 2, "DATA XREF: ")) { + p += 13; + if (is_xref_needed(p, rlist, rlist_len)) + return 1; + } + + pos = ftell(fasm); + while (1) + { + if (!my_fgets(line, sizeof(line), fasm)) + break; + // non-first line is always indented + if (!my_isblank(line[0])) + break; + + // should be no content, just comment + p = sskip(line); + if (*p != ';') + break; + + p = strrchr(p, ';'); + p += 2; + // it's printed once, but no harm to check again + if (IS_START(p, "DATA XREF: ")) + p += 11; + + if (is_xref_needed(p, rlist, rlist_len)) { + found_need = 1; + break; + } + } + fseek(fasm, pos, SEEK_SET); + return found_need; +} + +static void scan_variables(FILE *fasm, char **rlist, int rlist_len) { struct scanned_var *var; char line[256] = { 0, }; - char words[3][256]; + char words[4][256]; + int no_identifier; char *p = NULL; int wordc; int l; @@ -6922,8 +7607,7 @@ static void scan_variables(FILE *fasm) asmln++; p = line; - if (my_isblank(*p)) - continue; + no_identifier = my_isblank(*p); p = sskip(p); if (*p == 0 || *p == ';') @@ -6943,11 +7627,21 @@ static void scan_variables(FILE *fasm) if (wordc < 2) continue; + if (no_identifier) { + if (wordc >= 3 && IS(words[0], "dd") && IS(words[1], "offset")) + hg_ref_add(words[2]); + continue; + } + if (IS_START(words[0], "__IMPORT_DESCRIPTOR_")) { // when this starts, we don't need anything from this section break; } + // check refs comment(s) + if (!ida_xrefs_show_need(fasm, p, rlist, rlist_len)) + continue; + if ((hg_var_cnt & 0xff) == 0) { hg_vars = realloc(hg_vars, sizeof(hg_vars[0]) * (hg_var_cnt + 0x100)); @@ -6975,8 +7669,11 @@ static void scan_variables(FILE *fasm) continue; } - if (IS(words[1], "dd")) + if (IS(words[1], "dd")) { var->lmod = OPLM_DWORD; + if (wordc >= 4 && IS(words[2], "offset")) + hg_ref_add(words[3]); + } else if (IS(words[1], "dw")) var->lmod = OPLM_WORD; else if (IS(words[1], "db")) { @@ -7046,11 +7743,6 @@ static int cmp_chunks(const void *p1, const void *p2) return strcmp(c1->name, c2->name); } -static int cmpstringp(const void *p1, const void *p2) -{ - return strcmp(*(char * const *)p1, *(char * const *)p2); -} - static void scan_ahead(FILE *fasm) { char words[2][256]; @@ -7248,7 +7940,7 @@ int main(int argc, char *argv[]) } if (g_header_mode) - scan_variables(fasm); + scan_variables(fasm, rlist, rlist_len); while (my_fgets(line, sizeof(line), fasm)) { @@ -7302,6 +7994,46 @@ int main(int argc, char *argv[]) } } } + else if (p[2] == 's' && IS_START(p, "; sctattr:")) + { + static const char *attrs[] = { + "clear_sf", + "clear_regmask", + }; + + // parse manual attribute-list comment + g_sct_func_attr = 0; + p = sskip(p + 10); + + for (; *p != 0; p = sskip(p)) { + for (i = 0; i < ARRAY_SIZE(attrs); i++) { + if (!strncmp(p, attrs[i], strlen(attrs[i]))) { + g_sct_func_attr |= 1 << i; + p += strlen(attrs[i]); + break; + } + } + if (*p == '=') { + j = ret = 0; + if (i == 0) + // clear_sf=start,len (in dwords) + ret = sscanf(p, "=%d,%d%n", &g_stack_clear_start, + &g_stack_clear_len, &j); + else if (i == 1) + // clear_regmask= + ret = sscanf(p, "=%x%n", &g_regmask_init, &j) + 1; + if (ret < 2) { + anote("unparsed attr value: %s\n", p); + break; + } + p += j; + } + else if (i == ARRAY_SIZE(attrs)) { + anote("unparsed sct attr: %s\n", p); + break; + } + } + } else if (p[2] == 'S' && IS_START(p, "; START OF FUNCTION CHUNK FOR ")) { p += 30; @@ -7470,6 +8202,10 @@ do_pending_endp: pending_endp = 0; in_func = 0; g_ida_func_attr = 0; + g_sct_func_attr = 0; + g_stack_clear_start = 0; + g_stack_clear_len = 0; + g_regmask_init = 0; skip_warned = 0; g_skip_func = 0; g_func[0] = 0; @@ -7491,6 +8227,7 @@ do_pending_endp: pd->d = NULL; } g_func_pd_cnt = 0; + g_func_lmods = 0; pd = NULL; if (end) @@ -7635,11 +8372,8 @@ do_pending_endp: parse_op(&ops[pi], words, wordc); - if (sctproto != NULL) { - if (ops[pi].op == OP_CALL || ops[pi].op == OP_JMP) - ops[pi].datap = sctproto; - sctproto = NULL; - } + ops[pi].datap = sctproto; + sctproto = NULL; pi++; }