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 */
OPF_FLAGS = (1 << 2), /* sets flags */
- OPF_JMP = (1 << 3), /* branches, ret and call */
- OPF_CC = (1 << 4), /* uses flags */
- OPF_TAIL = (1 << 5), /* ret or tail call */
- OPF_RSAVE = (1 << 6), /* push/pop is local reg save/load */
- OPF_REP = (1 << 7), /* prefixed by rep */
- OPF_REPZ = (1 << 8), /* rep is repe/repz */
- OPF_REPNZ = (1 << 9), /* rep is repne/repnz */
- OPF_FARG = (1 << 10), /* push collected as func arg (no reuse) */
+ OPF_JMP = (1 << 3), /* branch, call */
+ OPF_CJMP = (1 << 4), /* cond. branch (cc or jecxz) */
+ OPF_CC = (1 << 5), /* uses flags */
+ OPF_TAIL = (1 << 6), /* ret or tail call */
+ OPF_RSAVE = (1 << 7), /* push/pop is local reg save/load */
+ 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 */
};
enum op_op {
OP_NOP,
OP_PUSH,
OP_POP,
+ OP_LEAVE,
OP_MOV,
OP_LEA,
OP_MOVZX,
OP_STOS,
OP_MOVS,
OP_CMPS,
+ OP_SCAS,
+ OP_STD,
+ OP_CLD,
OP_RET,
OP_ADD,
OP_SUB,
OP_CMP,
OP_CALL,
OP_JMP,
+ OP_JECXZ,
OP_JO,
OP_JNO,
OP_JC,
enum opr_lenmod lmod;
unsigned int is_ptr:1; // pointer in C
unsigned int is_array:1; // array in C
+ unsigned int type_from_var:1; // .. in header, sometimes wrong
unsigned int size_mismatch:1; // type override differs from C
unsigned int size_lt:1; // type override is larger than C
+ unsigned int had_ds:1; // had ds: prefix
int reg;
unsigned int val;
char name[256];
};
// datap:
-// OP_CALL - ptr to parsed_proto
+// OP_CALL - parser proto hint (str), ptr to struct parsed_proto
// (OPF_CC) - point to one of (OPF_FLAGS) that affects cc op
+// OP_POP - point to OP_PUSH in push/pop pair
struct parsed_equ {
char name[64];
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;
+static int g_allow_regfunc;
#define ferr(op_, fmt, ...) do { \
printf("error:%s:#%zd: '%s': " fmt, g_func, (op_) - ops, \
dump_op(op_), ##__VA_ARGS__); \
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;
// must be a number after esp+, already converted to 0x..
s = name + 4;
if (!('0' <= *s && *s <= '9')) {
- aerr("%s nan?\n", __func__);
+ aerr("%s nan?\n", __func__);
return NULL;
}
if (s[0] == '0' && s[1] == 'x')
const struct parsed_type *c_type)
{
static const char *dword_types[] = {
- "int", "_DWORD", "DWORD", "HANDLE", "HWND", "HMODULE",
+ "int", "_DWORD", "UINT_PTR", "DWORD",
"WPARAM", "LPARAM", "UINT",
+ "HIMC",
};
static const char *word_types[] = {
- "uint16_t", "int16_t",
+ "uint16_t", "int16_t", "_WORD",
"unsigned __int16", "__int16",
};
static const char *byte_types[] = {
"uint8_t", "int8_t", "char",
- "unsigned __int8", "__int8", "BYTE",
+ "unsigned __int8", "__int8", "BYTE", "_BYTE",
+ "_UNKNOWN",
};
const char *n;
int i;
const struct parsed_proto *pp;
enum opr_lenmod tmplmod;
unsigned long number;
+ char buf[256];
int ret, len;
int wordc_in;
- char *tmp;
+ char *p;
int i;
if (w >= wordc)
if (label != NULL) {
opr->type = OPT_LABEL;
- if (IS_START(label, "ds:"))
+ if (IS_START(label, "ds:")) {
+ opr->had_ds = 1;
label += 3;
+ }
strcpy(opr->name, label);
return wordc;
}
return wordc;
}
if (IS(words[w], "(offset")) {
- char *p = strchr(words[w + 1], ')');
+ p = strchr(words[w + 1], ')');
if (p == NULL)
aerr("parse of bracketed offset failed\n");
*p = 0;
if (wordc_in != 1)
aerr("parse_operand 1 word expected\n");
- tmp = words[w];
- if (IS_START(tmp, "ds:"))
- tmp += 3;
- strcpy(opr->name, tmp);
+ if (IS_START(words[w], "ds:")) {
+ opr->had_ds = 1;
+ memmove(words[w], words[w] + 3, strlen(words[w]) - 2);
+ }
+ strcpy(opr->name, words[w]);
if (words[w][0] == '[') {
opr->type = OPT_REGMEM;
}
else if (strchr(words[w], '[')) {
// label[reg] form
+ p = strchr(words[w], '[');
opr->type = OPT_REGMEM;
- if (opr->lmod == OPLM_UNSPEC)
- guess_lmod_from_name(opr);
- parse_indmode(strchr(words[w], '['), regmask_indirect, 0);
- return wordc;
+ parse_indmode(p, regmask_indirect, 0);
+ strncpy(buf, words[w], p - words[w]);
+ buf[p - words[w]] = 0;
+ pp = proto_parse(g_fhdr, buf, 1);
+ goto do_label;
}
else if (('0' <= words[w][0] && words[w][0] <= '9')
|| words[w][0] == '-')
// most likely var in data segment
opr->type = OPT_LABEL;
+ pp = proto_parse(g_fhdr, opr->name, 0);
- pp = proto_parse(g_fhdr, opr->name);
+do_label:
if (pp != NULL) {
- if (pp->is_fptr) {
+ if (pp->is_fptr || pp->is_func) {
opr->lmod = OPLM_DWORD;
opr->is_ptr = 1;
}
anote("unhandled C type '%s' for '%s'\n",
pp->type.name, opr->name);
- if (opr->lmod == OPLM_UNSPEC)
+ if (opr->lmod == OPLM_UNSPEC) {
opr->lmod = tmplmod;
+ opr->type_from_var = 1;
+ }
else if (opr->lmod != tmplmod) {
opr->size_mismatch = 1;
if (tmplmod < opr->lmod)
{ "repnz", OPF_REP|OPF_REPNZ },
};
+#define OPF_CJMP_CC (OPF_JMP|OPF_CJMP|OPF_CC)
+
static const struct {
const char *name;
enum op_op op;
{ "nop", OP_NOP, 0, 0, 0 },
{ "push", OP_PUSH, 1, 1, 0 },
{ "pop", OP_POP, 1, 1, OPF_DATA },
+ { "leave",OP_LEAVE, 0, 0, OPF_DATA },
{ "mov" , OP_MOV, 2, 2, OPF_DATA },
{ "lea", OP_LEA, 2, 2, OPF_DATA },
{ "movzx",OP_MOVZX, 2, 2, OPF_DATA },
{ "cmpsb",OP_CMPS, 0, 0, OPF_DATA|OPF_FLAGS },
{ "cmpsw",OP_CMPS, 0, 0, OPF_DATA|OPF_FLAGS },
{ "cmpsd",OP_CMPS, 0, 0, OPF_DATA|OPF_FLAGS },
+ { "scasb",OP_SCAS, 0, 0, OPF_DATA|OPF_FLAGS },
+ { "scasw",OP_SCAS, 0, 0, OPF_DATA|OPF_FLAGS },
+ { "scasd",OP_SCAS, 0, 0, OPF_DATA|OPF_FLAGS },
+ { "std", OP_STD, 0, 0, OPF_DATA }, // special flag
+ { "cld", OP_CLD, 0, 0, OPF_DATA },
{ "add", OP_ADD, 2, 2, OPF_DATA|OPF_FLAGS },
{ "sub", OP_SUB, 2, 2, OPF_DATA|OPF_FLAGS },
{ "and", OP_AND, 2, 2, OPF_DATA|OPF_FLAGS },
{ "idiv", OP_IDIV, 1, 1, OPF_DATA|OPF_FLAGS },
{ "test", OP_TEST, 2, 2, OPF_FLAGS },
{ "cmp", OP_CMP, 2, 2, OPF_FLAGS },
- { "retn", OP_RET, 0, 1, OPF_JMP|OPF_TAIL },
+ { "retn", OP_RET, 0, 1, OPF_TAIL },
{ "call", OP_CALL, 1, 1, OPF_JMP|OPF_DATA|OPF_FLAGS },
{ "jmp", OP_JMP, 1, 1, OPF_JMP },
- { "jo", OP_JO, 1, 1, OPF_JMP|OPF_CC }, // 70 OF=1
- { "jno", OP_JNO, 1, 1, OPF_JMP|OPF_CC }, // 71 OF=0
- { "jc", OP_JC, 1, 1, OPF_JMP|OPF_CC }, // 72 CF=1
- { "jb", OP_JC, 1, 1, OPF_JMP|OPF_CC }, // 72
- { "jnc", OP_JNC, 1, 1, OPF_JMP|OPF_CC }, // 73 CF=0
- { "jnb", OP_JNC, 1, 1, OPF_JMP|OPF_CC }, // 73
- { "jae", OP_JNC, 1, 1, OPF_JMP|OPF_CC }, // 73
- { "jz", OP_JZ, 1, 1, OPF_JMP|OPF_CC }, // 74 ZF=1
- { "je", OP_JZ, 1, 1, OPF_JMP|OPF_CC }, // 74
- { "jnz", OP_JNZ, 1, 1, OPF_JMP|OPF_CC }, // 75 ZF=0
- { "jne", OP_JNZ, 1, 1, OPF_JMP|OPF_CC }, // 75
- { "jbe", OP_JBE, 1, 1, OPF_JMP|OPF_CC }, // 76 CF=1 || ZF=1
- { "jna", OP_JBE, 1, 1, OPF_JMP|OPF_CC }, // 76
- { "ja", OP_JA, 1, 1, OPF_JMP|OPF_CC }, // 77 CF=0 && ZF=0
- { "jnbe", OP_JA, 1, 1, OPF_JMP|OPF_CC }, // 77
- { "js", OP_JS, 1, 1, OPF_JMP|OPF_CC }, // 78 SF=1
- { "jns", OP_JNS, 1, 1, OPF_JMP|OPF_CC }, // 79 SF=0
- { "jp", OP_JP, 1, 1, OPF_JMP|OPF_CC }, // 7a PF=1
- { "jpe", OP_JP, 1, 1, OPF_JMP|OPF_CC }, // 7a
- { "jnp", OP_JNP, 1, 1, OPF_JMP|OPF_CC }, // 7b PF=0
- { "jpo", OP_JNP, 1, 1, OPF_JMP|OPF_CC }, // 7b
- { "jl", OP_JL, 1, 1, OPF_JMP|OPF_CC }, // 7c SF!=OF
- { "jnge", OP_JL, 1, 1, OPF_JMP|OPF_CC }, // 7c
- { "jge", OP_JGE, 1, 1, OPF_JMP|OPF_CC }, // 7d SF=OF
- { "jnl", OP_JGE, 1, 1, OPF_JMP|OPF_CC }, // 7d
- { "jle", OP_JLE, 1, 1, OPF_JMP|OPF_CC }, // 7e ZF=1 || SF!=OF
- { "jng", OP_JLE, 1, 1, OPF_JMP|OPF_CC }, // 7e
- { "jg", OP_JG, 1, 1, OPF_JMP|OPF_CC }, // 7f ZF=0 && SF=OF
- { "jnle", OP_JG, 1, 1, OPF_JMP|OPF_CC }, // 7f
+ { "jecxz",OP_JECXZ, 1, 1, OPF_JMP|OPF_CJMP },
+ { "jo", OP_JO, 1, 1, OPF_CJMP_CC }, // 70 OF=1
+ { "jno", OP_JNO, 1, 1, OPF_CJMP_CC }, // 71 OF=0
+ { "jc", OP_JC, 1, 1, OPF_CJMP_CC }, // 72 CF=1
+ { "jb", OP_JC, 1, 1, OPF_CJMP_CC }, // 72
+ { "jnc", OP_JNC, 1, 1, OPF_CJMP_CC }, // 73 CF=0
+ { "jnb", OP_JNC, 1, 1, OPF_CJMP_CC }, // 73
+ { "jae", OP_JNC, 1, 1, OPF_CJMP_CC }, // 73
+ { "jz", OP_JZ, 1, 1, OPF_CJMP_CC }, // 74 ZF=1
+ { "je", OP_JZ, 1, 1, OPF_CJMP_CC }, // 74
+ { "jnz", OP_JNZ, 1, 1, OPF_CJMP_CC }, // 75 ZF=0
+ { "jne", OP_JNZ, 1, 1, OPF_CJMP_CC }, // 75
+ { "jbe", OP_JBE, 1, 1, OPF_CJMP_CC }, // 76 CF=1 || ZF=1
+ { "jna", OP_JBE, 1, 1, OPF_CJMP_CC }, // 76
+ { "ja", OP_JA, 1, 1, OPF_CJMP_CC }, // 77 CF=0 && ZF=0
+ { "jnbe", OP_JA, 1, 1, OPF_CJMP_CC }, // 77
+ { "js", OP_JS, 1, 1, OPF_CJMP_CC }, // 78 SF=1
+ { "jns", OP_JNS, 1, 1, OPF_CJMP_CC }, // 79 SF=0
+ { "jp", OP_JP, 1, 1, OPF_CJMP_CC }, // 7a PF=1
+ { "jpe", OP_JP, 1, 1, OPF_CJMP_CC }, // 7a
+ { "jnp", OP_JNP, 1, 1, OPF_CJMP_CC }, // 7b PF=0
+ { "jpo", OP_JNP, 1, 1, OPF_CJMP_CC }, // 7b
+ { "jl", OP_JL, 1, 1, OPF_CJMP_CC }, // 7c SF!=OF
+ { "jnge", OP_JL, 1, 1, OPF_CJMP_CC }, // 7c
+ { "jge", OP_JGE, 1, 1, OPF_CJMP_CC }, // 7d SF=OF
+ { "jnl", OP_JGE, 1, 1, OPF_CJMP_CC }, // 7d
+ { "jle", OP_JLE, 1, 1, OPF_CJMP_CC }, // 7e ZF=1 || SF!=OF
+ { "jng", OP_JLE, 1, 1, OPF_CJMP_CC }, // 7e
+ { "jg", OP_JG, 1, 1, OPF_CJMP_CC }, // 7f ZF=0 && SF=OF
+ { "jnle", OP_JG, 1, 1, OPF_CJMP_CC }, // 7f
{ "seto", OP_JO, 1, 1, OPF_DATA|OPF_CC },
{ "setno", OP_JNO, 1, 1, OPF_DATA|OPF_CC },
{ "setc", OP_JC, 1, 1, OPF_DATA|OPF_CC },
break;
case OP_STOS:
+ case OP_SCAS:
if (op->operand_cnt != 0)
break;
- if (IS(words[op_w], "stosb"))
+ if (words[op_w][4] == 'b')
lmod = OPLM_BYTE;
- else if (IS(words[op_w], "stosw"))
+ else if (words[op_w][4] == 'w')
lmod = OPLM_WORD;
- else if (IS(words[op_w], "stosd"))
+ else if (words[op_w][4] == 'd')
lmod = OPLM_DWORD;
op->operand_cnt = 3;
setup_reg_opr(&op->operand[0], xDI, lmod, &op->regmask_src);
op->regmask_dst = op->regmask_src;
break;
+ case OP_JECXZ:
+ op->operand_cnt = 1;
+ op->regmask_src = 1 << xCX;
+ op->operand[0].type = OPT_REG;
+ op->operand[0].reg = xCX;
+ op->operand[0].lmod = OPLM_DWORD;
+ break;
+
case OP_IMUL:
if (op->operand_cnt != 1)
break;
}
break;
+ case OP_CALL:
+ // trashed regs must be explicitly detected later
+ op->regmask_dst = 0;
+ break;
+
default:
break;
}
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 stack_ra = 0;
int offset = 0;
- int sf_ofs;
- int lim;
- if (!IS_START(name, "ebp-")) {
+ ofs_reg[0] = 0;
+
+ if (IS_START(name, "ebp-")
+ || (IS_START(name, "ebp+") && '0' <= name[4] && name[4] <= '9'))
+ {
+ p = name + 4;
+ if (IS_START(p, "0x"))
+ p += 2;
+ offset = strtoul(p, &endp, 16);
+ if (name[3] == '-')
+ offset = -offset;
+ if (*endp != 0)
+ ferr(po, "ebp- parse of '%s' failed\n", name);
+ }
+ else {
bp_arg = parse_stack_el(name, ofs_reg);
snprintf(g_comment, sizeof(g_comment), "%s", bp_arg);
eq = equ_find(po, bp_arg, &offset);
ferr(po, "detected but missing eq\n");
offset += eq->offset;
}
- else {
- p = name + 4;
- if (IS_START(p, "0x"))
- p += 2;
- offset = -strtoul(p, &endp, 16);
- if (*endp != 0)
- ferr(po, "ebp- parse of '%s' failed\n", name);
- }
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_lea)
ferr(po, "lea/word to arg?\n");
- if (offset & 1)
- ferr(po, "unaligned arg access\n");
- if (is_src && (offset & 2) == 0)
- snprintf(buf, buf_size, "(u16)a%d", i + 1);
+ if (offset & 1) {
+ unaligned = 1;
+ if (!is_src) {
+ if (offset & 2)
+ ferr(po, "problematic arg store\n");
+ 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, "%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:
prefix = cast;
else if (is_src)
prefix = "(u32)";
+
if (offset & 3) {
- snprintf(g_comment, sizeof(g_comment), "%s unaligned", bp_arg);
+ unaligned = 1;
if (is_lea)
snprintf(buf, buf_size, "(u32)&a%d + %d",
i + 1, offset & 3);
- else
+ else if (!is_src)
+ ferr(po, "unaligned arg store\n");
+ else {
+ // mov edx, [ebp+arg_4+2]; movsx ecx, dx
snprintf(buf, buf_size, "%s(a%d >> %d)",
prefix, i + 1, (offset & 3) * 8);
+ }
}
else {
snprintf(buf, buf_size, "%s%sa%d",
ferr(po, "bp_arg bad lmod: %d\n", popr->lmod);
}
+ if (unaligned)
+ snprintf(g_comment, sizeof(g_comment), "%s unaligned", bp_arg);
+
// common problem
guess_lmod_from_c_type(&tmp_lmod, &g_func_pp->arg[i].type);
- if ((offset & 3) && 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;
}
}
+static void check_func_pp(struct parsed_op *po,
+ const struct parsed_proto *pp, const char *pfx)
+{
+ char buf[256];
+
+ if (pp->argc_reg != 0) {
+ if (/*!g_allow_regfunc &&*/ !pp->is_fastcall) {
+ pp_print(buf, sizeof(buf), pp);
+ ferr(po, "%s: unexpected reg arg in icall: %s\n", pfx, buf);
+ }
+ if (pp->argc_stack > 0 && pp->argc_reg != 2)
+ ferr(po, "%s: %d reg arg(s) with %d stack arg(s)\n",
+ pfx, pp->argc_reg, pp->argc_stack);
+ }
+}
+
static void check_label_read_ref(struct parsed_op *po, const char *name)
{
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);
- // currently we can take __cdecl and __stdcall
- if (pp->is_func && pp->argc_reg != 0)
- ferr(po, "reg-arg func reference?\n");
+ if (pp->is_func)
+ check_func_pp(po, pp, "ref");
}
static char *out_src_opr(char *buf, size_t buf_size,
- struct parsed_op *po, struct parsed_opr *popr, const char *cast,
+ struct parsed_op *po, struct parsed_opr *popr, const char *cast,
int is_lea)
{
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 && 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);
+ if (popr->val == 0 && strchr(cast, '*'))
+ snprintf(buf, buf_size, "NULL");
+ else
+ 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 && 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);
popr1->lmod = popr2->lmod;
else if (popr2->lmod == OPLM_UNSPEC)
popr2->lmod = popr1->lmod;
- else if (popr1->lmod != popr2->lmod)
- ferr(po, "conflicting lmods: %d vs %d\n", popr1->lmod, popr2->lmod);
+ else if (popr1->lmod != popr2->lmod) {
+ if (popr1->type_from_var) {
+ popr1->size_mismatch = 1;
+ if (popr1->lmod < popr2->lmod)
+ popr1->size_lt = 1;
+ popr1->lmod = popr2->lmod;
+ }
+ else if (popr2->type_from_var) {
+ popr2->size_mismatch = 1;
+ if (popr2->lmod < popr1->lmod)
+ popr2->size_lt = 1;
+ popr2->lmod = popr1->lmod;
+ }
+ else
+ ferr(po, "conflicting lmods: %d vs %d\n",
+ popr1->lmod, popr2->lmod);
+ }
}
static const char *op_to_c(struct parsed_op *po)
}
}
-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
#define LAST_OP(_i) ((ops[_i].flags & OPF_TAIL) \
- || (ops[_i].flags & (OPF_JMP|OPF_CC)) == OPF_JMP)
+ || (ops[_i].flags & (OPF_JMP|OPF_CJMP)) == OPF_JMP)
static int scan_for_pop(int i, int opcnt, const char *reg,
int magic, int depth, int *maxdepth, int do_flags)
{
+ const struct parsed_proto *pp;
struct parsed_op *po;
int ret = 0;
int j;
break; // already checked
po->cc_scratch = magic;
- if (po->flags & OPF_TAIL)
+ if (po->flags & OPF_TAIL) {
+ if (po->op == OP_CALL) {
+ pp = proto_parse(g_fhdr, po->operand[0].name, 0);
+ if (pp != NULL && pp->is_noreturn)
+ // no stack cleanup for noreturn
+ return ret;
+ }
return -1; // deadend
+ }
if ((po->flags & OPF_RMD)
|| (po->op == OP_PUSH && po->argnum != 0)) // arg push
if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
if (po->btj != NULL) {
// jumptable
- for (j = 0; j < po->btj->count - 1; j++) {
+ for (j = 0; j < po->btj->count; j++) {
ret |= scan_for_pop(po->btj->d[j].bt_i, opcnt, reg, magic,
depth, maxdepth, do_flags);
if (ret < 0)
return ret; // dead end
}
- // follow last jumptable entry
- i = po->btj->d[j].bt_i - 1;
- continue;
+ return ret;
}
if (po->bt_i < 0) {
return -1;
}
- if (po->flags & OPF_CC) {
+ if (po->flags & OPF_CJMP) {
ret |= scan_for_pop(po->bt_i, opcnt, reg, magic,
depth, maxdepth, do_flags);
if (ret < 0)
&& po->operand[0].type == OPT_REG
&& IS(po->operand[0].name, reg))
{
- if (po->op == OP_PUSH) {
+ if (po->op == OP_PUSH && !(po->flags & OPF_FARG)) {
depth++;
if (depth > *maxdepth)
*maxdepth = depth;
if (do_flags)
- set_flag_no_dup(po, OPF_RSAVE, OPF_RMD);
- }
- else if (depth == 0) {
- if (do_flags)
- set_flag_no_dup(po, OPF_RMD, OPF_RSAVE);
- return 1;
+ op_set_clear_flag(po, OPF_RSAVE, OPF_RMD);
}
- else {
- depth--;
- if (depth < 0) // should not happen
- ferr(po, "fail with depth\n");
- if (do_flags)
- set_flag_no_dup(po, OPF_RSAVE, OPF_RMD);
+ else if (po->op == OP_POP) {
+ if (depth == 0) {
+ if (do_flags)
+ op_set_clear_flag(po, OPF_RMD, OPF_RSAVE);
+ return 1;
+ }
+ else {
+ depth--;
+ if (depth < 0) // should not happen
+ ferr(po, "fail with depth\n");
+ if (do_flags)
+ op_set_clear_flag(po, OPF_RSAVE, OPF_RMD);
+ }
}
}
}
return found ? 0 : -1;
}
-// is operand 'opr modified' by parsed_op 'po'?
+static void scan_propagate_df(int i, int opcnt)
+{
+ struct parsed_op *po = &ops[i];
+ int j;
+
+ for (; i < opcnt; i++) {
+ po = &ops[i];
+ if (po->flags & OPF_DF)
+ return; // already resolved
+ po->flags |= OPF_DF;
+
+ if (po->op == OP_CALL)
+ ferr(po, "call with DF set?\n");
+
+ if (po->flags & OPF_JMP) {
+ if (po->btj != NULL) {
+ // jumptable
+ for (j = 0; j < po->btj->count; j++)
+ scan_propagate_df(po->btj->d[j].bt_i, opcnt);
+ return;
+ }
+
+ if (po->bt_i < 0) {
+ ferr(po, "dead branch\n");
+ return;
+ }
+
+ if (po->flags & OPF_CJMP)
+ scan_propagate_df(po->bt_i, opcnt);
+ else
+ i = po->bt_i - 1;
+ continue;
+ }
+
+ if (po->flags & OPF_TAIL)
+ break;
+
+ if (po->op == OP_CLD) {
+ po->flags |= OPF_RMD;
+ return;
+ }
+ }
+
+ ferr(po, "missing DF clear?\n");
+}
+
+// is operand 'opr' modified by parsed_op 'po'?
static int is_opr_modified(const struct parsed_opr *opr,
const struct parsed_op *po)
{
+ int mask;
+
if ((po->flags & OPF_RMD) || !(po->flags & OPF_DATA))
return 0;
- if (opr->type == OPT_REG && po->operand[0].type == OPT_REG) {
- if (po->regmask_dst & (1 << opr->reg))
- return 1;
- else
- return 0;
+ if (opr->type == OPT_REG) {
+ if (po->op == OP_CALL) {
+ mask = (1 << xAX) | (1 << xCX) | (1 << xDX);
+ if ((1 << opr->reg) & mask)
+ return 1;
+ else
+ return 0;
+ }
+
+ if (po->operand[0].type == OPT_REG) {
+ if (po->regmask_dst & (1 << opr->reg))
+ return 1;
+ else
+ return 0;
+ }
}
return IS(po->operand[0].name, opr->name);
// is any operand of parsed_op 'po_test' modified by parsed_op 'po'?
static int is_any_opr_modified(const struct parsed_op *po_test,
- const struct parsed_op *po)
+ const struct parsed_op *po, int c_mode)
{
+ int mask;
int i;
if ((po->flags & OPF_RMD) || !(po->flags & OPF_DATA))
// in reality, it can wreck any register, but in decompiled C
// version it can only overwrite eax or edx:eax
+ mask = (1 << xAX) | (1 << xDX);
+ if (!c_mode)
+ mask |= 1 << xCX;
+
if (po->op == OP_CALL
- && ((po_test->regmask_src | po_test->regmask_dst)
- & ((1 << xAX)|(1 << xDX))))
+ && ((po_test->regmask_src | po_test->regmask_dst) & mask))
return 1;
for (i = 0; i < po_test->operand_cnt; i++)
}
// scan for any po_test operand modification in range given
-static int scan_for_mod(struct parsed_op *po_test, int i, int opcnt)
+static int scan_for_mod(struct parsed_op *po_test, int i, int opcnt,
+ int c_mode)
{
if (po_test->operand_cnt == 1 && po_test->operand[0].type == OPT_CONST)
return -1;
for (; i < opcnt; i++) {
- if (is_any_opr_modified(po_test, &ops[i]))
+ if (is_any_opr_modified(po_test, &ops[i], c_mode))
return i;
}
}
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,
return 0;
}
- if ((ops[i].flags & OPF_JMP) && !(ops[i].flags & OPF_CC))
+ if ((ops[i].flags & (OPF_JMP|OPF_CJMP)) == OPF_JMP)
return -1;
}
// 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;
if (ops[i].regmask_dst & (1 << xDX))
return -1;
- if (g_labels[i][0] != 0)
- return -1;
}
return -1;
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
if (ops[i].regmask_dst & (1 << reg))
return -1;
- if (g_labels[i][0] != 0)
- return -1;
}
return -1;
// scan for positive, constant esp adjust
static int scan_for_esp_adjust(int i, int opcnt, int *adj)
{
+ const struct parsed_proto *pp;
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 && !(po->flags & OPF_RMD)) {
+ if (first_pop == -1 && *adj >= 0)
+ 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;
- // TODO: should only allow combining __cdecl calls..
+ break;
+ pp = po->datap;
+ if (pp != NULL && pp->is_stdcall)
+ break;
}
+ }
- if (g_labels[i][0] != 0)
- return -1;
+ if (*adj <= 8 && 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 int collect_call_args(struct parsed_op *po, int i,
- struct parsed_proto *pp, int *save_arg_vars, int arg,
- int need_op_saving, int may_reuse)
+static void scan_fwd_set_flags(int i, int opcnt, int magic, int flags)
{
- struct parsed_proto *pp_tmp;
- struct label_ref *lr;
- int need_to_save_current;
- int ret = 0;
+ struct parsed_op *po;
int j;
if (i < 0)
- ferr(po, "no refs for '%s'?\n", g_labels[i]);
+ ferr(ops, "%s: followed bad branch?\n", __func__);
- for (; arg < pp->argc; arg++)
- if (pp->arg[arg].reg == NULL)
- break;
+ for (; i < opcnt; i++) {
+ po = &ops[i];
+ if (po->cc_scratch == magic)
+ return;
+ po->cc_scratch = magic;
+ po->flags |= flags;
- for (j = i; j >= 0 && arg < pp->argc; )
- {
- if (g_labels[j][0] != 0) {
+ if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
+ if (po->btj != NULL) {
+ // jumptable
+ for (j = 0; j < po->btj->count; j++)
+ scan_fwd_set_flags(po->btj->d[j].bt_i, opcnt, magic, flags);
+ return;
+ }
+
+ scan_fwd_set_flags(po->bt_i, opcnt, magic, flags);
+ if (!(po->flags & OPF_CJMP))
+ return;
+ }
+ if (po->flags & OPF_TAIL)
+ return;
+ }
+}
+
+static const struct parsed_proto *try_recover_pp(
+ struct parsed_op *po, const struct parsed_opr *opr, int *search_instead)
+{
+ const struct parsed_proto *pp = NULL;
+ char buf[256];
+ char *p;
+
+ // 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) {
+ // search who set the stack var instead
+ if (search_instead != NULL)
+ *search_instead = 1;
+ return NULL;
+ }
+
+ 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);
+ check_func_pp(po, pp, "icall arg");
+ }
+ else if (opr->type == OPT_REGMEM && strchr(opr->name + 1, '[')) {
+ // label[index]
+ p = strchr(opr->name + 1, '[');
+ memcpy(buf, opr->name, p - opr->name);
+ buf[p - opr->name] = 0;
+ pp = proto_parse(g_fhdr, buf, 0);
+ }
+ 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");
+ }
+
+ return pp;
+}
+
+static void scan_for_call_type(int i, const struct parsed_opr *opr,
+ int magic, const struct parsed_proto **pp_found, int *multi)
+{
+ 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, multi);
+ 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);
+ check_func_pp(po, pp, "icall reg-arg");
+ }
+ else
+ pp = try_recover_pp(po, opr, NULL);
+
+ if (*pp_found != NULL && pp != NULL && *pp_found != pp) {
+ if (!IS((*pp_found)->ret_type.name, pp->ret_type.name)
+ || (*pp_found)->is_stdcall != pp->is_stdcall
+ || (*pp_found)->is_fptr != pp->is_fptr
+ || (*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");
+ }
+ *multi = 1;
+ }
+ if (pp != NULL)
+ *pp_found = pp;
+}
+
+static const struct parsed_proto *resolve_icall(int i, int opcnt,
+ int *multi_src)
+{
+ const struct parsed_proto *pp = NULL;
+ int search_advice = 0;
+
+ *multi_src = 0;
+
+ 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], &search_advice);
+ if (!search_advice)
+ break;
+ // fallthrough
+ default:
+ scan_for_call_type(i, &ops[i].operand[0], i + opcnt * 9, &pp,
+ multi_src);
+ break;
+ }
+
+ 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,
+ int magic, int need_op_saving, int may_reuse)
+{
+ struct parsed_proto *pp_tmp;
+ struct label_ref *lr;
+ int need_to_save_current;
+ int ret = 0;
+ int j;
+
+ if (i < 0) {
+ ferr(po, "dead label encountered\n");
+ return -1;
+ }
+
+ for (; arg < pp->argc; arg++)
+ if (pp->arg[arg].reg == NULL)
+ break;
+ magic = (magic & 0xffffff) | (arg << 24);
+
+ for (j = i; j >= 0 && (arg < pp->argc || pp->is_unresolved); )
+ {
+ if (((ops[j].cc_scratch ^ magic) & 0xffffff) == 0) {
+ if (ops[j].cc_scratch != magic) {
+ ferr(&ops[j], "arg collect hit same path with diff args for %s\n",
+ pp->name);
+ return -1;
+ }
+ // ok: have already been here
+ return 0;
+ }
+ ops[j].cc_scratch = magic;
+
+ 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;
for (; lr->next; lr = lr->next) {
- if ((ops[lr->i].flags & (OPF_JMP|OPF_CC)) != OPF_JMP)
+ if ((ops[lr->i].flags & (OPF_JMP|OPF_CJMP)) != OPF_JMP)
may_reuse = 1;
- ret |= collect_call_args(po, lr->i, pp, save_arg_vars,
- arg, need_op_saving, may_reuse);
+ ret = collect_call_args_r(po, lr->i, pp, regmask, save_arg_vars,
+ arg, magic, need_op_saving, may_reuse);
+ if (ret < 0)
+ return ret;
}
- if ((ops[lr->i].flags & (OPF_JMP|OPF_CC)) != OPF_JMP)
+ if ((ops[lr->i].flags & (OPF_JMP|OPF_CJMP)) != OPF_JMP)
may_reuse = 1;
if (j > 0 && LAST_OP(j - 1)) {
// follow last branch in reverse
continue;
}
need_op_saving = 1;
- ret |= collect_call_args(po, lr->i, pp, save_arg_vars,
- arg, need_op_saving, may_reuse);
+ ret = collect_call_args_r(po, lr->i, pp, regmask, save_arg_vars,
+ arg, magic, need_op_saving, may_reuse);
+ if (ret < 0)
+ return ret;
}
j--;
if (ops[j].op == OP_CALL)
{
+ if (pp->is_unresolved)
+ break;
+
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);
}
else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP) {
+ if (pp->is_unresolved)
+ break;
+
ferr(po, "arg collect %d/%d hit esp adjust\n",
arg, pp->argc);
}
else if (ops[j].op == OP_POP) {
+ if (pp->is_unresolved)
+ break;
+
ferr(po, "arg collect %d/%d hit pop\n", arg, pp->argc);
}
- else if ((ops[j].flags & (OPF_JMP|OPF_CC)) == (OPF_JMP|OPF_CC))
+ else if (ops[j].flags & OPF_CJMP)
{
+ if (pp->is_unresolved)
+ break;
+
may_reuse = 1;
}
else if (ops[j].op == OP_PUSH && !(ops[j].flags & OPF_FARG))
{
+ if (pp->is_unresolved && (ops[j].flags & OPF_RMD))
+ break;
+
pp->arg[arg].datap = &ops[j];
need_to_save_current = 0;
if (!need_op_saving) {
- ret = scan_for_mod(&ops[j], j + 1, i);
+ ret = scan_for_mod(&ops[j], j + 1, i, 1);
need_to_save_current = (ret >= 0);
}
if (need_op_saving || need_to_save_current) {
if (!may_reuse)
ops[j].flags |= OPF_FARG;
- // next arg
- for (arg++; arg < pp->argc; arg++)
- if (pp->arg[arg].reg == NULL)
- break;
+ ops[j].flags &= ~OPF_RSAVE;
+
+ arg++;
+ if (!pp->is_unresolved) {
+ // next arg
+ for (; arg < pp->argc; arg++)
+ if (pp->arg[arg].reg == NULL)
+ break;
+ }
+ magic = (magic & 0xffffff) | (arg << 24);
+
+ // tracking reg usage
+ if (ops[j].operand[0].type == OPT_REG)
+ *regmask |= 1 << ops[j].operand[0].reg;
}
}
if (arg < pp->argc) {
ferr(po, "arg collect failed for '%s': %d/%d\n",
pp->name, arg, pp->argc);
- ret = -1;
+ return -1;
+ }
+
+ return arg;
+}
+
+static int collect_call_args(struct parsed_op *po, int i,
+ struct parsed_proto *pp, int *regmask, int *save_arg_vars,
+ int magic)
+{
+ int ret;
+ int a;
+
+ ret = collect_call_args_r(po, i, pp, regmask, save_arg_vars,
+ 0, magic, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ if (pp->is_unresolved) {
+ pp->argc += ret;
+ pp->argc_stack += ret;
+ for (a = 0; a < pp->argc; a++)
+ if (pp->arg[a].type.name == NULL)
+ pp->arg[a].type.name = strdup("int");
}
+
return ret;
}
+static void pp_insert_reg_arg(struct parsed_proto *pp, const char *reg)
+{
+ int i;
+
+ for (i = 0; i < pp->argc; i++)
+ if (pp->arg[i].reg == NULL)
+ break;
+
+ if (pp->argc_stack)
+ memmove(&pp->arg[i + 1], &pp->arg[i],
+ sizeof(pp->arg[0]) * pp->argc_stack);
+ memset(&pp->arg[i], 0, sizeof(pp->arg[i]));
+ pp->arg[i].reg = strdup(reg);
+ pp->arg[i].type.name = strdup("int");
+ pp->argc++;
+ pp->argc_reg++;
+}
+
static void add_label_ref(struct label_ref *lr, int op_i)
{
struct label_ref *lr_new;
}
}
+static void output_pp_attrs(FILE *fout, const struct parsed_proto *pp,
+ int is_noreturn)
+{
+ if (pp->is_fastcall)
+ fprintf(fout, "__fastcall ");
+ else if (pp->is_stdcall && pp->argc_reg == 0)
+ fprintf(fout, "__stdcall ");
+ if (pp->is_noreturn || is_noreturn)
+ fprintf(fout, "noreturn ");
+}
+
static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
{
struct parsed_op *po, *delayed_flag_op = NULL, *tmp_op;
int label_pending = 0;
int regmask_save = 0;
int regmask_arg = 0;
+ int regmask_now = 0;
+ int regmask_init = 0;
int regmask = 0;
int pfomask = 0;
int found = 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);
fprintf(fout, "%s ", g_func_pp->ret_type.name);
- if (g_func_pp->is_stdcall && g_func_pp->argc_reg == 0)
- fprintf(fout, "__stdcall ");
- if (g_ida_func_attr & IDAFA_NORETURN)
- fprintf(fout, "noreturn ");
+ output_pp_attrs(fout, g_func_pp, g_ida_func_attr & IDAFA_NORETURN);
fprintf(fout, "%s(", funcn);
for (i = 0; i < g_func_pp->argc; i++) {
// func pointer..
pp = g_func_pp->arg[i].fptr;
fprintf(fout, "%s (", pp->ret_type.name);
- if (pp->is_stdcall && pp->argc_reg == 0)
- fprintf(fout, "__stdcall ");
+ output_pp_attrs(fout, pp, 0);
fprintf(fout, "*a%d)(", i + 1);
for (j = 0; j < pp->argc; j++) {
if (j > 0)
else {
fprintf(fout, "%s a%d", g_func_pp->arg[i].type.name, i + 1);
}
+ 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;
+ }
}
if (g_func_pp->is_vararg) {
if (i > 0)
if (i == opcnt && (ops[i - 1].flags & OPF_JMP) && found)
break;
- if (ops[i - 1].op == OP_POP && IS(opr_name(&ops[i - 1], 0), "ebp"))
+ if ((ops[i - 1].op == OP_POP && IS(opr_name(&ops[i - 1], 0), "ebp"))
+ || ops[i - 1].op == OP_LEAVE)
+ {
ops[i - 1].flags |= OPF_RMD;
+ }
else if (!(g_ida_func_attr & IDAFA_NORETURN))
ferr(&ops[i - 1], "'pop ebp' expected\n");
{
ops[i - 2].flags |= OPF_RMD;
}
- else if (!(g_ida_func_attr & IDAFA_NORETURN))
+ else if (ops[i - 1].op != OP_LEAVE
+ && !(g_ida_func_attr & IDAFA_NORETURN))
+ {
ferr(&ops[i - 2], "esp restore expected\n");
+ }
if (ecx_push && ops[i - 3].op == OP_POP
&& IS(opr_name(&ops[i - 3], 0), "ecx"))
}
// 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) {
+ pp = NULL;
+
+ 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);
+
+ pp = proto_clone(pp_c);
+ my_assert_not(pp, NULL);
+ }
+ 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) {
+ if (pp->is_fptr)
+ check_func_pp(po, pp, "fptr var call");
+ if (pp->is_noreturn)
+ po->flags |= OPF_TAIL;
+ po->datap = pp;
+ }
+ continue;
+ }
+
+ if (!(po->flags & OPF_JMP) || po->op == OP_RET)
continue;
if (po->operand[0].type == OPT_REGMEM) {
char *p = strchr(po->operand[0].name, '[');
if (p == NULL)
- ferr(po, "unhandled indirect branch\n");
+ goto tailcall;
ret = p - po->operand[0].name;
strncpy(buf1, po->operand[0].name, ret);
buf1[ret] = 0;
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_icall(i, opcnt, &l);
+ if (pp_c != NULL) {
+ pp = proto_clone(pp_c);
+ my_assert_not(pp, NULL);
+ if (l)
+ // not resolved just to single func
+ pp->is_fptr = 1;
+ }
+ 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);
+ 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->datap = pp;
}
// look for and make use of esp adjust
tmpname, pp->argc_stack * 4, j);
ops[ret].flags |= OPF_RMD;
- // a bit of a hack, but deals with use of
- // single adj for multiple calls
- ops[ret].operand[1].val -= j;
+ 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 {
+ // 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);
- // can't call functions with non-__cdecl callbacks yet
- 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->is_unresolved) {
+ // since we know the args, collect them
+ collect_call_args(po, i, pp, ®mask, &save_arg_vars,
+ i + opcnt * 2);
}
- collect_call_args(po, i, pp, &save_arg_vars, 0, 0, 0);
-
if (strstr(pp->ret_type.name, "int64"))
need_mul_var = 1;
- po->datap = pp;
}
}
// pass4:
// - 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++) {
po = &ops[i];
if (po->flags & OPF_RMD)
continue;
- if (po->op == OP_PUSH
- && po->argnum == 0 && !(po->flags & OPF_RSAVE)
- && po->operand[0].type == OPT_REG)
- {
+ if (po->op == OP_PUSH && (po->flags & OPF_RSAVE)) {
reg = po->operand[0].reg;
- if (reg < 0)
- ferr(po, "reg not set for push?\n");
-
- depth = 0;
- ret = scan_for_pop(i + 1, opcnt,
- po->operand[0].name, i + opcnt, 0, &depth, 0);
- 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,
- i + opcnt * 2, 0, &depth, 1);
- continue;
+ 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))
+ {
+ if (po->operand[0].type == OPT_REG)
+ {
+ reg = po->operand[0].reg;
+ if (reg < 0)
+ ferr(po, "reg not set for push?\n");
+
+ depth = 0;
+ ret = scan_for_pop(i + 1, opcnt,
+ po->operand[0].name, i + opcnt * 3, 0, &depth, 0);
+ if (ret == 1) {
+ if (depth > 1)
+ ferr(po, "too much depth: %d\n", depth);
+
+ po->flags |= OPF_RMD;
+ scan_for_pop(i + 1, opcnt, po->operand[0].name,
+ i + opcnt * 4, 0, &depth, 1);
+ continue;
+ }
+ ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, 0);
+ if (ret == 0) {
+ arg = OPF_RMD;
+ if (regmask & (1 << reg)) {
+ if (regmask_save & (1 << reg))
+ ferr(po, "%s already saved?\n", po->operand[0].name);
+ arg = OPF_RSAVE;
+ }
+ po->flags |= arg;
+ scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, arg);
+ continue;
+ }
}
- ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, 0);
- if (ret == 0) {
- arg = OPF_RMD;
- if (regmask & (1 << reg)) {
- if (regmask_save & (1 << reg))
- ferr(po, "%s already saved?\n", po->operand[0].name);
- arg = OPF_RSAVE;
+ 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][0] != 0)
+ {
+ break;
+ }
+
+ if (!(ops[j].flags & OPF_RMD) && ops[j].op == OP_POP)
+ {
+ po->flags |= OPF_RMD;
+ ops[j].datap = po;
+ break;
+ }
}
- po->flags |= arg;
- scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, arg);
- continue;
}
}
- regmask |= po->regmask_src | po->regmask_dst;
+ if (po->op == OP_STD) {
+ po->flags |= OPF_DF | OPF_RMD;
+ scan_propagate_df(i + 1, opcnt);
+ }
+
+ regmask_now = po->regmask_src | po->regmask_dst;
+ if (regmask_now & (1 << xBP)) {
+ if (g_bp_frame && !(po->flags & OPF_EBP_S)) {
+ if (po->regmask_dst & (1 << xBP))
+ // compiler decided to drop bp frame and use ebp as scratch
+ scan_fwd_set_flags(i, opcnt, i + opcnt * 5, OPF_EBP_S);
+ else
+ regmask_now &= ~(1 << xBP);
+ }
+ }
+
+ regmask |= regmask_now;
if (po->flags & OPF_CC)
{
// to get nicer code, we try to delay test and cmp;
// if we can't because of operand modification, or if we
- // have math op, or branch, make it calculate flags explicitly
- if (tmp_op->op == OP_TEST || tmp_op->op == OP_CMP) {
- if (branched || scan_for_mod(tmp_op, setters[j] + 1, i) >= 0)
+ // have arith op, or branch, make it calculate flags explicitly
+ if (tmp_op->op == OP_TEST || tmp_op->op == OP_CMP)
+ {
+ if (branched || scan_for_mod(tmp_op, setters[j] + 1, i, 0) >= 0)
pfomask = 1 << pfo;
}
+ else if (tmp_op->op == OP_SCAS) {
+ pfomask = 1 << pfo;
+ }
else if (tmp_op->op == OP_CMPS) {
pfomask = 1 << PFO_Z;
}
if (po->op == OP_ADC || po->op == OP_SBB)
cmp_result_vars |= 1 << PFO_C;
}
+ else if (po->op == OP_CMPS || po->op == OP_SCAS) {
+ cmp_result_vars |= 1 << PFO_Z;
+ }
else if (po->op == OP_MUL
|| (po->op == OP_IMUL && po->operand_cnt == 1))
{
need_mul_var = 1;
}
- else if (po->op == OP_CALL && po->operand[0].type != OPT_LABEL) {
+ else if (po->op == OP_CALL) {
pp = po->datap;
- my_assert_not(pp, NULL);
- fprintf(fout, " %s (*icall%d)(", pp->ret_type.name, i);
- for (j = 0; j < pp->argc; j++) {
- if (j > 0)
- fprintf(fout, ", ");
- fprintf(fout, "%s a%d", pp->arg[j].type.name, j + 1);
+ if (pp == NULL)
+ ferr(po, "NULL pp\n");
+
+ if (pp->is_unresolved) {
+ int regmask_stack = 0;
+ collect_call_args(po, i, pp, ®mask, &save_arg_vars,
+ i + opcnt * 2);
+
+ // this is pretty rough guess:
+ // see ecx and edx were pushed (and not their saved versions)
+ for (arg = 0; arg < pp->argc; arg++) {
+ if (pp->arg[arg].reg != NULL)
+ continue;
+
+ tmp_op = pp->arg[arg].datap;
+ if (tmp_op == NULL)
+ ferr(po, "parsed_op missing for arg%d\n", arg);
+ if (tmp_op->argnum == 0 && tmp_op->operand[0].type == OPT_REG)
+ regmask_stack |= 1 << tmp_op->operand[0].reg;
+ }
+
+ if (!((regmask_stack & (1 << xCX))
+ && (regmask_stack & (1 << xDX))))
+ {
+ if (pp->argc_stack != 0
+ || ((regmask | regmask_arg) & ((1 << xCX)|(1 << xDX))))
+ {
+ pp_insert_reg_arg(pp, "ecx");
+ pp->is_fastcall = 1;
+ regmask_init |= 1 << xCX;
+ regmask |= 1 << xCX;
+ }
+ if (pp->argc_stack != 0
+ || ((regmask | regmask_arg) & (1 << xDX)))
+ {
+ pp_insert_reg_arg(pp, "edx");
+ regmask_init |= 1 << xDX;
+ regmask |= 1 << xDX;
+ }
+ }
+
+ // note: __cdecl doesn't fall into is_unresolved category
+ if (pp->argc_stack > 0)
+ pp->is_stdcall = 1;
+ }
+
+ for (arg = 0; arg < pp->argc; arg++) {
+ if (pp->arg[arg].reg != NULL) {
+ reg = char_array_i(regs_r32,
+ ARRAY_SIZE(regs_r32), pp->arg[arg].reg);
+ if (reg < 0)
+ ferr(ops, "arg '%s' is not a reg?\n", pp->arg[arg].reg);
+ if (!(regmask & (1 << reg))) {
+ regmask_init |= 1 << reg;
+ regmask |= 1 << reg;
+ }
+ }
+ }
+
+ if (pp->is_fptr) {
+ if (pp->name[0] != 0) {
+ memmove(pp->name + 2, pp->name, strlen(pp->name) + 1);
+ memcpy(pp->name, "i_", 2);
+
+ // might be declared already
+ found = 0;
+ for (j = 0; j < i; j++) {
+ if (ops[j].op == OP_CALL && (pp_tmp = ops[j].datap)) {
+ if (pp_tmp->is_fptr && IS(pp->name, pp_tmp->name)) {
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (found)
+ continue;
+ }
+ else
+ snprintf(pp->name, sizeof(pp->name), "icall%d", i);
+
+ fprintf(fout, " %s (", pp->ret_type.name);
+ output_pp_attrs(fout, pp, 0);
+ fprintf(fout, "*%s)(", pp->name);
+ for (j = 0; j < pp->argc; j++) {
+ if (j > 0)
+ fprintf(fout, ", ");
+ fprintf(fout, "%s a%d", pp->arg[j].type.name, j + 1);
+ }
+ fprintf(fout, ");\n");
}
- fprintf(fout, ");\n");
}
+ else if (po->op == OP_RET && !IS(g_func_pp->ret_type.name, "void"))
+ regmask |= 1 << xAX;
}
+ // pass4:
+ // - confirm regmask_save, it might have been reduced
+ if (regmask_save != 0)
+ {
+ regmask_save = 0;
+ for (i = 0; i < opcnt; i++) {
+ po = &ops[i];
+ if (po->flags & OPF_RMD)
+ continue;
+
+ if (po->op == OP_PUSH && (po->flags & OPF_RSAVE))
+ regmask_save |= 1 << po->operand[0].reg;
+ }
+ }
+
+
// output LUTs/jumptables
for (i = 0; i < g_func_pd_cnt; i++) {
pd = &g_func_pd[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;
- fprintf(fout, " u32 %s = (u32)a%d;\n",
- g_func_pp->arg[i].reg, i + 1);
+ if (regmask & (1 << reg)) {
+ fprintf(fout, " u32 %s = (u32)a%d;\n",
+ g_func_pp->arg[i].reg, i + 1);
+ }
+ else
+ fprintf(fout, " // %s = a%d; // unused\n",
+ g_func_pp->arg[i].reg, i + 1);
had_decl = 1;
}
}
- // declare other regs - special case for eax
- if (!((regmask | regmask_arg) & 1)
- && !IS(g_func_pp->ret_type.name, "void"))
- {
- fprintf(fout, " u32 eax = 0;\n");
- had_decl = 1;
- }
-
- regmask &= ~regmask_arg;
- regmask &= ~(1 << xSP);
- if (g_bp_frame)
- regmask &= ~(1 << xBP);
- if (regmask) {
+ regmask_now = regmask & ~regmask_arg;
+ regmask_now &= ~(1 << xSP);
+ if (regmask_now) {
for (reg = 0; reg < 8; reg++) {
- if (regmask & (1 << reg)) {
- fprintf(fout, " u32 %s;\n", regs_r32[reg]);
+ if (regmask_now & (1 << reg)) {
+ fprintf(fout, " u32 %s", regs_r32[reg]);
+ if (regmask_init & (1 << reg))
+ fprintf(fout, " = 0");
+ fprintf(fout, ";\n");
had_decl = 1;
}
}
// 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:
break;
case OP_STOS:
- // assumes DF=0
assert_operand_cnt(3);
if (po->flags & OPF_REP) {
- fprintf(fout, " for (; ecx != 0; ecx--, edi += %d)\n",
+ fprintf(fout, " for (; ecx != 0; ecx--, edi %c= %d)\n",
+ (po->flags & OPF_DF) ? '-' : '+',
lmod_bytes(po, po->operand[0].lmod));
fprintf(fout, " %sedi = eax;",
lmod_cast_u_ptr(po, po->operand[0].lmod));
strcpy(g_comment, "rep stos");
}
else {
- fprintf(fout, " %sedi = eax; edi += %d;",
+ fprintf(fout, " %sedi = eax; edi %c= %d;",
lmod_cast_u_ptr(po, po->operand[0].lmod),
+ (po->flags & OPF_DF) ? '-' : '+',
lmod_bytes(po, po->operand[0].lmod));
strcpy(g_comment, "stos");
}
break;
case OP_MOVS:
- // assumes DF=0
assert_operand_cnt(3);
j = lmod_bytes(po, po->operand[0].lmod);
strcpy(buf1, lmod_cast_u_ptr(po, po->operand[0].lmod));
+ l = (po->flags & OPF_DF) ? '-' : '+';
if (po->flags & OPF_REP) {
fprintf(fout,
- " for (; ecx != 0; ecx--, edi += %d, esi += %d)\n",
- j, j);
+ " for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d)\n",
+ l, j, l, j);
fprintf(fout,
" %sedi = %sesi;", buf1, buf1);
strcpy(g_comment, "rep movs");
}
else {
- fprintf(fout, " %sedi = %sesi; edi += %d; esi += %d;",
- buf1, buf1, j, j);
+ fprintf(fout, " %sedi = %sesi; edi %c= %d; esi %c= %d;",
+ buf1, buf1, l, j, l, j);
strcpy(g_comment, "movs");
}
break;
case OP_CMPS:
- // assumes DF=0
// repe ~ repeat while ZF=1
assert_operand_cnt(3);
j = lmod_bytes(po, po->operand[0].lmod);
strcpy(buf1, lmod_cast_u_ptr(po, po->operand[0].lmod));
+ l = (po->flags & OPF_DF) ? '-' : '+';
if (po->flags & OPF_REP) {
fprintf(fout,
- " for (; ecx != 0; ecx--, edi += %d, esi += %d)\n",
- j, j);
+ " for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d)\n",
+ l, j, l, j);
fprintf(fout,
" if ((cond_z = (%sedi == %sesi)) %s 0)\n",
buf1, buf1, (po->flags & OPF_REPZ) ? "==" : "!=");
}
else {
fprintf(fout,
- " cond_z = (%sedi = %sesi); edi += %d; esi += %d;",
- buf1, buf1, j, j);
+ " cond_z = (%sedi = %sesi); edi %c= %d; esi %c= %d;",
+ buf1, buf1, l, j, l, j);
strcpy(g_comment, "cmps");
}
pfomask &= ~(1 << PFO_Z);
delayed_flag_op = NULL;
break;
+ case OP_SCAS:
+ // only does ZF (for now)
+ // repe ~ repeat while ZF=1
+ assert_operand_cnt(3);
+ j = lmod_bytes(po, po->operand[0].lmod);
+ l = (po->flags & OPF_DF) ? '-' : '+';
+ if (po->flags & OPF_REP) {
+ fprintf(fout,
+ " for (; ecx != 0; ecx--, edi %c= %d)\n", l, j);
+ fprintf(fout,
+ " if ((cond_z = (%seax == %sedi)) %s 0)\n",
+ lmod_cast_u(po, po->operand[0].lmod),
+ lmod_cast_u_ptr(po, po->operand[0].lmod),
+ (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;",
+ lmod_cast_u(po, po->operand[0].lmod),
+ lmod_cast_u_ptr(po, po->operand[0].lmod), l, j);
+ strcpy(g_comment, "scas");
+ }
+ pfomask &= ~(1 << PFO_Z);
+ last_arith_dst = NULL;
+ delayed_flag_op = NULL;
+ break;
+
// arithmetic w/flags
case OP_ADD:
case OP_SUB:
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,
// else SETcc - should already be handled
break;
+ case OP_JECXZ:
+ fprintf(fout, " if (ecx == 0)\n");
+ fprintf(fout, " goto %s;", po->operand[0].name);
+ strcat(g_comment, "jecxz");
+ break;
+
case OP_JMP:
assert_operand_cnt(1);
last_arith_dst = NULL;
case OP_CALL:
assert_operand_cnt(1);
pp = po->datap;
- if (pp == NULL)
- ferr(po, "NULL pp\n");
+ my_assert_not(pp, NULL);
- 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]));
+ if (pp->is_fptr)
+ fprintf(fout, " %s = %s;\n", pp->name,
+ out_src_opr(buf1, sizeof(buf1), po, &po->operand[0],
+ "(void *)", 0));
fprintf(fout, " ");
if (strstr(pp->ret_type.name, "int64")) {
fprintf(fout, "(%s)", g_func_pp->ret_type.name);
}
}
- else {
+ else if (regmask & (1 << xAX)) {
fprintf(fout, "eax = ");
if (pp->ret_type.is_ptr)
fprintf(fout, "(u32)");
}
}
- if (po->operand[0].type != OPT_LABEL) {
- fprintf(fout, "icall%d(", i);
- }
- else {
- if (pp->name[0] == 0)
- ferr(po, "missing pp->name\n");
- fprintf(fout, "%s%s(", pp->name,
- pp->has_structarg ? "_sa" : "");
- }
+ if (pp->name[0] == 0)
+ ferr(po, "missing pp->name\n");
+ fprintf(fout, "%s%s(", pp->name,
+ pp->has_structarg ? "_sa" : "");
for (arg = 0; arg < pp->argc; arg++) {
if (arg > 0)
fprintf(fout, " eax = mul_tmp;");
}
+ if (pp->is_unresolved) {
+ snprintf(buf3, sizeof(buf3), " unresoved %dreg",
+ pp->argc_reg);
+ strcat(g_comment, buf3);
+ }
+
if (po->flags & OPF_TAIL) {
- strcpy(g_comment, "tailcall");
ret = 0;
- if (i == opcnt - 1)
+ if (i == opcnt - 1 || pp->is_noreturn)
ret = 0;
else if (IS(pp->ret_type.name, "void"))
ret = 1;
// else already handled as 'return f()'
if (ret) {
- fprintf(fout, "\n return;");
- strcpy(g_comment, "^ tailcall");
+ if (!IS(g_func_pp->ret_type.name, "void")) {
+ ferr(po, "int func -> void func tailcall?\n");
+ }
+ else {
+ fprintf(fout, "\n return;");
+ strcat(g_comment, " ^ tailcall");
+ }
}
+ else
+ strcat(g_comment, " tailcall");
}
+ if (pp->is_noreturn)
+ strcat(g_comment, " noreturn");
delayed_flag_op = NULL;
last_arith_dst = NULL;
break;
fprintf(fout, " %s = s_%s;", buf1, buf1);
break;
}
+ else if (po->datap != NULL) {
+ // push/pop pair
+ tmp_op = po->datap;
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ fprintf(fout, " %s = %s;", buf1,
+ out_src_opr(buf2, sizeof(buf2),
+ tmp_op, &tmp_op->operand[0],
+ po->operand[0].is_ptr ? "(void *)" : "", 0));
+ break;
+ }
ferr(po, "stray pop encountered\n");
break;
}
if (g_comment[0] != 0) {
- fprintf(fout, " // %s", g_comment);
+ char *p = g_comment;
+ while (my_isblank(*p))
+ p++;
+ fprintf(fout, " // %s", p);
g_comment[0] = 0;
no_output = 0;
}
fprintf(fout, "\n");
// some sanity checking
- if ((po->flags & OPF_REP) && po->op != OP_STOS
- && po->op != OP_MOVS && po->op != OP_CMPS)
- ferr(po, "unexpected rep\n");
- if ((po->flags & (OPF_REPZ|OPF_REPNZ)) && po->op != OP_CMPS)
+ if (po->flags & OPF_REP) {
+ if (po->op != OP_STOS && po->op != OP_MOVS
+ && po->op != OP_CMPS && po->op != OP_SCAS)
+ ferr(po, "unexpected rep\n");
+ if (!(po->flags & (OPF_REPZ|OPF_REPNZ))
+ && (po->op == OP_CMPS || po->op == OP_SCAS))
+ ferr(po, "cmps/scas with plain rep\n");
+ }
+ if ((po->flags & (OPF_REPZ|OPF_REPNZ))
+ && po->op != OP_CMPS && po->op != OP_SCAS)
ferr(po, "unexpected repz/repnz\n");
if (pfomask != 0)
// see is delayed flag stuff is still valid
if (delayed_flag_op != NULL && delayed_flag_op != po) {
- if (is_any_opr_modified(delayed_flag_op, po))
+ if (is_any_opr_modified(delayed_flag_op, po, 0))
delayed_flag_op = NULL;
}
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;
+ char *sctproto = NULL;
int in_func = 0;
int pending_endp = 0;
int skip_func = 0;
int eq_alloc;
int verbose = 0;
int arg_out;
- int arg = 1;
+ int arg;
int pi = 0;
int i, j;
int ret, len;
char *p;
int wordc;
- if (argv[1] && IS(argv[1], "-v")) {
- verbose = 1;
- arg++;
+ for (arg = 1; arg < argc; arg++) {
+ if (IS(argv[arg], "-v"))
+ verbose = 1;
+ else if (IS(argv[arg], "-rf"))
+ g_allow_regfunc = 1;
+ else
+ break;
}
if (argc < arg + 3) {
- printf("usage:\n%s [-v] <.c> <.asm> <hdrf> [rlist]*\n",
+ printf("usage:\n%s [-v] [-rf] <.c> <.asm> <hdrf> [rlist]*\n",
argv[0]);
return 1;
}
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")
+ || (g_allow_regfunc && IS_START(p, "#if NO_REGFUNC")))
+ {
+ 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);
- }
- 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;
+ aerr("missing name for func chunk?\n");
+
+ if (!scanned_ahead) {
+ add_func_chunk(fasm, words[0], asmln);
+ 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:")) {
- p = sskip(p + 11);
- if (*p == 0 || *p == ';')
- continue;
- goto parse_words; // lame
+ if (*p == ';') {
+ if (IS_START(p, "; sctpatch:")) {
+ p = sskip(p + 11);
+ if (*p == 0 || *p == ';')
+ continue;
+ goto parse_words; // lame
+ }
+ if (IS_START(p, "; sctproto:")) {
+ sctproto = strdup(p + 11);
+ }
}
if (wordc == 0) {
i = 2;
}
else {
+ if (pd == NULL) {
+ if (verbose)
+ anote("skipping alignment byte?\n");
+ continue;
+ }
lmod = lmod_from_directive(words[0]);
if (lmod != pd->lmod)
aerr("lmod change? %d->%d\n", pd->lmod, lmod);
aerr("proc '%s' while in_func '%s'?\n",
words[0], g_func);
p = words[0];
- if ((g_ida_func_attr & IDAFA_THUNK)
- || bsearch(&p, rlist, rlist_len, sizeof(rlist[0]), cmpstringp))
+ if (bsearch(&p, rlist, rlist_len, sizeof(rlist[0]), cmpstringp))
skip_func = 1;
strcpy(g_func, words[0]);
set_label(0, words[0]);
aerr("endp '%s' while in_func '%s'?\n",
words[0], g_func);
+ if ((g_ida_func_attr & IDAFA_THUNK) && pi == 1
+ && ops[0].op == OP_JMP && ops[0].operand[0].had_ds)
+ {
+ // import jump
+ skip_func = 1;
+ }
+
if (!skip_func && func_chunks_used) {
// start processing chunks
struct chunk_item *ci, key = { g_func, 0 };
continue;
}
+ if (wordc == 2 && IS(words[1], "ends"))
+ break;
+
p = strchr(words[0], ':');
if (p != NULL) {
set_label(pi, words[0]);
continue;
}
- if (wordc > 1 && IS(words[1], "=")) {
+ if (wordc > 1 && IS(words[1], "="))
+ {
if (wordc != 5)
aerr("unhandled equ, wc=%d\n", wordc);
if (g_eqcnt >= eq_alloc) {
aerr("too many ops\n");
parse_op(&ops[pi], words, wordc);
+
+ if (sctproto != NULL) {
+ if (ops[pi].op == OP_CALL)
+ ops[pi].datap = sctproto;
+ sctproto = NULL;
+ }
pi++;
}