#include "masm_tools.h"
enum op_flags {
- OPF_RMD = (1 << 0), /* removed or optimized out */
+ OPF_RMD = (1 << 0), /* removed from code generation */
OPF_DATA = (1 << 1), /* data processing - writes to dst opr */
OPF_FLAGS = (1 << 2), /* sets flags */
OPF_JMP = (1 << 3), /* branch, call */
OPF_32BIT = (1 << 16), /* 32bit division */
OPF_LOCK = (1 << 17), /* op has lock prefix */
OPF_VAPUSH = (1 << 18), /* vararg ptr push (as call arg) */
+ OPF_DONE = (1 << 19), /* already fully handled by analysis */
};
enum op_op {
printf("%s:%d: note: [%s] '%s': " fmt, asmfn, (op_)->asmln, g_func, \
dump_op(op_), ##__VA_ARGS__)
+#define ferr_assert(op_, cond) do { \
+ if (!(cond)) ferr(op_, "assertion '%s' failed on ln :%d\n", #cond, \
+ __LINE__); \
+} while (0)
+
const char *regs_r32[] = {
"eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp",
// not r32, but list here for easy parsing and printing
&& op->operand[0].reg == op->operand[1].reg
&& IS(op->operand[0].name, op->operand[1].name)) // ! ah, al..
{
- op->flags |= OPF_RMD;
+ op->flags |= OPF_RMD | OPF_DONE;
op->regmask_src = op->regmask_dst = 0;
}
break;
char buf[16];
snprintf(buf, sizeof(buf), "%s+0", op->operand[0].name);
if (IS(buf, op->operand[1].name))
- op->flags |= OPF_RMD;
+ op->flags |= OPF_RMD | OPF_DONE;
}
break;
// fptrs must use 32bit args, callsite might have no information and
// lack a cast to smaller types, which results in incorrectly masked
// args passed (callee may assume masked args, it does on ARM)
- if (!pp->is_oslib) {
+ if (!pp->is_osinc) {
for (i = 0; i < pp->argc; i++) {
ret = guess_lmod_from_c_type(&tmp_lmod, &pp->arg[i].type);
if (ret && tmp_lmod != OPLM_DWORD)
return -1; // deadend
}
- if ((po->flags & OPF_RMD)
+ if ((po->flags & (OPF_RMD|OPF_DONE))
|| (po->op == OP_PUSH && po->p_argnum != 0)) // arg push
continue;
continue;
for (j = i - 1; j >= 0; j--) {
- if (ops[j].flags & OPF_RMD)
+ if (ops[j].flags & (OPF_RMD|OPF_DONE))
continue;
if (ops[j].flags & OPF_JMP)
return -1;
- if (ops[j].op == OP_POP && ops[j].operand[0].type == OPT_REG
+ if (ops[j].op == OP_POP && ops[j].datap == NULL
+ && ops[j].operand[0].type == OPT_REG
&& IS(ops[j].operand[0].name, reg))
{
found = 1;
return found ? 0 : -1;
}
-// XXX: merge with scan_for_pop?
static void scan_for_pop_const(int i, int opcnt)
{
int j;
break;
}
- if (!(ops[j].flags & OPF_RMD) && ops[j].op == OP_POP)
+ if (ops[j].op == OP_POP && !(ops[j].flags & (OPF_RMD|OPF_DONE)))
{
- ops[i].flags |= OPF_RMD;
+ ops[i].flags |= OPF_RMD | OPF_DONE;
+ ops[j].flags |= OPF_DONE;
ops[j].datap = &ops[i];
break;
}
break;
if (po->op == OP_CLD) {
- po->flags |= OPF_RMD;
+ po->flags |= OPF_RMD | OPF_DONE;
return;
}
}
*adj = *multipath = 0;
for (; i < opcnt && *adj < adj_expect; i++) {
- po = &ops[i];
-
if (g_labels[i] != NULL)
*multipath = 1;
+ po = &ops[i];
+ if (po->flags & OPF_DONE)
+ continue;
+
if (po->op == OP_ADD && po->operand[0].reg == xSP) {
if (po->operand[1].type != OPT_CONST)
ferr(&ops[i], "non-const esp adjust?\n");
ferr(&ops[i], "unaligned esp adjust: %x\n", *adj);
return i;
}
- else if (po->op == OP_PUSH && !(po->flags & OPF_RMD)) {
+ else if (po->op == OP_PUSH) {
//if (first_pop == -1)
// first_pop = -2; // none
*adj -= lmod_bytes(po, po->operand[0].lmod);
}
- else if (po->op == OP_POP && !(po->flags & OPF_RMD)) {
+ else if (po->op == OP_POP) {
// seems like msvc only uses 'pop ecx' for stack realignment..
if (po->operand[0].type != OPT_REG || po->operand[0].reg != xCX)
break;
}
else if (po->flags & (OPF_JMP|OPF_TAIL)) {
if (po->op == OP_JMP && po->btj == NULL) {
+ if (po->bt_i <= i)
+ break;
i = po->bt_i - 1;
continue;
}
break;
if (po->operand[0].type != OPT_LABEL)
break;
- if (po->pp != NULL && po->pp->is_stdcall)
+ if (po->pp == NULL || po->pp->is_stdcall)
break;
}
}
&& IS(opr_name(&ops[1], 1), "esp"))
{
g_bp_frame = 1;
- ops[0].flags |= OPF_RMD;
- ops[1].flags |= OPF_RMD;
+ ops[0].flags |= OPF_RMD | OPF_DONE;
+ ops[1].flags |= OPF_RMD | OPF_DONE;
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;
+ ops[2].flags |= OPF_RMD | OPF_DONE;
i++;
}
else {
i = 2;
while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
g_stack_fsz += 4;
- ops[i].flags |= OPF_RMD;
+ ops[i].flags |= OPF_RMD | OPF_DONE;
ecx_push++;
i++;
}
&& IS(opr_name(&ops[i + 1], 0), "__alloca_probe"))
{
g_stack_fsz += ops[i].operand[1].val;
- ops[i].flags |= OPF_RMD;
+ ops[i].flags |= OPF_RMD | OPF_DONE;
i++;
- ops[i].flags |= OPF_RMD;
+ ops[i].flags |= OPF_RMD | OPF_DONE;
i++;
}
}
if ((ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ebp"))
|| ops[j].op == OP_LEAVE)
{
- ops[j].flags |= OPF_RMD;
+ ops[j].flags |= OPF_RMD | OPF_DONE;
}
else if (!(g_ida_func_attr & IDAFA_NORETURN))
ferr(&ops[j], "'pop ebp' expected\n");
&& IS(opr_name(&ops[j - 1], 0), "esp")
&& IS(opr_name(&ops[j - 1], 1), "ebp"))
{
- ops[j - 1].flags |= OPF_RMD;
+ ops[j - 1].flags |= OPF_RMD | OPF_DONE;
}
else if (ops[j].op != OP_LEAVE
&& !(g_ida_func_attr & IDAFA_NORETURN))
// non-bp frame
i = 0;
while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
- ops[i].flags |= OPF_RMD;
+ ops[i].flags |= OPF_RMD | OPF_DONE;
g_stack_fsz += 4;
ecx_push++;
i++;
&& ops[i].operand[1].type == OPT_CONST)
{
g_stack_fsz = ops[i].operand[1].val;
- ops[i].flags |= OPF_RMD;
+ ops[i].flags |= OPF_RMD | OPF_DONE;
esp_sub = 1;
break;
}
while (i > 0 && j > 0) {
i--;
if (ops[i].op == OP_PUSH) {
- ops[i].flags &= ~OPF_RMD;
+ ops[i].flags &= ~(OPF_RMD | OPF_DONE);
j--;
}
}
&& ops[j].operand[1].type == OPT_CONST)
{
/* add esp, N */
- ecx_push -= ops[j].operand[1].val / 4 - 1;
+ l += ops[j].operand[1].val / 4 - 1;
}
else
ferr(&ops[j], "'pop ecx' expected\n");
- ops[j].flags |= OPF_RMD;
+ ops[j].flags |= OPF_RMD | OPF_DONE;
j--;
}
if (l != ecx_push)
|| ops[j].operand[1].val != g_stack_fsz)
ferr(&ops[j], "'add esp' expected\n");
- ops[j].flags |= OPF_RMD;
+ ops[j].flags |= OPF_RMD | OPF_DONE;
ops[j].operand[1].val = 0; // hack for stack arg scanner
found = 1;
}
return -1;
}
+static struct parsed_proto *process_call_early(int i, int opcnt,
+ int *adj_i)
+{
+ struct parsed_op *po = &ops[i];
+ struct parsed_proto *pp;
+ int multipath = 0;
+ int adj = 0;
+ int ret;
+
+ pp = po->pp;
+ if (pp == NULL || pp->is_vararg || pp->argc_reg != 0)
+ // leave for later
+ return NULL;
+
+ // look for and make use of esp adjust
+ *adj_i = ret = -1;
+ if (!pp->is_stdcall && pp->argc_stack > 0)
+ ret = scan_for_esp_adjust(i + 1, opcnt,
+ pp->argc_stack * 4, &adj, &multipath);
+ if (ret >= 0) {
+ if (pp->argc_stack > adj / 4)
+ return NULL;
+ if (multipath)
+ return NULL;
+ if (ops[ret].op == OP_POP && adj != 4)
+ return NULL;
+ }
+
+ *adj_i = ret;
+ return pp;
+}
+
+static void patch_esp_adjust(struct parsed_op *po, int adj)
+{
+ ferr_assert(po, po->op == OP_ADD);
+ ferr_assert(po, IS(opr_name(po, 0), "esp"));
+ ferr_assert(po, po->operand[1].type == OPT_CONST);
+
+ // this is a bit of a hack, but deals with use of
+ // single adj for multiple calls
+ po->operand[1].val -= adj;
+ po->flags |= OPF_RMD;
+ if (po->operand[1].val == 0)
+ po->flags |= OPF_DONE;
+ ferr_assert(po, (int)po->operand[1].val >= 0);
+}
+
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 adj = 0, multipath = 0;
int ret, arg;
tmpname = opr_name(po, 0);
if (pp == NULL)
{
// indirect call
- pp_c = resolve_icall(i, opcnt, &l);
+ pp_c = resolve_icall(i, opcnt, &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);
pp = proto_clone(pp_c);
my_assert_not(pp, NULL);
- if (l)
+ if (multipath)
// not resolved just to single func
pp->is_fptr = 1;
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) {
+ ret = scan_for_esp_adjust(i + 1, opcnt, ~0, &adj, &multipath);
+ if (ret < 0 || adj < 0) {
if (!g_allow_regfunc)
ferr(po, "non-__cdecl indirect call unhandled yet\n");
pp->is_unresolved = 1;
- j = 0;
+ adj = 0;
}
- j /= 4;
- if (j > ARRAY_SIZE(pp->arg))
- ferr(po, "esp adjust too large: %d\n", j);
+ adj /= 4;
+ if (adj > ARRAY_SIZE(pp->arg))
+ ferr(po, "esp adjust too large: %d\n", adj);
pp->ret_type.name = strdup("int");
- pp->argc = pp->argc_stack = j;
+ pp->argc = pp->argc_stack = adj;
for (arg = 0; arg < pp->argc; arg++)
pp->arg[arg].type.name = strdup("int");
}
ret = -1;
if (!pp->is_stdcall && pp->argc_stack > 0)
ret = scan_for_esp_adjust(i + 1, opcnt,
- pp->argc_stack * 4, &j, &l);
+ pp->argc_stack * 4, &adj, &multipath);
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);
+ if (adj / 4 < pp->argc_stack) {
+ fnote(po, "(this call)\n");
+ ferr(&ops[ret], "esp adjust is too small: %x < %x\n",
+ adj, pp->argc_stack * 4);
+ }
// modify pp to make it have varargs as normal args
arg = pp->argc;
- pp->argc += j / 4 - pp->argc_stack;
+ pp->argc += adj / 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) {
+ if (pp->argc_stack > adj / 4) {
fnote(po, "(this call)\n");
ferr(&ops[ret], "stack tracking failed for '%s': %x %x\n",
- tmpname, pp->argc_stack * 4, j);
+ tmpname, pp->argc_stack * 4, adj);
}
ops[ret].flags |= OPF_RMD;
if (ops[ret].op == OP_POP) {
- if (j > 4) {
+ if (adj > 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--;
+ adj = pp->argc_stack;
+ while (ops[ret].op == OP_POP && adj > 0 && ret < opcnt) {
+ ops[ret].flags |= OPF_RMD | OPF_DONE;
+ adj--;
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 (!multipath)
+ patch_esp_adjust(&ops[ret], pp->argc_stack * 4);
}
else if (pp->is_vararg)
ferr(po, "missing esp_adjust for vararg func '%s'\n",
return pp;
}
+static int collect_call_args_early(struct parsed_op *po, int i,
+ struct parsed_proto *pp, int *regmask)
+{
+ int arg, ret;
+ int j;
+
+ for (arg = 0; arg < pp->argc; arg++)
+ if (pp->arg[arg].reg == NULL)
+ break;
+
+ // first see if it can be easily done
+ for (j = i; j > 0 && arg < pp->argc; )
+ {
+ if (g_labels[j] != NULL)
+ return -1;
+ j--;
+
+ if (ops[j].op == OP_CALL)
+ return -1;
+ else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP)
+ return -1;
+ else if (ops[j].op == OP_POP)
+ return -1;
+ else if (ops[j].flags & OPF_CJMP)
+ return -1;
+ else if (ops[j].op == OP_PUSH) {
+ if (ops[j].flags & (OPF_FARG|OPF_FARGNR))
+ return -1;
+ ret = scan_for_mod(&ops[j], j + 1, i, 1);
+ if (ret >= 0)
+ return -1;
+
+ if (pp->arg[arg].type.is_va_list)
+ return -1;
+
+ // next arg
+ for (arg++; arg < pp->argc; arg++)
+ if (pp->arg[arg].reg == NULL)
+ break;
+ }
+ }
+
+ if (arg < pp->argc)
+ return -1;
+
+ // now do it
+ for (arg = 0; arg < pp->argc; arg++)
+ if (pp->arg[arg].reg == NULL)
+ break;
+
+ for (j = i; j > 0 && arg < pp->argc; )
+ {
+ j--;
+
+ if (ops[j].op == OP_PUSH)
+ {
+ ops[j].p_argnext = -1;
+ ferr_assert(&ops[j], pp->arg[arg].datap == NULL);
+ pp->arg[arg].datap = &ops[j];
+
+ if (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;
+
+ // next arg
+ for (arg++; arg < pp->argc; arg++)
+ if (pp->arg[arg].reg == NULL)
+ break;
+ }
+ }
+
+ return 0;
+}
+
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 && !(ops[j].flags & OPF_RMD)
- && ops[j].datap == NULL)
+ else if (ops[j].op == OP_POP && !(ops[j].flags & OPF_DONE))
{
if (pp->is_unresolved)
break;
if (!g_func_pp->is_vararg
|| strstr(ops[k].operand[1].name, buf))
{
- ops[k].flags |= OPF_RMD;
+ ops[k].flags |= OPF_RMD | OPF_DONE;
ops[j].flags |= OPF_RMD | OPF_VAPUSH;
save_args &= ~(1 << arg);
reg = -1;
ret = stack_frame_access(&ops[k], &ops[k].operand[1],
buf, sizeof(buf), ops[k].operand[1].name, "", 1, 0);
if (ret >= 0) {
- ops[k].flags |= OPF_RMD;
+ ops[k].flags |= OPF_RMD | OPF_DONE;
ops[j].flags |= OPF_RMD;
ops[j].p_argpass = ret + 1;
save_args &= ~(1 << arg);
po->bt_i = -1;
po->btj = NULL;
- if (po->flags & OPF_RMD)
+ if (po->flags & (OPF_RMD|OPF_DONE))
continue;
if (po->op == OP_CALL) {
{
if (l == i + 1 && po->op == OP_JMP) {
// yet another alignment type..
- po->flags |= OPF_RMD;
+ po->flags |= OPF_RMD|OPF_DONE;
break;
}
add_label_ref(&g_label_refs[l], i);
// pass3:
// - remove dead labels
- // - process calls
+ // - process trivial calls
for (i = 0; i < opcnt; i++)
{
if (g_labels[i] != NULL && g_label_refs[i].i == -1) {
}
po = &ops[i];
- if (po->flags & OPF_RMD)
+ if (po->flags & (OPF_RMD|OPF_DONE))
continue;
if (po->op == OP_CALL)
+ {
+ pp = process_call_early(i, opcnt, &j);
+ if (pp != NULL) {
+ if (!(po->flags & OPF_ATAIL))
+ // since we know the args, try to collect them
+ if (collect_call_args_early(po, i, pp, ®mask) != 0)
+ pp = NULL;
+ }
+
+ if (pp != NULL) {
+ if (j >= 0) {
+ // commit esp adjust
+ ops[j].flags |= OPF_RMD;
+ if (ops[j].op != OP_POP)
+ patch_esp_adjust(&ops[j], pp->argc_stack * 4);
+ else
+ ops[j].flags |= OPF_DONE;
+ }
+
+ if (strstr(pp->ret_type.name, "int64"))
+ need_tmp64 = 1;
+
+ po->flags |= OPF_DONE;
+ }
+ }
+ }
+
+ // pass4:
+ // - process calls
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+
+ if (po->op == OP_CALL && !(po->flags & OPF_DONE))
{
pp = process_call(i, opcnt);
}
}
- // pass4:
+ // pass5:
// - find POPs for PUSHes, rm both
// - scan for STD/CLD, propagate DF
// - scan for all used registers
// - find flag set ops for their users
// - do unreselved calls
// - declare indirect functions
- for (i = 0; i < opcnt; i++) {
+ for (i = 0; i < opcnt; i++)
+ {
po = &ops[i];
- if (po->flags & OPF_RMD)
+ if (po->flags & (OPF_RMD|OPF_DONE))
continue;
if (po->op == OP_PUSH && (po->flags & OPF_RSAVE)) {
}
if (po->op == OP_STD) {
- po->flags |= OPF_DF | OPF_RMD;
+ po->flags |= OPF_DF | OPF_RMD | OPF_DONE;
scan_propagate_df(i + 1, opcnt);
}
need_tmp64 = 1;
}
else if (po->op == OP_CALL) {
+ // note: resolved non-reg calls are OPF_DONE already
pp = po->pp;
if (pp == NULL)
ferr(po, "NULL pp\n");
need_tmp64 = 1;
}
else if (po->op == OP_CLD)
- po->flags |= OPF_RMD;
+ po->flags |= OPF_RMD | OPF_DONE;
if (po->op == OP_RCL || po->op == OP_RCR || po->op == OP_XCHG) {
need_tmp_var = 1;
}
}
- // pass4:
+ // pass6:
// - confirm regmask_save, it might have been reduced
if (regmask_save != 0)
{
static struct func_prototype *hg_fp;
static int hg_fp_cnt;
+static struct scanned_var {
+ char name[NAMELEN];
+ enum opr_lenmod lmod;
+ unsigned int is_seeded:1;
+ unsigned int is_c_str:1;
+} *hg_vars;
+static int hg_var_cnt;
+
static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
int count);
static void gen_hdr(const char *funcn, int opcnt)
{
+ int save_arg_vars[MAX_ARG_GRP] = { 0, };
const struct parsed_proto *pp_c;
struct parsed_proto *pp;
struct func_prototype *fp;
struct parsed_data *pd;
struct parsed_op *po;
const char *tmpname;
+ int regmask_dummy = 0;
int regmask_save = 0;
int regmask_dst = 0;
int regmask_dep = 0;
po->bt_i = -1;
po->btj = NULL;
- if (po->flags & OPF_RMD)
+ if (po->flags & (OPF_RMD|OPF_DONE))
continue;
if (po->op == OP_CALL) {
// pass3:
// - remove dead labels
- // - process calls
+ // - process trivial calls
// - handle push <const>/pop pairs
for (i = 0; i < opcnt; i++)
{
}
po = &ops[i];
- if (po->flags & OPF_RMD)
+ if (po->flags & (OPF_RMD|OPF_DONE))
continue;
- if (po->op == OP_CALL) {
- pp = process_call(i, opcnt);
+ if (po->op == OP_CALL)
+ {
+ pp = process_call_early(i, opcnt, &j);
+ if (pp != NULL) {
+ if (!(po->flags & OPF_ATAIL))
+ // since we know the args, try to collect them
+ if (collect_call_args_early(po, i, pp, ®mask_dummy) != 0)
+ pp = NULL;
+ }
- 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);
+ if (pp != NULL) {
+ if (j >= 0) {
+ // commit esp adjust
+ ops[j].flags |= OPF_RMD;
+ if (ops[j].op != OP_POP)
+ patch_esp_adjust(&ops[j], pp->argc_stack * 4);
+ else
+ ops[j].flags |= OPF_DONE;
+ }
+
+ po->flags |= OPF_DONE;
}
}
else if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST) {
}
// pass4:
- // - track saved regs
+ // - track saved regs (simple)
+ // - process calls
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+
+ if (po->op == OP_PUSH && po->operand[0].type == OPT_REG)
+ {
+ ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, 0);
+ if (ret == 0) {
+ regmask_save |= 1 << po->operand[0].reg;
+ po->flags |= OPF_RMD;
+ scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, OPF_RMD);
+ }
+ }
+ else if (po->op == OP_CALL && !(po->flags & OPF_DONE))
+ {
+ pp = process_call(i, opcnt);
+
+ if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
+ // since we know the args, collect them
+ collect_call_args(po, i, pp, ®mask_dummy, save_arg_vars,
+ i + opcnt * 1);
+ }
+ }
+ }
+
+ // pass5:
+ // - track saved regs (part 2)
// - try to figure out arg-regs
+ // - calculate reg deps
for (i = 0; i < opcnt; i++)
{
po = &ops[i];
/* (just calculate register deps) */;
else if (po->flags & OPF_RMD)
continue;
- else if (po->op == OP_PUSH && po->operand[0].type == OPT_REG)
+ else if (po->op == OP_PUSH && po->operand[0].type == OPT_REG
+ && !(po->flags & OPF_DONE))
{
reg = po->operand[0].reg;
if (reg < 0)
depth = 0;
ret = scan_for_pop(i + 1, opcnt,
- po->operand[0].name, i + opcnt * 1, 0, &depth, 0);
+ po->operand[0].name, i + opcnt * 2, 0, &depth, 0);
if (ret == 1) {
regmask_save |= 1 << reg;
po->flags |= OPF_RMD;
scan_for_pop(i + 1, opcnt,
- po->operand[0].name, i + opcnt * 2, 0, &depth, 1);
- continue;
- }
- ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, 0);
- if (ret == 0) {
- regmask_save |= 1 << reg;
- po->flags |= OPF_RMD;
- scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, OPF_RMD);
+ po->operand[0].name, i + opcnt * 3, 0, &depth, 1);
continue;
}
}
opr.reg = xAX;
j = -1;
from_caller = 0;
- ret = resolve_origin(i, &opr, i + opcnt * 3, &j, &from_caller);
+ ret = resolve_origin(i, &opr, i + opcnt * 4, &j, &from_caller);
}
if (ret == -1 && from_caller) {
fp->regmask_dep = regmask_dep & ~(1 << xSP);
fp->has_ret = has_ret;
+ // output_hdr_fp(stdout, fp, 1);
gen_x_cleanup(opcnt);
}
static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
int count)
{
- char *p, buf[NAMELEN];
- const char *cp;
+ const struct parsed_proto *pp;
+ char *p, namebuf[NAMELEN];
+ const char *name;
int regmask_dep;
int argc_stack;
int j, arg;
fprintf(fout, "\n");
#endif
+ p = strchr(fp->name, '@');
+ if (p != NULL) {
+ memcpy(namebuf, fp->name, p - fp->name);
+ namebuf[p - fp->name] = 0;
+ name = namebuf;
+ }
+ else
+ name = fp->name;
+ if (name[0] == '_')
+ name++;
+
+ pp = proto_parse(g_fhdr, name, 1);
+ if (pp != NULL && pp->is_include)
+ continue;
+
regmask_dep = fp->regmask_dep;
argc_stack = fp->argc_stack;
else
fprintf(fout, " __cdecl ");
- 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);
+ fprintf(fout, "%s(", name);
arg = 0;
for (j = 0; j < xSP; j++) {
static void output_hdr(FILE *fout)
{
+ static const char *lmod_c_names[] = {
+ [OPLM_UNSPEC] = "???",
+ [OPLM_BYTE] = "uint8_t",
+ [OPLM_WORD] = "uint16_t",
+ [OPLM_DWORD] = "uint32_t",
+ [OPLM_QWORD] = "uint64_t",
+ };
+ const struct scanned_var *var;
int i;
// resolve deps
// note: messes up .proto ptr, don't use
//qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_id);
+ // output variables
+ for (i = 0; i < hg_var_cnt; i++) {
+ var = &hg_vars[i];
+
+ if (var->is_c_str)
+ fprintf(fout, "extern %-8s %s[];", "char", var->name);
+ else
+ fprintf(fout, "extern %-8s %s;",
+ lmod_c_names[var->lmod], var->name);
+
+ if (var->is_seeded)
+ fprintf(fout, " // seeded");
+ fprintf(fout, "\n");
+ }
+
+ fprintf(fout, "\n");
+
+ // output function prototypes
output_hdr_fp(fout, hg_fp, hg_fp_cnt);
}
+// read a line, truncating it if it doesn't fit
+static char *my_fgets(char *s, size_t size, FILE *stream)
+{
+ char *ret, *ret2;
+ char buf[64];
+ int p;
+
+ p = size - 2;
+ if (p >= 0)
+ s[p] = 0;
+
+ ret = fgets(s, size, stream);
+ if (ret != NULL && p >= 0 && s[p] != 0 && s[p] != '\n') {
+ p = sizeof(buf) - 2;
+ do {
+ buf[p] = 0;
+ ret2 = fgets(buf, sizeof(buf), stream);
+ }
+ while (ret2 != NULL && buf[p] != 0 && buf[p] != '\n');
+ }
+
+ return ret;
+}
+
+// '=' needs special treatment
+// also ' quote
+static char *next_word_s(char *w, size_t wsize, char *s)
+{
+ size_t i;
+
+ s = sskip(s);
+
+ i = 0;
+ if (*s == '\'') {
+ w[0] = s[0];
+ for (i = 1; i < wsize - 1; i++) {
+ if (s[i] == 0) {
+ printf("warning: missing closing quote: \"%s\"\n", s);
+ break;
+ }
+ if (s[i] == '\'')
+ break;
+ w[i] = s[i];
+ }
+ }
+
+ for (; i < wsize - 1; i++) {
+ if (s[i] == 0 || my_isblank(s[i]) || (s[i] == '=' && i > 0))
+ break;
+ w[i] = s[i];
+ }
+ w[i] = 0;
+
+ if (s[i] != 0 && !my_isblank(s[i]) && s[i] != '=')
+ printf("warning: '%s' truncated\n", w);
+
+ return s + i;
+}
+
+static void scan_variables(FILE *fasm)
+{
+ const struct parsed_proto *pp_c;
+ struct scanned_var *var;
+ char line[256] = { 0, };
+ char words[3][256];
+ char *p = NULL;
+ int wordc;
+ int l;
+
+ while (!feof(fasm))
+ {
+ // skip to next data section
+ while (my_fgets(line, sizeof(line), fasm))
+ {
+ asmln++;
+
+ p = sskip(line);
+ if (*p == 0 || *p == ';')
+ continue;
+
+ p = sskip(next_word_s(words[0], sizeof(words[0]), p));
+ if (*p == 0 || *p == ';')
+ continue;
+
+ if (*p != 's' || !IS_START(p, "segment para public"))
+ continue;
+
+ break;
+ }
+
+ if (p == NULL || !IS_START(p, "segment para public"))
+ break;
+ p = sskip(p + 19);
+
+ if (!IS_START(p, "'DATA'"))
+ continue;
+
+ // now process it
+ while (my_fgets(line, sizeof(line), fasm))
+ {
+ asmln++;
+
+ p = line;
+ if (my_isblank(*p))
+ continue;
+
+ p = sskip(p);
+ if (*p == 0 || *p == ';')
+ continue;
+
+ for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) {
+ words[wordc][0] = 0;
+ p = sskip(next_word_s(words[wordc], sizeof(words[0]), p));
+ if (*p == 0 || *p == ';') {
+ wordc++;
+ break;
+ }
+ }
+
+ if (wordc == 2 && IS(words[1], "ends"))
+ break;
+ if (wordc < 2)
+ continue;
+
+ if ((hg_var_cnt & 0xff) == 0) {
+ hg_vars = realloc(hg_vars, sizeof(hg_vars[0])
+ * (hg_var_cnt + 0x100));
+ my_assert_not(hg_vars, NULL);
+ memset(hg_vars + hg_var_cnt, 0, sizeof(hg_vars[0]) * 0x100);
+ }
+
+ var = &hg_vars[hg_var_cnt++];
+ snprintf(var->name, sizeof(var->name), "%s", words[0]);
+
+ // maybe already in seed header?
+ pp_c = proto_parse(g_fhdr, var->name, 1);
+ if (pp_c != NULL) {
+ if (pp_c->is_func)
+ aerr("func?\n");
+ else if (pp_c->is_fptr) {
+ var->lmod = OPLM_DWORD;
+ //var->is_ptr = 1;
+ }
+ else if (!guess_lmod_from_c_type(&var->lmod, &pp_c->type))
+ aerr("unhandled C type '%s' for '%s'\n",
+ pp_c->type.name, var->name);
+
+ var->is_seeded = 1;
+ continue;
+ }
+
+ if (IS(words[1], "dd"))
+ var->lmod = OPLM_DWORD;
+ else if (IS(words[1], "dw"))
+ var->lmod = OPLM_WORD;
+ else if (IS(words[1], "db")) {
+ var->lmod = OPLM_BYTE;
+ if (wordc >= 3 && (l = strlen(words[2])) > 4) {
+ if (words[2][0] == '\'' && IS(words[2] + l - 2, ",0"))
+ var->is_c_str = 1;
+ }
+ }
+ else if (IS(words[1], "dq"))
+ var->lmod = OPLM_QWORD;
+ //else if (IS(words[1], "dt"))
+ else
+ aerr("type '%s' not known\n", words[1]);
+ }
+ }
+
+ rewind(fasm);
+ asmln = 0;
+}
+
static void set_label(int i, const char *name)
{
const char *p;
g_labels[i][len] = 0;
}
-// '=' needs special treatment..
-static char *next_word_s(char *w, size_t wsize, char *s)
-{
- size_t i;
-
- s = sskip(s);
-
- for (i = 0; i < wsize - 1; i++) {
- if (s[i] == 0 || my_isblank(s[i]) || (s[i] == '=' && i > 0))
- break;
- w[i] = s[i];
- }
- w[i] = 0;
-
- if (s[i] != 0 && !my_isblank(s[i]) && s[i] != '=')
- printf("warning: '%s' truncated\n", w);
-
- return s + i;
-}
-
struct chunk_item {
char *name;
long fptr;
oldpos = ftell(fasm);
oldasmln = asmln;
- while (fgets(line, sizeof(line), fasm))
+ while (my_fgets(line, sizeof(line), fasm))
{
wordc = 0;
asmln++;
frlist = fopen(argv[arg], "r");
my_assert_not(frlist, NULL);
- while (fgets(line, sizeof(line), frlist)) {
+ while (my_fgets(line, sizeof(line), frlist)) {
p = sskip(line);
if (*p == 0 || *p == ';')
continue;
g_label_refs[i].next = NULL;
}
- while (fgets(line, sizeof(line), fasm))
+ if (g_header_mode)
+ scan_variables(fasm);
+
+ while (my_fgets(line, sizeof(line), fasm))
{
wordc = 0;
asmln++;
}
// scan for next text segment
- while (fgets(line, sizeof(line), fasm)) {
+ while (my_fgets(line, sizeof(line), fasm)) {
asmln++;
p = sskip(line);
if (*p == 0 || *p == ';')