#include "my_assert.h"
#include "my_str.h"
+#include "common.h"
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
#define IS(w, y) !strcmp(w, y)
OPF_DATA = (1 << 1), /* data processing - writes to dst opr */
OPF_FLAGS = (1 << 2), /* sets flags */
OPF_JMP = (1 << 3), /* branch, call */
- OPF_CJMP = (1 << 4), /* cond. branch (cc or jecxz) */
+ OPF_CJMP = (1 << 4), /* cond. branch (cc or jecxz/loop) */
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 */
OP_MOVSX,
OP_XCHG,
OP_NOT,
+ OP_XLAT,
OP_CDQ,
OP_LODS,
OP_STOS,
OP_SHL,
OP_SHR,
OP_SAR,
+ OP_SHLD,
OP_SHRD,
OP_ROL,
OP_ROR,
OP_CALL,
OP_JMP,
OP_JECXZ,
+ OP_LOOP,
OP_JCC,
OP_SCC,
// x87
OPLM_QWORD,
};
+#define MAX_EXITS 128
+
#define MAX_OPERANDS 3
#define NAMELEN 112
static int g_header_mode;
#define ferr(op_, fmt, ...) do { \
- printf("%s:%d: error: [%s] '%s': " fmt, asmfn, (op_)->asmln, g_func, \
- dump_op(op_), ##__VA_ARGS__); \
+ printf("%s:%d: error %u: [%s] '%s': " fmt, asmfn, (op_)->asmln, \
+ __LINE__, g_func, dump_op(op_), ##__VA_ARGS__); \
fcloseall(); \
exit(1); \
} while (0)
dump_op(op_), ##__VA_ARGS__)
#define ferr_assert(op_, cond) do { \
- if (!(cond)) ferr(op_, "assertion '%s' failed on ln :%d\n", #cond, \
- __LINE__); \
+ if (!(cond)) ferr(op_, "assertion '%s' failed\n", #cond); \
} while (0)
const char *regs_r32[] = {
static int guess_lmod_from_name(struct parsed_opr *opr)
{
- if (!strncmp(opr->name, "dword_", 6)) {
+ if (IS_START(opr->name, "dword_") || IS_START(opr->name, "off_")) {
opr->lmod = OPLM_DWORD;
return 1;
}
- if (!strncmp(opr->name, "word_", 5)) {
+ if (IS_START(opr->name, "word_")) {
opr->lmod = OPLM_WORD;
return 1;
}
- if (!strncmp(opr->name, "byte_", 5)) {
+ if (IS_START(opr->name, "byte_")) {
opr->lmod = OPLM_BYTE;
return 1;
}
- if (!strncmp(opr->name, "qword_", 6)) {
+ if (IS_START(opr->name, "qword_")) {
opr->lmod = OPLM_QWORD;
return 1;
}
{ "movsx",OP_MOVSX, 2, 2, OPF_DATA },
{ "xchg", OP_XCHG, 2, 2, OPF_DATA },
{ "not", OP_NOT, 1, 1, OPF_DATA },
+ { "xlat", OP_XLAT, 0, 0, OPF_DATA },
{ "cdq", OP_CDQ, 0, 0, OPF_DATA },
{ "lodsb",OP_LODS, 0, 0, OPF_DATA },
{ "lodsw",OP_LODS, 0, 0, OPF_DATA },
{ "shr", OP_SHR, 2, 2, OPF_DATA|OPF_FLAGS },
{ "sal", OP_SHL, 2, 2, OPF_DATA|OPF_FLAGS },
{ "sar", OP_SAR, 2, 2, OPF_DATA|OPF_FLAGS },
+ { "shld", OP_SHLD, 3, 3, OPF_DATA|OPF_FLAGS },
{ "shrd", OP_SHRD, 3, 3, OPF_DATA|OPF_FLAGS },
{ "rol", OP_ROL, 2, 2, OPF_DATA|OPF_FLAGS },
{ "ror", OP_ROR, 2, 2, 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 },
+ { "loop", OP_LOOP, 1, 1, OPF_JMP|OPF_CJMP|OPF_DATA },
{ "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
break;
// ops with implicit argumets
+ case OP_XLAT:
+ op->operand_cnt = 2;
+ setup_reg_opr(&op->operand[0], xAX, OPLM_BYTE, &op->regmask_src);
+ op->regmask_dst = op->regmask_src;
+ setup_reg_opr(&op->operand[1], xDX, OPLM_DWORD, &op->regmask_src);
+ break;
+
case OP_CDQ:
op->operand_cnt = 2;
setup_reg_opr(&op->operand[0], xDX, OPLM_DWORD, &op->regmask_dst);
op->regmask_dst = op->regmask_src;
break;
+ case OP_LOOP:
+ op->regmask_dst = 1 << xCX;
+ // fallthrough
case OP_JECXZ:
- op->operand_cnt = 1;
+ op->operand_cnt = 2;
op->regmask_src = 1 << xCX;
- op->operand[0].type = OPT_REG;
- op->operand[0].reg = xCX;
- op->operand[0].lmod = OPLM_DWORD;
+ op->operand[1].type = OPT_REG;
+ op->operand[1].reg = xCX;
+ op->operand[1].lmod = OPLM_DWORD;
break;
case OP_IMUL:
default:
break;
}
+
+ if (op->operand[0].type == OPT_REG
+ && op->operand[0].lmod == OPLM_DWORD
+ && op->operand[1].type == OPT_CONST)
+ {
+ if ((op->op == OP_AND && op->operand[1].val == 0)
+ || (op->op == OP_OR && op->operand[1].val == ~0))
+ {
+ op->regmask_src = 0;
+ }
+ }
}
static const char *op_name(struct parsed_op *po)
if ((i) < 0) \
ferr(po, "bad " #i ": %d\n", i)
-static int scan_for_pop(int i, int opcnt, const char *reg,
- int magic, int depth, int *maxdepth, int do_flags)
+// note: this skips over calls and rm'd stuff assuming they're handled
+// so it's intended to use at one of final passes
+static int scan_for_pop(int i, int opcnt, int magic, int reg,
+ int depth, int *maxdepth, int do_flags)
{
const struct parsed_proto *pp;
struct parsed_op *po;
+ int relevant;
int ret = 0;
int j;
return -1; // deadend
}
- if ((po->flags & (OPF_RMD|OPF_DONE))
- || (po->op == OP_PUSH && po->p_argnum != 0)) // arg push
+ if (po->flags & (OPF_RMD|OPF_DONE|OPF_FARG))
continue;
if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
// jumptable
for (j = 0; j < po->btj->count; j++) {
check_i(po, po->btj->d[j].bt_i);
- ret |= scan_for_pop(po->btj->d[j].bt_i, opcnt, reg, magic,
+ ret |= scan_for_pop(po->btj->d[j].bt_i, opcnt, magic, reg,
depth, maxdepth, do_flags);
if (ret < 0)
return ret; // dead end
check_i(po, po->bt_i);
if (po->flags & OPF_CJMP) {
- ret |= scan_for_pop(po->bt_i, opcnt, reg, magic,
+ ret |= scan_for_pop(po->bt_i, opcnt, magic, reg,
depth, maxdepth, do_flags);
if (ret < 0)
return ret; // dead end
continue;
}
+ relevant = 0;
if ((po->op == OP_POP || po->op == OP_PUSH)
- && po->operand[0].type == OPT_REG
- && IS(po->operand[0].name, reg))
+ && po->operand[0].type == OPT_REG && po->operand[0].reg == reg)
{
- if (po->op == OP_PUSH && !(po->flags & OPF_FARGNR)) {
- depth++;
+ relevant = 1;
+ }
+
+ if (po->op == OP_PUSH) {
+ depth++;
+ if (relevant) {
if (depth > *maxdepth)
*maxdepth = depth;
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);
- }
+ }
+ else if (po->op == OP_POP) {
+ if (depth == 0) {
+ if (relevant && 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 ret;
}
-// scan for pop starting from 'ret' op (all paths)
-static int scan_for_pop_ret(int i, int opcnt, const char *reg,
- int flag_set)
+// scan for 'reg' pop backwards starting from i
+// intended to use for register restore search, so other reg
+// references are considered an error
+static int scan_for_rsave_pop_reg(int i, int magic, int reg, int set_flags)
{
- int found = 0;
+ struct parsed_op *po;
+ struct label_ref *lr;
+ int ret = 0;
+
+ ops[i].cc_scratch = magic;
+
+ while (1)
+ {
+ if (g_labels[i] != NULL) {
+ lr = &g_label_refs[i];
+ for (; lr != NULL; lr = lr->next) {
+ check_i(&ops[i], lr->i);
+ ret |= scan_for_rsave_pop_reg(lr->i, magic, reg, set_flags);
+ if (ret < 0)
+ return ret;
+ }
+ if (i > 0 && LAST_OP(i - 1))
+ return ret;
+ }
+
+ i--;
+ if (i < 0)
+ break;
+
+ if (ops[i].cc_scratch == magic)
+ return ret;
+ ops[i].cc_scratch = magic;
+
+ po = &ops[i];
+ if (po->op == OP_POP && po->operand[0].reg == reg) {
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ return -1;
+
+ po->flags |= set_flags;
+ return 1;
+ }
+
+ // this also covers the case where we reach corresponding push
+ if ((po->regmask_dst | po->regmask_src) & (1 << reg))
+ return -1;
+ }
+
+ // nothing interesting on this path
+ return 0;
+}
+
+static void find_reachable_exits(int i, int opcnt, int magic,
+ int *exits, int *exit_count)
+{
+ struct parsed_op *po;
int j;
- for (; i < opcnt; i++) {
- if (!(ops[i].flags & OPF_TAIL))
- continue;
+ for (; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (po->cc_scratch == magic)
+ return;
+ po->cc_scratch = magic;
+
+ if (po->flags & OPF_TAIL) {
+ ferr_assert(po, *exit_count < MAX_EXITS);
+ exits[*exit_count] = i;
+ (*exit_count)++;
+ return;
+ }
- for (j = i - 1; j >= 0; j--) {
- if (ops[j].flags & (OPF_RMD|OPF_DONE))
+ if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
+ if (po->flags & OPF_RMD)
continue;
- if (ops[j].flags & OPF_JMP)
- return -1;
- if (ops[j].op == OP_POP && ops[j].datap == NULL
- && ops[j].operand[0].type == OPT_REG
- && IS(ops[j].operand[0].name, reg))
- {
- found = 1;
- ops[j].flags |= flag_set;
- break;
+ if (po->btj != NULL) {
+ for (j = 0; j < po->btj->count; j++) {
+ check_i(po, po->btj->d[j].bt_i);
+ find_reachable_exits(po->btj->d[j].bt_i, opcnt, magic,
+ exits, exit_count);
+ }
+ return;
}
- if (g_labels[j] != NULL)
- return -1;
+ check_i(po, po->bt_i);
+ if (po->flags & OPF_CJMP)
+ find_reachable_exits(po->bt_i, opcnt, magic, exits, exit_count);
+ else
+ i = po->bt_i - 1;
+ continue;
}
}
+}
+
+// scan for 'reg' pop backwards starting from exits (all paths)
+static int scan_for_pop_ret(int i, int opcnt, int reg, int set_flags)
+{
+ static int exits[MAX_EXITS];
+ static int exit_count;
+ int j, ret;
+
+ if (!set_flags) {
+ exit_count = 0;
+ find_reachable_exits(i, opcnt, i + opcnt * 15, exits,
+ &exit_count);
+ ferr_assert(&ops[i], exit_count > 0);
+ }
- return found ? 0 : -1;
+ for (j = 0; j < exit_count; j++) {
+ ret = scan_for_rsave_pop_reg(exits[j], i + opcnt * 16 + set_flags,
+ reg, set_flags);
+ if (ret == -1)
+ return -1;
+ }
+
+ return 0;
}
-static void scan_for_pop_const(int i, int opcnt, int *regmask_pp)
+// scan for one or more pop of push <const>
+static int scan_for_pop_const_r(int i, int opcnt, int magic,
+ int push_i, int is_probe)
{
struct parsed_op *po;
- int is_multipath = 0;
+ struct label_ref *lr;
+ int ret = 0;
int j;
- for (j = i + 1; j < opcnt; j++) {
- po = &ops[j];
+ for (; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (po->cc_scratch == magic)
+ return ret; // already checked
+ po->cc_scratch = magic;
+
+ if (po->flags & OPF_JMP) {
+ if (po->flags & OPF_RMD)
+ continue;
+ if (po->op == OP_CALL)
+ return -1;
- if (po->op == OP_JMP && po->btj == NULL) {
- ferr_assert(po, po->bt_i >= 0);
- j = po->bt_i - 1;
+ if (po->btj != NULL) {
+ for (j = 0; j < po->btj->count; j++) {
+ check_i(po, po->btj->d[j].bt_i);
+ ret |= scan_for_pop_const_r(po->btj->d[j].bt_i, opcnt, magic,
+ push_i, is_probe);
+ if (ret < 0)
+ return ret;
+ }
+ return ret;
+ }
+
+ check_i(po, po->bt_i);
+ if (po->flags & OPF_CJMP) {
+ ret |= scan_for_pop_const_r(po->bt_i, opcnt, magic, push_i,
+ is_probe);
+ if (ret < 0)
+ return ret;
+ }
+ else {
+ i = po->bt_i - 1;
+ }
continue;
}
- if ((po->flags & (OPF_JMP|OPF_TAIL|OPF_RSAVE))
- || po->op == OP_PUSH)
+ if ((po->flags & (OPF_TAIL|OPF_RSAVE)) || po->op == OP_PUSH)
+ return -1;
+
+ if (g_labels[i] != NULL) {
+ // all refs must be visited
+ lr = &g_label_refs[i];
+ for (; lr != NULL; lr = lr->next) {
+ check_i(po, lr->i);
+ if (ops[lr->i].cc_scratch != magic)
+ return -1;
+ }
+ if (i > 0 && !LAST_OP(i - 1) && ops[i - 1].cc_scratch != magic)
+ return -1;
+ }
+
+ if (po->op == OP_POP)
{
- break;
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ return -1;
+
+ if (!is_probe) {
+ po->flags |= OPF_DONE;
+ po->datap = &ops[push_i];
+ }
+ return 1;
}
+ }
- if (g_labels[j] != NULL)
- is_multipath = 1;
+ return -1;
+}
- if (po->op == OP_POP && !(po->flags & OPF_RMD))
- {
- is_multipath |= !!(po->flags & OPF_PPUSH);
- if (is_multipath) {
- ops[i].flags |= OPF_PPUSH | OPF_DONE;
- ops[i].datap = po;
- po->flags |= OPF_PPUSH | OPF_DONE;
- *regmask_pp |= 1 << po->operand[0].reg;
+static void scan_for_pop_const(int i, int opcnt, int magic)
+{
+ int ret;
+
+ ret = scan_for_pop_const_r(i + 1, opcnt, magic, i, 1);
+ if (ret == 1) {
+ ops[i].flags |= OPF_RMD | OPF_DONE;
+ scan_for_pop_const_r(i + 1, opcnt, magic + 1, i, 0);
+ }
+}
+
+// check if all branch targets within a marked path are also marked
+// note: the path checked must not be empty or end with a branch
+static int check_path_branches(int opcnt, int magic)
+{
+ struct parsed_op *po;
+ int i, j;
+
+ for (i = 0; i < opcnt; i++) {
+ po = &ops[i];
+ if (po->cc_scratch != magic)
+ continue;
+
+ if (po->flags & OPF_JMP) {
+ if ((po->flags & OPF_RMD) || po->op == OP_CALL)
+ continue;
+
+ if (po->btj != NULL) {
+ for (j = 0; j < po->btj->count; j++) {
+ check_i(po, po->btj->d[j].bt_i);
+ if (ops[po->btj->d[j].bt_i].cc_scratch != magic)
+ return 0;
+ }
}
- else {
- ops[i].flags |= OPF_RMD | OPF_DONE;
- po->flags |= OPF_DONE;
- po->datap = &ops[i];
+
+ check_i(po, po->bt_i);
+ if (ops[po->bt_i].cc_scratch != magic)
+ return 0;
+ if ((po->flags & OPF_CJMP) && ops[i + 1].cc_scratch != magic)
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+// scan for multiple pushes for given pop
+static int scan_pushes_for_pop_r(int i, int magic, int pop_i,
+ int is_probe)
+{
+ int reg = ops[pop_i].operand[0].reg;
+ struct parsed_op *po;
+ struct label_ref *lr;
+ int ret = 0;
+
+ ops[i].cc_scratch = magic;
+
+ while (1)
+ {
+ if (g_labels[i] != NULL) {
+ lr = &g_label_refs[i];
+ for (; lr != NULL; lr = lr->next) {
+ check_i(&ops[i], lr->i);
+ ret |= scan_pushes_for_pop_r(lr->i, magic, pop_i, is_probe);
+ if (ret < 0)
+ return ret;
}
+ if (i > 0 && LAST_OP(i - 1))
+ return ret;
+ }
+
+ i--;
+ if (i < 0)
break;
+
+ if (ops[i].cc_scratch == magic)
+ return ret;
+ ops[i].cc_scratch = magic;
+
+ po = &ops[i];
+ if (po->op == OP_CALL)
+ return -1;
+ if ((po->flags & (OPF_TAIL|OPF_RSAVE)) || po->op == OP_POP)
+ return -1;
+
+ if (po->op == OP_PUSH)
+ {
+ if (po->datap != NULL)
+ return -1;
+ if (po->operand[0].type == OPT_REG && po->operand[0].reg == reg)
+ // leave this case for reg save/restore handlers
+ return -1;
+
+ if (!is_probe) {
+ po->flags |= OPF_PPUSH | OPF_DONE;
+ po->datap = &ops[pop_i];
+ }
+ return 1;
+ }
+ }
+
+ return -1;
+}
+
+static void scan_pushes_for_pop(int i, int opcnt, int *regmask_pp)
+{
+ int magic = i + opcnt * 14;
+ int ret;
+
+ ret = scan_pushes_for_pop_r(i, magic, i, 1);
+ if (ret == 1) {
+ ret = check_path_branches(opcnt, magic);
+ if (ret == 1) {
+ ops[i].flags |= OPF_PPUSH | OPF_DONE;
+ *regmask_pp |= 1 << ops[i].operand[0].reg;
+ scan_pushes_for_pop_r(i, magic + 1, i, 0);
}
}
}
while (i >= 0) {
if (ops[i].cc_scratch == magic) {
- ferr(&ops[i], "%s looped\n", __func__);
- return -1;
+ // is this a problem?
+ //ferr(&ops[i], "%s looped\n", __func__);
+ return 0;
}
ops[i].cc_scratch = magic;
static int scan_for_esp_adjust(int i, int opcnt,
int adj_expect, int *adj, int *is_multipath, int do_update)
{
+ int adj_expect_unknown = 0;
struct parsed_op *po;
int first_pop = -1;
+ int adj_best = 0;
*adj = *is_multipath = 0;
+ if (adj_expect < 0) {
+ adj_expect_unknown = 1;
+ adj_expect = 32 * 4; // enough?
+ }
for (; i < opcnt && *adj < adj_expect; i++) {
if (g_labels[i] != NULL)
}
*adj += lmod_bytes(po, po->operand[0].lmod);
+ if (*adj > adj_best)
+ adj_best = *adj;
}
else if (po->flags & (OPF_JMP|OPF_TAIL)) {
if (po->op == OP_JMP && po->btj == NULL) {
break;
if (po->pp != NULL && po->pp->is_stdcall)
break;
+ if (adj_expect_unknown && first_pop >= 0)
+ break;
// assume it's another cdecl call
}
}
if (first_pop >= 0) {
- // probably 'pop ecx' was used..
+ // probably only 'pop ecx' was used
+ *adj = adj_best;
return first_pop;
}
if (i < 0) {
// reached the top - can only be an arg-reg
- if (opr->type != OPT_REG)
+ if (opr->type != OPT_REG || g_func_pp == NULL)
return;
for (i = 0; i < g_func_pp->argc; i++) {
ferr(&ops[j], "'pop ebp' expected\n");
if (g_stack_fsz != 0) {
- if (ops[j - 1].op == OP_MOV
+ if (ops[j].op == OP_LEAVE)
+ j--;
+ else if (ops[j].op == OP_POP
+ && 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 | OPF_DONE;
+ j -= 2;
}
- else if (ops[j].op != OP_LEAVE
- && !(g_ida_func_attr & IDAFA_NORETURN))
+ else if (!(g_ida_func_attr & IDAFA_NORETURN))
{
- ferr(&ops[j - 1], "esp restore expected\n");
+ ferr(&ops[j], "esp restore expected\n");
}
- if (ecx_push && ops[j - 2].op == OP_POP
- && IS(opr_name(&ops[j - 2], 0), "ecx"))
+ if (ecx_push && j >= 0 && ops[j].op == OP_POP
+ && IS(opr_name(&ops[j], 0), "ecx"))
{
- ferr(&ops[j - 2], "unexpected ecx pop\n");
+ ferr(&ops[j], "unexpected ecx pop\n");
}
}
struct parsed_proto *pp;
int multipath = 0;
int adj = 0;
- int ret;
+ int j, ret;
pp = po->pp;
if (pp == NULL || pp->is_vararg || pp->argc_reg != 0)
return NULL;
if (multipath)
return NULL;
- if (ops[ret].op == OP_POP && adj != 4)
- return NULL;
+ if (ops[ret].op == OP_POP) {
+ for (j = 1; j < adj / 4; j++) {
+ if (ops[ret + j].op != OP_POP
+ || ops[ret + j].operand[0].reg != xCX)
+ {
+ return NULL;
+ }
+ }
+ }
}
*adj_i = ret;
&& ops[call_i].operand[1].type == OPT_LABEL)
{
// no other source users?
- ret = resolve_last_ref(i, &po->operand[0], opcnt * 10,
+ ret = resolve_last_ref(i, &po->operand[0], i + opcnt * 10,
&ref_i);
if (ret == 1 && call_i == ref_i) {
// and nothing uses it after us?
ref_i = -1;
ret = find_next_read(i + 1, opcnt, &po->operand[0],
- opcnt * 11, &ref_i);
+ i + opcnt * 11, &ref_i);
if (ret != 1)
// then also don't need the source mov
ops[call_i].flags |= OPF_RMD;
pp->is_fptr = 1;
ret = scan_for_esp_adjust(i + 1, opcnt,
- 32*4, &adj, &multipath, 0);
+ -1, &adj, &multipath, 0);
if (ret < 0 || adj < 0) {
if (!g_allow_regfunc)
ferr(po, "non-__cdecl indirect call unhandled yet\n");
// look for and make use of esp adjust
multipath = 0;
ret = -1;
- if (!pp->is_stdcall && pp->argc_stack > 0)
+ if (!pp->is_stdcall && pp->argc_stack > 0) {
+ int adj_expect = pp->is_vararg ? -1 : pp->argc_stack * 4;
ret = scan_for_esp_adjust(i + 1, opcnt,
- pp->argc_stack * 4, &adj, &multipath, 0);
+ adj_expect, &adj, &multipath, 0);
+ }
if (ret >= 0) {
if (pp->is_vararg) {
if (adj / 4 < pp->argc_stack) {
if (pp->is_unresolved)
break;
- ferr(po, "arg collect %d/%d hit esp adjust of %d\n",
+ fnote(po, "(this call)\n");
+ ferr(&ops[j], "arg collect %d/%d hit esp adjust of %d\n",
arg, pp->argc, ops[j].operand[1].val);
}
else if (ops[j].op == OP_POP && !(ops[j].flags & OPF_DONE))
if (pp->is_unresolved)
break;
- ferr(po, "arg collect %d/%d hit pop\n", arg, pp->argc);
+ fnote(po, "(this call)\n");
+ ferr(&ops[j], "arg collect %d/%d hit pop\n", arg, pp->argc);
}
else if (ops[j].flags & OPF_CJMP)
{
ops[j].flags &= ~OPF_RSAVE;
// check for __VALIST
- if (!pp->is_unresolved && pp->arg[arg].type.is_va_list) {
+ if (!pp->is_unresolved && g_func_pp != NULL
+ && pp->arg[arg].type.is_va_list)
+ {
k = -1;
ret = resolve_origin(j, &ops[j].operand[0],
magic + 1, &k, NULL);
if (pp != NULL) {
if (j >= 0) {
// commit esp adjust
- ops[j].flags |= OPF_RMD;
if (ops[j].op != OP_POP)
patch_esp_adjust(&ops[j], pp->argc_stack * 4);
- else
- ops[j].flags |= OPF_DONE;
+ else {
+ for (l = 0; l < pp->argc_stack; l++)
+ ops[j + l].flags |= OPF_DONE | OPF_RMD;
+ }
}
if (strstr(pp->ret_type.name, "int64"))
}
else if (po->op == OP_PUSH && !(po->flags & OPF_FARG)
&& !(po->flags & OPF_RSAVE) && po->operand[0].type == OPT_CONST)
- scan_for_pop_const(i, opcnt, ®mask_pp);
+ {
+ scan_for_pop_const(i, opcnt, i + opcnt * 12);
+ }
+ else if (po->op == OP_POP)
+ scan_pushes_for_pop(i, opcnt, ®mask_pp);
}
// pass6:
if (po->flags & (OPF_RMD|OPF_DONE))
continue;
- if (po->op == OP_PUSH && (po->flags & OPF_RSAVE)) {
- reg = po->operand[0].reg;
- if (!(regmask & (1 << reg)))
- // not a reg save after all, rerun scan_for_pop
- po->flags &= ~OPF_RSAVE;
- else
- regmask_save |= 1 << reg;
- }
-
if (po->op == OP_PUSH && !(po->flags & OPF_FARG)
&& !(po->flags & OPF_RSAVE) && !g_func_pp->is_userstack)
{
if (reg < 0)
ferr(po, "reg not set for push?\n");
+ // FIXME: OPF_RSAVE, regmask_save
depth = 0;
- ret = scan_for_pop(i + 1, opcnt,
- po->operand[0].name, i + opcnt * 3, 0, &depth, 0);
+ ret = scan_for_pop(i + 1, opcnt, i + opcnt * 3,
+ po->operand[0].reg, 0, &depth, 0);
if (ret == 1) {
- if (depth > 1)
+ if (depth > 0)
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);
+ scan_for_pop(i + 1, opcnt, i + opcnt * 4,
+ po->operand[0].reg, 0, &depth, 1);
continue;
}
- ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, 0);
+ ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, 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;
+ // arg = OPF_RSAVE; // FIXME
}
po->flags |= arg;
- scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, arg);
+ scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, arg);
continue;
}
}
fprintf(fout, " %s = ~%s;", buf1, buf1);
break;
+ case OP_XLAT:
+ assert_operand_cnt(2);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
+ fprintf(fout, " %s = *(u8 *)(%s + %s);", buf1, buf2, buf1);
+ strcpy(g_comment, "xlat");
+ break;
+
case OP_CDQ:
assert_operand_cnt(2);
fprintf(fout, " %s = (s32)%s >> 31;",
// arithmetic w/flags
case OP_AND:
- if (po->operand[1].type == OPT_CONST && !po->operand[1].val) {
- // deal with complex dst clear
- assert_operand_cnt(2);
- 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));
- output_std_flags(fout, po, &pfomask, buf1);
- last_arith_dst = &po->operand[0];
- delayed_flag_op = NULL;
- break;
- }
- // fallthrough
+ if (po->operand[1].type == OPT_CONST && !po->operand[1].val)
+ goto dualop_arith_const;
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ goto dualop_arith;
+
case OP_OR:
propagate_lmod(po, &po->operand[0], &po->operand[1]);
- // fallthrough
+ if (po->operand[1].type == OPT_CONST) {
+ j = lmod_bytes(po, po->operand[0].lmod);
+ if (((1ull << j * 8) - 1) == po->operand[1].val)
+ goto dualop_arith_const;
+ }
+ goto dualop_arith;
+
dualop_arith:
assert_operand_cnt(2);
fprintf(fout, " %s %s= %s;",
delayed_flag_op = NULL;
break;
+ dualop_arith_const:
+ // and 0, or ~0 used instead mov
+ assert_operand_cnt(2);
+ 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));
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
case OP_SHL:
case OP_SHR:
assert_operand_cnt(2);
ferr(po, "TODO\n");
pfomask &= ~(1 << PFO_C);
}
- fprintf(fout, " %s %s= %s;", buf1, op_to_c(po),
+ fprintf(fout, " %s %s= %s", buf1, op_to_c(po),
out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ if (po->operand[1].type != OPT_CONST)
+ fprintf(fout, " & 0x1f");
+ fprintf(fout, ";");
output_std_flags(fout, po, &pfomask, buf1);
last_arith_dst = &po->operand[0];
delayed_flag_op = NULL;
delayed_flag_op = NULL;
break;
+ case OP_SHLD:
case OP_SHRD:
assert_operand_cnt(3);
propagate_lmod(po, &po->operand[0], &po->operand[1]);
out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
out_src_opr_u32(buf3, sizeof(buf3), po, &po->operand[2]);
- fprintf(fout, " %s >>= %s; %s |= %s << (%d - %s);",
- buf1, buf3, buf1, buf2, l, buf3);
- strcpy(g_comment, "shrd");
+ if (po->operand[2].type != OPT_CONST)
+ ferr(po, "TODO: masking\n");
+ if (po->op == OP_SHLD) {
+ fprintf(fout, " %s <<= %s; %s |= %s >> (%d - %s);",
+ buf1, buf3, buf1, buf2, l, buf3);
+ strcpy(g_comment, "shld");
+ }
+ else {
+ fprintf(fout, " %s >>= %s; %s |= %s << (%d - %s);",
+ buf1, buf3, buf1, buf2, l, buf3);
+ strcpy(g_comment, "shrd");
+ }
output_std_flags(fout, po, &pfomask, buf1);
last_arith_dst = &po->operand[0];
delayed_flag_op = NULL;
strcat(g_comment, "jecxz");
break;
+ case OP_LOOP:
+ fprintf(fout, " if (--ecx == 0)\n");
+ fprintf(fout, " goto %s;", po->operand[0].name);
+ strcat(g_comment, "loop");
+ break;
+
case OP_JMP:
assert_operand_cnt(1);
last_arith_dst = NULL;
break;
}
else if (po->flags & OPF_PPUSH) {
- // push/pop graph
+ // push/pop graph / non-const
ferr_assert(po, po->datap == NULL);
fprintf(fout, " %s = pp_%s;", buf1, buf1);
break;
po = &ops[i];
if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
+ if (po->flags & OPF_RMD)
+ continue;
+
if (po->btj != NULL) {
// jumptable
for (j = 0; j < po->btj->count; j++) {
continue;
depth = 0;
- ret = scan_for_pop(i + 1, opcnt,
- po->operand[0].name, i + opcnt * 2, 0, &depth, 0);
+ ret = scan_for_pop(i + 1, opcnt, i + opcnt * 2,
+ po->operand[0].reg, 0, &depth, 0);
if (ret == 1) {
regmask_save |= 1 << reg;
po->flags |= OPF_RMD;
- scan_for_pop(i + 1, opcnt,
- po->operand[0].name, i + opcnt * 3, 0, &depth, 1);
+ scan_for_pop(i + 1, opcnt, i + opcnt * 3,
+ po->operand[0].reg, 0, &depth, 1);
continue;
}
}
int regmask_dep;
int max_bp_offset = 0;
int has_ret;
- int i, j, ret;
+ int i, j, l;
+ int ret;
pp_c = proto_parse(g_fhdr, funcn, 1);
if (pp_c != NULL)
continue;
if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST)
- scan_for_pop_const(i, opcnt, ®mask_dummy);
+ scan_for_pop_const(i, opcnt, i + opcnt * 13);
}
// pass5:
if (pp != NULL) {
if (j >= 0) {
// commit esp adjust
- ops[j].flags |= OPF_RMD;
if (ops[j].op != OP_POP)
patch_esp_adjust(&ops[j], pp->argc_stack * 4);
- else
- ops[j].flags |= OPF_DONE;
+ else {
+ for (l = 0; l < pp->argc_stack; l++)
+ ops[j + l].flags |= OPF_DONE | OPF_RMD;
+ }
}
po->flags |= OPF_DONE;
if (po->flags & (OPF_RMD|OPF_DONE))
continue;
- if (po->op == OP_PUSH && po->operand[0].type == OPT_REG)
+ if (po->op == OP_PUSH && po->operand[0].type == OPT_REG
+ && po->operand[0].reg != xCX)
{
- ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, 0);
+ ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, 0);
if (ret == 0) {
// regmask_save |= 1 << po->operand[0].reg; // do it later
po->flags |= OPF_RSAVE | OPF_RMD | OPF_DONE;
- scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, OPF_RMD);
+ scan_for_pop_ret(i + 1, opcnt, po->operand[0].reg, OPF_RMD);
}
}
- else if (po->op == OP_CALL && !(po->flags & OPF_DONE))
+ else if (po->op == OP_CALL)
{
pp = process_call(i, opcnt);
if (cbits[i >> 3] & (1 << (i & 7)))
continue;
+ if (g_labels[i] == NULL && i > 0 && ops[i - 1].op == OP_CALL
+ && ops[i - 1].pp != NULL && ops[i - 1].pp->is_osinc)
+ {
+ // the compiler sometimes still generates code after
+ // noreturn OS functions
+ break;
+ }
if (ops[i].op != OP_NOP)
ferr(&ops[i], "unreachable code\n");
}
fwrite(line, 1, strlen(line), fout);
}
-// read a line, truncating it if it doesn't fit
-static char *my_fgets(char *s, size_t size, FILE *stream)
-{
- char *ret, *ret2;
- char buf[64];
- int p;
-
- p = size - 2;
- if (p >= 0)
- s[p] = 0;
-
- ret = fgets(s, size, stream);
- if (ret != NULL && p >= 0 && s[p] != 0 && s[p] != '\n') {
- p = sizeof(buf) - 2;
- do {
- buf[p] = 0;
- ret2 = fgets(buf, sizeof(buf), stream);
- }
- while (ret2 != NULL && buf[p] != 0 && buf[p] != '\n');
- }
-
- return ret;
-}
-
// '=' needs special treatment
// also ' quote
static char *next_word_s(char *w, size_t wsize, char *s)
if (wordc < 2)
continue;
+ if (IS_START(words[0], "__IMPORT_DESCRIPTOR_")) {
+ // when this starts, we don't need anything from this section
+ break;
+ }
+
if ((hg_var_cnt & 0xff) == 0) {
hg_vars = realloc(hg_vars, sizeof(hg_vars[0])
* (hg_var_cnt + 0x100));