X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=tools%2Ftranslate.c;h=eca8490aceb446ab804064aa10d7dfca80fb8359;hb=226e8df1fab849b7f0d55989b13a002dadaf6b9c;hp=dadf7a915098cf64c3d2c4acebca801228046f30;hpb=d4a985bd88473515445c6f9f0ae51be9895b9d60;p=ia32rtools.git diff --git a/tools/translate.c b/tools/translate.c index dadf7a9..eca8490 100644 --- a/tools/translate.c +++ b/tools/translate.c @@ -208,7 +208,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 @@ -248,6 +249,11 @@ 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, @@ -278,6 +284,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; @@ -318,6 +328,7 @@ enum x86_regs { }; #define mxAX (1 << xAX) +#define mxCX (1 << xCX) #define mxDX (1 << xDX) #define mxST0 (1 << xST0) #define mxST1 (1 << xST1) @@ -641,7 +652,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) @@ -3450,17 +3461,31 @@ 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 mxST0; + return regmask | mxST0; } if (strcasecmp(pp->ret_type.name, "void") == 0) - return 0; + return regmask; - return mxAX; + return regmask | mxAX; } static void resolve_branches_parse_calls(int opcnt) @@ -3488,10 +3513,25 @@ 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"); @@ -3521,16 +3561,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) @@ -3807,36 +3837,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) { @@ -4015,6 +4020,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) { @@ -4679,21 +4774,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); @@ -4723,7 +4803,10 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits, if (po->flags & OPF_TAIL) { if (regmask_now & (mxST0 | mxST1)) ferr(po, "float regs on tail: %x\n", regmask_now); - return; + + // there is support for "conditional tailcall", sort of + if (!(po->flags & OPF_CC)) + return; } } } @@ -4870,6 +4953,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) @@ -4878,17 +4963,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 @@ -5007,7 +5081,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: @@ -5404,6 +5478,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"); @@ -7108,7 +7200,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++) { @@ -7146,18 +7238,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) { @@ -7187,7 +7286,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, ", "); @@ -7308,7 +7407,78 @@ 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 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, }; @@ -7377,6 +7547,10 @@ static void scan_variables(FILE *fasm) break; } + // check refs comment(s) + if (!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)); @@ -7475,11 +7649,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]; @@ -7677,7 +7846,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)) { @@ -7731,6 +7900,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, "=%d%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; @@ -7899,6 +8108,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; @@ -8065,11 +8278,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++; }