OPF_REP = (1 << 8), /* prefixed by rep */
OPF_REPZ = (1 << 9), /* rep is repe/repz */
OPF_REPNZ = (1 << 10), /* rep is repne/repnz */
- OPF_FARG = (1 << 11), /* push collected as func arg (no reuse) */
- OPF_EBP_S = (1 << 12), /* ebp used as scratch here, not BP */
- 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 */
- OPF_VAPUSH = (1 << 17), /* vararg ptr push (as call arg) */
+ OPF_FARG = (1 << 11), /* push collected as func arg */
+ OPF_FARGNR = (1 << 12), /* push collected as func arg (no reuse) */
+ OPF_EBP_S = (1 << 13), /* ebp used as scratch here, not BP */
+ OPF_DF = (1 << 14), /* DF flag set */
+ OPF_ATAIL = (1 << 15), /* tail call with reused arg frame */
+ OPF_32BIT = (1 << 16), /* 32bit division */
+ OPF_LOCK = (1 << 17), /* op has lock prefix */
+ OPF_VAPUSH = (1 << 18), /* vararg ptr push (as call arg) */
};
enum op_op {
static int g_ida_func_attr;
static int g_allow_regfunc;
static int g_quiet_pp;
+static int g_header_mode;
#define ferr(op_, fmt, ...) do { \
printf("%s:%d: error: [%s] '%s': " fmt, asmfn, (op_)->asmln, g_func, \
&& po->operand[0].type == OPT_REG
&& IS(po->operand[0].name, reg))
{
- if (po->op == OP_PUSH && !(po->flags & OPF_FARG)) {
+ if (po->op == OP_PUSH && !(po->flags & OPF_FARGNR)) {
depth++;
if (depth > *maxdepth)
*maxdepth = depth;
return found ? 0 : -1;
}
+// XXX: merge with scan_for_pop?
+static void scan_for_pop_const(int i, int opcnt)
+{
+ int j;
+
+ for (j = i + 1; j < opcnt; j++) {
+ if ((ops[j].flags & (OPF_JMP|OPF_TAIL|OPF_RSAVE))
+ || ops[j].op == OP_PUSH || g_labels[i] != NULL)
+ {
+ break;
+ }
+
+ if (!(ops[j].flags & OPF_RMD) && ops[j].op == OP_POP)
+ {
+ ops[i].flags |= OPF_RMD;
+ ops[j].datap = &ops[i];
+ break;
+ }
+ }
+}
+
static void scan_propagate_df(int i, int opcnt)
{
struct parsed_op *po = &ops[i];
}
// scan for positive, constant esp adjust
-static int scan_for_esp_adjust(int i, int opcnt, int *adj,
- int *multipath)
+static int scan_for_esp_adjust(int i, int opcnt,
+ unsigned int adj_expect, int *adj, int *multipath)
{
struct parsed_op *po;
int first_pop = -1;
*adj = *multipath = 0;
- for (; i < opcnt; i++) {
+ for (; i < opcnt && *adj < adj_expect; i++) {
po = &ops[i];
if (g_labels[i] != NULL)
int stack_ra = 0;
int offset = 0;
+ if (g_header_mode)
+ return NULL;
+
parse_stack_access(po, opr->name, ofs_reg,
&offset, &stack_ra, NULL, 0);
if (ofs_reg[0] != 0)
pp = proto_parse(g_fhdr, buf, g_quiet_pp);
}
else if (opr->type == OPT_OFFSET || opr->type == OPT_LABEL) {
- pp = proto_parse(g_fhdr, opr->name, 0);
- if (pp == NULL)
- ferr(po, "proto_parse failed for icall from '%s'\n", opr->name);
- check_func_pp(po, pp, "reg-fptr ref");
+ pp = proto_parse(g_fhdr, opr->name, g_quiet_pp);
+ if (pp == NULL) {
+ if (!g_header_mode)
+ ferr(po, "proto_parse failed for icall to '%s'\n", opr->name);
+ }
+ else
+ check_func_pp(po, pp, "reg-fptr ref");
}
return pp;
*pp_found = pp;
}
+// 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 scan_prologue_epilogue(int opcnt)
+{
+ int ecx_push = 0, esp_sub = 0;
+ int found;
+ int i, j, l;
+
+ if (ops[0].op == OP_PUSH && IS(opr_name(&ops[0], 0), "ebp")
+ && ops[1].op == OP_MOV
+ && IS(opr_name(&ops[1], 0), "ebp")
+ && IS(opr_name(&ops[1], 1), "esp"))
+ {
+ g_bp_frame = 1;
+ ops[0].flags |= OPF_RMD;
+ ops[1].flags |= OPF_RMD;
+ i = 2;
+
+ if (ops[2].op == OP_SUB && IS(opr_name(&ops[2], 0), "esp")) {
+ g_stack_fsz = opr_const(&ops[2], 1);
+ ops[2].flags |= OPF_RMD;
+ i++;
+ }
+ else {
+ // another way msvc builds stack frame..
+ i = 2;
+ while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
+ g_stack_fsz += 4;
+ ops[i].flags |= OPF_RMD;
+ ecx_push++;
+ i++;
+ }
+ // and another way..
+ if (i == 2 && ops[i].op == OP_MOV && ops[i].operand[0].reg == xAX
+ && ops[i].operand[1].type == OPT_CONST
+ && ops[i + 1].op == OP_CALL
+ && IS(opr_name(&ops[i + 1], 0), "__alloca_probe"))
+ {
+ g_stack_fsz += ops[i].operand[1].val;
+ ops[i].flags |= OPF_RMD;
+ i++;
+ ops[i].flags |= OPF_RMD;
+ i++;
+ }
+ }
+
+ found = 0;
+ do {
+ for (; i < opcnt; i++)
+ if (ops[i].op == OP_RET)
+ break;
+ j = i - 1;
+ if (i == opcnt && (ops[j].flags & OPF_JMP)) {
+ if (found && is_like_tailjmp(j))
+ break;
+ j--;
+ }
+
+ if ((ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ebp"))
+ || ops[j].op == OP_LEAVE)
+ {
+ ops[j].flags |= OPF_RMD;
+ }
+ else if (!(g_ida_func_attr & IDAFA_NORETURN))
+ ferr(&ops[j], "'pop ebp' expected\n");
+
+ if (g_stack_fsz != 0) {
+ if (ops[j - 1].op == OP_MOV
+ && IS(opr_name(&ops[j - 1], 0), "esp")
+ && IS(opr_name(&ops[j - 1], 1), "ebp"))
+ {
+ ops[j - 1].flags |= OPF_RMD;
+ }
+ else if (ops[j].op != OP_LEAVE
+ && !(g_ida_func_attr & IDAFA_NORETURN))
+ {
+ ferr(&ops[j - 1], "esp restore expected\n");
+ }
+
+ if (ecx_push && ops[j - 2].op == OP_POP
+ && IS(opr_name(&ops[j - 2], 0), "ecx"))
+ {
+ ferr(&ops[j - 2], "unexpected ecx pop\n");
+ }
+ }
+
+ found = 1;
+ i++;
+ } while (i < opcnt);
+
+ return;
+ }
+
+ // non-bp frame
+ i = 0;
+ while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
+ ops[i].flags |= OPF_RMD;
+ g_stack_fsz += 4;
+ ecx_push++;
+ i++;
+ }
+
+ for (; i < opcnt; i++) {
+ if (ops[i].op == OP_PUSH || (ops[i].flags & (OPF_JMP|OPF_TAIL)))
+ break;
+ if (ops[i].op == OP_SUB && ops[i].operand[0].reg == xSP
+ && ops[i].operand[1].type == OPT_CONST)
+ {
+ g_stack_fsz = ops[i].operand[1].val;
+ ops[i].flags |= OPF_RMD;
+ esp_sub = 1;
+ break;
+ }
+ }
+
+ 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;
+ j--;
+ }
+ }
+ if (j != 0)
+ ferr(&ops[i], "unhandled prologue\n");
+
+ // recheck
+ i = g_stack_fsz = ecx_push = 0;
+ 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)
+ {
+ g_sp_frame = 1;
+
+ i++;
+ do {
+ for (; i < opcnt; i++)
+ if (ops[i].op == OP_RET)
+ break;
+ j = i - 1;
+ if (i == opcnt && (ops[j].flags & OPF_JMP)) {
+ if (found && is_like_tailjmp(j))
+ break;
+ j--;
+ }
+
+ if (ecx_push > 0) {
+ for (l = 0; l < ecx_push; l++) {
+ if (ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ecx"))
+ /* pop ecx */;
+ else if (ops[j].op == OP_ADD
+ && IS(opr_name(&ops[j], 0), "esp")
+ && ops[j].operand[1].type == OPT_CONST)
+ {
+ /* add esp, N */
+ ecx_push -= ops[j].operand[1].val / 4 - 1;
+ }
+ else
+ ferr(&ops[j], "'pop ecx' expected\n");
+
+ ops[j].flags |= OPF_RMD;
+ j--;
+ }
+ if (l != ecx_push)
+ ferr(&ops[j], "epilogue scan failed\n");
+
+ found = 1;
+ }
+
+ if (esp_sub) {
+ 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;
+ ops[j].operand[1].val = 0; // hack for stack arg scanner
+ found = 1;
+ }
+
+ i++;
+ } while (i < opcnt);
+ }
+}
+
static const struct parsed_proto *resolve_icall(int i, int opcnt,
int *multi_src)
{
return -1;
}
+static struct parsed_proto *process_call(int i, int opcnt)
+{
+ struct parsed_op *po = &ops[i];
+ const struct parsed_proto *pp_c;
+ struct parsed_proto *pp;
+ const char *tmpname;
+ int j = 0, l = 0;
+ int ret, arg;
+
+ tmpname = opr_name(po, 0);
+ pp = po->pp;
+ if (pp == NULL)
+ {
+ // indirect call
+ pp_c = resolve_icall(i, opcnt, &l);
+ if (pp_c != NULL) {
+ if (!pp_c->is_func && !pp_c->is_fptr)
+ ferr(po, "call to non-func: %s\n", pp_c->name);
+ pp = proto_clone(pp_c);
+ my_assert_not(pp, NULL);
+ if (l)
+ // not resolved just to single func
+ pp->is_fptr = 1;
+
+ switch (po->operand[0].type) {
+ case OPT_REG:
+ // we resolved this call and no longer need the register
+ po->regmask_src &= ~(1 << po->operand[0].reg);
+ break;
+ case OPT_REGMEM:
+ pp->is_fptr = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ if (pp == NULL) {
+ pp = calloc(1, sizeof(*pp));
+ my_assert_not(pp, NULL);
+
+ pp->is_fptr = 1;
+ ret = scan_for_esp_adjust(i + 1, opcnt, ~0, &j, &l);
+ if (ret < 0 || j < 0) {
+ if (!g_allow_regfunc)
+ ferr(po, "non-__cdecl indirect call unhandled yet\n");
+ pp->is_unresolved = 1;
+ j = 0;
+ }
+ j /= 4;
+ if (j > ARRAY_SIZE(pp->arg))
+ ferr(po, "esp adjust too large: %d\n", j);
+ pp->ret_type.name = strdup("int");
+ pp->argc = pp->argc_stack = j;
+ for (arg = 0; arg < pp->argc; arg++)
+ pp->arg[arg].type.name = strdup("int");
+ }
+ po->pp = pp;
+ }
+
+ // look for and make use of esp adjust
+ ret = -1;
+ if (!pp->is_stdcall && pp->argc_stack > 0)
+ ret = scan_for_esp_adjust(i + 1, opcnt,
+ pp->argc_stack * 4, &j, &l);
+ if (ret >= 0) {
+ if (pp->is_vararg) {
+ if (j / 4 < pp->argc_stack)
+ ferr(po, "esp adjust is too small: %x < %x\n",
+ j, pp->argc_stack * 4);
+ // modify pp to make it have varargs as normal args
+ arg = pp->argc;
+ pp->argc += j / 4 - pp->argc_stack;
+ for (; arg < pp->argc; arg++) {
+ pp->arg[arg].type.name = strdup("int");
+ pp->argc_stack++;
+ }
+ if (pp->argc > ARRAY_SIZE(pp->arg))
+ ferr(po, "too many args for '%s'\n", tmpname);
+ }
+ if (pp->argc_stack > j / 4) {
+ fnote(po, "(this call)\n");
+ ferr(&ops[ret], "stack tracking failed for '%s': %x %x\n",
+ tmpname, pp->argc_stack * 4, j);
+ }
+
+ ops[ret].flags |= OPF_RMD;
+ if (ops[ret].op == OP_POP) {
+ if (j > 4) {
+ // deal with multi-pop stack adjust
+ j = pp->argc_stack;
+ while (ops[ret].op == OP_POP && j > 0 && ret < opcnt) {
+ ops[ret].flags |= OPF_RMD;
+ j--;
+ ret++;
+ }
+ }
+ }
+ else if (!l) {
+ // a bit of a hack, but deals with use of
+ // single adj for multiple calls
+ ops[ret].operand[1].val -= j;
+ }
+ }
+ else if (pp->is_vararg)
+ ferr(po, "missing esp_adjust for vararg func '%s'\n",
+ pp->name);
+
+ return pp;
+}
+
static int collect_call_args_r(struct parsed_op *po, int i,
struct parsed_proto *pp, int *regmask, int *save_arg_vars,
int *arg_grp, int arg, int magic, int need_op_saving, int may_reuse)
ferr(po, "arg collect %d/%d hit esp adjust of %d\n",
arg, pp->argc, ops[j].operand[1].val);
}
- else if (ops[j].op == OP_POP) {
+ else if (ops[j].op == OP_POP && !(ops[j].flags & OPF_RMD)
+ && ops[j].datap == NULL)
+ {
if (pp->is_unresolved)
break;
may_reuse = 1;
}
- else if (ops[j].op == OP_PUSH && !(ops[j].flags & OPF_FARG))
+ else if (ops[j].op == OP_PUSH && !(ops[j].flags & OPF_FARGNR))
{
if (pp->is_unresolved && (ops[j].flags & OPF_RMD))
break;
// but that can't happen if we didn't branch, so they
// can be removed from future searches (handles nested calls)
if (!may_reuse)
- ops[j].flags |= OPF_FARG;
+ ops[j].flags |= OPF_FARGNR;
+ ops[j].flags |= OPF_FARG;
ops[j].flags &= ~OPF_RSAVE;
// check for __VALIST
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;
fprintf(fout, "noreturn ");
}
+static int get_pp_arg_regmask(const struct parsed_proto *pp)
+{
+ int regmask = 0;
+ int i, reg;
+
+ for (i = 0; i < pp->argc; i++) {
+ if (pp->arg[i].reg != NULL) {
+ reg = char_array_i(regs_r32,
+ ARRAY_SIZE(regs_r32), pp->arg[i].reg);
+ if (reg < 0)
+ ferr(ops, "arg '%s' of func '%s' is not a reg?\n",
+ pp->arg[i].reg, pp->name);
+ regmask |= 1 << reg;
+ }
+ }
+
+ return regmask;
+}
+
static char *saved_arg_name(char *buf, size_t buf_size, int grp, int num)
{
char buf1[16];
return buf;
}
+static void gen_x_cleanup(int opcnt);
+
static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
{
struct parsed_op *po, *delayed_flag_op = NULL, *tmp_op;
if (g_func_pp == NULL)
ferr(ops, "proto_parse failed for '%s'\n", funcn);
- for (i = 0; i < g_func_pp->argc; i++) {
- if (g_func_pp->arg[i].reg != NULL) {
- reg = char_array_i(regs_r32,
- ARRAY_SIZE(regs_r32), g_func_pp->arg[i].reg);
- if (reg < 0)
- ferr(ops, "arg '%s' is not a reg?\n", g_func_pp->arg[i].reg);
- regmask_arg |= 1 << reg;
- }
- }
+ regmask_arg = get_pp_arg_regmask(g_func_pp);
// pass1:
// - handle ebp/esp frame, remove ops related to it
- if (ops[0].op == OP_PUSH && IS(opr_name(&ops[0], 0), "ebp")
- && ops[1].op == OP_MOV
- && IS(opr_name(&ops[1], 0), "ebp")
- && IS(opr_name(&ops[1], 1), "esp"))
- {
- int ecx_push = 0;
-
- g_bp_frame = 1;
- ops[0].flags |= OPF_RMD;
- ops[1].flags |= OPF_RMD;
- i = 2;
-
- if (ops[2].op == OP_SUB && IS(opr_name(&ops[2], 0), "esp")) {
- g_stack_fsz = opr_const(&ops[2], 1);
- ops[2].flags |= OPF_RMD;
- i++;
- }
- else {
- // another way msvc builds stack frame..
- i = 2;
- while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
- g_stack_fsz += 4;
- ops[i].flags |= OPF_RMD;
- ecx_push++;
- i++;
- }
- // and another way..
- if (i == 2 && ops[i].op == OP_MOV && ops[i].operand[0].reg == xAX
- && ops[i].operand[1].type == OPT_CONST
- && ops[i + 1].op == OP_CALL
- && IS(opr_name(&ops[i + 1], 0), "__alloca_probe"))
- {
- g_stack_fsz += ops[i].operand[1].val;
- ops[i].flags |= OPF_RMD;
- i++;
- ops[i].flags |= OPF_RMD;
- i++;
- }
- }
-
- found = 0;
- do {
- for (; i < opcnt; i++)
- if (ops[i].op == OP_RET)
- break;
- j = i - 1;
- if (i == opcnt && (ops[j].flags & OPF_JMP)) {
- if (found && is_like_tailjmp(j))
- break;
- j--;
- }
-
- if ((ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ebp"))
- || ops[j].op == OP_LEAVE)
- {
- ops[j].flags |= OPF_RMD;
- }
- else if (!(g_ida_func_attr & IDAFA_NORETURN))
- ferr(&ops[j], "'pop ebp' expected\n");
-
- if (g_stack_fsz != 0) {
- if (ops[j - 1].op == OP_MOV
- && IS(opr_name(&ops[j - 1], 0), "esp")
- && IS(opr_name(&ops[j - 1], 1), "ebp"))
- {
- ops[j - 1].flags |= OPF_RMD;
- }
- else if (ops[j].op != OP_LEAVE
- && !(g_ida_func_attr & IDAFA_NORETURN))
- {
- ferr(&ops[j - 1], "esp restore expected\n");
- }
-
- if (ecx_push && ops[j - 2].op == OP_POP
- && IS(opr_name(&ops[j - 2], 0), "ecx"))
- {
- ferr(&ops[j - 2], "unexpected ecx pop\n");
- }
- }
-
- found = 1;
- i++;
- } while (i < opcnt);
- }
- else {
- int ecx_push = 0, esp_sub = 0;
-
- i = 0;
- while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
- ops[i].flags |= OPF_RMD;
- g_stack_fsz += 4;
- ecx_push++;
- i++;
- }
-
- for (; i < opcnt; i++) {
- if (ops[i].op == OP_PUSH || (ops[i].flags & (OPF_JMP|OPF_TAIL)))
- break;
- if (ops[i].op == OP_SUB && ops[i].operand[0].reg == xSP
- && ops[i].operand[1].type == OPT_CONST)
- {
- g_stack_fsz = ops[i].operand[1].val;
- ops[i].flags |= OPF_RMD;
- esp_sub = 1;
- break;
- }
- }
-
- found = 0;
- if (ecx_push || esp_sub)
- {
- g_sp_frame = 1;
-
- i++;
- do {
- for (; i < opcnt; i++)
- if (ops[i].op == OP_RET)
- break;
- j = i - 1;
- if (i == opcnt && (ops[j].flags & OPF_JMP)) {
- if (found && is_like_tailjmp(j))
- break;
- j--;
- }
-
- if (ecx_push > 0) {
- for (l = 0; l < ecx_push; l++) {
- if (ops[j].op != OP_POP
- || !IS(opr_name(&ops[j], 0), "ecx"))
- {
- ferr(&ops[j], "'pop ecx' expected\n");
- }
- ops[j].flags |= OPF_RMD;
- j--;
- }
-
- found = 1;
- }
-
- if (esp_sub) {
- 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);
- }
- }
+ scan_prologue_epilogue(opcnt);
// pass2:
// - parse calls with labels
if (po->op == OP_CALL)
{
- tmpname = opr_name(po, 0);
- pp = po->pp;
- if (pp == NULL)
- {
- // indirect call
- pp_c = resolve_icall(i, opcnt, &l);
- if (pp_c != NULL) {
- if (!pp_c->is_func && !pp_c->is_fptr)
- ferr(po, "call to non-func: %s\n", pp_c->name);
- pp = proto_clone(pp_c);
- my_assert_not(pp, NULL);
- if (l)
- // not resolved just to single func
- pp->is_fptr = 1;
-
- switch (po->operand[0].type) {
- case OPT_REG:
- // we resolved this call and no longer need the register
- po->regmask_src &= ~(1 << po->operand[0].reg);
- break;
- case OPT_REGMEM:
- pp->is_fptr = 1;
- break;
- default:
- break;
- }
- }
- if (pp == NULL) {
- pp = calloc(1, sizeof(*pp));
- my_assert_not(pp, NULL);
- pp->is_fptr = 1;
- ret = scan_for_esp_adjust(i + 1, opcnt, &j, &l);
- if (ret < 0) {
- if (!g_allow_regfunc)
- ferr(po, "non-__cdecl indirect call unhandled yet\n");
- pp->is_unresolved = 1;
- j = 0;
- }
- j /= 4;
- if (j > ARRAY_SIZE(pp->arg))
- ferr(po, "esp adjust too large: %d\n", j);
- pp->ret_type.name = strdup("int");
- pp->argc = pp->argc_stack = j;
- for (arg = 0; arg < pp->argc; arg++)
- pp->arg[arg].type.name = strdup("int");
- }
- po->pp = pp;
- }
-
- // look for and make use of esp adjust
- ret = -1;
- if (!pp->is_stdcall && pp->argc_stack > 0)
- ret = scan_for_esp_adjust(i + 1, opcnt, &j, &l);
- if (ret >= 0) {
- if (pp->is_vararg) {
- if (j / 4 < pp->argc_stack)
- ferr(po, "esp adjust is too small: %x < %x\n",
- j, pp->argc_stack * 4);
- // modify pp to make it have varargs as normal args
- arg = pp->argc;
- pp->argc += j / 4 - pp->argc_stack;
- for (; arg < pp->argc; arg++) {
- pp->arg[arg].type.name = strdup("int");
- pp->argc_stack++;
- }
- if (pp->argc > ARRAY_SIZE(pp->arg))
- ferr(po, "too many args for '%s'\n", tmpname);
- }
- if (pp->argc_stack != j / 4)
- ferr(po, "stack tracking failed for '%s': %x %x\n",
- tmpname, pp->argc_stack * 4, j);
-
- ops[ret].flags |= OPF_RMD;
- if (ops[ret].op == OP_POP && j > 4) {
- // deal with multi-pop stack adjust
- j = pp->argc_stack;
- while (ops[ret].op == OP_POP && j > 0 && ret < opcnt) {
- ops[ret].flags |= OPF_RMD;
- j--;
- ret++;
- }
- }
- else if (!l) {
- // a bit of a hack, but deals with use of
- // single adj for multiple calls
- ops[ret].operand[1].val -= j;
- }
- }
- else if (pp->is_vararg)
- ferr(po, "missing esp_adjust for vararg func '%s'\n",
- pp->name);
+ pp = process_call(i, opcnt);
if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
// since we know the args, collect them
regmask_save |= 1 << reg;
}
- if (po->op == OP_PUSH && po->p_argnum == 0
+ if (po->op == OP_PUSH && !(po->flags & OPF_FARG)
&& !(po->flags & OPF_RSAVE) && !g_func_pp->is_userstack)
{
if (po->operand[0].type == OPT_REG)
}
}
else if (po->operand[0].type == OPT_CONST) {
- for (j = i + 1; j < opcnt; j++) {
- if ((ops[j].flags & (OPF_JMP|OPF_TAIL|OPF_RSAVE))
- || ops[j].op == OP_PUSH || g_labels[i] != NULL)
- {
- break;
- }
-
- if (!(ops[j].flags & OPF_RMD) && ops[j].op == OP_POP)
- {
- po->flags |= OPF_RMD;
- ops[j].datap = po;
- break;
- }
- }
+ scan_for_pop_const(i, opcnt);
}
}
fprintf(fout, "}\n\n");
- // cleanup
+ gen_x_cleanup(opcnt);
+}
+
+static void gen_x_cleanup(int opcnt)
+{
+ int i;
+
for (i = 0; i < opcnt; i++) {
struct label_ref *lr, *lr_del;
static struct func_prototype *hg_fp;
static int hg_fp_cnt;
+static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
+ int count);
+
static struct func_proto_dep *hg_fp_find_dep(struct func_prototype *fp,
const char *name)
{
static void gen_hdr(const char *funcn, int opcnt)
{
+ const struct parsed_proto *pp_c;
+ struct parsed_proto *pp;
struct func_prototype *fp;
struct func_proto_dep *dep;
struct parsed_data *pd;
struct parsed_op *po;
+ const char *tmpname;
int regmask_save = 0;
int regmask_dst = 0;
int regmask_dep = 0;
fp = &hg_fp[hg_fp_cnt];
snprintf(fp->name, sizeof(fp->name), "%s", funcn);
fp->id = hg_fp_cnt;
+ fp->argc_stack = -1;
hg_fp_cnt++;
+ // perhaps already in seed header?
+ pp_c = proto_parse(g_fhdr, funcn, 1);
+ if (pp_c != NULL) {
+ fp->argc_stack = pp_c->argc_stack;
+ fp->regmask_dep = get_pp_arg_regmask(pp_c);
+ fp->has_ret = !IS(pp_c->ret_type.name, "void");
+ return;
+ }
+
+ g_bp_frame = g_sp_frame = g_stack_fsz = 0;
+ g_stack_frame_used = 0;
+
// pass1:
+ // - handle ebp/esp frame, remove ops related to it
+ scan_prologue_epilogue(opcnt);
+
+ // pass2:
// - collect calls
// - resolve all branches
for (i = 0; i < opcnt; i++)
continue;
if (po->op == OP_CALL) {
- if (po->operand[0].type == OPT_LABEL)
- hg_fp_add_dep(fp, opr_name(po, 0));
+ tmpname = opr_name(po, 0);
+ pp = NULL;
+ if (po->operand[0].type == OPT_LABEL) {
+ hg_fp_add_dep(fp, tmpname);
+
+ // perhaps a call to already known func?
+ pp_c = proto_parse(g_fhdr, tmpname, 1);
+ if (pp_c != NULL)
+ pp = proto_clone(pp_c);
+ }
+ 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 && pp->is_noreturn)
+ po->flags |= OPF_TAIL;
+
+ po->pp = pp;
continue;
}
i--; // reprocess
}
- // pass2:
+ // pass3:
// - remove dead labels
+ // - process calls
+ // - handle push <const>/pop pairs
for (i = 0; i < opcnt; i++)
{
if (g_labels[i] != NULL && g_label_refs[i].i == -1) {
free(g_labels[i]);
g_labels[i] = NULL;
}
+
+ po = &ops[i];
+ if (po->flags & OPF_RMD)
+ continue;
+
+ if (po->op == OP_CALL) {
+ pp = process_call(i, opcnt);
+
+ if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
+ int regmask_dummy = 0, save_arg_vars[MAX_ARG_GRP] = { 0, };
+ // since we know the args, collect them
+ collect_call_args(po, i, pp, ®mask_dummy, save_arg_vars,
+ i + opcnt * 2);
+ }
+ }
+ else if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST) {
+ scan_for_pop_const(i, opcnt);
+ }
}
- // pass3:
+ // pass4:
// - track saved regs
// - try to figure out arg-regs
for (i = 0; i < opcnt; i++)
{
po = &ops[i];
- if (po->flags & OPF_RMD)
- continue;
- if (po->op == OP_PUSH && po->operand[0].type == OPT_REG)
+ if (po->flags & OPF_FARG)
+ /* (just calculate register deps) */;
+ else if (po->flags & OPF_RMD)
+ continue;
+ else if (po->op == OP_PUSH && po->operand[0].type == OPT_REG)
{
reg = po->operand[0].reg;
if (reg < 0)
continue;
}
}
- else if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST) {
- for (j = i + 1; j < opcnt; j++) {
- if ((ops[j].flags & (OPF_JMP|OPF_TAIL|OPF_RSAVE))
- || ops[j].op == OP_PUSH || g_labels[i] != NULL)
- {
- break;
- }
-
- if (!(ops[j].flags & OPF_RMD) && ops[j].op == OP_POP)
- {
- po->flags |= OPF_RMD;
- ops[j].datap = po;
- break;
- }
- }
- continue;
- }
else if (po->op == OP_CALL) {
po->regmask_dst |= 1 << xAX;
dep->regmask_live = regmask_save | regmask_dst;
}
else if (po->op == OP_RET) {
- if (po->operand_cnt > 0)
+ if (po->operand_cnt > 0) {
fp->is_stdcall = 1;
+ if (fp->argc_stack >= 0
+ && fp->argc_stack != po->operand[0].val / 4)
+ ferr(po, "ret mismatch? (%d)\n", fp->argc_stack * 4);
+ fp->argc_stack = po->operand[0].val / 4;
+ }
}
if (has_ret != 0 && (po->flags & OPF_TAIL)) {
}
}
- l = po->regmask_src & ~(regmask_save | regmask_dst);
+ l = regmask_save | regmask_dst;
+ if (g_bp_frame && !(po->flags & OPF_EBP_S))
+ l |= 1 << xBP;
+
+ l = po->regmask_src & ~l;
#if 0
if (l)
fnote(po, "dep |= %04x, dst %04x, save %04x\n", l,
if (has_ret == -1 && (regmask_dep & (1 << xAX)))
has_ret = 1;
- for (i = 0; i < g_eqcnt; i++)
+ for (i = 0; i < g_eqcnt; i++) {
if (g_eqs[i].offset > max_bp_offset && g_eqs[i].offset < 4*32)
max_bp_offset = g_eqs[i].offset;
+ }
- if (max_bp_offset > 0) {
+ if (fp->argc_stack < 0) {
max_bp_offset = (max_bp_offset + 3) & ~3;
- fp->argc_stack = max_bp_offset / 4 - 1;
- if (!(g_ida_func_attr & IDAFA_BP_FRAME))
+ fp->argc_stack = max_bp_offset / 4;
+ if ((g_ida_func_attr & IDAFA_BP_FRAME) && fp->argc_stack > 0)
fp->argc_stack--;
}
fp->regmask_dep = regmask_dep & ~(1 << xSP);
fp->has_ret = has_ret;
+
+ gen_x_cleanup(opcnt);
}
static void hg_fp_resolve_deps(struct func_prototype *fp)
}
}
-static void output_hdr(FILE *fout)
+static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
+ int count)
{
- struct func_prototype *fp;
- int had_usercall = 0;
+ char *p, buf[NAMELEN];
+ const char *cp;
int regmask_dep;
int argc_stack;
- int i, j, arg;
+ int j, arg;
- // resolve deps
- qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_name);
- for (i = 0; i < hg_fp_cnt; i++)
- hg_fp_resolve_deps(&hg_fp[i]);
-
- // note: messes up .proto ptr, don't use
- //qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_id);
-
- for (i = 0; i < hg_fp_cnt; i++) {
- fp = &hg_fp[i];
+ for (; count > 0; count--, fp++) {
if (fp->has_ret == -1)
fprintf(fout, "// ret unresolved\n");
#if 0
if (regmask_dep && (fp->is_stdcall || argc_stack == 0)
&& (regmask_dep & ~((1 << xCX) | (1 << xDX))) == 0)
{
- fprintf(fout, "__fastcall ");
- if (had_usercall)
- fprintf(fout, " "); // align
+ fprintf(fout, " __fastcall ");
if (!(regmask_dep & (1 << xDX)) && argc_stack == 0)
argc_stack = 1;
else
}
else if (regmask_dep && !fp->is_stdcall) {
fprintf(fout, "/*__usercall*/ ");
- had_usercall = 1;
}
else if (regmask_dep) {
fprintf(fout, "/*__userpurge*/ ");
- had_usercall = 1;
}
else if (fp->is_stdcall)
- fprintf(fout, "__stdcall ");
+ fprintf(fout, " __stdcall ");
else
- fprintf(fout, "__cdecl ");
+ fprintf(fout, " __cdecl ");
- fprintf(fout, "%s(", fp->name);
+ p = strchr(fp->name, '@');
+ if (p != NULL) {
+ memcpy(buf, fp->name, p - fp->name);
+ buf[p - fp->name] = 0;
+ cp = buf;
+ }
+ else
+ cp = fp->name;
+ if (cp[0] == '_')
+ cp++;
+ fprintf(fout, "%s(", cp);
arg = 0;
for (j = 0; j < xSP; j++) {
}
}
+static void output_hdr(FILE *fout)
+{
+ int i;
+
+ // resolve deps
+ qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_name);
+ for (i = 0; i < hg_fp_cnt; i++)
+ hg_fp_resolve_deps(&hg_fp[i]);
+
+ // note: messes up .proto ptr, don't use
+ //qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_id);
+
+ output_hdr_fp(fout, hg_fp, hg_fp_cnt);
+}
+
static void set_label(int i, const char *name)
{
const char *p;
int pending_endp = 0;
int skip_func = 0;
int skip_warned = 0;
- int header_mode = 0;
int eq_alloc;
int verbose = 0;
int multi_seg = 0;
else if (IS(argv[arg], "-m"))
multi_seg = 1;
else if (IS(argv[arg], "-hdr"))
- header_mode = g_quiet_pp = 1;
+ g_header_mode = g_quiet_pp = g_allow_regfunc = 1;
else
break;
}
if (argc < arg + 3) {
printf("usage:\n%s [-v] [-rf] [-m] <.c> <.asm> <hdrf> [rlist]*\n"
- "%s -hdr <.h> <.asm> <seed_hdrf> [rlist]*\n",
+ "%s -hdr <out.h> <.asm> <seed.h> [rlist]*\n",
argv[0], argv[0]);
return 1;
}
}
if (in_func && !skip_func) {
- if (header_mode)
+ if (g_header_mode)
gen_hdr(g_func, pi);
else
gen_func(fout, g_fhdr, g_func, pi);
pi++;
}
- if (header_mode)
+ if (g_header_mode)
output_hdr(fout);
fclose(fout);