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_EBP_S = (1 << 11), /* ebp used as scratch, not BP */
+ 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 */
+ OPF_ATAIL = (1 << 14), /* tail call with reused arg frame */
+ OPF_32BIT = (1 << 15), /* 32bit division */
+ OPF_LOCK = (1 << 16), /* op has lock prefix */
};
enum op_op {
OP_NOP,
OP_PUSH,
OP_POP,
+ OP_LEAVE,
OP_MOV,
OP_LEA,
OP_MOVZX,
OP_MOVSX,
+ OP_XCHG,
OP_NOT,
OP_CDQ,
+ OP_LODS,
OP_STOS,
OP_MOVS,
OP_CMPS,
+ OP_SCAS,
+ OP_STD,
+ OP_CLD,
OP_RET,
OP_ADD,
OP_SUB,
OP_SAR,
OP_ROL,
OP_ROR,
+ OP_RCL,
+ OP_RCR,
OP_ADC,
OP_SBB,
+ OP_BSF,
OP_INC,
OP_DEC,
OP_NEG,
OP_CMP,
OP_CALL,
OP_JMP,
- OP_JO,
- OP_JNO,
- OP_JC,
- OP_JNC,
- OP_JZ,
- OP_JNZ,
- OP_JBE,
- OP_JA,
- OP_JS,
- OP_JNS,
- OP_JP,
- OP_JNP,
- OP_JL,
- OP_JGE,
- OP_JLE,
- OP_JG,
+ OP_JECXZ,
+ OP_JCC,
+ OP_SCC,
};
enum opr_type {
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
+ const struct parsed_proto *pp; // for OPT_LABEL
int reg;
unsigned int val;
- char name[256];
+ char name[112];
};
struct parsed_op {
enum op_op op;
struct parsed_opr operand[MAX_OPERANDS];
unsigned int flags;
- int operand_cnt;
+ unsigned char pfo;
+ unsigned char pfo_inv;
+ unsigned char operand_cnt;
+ unsigned char pad;
int regmask_src; // all referensed regs
int regmask_dst;
int pfomask; // flagop: parsed_flag_op that can't be delayed
int cc_scratch; // scratch storage during analysis
int bt_i; // branch target for branches
struct parsed_data *btj;// branch targets for jumptables
+ struct parsed_proto *pp;// parsed_proto for OP_CALL
void *datap;
};
// datap:
-// OP_CALL - ptr to parsed_proto
+// OP_CALL - parser proto hint (str)
// (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 struct parsed_op ops[MAX_OPS];
static struct parsed_equ *g_eqs;
static int g_eqcnt;
-static char g_labels[MAX_OPS][32];
+static char g_labels[MAX_OPS][48];
static struct label_ref g_label_refs[MAX_OPS];
static const struct parsed_proto *g_func_pp;
static struct parsed_data *g_func_pd;
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;
if (!IS_START(name, "esp+"))
return NULL;
- p = strchr(name + 4, '+');
+ s = name + 4;
+ p = strchr(s, '+');
if (p) {
- // must be a number after esp+, already converted to 0x..
- s = name + 4;
+ if (is_reg_in_str(s)) {
+ if (extra_reg != NULL) {
+ strncpy(extra_reg, s, p - s);
+ extra_reg[p - s] = 0;
+ }
+ s = p + 1;
+ p = strchr(s, '+');
+ if (p == NULL)
+ aerr("%s IDA stackvar not set?\n", __func__);
+ }
if (!('0' <= *s && *s <= '9')) {
- aerr("%s nan?\n", __func__);
+ aerr("%s IDA stackvar offset not set?\n", __func__);
return NULL;
}
if (s[0] == '0' && s[1] == 'x')
const struct parsed_type *c_type)
{
static const char *dword_types[] = {
- "int", "_DWORD", "UINT_PTR",
- "DWORD", "HANDLE", "HWND", "HMODULE",
- "WPARAM", "LPARAM", "UINT",
+ "int", "_DWORD", "UINT_PTR", "DWORD",
+ "WPARAM", "LPARAM", "UINT", "__int32",
+ "LONG", "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",
- "_UNKNOWN",
+ "unsigned __int8", "__int8", "BYTE", "_BYTE",
+ "CHAR", "_UNKNOWN",
};
const char *n;
int i;
return 0;
}
+static char *default_cast_to(char *buf, size_t buf_size,
+ struct parsed_opr *opr)
+{
+ buf[0] = 0;
+
+ if (!opr->is_ptr)
+ return buf;
+ if (opr->pp == NULL || opr->pp->type.name == NULL
+ || opr->pp->is_fptr)
+ {
+ snprintf(buf, buf_size, "%s", "(void *)");
+ return buf;
+ }
+
+ snprintf(buf, buf_size, "(%s)", opr->pp->type.name);
+ return buf;
+}
+
static enum opr_type lmod_from_directive(const char *d)
{
if (IS(d, "dd"))
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;
}
}
opr->is_array = pp->type.is_array;
}
+ opr->pp = pp;
if (opr->lmod == OPLM_UNSPEC)
guess_lmod_from_name(opr);
{ "repz", OPF_REP|OPF_REPZ },
{ "repne", OPF_REP|OPF_REPNZ },
{ "repnz", OPF_REP|OPF_REPNZ },
+ { "lock", OPF_LOCK }, // ignored for now..
};
+#define OPF_CJMP_CC (OPF_JMP|OPF_CJMP|OPF_CC)
+
static const struct {
const char *name;
enum op_op op;
- unsigned int minopr;
- unsigned int maxopr;
+ unsigned short minopr;
+ unsigned short maxopr;
unsigned int flags;
+ unsigned char pfo;
+ unsigned char pfo_inv;
} op_table[] = {
{ "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 },
{ "movsx",OP_MOVSX, 2, 2, OPF_DATA },
+ { "xchg", OP_XCHG, 2, 2, OPF_DATA },
{ "not", OP_NOT, 1, 1, OPF_DATA },
{ "cdq", OP_CDQ, 0, 0, OPF_DATA },
+ { "lodsb",OP_LODS, 0, 0, OPF_DATA },
+ { "lodsw",OP_LODS, 0, 0, OPF_DATA },
+ { "lodsd",OP_LODS, 0, 0, OPF_DATA },
{ "stosb",OP_STOS, 0, 0, OPF_DATA },
{ "stosw",OP_STOS, 0, 0, OPF_DATA },
{ "stosd",OP_STOS, 0, 0, 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 },
{ "sar", OP_SAR, 2, 2, OPF_DATA|OPF_FLAGS },
{ "rol", OP_ROL, 2, 2, OPF_DATA|OPF_FLAGS },
{ "ror", OP_ROR, 2, 2, OPF_DATA|OPF_FLAGS },
- { "adc", OP_ADC, 2, 2, OPF_DATA|OPF_FLAGS|OPF_CC },
- { "sbb", OP_SBB, 2, 2, OPF_DATA|OPF_FLAGS|OPF_CC },
+ { "rcl", OP_RCL, 2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C },
+ { "rcr", OP_RCR, 2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C },
+ { "adc", OP_ADC, 2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C },
+ { "sbb", OP_SBB, 2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C },
+ { "bsf", OP_BSF, 2, 2, OPF_DATA|OPF_FLAGS },
{ "inc", OP_INC, 1, 1, OPF_DATA|OPF_FLAGS },
{ "dec", OP_DEC, 1, 1, OPF_DATA|OPF_FLAGS },
{ "neg", OP_NEG, 1, 1, OPF_DATA|OPF_FLAGS },
{ "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
- { "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 },
- { "setb", OP_JC, 1, 1, OPF_DATA|OPF_CC },
- { "setnc", OP_JNC, 1, 1, OPF_DATA|OPF_CC },
- { "setae", OP_JNC, 1, 1, OPF_DATA|OPF_CC },
- { "setz", OP_JZ, 1, 1, OPF_DATA|OPF_CC },
- { "sete", OP_JZ, 1, 1, OPF_DATA|OPF_CC },
- { "setnz", OP_JNZ, 1, 1, OPF_DATA|OPF_CC },
- { "setne", OP_JNZ, 1, 1, OPF_DATA|OPF_CC },
- { "setbe", OP_JBE, 1, 1, OPF_DATA|OPF_CC },
- { "setna", OP_JBE, 1, 1, OPF_DATA|OPF_CC },
- { "seta", OP_JA, 1, 1, OPF_DATA|OPF_CC },
- { "setnbe", OP_JA, 1, 1, OPF_DATA|OPF_CC },
- { "sets", OP_JS, 1, 1, OPF_DATA|OPF_CC },
- { "setns", OP_JNS, 1, 1, OPF_DATA|OPF_CC },
- { "setp", OP_JP, 1, 1, OPF_DATA|OPF_CC },
- { "setpe", OP_JP, 1, 1, OPF_DATA|OPF_CC },
- { "setnp", OP_JNP, 1, 1, OPF_DATA|OPF_CC },
- { "setpo", OP_JNP, 1, 1, OPF_DATA|OPF_CC },
- { "setl", OP_JL, 1, 1, OPF_DATA|OPF_CC },
- { "setnge", OP_JL, 1, 1, OPF_DATA|OPF_CC },
- { "setge", OP_JGE, 1, 1, OPF_DATA|OPF_CC },
- { "setnl", OP_JGE, 1, 1, OPF_DATA|OPF_CC },
- { "setle", OP_JLE, 1, 1, OPF_DATA|OPF_CC },
- { "setng", OP_JLE, 1, 1, OPF_DATA|OPF_CC },
- { "setg", OP_JG, 1, 1, OPF_DATA|OPF_CC },
- { "setnle", OP_JG, 1, 1, OPF_DATA|OPF_CC },
+ { "jecxz",OP_JECXZ, 1, 1, OPF_JMP|OPF_CJMP },
+ { "jo", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_O, 0 }, // 70 OF=1
+ { "jno", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_O, 1 }, // 71 OF=0
+ { "jc", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_C, 0 }, // 72 CF=1
+ { "jb", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_C, 0 }, // 72
+ { "jnc", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_C, 1 }, // 73 CF=0
+ { "jnb", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_C, 1 }, // 73
+ { "jae", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_C, 1 }, // 73
+ { "jz", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_Z, 0 }, // 74 ZF=1
+ { "je", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_Z, 0 }, // 74
+ { "jnz", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_Z, 1 }, // 75 ZF=0
+ { "jne", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_Z, 1 }, // 75
+ { "jbe", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_BE, 0 }, // 76 CF=1||ZF=1
+ { "jna", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_BE, 0 }, // 76
+ { "ja", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_BE, 1 }, // 77 CF=0&&ZF=0
+ { "jnbe", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_BE, 1 }, // 77
+ { "js", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_S, 0 }, // 78 SF=1
+ { "jns", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_S, 1 }, // 79 SF=0
+ { "jp", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_P, 0 }, // 7a PF=1
+ { "jpe", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_P, 0 }, // 7a
+ { "jnp", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_P, 1 }, // 7b PF=0
+ { "jpo", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_P, 1 }, // 7b
+ { "jl", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_L, 0 }, // 7c SF!=OF
+ { "jnge", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_L, 0 }, // 7c
+ { "jge", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_L, 1 }, // 7d SF=OF
+ { "jnl", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_L, 1 }, // 7d
+ { "jle", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_LE, 0 }, // 7e ZF=1||SF!=OF
+ { "jng", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_LE, 0 }, // 7e
+ { "jg", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_LE, 1 }, // 7f ZF=0&&SF=OF
+ { "jnle", OP_JCC, 1, 1, OPF_CJMP_CC, PFO_LE, 1 }, // 7f
+ { "seto", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_O, 0 },
+ { "setno", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_O, 1 },
+ { "setc", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_C, 0 },
+ { "setb", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_C, 0 },
+ { "setnc", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_C, 1 },
+ { "setae", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_C, 1 },
+ { "setnb", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_C, 1 },
+ { "setz", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_Z, 0 },
+ { "sete", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_Z, 0 },
+ { "setnz", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_Z, 1 },
+ { "setne", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_Z, 1 },
+ { "setbe", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_BE, 0 },
+ { "setna", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_BE, 0 },
+ { "seta", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_BE, 1 },
+ { "setnbe", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_BE, 1 },
+ { "sets", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_S, 0 },
+ { "setns", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_S, 1 },
+ { "setp", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_P, 0 },
+ { "setpe", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_P, 0 },
+ { "setnp", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_P, 1 },
+ { "setpo", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_P, 1 },
+ { "setl", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_L, 0 },
+ { "setnge", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_L, 0 },
+ { "setge", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_L, 1 },
+ { "setnl", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_L, 1 },
+ { "setle", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_LE, 0 },
+ { "setng", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_LE, 0 },
+ { "setg", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_LE, 1 },
+ { "setnle", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_LE, 1 },
};
static void parse_op(struct parsed_op *op, char words[16][256], int wordc)
op->op = op_table[i].op;
op->flags = op_table[i].flags | prefix_flags;
+ op->pfo = op_table[i].pfo;
+ op->pfo_inv = op_table[i].pfo_inv;
op->regmask_src = op->regmask_dst = 0;
for (opr = 0; opr < op_table[i].minopr; opr++) {
setup_reg_opr(&op->operand[1], xAX, OPLM_DWORD, &op->regmask_src);
break;
+ case OP_LODS:
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);
+ setup_reg_opr(&op->operand[0], op->op == OP_LODS ? xSI : xDI,
+ lmod, &op->regmask_src);
setup_reg_opr(&op->operand[1], xCX, OPLM_DWORD, &op->regmask_src);
op->regmask_dst = op->regmask_src;
- setup_reg_opr(&op->operand[2], xAX, OPLM_DWORD, &op->regmask_src);
+ setup_reg_opr(&op->operand[2], xAX, OPLM_DWORD,
+ op->op == OP_LODS ? &op->regmask_dst : &op->regmask_src);
break;
case OP_MOVS:
op->regmask_dst = op->regmask_src;
break;
+ case OP_XCHG:
+ op->regmask_src |= op->regmask_dst;
+ op->regmask_dst |= op->regmask_src;
+ goto check_align;
+
+ 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;
// alignment
case OP_MOV:
+ check_align:
if (op->operand[0].type == OPT_REG && op->operand[1].type == OPT_REG
- && op->operand[0].reg == xDI && op->operand[1].reg == xDI)
+ && op->operand[0].lmod == op->operand[1].lmod
+ && op->operand[0].reg == op->operand[1].reg
+ && IS(op->operand[0].name, op->operand[1].name)) // ah, al..
{
op->flags |= OPF_RMD;
+ op->regmask_src = op->regmask_dst = 0;
}
break;
}
break;
+ case OP_CALL:
+ // trashed regs must be explicitly detected later
+ op->regmask_dst = 0;
+ break;
+
default:
break;
}
}
-static const char *op_name(enum op_op op)
+static const char *op_name(struct parsed_op *po)
{
+ static char buf[16];
+ char *p;
int i;
+ if (po->op == OP_JCC || po->op == OP_SCC) {
+ p = buf;
+ *p++ = (po->op == OP_JCC) ? 'j' : 's';
+ if (po->pfo_inv)
+ *p++ = 'n';
+ strcpy(p, parsed_flag_op_names[po->pfo]);
+ return buf;
+ }
+
for (i = 0; i < ARRAY_SIZE(op_table); i++)
- if (op_table[i].op == op)
+ if (op_table[i].op == po->op)
return op_table[i].name;
return "???";
if (po == NULL)
return "???";
- snprintf(out, sizeof(out), "%s", op_name(po->op));
+ snprintf(out, sizeof(out), "%s", op_name(po));
for (i = 0; i < po->operand_cnt; i++) {
p += strlen(p);
if (i > 0)
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, int is_lea)
+{
+ 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)
+ // yes it sometimes LEAs ra for compares..
+ if (!is_lea && 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, is_lea);
if (offset > stack_ra)
{
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);
+ }
+ // can't check this because msvc likes to reuse
+ // arg space for scratch..
+ //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_label_read_ref(struct parsed_op *po, const char *name)
+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 const char *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");
+
+ return pp->name;
}
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];
+ const char *name;
+ 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:
- check_label_read_ref(po, popr->name);
+ name = check_label_read_ref(po, popr->name);
if (cast[0] == 0 && popr->is_ptr)
cast = "(u32)";
if (is_lea)
- snprintf(buf, buf_size, "(u32)&%s", popr->name);
+ snprintf(buf, buf_size, "(u32)&%s", name);
else if (popr->size_lt)
snprintf(buf, buf_size, "%s%s%s%s", cast,
lmod_cast_u_ptr(po, popr->lmod),
- popr->is_array ? "" : "&",
- popr->name);
+ popr->is_array ? "" : "&", name);
else
- snprintf(buf, buf_size, "%s%s%s", cast, popr->name,
+ snprintf(buf, buf_size, "%s%s%s", cast, name,
popr->is_array ? "[0]" : "");
break;
case OPT_OFFSET:
- check_label_read_ref(po, popr->name);
+ name = check_label_read_ref(po, popr->name);
if (cast[0] == 0)
cast = "(u32)";
if (is_lea)
ferr(po, "lea an offset?\n");
- snprintf(buf, buf_size, "%s&%s", cast, popr->name);
+ snprintf(buf, buf_size, "%s&%s", cast, name);
break;
case OPT_CONST:
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 && !(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;
return out_src_opr(buf, buf_size, po, popr, NULL, 0);
}
-static enum parsed_flag_op split_cond(struct parsed_op *po,
- enum op_op op, int *is_inv)
-{
- *is_inv = 0;
-
- switch (op) {
- case OP_JO:
- return PFO_O;
- case OP_JC:
- return PFO_C;
- case OP_JZ:
- return PFO_Z;
- case OP_JBE:
- return PFO_BE;
- case OP_JS:
- return PFO_S;
- case OP_JP:
- return PFO_P;
- case OP_JL:
- return PFO_L;
- case OP_JLE:
- return PFO_LE;
-
- case OP_JNO:
- *is_inv = 1;
- return PFO_O;
- case OP_JNC:
- *is_inv = 1;
- return PFO_C;
- case OP_JNZ:
- *is_inv = 1;
- return PFO_Z;
- case OP_JA:
- *is_inv = 1;
- return PFO_BE;
- case OP_JNS:
- *is_inv = 1;
- return PFO_S;
- case OP_JNP:
- *is_inv = 1;
- return PFO_P;
- case OP_JGE:
- *is_inv = 1;
- return PFO_L;
- case OP_JG:
- *is_inv = 1;
- return PFO_LE;
-
- case OP_ADC:
- case OP_SBB:
- return PFO_C;
-
- default:
- ferr(po, "split_cond: bad op %d\n", op);
- return -1;
- }
-}
-
static void out_test_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 *expr)
}
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);
// 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_RMD)) == OPF_JMP \
+ && ops[_i].op != OP_CALL))
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)
op_set_clear_flag(po, OPF_RSAVE, OPF_RMD);
}
- else 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);
+ 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;
}
if (ops[i].regmask_dst & (1 << xDX))
return -1;
- if (g_labels[i][0] != 0)
- return -1;
}
return -1;
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)
+static int scan_for_esp_adjust(int i, int opcnt, int *adj,
+ int *multipath)
{
struct parsed_op *po;
- *adj = 0;
+ int first_pop = -1;
+
+ *adj = *multipath = 0;
for (; i < opcnt; i++) {
po = &ops[i];
+ if (g_labels[i][0] != 0)
+ *multipath = 1;
+
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 && !(po->flags & OPF_RMD)) {
+ //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)) {
+ // seems like msvc only uses 'pop ecx' for stack realignment..
+ if (po->operand[0].type != OPT_REG || po->operand[0].reg != xCX)
+ break;
+ 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_JMP && po->btj == NULL) {
+ i = po->bt_i - 1;
+ continue;
+ }
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;
+ if (po->pp != NULL && po->pp->is_stdcall)
+ break;
}
+ }
- if (g_labels[i][0] != 0)
- return -1;
+ if (first_pop >= 0) {
+ // probably 'pop ecx' was used..
+ return first_pop;
}
return -1;
}
scan_fwd_set_flags(po->bt_i, opcnt, magic, flags);
- if (!(po->flags & OPF_CC))
+ if (!(po->flags & OPF_CJMP))
return;
}
if (po->flags & OPF_TAIL)
}
}
-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)
+static const struct parsed_proto *try_recover_pp(
+ struct parsed_op *po, const struct parsed_opr *opr, int *search_instead)
{
- struct parsed_proto *pp_tmp;
- struct label_ref *lr;
- int need_to_save_current;
- int ret = 0;
- int j;
-
- if (i < 0) {
- ferr(po, "no refs for '%s'?\n", g_labels[i]);
- return -1;
- }
-
- for (; arg < pp->argc; arg++)
- if (pp->arg[arg].reg == NULL)
- break;
- magic = (magic & 0xffffff) | (arg << 24);
+ const struct parsed_proto *pp = NULL;
+ char buf[256];
+ char *p;
- for (j = i; j >= 0 && arg < pp->argc; )
+ // maybe an arg of g_func?
+ if (opr->type == OPT_REGMEM && is_stack_access(po, opr))
{
- 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;
+ char ofs_reg[16] = { 0, };
+ int arg, arg_s, arg_i;
+ int stack_ra = 0;
+ int offset = 0;
- if (g_labels[j][0] != 0) {
- lr = &g_label_refs[j];
+ parse_stack_access(po, opr->name, ofs_reg,
+ &offset, &stack_ra, NULL, 0);
+ 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;
+
+ ops[i].cc_scratch = magic;
+
+ while (1) {
+ 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 (i < 0)
+ break;
+
+ if (ops[i].cc_scratch == magic)
+ return;
+ ops[i].cc_scratch = magic;
+
+ if (!(ops[i].flags & OPF_DATA))
+ continue;
+ if (!is_opr_modified(opr, &ops[i]))
+ continue;
+ if (ops[i].op != OP_MOV && ops[i].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 try_resolve_const(int i, const struct parsed_opr *opr,
+ int magic, unsigned int *val)
+{
+ struct label_ref *lr;
+ int ret = 0;
+
+ ops[i].cc_scratch = magic;
+
+ while (1) {
+ if (g_labels[i][0] != 0) {
+ lr = &g_label_refs[i];
+ for (; lr != NULL; lr = lr->next)
+ ret |= try_resolve_const(lr->i, opr, magic, val);
+ if (i > 0 && LAST_OP(i - 1))
+ return ret;
+ }
+
+ i--;
+ if (i < 0)
+ return -1;
+
+ if (ops[i].cc_scratch == magic)
+ return 0;
+ ops[i].cc_scratch = magic;
+
+ if (!(ops[i].flags & OPF_DATA))
+ continue;
+ if (!is_opr_modified(opr, &ops[i]))
+ continue;
+ if (ops[i].op != OP_MOV && ops[i].operand[1].type != OPT_CONST)
+ return -1;
+
+ *val = ops[i].operand[1].val;
+ return 1;
+ }
+}
+
+static int collect_call_args_r(struct parsed_op *po, int i,
+ struct parsed_proto *pp, int *regmask, int *save_arg_vars, int arg,
+ int magic, int need_op_saving, int may_reuse)
+{
+ struct parsed_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,
+ 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,
+ 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[j].op == OP_CALL)
{
- pp_tmp = ops[j].datap;
+ if (pp->is_unresolved)
+ break;
+
+ pp_tmp = ops[j].pp;
if (pp_tmp == NULL)
ferr(po, "arg collect hit unparsed call '%s'\n",
ops[j].operand[0].name);
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;
}
+// early check for tail call or branch back
+static int is_like_tailjmp(int j)
+{
+ if (!(ops[j].flags & OPF_JMP))
+ return 0;
+
+ if (ops[j].op == OP_JMP && !ops[j].operand[0].had_ds)
+ // probably local branch back..
+ return 1;
+ if (ops[j].op == OP_CALL)
+ // probably noreturn call..
+ return 1;
+
+ return 0;
+}
+
+static void pp_insert_reg_arg(struct parsed_proto *pp, const char *reg)
+{
+ int i;
+
+ 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;
struct parsed_proto *pp, *pp_tmp;
struct parsed_data *pd;
const char *tmpname;
- enum parsed_flag_op pfo;
+ unsigned int uval;
int save_arg_vars = 0;
- int cmp_result_vars = 0;
- int need_mul_var = 0;
- int have_func_ret = 0;
+ int cond_vars = 0;
+ int need_tmp_var = 0;
+ int need_tmp64 = 0;
int had_decl = 0;
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 depth = 0;
int no_output;
int i, j, l;
- int dummy;
int arg;
int reg;
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 ");
- fprintf(fout, "%s(", funcn);
+ output_pp_attrs(fout, g_func_pp, g_ida_func_attr & IDAFA_NORETURN);
+ fprintf(fout, "%s(", g_func_pp->name);
for (i = 0; i < g_func_pp->argc; i++) {
if (i > 0)
// 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)
ferr(ops, "nested fptr\n");
fprintf(fout, "%s", pp->arg[j].type.name);
}
+ if (pp->is_vararg) {
+ if (j > 0)
+ fprintf(fout, ", ");
+ fprintf(fout, "...");
+ }
fprintf(fout, ")");
}
+ else if (g_func_pp->arg[i].type.is_retreg) {
+ fprintf(fout, "u32 *r_%s", g_func_pp->arg[i].reg);
+ }
else {
fprintf(fout, "%s a%d", g_func_pp->arg[i].type.name, i + 1);
}
+ if (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)
for (; i < opcnt; i++)
if (ops[i].op == OP_RET)
break;
- if (i == opcnt && (ops[i - 1].flags & OPF_JMP) && found)
- break;
+ j = i - 1;
+ if (i == opcnt && (ops[j].flags & OPF_JMP)) {
+ if (found && is_like_tailjmp(j))
+ break;
+ j--;
+ }
- if (ops[i - 1].op == OP_POP && IS(opr_name(&ops[i - 1], 0), "ebp"))
- ops[i - 1].flags |= OPF_RMD;
+ if ((ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ebp"))
+ || ops[j].op == OP_LEAVE)
+ {
+ ops[j].flags |= OPF_RMD;
+ }
else if (!(g_ida_func_attr & IDAFA_NORETURN))
- ferr(&ops[i - 1], "'pop ebp' expected\n");
+ ferr(&ops[j], "'pop ebp' expected\n");
if (g_stack_fsz != 0) {
- if (ops[i - 2].op == OP_MOV
- && IS(opr_name(&ops[i - 2], 0), "esp")
- && IS(opr_name(&ops[i - 2], 1), "ebp"))
+ if (ops[j - 1].op == OP_MOV
+ && IS(opr_name(&ops[j - 1], 0), "esp")
+ && IS(opr_name(&ops[j - 1], 1), "ebp"))
+ {
+ ops[j - 1].flags |= OPF_RMD;
+ }
+ else if (ops[j].op != OP_LEAVE
+ && !(g_ida_func_attr & IDAFA_NORETURN))
{
- ops[i - 2].flags |= OPF_RMD;
+ ferr(&ops[j - 1], "esp restore expected\n");
}
- else if (!(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"))
+ if (ecx_push && ops[j - 2].op == OP_POP
+ && IS(opr_name(&ops[j - 2], 0), "ecx"))
{
- ferr(&ops[i - 3], "unexpected ecx pop\n");
+ ferr(&ops[j - 2], "unexpected ecx pop\n");
}
}
}
}
+ found = 0;
if (g_sp_frame)
{
g_stack_fsz = ops[i].operand[1].val;
for (; i < opcnt; i++)
if (ops[i].op == OP_RET)
break;
- if (ops[i - 1].op != OP_ADD
- || !IS(opr_name(&ops[i - 1], 0), "esp")
- || ops[i - 1].operand[1].type != OPT_CONST
- || ops[i - 1].operand[1].val != g_stack_fsz)
- ferr(&ops[i - 1], "'add esp' expected\n");
- ops[i - 1].flags |= OPF_RMD;
+ j = i - 1;
+ if (i == opcnt && (ops[j].flags & OPF_JMP)) {
+ if (found && is_like_tailjmp(j))
+ break;
+ j--;
+ }
+ if (ops[j].op != OP_ADD
+ || !IS(opr_name(&ops[j], 0), "esp")
+ || ops[j].operand[1].type != OPT_CONST
+ || ops[j].operand[1].val != g_stack_fsz)
+ ferr(&ops[j], "'add esp' expected\n");
+ ops[j].flags |= OPF_RMD;
+
+ found = 1;
i++;
} while (i < opcnt);
}
continue;
if (po->op == OP_CALL) {
+ pp = NULL;
+
if (po->operand[0].type == OPT_LABEL) {
tmpname = opr_name(po, 0);
- pp_c = proto_parse(fhdr, tmpname);
+ 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;
}
+ 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->pp = pp;
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;
for (l = 0; l < opcnt; l++) {
if (g_labels[l][0] && IS(po->operand[0].name, g_labels[l])) {
+ if (l == i + 1 && po->op == OP_JMP) {
+ // yet another alignment type..
+ po->flags |= OPF_RMD;
+ break;
+ }
add_label_ref(&g_label_refs[l], i);
po->bt_i = l;
break;
}
}
- if (po->bt_i != -1)
+ if (po->bt_i != -1 || (po->flags & OPF_RMD))
continue;
if (po->operand[0].type == OPT_LABEL)
tailcall:
po->op = OP_CALL;
po->flags |= OPF_TAIL;
+ if (i > 0 && ops[i - 1].op == OP_POP)
+ po->flags |= OPF_ATAIL;
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)
{
tmpname = opr_name(po, 0);
- pp = po->datap;
+ pp = po->pp;
if (pp == NULL)
{
// indirect call
- 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;
+ 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;
+
+ switch (po->operand[0].type) {
+ case OPT_REG:
+ // we resolved this call and no longer need the register
+ po->regmask_src &= ~(1 << po->operand[0].reg);
+ break;
+ case OPT_REGMEM:
+ pp->is_fptr = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ if (pp == NULL) {
+ pp = calloc(1, sizeof(*pp));
+ my_assert_not(pp, NULL);
+ pp->is_fptr = 1;
+ ret = scan_for_esp_adjust(i + 1, opcnt, &j, &l);
+ if (ret < 0) {
+ if (!g_allow_regfunc)
+ ferr(po, "non-__cdecl indirect call unhandled yet\n");
+ pp->is_unresolved = 1;
+ j = 0;
+ }
+ j /= 4;
+ if (j > ARRAY_SIZE(pp->arg))
+ ferr(po, "esp adjust too large: %d\n", j);
+ pp->ret_type.name = strdup("int");
+ pp->argc = pp->argc_stack = j;
+ for (arg = 0; arg < pp->argc; arg++)
+ pp->arg[arg].type.name = strdup("int");
+ }
+ po->pp = pp;
}
// look for and make use of esp adjust
ret = -1;
if (!pp->is_stdcall && pp->argc_stack > 0)
- ret = scan_for_esp_adjust(i + 1, opcnt, &j);
+ ret = scan_for_esp_adjust(i + 1, opcnt, &j, &l);
if (ret >= 0) {
if (pp->is_vararg) {
if (j / 4 < pp->argc_stack)
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 if (!l) {
+ // a bit of a hack, but deals with use of
+ // single adj for multiple calls
+ ops[ret].operand[1].val -= j;
+ }
}
else if (pp->is_vararg)
ferr(po, "missing esp_adjust for vararg func '%s'\n",
pp->name);
- // 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 && !(po->flags & OPF_ATAIL)) {
+ // 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, i + opcnt * 2, 0, 0);
-
if (strstr(pp->ret_type.name, "int64"))
- need_mul_var = 1;
- if (!(po->flags & OPF_TAIL) && !IS(pp->ret_type.name, "void"))
- have_func_ret = 1;
+ need_tmp64 = 1;
}
}
// 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];
regmask_save |= 1 << reg;
}
- if (po->op == OP_PUSH
- && po->argnum == 0 && !(po->flags & OPF_RSAVE)
- && po->operand[0].type == OPT_REG)
+ if (po->op == OP_PUSH && po->argnum == 0
+ && !(po->flags & OPF_RSAVE) && !g_func_pp->is_userstack)
{
- 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;
+ 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;
}
}
+ 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);
+ scan_fwd_set_flags(i + 1, opcnt, i + opcnt * 5, OPF_EBP_S);
else
regmask_now &= ~(1 << xBP);
}
if (cnt > ARRAY_SIZE(setters))
ferr(po, "too many flag setters\n");
- pfo = split_cond(po, po->op, &dummy);
for (j = 0; j < cnt; j++)
{
tmp_op = &ops[setters[j]]; // flag setter
// 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)
- pfomask = 1 << pfo;
+ // 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 << po->pfo;
}
- else if (tmp_op->op == OP_CMPS) {
- pfomask = 1 << PFO_Z;
+ else if (tmp_op->op == OP_CMPS || tmp_op->op == OP_SCAS) {
+ pfomask = 1 << po->pfo;
}
else {
// see if we'll be able to handle based on op result
if ((tmp_op->op != OP_AND && tmp_op->op != OP_OR
- && pfo != PFO_Z && pfo != PFO_S && pfo != PFO_P)
+ && po->pfo != PFO_Z && po->pfo != PFO_S
+ && po->pfo != PFO_P)
|| branched
|| scan_for_mod_opr0(tmp_op, setters[j] + 1, i) >= 0)
- pfomask = 1 << pfo;
+ {
+ pfomask = 1 << po->pfo;
+ }
+
+ if (tmp_op->op == OP_ADD && po->pfo == PFO_C)
+ need_tmp64 = 1;
+ }
+ if (pfomask) {
+ tmp_op->pfomask |= pfomask;
+ cond_vars |= pfomask;
+ }
+ // note: may overwrite, currently not a problem
+ po->datap = tmp_op;
+ }
+
+ if (po->op == OP_RCL || po->op == OP_RCR
+ || po->op == OP_ADC || po->op == OP_SBB)
+ cond_vars |= 1 << PFO_C;
+ }
+
+ if (po->op == OP_CMPS || po->op == OP_SCAS) {
+ cond_vars |= 1 << PFO_Z;
+ }
+ else if (po->op == OP_MUL
+ || (po->op == OP_IMUL && po->operand_cnt == 1))
+ {
+ need_tmp64 = 1;
+ }
+ else if (po->op == OP_CALL) {
+ pp = po->pp;
+ 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;
+ }
}
- if (pfomask) {
- tmp_op->pfomask |= pfomask;
- cmp_result_vars |= pfomask;
+
+ // 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;
+ }
}
- // note: may overwrite, currently not a problem
- po->datap = tmp_op;
}
- if (po->op == OP_ADC || po->op == OP_SBB)
- cmp_result_vars |= 1 << PFO_C;
+ // declare indirect funcs
+ if (pp->is_fptr && !(pp->name[0] != 0 && pp->is_arg)) {
+ 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].pp)) {
+ 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");
+ }
}
- else if (po->op == OP_MUL
- || (po->op == OP_IMUL && po->operand_cnt == 1))
- {
- need_mul_var = 1;
+ else if (po->op == OP_RET && !IS(g_func_pp->ret_type.name, "void"))
+ regmask |= 1 << xAX;
+ else if (po->op == OP_DIV || po->op == OP_IDIV) {
+ // 32bit division is common, look for it
+ if (po->op == OP_DIV)
+ ret = scan_for_reg_clear(i, xDX);
+ else
+ ret = scan_for_cdq_edx(i);
+ if (ret >= 0)
+ po->flags |= OPF_32BIT;
+ else
+ need_tmp64 = 1;
}
- 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);
- 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");
+ else if (po->op == OP_CLD)
+ po->flags |= OPF_RMD;
+
+ if (po->op == OP_RCL || po->op == OP_RCR || po->op == OP_XCHG) {
+ need_tmp_var = 1;
+ }
+ }
+
+ // 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];
}
}
fprintf(fout, " };\n");
+ had_decl = 1;
}
// declare stack frame, va_arg
- if (g_stack_fsz)
+ if (g_stack_fsz) {
fprintf(fout, " union { u32 d[%d]; u16 w[%d]; u8 b[%d]; } sf;\n",
(g_stack_fsz + 3) / 4, (g_stack_fsz + 1) / 2, g_stack_fsz);
+ had_decl = 1;
+ }
- if (g_func_pp->is_vararg)
+ if (g_func_pp->is_userstack) {
+ fprintf(fout, " u32 fake_sf[1024];\n");
+ fprintf(fout, " u32 *esp = &fake_sf[1024];\n");
+ had_decl = 1;
+ }
+
+ if (g_func_pp->is_vararg) {
fprintf(fout, " va_list ap;\n");
+ had_decl = 1;
+ }
// declare arg-registers
for (i = 0; i < g_func_pp->argc; i++) {
if (g_func_pp->arg[i].reg != NULL) {
reg = char_array_i(regs_r32,
ARRAY_SIZE(regs_r32), g_func_pp->arg[i].reg);
- if (reg < 0)
- ferr(ops, "arg '%s' is not a reg?\n", g_func_pp->arg[i].reg);
-
- regmask_arg |= 1 << reg;
- fprintf(fout, " u32 %s = (u32)a%d;\n",
- g_func_pp->arg[i].reg, i + 1);
+ if (regmask & (1 << reg)) {
+ if (g_func_pp->arg[i].type.is_retreg)
+ fprintf(fout, " u32 %s = *r_%s;\n",
+ g_func_pp->arg[i].reg, g_func_pp->arg[i].reg);
+ else
+ fprintf(fout, " u32 %s = (u32)a%d;\n",
+ g_func_pp->arg[i].reg, i + 1);
+ }
+ else {
+ if (g_func_pp->arg[i].type.is_retreg)
+ ferr(ops, "retreg '%s' is unused?\n",
+ g_func_pp->arg[i].reg);
+ fprintf(fout, " // %s = a%d; // unused\n",
+ g_func_pp->arg[i].reg, i + 1);
+ }
had_decl = 1;
}
}
- // declare other regs - special case for eax
- if (!((regmask | regmask_arg) & 1) && have_func_ret
- && !IS(g_func_pp->ret_type.name, "void"))
- {
- fprintf(fout, " u32 eax = 0;\n");
- had_decl = 1;
- }
-
- regmask &= ~regmask_arg;
- regmask &= ~(1 << xSP);
- 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;
}
}
}
}
- if (cmp_result_vars) {
+ if (cond_vars) {
for (i = 0; i < 8; i++) {
- if (cmp_result_vars & (1 << i)) {
+ if (cond_vars & (1 << i)) {
fprintf(fout, " u32 cond_%s;\n", parsed_flag_op_names[i]);
had_decl = 1;
}
}
}
- if (need_mul_var) {
- fprintf(fout, " u64 mul_tmp;\n");
+ if (need_tmp_var) {
+ fprintf(fout, " u32 tmp;\n");
+ had_decl = 1;
+ }
+
+ if (need_tmp64) {
+ fprintf(fout, " u64 tmp64;\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;
if (po->flags & OPF_CC)
{
int is_delayed = 0;
- int is_inv = 0;
- pfo = split_cond(po, po->op, &is_inv);
tmp_op = po->datap;
// we go through all this trouble to avoid using parsed_flag_op,
// which makes generated code much nicer
if (delayed_flag_op != NULL)
{
- out_cmp_test(buf1, sizeof(buf1), delayed_flag_op, pfo, is_inv);
+ out_cmp_test(buf1, sizeof(buf1), delayed_flag_op,
+ po->pfo, po->pfo_inv);
is_delayed = 1;
}
else if (last_arith_dst != NULL
- && (pfo == PFO_Z || pfo == PFO_S || pfo == PFO_P
+ && (po->pfo == PFO_Z || po->pfo == PFO_S || po->pfo == PFO_P
|| (tmp_op && (tmp_op->op == OP_AND || tmp_op->op == OP_OR))
))
{
out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
- out_test_for_cc(buf1, sizeof(buf1), po, pfo, is_inv,
+ out_test_for_cc(buf1, sizeof(buf1), po, po->pfo, po->pfo_inv,
last_arith_dst->lmod, buf3);
is_delayed = 1;
}
else if (tmp_op != NULL) {
// use preprocessed flag calc results
- if (!(tmp_op->pfomask & (1 << pfo)))
- ferr(po, "not prepared for pfo %d\n", pfo);
+ if (!(tmp_op->pfomask & (1 << po->pfo)))
+ ferr(po, "not prepared for pfo %d\n", po->pfo);
- // note: is_inv was not yet applied
+ // note: pfo_inv was not yet applied
snprintf(buf1, sizeof(buf1), "(%scond_%s)",
- is_inv ? "!" : "", parsed_flag_op_names[pfo]);
+ po->pfo_inv ? "!" : "", parsed_flag_op_names[po->pfo]);
}
else {
ferr(po, "all methods of finding comparison failed\n");
}
if (po->flags & OPF_JMP) {
- fprintf(fout, " if %s\n", buf1);
+ fprintf(fout, " if %s", buf1);
}
- else if (po->op == OP_ADC || po->op == OP_SBB) {
+ else if (po->op == OP_RCL || po->op == OP_RCR
+ || po->op == OP_ADC || po->op == OP_SBB)
+ {
if (is_delayed)
fprintf(fout, " cond_%s = %s;\n",
- parsed_flag_op_names[pfo], buf1);
+ parsed_flag_op_names[po->pfo], buf1);
}
else if (po->flags & OPF_DATA) { // SETcc
out_dst_opr(buf2, sizeof(buf2), po, &po->operand[0]);
pfomask = po->pfomask;
+ if (po->flags & (OPF_REPZ|OPF_REPNZ)) {
+ struct parsed_opr opr = {0,};
+ opr.type = OPT_REG;
+ opr.reg = xCX;
+ opr.lmod = OPLM_DWORD;
+ ret = try_resolve_const(i, &opr, opcnt * 7 + i, &uval);
+
+ if (ret != 1 || uval == 0) {
+ // we need initial flags for ecx=0 case..
+ if (i > 0 && ops[i - 1].op == OP_XOR
+ && IS(ops[i - 1].operand[0].name,
+ ops[i - 1].operand[1].name))
+ {
+ fprintf(fout, " cond_z = ");
+ if (pfomask & (1 << PFO_C))
+ fprintf(fout, "cond_c = ");
+ fprintf(fout, "0;\n");
+ }
+ else if (last_arith_dst != NULL) {
+ out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
+ out_test_for_cc(buf1, sizeof(buf1), po, PFO_Z, 0,
+ last_arith_dst->lmod, buf3);
+ fprintf(fout, " cond_z = %s;\n", buf1);
+ }
+ else
+ ferr(po, "missing initial ZF\n");
+ }
+ }
+
switch (po->op)
{
case OP_MOV:
assert_operand_cnt(2);
propagate_lmod(po, &po->operand[0], &po->operand[1]);
out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ default_cast_to(buf3, sizeof(buf3), &po->operand[0]);
fprintf(fout, " %s = %s;", buf1,
out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
- po->operand[0].is_ptr ? "(void *)" : "", 0));
+ buf3, 0));
break;
case OP_LEA:
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_XCHG:
+ assert_operand_cnt(2);
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ fprintf(fout, " tmp = %s;",
+ out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], "", 0));
+ fprintf(fout, " %s = %s;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
+ default_cast_to(buf3, sizeof(buf3), &po->operand[0]), 0));
+ fprintf(fout, " %s = %stmp;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[1]),
+ default_cast_to(buf3, sizeof(buf3), &po->operand[1]));
+ snprintf(g_comment, sizeof(g_comment), "xchg");
break;
case OP_NOT:
strcpy(g_comment, "cdq");
break;
+ case OP_LODS:
+ assert_operand_cnt(3);
+ if (po->flags & OPF_REP) {
+ // hmh..
+ ferr(po, "TODO\n");
+ }
+ else {
+ fprintf(fout, " eax = %sesi; esi %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, "lods");
+ }
+ 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--) {\n");
+ if (pfomask & (1 << PFO_C)) {
+ // ugh..
+ fprintf(fout,
+ " cond_c = %sesi < %sedi;\n", buf1, buf1);
+ pfomask &= ~(1 << PFO_C);
+ }
+ fprintf(fout,
+ " cond_z = (%sesi == %sedi); esi %c= %d, edi %c= %d;\n",
+ buf1, buf1, l, j, l, j);
fprintf(fout,
- " if ((cond_z = (%sedi == %sesi)) %s 0)\n",
- buf1, buf1, (po->flags & OPF_REPZ) ? "==" : "!=");
+ " if (cond_z %s 0) break;\n",
+ (po->flags & OPF_REPZ) ? "==" : "!=");
fprintf(fout,
- " break;");
+ " }");
snprintf(g_comment, sizeof(g_comment), "rep%s cmps",
(po->flags & OPF_REPZ) ? "e" : "ne");
}
else {
fprintf(fout,
- " cond_z = (%sedi = %sesi); edi += %d; esi += %d;",
- buf1, buf1, j, j);
+ " cond_z = (%sesi == %sedi); esi %c= %d; edi %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--) {\n");
+ fprintf(fout,
+ " cond_z = (%seax == %sedi); edi %c= %d;\n",
+ lmod_cast_u(po, po->operand[0].lmod),
+ lmod_cast_u_ptr(po, po->operand[0].lmod), l, j);
+ fprintf(fout,
+ " if (cond_z %s 0) break;\n",
+ (po->flags & OPF_REPZ) ? "==" : "!=");
+ fprintf(fout,
+ " }");
+ 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_AND:
case OP_OR:
propagate_lmod(po, &po->operand[0], &po->operand[1]);
j = l - j;
else
j -= 1;
- fprintf(fout, " cond_c = (%s & 0x%02x) ? 1 : 0;\n",
- buf1, 1 << j);
+ fprintf(fout, " cond_c = (%s >> %d) & 1;\n",
+ buf1, j);
}
else
ferr(po, "zero shift?\n");
delayed_flag_op = NULL;
break;
+ case OP_RCL:
+ case OP_RCR:
+ assert_operand_cnt(2);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ l = lmod_bytes(po, po->operand[0].lmod) * 8;
+ if (po->operand[1].type == OPT_CONST) {
+ j = po->operand[1].val % l;
+ if (j == 0)
+ ferr(po, "zero rotate\n");
+ fprintf(fout, " tmp = (%s >> %d) & 1;\n",
+ buf1, (po->op == OP_RCL) ? (l - j) : (j - 1));
+ if (po->op == OP_RCL) {
+ fprintf(fout,
+ " %s = (%s << %d) | (cond_c << %d)",
+ buf1, buf1, j, j - 1);
+ if (j != 1)
+ fprintf(fout, " | (%s >> %d)", buf1, l + 1 - j);
+ }
+ else {
+ fprintf(fout,
+ " %s = (%s >> %d) | (cond_c << %d)",
+ buf1, buf1, j, l - j);
+ if (j != 1)
+ fprintf(fout, " | (%s << %d)", buf1, l + 1 - j);
+ }
+ fprintf(fout, ";\n");
+ fprintf(fout, " cond_c = tmp;");
+ }
+ else
+ ferr(po, "TODO\n");
+ strcpy(g_comment, (po->op == OP_RCL) ? "rcl" : "rcr");
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
case OP_XOR:
assert_operand_cnt(2);
propagate_lmod(po, &po->operand[0], &po->operand[1]);
if (IS(opr_name(po, 0), opr_name(po, 1))) {
// special case for XOR
+ if (pfomask & (1 << PFO_BE)) { // weird, but it happens..
+ fprintf(fout, " cond_be = 1;\n");
+ pfomask &= ~(1 << PFO_BE);
+ }
fprintf(fout, " %s = 0;",
out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
last_arith_dst = &po->operand[0];
}
goto dualop_arith;
+ case OP_ADD:
+ assert_operand_cnt(2);
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ if (pfomask & (1 << PFO_C)) {
+ fprintf(fout, " tmp64 = (u64)%s + %s;\n",
+ out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]),
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ fprintf(fout, " cond_c = tmp64 >> 32;\n");
+ fprintf(fout, " %s = (u32)tmp64;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
+ strcat(g_comment, "add64");
+ pfomask &= ~(1 << PFO_C);
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+ }
+ goto dualop_arith;
+
+ case OP_SUB:
+ assert_operand_cnt(2);
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ if (pfomask & (1 << PFO_C)) {
+ fprintf(fout, " cond_c = %s < %s;\n",
+ out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]),
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ pfomask &= ~(1 << PFO_C);
+ }
+ goto dualop_arith;
+
case OP_ADC:
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;
+
+ case OP_BSF:
+ assert_operand_cnt(2);
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
+ fprintf(fout, " %s = %s ? __builtin_ffs(%s) - 1 : 0;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ buf2, buf2);
+ output_std_flags(fout, po, &pfomask, buf1);
last_arith_dst = &po->operand[0];
delayed_flag_op = NULL;
+ strcat(g_comment, "bsf");
break;
case OP_INC:
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;
case OP_MUL:
assert_operand_cnt(1);
strcpy(buf1, po->op == OP_IMUL ? "(s64)(s32)" : "(u64)");
- fprintf(fout, " mul_tmp = %seax * %s%s;\n", buf1, buf1,
+ fprintf(fout, " tmp64 = %seax * %s%s;\n", buf1, buf1,
out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]));
- fprintf(fout, " edx = mul_tmp >> 32;\n");
- fprintf(fout, " eax = mul_tmp;");
+ fprintf(fout, " edx = tmp64 >> 32;\n");
+ fprintf(fout, " eax = tmp64;");
last_arith_dst = NULL;
delayed_flag_op = NULL;
break;
if (po->operand[0].lmod != OPLM_DWORD)
ferr(po, "unhandled lmod %d\n", po->operand[0].lmod);
- // 32bit division is common, look for it
- if (po->op == OP_DIV)
- ret = scan_for_reg_clear(i, xDX);
- else
- 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,
- po->op == OP_IDIV));
- fprintf(fout, " edx = %seax %% %s%s;\n", buf2, buf2, buf1);
- fprintf(fout, " eax = %seax / %s%s;", buf2, buf2, buf1);
+ out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
+ strcpy(buf2, lmod_cast(po, po->operand[0].lmod,
+ po->op == OP_IDIV));
+ switch (po->operand[0].lmod) {
+ case OPLM_DWORD:
+ if (po->flags & OPF_32BIT)
+ snprintf(buf3, sizeof(buf3), "%seax", buf2);
+ else {
+ fprintf(fout, " tmp64 = ((u64)edx << 32) | eax;\n");
+ snprintf(buf3, sizeof(buf3), "%stmp64",
+ (po->op == OP_IDIV) ? "(s64)" : "");
+ }
+ if (po->operand[0].type == OPT_REG
+ && po->operand[0].reg == xDX)
+ {
+ fprintf(fout, " eax = %s / %s%s;", buf3, buf2, buf1);
+ fprintf(fout, " edx = %s %% %s%s;\n", buf3, buf2, buf1);
+ }
+ else {
+ fprintf(fout, " edx = %s %% %s%s;\n", buf3, buf2, buf1);
+ fprintf(fout, " eax = %s / %s%s;", buf3, buf2, buf1);
+ }
+ break;
+ default:
+ ferr(po, "unhandled division type\n");
}
- else
- ferr(po, "TODO 64bit divident\n");
last_arith_dst = NULL;
delayed_flag_op = NULL;
break;
delayed_flag_op = po;
break;
+ case OP_SCC:
+ // SETcc - should already be handled
+ break;
+
// note: we reuse OP_Jcc for SETcc, only flags differ
- case OP_JO ... OP_JG:
- if (po->flags & OPF_JMP)
- fprintf(fout, " goto %s;", po->operand[0].name);
- // else SETcc - should already be handled
+ case OP_JCC:
+ fprintf(fout, "\n goto %s;", po->operand[0].name);
+ 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:
case OP_CALL:
assert_operand_cnt(1);
- pp = po->datap;
- if (pp == NULL)
- ferr(po, "NULL pp\n");
+ pp = po->pp;
+ my_assert_not(pp, NULL);
+
+ strcpy(buf3, " ");
+ if (po->flags & OPF_CC) {
+ // we treat conditional branch to another func
+ // (yes such code exists..) as conditional tailcall
+ strcat(buf3, " ");
+ fprintf(fout, " {\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]));
+ if (pp->is_fptr && !pp->is_arg)
+ fprintf(fout, "%s%s = %s;\n", buf3, pp->name,
+ out_src_opr(buf1, sizeof(buf1), po, &po->operand[0],
+ "(void *)", 0));
- fprintf(fout, " ");
+ fprintf(fout, "%s", buf3);
if (strstr(pp->ret_type.name, "int64")) {
if (po->flags & OPF_TAIL)
ferr(po, "int64 and tail?\n");
- fprintf(fout, "mul_tmp = ");
+ fprintf(fout, "tmp64 = ");
}
else if (!IS(pp->ret_type.name, "void")) {
if (po->flags & OPF_TAIL) {
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);
+ if (pp->name[0] == 0)
+ ferr(po, "missing pp->name\n");
+ fprintf(fout, "%s%s(", pp->name,
+ pp->has_structarg ? "_sa" : "");
+
+ if (po->flags & OPF_ATAIL) {
+ if (pp->argc_stack != g_func_pp->argc_stack
+ || (pp->argc_stack > 0
+ && pp->is_stdcall != g_func_pp->is_stdcall))
+ ferr(po, "incompatible tailcall\n");
+ if (g_func_pp->has_retreg)
+ ferr(po, "TODO: retreg+tailcall\n");
+
+ for (arg = j = 0; arg < pp->argc; arg++) {
+ if (arg > 0)
+ fprintf(fout, ", ");
+
+ cast[0] = 0;
+ if (pp->arg[arg].type.is_ptr)
+ snprintf(cast, sizeof(cast), "(%s)",
+ pp->arg[arg].type.name);
+
+ if (pp->arg[arg].reg != NULL) {
+ fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
+ continue;
+ }
+ // stack arg
+ for (; j < g_func_pp->argc; j++)
+ if (g_func_pp->arg[j].reg == NULL)
+ break;
+ fprintf(fout, "%sa%d", cast, j + 1);
+ j++;
+ }
}
else {
- 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, ", ");
-
- cast[0] = 0;
- if (pp->arg[arg].type.is_ptr)
- snprintf(cast, sizeof(cast), "(%s)", pp->arg[arg].type.name);
-
- if (pp->arg[arg].reg != NULL) {
- fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
- continue;
- }
+ for (arg = 0; arg < pp->argc; arg++) {
+ if (arg > 0)
+ fprintf(fout, ", ");
+
+ cast[0] = 0;
+ if (pp->arg[arg].type.is_ptr)
+ snprintf(cast, sizeof(cast), "(%s)",
+ pp->arg[arg].type.name);
+
+ if (pp->arg[arg].reg != NULL) {
+ if (pp->arg[arg].type.is_retreg)
+ fprintf(fout, "&%s", pp->arg[arg].reg);
+ else
+ fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
+ continue;
+ }
- // stack arg
- 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) {
- fprintf(fout, "%ss_a%d", cast, tmp_op->argnum);
- }
- else {
- fprintf(fout, "%s",
- out_src_opr(buf1, sizeof(buf1),
- tmp_op, &tmp_op->operand[0], cast, 0));
+ // stack arg
+ 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) {
+ fprintf(fout, "%ss_a%d", cast, tmp_op->argnum);
+ }
+ else {
+ fprintf(fout, "%s",
+ out_src_opr(buf1, sizeof(buf1),
+ tmp_op, &tmp_op->operand[0], cast, 0));
+ }
}
}
fprintf(fout, ");");
if (strstr(pp->ret_type.name, "int64")) {
fprintf(fout, "\n");
- fprintf(fout, " edx = mul_tmp >> 32;\n");
- fprintf(fout, " eax = mul_tmp;");
+ fprintf(fout, "%sedx = tmp64 >> 32;\n", buf3);
+ fprintf(fout, "%seax = tmp64;", buf3);
+ }
+
+ if (pp->is_unresolved) {
+ snprintf(buf2, sizeof(buf2), " unresoved %dreg",
+ pp->argc_reg);
+ strcat(g_comment, buf2);
}
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%sreturn;", buf3);
+ strcat(g_comment, " ^ tailcall");
+ }
}
+ else
+ strcat(g_comment, " tailcall");
}
+ if (pp->is_noreturn)
+ strcat(g_comment, " noreturn");
+ if ((po->flags & OPF_ATAIL) && pp->argc_stack > 0)
+ strcat(g_comment, " argframe");
+ if (po->flags & OPF_CC)
+ strcat(g_comment, " cond");
+
+ if (po->flags & OPF_CC)
+ fprintf(fout, "\n }");
+
delayed_flag_op = NULL;
last_arith_dst = NULL;
break;
case OP_RET:
if (g_func_pp->is_vararg)
fprintf(fout, " va_end(ap);\n");
+ if (g_func_pp->has_retreg) {
+ for (arg = 0; arg < g_func_pp->argc; arg++)
+ if (g_func_pp->arg[arg].type.is_retreg)
+ fprintf(fout, " *r_%s = %s;\n",
+ g_func_pp->arg[arg].reg, g_func_pp->arg[arg].reg);
+ }
if (IS(g_func_pp->ret_type.name, "void")) {
if (i != opcnt - 1 || label_pending)
fprintf(fout, " return (%s)eax;",
g_func_pp->ret_type.name);
}
+ else if (IS(g_func_pp->ret_type.name, "__int64"))
+ fprintf(fout, " return ((u64)edx << 32) | eax;");
else
fprintf(fout, " return eax;");
break;
case OP_PUSH:
+ out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
if (po->argnum != 0) {
// special case - saved func arg
- out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
fprintf(fout, " s_a%d = %s;", po->argnum, buf1);
break;
}
else if (po->flags & OPF_RSAVE) {
- out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
fprintf(fout, " s_%s = %s;", buf1, buf1);
break;
}
+ else if (g_func_pp->is_userstack) {
+ fprintf(fout, " *(--esp) = %s;", buf1);
+ break;
+ }
if (!(g_ida_func_attr & IDAFA_NORETURN))
ferr(po, "stray push encountered\n");
no_output = 1;
fprintf(fout, " %s = s_%s;", buf1, buf1);
break;
}
- ferr(po, "stray pop encountered\n");
+ 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],
+ default_cast_to(buf3, sizeof(buf3), &po->operand[0]), 0));
+ break;
+ }
+ else if (g_func_pp->is_userstack) {
+ fprintf(fout, " %s = *esp++;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
+ break;
+ }
+ else
+ ferr(po, "stray pop encountered\n");
break;
case OP_NOP:
}
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
g_label_refs[i].next = NULL;
if (ops[i].op == OP_CALL) {
- pp = ops[i].datap;
- if (pp)
- proto_release(pp);
+ if (ops[i].pp)
+ proto_release(ops[i].pp);
}
}
g_func_pp = NULL;
add_func_chunk(fasm, words[0], asmln);
}
+ else if (IS_START(p, "; sctend"))
+ break;
+
continue;
} // *p == ';'
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 skip_warned = 0;
int eq_alloc;
int verbose = 0;
+ int multi_seg = 0;
+ int end = 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 if (IS(argv[arg], "-m"))
+ multi_seg = 1;
+ else
+ break;
}
if (argc < arg + 3) {
- printf("usage:\n%s [-v] <.c> <.asm> <hdrf> [rlist]*\n",
+ printf("usage:\n%s [-v] [-rf] [-m] <.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;
unsigned long addr = strtoul(p, NULL, 16);
unsigned long f_addr = strtoul(g_func + 4, NULL, 16);
if (addr > f_addr && !scanned_ahead) {
- anote("scan_ahead caused by '%s', f_addr %lx\n",
- g_func, f_addr);
+ anote("scan_ahead caused by '%s', addr %lx\n",
+ g_func, addr);
scan_ahead(fasm);
scanned_ahead = 1;
func_chunks_sorted = 0;
} // *p == ';'
parse_words:
- memset(words, 0, sizeof(words));
+ 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 == ';') {
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);
+ }
+ else if (IS_START(p, "; sctend")) {
+ end = 1;
+ if (!pending_endp)
+ break;
+ }
}
if (wordc == 0) {
do_pending_endp:
// do delayed endp processing to collect switch jumptables
if (pending_endp) {
- if (in_func && !skip_func && wordc >= 2
+ if (in_func && !skip_func && !end && wordc >= 2
&& ((words[0][0] == 'd' && words[0][2] == 0)
|| (words[1][0] == 'd' && words[1][2] == 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);
}
g_func_pd_cnt = 0;
pd = NULL;
+
+ if (end)
+ break;
if (wordc == 0)
continue;
}
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")) {
+ if (!multi_seg) {
+ end = 1;
+ if (pending_endp)
+ goto do_pending_endp;
+ break;
+ }
+
+ // scan for next text segment
+ while (fgets(line, sizeof(line), fasm)) {
+ asmln++;
+ p = sskip(line);
+ if (*p == 0 || *p == ';')
+ continue;
+
+ if (strstr(p, "segment para public 'CODE' use32"))
+ break;
+ }
+
+ continue;
+ }
+
p = strchr(words[0], ':');
if (p != NULL) {
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++;
}