#include "protoparse.h"
+static const char *c_save_regs[] = { "ebx", "esi", "edi", "ebp" };
+
static int is_x86_reg_saved(const char *reg)
{
static const char *nosave_regs[] = { "eax", "edx", "ecx" };
}
if (pp->argc_stack == 0 && !must_save && !pp->is_stdcall
- && !pp->is_vararg)
+ && !pp->is_vararg && !pp->has_retreg)
{
// load arg regs
for (i = 0; i < pp->argc; i++) {
return;
}
+ // asm_stack_args | saved_regs | ra | args_from_c
+
// save the regs
- for (i = 0; i < pp->argc; i++) {
- if (pp->arg[i].reg != NULL && is_x86_reg_saved(pp->arg[i].reg)) {
- fprintf(f, "\tpushl %%%s\n", pp->arg[i].reg);
- sarg_ofs++;
- }
+ // because we don't always know what we are calling,
+ // be safe and save everything that has to be saved in __cdecl
+ for (i = 0; i < ARRAY_SIZE(c_save_regs); i++) {
+ fprintf(f, "\tpushl %%%s\n", c_save_regs[i]);
+ sarg_ofs++;
}
- // reconstruct arg stack
+ // reconstruct arg stack for asm
for (i = argc_repush - 1; i >= 0; i--) {
if (pp->arg[i].reg == NULL) {
fprintf(f, "\tmovl %d(%%esp), %%eax\n",
args_repushed++;
}
}
- // my_assert(args_repushed, pp->argc_stack);
// load arg regs
for (i = 0; i < pp->argc; i++) {
if (pp->arg[i].reg != NULL) {
fprintf(f, "\tmovl %d(%%esp), %%%s\n",
(i + sarg_ofs) * 4, pp->arg[i].reg);
+ if (pp->arg[i].type.is_retreg)
+ fprintf(f, "\tmovl (%%%s), %%%s\n",
+ pp->arg[i].reg, pp->arg[i].reg);
}
}
fprintf(f, "\n\t# %s\n", pp->is_stdcall ? "__stdcall" : "__cdecl");
fprintf(f, "\tcall %s\n\n", sym_out);
- if (args_repushed && !pp->is_stdcall)
+ if (args_repushed && !pp->is_stdcall) {
fprintf(f, "\tadd $%d,%%esp\n", args_repushed * 4);
+ sarg_ofs -= args_repushed;
+ }
- // restore regs
- for (i = pp->argc - 1; i >= 0; i--) {
- if (pp->arg[i].reg != NULL && is_x86_reg_saved(pp->arg[i].reg))
- fprintf(f, "\tpopl %%%s\n", pp->arg[i].reg);
+ // update the retreg regs
+ if (pp->has_retreg) {
+ for (i = 0; i < pp->argc; i++) {
+ if (pp->arg[i].type.is_retreg) {
+ fprintf(f, "\tmovl %d(%%esp), %%ecx\n"
+ "\tmovl %%%s, (%%ecx)\n",
+ (i + sarg_ofs) * 4, pp->arg[i].reg);
+ }
+ }
}
+ // restore regs
+ for (i = ARRAY_SIZE(c_save_regs) - 1; i >= 0; i--)
+ fprintf(f, "\tpopl %%%s\n", c_save_regs[i]);
+
fprintf(f, "\tret\n\n");
}
static void out_fromasm_x86(FILE *f, const char *sym,
const struct parsed_proto *pp)
{
+ int reg_ofs[ARRAY_SIZE(pp->arg)];
int sarg_ofs = 1; // stack offset to args, in DWORDs
int saved_regs = 0;
+ int ecx_ofs = -1;
+ int edx_ofs = -1;
int c_is_stdcall;
int argc_repush;
int stack_args;
fprintf(f, "\tpushl %%ecx\n");
saved_regs++;
sarg_ofs++;
+ ecx_ofs = sarg_ofs;
if (!ret64) {
fprintf(f, "\tpushl %%edx\n");
saved_regs++;
sarg_ofs++;
+ edx_ofs = sarg_ofs;
+ }
+
+ // need space for retreg args
+ if (pp->has_retreg) {
+ for (i = 0; i < pp->argc; i++) {
+ if (!pp->arg[i].type.is_retreg)
+ continue;
+ if (IS(pp->arg[i].reg, "ecx") && ecx_ofs >= 0) {
+ reg_ofs[i] = ecx_ofs;
+ continue;
+ }
+ if (IS(pp->arg[i].reg, "edx") && edx_ofs >= 0) {
+ reg_ofs[i] = edx_ofs;
+ continue;
+ }
+ fprintf(f, "\tpushl %%%s\n", pp->arg[i].reg);
+ saved_regs++;
+ sarg_ofs++;
+ reg_ofs[i] = sarg_ofs;
+ }
}
// construct arg stack
stack_args--;
}
else {
- if (IS(pp->arg[i].reg, "ecx"))
+ const char *reg = pp->arg[i].reg;
+ if (pp->arg[i].type.is_retreg) {
+ reg = "ecx";
+ fprintf(f, "\tlea %d(%%esp), %%ecx\n",
+ (sarg_ofs - reg_ofs[i]) * 4);
+ }
+ else if (IS(reg, "ecx"))
// must reload original ecx
fprintf(f, "\tmovl %d(%%esp), %%ecx\n",
(sarg_ofs - 2) * 4);
- fprintf(f, "\tpushl %%%s\n", pp->arg[i].reg);
+ fprintf(f, "\tpushl %%%s\n", reg);
}
sarg_ofs++;
}
fprintf(f, "\tadd $%d,%%esp\n",
(sarg_ofs - (saved_regs + 1)) * 4);
+ // pop retregs
+ if (pp->has_retreg) {
+ for (i = pp->argc - 1; i >= 0; i--) {
+ if (!pp->arg[i].type.is_retreg)
+ continue;
+ if (IS(pp->arg[i].reg, "ecx") && ecx_ofs >= 0) {
+ continue;
+ }
+ if (IS(pp->arg[i].reg, "edx") && edx_ofs >= 0) {
+ continue;
+ }
+ fprintf(f, "\tpopl %%%s\n", pp->arg[i].reg);
+ }
+ }
+
if (!ret64)
fprintf(f, "\tpopl %%edx\n");
fprintf(f, "\tpopl %%ecx\n");
OP_RCR,
OP_ADC,
OP_SBB,
+ OP_BSF,
OP_INC,
OP_DEC,
OP_NEG,
{ "rcr", OP_RCR, 2, 2, OPF_DATA|OPF_FLAGS|OPF_CC },
{ "adc", OP_ADC, 2, 2, OPF_DATA|OPF_FLAGS|OPF_CC },
{ "sbb", OP_SBB, 2, 2, OPF_DATA|OPF_FLAGS|OPF_CC },
+ { "bsf", OP_BSF, 2, 2, OPF_DATA|OPF_FLAGS },
{ "inc", OP_INC, 1, 1, OPF_DATA|OPF_FLAGS },
{ "dec", OP_DEC, 1, 1, OPF_DATA|OPF_FLAGS },
{ "neg", OP_NEG, 1, 1, OPF_DATA|OPF_FLAGS },
return pp;
}
+static int try_resolve_const(int i, const struct parsed_opr *opr,
+ int magic, unsigned int *val)
+{
+ struct label_ref *lr;
+ int ret = 0;
+
+ ops[i].cc_scratch = magic;
+
+ while (1) {
+ if (g_labels[i][0] != 0) {
+ lr = &g_label_refs[i];
+ for (; lr != NULL; lr = lr->next)
+ ret |= try_resolve_const(lr->i, opr, magic, val);
+ if (i > 0 && LAST_OP(i - 1))
+ return ret;
+ }
+
+ i--;
+ if (i < 0)
+ return -1;
+
+ if (ops[i].cc_scratch == magic)
+ return 0;
+ ops[i].cc_scratch = magic;
+
+ if (!(ops[i].flags & OPF_DATA))
+ continue;
+ if (!is_opr_modified(opr, &ops[i]))
+ continue;
+ if (ops[i].op != OP_MOV && ops[i].operand[1].type != OPT_CONST)
+ return -1;
+
+ *val = ops[i].operand[1].val;
+ return 1;
+ }
+}
+
static int collect_call_args_r(struct parsed_op *po, int i,
struct parsed_proto *pp, int *regmask, int *save_arg_vars, int arg,
int magic, int need_op_saving, int may_reuse)
struct parsed_data *pd;
const char *tmpname;
enum parsed_flag_op pfo;
+ unsigned int uval;
int save_arg_vars = 0;
int cond_vars = 0;
int need_tmp_var = 0;
}
fprintf(fout, ")");
}
+ else if (g_func_pp->arg[i].type.is_retreg) {
+ fprintf(fout, "u32 *r_%s", g_func_pp->arg[i].reg);
+ }
else {
fprintf(fout, "%s a%d", g_func_pp->arg[i].type.name, i + 1);
}
if (l)
// not resolved just to single func
pp->is_fptr = 1;
- if (po->operand[0].type == OPT_REG)
+
+ 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));
}
if (po->op == OP_PUSH && po->argnum == 0
- && !(po->flags & OPF_RSAVE))
+ && !(po->flags & OPF_RSAVE) && !g_func_pp->is_userstack)
{
if (po->operand[0].type == OPT_REG)
{
}
}
fprintf(fout, " };\n");
+ had_decl = 1;
}
// declare stack frame, va_arg
- if (g_stack_fsz)
+ if (g_stack_fsz) {
fprintf(fout, " union { u32 d[%d]; u16 w[%d]; u8 b[%d]; } sf;\n",
(g_stack_fsz + 3) / 4, (g_stack_fsz + 1) / 2, g_stack_fsz);
+ had_decl = 1;
+ }
+
+ if (g_func_pp->is_userstack) {
+ fprintf(fout, " u32 fake_sf[1024];\n");
+ fprintf(fout, " u32 *esp = &fake_sf[1024];\n");
+ had_decl = 1;
+ }
- if (g_func_pp->is_vararg)
+ if (g_func_pp->is_vararg) {
fprintf(fout, " va_list ap;\n");
+ had_decl = 1;
+ }
// declare arg-registers
for (i = 0; i < g_func_pp->argc; i++) {
reg = char_array_i(regs_r32,
ARRAY_SIZE(regs_r32), g_func_pp->arg[i].reg);
if (regmask & (1 << reg)) {
- fprintf(fout, " u32 %s = (u32)a%d;\n",
- g_func_pp->arg[i].reg, i + 1);
+ if (g_func_pp->arg[i].type.is_retreg)
+ fprintf(fout, " u32 %s = *r_%s;\n",
+ g_func_pp->arg[i].reg, g_func_pp->arg[i].reg);
+ else
+ fprintf(fout, " u32 %s = (u32)a%d;\n",
+ g_func_pp->arg[i].reg, i + 1);
}
- else
+ else {
+ if (g_func_pp->arg[i].type.is_retreg)
+ ferr(ops, "retreg '%s' is unused?\n",
+ g_func_pp->arg[i].reg);
fprintf(fout, " // %s = a%d; // unused\n",
g_func_pp->arg[i].reg, i + 1);
+ }
had_decl = 1;
}
}
pfomask = po->pfomask;
if (po->flags & (OPF_REPZ|OPF_REPNZ)) {
- // we need initial flags for ecx=0 case..
- if (i > 0 && ops[i - 1].op == OP_XOR
- && IS(ops[i - 1].operand[0].name,
- ops[i - 1].operand[1].name))
- {
- fprintf(fout, " cond_z = ");
- if (pfomask & (1 << PFO_C))
- fprintf(fout, "cond_c = ");
- fprintf(fout, "0;\n");
- }
- else if (last_arith_dst != NULL) {
- out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
- out_test_for_cc(buf1, sizeof(buf1), po, PFO_Z, 0,
- last_arith_dst->lmod, buf3);
- fprintf(fout, " cond_z = %s;\n", buf1);
+ struct parsed_opr opr = {0,};
+ opr.type = OPT_REG;
+ opr.reg = xCX;
+ opr.lmod = OPLM_DWORD;
+ ret = try_resolve_const(i, &opr, opcnt * 7 + i, &uval);
+
+ if (ret != 1 || uval == 0) {
+ // we need initial flags for ecx=0 case..
+ if (i > 0 && ops[i - 1].op == OP_XOR
+ && IS(ops[i - 1].operand[0].name,
+ ops[i - 1].operand[1].name))
+ {
+ fprintf(fout, " cond_z = ");
+ if (pfomask & (1 << PFO_C))
+ fprintf(fout, "cond_c = ");
+ fprintf(fout, "0;\n");
+ }
+ else if (last_arith_dst != NULL) {
+ out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
+ out_test_for_cc(buf1, sizeof(buf1), po, PFO_Z, 0,
+ last_arith_dst->lmod, buf3);
+ fprintf(fout, " cond_z = %s;\n", buf1);
+ }
+ else
+ ferr(po, "missing initial ZF\n");
}
- else
- ferr(po, "missing initial ZF\n");
}
switch (po->op)
l = (po->flags & OPF_DF) ? '-' : '+';
if (po->flags & OPF_REP) {
fprintf(fout,
- " for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d) {\n",
- l, j, l, j);
+ " for (; ecx != 0; ecx--) {\n");
if (pfomask & (1 << PFO_C)) {
// ugh..
fprintf(fout,
pfomask &= ~(1 << PFO_C);
}
fprintf(fout,
- " if ((cond_z = (%sedi == %sesi)) %s 0)\n",
- buf1, buf1, (po->flags & OPF_REPZ) ? "==" : "!=");
+ " cond_z = (%sedi == %sesi); edi %c= %d, esi %c= %d;\n",
+ buf1, buf1, l, j, l, j);
+ fprintf(fout,
+ " if (cond_z %s 0) break;\n",
+ (po->flags & OPF_REPZ) ? "==" : "!=");
fprintf(fout,
- " break;\n"
" }");
snprintf(g_comment, sizeof(g_comment), "rep%s cmps",
(po->flags & OPF_REPZ) ? "e" : "ne");
l = (po->flags & OPF_DF) ? '-' : '+';
if (po->flags & OPF_REP) {
fprintf(fout,
- " for (; ecx != 0; ecx--, edi %c= %d)\n", l, j);
+ " for (; ecx != 0; ecx--) {\n");
fprintf(fout,
- " if ((cond_z = (%seax == %sedi)) %s 0)\n",
+ " cond_z = (%seax == %sedi); edi %c= %d;\n",
lmod_cast_u(po, po->operand[0].lmod),
- lmod_cast_u_ptr(po, po->operand[0].lmod),
+ lmod_cast_u_ptr(po, po->operand[0].lmod), l, j);
+ fprintf(fout,
+ " if (cond_z %s 0) break;\n",
(po->flags & OPF_REPZ) ? "==" : "!=");
fprintf(fout,
- " break;");
+ " }");
snprintf(g_comment, sizeof(g_comment), "rep%s scas",
(po->flags & OPF_REPZ) ? "e" : "ne");
}
else {
- fprintf(fout, " cond_z = (%seax = %sedi); edi %c= %d;",
+ fprintf(fout, " cond_z = (%seax = %sedi); edi %c= %d;",
lmod_cast_u(po, po->operand[0].lmod),
lmod_cast_u_ptr(po, po->operand[0].lmod), l, j);
strcpy(g_comment, "scas");
delayed_flag_op = NULL;
break;
+ case OP_BSF:
+ assert_operand_cnt(2);
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
+ fprintf(fout, " %s = %s ? __builtin_ffs(%s) - 1 : 0;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ buf2, buf2);
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ strcat(g_comment, "bsf");
+ break;
+
case OP_INC:
case OP_DEC:
out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
|| (pp->argc_stack > 0
&& pp->is_stdcall != g_func_pp->is_stdcall))
ferr(po, "incompatible tailcall\n");
+ if (g_func_pp->has_retreg)
+ ferr(po, "TODO: retreg+tailcall\n");
for (arg = j = 0; arg < pp->argc; arg++) {
if (arg > 0)
pp->arg[arg].type.name);
if (pp->arg[arg].reg != NULL) {
- fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
+ if (pp->arg[arg].type.is_retreg)
+ fprintf(fout, "&%s", pp->arg[arg].reg);
+ else
+ fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
continue;
}
case OP_RET:
if (g_func_pp->is_vararg)
fprintf(fout, " va_end(ap);\n");
+ if (g_func_pp->has_retreg) {
+ for (arg = 0; arg < g_func_pp->argc; arg++)
+ if (g_func_pp->arg[arg].type.is_retreg)
+ fprintf(fout, " *r_%s = %s;\n",
+ g_func_pp->arg[arg].reg, g_func_pp->arg[arg].reg);
+ }
if (IS(g_func_pp->ret_type.name, "void")) {
if (i != opcnt - 1 || label_pending)
break;
case OP_PUSH:
+ out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
if (po->argnum != 0) {
// special case - saved func arg
- out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
fprintf(fout, " s_a%d = %s;", po->argnum, buf1);
break;
}
else if (po->flags & OPF_RSAVE) {
- out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
fprintf(fout, " s_%s = %s;", buf1, buf1);
break;
}
+ else if (g_func_pp->is_userstack) {
+ fprintf(fout, " *(--esp) = %s;", buf1);
+ break;
+ }
if (!(g_ida_func_attr & IDAFA_NORETURN))
ferr(po, "stray push encountered\n");
no_output = 1;
po->operand[0].is_ptr ? "(void *)" : "", 0));
break;
}
- ferr(po, "stray pop encountered\n");
+ else if (g_func_pp->is_userstack) {
+ fprintf(fout, " %s = *esp++;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
+ break;
+ }
+ else
+ ferr(po, "stray pop encountered\n");
break;
case OP_NOP:
int skip_warned = 0;
int eq_alloc;
int verbose = 0;
+ int multi_seg = 0;
int arg_out;
int arg;
int pi = 0;
verbose = 1;
else if (IS(argv[arg], "-rf"))
g_allow_regfunc = 1;
+ else if (IS(argv[arg], "-m"))
+ multi_seg = 1;
else
break;
}
if (argc < arg + 3) {
- printf("usage:\n%s [-v] [-rf] <.c> <.asm> <hdrf> [rlist]*\n",
+ printf("usage:\n%s [-v] [-rf] [-m] <.c> <.asm> <hdrf> [rlist]*\n",
argv[0]);
return 1;
}
continue;
}
- if (wordc == 2 && IS(words[1], "ends"))
- break;
+ if (wordc == 2 && IS(words[1], "ends")) {
+ if (!multi_seg)
+ break;
+
+ // scan for next text segment
+ while (fgets(line, sizeof(line), fasm)) {
+ asmln++;
+ p = sskip(line);
+ if (*p == 0 || *p == ';')
+ continue;
+
+ if (strstr(p, "segment para public 'CODE' use32"))
+ break;
+ }
+
+ continue;
+ }
p = strchr(words[0], ':');
if (p != NULL) {