OPF_DF = (1 << 13), /* DF flag set */
OPF_ATAIL = (1 << 14), /* tail call with reused arg frame */
OPF_32BIT = (1 << 15), /* 32bit division */
+ OPF_LOCK = (1 << 16), /* op has lock prefix */
};
enum op_op {
static struct parsed_op ops[MAX_OPS];
static struct parsed_equ *g_eqs;
static int g_eqcnt;
-static char g_labels[MAX_OPS][32];
+static char g_labels[MAX_OPS][48];
static struct label_ref g_label_refs[MAX_OPS];
static const struct parsed_proto *g_func_pp;
static struct parsed_data *g_func_pd;
if (!IS_START(name, "esp+"))
return NULL;
- p = strchr(name + 4, '+');
+ s = name + 4;
+ p = strchr(s, '+');
if (p) {
- // must be a number after esp+, already converted to 0x..
- s = name + 4;
+ if (is_reg_in_str(s)) {
+ if (extra_reg != NULL) {
+ strncpy(extra_reg, s, p - s);
+ extra_reg[p - s] = 0;
+ }
+ s = p + 1;
+ p = strchr(s, '+');
+ if (p == NULL)
+ aerr("%s IDA stackvar not set?\n", __func__);
+ }
if (!('0' <= *s && *s <= '9')) {
- aerr("%s nan?\n", __func__);
+ aerr("%s IDA stackvar offset not set?\n", __func__);
return NULL;
}
if (s[0] == '0' && s[1] == 'x')
{ "repz", OPF_REP|OPF_REPZ },
{ "repne", OPF_REP|OPF_REPNZ },
{ "repnz", OPF_REP|OPF_REPNZ },
+ { "lock", OPF_LOCK }, // ignored for now..
};
#define OPF_CJMP_CC (OPF_JMP|OPF_CJMP|OPF_CC)
static void parse_stack_access(struct parsed_op *po,
const char *name, char *ofs_reg, int *offset_out,
- int *stack_ra_out, const char **bp_arg_out)
+ int *stack_ra_out, const char **bp_arg_out, int is_lea)
{
const char *bp_arg = "";
const char *p = NULL;
if (!strncmp(name, "ebp", 3))
stack_ra = 4;
- if (ofs_reg[0] == 0 && stack_ra <= offset && offset < stack_ra + 4)
+ // yes it sometimes LEAs ra for compares..
+ if (!is_lea && ofs_reg[0] == 0
+ && stack_ra <= offset && offset < stack_ra + 4)
+ {
ferr(po, "reference to ra? %d %d\n", offset, stack_ra);
+ }
*offset_out = offset;
*stack_ra_out = stack_ra;
if (po->flags & OPF_EBP_S)
ferr(po, "stack_frame_access while ebp is scratch\n");
- parse_stack_access(po, name, ofs_reg, &offset, &stack_ra, &bp_arg);
+ parse_stack_access(po, name, ofs_reg, &offset,
+ &stack_ra, &bp_arg, is_lea);
if (offset > stack_ra)
{
// last op in stream - unconditional branch or ret
#define LAST_OP(_i) ((ops[_i].flags & OPF_TAIL) \
- || (ops[_i].flags & (OPF_JMP|OPF_CJMP)) == OPF_JMP)
+ || ((ops[_i].flags & (OPF_JMP|OPF_CJMP)) == OPF_JMP \
+ && ops[_i].op != OP_CALL))
static int scan_for_pop(int i, int opcnt, const char *reg,
int magic, int depth, int *maxdepth, int do_flags)
int offset = 0;
parse_stack_access(po, opr->name, ofs_reg,
- &offset, &stack_ra, NULL);
+ &offset, &stack_ra, NULL, 0);
if (ofs_reg[0] != 0)
ferr(po, "offset reg on arg access?\n");
if (offset <= stack_ra) {
struct parsed_op *po;
struct label_ref *lr;
- while (i >= 0) {
- if (ops[i].cc_scratch == magic)
- return;
- ops[i].cc_scratch = magic;
+ ops[i].cc_scratch = magic;
+ while (1) {
if (g_labels[i][0] != 0) {
lr = &g_label_refs[i];
for (; lr != NULL; lr = lr->next)
if (i > 0 && LAST_OP(i - 1))
return;
}
+
i--;
+ if (i < 0)
+ break;
+
+ if (ops[i].cc_scratch == magic)
+ return;
+ ops[i].cc_scratch = magic;
if (!(ops[i].flags & OPF_DATA))
continue;
return ret;
}
+// early check for tail call or branch back
+static int is_like_tailjmp(int j)
+{
+ if (!(ops[j].flags & OPF_JMP))
+ return 0;
+
+ if (ops[j].op == OP_JMP && !ops[j].operand[0].had_ds)
+ // probably local branch back..
+ return 1;
+ if (ops[j].op == OP_CALL)
+ // probably noreturn call..
+ return 1;
+
+ return 0;
+}
+
static void pp_insert_reg_arg(struct parsed_proto *pp, const char *reg)
{
int i;
break;
j = i - 1;
if (i == opcnt && (ops[j].flags & OPF_JMP)) {
- if (found) {
- if (ops[j].op == OP_JMP && !ops[j].operand[0].had_ds)
- // probably local branch back..
- break;
- if (ops[j].op == OP_CALL)
- // probably noreturn call..
+ if (found && is_like_tailjmp(j))
break;
- }
j--;
}
}
}
+ found = 0;
if (g_sp_frame)
{
g_stack_fsz = ops[i].operand[1].val;
for (; i < opcnt; i++)
if (ops[i].op == OP_RET)
break;
- if (ops[i - 1].op != OP_ADD
- || !IS(opr_name(&ops[i - 1], 0), "esp")
- || ops[i - 1].operand[1].type != OPT_CONST
- || ops[i - 1].operand[1].val != g_stack_fsz)
- ferr(&ops[i - 1], "'add esp' expected\n");
- ops[i - 1].flags |= OPF_RMD;
+ j = i - 1;
+ if (i == opcnt && (ops[j].flags & OPF_JMP)) {
+ if (found && is_like_tailjmp(j))
+ break;
+ j--;
+ }
+ if (ops[j].op != OP_ADD
+ || !IS(opr_name(&ops[j], 0), "esp")
+ || ops[j].operand[1].type != OPT_CONST
+ || ops[j].operand[1].val != g_stack_fsz)
+ ferr(&ops[j], "'add esp' expected\n");
+ ops[j].flags |= OPF_RMD;
+
+ found = 1;
i++;
} while (i < opcnt);
}
if (l)
// not resolved just to single func
pp->is_fptr = 1;
+ if (po->operand[0].type == OPT_REG)
+ // we resolved this call and no longer need the register
+ po->regmask_src &= ~(1 << po->operand[0].reg);
}
if (pp == NULL) {
pp = calloc(1, sizeof(*pp));
if (g_bp_frame && !(po->flags & OPF_EBP_S)) {
if (po->regmask_dst & (1 << xBP))
// compiler decided to drop bp frame and use ebp as scratch
- scan_fwd_set_flags(i, opcnt, i + opcnt * 5, OPF_EBP_S);
+ scan_fwd_set_flags(i + 1, opcnt, i + opcnt * 5, OPF_EBP_S);
else
regmask_now &= ~(1 << xBP);
}
else
need_tmp64 = 1;
}
+ else if (po->op == OP_CLD)
+ po->flags |= OPF_RMD;
if (po->op == OP_RCL || po->op == OP_RCR || po->op == OP_XCHG) {
need_tmp_var = 1;