X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=tools%2Ftranslate.c;h=510478f85bf54293d387cc2c38f45330932e7730;hb=77d7983adfe3ff5944d26b3af274a15e8af05556;hp=7da3b5412ed836b7c97f09ab879042ae8da8c27a;hpb=9bbecbfbce7530f47af567605611f091f2bd84ad;p=ia32rtools.git diff --git a/tools/translate.c b/tools/translate.c index 7da3b54..510478f 100644 --- a/tools/translate.c +++ b/tools/translate.c @@ -18,6 +18,7 @@ #define _GNU_SOURCE #include #include +#include #include #include #include @@ -140,6 +141,7 @@ enum op_op { OP_FLDc, OP_FST, OP_FIST, + OP_FABS, OP_FADD, OP_FDIV, OP_FMUL, @@ -222,10 +224,10 @@ struct parsed_op { unsigned char pfo; unsigned char pfo_inv; unsigned char operand_cnt; - unsigned char p_argnum; // arg push: altered before call arg # + unsigned char p_argnum; // arg push: call's saved arg # unsigned char p_arggrp; // arg push: arg group # for above unsigned char p_argpass;// arg push: arg of host func - short p_argnext;// arg push: same arg pushed elsewhere or -1 + short pad; int regmask_src; // all referensed regs int regmask_dst; int pfomask; // flagop: parsed_flag_op that can't be delayed @@ -308,7 +310,6 @@ enum segment { SEG_GS, }; -// note: limited to 32k due to p_argnext #define MAX_OPS 4096 #define MAX_ARG_GRP 2 @@ -338,6 +339,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; @@ -1111,6 +1113,7 @@ static const struct { { "fstp", OP_FST, 1, 1, OPF_FPOP }, { "fist", OP_FIST, 1, 1, OPF_FINT }, { "fistp", OP_FIST, 1, 1, OPF_FPOP|OPF_FINT }, + { "fabs", OP_FABS, 0, 0, 0 }, { "fadd", OP_FADD, 0, 2, 0 }, { "faddp", OP_FADD, 0, 2, OPF_FPOP }, { "fdiv", OP_FDIV, 0, 2, 0 }, @@ -1505,6 +1508,7 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) case OP_FISUB: case OP_FIDIVR: case OP_FISUBR: + case OP_FABS: case OP_FCHS: case OP_FCOS: case OP_FSIN: @@ -1934,6 +1938,9 @@ static int parse_stack_esp_offset(struct parsed_op *po, return 0; } +// returns g_func_pp arg number if arg is accessed +// -1 otherwise (stack vars, va_list) +// note: 'popr' must be from 'po', not some other op static int stack_frame_access(struct parsed_op *po, struct parsed_opr *popr, char *buf, size_t buf_size, const char *name, const char *cast, int is_src, int is_lea) @@ -1964,13 +1971,15 @@ static int stack_frame_access(struct parsed_op *po, arg_i = (offset - stack_ra - 4) / 4; if (arg_i < 0 || arg_i >= g_func_pp->argc_stack) { - if (g_func_pp->is_vararg - && arg_i == g_func_pp->argc_stack && is_lea) - { - // should be va_list - if (cast[0] == 0) - cast = "(u32)"; - snprintf(buf, buf_size, "%sap", cast); + if (g_func_pp->is_vararg && arg_i == g_func_pp->argc_stack) { + if (is_lea) { + // should be va_list + if (cast[0] == 0) + cast = "(u32)"; + snprintf(buf, buf_size, "%sap", cast); + } + else + snprintf(buf, buf_size, "%sva_arg(ap, u32)", cast); return -1; } ferr(po, "offset 0x%x (%s,%d) doesn't map to any arg\n", @@ -2707,8 +2716,9 @@ static const char *op_to_c(struct parsed_op *po) // note: this skips over calls and rm'd stuff assuming they're handled // so it's intended to use at one of final passes +// exception: doesn't skip OPF_RSAVE stuff static int scan_for_pop(int i, int opcnt, int magic, int reg, - int depth, int seen_noreturn, int flags_set) + int depth, int seen_noreturn, int save_level, int flags_set) { struct parsed_op *po; int relevant; @@ -2722,18 +2732,28 @@ static int scan_for_pop(int i, int opcnt, int magic, int reg, po->cc_scratch = magic; if (po->flags & OPF_TAIL) { - if (po->op == OP_CALL) { - if (po->pp != NULL && po->pp->is_noreturn) - seen_noreturn = 1; - else + if (po->op == OP_CALL && po->pp != NULL && po->pp->is_noreturn) { + // msvc sometimes generates stack cleanup code after + // noreturn, set a flag and continue + seen_noreturn = 1; + + // ... but stop if there is another path to next insn - + // if msvc skipped something stack tracking may mess up + if (i + 1 < opcnt && g_labels[i + 1] != NULL) goto out; } else goto out; } - if (po->flags & (OPF_RMD|OPF_DONE|OPF_FARG)) + if (po->flags & OPF_FARG) continue; + if (po->flags & (OPF_RMD|OPF_DONE)) { + if (!(po->flags & OPF_RSAVE)) + continue; + // reprocess, there might be another push in some "parallel" + // path that took a pop what we should also take + } if ((po->flags & OPF_JMP) && po->op != OP_CALL) { if (po->btj != NULL) { @@ -2741,7 +2761,7 @@ static int scan_for_pop(int i, int opcnt, int magic, int reg, for (j = 0; j < po->btj->count; j++) { check_i(po, po->btj->d[j].bt_i); ret |= scan_for_pop(po->btj->d[j].bt_i, opcnt, magic, reg, - depth, seen_noreturn, flags_set); + depth, seen_noreturn, save_level, flags_set); if (ret < 0) return ret; // dead end } @@ -2751,7 +2771,7 @@ static int scan_for_pop(int i, int opcnt, int magic, int reg, check_i(po, po->bt_i); if (po->flags & OPF_CJMP) { ret |= scan_for_pop(po->bt_i, opcnt, magic, reg, - depth, seen_noreturn, flags_set); + depth, seen_noreturn, save_level, flags_set); if (ret < 0) return ret; // dead end } @@ -2773,6 +2793,13 @@ static int scan_for_pop(int i, int opcnt, int magic, int reg, } else if (po->op == OP_POP) { if (relevant && depth == 0) { + if (flags_set == 0 && save_level > 0) { + ret = scan_for_pop(i + 1, opcnt, magic, reg, + depth, seen_noreturn, save_level - 1, flags_set); + if (ret != 1) + // no pop for other levels, current one must be false + return -1; + } po->flags |= flags_set; return 1; } @@ -3611,8 +3638,8 @@ static const struct parsed_proto *try_recover_pp( } static void scan_for_call_type(int i, const struct parsed_opr *opr, - int magic, const struct parsed_proto **pp_found, int *pp_i, - int *multi) + int magic, int is_call_op, const struct parsed_proto **pp_found, + int *pp_i, int *multi) { const struct parsed_proto *pp = NULL; struct parsed_op *po; @@ -3625,7 +3652,8 @@ static void scan_for_call_type(int i, const struct parsed_opr *opr, lr = &g_label_refs[i]; for (; lr != NULL; lr = lr->next) { check_i(&ops[i], lr->i); - scan_for_call_type(lr->i, opr, magic, pp_found, pp_i, multi); + scan_for_call_type(lr->i, opr, magic, is_call_op, + pp_found, pp_i, multi); } if (i > 0 && LAST_OP(i - 1)) return; @@ -3670,29 +3698,30 @@ static void scan_for_call_type(int i, const struct parsed_opr *opr, if (i == g_func_pp->argc) return; pp = g_func_pp->arg[i].pp; - if (pp == NULL) - ferr(po, "icall: arg%d (%s) is not a fptr?\n", - i + 1, g_func_pp->arg[i].reg); + if (pp == NULL) { + if (is_call_op) + ferr(po, "icall: arg%d (%s) is not a fptr?\n", + i + 1, g_func_pp->arg[i].reg); + return; + } check_func_pp(po, pp, "icall reg-arg"); } else - pp = try_recover_pp(po, opr, 1, NULL); + pp = try_recover_pp(po, opr, is_call_op, NULL); if (*pp_found != NULL && pp != NULL && *pp_found != pp) { - if (!IS((*pp_found)->ret_type.name, pp->ret_type.name) - || (*pp_found)->is_stdcall != pp->is_stdcall - //|| (*pp_found)->is_fptr != pp->is_fptr - || (*pp_found)->argc != pp->argc - || (*pp_found)->argc_reg != pp->argc_reg - || (*pp_found)->argc_stack != pp->argc_stack) - { + if (pp_cmp_func(*pp_found, pp)) { + if (pp_i != NULL && *pp_i != -1) + fnote(&ops[*pp_i], "(other ref)\n"); ferr(po, "icall: parsed_proto mismatch\n"); } - *multi = 1; + if (multi != NULL) + *multi = 1; } if (pp != NULL) { *pp_found = pp; - *pp_i = po - ops; + if (pp_i != NULL) + *pp_i = po - ops; } } @@ -3896,7 +3925,8 @@ static void resolve_branches_parse_calls(int opcnt) po->operand_cnt = 0; po->regmask_src = pseudo_ops[l].regmask_src; po->regmask_dst = pseudo_ops[l].regmask_dst; - po->flags = pseudo_ops[l].flags; + po->flags &= OPF_TAIL; + po->flags |= pseudo_ops[l].flags; po->flags |= po->regmask_dst ? OPF_DATA : 0; break; } @@ -3957,7 +3987,8 @@ static void resolve_branches_parse_calls(int opcnt) if (po->bt_i != -1 || (po->flags & OPF_RMD)) continue; - if (po->operand[0].type == OPT_LABEL) + if (po->operand[0].type == OPT_LABEL + || po->operand[0].type == OPT_REG) // assume tail call goto tailcall; @@ -4231,30 +4262,86 @@ static void check_simple_sequence(int opcnt, int *fsz) } // unlike pushes after sub esp, - // IDA treats pushed like this as part of var area + // IDA treats pushes like this as part of var area *fsz += seq_len * 4; } +static int scan_prologue_ecx(int i, int opcnt, int flags_set, + int limit, int *ecx_push_out) +{ + const struct parsed_proto *pp; + int ecx_push = 0, other_push = 0; + int ret; + + while (limit > 0 && ops[i].op == OP_PUSH + && IS(opr_name(&ops[i], 0), "ecx")) + { + ops[i].flags |= flags_set; + ecx_push++; + i++; + limit--; + } + + ret = i; + if (ecx_push == 0 || flags_set != 0) + goto out; + + // check if some of the pushes aren't really call args + for (; i < opcnt; i++) { + if (i > 0 && g_labels[i] != NULL) + break; + if (ops[i].flags & (OPF_JMP|OPF_TAIL)) + break; + if (ops[i].op == OP_PUSH) + other_push++; + } + + if (ops[i].op != OP_CALL) + goto out; + + pp = ops[i].pp; + if (pp == NULL && ops[i].operand[0].type == OPT_LABEL) + pp = proto_parse(g_fhdr, opr_name(&ops[i], 0), 1); + if (pp == NULL) + goto out; + + ferr_assert(&ops[i], ecx_push + other_push >= pp->argc_stack); + if (other_push < pp->argc_stack) + ecx_push -= pp->argc_stack - other_push; + +out: + if (ecx_push_out != NULL) + *ecx_push_out = ecx_push; + return ret; +} + static int scan_prologue(int i, int opcnt, int *ecx_push, int *esp_sub) { const char *name; int j, len, ret; + int ecx_tmp = 0; for (; i < opcnt; i++) if (!(ops[i].flags & OPF_DONE)) break; - while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) { - ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; - g_stack_fsz += 4; - (*ecx_push)++; - i++; + ret = scan_prologue_ecx(i, opcnt, 0, 4, &ecx_tmp); + if (ecx_tmp > 0) { + scan_prologue_ecx(i, opcnt, OPF_RMD | OPF_DONE | OPF_NOREGS, + ecx_tmp, NULL); + g_stack_fsz += 4 * ecx_tmp; + *ecx_push += ecx_tmp; + i = ret; } for (; i < opcnt; i++) { if (i > 0 && g_labels[i] != NULL) break; - if (ops[i].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) @@ -4292,8 +4379,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; } } @@ -4432,39 +4519,6 @@ static void scan_prologue_epilogue(int opcnt, int *stack_align) check_simple_sequence(opcnt, &push_fsz); i = scan_prologue(0, opcnt, &ecx_push, &esp_sub); - if (ecx_push && !esp_sub) { - // could actually be args for a call.. - for (; i < opcnt; i++) - if (ops[i].op != OP_PUSH) - break; - - if (ops[i].op == OP_CALL && ops[i].operand[0].type == OPT_LABEL) { - const struct parsed_proto *pp; - pp = proto_parse(g_fhdr, opr_name(&ops[i], 0), 1); - j = pp ? pp->argc_stack : 0; - while (i > 0 && j > 0) { - i--; - if (ops[i].op == OP_PUSH) { - ops[i].flags &= ~(OPF_RMD | OPF_DONE | OPF_NOREGS); - j--; - } - } - if (j != 0) - ferr(&ops[i], "unhandled prologue\n"); - - // recheck - i = ecx_push = 0; - g_stack_fsz = g_seh_size; - while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) { - if (!(ops[i].flags & OPF_RMD)) - break; - g_stack_fsz += 4; - ecx_push++; - i++; - } - } - } - found = 0; if (ecx_push || esp_sub) { @@ -4487,10 +4541,12 @@ 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; } } @@ -4628,6 +4684,17 @@ static int resolve_origin(int i, const struct parsed_opr *opr, } } +static int resolve_origin_reg(int i, int reg, int magic, int *op_i, + int *is_caller) +{ + struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, reg); + + *op_i = -1; + if (is_caller != NULL) + *is_caller = 0; + return resolve_origin(i, &opr, magic, op_i, is_caller); +} + // find an instruction that previously referenced opr // if multiple results are found - fail // *op_i must be set to -1 by the caller @@ -4901,9 +4968,8 @@ static int resolve_used_bits(int i, int opcnt, int reg, } static const struct parsed_proto *resolve_deref(int i, int magic, - struct parsed_opr *opr, int level) + const struct parsed_opr *opr, int level) { - struct parsed_opr opr_s = OPR_INIT(OPT_REG, OPLM_DWORD, 0); const struct parsed_proto *pp = NULL; int from_caller = 0; char s_reg[4]; @@ -4925,8 +4991,7 @@ static const struct parsed_proto *resolve_deref(int i, int magic, if (reg < 0) return NULL; - opr_s.reg = reg; - ret = resolve_origin(i, &opr_s, i + magic, &j, NULL); + ret = resolve_origin_reg(i, reg, i + magic, &j, NULL); if (ret != 1) return NULL; @@ -4941,8 +5006,7 @@ static const struct parsed_proto *resolve_deref(int i, int magic, ops[j].operand[1].name); if (reg < 0) return NULL; - opr_s.reg = reg; - ret = resolve_origin(j, &opr_s, j + magic, &k, NULL); + ret = resolve_origin_reg(j, reg, j + magic, &k, NULL); if (ret != 1) return NULL; j = k; @@ -4992,32 +5056,34 @@ static const struct parsed_proto *resolve_deref(int i, int magic, return proto_lookup_struct(g_fhdr, pp->type.name, offset); } -static const struct parsed_proto *resolve_icall(int i, int opcnt, +static const struct parsed_proto *resolve_func_ptr(int i, int opcnt, + int is_call_op, const struct parsed_opr *opr, int *pp_i, int *multi_src) { const struct parsed_proto *pp = NULL; int search_advice = 0; - *multi_src = 0; - *pp_i = -1; + if (multi_src != NULL) + *multi_src = 0; + if (pp_i != NULL) + *pp_i = -1; - switch (ops[i].operand[0].type) { + switch (opr->type) { case OPT_REGMEM: // try to resolve struct member calls - pp = resolve_deref(i, i + opcnt * 19, &ops[i].operand[0], 0); + pp = resolve_deref(i, i + opcnt * 19, opr, 0); if (pp != NULL) break; // fallthrough case OPT_LABEL: case OPT_OFFSET: - pp = try_recover_pp(&ops[i], &ops[i].operand[0], - 1, &search_advice); + pp = try_recover_pp(&ops[i], opr, is_call_op, &search_advice); if (!search_advice) break; // fallthrough default: - scan_for_call_type(i, &ops[i].operand[0], i + opcnt * 9, &pp, - pp_i, multi_src); + scan_for_call_type(i, opr, i + opcnt * 9, is_call_op, + &pp, pp_i, multi_src); break; } @@ -5078,7 +5144,8 @@ static struct parsed_proto *process_call(int i, int opcnt) if (pp == NULL) { // indirect call - pp_c = resolve_icall(i, opcnt, &call_i, &multipath); + pp_c = resolve_func_ptr(i, opcnt, 1, &ops[i].operand[0], + &call_i, &multipath); if (pp_c != NULL) { if (!pp_c->is_func && !pp_c->is_fptr) ferr(po, "call to non-func: %s\n", pp_c->name); @@ -5186,13 +5253,77 @@ out: return pp; } +static void check_fptr_args(int i, int opcnt, struct parsed_proto *pp) +{ + struct parsed_opr s_opr = OPR_INIT(OPT_REG, OPLM_DWORD, 0); + const struct parsed_proto *pp_arg, *pp_cmp; + const struct parsed_op *po_a; + const char *s_reg; + int pp_cmp_i; + int arg, reg; + int bad = 0; + int j; + + for (arg = 0; arg < pp->argc; arg++) { + pp_cmp = NULL; + pp_cmp_i = -1; + + pp_arg = pp->arg[arg].pp; + if (pp_arg == NULL || !pp_arg->is_func) + continue; + + s_reg = pp->arg[arg].reg; + if (s_reg != NULL) { + reg = char_array_i(regs_r32, ARRAY_SIZE(regs_r32), s_reg); + ferr_assert(&ops[i], reg >= 0); + s_opr.reg = reg; + scan_for_call_type(i, &s_opr, i + arg + opcnt * 28, 0, + &pp_cmp, &pp_cmp_i, NULL); + if (pp_cmp != NULL && !pp_compatible_func(pp_arg, pp_cmp)) { + bad = 1; + if (pp_cmp_i >= 0) + fnote(&ops[pp_cmp_i], "(referenced here)\n"); + } + } + else { + for (j = 0; j < pp->arg[arg].push_ref_cnt; j++) { + po_a = pp->arg[arg].push_refs[j]; + if (po_a == NULL || po_a->op != OP_PUSH) + continue; + pp_cmp = resolve_func_ptr(po_a - ops, opcnt, 0, + &po_a->operand[0], &pp_cmp_i, NULL); + if (pp_cmp != NULL && !pp_compatible_func(pp_arg, pp_cmp)) { + bad = 1; + if (pp_cmp_i < 0) + pp_cmp_i = po_a - ops; + if (pp_cmp_i >= 0) + fnote(&ops[pp_cmp_i], "(referenced here)\n"); + } + } + } + + if (bad) + ferr(&ops[i], "incompatible fptr arg %d\n", arg + 1); + } +} + +static void pp_add_push_ref(struct parsed_proto *pp, + int arg, struct parsed_op *po) +{ + pp->arg[arg].push_refs = realloc(pp->arg[arg].push_refs, + (pp->arg[arg].push_ref_cnt + 1) + * sizeof(pp->arg[arg].push_refs[0])); + ferr_assert(po, pp->arg[arg].push_refs != NULL); + pp->arg[arg].push_refs[pp->arg[arg].push_ref_cnt++] = po; +} + static void mark_float_arg(struct parsed_op *po, struct parsed_proto *pp, int arg, int *regmask_ffca) { - po->p_argnext = -1; + ferr_assert(po, pp->arg[arg].push_ref_cnt == 0); + pp_add_push_ref(pp, arg, po); + po->p_argnum = arg + 1; - ferr_assert(po, pp->arg[arg].datap == NULL); - pp->arg[arg].datap = po; po->flags |= OPF_DONE | OPF_FARGNR | OPF_FARG; if (regmask_ffca != NULL) *regmask_ffca |= 1 << arg; @@ -5262,9 +5393,9 @@ static int collect_call_args_no_push(int i, struct parsed_proto *pp, for (arg = base_arg; arg < pp->argc; arg++) { ferr_assert(&ops[i], pp->arg[arg].reg == NULL); - po = pp->arg[arg].datap; - if (po == NULL) - ferr(&ops[i], "arg %d/%d not found\n", arg, pp->argc); + if (pp->arg[arg].push_ref_cnt != 1) + ferr(&ops[i], "arg %d/%d not found or bad\n", arg, pp->argc); + po = pp->arg[arg].push_refs[0]; if (po->operand[0].lmod == OPLM_QWORD) arg++; } @@ -5272,8 +5403,8 @@ static int collect_call_args_no_push(int i, struct parsed_proto *pp, return 0; } -static int collect_call_args_early(int i, struct parsed_proto *pp, - int *regmask, int *regmask_ffca) +static int collect_call_args_early(int i, int opcnt, + struct parsed_proto *pp, int *regmask, int *regmask_ffca) { struct parsed_op *po; int arg, ret; @@ -5344,8 +5475,7 @@ static int collect_call_args_early(int i, struct parsed_proto *pp, if (ops[j].op == OP_PUSH) { - ops[j].p_argnext = -1; - ferr_assert(&ops[j], pp->arg[arg].datap == NULL); + int ref_handled = 0; k = check_for_stp(j + 1, i); if (k != -1) { @@ -5356,15 +5486,18 @@ static int collect_call_args_early(int i, struct parsed_proto *pp, if (!pp->arg[arg].type.is_float) ferr(&ops[i], "arg %d should be float\n", arg + 1); mark_float_arg(&ops[k], pp, arg, regmask_ffca); + ref_handled = 1; } } - if (pp->arg[arg].datap == NULL) { - pp->arg[arg].datap = &ops[j]; - if (regmask != NULL && ops[j].operand[0].type == OPT_REG) - *regmask |= 1 << ops[j].operand[0].reg; + if (!ref_handled) { + ferr_assert(&ops[j], pp->arg[arg].push_ref_cnt == 0); + pp_add_push_ref(pp, arg, &ops[j]); } + if (regmask != NULL && ops[j].operand[0].type == OPT_REG) + *regmask |= 1 << ops[j].operand[0].reg; + ops[j].flags |= OPF_RMD | OPF_DONE | OPF_FARGNR | OPF_FARG; ops[j].flags &= ~OPF_RSAVE; @@ -5375,41 +5508,56 @@ static int collect_call_args_early(int i, struct parsed_proto *pp, } } + if (!g_header_mode) + check_fptr_args(i, opcnt, pp); + return 0; } -static int sync_argnum(struct parsed_op *po, int argnum) +// ensure all s_a* numbers match for a given func arg in all branches +// returns 1 if any changes were made, 0 if not +static int sync_argnum(struct parsed_proto *pp, int arg, + int *argnum, int *arggrp) { struct parsed_op *po_tmp; + int changed = 0; + int i; // see if other branches don't have higher argnum - for (po_tmp = po; po_tmp != NULL; ) { - if (argnum < po_tmp->p_argnum) - argnum = po_tmp->p_argnum; - // note: p_argnext is active on current collect_call_args only - po_tmp = po_tmp->p_argnext >= 0 ? &ops[po_tmp->p_argnext] : NULL; + for (i = 0; i < pp->arg[arg].push_ref_cnt; i++) { + po_tmp = pp->arg[arg].push_refs[i]; + if (*argnum < po_tmp->p_argnum) + *argnum = po_tmp->p_argnum; + if (*arggrp < po_tmp->p_arggrp) + *arggrp = po_tmp->p_arggrp; } // make all argnums consistent - for (po_tmp = po; po_tmp != NULL; ) { - if (po_tmp->p_argnum != 0) - po_tmp->p_argnum = argnum; - po_tmp = po_tmp->p_argnext >= 0 ? &ops[po_tmp->p_argnext] : NULL; + for (i = 0; i < pp->arg[arg].push_ref_cnt; i++) { + po_tmp = pp->arg[arg].push_refs[i]; + if (po_tmp->p_argnum == 0) + continue; + if (po_tmp->p_argnum != *argnum || po_tmp->p_arggrp != *arggrp) { + po_tmp->p_argnum = *argnum; + po_tmp->p_arggrp = *arggrp; + changed = 1; + } } - return argnum; + return changed; } static int collect_call_args_r(struct parsed_op *po, int i, - struct parsed_proto *pp, int *regmask, int *arg_grp, - int arg, int argnum, int magic, int need_op_saving, int may_reuse) + struct parsed_proto *pp, int *regmask, + int arg, int argnum, int magic, + int skip, int need_op_saving, int may_reuse) { struct parsed_proto *pp_tmp; - struct parsed_op *po_tmp; struct label_ref *lr; int need_to_save_current; int arg_grp_current = 0; int save_args_seen = 0; + int dummy = 0; int ret = 0; int reg; char buf[32]; @@ -5446,8 +5594,8 @@ static int collect_call_args_r(struct parsed_op *po, int i, check_i(&ops[j], lr->i); if ((ops[lr->i].flags & (OPF_JMP|OPF_CJMP)) != OPF_JMP) may_reuse = 1; - ret = collect_call_args_r(po, lr->i, pp, regmask, arg_grp, - arg, argnum, magic, need_op_saving, may_reuse); + ret = collect_call_args_r(po, lr->i, pp, regmask, + arg, argnum, magic, skip, need_op_saving, may_reuse); if (ret < 0) return ret; } @@ -5461,8 +5609,8 @@ static int collect_call_args_r(struct parsed_op *po, int i, continue; } need_op_saving = 1; - ret = collect_call_args_r(po, lr->i, pp, regmask, arg_grp, - arg, argnum, magic, need_op_saving, may_reuse); + ret = collect_call_args_r(po, lr->i, pp, regmask, + arg, argnum, magic, skip, need_op_saving, may_reuse); if (ret < 0) return ret; } @@ -5480,6 +5628,8 @@ static int collect_call_args_r(struct parsed_op *po, int i, if (may_reuse && pp_tmp->argc_stack > 0) ferr(po, "arg collect %d/%d hit '%s' with %d stack args\n", arg, pp->argc, opr_name(&ops[j], 0), pp_tmp->argc_stack); + if (!pp_tmp->is_unresolved) + skip = pp_tmp->argc_stack; } // esp adjust of 0 means we collected it before else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP @@ -5508,19 +5658,19 @@ static int collect_call_args_r(struct parsed_op *po, int i, may_reuse = 1; } + else if (ops[j].op == OP_PUSH && skip > 0) { + // XXX: might want to rm OPF_FARGNR and only use this + skip--; + } else if (ops[j].op == OP_PUSH && !(ops[j].flags & (OPF_FARGNR|OPF_DONE))) { if (pp->is_unresolved && (ops[j].flags & OPF_RMD)) break; - ops[j].p_argnext = -1; - po_tmp = pp->arg[arg].datap; - if (po_tmp != NULL) - ops[j].p_argnext = po_tmp - ops; - pp->arg[arg].datap = &ops[j]; + pp_add_push_ref(pp, arg, &ops[j]); - argnum = sync_argnum(&ops[j], argnum); + sync_argnum(pp, arg, &argnum, &dummy); need_to_save_current = 0; reg = -1; @@ -5602,6 +5752,7 @@ static int collect_call_args_r(struct parsed_op *po, int i, if (pp->arg[arg].is_saved) { ops[j].flags &= ~OPF_RMD; ops[j].p_argnum = argnum; + ops[j].p_arggrp = arg_grp_current; } // tracking reg usage @@ -5633,41 +5784,19 @@ static int collect_call_args_r(struct parsed_op *po, int i, return -1; } - if (arg_grp_current > *arg_grp) - *arg_grp = arg_grp_current; - return arg; } -static int collect_call_args(struct parsed_op *po, int i, +static int collect_call_args(struct parsed_op *po, int i, int opcnt, struct parsed_proto *pp, int *regmask, int magic) { - // arg group is for cases when pushes for - // multiple funcs are going on - struct parsed_op *po_tmp; - int arg_grp = 0; - int ret; - int a; + int a, ret; - ret = collect_call_args_r(po, i, pp, regmask, &arg_grp, - 0, 1, magic, 0, 0); + ret = collect_call_args_r(po, i, pp, regmask, 0, 1, magic, + 0, 0, 0); if (ret < 0) return ret; - if (arg_grp != 0) { - // propagate arg_grp - for (a = 0; a < pp->argc; a++) { - if (pp->arg[a].reg != NULL) - continue; - - po_tmp = pp->arg[a].datap; - while (po_tmp != NULL) { - po_tmp->p_arggrp = arg_grp; - po_tmp = po_tmp->p_argnext >= 0 ? &ops[po_tmp->p_argnext] : NULL; - } - } - } - if (pp->is_unresolved) { pp->argc += ret; pp->argc_stack += ret; @@ -5676,6 +5805,13 @@ static int collect_call_args(struct parsed_op *po, int i, pp->arg[a].type.name = strdup("int"); } + // note: p_argnum, p_arggrp will be propagated in a later pass, + // look for sync_argnum() (p_arggrp is for cases when mixed pushes + // for multiple funcs are going on) + + if (!g_header_mode) + check_fptr_args(i, opcnt, pp); + return ret; } @@ -5726,6 +5862,8 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits, && !g_func_pp->is_userstack && po->operand[0].type == OPT_REG) { + int save_level = 0; + reg = po->operand[0].reg; ferr_assert(po, reg >= 0); @@ -5734,12 +5872,14 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits, if (regmask_now & (1 << reg)) { already_saved = regmask_save_now & (1 << reg); flags_set = OPF_RSAVE | OPF_DONE; + save_level++; } - ret = scan_for_pop(i + 1, opcnt, i + opcnt * 3, reg, 0, 0, 0); + ret = scan_for_pop(i + 1, opcnt, i + opcnt * 3, + reg, 0, 0, save_level, 0); if (ret == 1) { scan_for_pop(i + 1, opcnt, i + opcnt * 4, - reg, 0, 0, flags_set); + reg, 0, 0, save_level, flags_set); } else { ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, 0); @@ -6108,7 +6248,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (pp != NULL) { if (!(po->flags & OPF_ATAIL)) { // since we know the args, try to collect them - ret = collect_call_args_early(i, pp, ®mask, ®mask_ffca); + ret = collect_call_args_early(i, opcnt, pp, + ®mask, ®mask_ffca); if (ret != 0) pp = NULL; } @@ -6153,7 +6294,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) { // since we know the args, collect them - collect_call_args(po, i, pp, ®mask, i + opcnt * 2); + collect_call_args(po, i, opcnt, pp, ®mask, i + opcnt * 2); } // for unresolved, collect after other passes } @@ -6313,7 +6454,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (pp->is_unresolved) { int regmask_stack = 0; - collect_call_args(po, i, pp, ®mask, i + opcnt * 2); + collect_call_args(po, i, opcnt, pp, ®mask, i + opcnt * 2); // this is pretty rough guess: // see ecx and edx were pushed (and not their saved versions) @@ -6321,18 +6462,23 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (pp->arg[arg].reg != NULL && !pp->arg[arg].is_saved) continue; - tmp_op = pp->arg[arg].datap; - if (tmp_op == NULL) + if (pp->arg[arg].push_ref_cnt == 0) ferr(po, "parsed_op missing for arg%d\n", arg); + tmp_op = pp->arg[arg].push_refs[0]; if (tmp_op->operand[0].type == OPT_REG) regmask_stack |= 1 << tmp_op->operand[0].reg; } - if (!((regmask_stack & (1 << xCX)) - && (regmask_stack & (1 << xDX)))) + // quick dumb check for potential reg-args + for (j = i - 1; j >= 0 && ops[j].op == OP_MOV; j--) + if (ops[j].operand[0].type == OPT_REG) + regmask_stack &= ~(1 << ops[j].operand[0].reg); + + if ((regmask_stack & (mxCX|mxDX)) != (mxCX|mxDX) + && ((regmask | regmask_arg) & (mxCX|mxDX))) { if (pp->argc_stack != 0 - || ((regmask | regmask_arg) & ((1 << xCX)|(1 << xDX)))) + || ((regmask | regmask_arg) & (mxCX|mxDX))) { pp_insert_reg_arg(pp, "ecx"); pp->is_fastcall = 1; @@ -6340,7 +6486,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) regmask |= 1 << xCX; } if (pp->argc_stack != 0 - || ((regmask | regmask_arg) & (1 << xDX))) + || ((regmask | regmask_arg) & mxDX)) { pp_insert_reg_arg(pp, "edx"); regmask_init |= 1 << xDX; @@ -6353,7 +6499,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) pp->is_stdcall = 1; } if (!(po->flags & OPF_TAIL) - && !(g_sct_func_attr & SCTFA_NOWARN)) + && !(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) { @@ -6461,8 +6607,49 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) default: break; } + } + + // pass8: sync all push arg numbers + // some calls share args and not all of them + // (there's only partial intersection) + do { + int changed, argnum, arggrp; + + found = 0; + for (i = 0; i < opcnt; i++) + { + po = &ops[i]; + if ((po->flags & (OPF_RMD|OPF_DONE)) || po->op != OP_CALL) + continue; + + pp = po->pp; + arggrp = 0; + do { + changed = 0; + for (arg = argnum = 0; arg < pp->argc; arg++) { + if (pp->arg[arg].reg != NULL) + continue; + if (pp->arg[arg].is_saved) + changed |= sync_argnum(pp, arg, &argnum, &arggrp); + argnum++; + } + found |= changed; + } + while (changed); + + if (argnum > 32) + ferr(po, "too many args or looping in graph\n"); + } + } + while (found); + + // pass9: final adjustments + for (i = 0; i < opcnt; i++) + { + po = &ops[i]; + if (po->flags & (OPF_RMD|OPF_DONE)) + continue; - // this might need it's own pass... if (po->op != OP_FST && po->p_argnum > 0) save_arg_vars[po->p_arggrp] |= 1 << (po->p_argnum - 1); @@ -6820,7 +7007,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) || (tmp_op && (tmp_op->op == OP_AND || tmp_op->op == OP_OR)) )) { - out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst); + struct parsed_op *po_arith = (void *)((char *)last_arith_dst + - offsetof(struct parsed_op, operand[0])); + ferr_assert(po, &ops[po_arith - ops] == po_arith); + out_src_opr_u32(buf3, sizeof(buf3), po_arith, last_arith_dst); out_test_for_cc(buf1, sizeof(buf1), po, po->pfo, po->pfo_inv, last_arith_dst->lmod, buf3); is_delayed = 1; @@ -7691,9 +7881,12 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) } // stack arg - tmp_op = pp->arg[arg].datap; - if (tmp_op == NULL) + if (pp->arg[arg].push_ref_cnt == 0) ferr(po, "parsed_op missing for arg%d\n", arg); + if (pp->arg[arg].push_ref_cnt > 1) + ferr_assert(po, pp->arg[arg].is_saved); + tmp_op = pp->arg[arg].push_refs[0]; + ferr_assert(po, tmp_op != NULL); if (tmp_op->flags & OPF_VAPUSH) { fprintf(fout, "ap"); @@ -7710,7 +7903,9 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) ferr_assert(po, cast[0] == 0); out_src_opr(buf1, sizeof(buf1), tmp_op, &tmp_op->operand[0], cast, 0); - tmp_op = pp->arg[++arg].datap; + arg++; + ferr_assert(po, pp->arg[arg].push_ref_cnt == 1); + tmp_op = pp->arg[arg].push_refs[0]; ferr_assert(po, tmp_op != NULL); out_src_opr(buf2, sizeof(buf2), tmp_op, &tmp_op->operand[0], cast, 0); @@ -7793,6 +7988,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) break; case OP_RET: + do_tail: if (g_func_pp->is_vararg) fprintf(fout, " va_end(ap);\n"); if (g_func_pp->has_retreg) { @@ -7997,6 +8193,11 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) strcat(g_comment, " fist"); break; + case OP_FABS: + fprintf(fout, " %s = fabs%s(%s);", float_st0, + need_double ? "" : "f", float_st0); + break; + case OP_FADD: case OP_FDIV: case OP_FMUL: @@ -8208,7 +8409,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) fprintf(fout, " f_st0 = f_st1;"); } strcat(g_comment, " ftol"); - break; + goto tail_check; case OPP_CIPOW: if (need_float_stack) { @@ -8221,7 +8422,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) need_double ? "" : "f"); } strcat(g_comment, " CIpow"); - break; + goto tail_check; case OPP_ABORT: fprintf(fout, " do_skip_code_abort();"); @@ -8232,6 +8433,14 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) fprintf(fout, " do_emms();"); break; + tail_check: + if (po->flags & OPF_TAIL) { + fprintf(fout, "\n"); + strcat(g_comment, " tail"); + goto do_tail; + } + break; + default: no_output = 1; ferr(po, "unhandled op type %d, flags %x\n", @@ -8329,6 +8538,7 @@ struct func_prototype { 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 @@ -8341,6 +8551,7 @@ struct func_proto_dep { 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; @@ -8392,10 +8603,14 @@ static struct func_proto_dep *hg_fp_find_dep(struct func_prototype *fp, return NULL; } -static void hg_fp_add_dep(struct func_prototype *fp, const char *name) +static void hg_fp_add_dep(struct func_prototype *fp, const char *name, + unsigned int ptr_taken) { + struct func_proto_dep * dep; + // is it a dupe? - if (hg_fp_find_dep(fp, name)) + dep = hg_fp_find_dep(fp, name); + if (dep != NULL && dep->ptr_taken == ptr_taken) return; if ((fp->dep_func_cnt & 0xff) == 0) { @@ -8406,6 +8621,7 @@ static void hg_fp_add_dep(struct func_prototype *fp, const char *name) sizeof(fp->dep_func[0]) * 0x100); } fp->dep_func[fp->dep_func_cnt].name = strdup(name); + fp->dep_func[fp->dep_func_cnt].ptr_taken = ptr_taken; fp->dep_func_cnt++; } @@ -8500,11 +8716,13 @@ static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits, if (po->flags & OPF_DONE) continue; - ret = scan_for_pop(i + 1, opcnt, i + opcnt * 2, reg, 0, 0, 0); + ret = scan_for_pop(i + 1, opcnt, i + opcnt * 2, + reg, 0, 0, 0, 0); if (ret == 1) { regmask_save |= 1 << reg; po->flags |= OPF_RMD; - scan_for_pop(i + 1, opcnt, i + opcnt * 3, reg, 0, 0, OPF_RMD); + scan_for_pop(i + 1, opcnt, i + opcnt * 3, + reg, 0, 0, 0, OPF_RMD); continue; } } @@ -8519,6 +8737,9 @@ static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits, if (g_bp_frame && !(po->flags & OPF_EBP_S)) dep->regmask_live |= 1 << xBP; } + if ((po->flags & OPF_TAIL) && po->pp != NULL + && po->pp->is_stdcall) + fp->is_stdcall = 1; } else if (po->op == OP_RET) { if (po->operand_cnt > 0) { @@ -8536,10 +8757,9 @@ static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits, ret = 1; } else { - struct parsed_opr opr = OPR_INIT(OPT_REG, OPLM_DWORD, xAX); j = -1; from_caller = 0; - ret = resolve_origin(i, &opr, i + opcnt * 4, &j, &from_caller); + ret = resolve_origin_reg(i, xAX, i + opcnt * 4, &j, &from_caller); } if (ret != 1 && from_caller) { @@ -8602,6 +8822,7 @@ static void gen_hdr(const char *funcn, int opcnt) 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; @@ -8633,6 +8854,7 @@ static void gen_hdr(const char *funcn, int opcnt) // pass3: // - remove dead labels // - collect calls + // - collect function ptr refs for (i = 0; i < opcnt; i++) { if (g_labels[i] != NULL && g_label_refs[i].i == -1) { @@ -8646,9 +8868,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); } } @@ -8678,7 +8910,7 @@ static void gen_hdr(const char *funcn, int opcnt) if (pp != NULL) { if (!(po->flags & OPF_ATAIL)) // since we know the args, try to collect them - if (collect_call_args_early(i, pp, NULL, NULL) != 0) + if (collect_call_args_early(i, opcnt, pp, NULL, NULL) != 0) pp = NULL; } @@ -8723,7 +8955,7 @@ static void gen_hdr(const char *funcn, int opcnt) if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) { // since we know the args, collect them - ret = collect_call_args(po, i, pp, ®mask_dummy, + ret = collect_call_args(po, i, opcnt, pp, ®mask_dummy, i + opcnt * 1); } if (!(po->flags & OPF_TAIL) @@ -8814,6 +9046,11 @@ static void hg_fp_resolve_deps(struct func_prototype *fp) dep->proto = bsearch(&fp_s, hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_name); 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); @@ -8843,11 +9080,8 @@ static void do_func_refs_from_data(void) strcpy(fp_s.name, hg_refs[i]); fp = bsearch(&fp_s, hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_name); - if (fp == NULL) - continue; - - if (fp->argc_stack != 0 && (fp->regmask_dep & (mxCX | mxDX))) - fp->regmask_dep |= mxCX | mxDX; + if (fp != NULL) + fp->ptr_taken = 1; } } @@ -8897,14 +9131,18 @@ 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_ret64 ? "__int64" : fp->has_ret ? "int" : "void"); - if (regmask_dep && (fp->is_stdcall || fp->argc_stack > 0) - && (regmask_dep & ~mxCX) == 0) - { + if (regmask_dep == mxCX && fp->is_stdcall && fp->argc_stack > 0) { fprintf(fout, "/*__thiscall*/ "); argc_normal++; regmask_dep = 0; @@ -9441,6 +9679,8 @@ int main(int argc, char *argv[]) g_allow_regfunc = 1; else if (IS(argv[arg], "-uc")) g_allow_user_icall = 1; + else if (IS(argv[arg], "-wu")) + g_nowarn_reguse = 1; else if (IS(argv[arg], "-m")) multi_seg = 1; else if (IS(argv[arg], "-hdr")) @@ -9450,13 +9690,14 @@ int main(int argc, char *argv[]) } if (argc < arg + 3) { - printf("usage:\n%s [-v] [-rf] [-m] <.c> <.asm> [rlist]*\n" + printf("usage:\n%s [options] <.c> <.asm> [rlist]*\n" "%s -hdr <.asm> [rlist]*\n" "options:\n" " -hdr - header generation mode\n" " -rf - allow unannotated indirect calls\n" " -uc - allow ind. calls/refs to __usercall\n" " -m - allow multiple .text sections\n" + " -wu - don't warn about bad reg use\n" "[rlist] is a file with function names to skip," " one per line\n", argv[0], argv[0]); @@ -9725,16 +9966,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;