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,
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;
};
#define mxAX (1 << xAX)
+#define mxCX (1 << xCX)
#define mxDX (1 << xDX)
#define mxST0 (1 << xST0)
#define mxST1 (1 << xST1)
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)
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);
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;
}
}
}
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)
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
// - 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:
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");
char *p, namebuf[NAMELEN];
const char *name;
int regmask_dep;
- int argc_stack;
+ int argc_normal;
int j, arg;
for (; count > 0; count--, 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) {
}
}
- for (j = 0; j < argc_stack; j++) {
+ for (j = 0; j < argc_normal; j++) {
arg++;
if (arg != 1)
fprintf(fout, ", ");
}
}
}
+ 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=<mask>
+ 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;
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;