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_XCHG,
OP_NOT,
OP_CDQ,
+ OP_LODS,
OP_STOS,
OP_MOVS,
OP_CMPS,
OP_SAR,
OP_ROL,
OP_ROR,
+ OP_RCL,
+ OP_RCR,
OP_ADC,
OP_SBB,
+ OP_BSF,
OP_INC,
OP_DEC,
OP_NEG,
OP_CALL,
OP_JMP,
OP_JECXZ,
- 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_JCC,
+ OP_SCC,
};
enum opr_type {
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;
+ int asmln;
};
// datap:
-// OP_CALL - parser proto hint (str), ptr to struct 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
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 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, \
+ printf("%s:%d: error: [%s] '%s': " fmt, asmfn, (op_)->asmln, g_func, \
dump_op(op_), ##__VA_ARGS__); \
fcloseall(); \
exit(1); \
} while (0)
#define fnote(op_, fmt, ...) \
- printf("error:%s:#%zd: '%s': " fmt, g_func, (op_) - ops, \
+ printf("%s:%d: note: [%s] '%s': " fmt, asmfn, (op_)->asmln, g_func, \
dump_op(op_), ##__VA_ARGS__)
#define MAX_REGS 8
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')
"uint8_t", "int8_t", "char",
"unsigned __int8", "__int8", "BYTE", "_BYTE",
"CHAR", "_UNKNOWN",
+ // structures.. deal the same as with _UNKNOWN for now
+ "CRITICAL_SECTION",
};
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"))
if (wordc_in == 2) {
if (IS(words[w], "offset")) {
opr->type = OPT_OFFSET;
+ opr->lmod = OPLM_DWORD;
strcpy(opr->name, words[w + 1]);
- return wordc;
+ pp = proto_parse(g_fhdr, opr->name, 1);
+ goto do_label;
}
if (IS(words[w], "(offset")) {
p = strchr(words[w + 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 },
{ "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 },
{ "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 },
{ "call", OP_CALL, 1, 1, OPF_JMP|OPF_DATA|OPF_FLAGS },
{ "jmp", OP_JMP, 1, 1, OPF_JMP },
{ "jecxz",OP_JECXZ, 1, 1, OPF_JMP|OPF_CJMP },
- { "jo", OP_JO, 1, 1, OPF_CJMP_CC }, // 70 OF=1
- { "jno", OP_JNO, 1, 1, OPF_CJMP_CC }, // 71 OF=0
- { "jc", OP_JC, 1, 1, OPF_CJMP_CC }, // 72 CF=1
- { "jb", OP_JC, 1, 1, OPF_CJMP_CC }, // 72
- { "jnc", OP_JNC, 1, 1, OPF_CJMP_CC }, // 73 CF=0
- { "jnb", OP_JNC, 1, 1, OPF_CJMP_CC }, // 73
- { "jae", OP_JNC, 1, 1, OPF_CJMP_CC }, // 73
- { "jz", OP_JZ, 1, 1, OPF_CJMP_CC }, // 74 ZF=1
- { "je", OP_JZ, 1, 1, OPF_CJMP_CC }, // 74
- { "jnz", OP_JNZ, 1, 1, OPF_CJMP_CC }, // 75 ZF=0
- { "jne", OP_JNZ, 1, 1, OPF_CJMP_CC }, // 75
- { "jbe", OP_JBE, 1, 1, OPF_CJMP_CC }, // 76 CF=1 || ZF=1
- { "jna", OP_JBE, 1, 1, OPF_CJMP_CC }, // 76
- { "ja", OP_JA, 1, 1, OPF_CJMP_CC }, // 77 CF=0 && ZF=0
- { "jnbe", OP_JA, 1, 1, OPF_CJMP_CC }, // 77
- { "js", OP_JS, 1, 1, OPF_CJMP_CC }, // 78 SF=1
- { "jns", OP_JNS, 1, 1, OPF_CJMP_CC }, // 79 SF=0
- { "jp", OP_JP, 1, 1, OPF_CJMP_CC }, // 7a PF=1
- { "jpe", OP_JP, 1, 1, OPF_CJMP_CC }, // 7a
- { "jnp", OP_JNP, 1, 1, OPF_CJMP_CC }, // 7b PF=0
- { "jpo", OP_JNP, 1, 1, OPF_CJMP_CC }, // 7b
- { "jl", OP_JL, 1, 1, OPF_CJMP_CC }, // 7c SF!=OF
- { "jnge", OP_JL, 1, 1, OPF_CJMP_CC }, // 7c
- { "jge", OP_JGE, 1, 1, OPF_CJMP_CC }, // 7d SF=OF
- { "jnl", OP_JGE, 1, 1, OPF_CJMP_CC }, // 7d
- { "jle", OP_JLE, 1, 1, OPF_CJMP_CC }, // 7e ZF=1 || SF!=OF
- { "jng", OP_JLE, 1, 1, OPF_CJMP_CC }, // 7e
- { "jg", OP_JG, 1, 1, OPF_CJMP_CC }, // 7f ZF=0 && SF=OF
- { "jnle", OP_JG, 1, 1, OPF_CJMP_CC }, // 7f
- { "seto", OP_JO, 1, 1, OPF_DATA|OPF_CC },
- { "setno", OP_JNO, 1, 1, OPF_DATA|OPF_CC },
- { "setc", OP_JC, 1, 1, OPF_DATA|OPF_CC },
- { "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 },
+ { "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;
+ op->asmln = asmln;
for (opr = 0; opr < op_table[i].minopr; opr++) {
regmask = regmask_ind = 0;
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)
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:
case OP_XCHG:
op->regmask_src |= op->regmask_dst;
op->regmask_dst |= op->regmask_src;
- break;
+ goto check_align;
case OP_JECXZ:
op->operand_cnt = 1;
// 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;
}
}
-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)
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 *stack_ra_out, const char **bp_arg_out, int is_lea)
{
const char *bp_arg = "";
const char *p = NULL;
if (!strncmp(name, "ebp", 3))
stack_ra = 4;
- if (ofs_reg[0] == 0 && 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 (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);
+ parse_stack_access(po, name, ofs_reg, &offset,
+ &stack_ra, &bp_arg, is_lea);
if (offset > stack_ra)
{
}
}
-static void check_label_read_ref(struct parsed_op *po, const char *name)
+static const char *check_label_read_ref(struct parsed_op *po,
+ const char *name)
{
const struct parsed_proto *pp;
if (pp->is_func)
check_func_pp(po, pp, "ref");
+
+ return pp->name;
}
static char *out_src_opr(char *buf, size_t buf_size,
{
char tmp1[256], tmp2[256];
char expr[256];
+ const char *name;
char *p;
int ret;
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:
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)
// last op in stream - unconditional branch or ret
#define LAST_OP(_i) ((ops[_i].flags & OPF_TAIL) \
- || (ops[_i].flags & (OPF_JMP|OPF_CJMP)) == 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)
static int scan_for_esp_adjust(int i, int opcnt, int *adj,
int *multipath)
{
- const struct parsed_proto *pp;
struct parsed_op *po;
int first_pop = -1;
break;
if (po->operand[0].type != OPT_LABEL)
break;
- pp = po->datap;
- if (pp != NULL && pp->is_stdcall)
+ if (po->pp != NULL && po->pp->is_stdcall)
break;
}
}
int offset = 0;
parse_stack_access(po, opr->name, ofs_reg,
- &offset, &stack_ra, NULL);
+ &offset, &stack_ra, NULL, 0);
if (ofs_reg[0] != 0)
ferr(po, "offset reg on arg access?\n");
if (offset <= stack_ra) {
struct parsed_op *po;
struct label_ref *lr;
- while (i >= 0) {
- if (ops[i].cc_scratch == magic)
- return;
- ops[i].cc_scratch = magic;
+ ops[i].cc_scratch = magic;
+ while (1) {
if (g_labels[i][0] != 0) {
lr = &g_label_refs[i];
for (; lr != NULL; lr = lr->next)
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;
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)
if (pp->is_unresolved)
break;
- pp_tmp = ops[j].datap;
+ pp_tmp = ops[j].pp;
if (pp_tmp == NULL)
ferr(po, "arg collect hit unparsed call '%s'\n",
ops[j].operand[0].name);
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;
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 cond_vars = 0;
int need_tmp_var = 0;
int need_tmp64 = 0;
int had_decl = 0;
int depth = 0;
int no_output;
int i, j, l;
- int dummy;
int arg;
int reg;
int ret;
if (g_func_pp == NULL)
ferr(ops, "proto_parse failed for '%s'\n", funcn);
- fprintf(fout, "%s ", g_func_pp->ret_type.name);
- output_pp_attrs(fout, g_func_pp, g_ida_func_attr & IDAFA_NORETURN);
- fprintf(fout, "%s(", funcn);
-
for (i = 0; i < g_func_pp->argc; i++) {
- if (i > 0)
- fprintf(fout, ", ");
- if (g_func_pp->arg[i].fptr != NULL) {
- // func pointer..
- pp = g_func_pp->arg[i].fptr;
- fprintf(fout, "%s (", pp->ret_type.name);
- output_pp_attrs(fout, pp, 0);
- fprintf(fout, "*a%d)(", i + 1);
- for (j = 0; j < pp->argc; j++) {
- if (j > 0)
- fprintf(fout, ", ");
- if (pp->arg[j].fptr)
- ferr(ops, "nested fptr\n");
- fprintf(fout, "%s", pp->arg[j].type.name);
- }
- fprintf(fout, ")");
- }
- 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);
regmask_arg |= 1 << reg;
}
}
- if (g_func_pp->is_vararg) {
- if (i > 0)
- fprintf(fout, ", ");
- fprintf(fout, "...");
- }
-
- fprintf(fout, ")\n{\n");
// pass1:
// - handle ebp/esp frame, remove ops related to it
break;
j = i - 1;
if (i == opcnt && (ops[j].flags & OPF_JMP)) {
- if (found) {
- if (ops[j].op == OP_JMP && !ops[j].operand[0].had_ds)
- // probably local branch back..
- break;
- if (ops[j].op == OP_CALL)
- // probably noreturn call..
+ if (found && is_like_tailjmp(j))
break;
- }
j--;
}
}
}
+ 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);
}
check_func_pp(po, pp, "fptr var call");
if (pp->is_noreturn)
po->flags |= OPF_TAIL;
- po->datap = pp;
}
+ po->pp = pp;
continue;
}
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)
if (po->op == OP_CALL)
{
tmpname = opr_name(po, 0);
- pp = po->datap;
+ pp = po->pp;
if (pp == NULL)
{
// indirect call
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));
for (arg = 0; arg < pp->argc; arg++)
pp->arg[arg].type.name = strdup("int");
}
- po->datap = pp;
+ po->pp = pp;
}
// look for and make use of esp adjust
}
if (po->op == OP_PUSH && po->argnum == 0
- && !(po->flags & OPF_RSAVE))
+ && !(po->flags & OPF_RSAVE) && !g_func_pp->is_userstack)
{
if (po->operand[0].type == OPT_REG)
{
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
if (tmp_op->op == OP_TEST || tmp_op->op == OP_CMP)
{
if (branched || scan_for_mod(tmp_op, setters[j] + 1, i, 0) >= 0)
- pfomask = 1 << pfo;
+ pfomask = 1 << po->pfo;
}
else if (tmp_op->op == OP_CMPS || tmp_op->op == OP_SCAS) {
- pfomask = 1 << pfo;
+ 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 && pfo == PFO_C)
+ if (tmp_op->op == OP_ADD && po->pfo == PFO_C)
need_tmp64 = 1;
}
if (pfomask) {
tmp_op->pfomask |= pfomask;
- cmp_result_vars |= pfomask;
+ cond_vars |= pfomask;
}
// 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;
+ if (po->op == OP_RCL || po->op == OP_RCR
+ || po->op == OP_ADC || po->op == OP_SBB)
+ cond_vars |= 1 << PFO_C;
}
- else if (po->op == OP_CMPS || po->op == OP_SCAS) {
- cmp_result_vars |= 1 << PFO_Z;
+
+ 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_XCHG) {
- need_tmp_var = 1;
- }
else if (po->op == OP_CALL) {
- pp = po->datap;
+ pp = po->pp;
if (pp == NULL)
ferr(po, "NULL pp\n");
}
}
}
+ }
+ else if (po->op == OP_MOV && po->operand[0].pp != NULL
+ && po->operand[1].pp != NULL)
+ {
+ // <var> = offset <something>
+ if ((po->operand[1].pp->is_func || po->operand[1].pp->is_fptr)
+ && !IS_START(po->operand[1].name, "off_"))
+ {
+ if (!po->operand[0].pp->is_fptr)
+ ferr(po, "%s not declared as fptr when it should be\n",
+ po->operand[0].name);
+ if (pp_cmp_func(po->operand[0].pp, po->operand[1].pp)) {
+ pp_print(buf1, sizeof(buf1), po->operand[0].pp);
+ pp_print(buf2, sizeof(buf2), po->operand[1].pp);
+ fnote(po, "var: %s\n", buf1);
+ fnote(po, "func: %s\n", buf2);
+ ferr(po, "^ mismatch\n");
+ }
+ }
+ }
+ 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_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 starts here
+
+ // define userstack size
+ if (g_func_pp->is_userstack) {
+ fprintf(fout, "#ifndef US_SZ_%s\n", g_func_pp->name);
+ fprintf(fout, "#define US_SZ_%s USERSTACK_SIZE\n", g_func_pp->name);
+ fprintf(fout, "#endif\n");
+ }
+
+ // the function itself
+ fprintf(fout, "%s ", g_func_pp->ret_type.name);
+ 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)
+ fprintf(fout, ", ");
+ if (g_func_pp->arg[i].fptr != NULL) {
+ // func pointer..
+ pp = g_func_pp->arg[i].fptr;
+ fprintf(fout, "%s (", pp->ret_type.name);
+ output_pp_attrs(fout, pp, 0);
+ fprintf(fout, "*a%d)(", i + 1);
+ for (j = 0; j < pp->argc; j++) {
+ if (j > 0)
+ fprintf(fout, ", ");
+ if (pp->arg[j].fptr)
+ 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->is_vararg) {
+ if (i > 0)
+ fprintf(fout, ", ");
+ fprintf(fout, "...");
+ }
+
+ fprintf(fout, ")\n{\n");
- if (pp->is_fptr) {
+ // declare indirect functions
+ for (i = 0; i < opcnt; i++) {
+ po = &ops[i];
+ if (po->flags & OPF_RMD)
+ continue;
+
+ if (po->op == OP_CALL) {
+ pp = po->pp;
+ if (pp == NULL)
+ ferr(po, "NULL pp\n");
+
+ 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].datap)) {
+ 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;
fprintf(fout, ");\n");
}
}
- else if (po->op == OP_RET && !IS(g_func_pp->ret_type.name, "void"))
- regmask |= 1 << xAX;
- }
-
- // pass4:
- // - confirm regmask_save, it might have been reduced
- if (regmask_save != 0)
- {
- regmask_save = 0;
- for (i = 0; i < opcnt; i++) {
- po = &ops[i];
- if (po->flags & OPF_RMD)
- continue;
-
- if (po->op == OP_PUSH && (po->flags & OPF_RSAVE))
- regmask_save |= 1 << po->operand[0].reg;
- }
}
-
// output LUTs/jumptables
for (i = 0; i < g_func_pd_cnt; i++) {
pd = &g_func_pd[i];
}
}
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[US_SZ_%s / 4];\n", g_func_pp->name);
+ fprintf(fout, " u32 *esp = &fake_sf[sizeof(fake_sf) / 4];\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++) {
reg = char_array_i(regs_r32,
ARRAY_SIZE(regs_r32), g_func_pp->arg[i].reg);
if (regmask & (1 << reg)) {
- fprintf(fout, " u32 %s = (u32)a%d;\n",
- g_func_pp->arg[i].reg, i + 1);
+ if (g_func_pp->arg[i].type.is_retreg)
+ fprintf(fout, " u32 %s = *r_%s;\n",
+ g_func_pp->arg[i].reg, g_func_pp->arg[i].reg);
+ else
+ fprintf(fout, " u32 %s = (u32)a%d;\n",
+ g_func_pp->arg[i].reg, i + 1);
}
- else
+ else {
+ if (g_func_pp->arg[i].type.is_retreg)
+ ferr(ops, "retreg '%s' is unused?\n",
+ g_func_pp->arg[i].reg);
fprintf(fout, " // %s = a%d; // unused\n",
g_func_pp->arg[i].reg, i + 1);
+ }
had_decl = 1;
}
}
}
}
- 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 (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)) {
- // we need initial flags for ecx=0 case..
- if (i > 0 && ops[i - 1].op == OP_XOR
- && IS(ops[i - 1].operand[0].name,
- ops[i - 1].operand[1].name))
- {
- fprintf(fout, " cond_z = ");
- if (pfomask & (1 << PFO_C))
- fprintf(fout, "cond_c = ");
- fprintf(fout, "0;\n");
- }
- else if (last_arith_dst != NULL) {
- out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
- out_test_for_cc(buf1, sizeof(buf1), po, PFO_Z, 0,
- last_arith_dst->lmod, buf3);
- fprintf(fout, " cond_z = %s;\n", buf1);
+ struct parsed_opr opr = {0,};
+ opr.type = OPT_REG;
+ opr.reg = xCX;
+ opr.lmod = OPLM_DWORD;
+ ret = try_resolve_const(i, &opr, opcnt * 7 + i, &uval);
+
+ if (ret != 1 || uval == 0) {
+ // we need initial flags for ecx=0 case..
+ if (i > 0 && ops[i - 1].op == OP_XOR
+ && IS(ops[i - 1].operand[0].name,
+ ops[i - 1].operand[1].name))
+ {
+ fprintf(fout, " cond_z = ");
+ if (pfomask & (1 << PFO_C))
+ fprintf(fout, "cond_c = ");
+ fprintf(fout, "0;\n");
+ }
+ else if (last_arith_dst != NULL) {
+ out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
+ out_test_for_cc(buf1, sizeof(buf1), po, PFO_Z, 0,
+ last_arith_dst->lmod, buf3);
+ fprintf(fout, " cond_z = %s;\n", buf1);
+ }
+ else
+ ferr(po, "missing initial ZF\n");
}
- else
- ferr(po, "missing initial ZF\n");
}
switch (po->op)
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:
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], "", 0));
- fprintf(fout, " %s = tmp;",
- out_dst_opr(buf1, sizeof(buf1), po, &po->operand[1]));
+ 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;
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:
assert_operand_cnt(3);
if (po->flags & OPF_REP) {
strcpy(g_comment, "rep stos");
}
else {
- fprintf(fout, " %sedi = eax; edi %c= %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, "rep movs");
}
else {
- fprintf(fout, " %sedi = %sesi; edi %c= %d; esi %c= %d;",
+ fprintf(fout, " %sedi = %sesi; edi %c= %d; esi %c= %d;",
buf1, buf1, l, j, l, j);
strcpy(g_comment, "movs");
}
l = (po->flags & OPF_DF) ? '-' : '+';
if (po->flags & OPF_REP) {
fprintf(fout,
- " for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d) {\n",
- l, j, l, j);
+ " for (; ecx != 0; ecx--) {\n");
if (pfomask & (1 << PFO_C)) {
// ugh..
fprintf(fout,
- " cond_c = %sedi < %sesi;\n", buf1, buf1);
+ " cond_c = %sesi < %sedi;\n", buf1, buf1);
pfomask &= ~(1 << PFO_C);
}
fprintf(fout,
- " if ((cond_z = (%sedi == %sesi)) %s 0)\n",
- buf1, buf1, (po->flags & OPF_REPZ) ? "==" : "!=");
+ " cond_z = (%sesi == %sedi); esi %c= %d, edi %c= %d;\n",
+ buf1, buf1, l, j, l, j);
+ fprintf(fout,
+ " if (cond_z %s 0) break;\n",
+ (po->flags & OPF_REPZ) ? "==" : "!=");
fprintf(fout,
- " break;\n"
" }");
snprintf(g_comment, sizeof(g_comment), "rep%s cmps",
(po->flags & OPF_REPZ) ? "e" : "ne");
}
else {
fprintf(fout,
- " cond_z = (%sedi = %sesi); edi %c= %d; esi %c= %d;",
+ " cond_z = (%sesi == %sedi); esi %c= %d; edi %c= %d;",
buf1, buf1, l, j, l, j);
strcpy(g_comment, "cmps");
}
l = (po->flags & OPF_DF) ? '-' : '+';
if (po->flags & OPF_REP) {
fprintf(fout,
- " for (; ecx != 0; ecx--, edi %c= %d)\n", l, j);
+ " for (; ecx != 0; ecx--) {\n");
fprintf(fout,
- " if ((cond_z = (%seax == %sedi)) %s 0)\n",
+ " cond_z = (%seax == %sedi); edi %c= %d;\n",
lmod_cast_u(po, po->operand[0].lmod),
- lmod_cast_u_ptr(po, po->operand[0].lmod),
+ lmod_cast_u_ptr(po, po->operand[0].lmod), l, j);
+ fprintf(fout,
+ " if (cond_z %s 0) break;\n",
(po->flags & OPF_REPZ) ? "==" : "!=");
fprintf(fout,
- " break;");
+ " }");
snprintf(g_comment, sizeof(g_comment), "rep%s scas",
(po->flags & OPF_REPZ) ? "e" : "ne");
}
else {
- fprintf(fout, " cond_z = (%seax = %sedi); edi %c= %d;",
+ fprintf(fout, " cond_z = (%seax == %sedi); edi %c= %d;",
lmod_cast_u(po, po->operand[0].lmod),
lmod_cast_u_ptr(po, po->operand[0].lmod), l, j);
strcpy(g_comment, "scas");
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]);
delayed_flag_op = NULL;
break;
+ case OP_BSF:
+ assert_operand_cnt(2);
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
+ fprintf(fout, " %s = %s ? __builtin_ffs(%s) - 1 : 0;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ buf2, buf2);
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ strcat(g_comment, "bsf");
+ break;
+
case OP_INC:
case OP_DEC:
out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
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:
case OP_CALL:
assert_operand_cnt(1);
- pp = po->datap;
+ pp = po->pp;
my_assert_not(pp, NULL);
- if (pp->is_fptr)
- fprintf(fout, " %s = %s;\n", pp->name,
+ 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 (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));
+ if (pp->is_unresolved)
+ fprintf(fout, "%sunresolved_call(\"%s:%d\", %s);\n",
+ buf3, asmfn, po->asmln, pp->name);
+ }
- fprintf(fout, " ");
+ fprintf(fout, "%s", buf3);
if (strstr(pp->ret_type.name, "int64")) {
if (po->flags & OPF_TAIL)
ferr(po, "int64 and tail?\n");
|| (pp->argc_stack > 0
&& pp->is_stdcall != g_func_pp->is_stdcall))
ferr(po, "incompatible tailcall\n");
+ if (g_func_pp->has_retreg)
+ ferr(po, "TODO: retreg+tailcall\n");
for (arg = j = 0; arg < pp->argc; arg++) {
if (arg > 0)
pp->arg[arg].type.name);
if (pp->arg[arg].reg != NULL) {
- fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
+ if (pp->arg[arg].type.is_retreg)
+ fprintf(fout, "&%s", pp->arg[arg].reg);
+ else
+ fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
continue;
}
if (strstr(pp->ret_type.name, "int64")) {
fprintf(fout, "\n");
- fprintf(fout, " edx = tmp64 >> 32;\n");
- fprintf(fout, " eax = tmp64;");
+ fprintf(fout, "%sedx = tmp64 >> 32;\n", buf3);
+ fprintf(fout, "%seax = tmp64;", buf3);
}
if (pp->is_unresolved) {
- snprintf(buf3, sizeof(buf3), " unresoved %dreg",
+ snprintf(buf2, sizeof(buf2), " unresolved %dreg",
pp->argc_reg);
- strcat(g_comment, buf3);
+ strcat(g_comment, buf2);
}
if (po->flags & OPF_TAIL) {
ferr(po, "int func -> void func tailcall?\n");
}
else {
- fprintf(fout, "\n return;");
+ fprintf(fout, "\n%sreturn;", buf3);
strcat(g_comment, " ^ tailcall");
}
}
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)
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;", buf1,
out_src_opr(buf2, sizeof(buf2),
tmp_op, &tmp_op->operand[0],
- po->operand[0].is_ptr ? "(void *)" : "", 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;
}
- ferr(po, "stray pop encountered\n");
+ else
+ ferr(po, "stray pop encountered\n");
break;
case OP_NOP:
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 skip_warned = 0;
int eq_alloc;
int verbose = 0;
+ int multi_seg = 0;
+ int end = 0;
int arg_out;
int arg;
int pi = 0;
verbose = 1;
else if (IS(argv[arg], "-rf"))
g_allow_regfunc = 1;
+ else if (IS(argv[arg], "-m"))
+ multi_seg = 1;
else
break;
}
if (argc < arg + 3) {
- printf("usage:\n%s [-v] [-rf] <.c> <.asm> <hdrf> [rlist]*\n",
+ printf("usage:\n%s [-v] [-rf] [-m] <.c> <.asm> <hdrf> [rlist]*\n",
argv[0]);
return 1;
}
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)))
{
}
g_func_pd_cnt = 0;
pd = NULL;
+
+ if (end)
+ break;
if (wordc == 0)
continue;
}
continue;
}
- if (wordc == 2 && IS(words[1], "ends"))
- break;
+ 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) {
parse_op(&ops[pi], words, wordc);
if (sctproto != NULL) {
- if (ops[pi].op == OP_CALL)
+ if (ops[pi].op == OP_CALL || ops[pi].op == OP_JMP)
ops[pi].datap = sctproto;
sctproto = NULL;
}