exit(1); \
} while (0)
+#include "masm_tools.h"
+
enum op_flags {
OPF_RMD = (1 << 0), /* removed or optimized out */
OPF_DATA = (1 << 1), /* data processing - writes to dst opr */
static char g_comment[256];
static int g_bp_frame;
static int g_sp_frame;
+static int g_stack_frame_used;
static int g_stack_fsz;
static int g_ida_func_attr;
#define ferr(op_, fmt, ...) do { \
return -1;
}
-static unsigned long parse_number(const char *number)
-{
- int len = strlen(number);
- const char *p = number;
- char *endp = NULL;
- unsigned long ret;
- int neg = 0;
- int bad;
-
- if (*p == '-') {
- neg = 1;
- p++;
- }
- if (len > 1 && *p == '0')
- p++;
- if (number[len - 1] == 'h') {
- ret = strtoul(p, &endp, 16);
- bad = (*endp != 'h');
- }
- else {
- ret = strtoul(p, &endp, 10);
- bad = (*endp != 0);
- }
- if (bad)
- aerr("number parsing failed\n");
- if (neg)
- ret = -ret;
- return ret;
-}
-
static int parse_indmode(char *name, int *regmask, int need_c_cvt)
{
enum opr_lenmod lmod;
// most likely var in data segment
opr->type = OPT_LABEL;
- pp = proto_parse(g_fhdr, opr->name);
+ pp = proto_parse(g_fhdr, opr->name, 0);
if (pp != NULL) {
- if (pp->is_fptr) {
+ if (pp->is_fptr || pp->is_func) {
opr->lmod = OPLM_DWORD;
opr->is_ptr = 1;
}
return regs_r32[popr->reg];
}
+// cast1 is the "final" cast
+static const char *simplify_cast(const char *cast1, const char *cast2)
+{
+ static char buf[256];
+
+ if (cast1[0] == 0)
+ return cast2;
+ if (cast2[0] == 0)
+ return cast1;
+ if (IS(cast1, cast2))
+ return cast1;
+ if (IS(cast1, "(s8)") && IS(cast2, "(u8)"))
+ return cast1;
+ if (IS(cast1, "(s16)") && IS(cast2, "(u16)"))
+ return cast1;
+ if (IS(cast1, "(u8)") && IS_START(cast2, "*(u8 *)"))
+ return cast2;
+ if (IS(cast1, "(u16)") && IS_START(cast2, "*(u16 *)"))
+ return cast2;
+ if (strchr(cast1, '*') && IS_START(cast2, "(u32)"))
+ return cast1;
+
+ snprintf(buf, sizeof(buf), "%s%s", cast1, cast2);
+ return buf;
+}
+
+static const char *simplify_cast_num(const char *cast, unsigned int val)
+{
+ if (IS(cast, "(u8)") && val < 0x100)
+ return "";
+ if (IS(cast, "(s8)") && val < 0x80)
+ return "";
+ if (IS(cast, "(u16)") && val < 0x10000)
+ return "";
+ if (IS(cast, "(s16)") && val < 0x8000)
+ return "";
+ if (IS(cast, "(s32)") && val < 0x80000000)
+ return "";
+
+ return cast;
+}
+
static struct parsed_equ *equ_find(struct parsed_op *po, const char *name,
int *extra_offs)
{
return &g_eqs[i];
}
-static void stack_frame_access(struct parsed_op *po,
- struct parsed_opr *popr, char *buf, size_t buf_size,
- const char *name, const char *cast, int is_src, int is_lea)
+static int is_stack_access(struct parsed_op *po,
+ const struct parsed_opr *popr)
{
- enum opr_lenmod tmp_lmod = OPLM_UNSPEC;
- const char *prefix = "";
- const char *bp_arg = NULL;
- char ofs_reg[16] = { 0, };
+ return (parse_stack_el(popr->name, NULL)
+ || (g_bp_frame && !(po->flags & OPF_EBP_S)
+ && IS_START(popr->name, "ebp")));
+}
+
+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)
+{
+ const char *bp_arg = "";
+ const char *p = NULL;
struct parsed_equ *eq;
- const char *p;
char *endp = NULL;
- int i, arg_i, arg_s;
- int unaligned = 0;
int stack_ra = 0;
int offset = 0;
- int sf_ofs;
- int lim;
- if (po->flags & OPF_EBP_S)
- ferr(po, "stack_frame_access while ebp is scratch\n");
+ ofs_reg[0] = 0;
if (IS_START(name, "ebp-")
|| (IS_START(name, "ebp+") && '0' <= name[4] && name[4] <= '9'))
if (!strncmp(name, "ebp", 3))
stack_ra = 4;
- if (stack_ra <= offset && offset < stack_ra + 4)
+ if (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 (bp_arg_out)
+ *bp_arg_out = bp_arg;
+}
+
+static void stack_frame_access(struct parsed_op *po,
+ struct parsed_opr *popr, char *buf, size_t buf_size,
+ const char *name, const char *cast, int is_src, int is_lea)
+{
+ enum opr_lenmod tmp_lmod = OPLM_UNSPEC;
+ const char *prefix = "";
+ const char *bp_arg = NULL;
+ char ofs_reg[16] = { 0, };
+ int i, arg_i, arg_s;
+ int unaligned = 0;
+ int stack_ra = 0;
+ int offset = 0;
+ int sf_ofs;
+ int lim;
+
+ 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);
+
if (offset > stack_ra)
{
arg_i = (offset - stack_ra - 4) / 4;
if (is_lea)
ferr(po, "lea/byte to arg?\n");
if (is_src && (offset & 3) == 0)
- snprintf(buf, buf_size, "(u8)a%d", i + 1);
+ snprintf(buf, buf_size, "%sa%d",
+ simplify_cast(cast, "(u8)"), i + 1);
else
- snprintf(buf, buf_size, "BYTE%d(a%d)", offset & 3, i + 1);
+ snprintf(buf, buf_size, "%sBYTE%d(a%d)",
+ cast, offset & 3, i + 1);
break;
case OPLM_WORD:
if (!is_src) {
if (offset & 2)
ferr(po, "problematic arg store\n");
- snprintf(buf, buf_size, "*(u16 *)((char *)&a%d + 1)", i + 1);
+ snprintf(buf, buf_size, "%s((char *)&a%d + 1)",
+ simplify_cast(cast, "*(u16 *)"), i + 1);
}
else
ferr(po, "unaligned arg word load\n");
}
else if (is_src && (offset & 2) == 0)
- snprintf(buf, buf_size, "(u16)a%d", i + 1);
+ snprintf(buf, buf_size, "%sa%d",
+ simplify_cast(cast, "(u16)"), i + 1);
else
- snprintf(buf, buf_size, "%sWORD(a%d)",
- (offset & 2) ? "HI" : "LO", i + 1);
+ snprintf(buf, buf_size, "%s%sWORD(a%d)",
+ cast, (offset & 2) ? "HI" : "LO", i + 1);
break;
case OPLM_DWORD:
// common problem
guess_lmod_from_c_type(&tmp_lmod, &g_func_pp->arg[i].type);
- if (unaligned && tmp_lmod != OPLM_DWORD)
- ferr(po, "bp_arg arg/w offset %d and type '%s'\n",
- offset, g_func_pp->arg[i].type.name);
+ if (tmp_lmod != OPLM_DWORD
+ && (unaligned || (!is_src && tmp_lmod < popr->lmod)))
+ {
+ ferr(po, "bp_arg arg%d/w offset %d and type '%s' is too small\n",
+ i + 1, offset, g_func_pp->arg[i].type.name);
+ }
+ if (popr->is_ptr && popr->lmod != OPLM_DWORD)
+ ferr(po, "bp_arg arg%d: non-dword ptr access\n", i + 1);
}
else
{
if (g_stack_fsz == 0)
ferr(po, "stack var access without stackframe\n");
+ g_stack_frame_used = 1;
sf_ofs = g_stack_fsz + offset;
lim = (ofs_reg[0] != 0) ? -4 : 0;
{
const struct parsed_proto *pp;
- pp = proto_parse(g_fhdr, name);
+ pp = proto_parse(g_fhdr, name, 0);
if (pp == NULL)
ferr(po, "proto_parse failed for ref '%s'\n", name);
{
char tmp1[256], tmp2[256];
char expr[256];
+ char *p;
int ret;
if (cast == NULL)
snprintf(buf, buf_size, "%s%s", cast, opr_reg_p(po, popr));
break;
case OPLM_WORD:
- snprintf(buf, buf_size, "(u16)%s", opr_reg_p(po, popr));
+ snprintf(buf, buf_size, "%s%s",
+ simplify_cast(cast, "(u16)"), opr_reg_p(po, popr));
break;
case OPLM_BYTE:
if (popr->name[1] == 'h') // XXX..
- snprintf(buf, buf_size, "(u8)(%s >> 8)", opr_reg_p(po, popr));
+ snprintf(buf, buf_size, "%s(%s >> 8)",
+ simplify_cast(cast, "(u8)"), opr_reg_p(po, popr));
else
- snprintf(buf, buf_size, "(u8)%s", opr_reg_p(po, popr));
+ snprintf(buf, buf_size, "%s%s",
+ simplify_cast(cast, "(u8)"), opr_reg_p(po, popr));
break;
default:
ferr(po, "invalid src lmod: %d\n", popr->lmod);
break;
case OPT_REGMEM:
- if (parse_stack_el(popr->name, NULL)
- || (g_bp_frame && !(po->flags & OPF_EBP_S)
- && IS_START(popr->name, "ebp")))
- {
+ if (is_stack_access(po, popr)) {
stack_frame_access(po, popr, buf, buf_size,
popr->name, cast, 1, is_lea);
break;
ret = sscanf(expr, "%[^[][%[^]]]", tmp1, tmp2);
if (ret != 2)
ferr(po, "parse failure for '%s'\n", expr);
+ if (tmp1[0] == '(') {
+ // (off_4FFF50+3)[eax]
+ p = strchr(tmp1 + 1, ')');
+ if (p == NULL || p[1] != 0)
+ ferr(po, "parse failure (2) for '%s'\n", expr);
+ *p = 0;
+ memmove(tmp1, tmp1 + 1, strlen(tmp1));
+ }
snprintf(expr, sizeof(expr), "(u32)&%s + %s", tmp1, tmp2);
}
break;
}
- if (cast[0] == 0)
- cast = lmod_cast_u_ptr(po, popr->lmod);
- snprintf(buf, buf_size, "%s(%s)", cast, expr);
+ snprintf(buf, buf_size, "%s(%s)",
+ simplify_cast(cast, lmod_cast_u_ptr(po, popr->lmod)), expr);
break;
case OPT_LABEL:
if (is_lea)
ferr(po, "lea from const?\n");
- snprintf(buf, buf_size, "%s", cast);
- ret = strlen(buf);
- printf_number(buf + ret, buf_size - ret, popr->val);
+ printf_number(tmp1, sizeof(tmp1), popr->val);
+ snprintf(buf, buf_size, "%s%s",
+ simplify_cast_num(cast, popr->val), tmp1);
break;
default:
break;
case OPT_REGMEM:
- if (parse_stack_el(popr->name, NULL)
- || (g_bp_frame && !(po->flags & OPF_EBP_S)
- && IS_START(popr->name, "ebp")))
- {
+ if (is_stack_access(po, popr)) {
stack_frame_access(po, popr, buf, buf_size,
popr->name, "", 0, 0);
break;
}
static void out_cmp_for_cc(char *buf, size_t buf_size,
- struct parsed_op *po, enum parsed_flag_op pfo, int is_inv,
- enum opr_lenmod lmod, const char *expr1, const char *expr2)
+ struct parsed_op *po, enum parsed_flag_op pfo, int is_inv)
{
- const char *cast, *scast;
+ const char *cast, *scast, *cast_use;
+ char buf1[256], buf2[256];
+ enum opr_lenmod lmod;
+
+ if (po->operand[0].lmod != po->operand[1].lmod)
+ ferr(po, "%s: lmod mismatch: %d %d\n", __func__,
+ po->operand[0].lmod, po->operand[1].lmod);
+ lmod = po->operand[0].lmod;
cast = lmod_cast_u(po, lmod);
scast = lmod_cast_s(po, lmod);
+ switch (pfo) {
+ case PFO_C:
+ case PFO_Z:
+ case PFO_BE: // !a
+ cast_use = cast;
+ break;
+
+ case PFO_S:
+ case PFO_L: // !ge
+ case PFO_LE:
+ cast_use = scast;
+ break;
+
+ default:
+ ferr(po, "%s: unhandled parsed_flag_op: %d\n", __func__, pfo);
+ }
+
+ out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], cast_use, 0);
+ out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], cast_use, 0);
+
switch (pfo) {
case PFO_C:
// note: must be unsigned compare
- snprintf(buf, buf_size, "(%s%s %s %s%s)",
- cast, expr1, is_inv ? ">=" : "<", cast, expr2);
+ snprintf(buf, buf_size, "(%s %s %s)",
+ buf1, is_inv ? ">=" : "<", buf2);
break;
case PFO_Z:
- snprintf(buf, buf_size, "(%s%s %s %s%s)",
- cast, expr1, is_inv ? "!=" : "==", cast, expr2);
+ snprintf(buf, buf_size, "(%s %s %s)",
+ buf1, is_inv ? "!=" : "==", buf2);
break;
case PFO_BE: // !a
// note: must be unsigned compare
- snprintf(buf, buf_size, "(%s%s %s %s%s)",
- cast, expr1, is_inv ? ">" : "<=", cast, expr2);
+ snprintf(buf, buf_size, "(%s %s %s)",
+ buf1, is_inv ? ">" : "<=", buf2);
+
+ // annoying case
+ if (is_inv && lmod == OPLM_BYTE
+ && po->operand[1].type == OPT_CONST
+ && po->operand[1].val == 0xff)
+ {
+ snprintf(g_comment, sizeof(g_comment), "if %s", buf);
+ snprintf(buf, buf_size, "(0)");
+ }
break;
// note: must be signed compare
case PFO_S:
snprintf(buf, buf_size, "(%s(%s - %s) %s 0)",
- scast, expr1, expr2, is_inv ? ">=" : "<");
+ scast, buf1, buf2, is_inv ? ">=" : "<");
break;
case PFO_L: // !ge
- snprintf(buf, buf_size, "(%s%s %s %s%s)",
- scast, expr1, is_inv ? ">=" : "<", scast, expr2);
+ snprintf(buf, buf_size, "(%s %s %s)",
+ buf1, is_inv ? ">=" : "<", buf2);
break;
case PFO_LE:
- snprintf(buf, buf_size, "(%s%s %s %s%s)",
- scast, expr1, is_inv ? ">" : "<=", scast, expr2);
+ snprintf(buf, buf_size, "(%s %s %s)",
+ buf1, is_inv ? ">" : "<=", buf2);
break;
default:
- ferr(po, "%s: unhandled parsed_flag_op: %d\n", __func__, pfo);
+ break;
}
}
po->operand[0].lmod, buf3);
}
else if (po->op == OP_CMP) {
- out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]);
- out_src_opr_u32(buf3, sizeof(buf3), po, &po->operand[1]);
- out_cmp_for_cc(buf, buf_size, po, pfo, is_inv,
- po->operand[0].lmod, buf2, buf3);
+ out_cmp_for_cc(buf, buf_size, po, pfo, is_inv);
}
else
ferr(po, "%s: unhandled op: %d\n", __func__, po->op);
}
}
-static void set_flag_no_dup(struct parsed_op *po, enum op_flags flag,
- enum op_flags flag_check)
+static void op_set_clear_flag(struct parsed_op *po,
+ enum op_flags flag_set, enum op_flags flag_clear)
{
- if (po->flags & flag)
- ferr(po, "flag %x already set\n", flag);
- if (po->flags & flag_check)
- ferr(po, "flag_check %x already set\n", flag_check);
-
- po->flags |= flag;
+ po->flags |= flag_set;
+ po->flags &= ~flag_clear;
}
// last op in stream - unconditional branch or ret
if (depth > *maxdepth)
*maxdepth = depth;
if (do_flags)
- set_flag_no_dup(po, OPF_RSAVE, OPF_RMD);
+ op_set_clear_flag(po, OPF_RSAVE, OPF_RMD);
}
else if (depth == 0) {
if (do_flags)
- set_flag_no_dup(po, OPF_RMD, OPF_RSAVE);
+ op_set_clear_flag(po, OPF_RMD, OPF_RSAVE);
return 1;
}
else {
if (depth < 0) // should not happen
ferr(po, "fail with depth\n");
if (do_flags)
- set_flag_no_dup(po, OPF_RSAVE, OPF_RMD);
+ op_set_clear_flag(po, OPF_RSAVE, OPF_RMD);
}
}
}
return found ? 0 : -1;
}
-// is operand 'opr modified' by parsed_op 'po'?
+// is operand 'opr' modified by parsed_op 'po'?
static int is_opr_modified(const struct parsed_opr *opr,
const struct parsed_op *po)
{
}
if (i > 0 && LAST_OP(i - 1)) {
- i = g_label_refs[i].i;
+ i = lr->i;
continue;
}
ret = scan_for_flag_set(lr->i, magic,
// scan back for cdq, if anything modifies edx, fail
static int scan_for_cdq_edx(int i)
{
- for (; i >= 0; i--) {
+ while (i >= 0) {
+ if (g_labels[i][0] != 0) {
+ if (g_label_refs[i].next != NULL)
+ return -1;
+ if (i > 0 && LAST_OP(i - 1)) {
+ i = g_label_refs[i].i;
+ continue;
+ }
+ return -1;
+ }
+ i--;
+
if (ops[i].op == OP_CDQ)
return i;
static int scan_for_reg_clear(int i, int reg)
{
- for (; i >= 0; i--) {
+ while (i >= 0) {
+ if (g_labels[i][0] != 0) {
+ if (g_label_refs[i].next != NULL)
+ return -1;
+ if (i > 0 && LAST_OP(i - 1)) {
+ i = g_label_refs[i].i;
+ continue;
+ }
+ return -1;
+ }
+ i--;
+
if (ops[i].op == OP_XOR
&& ops[i].operand[0].lmod == OPLM_DWORD
&& ops[i].operand[0].reg == ops[i].operand[1].reg
static int scan_for_esp_adjust(int i, int opcnt, int *adj)
{
struct parsed_op *po;
+ int first_pop = -1;
*adj = 0;
for (; i < opcnt; i++) {
po = &ops[i];
+ if (g_labels[i][0] != 0)
+ break;
+
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)
+ 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)
+ }
+ else if (po->op == OP_POP) {
+ if (first_pop == -1)
+ first_pop = i;
*adj += lmod_bytes(po, po->operand[0].lmod);
+ }
else if (po->flags & (OPF_JMP|OPF_TAIL)) {
if (po->op != OP_CALL)
- return -1;
+ break;
if (po->operand[0].type != OPT_LABEL)
- return -1;
+ break;
// TODO: should only allow combining __cdecl calls..
}
+ }
- if (g_labels[i][0] != 0)
- return -1;
+ if (*adj == 4 && first_pop >= 0 && ops[first_pop].op == OP_POP
+ && ops[first_pop].operand[0].type == OPT_REG
+ && ops[first_pop].operand[0].reg == xCX)
+ {
+ // probably 'pop ecx' was used..
+ return first_pop;
}
return -1;
}
}
+static const struct parsed_proto *try_recover_pp(
+ struct parsed_op *po, const struct parsed_opr *opr)
+{
+ const struct parsed_proto *pp = NULL;
+
+ // maybe an arg of g_func?
+ if (opr->type == OPT_REGMEM && is_stack_access(po, opr))
+ {
+ char ofs_reg[16] = { 0, };
+ int arg, arg_s, arg_i;
+ int stack_ra = 0;
+ int offset = 0;
+
+ parse_stack_access(po, opr->name, ofs_reg,
+ &offset, &stack_ra, NULL);
+ if (ofs_reg[0] != 0)
+ ferr(po, "offset reg on arg access?\n");
+ if (offset <= stack_ra)
+ ferr(po, "stack var call unhandled yet\n");
+
+ arg_i = (offset - stack_ra - 4) / 4;
+ for (arg = arg_s = 0; arg < g_func_pp->argc; arg++) {
+ if (g_func_pp->arg[arg].reg != NULL)
+ continue;
+ if (arg_s == arg_i)
+ break;
+ arg_s++;
+ }
+ if (arg == g_func_pp->argc)
+ ferr(po, "stack arg %d not in prototype?\n", arg_i);
+
+ pp = g_func_pp->arg[arg].fptr;
+ if (pp == NULL)
+ ferr(po, "icall sa: arg%d is not a fptr?\n", arg + 1);
+ if (pp->argc_reg != 0)
+ ferr(po, "icall sa: reg arg in arg-call unhandled yet\n");
+ }
+ 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);
+ if (pp->argc_reg != 0)
+ ferr(po, "arg-call unhandled yet for '%s'\n", opr->name);
+ }
+
+ return pp;
+}
+
+static void scan_for_call_type(int i, struct parsed_opr *opr,
+ int magic, const struct parsed_proto **pp_found)
+{
+ const struct parsed_proto *pp = NULL;
+ struct parsed_op *po;
+ struct label_ref *lr;
+
+ while (i >= 0) {
+ if (ops[i].cc_scratch == magic)
+ return;
+ ops[i].cc_scratch = magic;
+
+ if (g_labels[i][0] != 0) {
+ lr = &g_label_refs[i];
+ for (; lr != NULL; lr = lr->next)
+ scan_for_call_type(lr->i, opr, magic, pp_found);
+ if (i > 0 && LAST_OP(i - 1))
+ return;
+ }
+ i--;
+
+ if (!(ops[i].flags & OPF_DATA))
+ continue;
+ if (!is_opr_modified(opr, &ops[i]))
+ continue;
+ if (ops[i].op != OP_MOV && ops[i].op != OP_LEA) {
+ // most probably trashed by some processing
+ *pp_found = NULL;
+ return;
+ }
+
+ opr = &ops[i].operand[1];
+ if (opr->type != OPT_REG)
+ break;
+ }
+
+ po = (i >= 0) ? &ops[i] : ops;
+
+ if (i < 0) {
+ // reached the top - can only be an arg-reg
+ if (opr->type != OPT_REG)
+ return;
+
+ for (i = 0; i < g_func_pp->argc; i++) {
+ if (g_func_pp->arg[i].reg == NULL)
+ continue;
+ if (IS(opr->name, g_func_pp->arg[i].reg))
+ break;
+ }
+ if (i == g_func_pp->argc)
+ return;
+ pp = g_func_pp->arg[i].fptr;
+ if (pp == NULL)
+ ferr(po, "icall: arg%d (%s) is not a fptr?\n",
+ i + 1, g_func_pp->arg[i].reg);
+ if (pp->argc_reg != 0)
+ ferr(po, "icall: reg arg in arg-call unhandled yet\n");
+ }
+ else
+ pp = try_recover_pp(po, opr);
+
+ if (*pp_found != NULL && pp != NULL) {
+ if (!IS((*pp_found)->ret_type.name, pp->ret_type.name)
+ || (*pp_found)->is_stdcall != pp->is_stdcall
+ || (*pp_found)->argc != pp->argc
+ || (*pp_found)->argc_reg != pp->argc_reg
+ || (*pp_found)->argc_stack != pp->argc_stack)
+ {
+ ferr(po, "icall: parsed_proto mismatch\n");
+ }
+ }
+ if (pp != NULL)
+ *pp_found = pp;
+}
+
+static const struct parsed_proto *resolve_call(int i, int opcnt)
+{
+ const struct parsed_proto *pp = NULL;
+
+ switch (ops[i].operand[0].type) {
+ case OPT_REGMEM:
+ case OPT_LABEL:
+ case OPT_OFFSET:
+ pp = try_recover_pp(&ops[i], &ops[i].operand[0]);
+ break;
+ default:
+ scan_for_call_type(i, &ops[i].operand[0], i + opcnt * 9, &pp);
+ break;
+ }
+
+ return pp;
+}
+
static int collect_call_args(struct parsed_op *po, int i,
struct parsed_proto *pp, int *save_arg_vars, int arg,
int magic, int need_op_saving, int may_reuse)
int j;
if (i < 0) {
- ferr(po, "no refs for '%s'?\n", g_labels[i]);
+ ferr(po, "dead label encountered\n");
return -1;
}
}
ops[j].cc_scratch = magic;
- if (g_labels[j][0] != 0) {
+ if (g_labels[j][0] != 0 && g_label_refs[j].i != -1) {
lr = &g_label_refs[j];
if (lr->next != NULL)
need_op_saving = 1;
{
pp_tmp = ops[j].datap;
if (pp_tmp == NULL)
- ferr(po, "arg collect hit unparsed call\n");
+ ferr(po, "arg collect hit unparsed call '%s'\n",
+ ops[j].operand[0].name);
if (may_reuse && pp_tmp->argc_stack > 0)
ferr(po, "arg collect %d/%d hit '%s' with %d stack args\n",
arg, pp->argc, opr_name(&ops[j], 0), pp_tmp->argc_stack);
int cmp_result_vars = 0;
int need_mul_var = 0;
int have_func_ret = 0;
+ int have_normal_ret = 0;
int had_decl = 0;
int label_pending = 0;
int regmask_save = 0;
int ret;
g_bp_frame = g_sp_frame = g_stack_fsz = 0;
+ g_stack_frame_used = 0;
- g_func_pp = proto_parse(fhdr, funcn);
+ g_func_pp = proto_parse(fhdr, funcn, 0);
if (g_func_pp == NULL)
ferr(ops, "proto_parse failed for '%s'\n", funcn);
}
// pass2:
+ // - parse calls with labels
// - resolve all branches
for (i = 0; i < opcnt; i++)
{
po->bt_i = -1;
po->btj = NULL;
- if ((po->flags & OPF_RMD) || !(po->flags & OPF_JMP)
- || po->op == OP_CALL || po->op == OP_RET)
+ if (po->flags & OPF_RMD)
+ continue;
+
+ if (po->op == OP_CALL) {
+ if (po->operand[0].type == OPT_LABEL) {
+ tmpname = opr_name(po, 0);
+ if (IS_START(tmpname, "loc_"))
+ ferr(po, "call to loc_*\n");
+ pp_c = proto_parse(fhdr, tmpname, 0);
+ if (pp_c == NULL)
+ ferr(po, "proto_parse failed for call '%s'\n", tmpname);
+ if (pp_c->is_fptr && pp_c->argc_reg != 0)
+ ferr(po, "fptr call with reg arg\n");
+ pp = proto_clone(pp_c);
+ my_assert_not(pp, NULL);
+ po->datap = pp;
+ }
+ continue;
+ }
+
+ if (!(po->flags & OPF_JMP) || po->op == OP_RET)
continue;
if (po->operand[0].type == OPT_REGMEM) {
tailcall:
po->op = OP_CALL;
po->flags |= OPF_TAIL;
+ i--; // reprocess
}
// pass3:
+ // - remove dead labels
// - process calls
for (i = 0; i < opcnt; i++)
{
+ if (g_labels[i][0] != 0 && g_label_refs[i].i == -1)
+ g_labels[i][0] = 0;
+
po = &ops[i];
if (po->flags & OPF_RMD)
continue;
if (po->op == OP_CALL)
{
- pp = calloc(1, sizeof(*pp));
- my_assert_not(pp, NULL);
tmpname = opr_name(po, 0);
- if (po->operand[0].type != OPT_LABEL)
+ pp = po->datap;
+ if (pp == NULL)
{
- ret = scan_for_esp_adjust(i + 1, opcnt, &j);
- if (ret < 0)
- ferr(po, "non-__cdecl indirect call unhandled yet\n");
- j /= 4;
- if (j > ARRAY_SIZE(pp->arg))
- ferr(po, "esp adjust too large?\n");
- 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");
- }
- else {
- pp_c = proto_parse(fhdr, tmpname);
- if (pp_c == NULL)
- ferr(po, "proto_parse failed for call '%s'\n", tmpname);
- pp = proto_clone(pp_c);
+ // indirect call
+ pp_c = resolve_call(i, opcnt);
+ if (pp_c != NULL)
+ pp = proto_clone(pp_c);
+ if (pp == NULL) {
+ pp = calloc(1, sizeof(*pp));
+ my_assert_not(pp, NULL);
+ ret = scan_for_esp_adjust(i + 1, opcnt, &j);
+ if (ret < 0)
+ ferr(po, "non-__cdecl indirect call unhandled yet\n");
+ j /= 4;
+ if (j > ARRAY_SIZE(pp->arg))
+ ferr(po, "esp adjust too large?\n");
+ 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->datap = pp;
}
// look for and make use of esp adjust
for (arg = 0; arg < pp->argc; arg++) {
if (pp->arg[arg].fptr != NULL) {
pp_tmp = pp->arg[arg].fptr;
- if (pp_tmp->is_stdcall || pp_tmp->argc != pp_tmp->argc_stack)
- ferr(po, "'%s' has a non-__cdecl callback\n", tmpname);
+ if (pp_tmp->argc_reg != 0)
+ ferr(po, "'%s' has a callback with reg-args\n", tmpname);
}
}
need_mul_var = 1;
if (!(po->flags & OPF_TAIL) && !IS(pp->ret_type.name, "void"))
have_func_ret = 1;
- po->datap = pp;
}
}
if (po->flags & OPF_RMD)
continue;
+ if (po->op == OP_PUSH && (po->flags & OPF_RSAVE)) {
+ reg = po->operand[0].reg;
+ if (!(regmask & (1 << reg)))
+ // not a reg save after all, rerun scan_for_pop
+ po->flags &= ~OPF_RSAVE;
+ else
+ regmask_save |= 1 << reg;
+ }
+
if (po->op == OP_PUSH
&& po->argnum == 0 && !(po->flags & OPF_RSAVE)
&& po->operand[0].type == OPT_REG)
if (ret == 1) {
if (depth > 1)
ferr(po, "too much depth: %d\n", depth);
- if (depth > 0)
- regmask_save |= 1 << reg;
po->flags |= OPF_RMD;
scan_for_pop(i + 1, opcnt, po->operand[0].name,
else if (po->op == OP_CALL && po->operand[0].type != OPT_LABEL) {
pp = po->datap;
my_assert_not(pp, NULL);
- fprintf(fout, " %s (*icall%d)(", pp->ret_type.name, i);
+ fprintf(fout, " %s (", pp->ret_type.name);
+ if (pp->is_stdcall && pp->argc_reg == 0)
+ fprintf(fout, "__stdcall ");
+ fprintf(fout, "*icall%d)(", i);
for (j = 0; j < pp->argc; j++) {
if (j > 0)
fprintf(fout, ", ");
}
fprintf(fout, ");\n");
}
+ else if (po->op == OP_RET)
+ have_normal_ret = 1;
}
// output LUTs/jumptables
}
// declare other regs - special case for eax
- if (!((regmask | regmask_arg) & 1) && have_func_ret
+ if (!((regmask | regmask_arg) & 1)
+ && (have_func_ret || have_normal_ret)
&& !IS(g_func_pp->ret_type.name, "void"))
{
fprintf(fout, " u32 eax = 0;\n");
// output ops
for (i = 0; i < opcnt; i++)
{
- if (g_labels[i][0] != 0 && g_label_refs[i].i != -1) {
+ if (g_labels[i][0] != 0) {
fprintf(fout, "\n%s:\n", g_labels[i]);
label_pending = 1;
default:
ferr(po, "invalid src lmod: %d\n", po->operand[1].lmod);
}
- fprintf(fout, " %s = %s%s;",
+ fprintf(fout, " %s = %s;",
out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
- buf3,
- out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
+ buf3, 0));
break;
case OP_NOT:
case OP_SBB:
assert_operand_cnt(2);
propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
if (po->op == OP_SBB
&& IS(po->operand[0].name, po->operand[1].name))
{
// avoid use of unitialized var
- fprintf(fout, " %s = -cond_c;",
- out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
+ fprintf(fout, " %s = -cond_c;", buf1);
+ // carry remains what it was
+ pfomask &= ~(1 << PFO_C);
}
else {
- fprintf(fout, " %s %s= %s + cond_c;",
- out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
- op_to_c(po),
+ fprintf(fout, " %s %s= %s + cond_c;", buf1, op_to_c(po),
out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
}
+ output_std_flags(fout, po, &pfomask, buf1);
last_arith_dst = &po->operand[0];
delayed_flag_op = NULL;
break;
strcpy(buf2, po->op == OP_INC ? "+" : "-");
fprintf(fout, " %s %s= 1;", buf1, buf2);
}
+ output_std_flags(fout, po, &pfomask, buf1);
last_arith_dst = &po->operand[0];
delayed_flag_op = NULL;
break;
// 32bit division is common, look for it
if (po->op == OP_DIV)
- ret = scan_for_reg_clear(i - 1, xDX);
+ ret = scan_for_reg_clear(i, xDX);
else
- ret = scan_for_cdq_edx(i - 1);
+ ret = scan_for_cdq_edx(i);
if (ret >= 0) {
out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
strcpy(buf2, lmod_cast(po, po->operand[0].lmod,
ferr(po, "NULL pp\n");
if (po->operand[0].type != OPT_LABEL)
- fprintf(fout, " icall%d = (void *)%s;\n", i,
- out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]));
+ fprintf(fout, " icall%d = %s;\n", i,
+ out_src_opr(buf1, sizeof(buf1), po, &po->operand[0],
+ "(void *)", 0));
fprintf(fout, " ");
if (strstr(pp->ret_type.name, "int64")) {
// else already handled as 'return f()'
if (ret) {
+ if (!IS(g_func_pp->ret_type.name, "void"))
+ ferr(po, "int func -> void func tailcall?\n");
fprintf(fout, "\n return;");
strcpy(g_comment, "^ tailcall");
}
label_pending = 0;
}
+ if (g_stack_fsz && !g_stack_frame_used)
+ fprintf(fout, " (void)sf;\n");
+
fprintf(fout, "}\n\n");
// cleanup
int asmln;
};
+static struct chunk_item *func_chunks;
+static int func_chunk_cnt;
+static int func_chunk_alloc;
+
+static void add_func_chunk(FILE *fasm, const char *name, int line)
+{
+ if (func_chunk_cnt >= func_chunk_alloc) {
+ func_chunk_alloc *= 2;
+ func_chunks = realloc(func_chunks,
+ func_chunk_alloc * sizeof(func_chunks[0]));
+ my_assert_not(func_chunks, NULL);
+ }
+ func_chunks[func_chunk_cnt].fptr = ftell(fasm);
+ func_chunks[func_chunk_cnt].name = strdup(name);
+ func_chunks[func_chunk_cnt].asmln = line;
+ func_chunk_cnt++;
+}
+
static int cmp_chunks(const void *p1, const void *p2)
{
const struct chunk_item *c1 = p1, *c2 = p2;
return strcmp(*(char * const *)p1, *(char * const *)p2);
}
+static void scan_ahead(FILE *fasm)
+{
+ char words[2][256];
+ char line[256];
+ long oldpos;
+ int oldasmln;
+ int wordc;
+ char *p;
+ int i;
+
+ oldpos = ftell(fasm);
+ oldasmln = asmln;
+
+ while (fgets(line, sizeof(line), fasm))
+ {
+ wordc = 0;
+ asmln++;
+
+ p = sskip(line);
+ if (*p == 0)
+ continue;
+
+ if (*p == ';')
+ {
+ // get rid of random tabs
+ for (i = 0; line[i] != 0; i++)
+ if (line[i] == '\t')
+ line[i] = ' ';
+
+ if (p[2] == 'S' && IS_START(p, "; START OF FUNCTION CHUNK FOR "))
+ {
+ p += 30;
+ next_word(words[0], sizeof(words[0]), p);
+ if (words[0][0] == 0)
+ aerr("missing name for func chunk?\n");
+
+ add_func_chunk(fasm, words[0], asmln);
+ }
+ continue;
+ } // *p == ';'
+
+ 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;
+ }
+
+ fseek(fasm, oldpos, SEEK_SET);
+ asmln = oldasmln;
+}
+
int main(int argc, char *argv[])
{
FILE *fout, *fasm, *frlist;
char **rlist = NULL;
int rlist_len = 0;
int rlist_alloc = 0;
- struct chunk_item *func_chunks;
int func_chunks_used = 0;
int func_chunks_sorted = 0;
- int func_chunk_cnt = 0;
- int func_chunk_alloc;
int func_chunk_i = -1;
long func_chunk_ret = 0;
int func_chunk_ret_ln = 0;
+ int scanned_ahead = 0;
char line[256];
- char words[16][256];
+ char words[20][256];
enum opr_lenmod lmod;
int in_func = 0;
int pending_endp = 0;
func_chunks = malloc(func_chunk_alloc * sizeof(func_chunks[0]));
my_assert_not(func_chunks, NULL);
+ memset(words, 0, sizeof(words));
+
for (; arg < argc; arg++) {
frlist = fopen(argv[arg], "r");
my_assert_not(frlist, NULL);
while (fgets(line, sizeof(line), frlist)) {
p = sskip(line);
- if (*p == 0 || *p == ';' || *p == '#')
+ if (*p == 0 || *p == ';')
+ continue;
+ if (*p == '#') {
+ if (IS_START(p, "#if 0"))
+ skip_func = 1;
+ else if (IS_START(p, "#endif"))
+ skip_func = 0;
+ continue;
+ }
+ if (skip_func)
continue;
p = next_word(words[0], sizeof(words[0]), p);
}
rlist[rlist_len++] = strdup(words[0]);
}
+ skip_func = 0;
fclose(frlist);
frlist = NULL;
p += 30;
next_word(words[0], sizeof(words[0]), p);
if (words[0][0] == 0)
- aerr("missing nam for func chunk?\n");
- if (func_chunk_cnt >= func_chunk_alloc) {
- func_chunk_alloc *= 2;
- func_chunks = realloc(func_chunks,
- func_chunk_alloc * sizeof(func_chunks[0]));
- my_assert_not(func_chunks, NULL);
+ aerr("missing name for func chunk?\n");
+
+ if (!scanned_ahead) {
+ add_func_chunk(fasm, words[0], asmln);
+ func_chunks_sorted = 0;
}
- func_chunks[func_chunk_cnt].fptr = ftell(fasm);
- func_chunks[func_chunk_cnt].name = strdup(words[0]);
- func_chunks[func_chunk_cnt].asmln = asmln;
- func_chunk_cnt++;
- func_chunks_sorted = 0;
}
else if (p[2] == 'E' && IS_START(p, "; END OF FUNCTION CHUNK"))
{
if (IS_START(g_func, "sub_")) {
unsigned long addr = strtoul(p, NULL, 16);
unsigned long f_addr = strtoul(g_func + 4, NULL, 16);
- if (addr > f_addr)
- aerr("need a chunk %lX that is after %s\n", addr, g_func);
+ if (addr > f_addr && !scanned_ahead) {
+ anote("scan_ahead caused by '%s', addr %lx\n",
+ g_func, addr);
+ scan_ahead(fasm);
+ scanned_ahead = 1;
+ func_chunks_sorted = 0;
+ }
}
}
continue;
} // *p == ';'
parse_words:
- memset(words, 0, sizeof(words));
- for (wordc = 0; wordc < 16; wordc++) {
+ for (i = wordc; i < ARRAY_SIZE(words); i++)
+ words[i][0] = 0;
+ for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) {
p = sskip(next_word_s(words[wordc], sizeof(words[0]), p));
if (*p == 0 || *p == ';') {
wordc++;
break;
}
}
+ if (*p != 0 && *p != ';')
+ aerr("too many words\n");
// alow asm patches in comments
if (*p == ';' && IS_START(p, "; sctpatch:")) {
continue;
}
+ if (wordc == 2 && IS(words[1], "ends"))
+ break;
+
p = strchr(words[0], ':');
if (p != NULL) {
set_label(pi, words[0]);