/*
* ia32rtools
- * (C) notaz, 2013,2014
+ * (C) notaz, 2013-2015
*
* This work is licensed under the terms of 3-clause BSD license.
* See COPYING file in the top-level directory.
#include "masm_tools.h"
enum op_flags {
- OPF_RMD = (1 << 0), /* removed or optimized out */
+ OPF_RMD = (1 << 0), /* removed from code generation */
OPF_DATA = (1 << 1), /* data processing - writes to dst opr */
OPF_FLAGS = (1 << 2), /* sets flags */
OPF_JMP = (1 << 3), /* branch, call */
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 */
- OPF_VAPUSH = (1 << 17), /* vararg ptr push (as call arg) */
+ OPF_FARG = (1 << 11), /* push collected as func arg */
+ OPF_FARGNR = (1 << 12), /* push collected as func arg (no reuse) */
+ OPF_EBP_S = (1 << 13), /* ebp used as scratch here, not BP */
+ OPF_DF = (1 << 14), /* DF flag set */
+ OPF_ATAIL = (1 << 15), /* tail call with reused arg frame */
+ OPF_32BIT = (1 << 16), /* 32bit division */
+ OPF_LOCK = (1 << 17), /* op has lock prefix */
+ OPF_VAPUSH = (1 << 18), /* vararg ptr push (as call arg) */
+ OPF_DONE = (1 << 19), /* already fully handled by analysis */
};
enum op_op {
OP_SHL,
OP_SHR,
OP_SAR,
+ OP_SHRD,
OP_ROL,
OP_ROR,
OP_RCL,
OP_JECXZ,
OP_JCC,
OP_SCC,
+ // x87
+ // mmx
+ OP_EMMS,
+ // undefined
+ OP_UD2,
};
enum opr_type {
OPLM_BYTE,
OPLM_WORD,
OPLM_DWORD,
+ OPLM_QWORD,
};
#define MAX_OPERANDS 3
+#define NAMELEN 112
struct parsed_opr {
enum opr_type type;
const struct parsed_proto *pp; // for OPT_LABEL
int reg;
unsigned int val;
- char name[112];
+ char name[NAMELEN];
};
struct parsed_op {
unsigned char pfo;
unsigned char pfo_inv;
unsigned char operand_cnt;
- unsigned char p_argnum; // push: altered before call arg #
- unsigned char p_argpass;// push: arg of host func
- unsigned char pad[3];
+ unsigned char p_argnum; // arg push: altered before call arg #
+ unsigned char p_arggrp; // arg push: arg group # for above
+ unsigned char p_argpass;// arg push: arg of host func
+ short p_argnext;// arg push: same arg pushed elsewhere or -1
int regmask_src; // all referensed regs
int regmask_dst;
int pfomask; // flagop: parsed_flag_op that can't be delayed
// datap:
// 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
+// (OPF_CC) - points to one of (OPF_FLAGS) that affects cc op
+// OP_POP - points to OP_PUSH in push/pop pair
struct parsed_equ {
char name[64];
IDAFA_FPD = (1 << 5),
};
-#define MAX_OPS 4096
+// note: limited to 32k due to p_argnext
+#define MAX_OPS 4096
+#define MAX_ARG_GRP 2
static struct parsed_op ops[MAX_OPS];
static struct parsed_equ *g_eqs;
static int g_eqcnt;
-static char g_labels[MAX_OPS][48];
+static char *g_labels[MAX_OPS];
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_stack_frame_used;
static int g_stack_fsz;
static int g_ida_func_attr;
+static int g_skip_func;
static int g_allow_regfunc;
+static int g_quiet_pp;
+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: note: [%s] '%s': " fmt, asmfn, (op_)->asmln, g_func, \
dump_op(op_), ##__VA_ARGS__)
-#define MAX_REGS 8
+#define ferr_assert(op_, cond) do { \
+ if (!(cond)) ferr(op_, "assertion '%s' failed on ln :%d\n", #cond, \
+ __LINE__); \
+} while (0)
-const char *regs_r32[] = { "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp" };
+const char *regs_r32[] = {
+ "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp",
+ // not r32, but list here for easy parsing and printing
+ "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
+};
const char *regs_r16[] = { "ax", "bx", "cx", "dx", "si", "di", "bp", "sp" };
const char *regs_r8l[] = { "al", "bl", "cl", "dl" };
const char *regs_r8h[] = { "ah", "bh", "ch", "dh" };
PFO_LE, // e ZF=1||SF!=OF
};
+#define PFOB_O (1 << PFO_O)
+#define PFOB_C (1 << PFO_C)
+#define PFOB_Z (1 << PFO_Z)
+#define PFOB_S (1 << PFO_S)
+
static const char *parsed_flag_op_names[] = {
"o", "c", "z", "be", "s", "p", "l", "le"
};
snprintf(buf, buf_size, number < 10 ? "%lu" : "0x%02lx", number);
}
+static int check_segment_prefix(const char *s)
+{
+ if (s[0] == 0 || s[1] != 's' || s[2] != ':')
+ return 0;
+
+ switch (s[0]) {
+ case 'c': return 1;
+ case 'd': return 2;
+ case 's': return 3;
+ case 'e': return 4;
+ case 'f': return 5;
+ case 'g': return 6;
+ default: return 0;
+ }
+}
+
static int parse_reg(enum opr_lenmod *reg_lmod, const char *s)
{
int reg;
reg = char_array_i(regs_r32, ARRAY_SIZE(regs_r32), s);
+ if (reg >= 8) {
+ *reg_lmod = OPLM_QWORD;
+ return reg;
+ }
if (reg >= 0) {
*reg_lmod = OPLM_DWORD;
return reg;
s++;
*d = 0;
- // skip 'ds:' prefix
- if (IS_START(s, "ds:"))
+ // skip '?s:' prefixes
+ if (check_segment_prefix(s))
s += 3;
s = next_idt(w, sizeof(w), s);
return 0;
}
-static const char *parse_stack_el(const char *name, char *extra_reg)
+static const char *parse_stack_el(const char *name, char *extra_reg,
+ int early_try)
{
const char *p, *p2, *s;
char *endp = NULL;
long val;
int len;
- p = name;
- if (IS_START(p + 3, "+ebp+") && is_reg_in_str(p)) {
- p += 4;
- if (extra_reg != NULL) {
- strncpy(extra_reg, name, 3);
- extra_reg[4] = 0;
+ if (g_bp_frame || early_try)
+ {
+ p = name;
+ if (IS_START(p + 3, "+ebp+") && is_reg_in_str(p)) {
+ p += 4;
+ if (extra_reg != NULL) {
+ strncpy(extra_reg, name, 3);
+ extra_reg[4] = 0;
+ }
}
- }
- if (IS_START(p, "ebp+")) {
- p += 4;
+ if (IS_START(p, "ebp+")) {
+ p += 4;
- p2 = strchr(p, '+');
- if (p2 != NULL && is_reg_in_str(p)) {
- if (extra_reg != NULL) {
- strncpy(extra_reg, p, p2 - p);
- extra_reg[p2 - p] = 0;
+ p2 = strchr(p, '+');
+ if (p2 != NULL && is_reg_in_str(p)) {
+ if (extra_reg != NULL) {
+ strncpy(extra_reg, p, p2 - p);
+ extra_reg[p2 - p] = 0;
+ }
+ p = p2 + 1;
}
- p = p2 + 1;
- }
- if (!('0' <= *p && *p <= '9'))
- return p;
+ if (!('0' <= *p && *p <= '9'))
+ return p;
- return NULL;
+ return NULL;
+ }
}
if (!IS_START(name, "esp+"))
opr->lmod = OPLM_BYTE;
return 1;
}
+ if (!strncmp(opr->name, "qword_", 6)) {
+ opr->lmod = OPLM_QWORD;
+ return 1;
+ }
return 0;
}
const struct parsed_type *c_type)
{
static const char *dword_types[] = {
- "int", "_DWORD", "UINT_PTR", "DWORD",
+ "uint32_t", "int", "_DWORD", "UINT_PTR", "DWORD",
"WPARAM", "LPARAM", "UINT", "__int32",
- "LONG", "HIMC", "BOOL",
+ "LONG", "HIMC", "BOOL", "size_t",
+ "float",
};
static const char *word_types[] = {
"uint16_t", "int16_t", "_WORD", "WORD",
int *regmask, int *regmask_indirect,
char words[16][256], int wordc, int w, unsigned int op_flags)
{
- const struct parsed_proto *pp;
+ const struct parsed_proto *pp = NULL;
enum opr_lenmod tmplmod;
unsigned long number;
char buf[256];
if (label != NULL) {
opr->type = OPT_LABEL;
- if (IS_START(label, "ds:")) {
+ ret = check_segment_prefix(label);
+ if (ret != 0) {
+ if (ret >= 5)
+ aerr("fs/gs used\n");
opr->had_ds = 1;
label += 3;
}
opr->lmod = OPLM_WORD;
else if (IS(words[w], "byte"))
opr->lmod = OPLM_BYTE;
+ else if (IS(words[w], "qword"))
+ opr->lmod = OPLM_QWORD;
else
aerr("type parsing failed\n");
w += 2;
if (wordc_in != 1)
aerr("parse_operand 1 word expected\n");
- if (IS_START(words[w], "ds:")) {
+ ret = check_segment_prefix(words[w]);
+ if (ret != 0) {
+ if (ret >= 5)
+ aerr("fs/gs used\n");
opr->had_ds = 1;
memmove(words[w], words[w] + 3, strlen(words[w]) - 2);
}
aerr("[] parse failure\n");
parse_indmode(opr->name, regmask_indirect, 1);
- if (opr->lmod == OPLM_UNSPEC && parse_stack_el(opr->name, NULL)) {
+ if (opr->lmod == OPLM_UNSPEC && parse_stack_el(opr->name, NULL, 1))
+ {
// might be an equ
struct parsed_equ *eq =
- equ_find(NULL, parse_stack_el(opr->name, NULL), &i);
+ equ_find(NULL, parse_stack_el(opr->name, NULL, 1), &i);
if (eq)
opr->lmod = eq->lmod;
}
// 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, g_quiet_pp);
do_label:
if (pp != NULL) {
{ "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 },
+ { "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 },
{ "rcl", OP_RCL, 2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C },
{ "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 },
+ // x87
+ // mmx
+ { "emms", OP_EMMS, 0, 0, OPF_DATA },
+ { "movq", OP_MOV, 2, 2, OPF_DATA },
+ // must be last
+ { "ud2", OP_UD2 },
};
static void parse_op(struct parsed_op *op, char words[16][256], int wordc)
break;
}
- if (i == ARRAY_SIZE(op_table))
- aerr("unhandled op: '%s'\n", words[0]);
+ if (i == ARRAY_SIZE(op_table)) {
+ if (!g_skip_func)
+ aerr("unhandled op: '%s'\n", words[0]);
+ i--; // OP_UD2
+ }
w++;
op->op = op_table[i].op;
op->regmask_src = op->regmask_dst = 0;
op->asmln = asmln;
- for (opr = 0; opr < op_table[i].minopr; opr++) {
+ if (op->op == OP_UD2)
+ return;
+
+ for (opr = 0; opr < op_table[i].maxopr; opr++) {
+ if (opr >= op_table[i].minopr && w >= wordc)
+ break;
+
regmask = regmask_ind = 0;
w = parse_operand(&op->operand[opr], ®mask, ®mask_ind,
words, wordc, w, op->flags);
if (opr == 0 && (op->flags & OPF_DATA))
op->regmask_dst = regmask;
- // for now, mark dst as src too
- op->regmask_src |= regmask | regmask_ind;
- }
-
- for (; w < wordc && opr < op_table[i].maxopr; opr++) {
- w = parse_operand(&op->operand[opr],
- &op->regmask_src, &op->regmask_src,
- words, wordc, w, op->flags);
+ else
+ op->regmask_src |= regmask;
+ op->regmask_src |= regmask_ind;
}
if (w < wordc)
if (!strncmp(op_table[i].name, "set", 3))
op->operand[0].lmod = OPLM_BYTE;
- // ops with implicit argumets
switch (op->op) {
+ // first operand is not dst
+ case OP_CMP:
+ case OP_TEST:
+ op->regmask_src |= op->regmask_dst;
+ op->regmask_dst = 0;
+ break;
+
+ // first operand is src too
+ case OP_NOT:
+ case OP_ADD:
+ case OP_AND:
+ case OP_OR:
+ case OP_RCL:
+ case OP_RCR:
+ case OP_ADC:
+ case OP_INC:
+ case OP_DEC:
+ case OP_NEG:
+ // more below..
+ op->regmask_src |= op->regmask_dst;
+ break;
+
+ // special
+ case OP_XCHG:
+ op->regmask_src |= op->regmask_dst;
+ op->regmask_dst |= op->regmask_src;
+ goto check_align;
+
+ case OP_SUB:
+ case OP_SBB:
+ case OP_XOR:
+ if (op->operand[0].type == OPT_REG && op->operand[1].type == OPT_REG
+ && 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->regmask_src = 0;
+ }
+ else
+ op->regmask_src |= op->regmask_dst;
+ break;
+
+ // ops with implicit argumets
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_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;
// fallthrough
case OP_MUL:
// singleop mul
+ op->regmask_src |= op->regmask_dst;
op->regmask_dst = (1 << xDX) | (1 << xAX);
- op->regmask_src |= (1 << xAX);
if (op->operand[0].lmod == OPLM_UNSPEC)
op->operand[0].lmod = OPLM_DWORD;
break;
case OP_IDIV:
// we could set up operands for edx:eax, but there is no real need to
// (see is_opr_modified())
- regmask = (1 << xDX) | (1 << xAX);
- op->regmask_dst = regmask;
- op->regmask_src |= regmask;
+ op->regmask_src |= op->regmask_dst;
+ op->regmask_dst = (1 << xDX) | (1 << xAX);
if (op->operand[0].lmod == OPLM_UNSPEC)
op->operand[0].lmod = OPLM_DWORD;
break;
case OP_SAR:
case OP_ROL:
case OP_ROR:
+ op->regmask_src |= op->regmask_dst;
if (op->operand[1].lmod == OPLM_UNSPEC)
op->operand[1].lmod = OPLM_BYTE;
break;
+ case OP_SHRD:
+ op->regmask_src |= op->regmask_dst;
+ if (op->operand[2].lmod == OPLM_UNSPEC)
+ op->operand[2].lmod = OPLM_BYTE;
+ break;
+
case OP_PUSH:
+ op->regmask_src |= op->regmask_dst;
+ op->regmask_dst = 0;
if (op->operand[0].lmod == OPLM_UNSPEC
&& (op->operand[0].type == OPT_CONST
|| op->operand[0].type == OPT_OFFSET
if (op->operand[0].type == OPT_REG && op->operand[1].type == OPT_REG
&& 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..
+ && IS(op->operand[0].name, op->operand[1].name)) // ! ah, al..
{
- op->flags |= OPF_RMD;
+ op->flags |= OPF_RMD | OPF_DONE;
op->regmask_src = op->regmask_dst = 0;
}
break;
char buf[16];
snprintf(buf, sizeof(buf), "%s+0", op->operand[0].name);
if (IS(buf, op->operand[1].name))
- op->flags |= OPF_RMD;
+ op->flags |= OPF_RMD | OPF_DONE;
}
break;
op->regmask_dst = 0;
break;
+ case OP_LEAVE:
+ op->regmask_dst = (1 << xBP) | (1 << xSP);
+ op->regmask_src = 1 << xBP;
+ break;
+
default:
break;
}
enum opr_lenmod lmod)
{
switch (lmod) {
+ case OPLM_QWORD:
+ return "u64";
case OPLM_DWORD:
return "u32";
case OPLM_WORD:
enum opr_lenmod lmod)
{
switch (lmod) {
+ case OPLM_QWORD:
+ return "";
case OPLM_DWORD:
return "";
case OPLM_WORD:
enum opr_lenmod lmod)
{
switch (lmod) {
+ case OPLM_QWORD:
+ return "*(u64 *)";
case OPLM_DWORD:
return "*(u32 *)";
case OPLM_WORD:
enum opr_lenmod lmod)
{
switch (lmod) {
+ case OPLM_QWORD:
+ return "(s64)";
case OPLM_DWORD:
return "(s32)";
case OPLM_WORD:
static int lmod_bytes(struct parsed_op *po, enum opr_lenmod lmod)
{
switch (lmod) {
+ case OPLM_QWORD:
+ return 8;
case OPLM_DWORD:
return 4;
case OPLM_WORD:
static const char *opr_reg_p(struct parsed_op *po, struct parsed_opr *popr)
{
- if ((unsigned int)popr->reg >= MAX_REGS)
+ if ((unsigned int)popr->reg >= ARRAY_SIZE(regs_r32))
ferr(po, "invalid reg: %d\n", popr->reg);
return regs_r32[popr->reg];
}
static int is_stack_access(struct parsed_op *po,
const struct parsed_opr *popr)
{
- return (parse_stack_el(popr->name, NULL)
+ return (parse_stack_el(popr->name, NULL, 0)
|| (g_bp_frame && !(po->flags & OPF_EBP_S)
&& IS_START(popr->name, "ebp")));
}
ferr(po, "ebp- parse of '%s' failed\n", name);
}
else {
- bp_arg = parse_stack_el(name, ofs_reg);
+ bp_arg = parse_stack_el(name, ofs_reg, 0);
snprintf(g_comment, sizeof(g_comment), "%s", bp_arg);
eq = equ_find(po, bp_arg, &offset);
if (eq == NULL)
// fptrs must use 32bit args, callsite might have no information and
// lack a cast to smaller types, which results in incorrectly masked
// args passed (callee may assume masked args, it does on ARM)
- if (!pp->is_oslib) {
+ if (!pp->is_osinc) {
for (i = 0; i < pp->argc; i++) {
ret = guess_lmod_from_c_type(&tmp_lmod, &pp->arg[i].type);
if (ret && tmp_lmod != OPLM_DWORD)
ferr(po, "lea from reg?\n");
switch (popr->lmod) {
+ case OPLM_QWORD:
+ snprintf(buf, buf_size, "%s%s.q", cast, opr_reg_p(po, popr));
+ break;
case OPLM_DWORD:
snprintf(buf, buf_size, "%s%s", cast, opr_reg_p(po, popr));
break;
switch (popr->type) {
case OPT_REG:
switch (popr->lmod) {
+ case OPLM_QWORD:
+ snprintf(buf, buf_size, "%s.q", opr_reg_p(po, popr));
+ break;
case OPLM_DWORD:
snprintf(buf, buf_size, "%s", opr_reg_p(po, popr));
break;
char buf1[256], buf2[256];
enum opr_lenmod lmod;
- if (po->operand[0].lmod != po->operand[1].lmod)
+ if (po->op != OP_DEC && 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;
}
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);
+ if (po->op == OP_DEC)
+ snprintf(buf2, sizeof(buf2), "1");
+ else
+ out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], cast_use, 0);
switch (pfo) {
case PFO_C:
buf1, is_inv ? ">=" : "<", buf2);
break;
- case PFO_LE:
+ case PFO_LE: // !g
snprintf(buf, buf_size, "(%s %s %s)",
buf1, is_inv ? ">" : "<=", buf2);
break;
if (po->flags & OPF_TAIL) {
if (po->op == OP_CALL) {
- pp = proto_parse(g_fhdr, po->operand[0].name, 0);
+ pp = proto_parse(g_fhdr, po->operand[0].name, g_quiet_pp);
if (pp != NULL && pp->is_noreturn)
// no stack cleanup for noreturn
return ret;
return -1; // deadend
}
- if ((po->flags & OPF_RMD)
+ if ((po->flags & (OPF_RMD|OPF_DONE))
|| (po->op == OP_PUSH && po->p_argnum != 0)) // arg push
continue;
&& po->operand[0].type == OPT_REG
&& IS(po->operand[0].name, reg))
{
- if (po->op == OP_PUSH && !(po->flags & OPF_FARG)) {
+ if (po->op == OP_PUSH && !(po->flags & OPF_FARGNR)) {
depth++;
if (depth > *maxdepth)
*maxdepth = depth;
continue;
for (j = i - 1; j >= 0; j--) {
- if (ops[j].flags & OPF_RMD)
+ if (ops[j].flags & (OPF_RMD|OPF_DONE))
continue;
if (ops[j].flags & OPF_JMP)
return -1;
- if (ops[j].op == OP_POP && ops[j].operand[0].type == OPT_REG
+ 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;
break;
}
- if (g_labels[j][0] != 0)
+ if (g_labels[j] != NULL)
return -1;
}
}
return found ? 0 : -1;
}
+static void scan_for_pop_const(int i, int opcnt)
+{
+ int j;
+
+ 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] != NULL)
+ {
+ break;
+ }
+
+ if (ops[j].op == OP_POP && !(ops[j].flags & (OPF_RMD|OPF_DONE)))
+ {
+ ops[i].flags |= OPF_RMD | OPF_DONE;
+ ops[j].flags |= OPF_DONE;
+ ops[j].datap = &ops[i];
+ break;
+ }
+ }
+}
+
static void scan_propagate_df(int i, int opcnt)
{
struct parsed_op *po = &ops[i];
break;
if (po->op == OP_CLD) {
- po->flags |= OPF_RMD;
+ po->flags |= OPF_RMD | OPF_DONE;
return;
}
}
return -1;
}
+#define check_i(po, i) \
+ if ((i) < 0) \
+ ferr(po, "bad " #i ": %d\n", i)
+
static int scan_for_flag_set(int i, int magic, int *branched,
int *setters, int *setter_cnt)
{
}
ops[i].cc_scratch = magic;
- if (g_labels[i][0] != 0) {
+ if (g_labels[i] != NULL) {
*branched = 1;
lr = &g_label_refs[i];
for (; lr->next; lr = lr->next) {
+ check_i(&ops[i], lr->i);
ret = scan_for_flag_set(lr->i, magic,
branched, setters, setter_cnt);
if (ret < 0)
return ret;
}
+ check_i(&ops[i], lr->i);
if (i > 0 && LAST_OP(i - 1)) {
i = lr->i;
continue;
static int scan_for_cdq_edx(int i)
{
while (i >= 0) {
- if (g_labels[i][0] != 0) {
+ if (g_labels[i] != NULL) {
if (g_label_refs[i].next != NULL)
return -1;
if (i > 0 && LAST_OP(i - 1)) {
static int scan_for_reg_clear(int i, int reg)
{
while (i >= 0) {
- if (g_labels[i][0] != 0) {
+ if (g_labels[i] != NULL) {
if (g_label_refs[i].next != NULL)
return -1;
if (i > 0 && LAST_OP(i - 1)) {
return -1;
}
+static void patch_esp_adjust(struct parsed_op *po, int adj)
+{
+ ferr_assert(po, po->op == OP_ADD);
+ ferr_assert(po, IS(opr_name(po, 0), "esp"));
+ ferr_assert(po, po->operand[1].type == OPT_CONST);
+
+ // this is a bit of a hack, but deals with use of
+ // single adj for multiple calls
+ po->operand[1].val -= adj;
+ po->flags |= OPF_RMD;
+ if (po->operand[1].val == 0)
+ po->flags |= OPF_DONE;
+ ferr_assert(po, (int)po->operand[1].val >= 0);
+}
+
// scan for positive, constant esp adjust
-static int scan_for_esp_adjust(int i, int opcnt, int *adj,
- int *multipath)
+// multipath case is preliminary
+static int scan_for_esp_adjust(int i, int opcnt,
+ int adj_expect, int *adj, int *is_multipath, int do_update)
{
struct parsed_op *po;
int first_pop = -1;
- *adj = *multipath = 0;
+ *adj = *is_multipath = 0;
- for (; i < opcnt; i++) {
- po = &ops[i];
+ for (; i < opcnt && *adj < adj_expect; i++) {
+ if (g_labels[i] != NULL)
+ *is_multipath = 1;
- if (g_labels[i][0] != 0)
- *multipath = 1;
+ po = &ops[i];
+ if (po->flags & OPF_DONE)
+ continue;
if (po->op == OP_ADD && po->operand[0].reg == xSP) {
if (po->operand[1].type != OPT_CONST)
*adj += po->operand[1].val;
if (*adj & 3)
ferr(&ops[i], "unaligned esp adjust: %x\n", *adj);
+ if (do_update) {
+ if (!*is_multipath)
+ patch_esp_adjust(po, adj_expect);
+ else
+ po->flags |= OPF_RMD;
+ }
return i;
}
- else if (po->op == OP_PUSH && !(po->flags & OPF_RMD)) {
+ else if (po->op == OP_PUSH) {
//if (first_pop == -1)
// first_pop = -2; // none
*adj -= lmod_bytes(po, po->operand[0].lmod);
}
- else if (po->op == OP_POP && !(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;
+ else if (po->op == OP_POP) {
+ if (!(po->flags & OPF_DONE)) {
+ // 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;
+ }
+ if (do_update && *adj >= 0) {
+ po->flags |= OPF_RMD;
+ if (!*is_multipath)
+ po->flags |= OPF_DONE;
+ }
+
*adj += lmod_bytes(po, po->operand[0].lmod);
}
else if (po->flags & (OPF_JMP|OPF_TAIL)) {
if (po->op == OP_JMP && po->btj == NULL) {
+ if (po->bt_i <= i)
+ break;
i = po->bt_i - 1;
continue;
}
break;
if (po->pp != NULL && po->pp->is_stdcall)
break;
+ // assume it's another cdecl call
}
}
int stack_ra = 0;
int offset = 0;
+ if (g_header_mode)
+ return NULL;
+
parse_stack_access(po, opr->name, ofs_reg,
&offset, &stack_ra, NULL, 0);
if (ofs_reg[0] != 0)
p = strchr(opr->name + 1, '[');
memcpy(buf, opr->name, p - opr->name);
buf[p - opr->name] = 0;
- pp = proto_parse(g_fhdr, buf, 0);
+ pp = proto_parse(g_fhdr, buf, g_quiet_pp);
}
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");
+ pp = proto_parse(g_fhdr, opr->name, g_quiet_pp);
+ if (pp == NULL) {
+ if (!g_header_mode)
+ ferr(po, "proto_parse failed for icall to '%s'\n", opr->name);
+ }
+ else
+ check_func_pp(po, pp, "reg-fptr ref");
}
return pp;
ops[i].cc_scratch = magic;
while (1) {
- if (g_labels[i][0] != 0) {
+ if (g_labels[i] != NULL) {
lr = &g_label_refs[i];
- for (; lr != NULL; lr = lr->next)
+ for (; lr != NULL; lr = lr->next) {
+ check_i(&ops[i], lr->i);
scan_for_call_type(lr->i, opr, magic, pp_found, multi);
+ }
if (i > 0 && LAST_OP(i - 1))
return;
}
*pp_found = pp;
}
-static const struct parsed_proto *resolve_icall(int i, int opcnt,
- int *multi_src)
+// early check for tail call or branch back
+static int is_like_tailjmp(int j)
{
- const struct parsed_proto *pp = NULL;
- int search_advice = 0;
-
- *multi_src = 0;
+ if (!(ops[j].flags & OPF_JMP))
+ return 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;
- }
+ 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 pp;
+ return 0;
}
-// find an instruction that changed opr before i op
-// *op_i must be set to -1
-static int resolve_origin(int i, const struct parsed_opr *opr,
- int magic, int *op_i)
+static void scan_prologue_epilogue(int opcnt)
{
- struct label_ref *lr;
- int ret = 0;
+ int ecx_push = 0, esp_sub = 0;
+ int found;
+ int i, j, l;
- ops[i].cc_scratch = magic;
+ if (ops[0].op == OP_PUSH && IS(opr_name(&ops[0], 0), "ebp")
+ && ops[1].op == OP_MOV
+ && IS(opr_name(&ops[1], 0), "ebp")
+ && IS(opr_name(&ops[1], 1), "esp"))
+ {
+ g_bp_frame = 1;
+ ops[0].flags |= OPF_RMD | OPF_DONE;
+ ops[1].flags |= OPF_RMD | OPF_DONE;
+ i = 2;
- while (1) {
- if (g_labels[i][0] != 0) {
- lr = &g_label_refs[i];
- for (; lr != NULL; lr = lr->next)
- ret |= resolve_origin(lr->i, opr, magic, op_i);
- if (i > 0 && LAST_OP(i - 1))
- return ret;
+ if (ops[2].op == OP_SUB && IS(opr_name(&ops[2], 0), "esp")) {
+ g_stack_fsz = opr_const(&ops[2], 1);
+ ops[2].flags |= OPF_RMD | OPF_DONE;
+ i++;
+ }
+ else {
+ // another way msvc builds stack frame..
+ i = 2;
+ while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
+ g_stack_fsz += 4;
+ ops[i].flags |= OPF_RMD | OPF_DONE;
+ ecx_push++;
+ i++;
+ }
+ // and another way..
+ if (i == 2 && ops[i].op == OP_MOV && ops[i].operand[0].reg == xAX
+ && ops[i].operand[1].type == OPT_CONST
+ && ops[i + 1].op == OP_CALL
+ && IS(opr_name(&ops[i + 1], 0), "__alloca_probe"))
+ {
+ g_stack_fsz += ops[i].operand[1].val;
+ ops[i].flags |= OPF_RMD | OPF_DONE;
+ i++;
+ ops[i].flags |= OPF_RMD | OPF_DONE;
+ i++;
+ }
}
- i--;
- if (i < 0)
- return -1;
-
- if (ops[i].cc_scratch == magic)
- return 0;
- ops[i].cc_scratch = magic;
+ found = 0;
+ do {
+ for (; i < opcnt; i++)
+ if (ops[i].op == OP_RET)
+ break;
+ j = i - 1;
+ if (i == opcnt && (ops[j].flags & OPF_JMP)) {
+ if (found && is_like_tailjmp(j))
+ break;
+ j--;
+ }
- if (!(ops[i].flags & OPF_DATA))
- continue;
- if (!is_opr_modified(opr, &ops[i]))
- continue;
+ if ((ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ebp"))
+ || ops[j].op == OP_LEAVE)
+ {
+ ops[j].flags |= OPF_RMD | OPF_DONE;
+ }
+ else if (!(g_ida_func_attr & IDAFA_NORETURN))
+ ferr(&ops[j], "'pop ebp' expected\n");
- if (*op_i >= 0) {
- if (*op_i == i)
- return 1;
- // XXX: could check if the other op does the same
- return -1;
- }
+ if (g_stack_fsz != 0) {
+ 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 | OPF_DONE;
+ }
+ else if (ops[j].op != OP_LEAVE
+ && !(g_ida_func_attr & IDAFA_NORETURN))
+ {
+ ferr(&ops[j - 1], "esp restore expected\n");
+ }
- *op_i = i;
- return 1;
- }
-}
+ if (ecx_push && ops[j - 2].op == OP_POP
+ && IS(opr_name(&ops[j - 2], 0), "ecx"))
+ {
+ ferr(&ops[j - 2], "unexpected ecx pop\n");
+ }
+ }
-static int try_resolve_const(int i, const struct parsed_opr *opr,
- int magic, unsigned int *val)
-{
- int s_i = -1;
- int ret = 0;
+ found = 1;
+ i++;
+ } while (i < opcnt);
- ret = resolve_origin(i, opr, magic, &s_i);
- if (ret == 1) {
- i = s_i;
- if (ops[i].op != OP_MOV && ops[i].operand[1].type != OPT_CONST)
- return -1;
+ return;
+ }
- *val = ops[i].operand[1].val;
- return 1;
+ // non-bp frame
+ i = 0;
+ while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
+ ops[i].flags |= OPF_RMD | OPF_DONE;
+ g_stack_fsz += 4;
+ ecx_push++;
+ i++;
+ }
+
+ for (; i < opcnt; i++) {
+ if (ops[i].op == OP_PUSH || (ops[i].flags & (OPF_JMP|OPF_TAIL)))
+ break;
+ if (ops[i].op == OP_SUB && ops[i].operand[0].reg == xSP
+ && ops[i].operand[1].type == OPT_CONST)
+ {
+ g_stack_fsz = ops[i].operand[1].val;
+ ops[i].flags |= OPF_RMD | OPF_DONE;
+ esp_sub = 1;
+ break;
+ }
+ }
+
+ if (ecx_push && !esp_sub) {
+ // could actually be args for a call..
+ for (; i < opcnt; i++)
+ if (ops[i].op != OP_PUSH)
+ break;
+
+ if (ops[i].op == OP_CALL && ops[i].operand[0].type == OPT_LABEL) {
+ const struct parsed_proto *pp;
+ pp = proto_parse(g_fhdr, opr_name(&ops[i], 0), 1);
+ j = pp ? pp->argc_stack : 0;
+ while (i > 0 && j > 0) {
+ i--;
+ if (ops[i].op == OP_PUSH) {
+ ops[i].flags &= ~(OPF_RMD | OPF_DONE);
+ j--;
+ }
+ }
+ if (j != 0)
+ ferr(&ops[i], "unhandled prologue\n");
+
+ // recheck
+ i = g_stack_fsz = ecx_push = 0;
+ while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
+ if (!(ops[i].flags & OPF_RMD))
+ break;
+ g_stack_fsz += 4;
+ ecx_push++;
+ i++;
+ }
+ }
+ }
+
+ found = 0;
+ if (ecx_push || esp_sub)
+ {
+ g_sp_frame = 1;
+
+ i++;
+ do {
+ for (; i < opcnt; i++)
+ if (ops[i].op == OP_RET)
+ break;
+ j = i - 1;
+ if (i == opcnt && (ops[j].flags & OPF_JMP)) {
+ if (found && is_like_tailjmp(j))
+ break;
+ j--;
+ }
+
+ if (ecx_push > 0) {
+ for (l = 0; l < ecx_push; l++) {
+ if (ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ecx"))
+ /* pop ecx */;
+ else if (ops[j].op == OP_ADD
+ && IS(opr_name(&ops[j], 0), "esp")
+ && ops[j].operand[1].type == OPT_CONST)
+ {
+ /* add esp, N */
+ l += ops[j].operand[1].val / 4 - 1;
+ }
+ else
+ ferr(&ops[j], "'pop ecx' expected\n");
+
+ ops[j].flags |= OPF_RMD | OPF_DONE;
+ j--;
+ }
+ if (l != ecx_push)
+ ferr(&ops[j], "epilogue scan failed\n");
+
+ found = 1;
+ }
+
+ if (esp_sub) {
+ 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 | OPF_DONE;
+ ops[j].operand[1].val = 0; // hack for stack arg scanner
+ found = 1;
+ }
+
+ i++;
+ } while (i < opcnt);
+ }
+}
+
+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;
+}
+
+// find an instruction that changed opr before i op
+// *op_i must be set to -1 by caller
+// *entry is set to 1 if one source is determined to be the caller
+// returns 1 if found, *op_i is then set to origin
+static int resolve_origin(int i, const struct parsed_opr *opr,
+ int magic, int *op_i, int *is_caller)
+{
+ struct label_ref *lr;
+ int ret = 0;
+
+ if (ops[i].cc_scratch == magic)
+ return 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 |= resolve_origin(lr->i, opr, magic, op_i, is_caller);
+ }
+ if (i > 0 && LAST_OP(i - 1))
+ return ret;
+ }
+
+ i--;
+ if (i < 0) {
+ if (is_caller != NULL)
+ *is_caller = 1;
+ 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 (*op_i >= 0) {
+ if (*op_i == i)
+ return 1;
+ // XXX: could check if the other op does the same
+ return -1;
+ }
+
+ *op_i = i;
+ return 1;
+ }
+}
+
+static int try_resolve_const(int i, const struct parsed_opr *opr,
+ int magic, unsigned int *val)
+{
+ int s_i = -1;
+ int ret;
+
+ ret = resolve_origin(i, opr, magic, &s_i, NULL);
+ if (ret == 1) {
+ i = s_i;
+ if (ops[i].op != OP_MOV && ops[i].operand[1].type != OPT_CONST)
+ return -1;
+
+ *val = ops[i].operand[1].val;
+ return 1;
}
return -1;
}
+static struct parsed_proto *process_call_early(int i, int opcnt,
+ int *adj_i)
+{
+ struct parsed_op *po = &ops[i];
+ struct parsed_proto *pp;
+ int multipath = 0;
+ int adj = 0;
+ int ret;
+
+ pp = po->pp;
+ if (pp == NULL || pp->is_vararg || pp->argc_reg != 0)
+ // leave for later
+ return NULL;
+
+ // look for and make use of esp adjust
+ *adj_i = ret = -1;
+ if (!pp->is_stdcall && pp->argc_stack > 0)
+ ret = scan_for_esp_adjust(i + 1, opcnt,
+ pp->argc_stack * 4, &adj, &multipath, 0);
+ if (ret >= 0) {
+ if (pp->argc_stack > adj / 4)
+ return NULL;
+ if (multipath)
+ return NULL;
+ if (ops[ret].op == OP_POP && adj != 4)
+ return NULL;
+ }
+
+ *adj_i = ret;
+ return pp;
+}
+
+static struct parsed_proto *process_call(int i, int opcnt)
+{
+ struct parsed_op *po = &ops[i];
+ const struct parsed_proto *pp_c;
+ struct parsed_proto *pp;
+ const char *tmpname;
+ int adj = 0, multipath = 0;
+ int ret, arg;
+
+ tmpname = opr_name(po, 0);
+ pp = po->pp;
+ if (pp == NULL)
+ {
+ // indirect call
+ pp_c = resolve_icall(i, opcnt, &multipath);
+ if (pp_c != NULL) {
+ if (!pp_c->is_func && !pp_c->is_fptr)
+ ferr(po, "call to non-func: %s\n", pp_c->name);
+ pp = proto_clone(pp_c);
+ my_assert_not(pp, NULL);
+ if (multipath)
+ // 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,
+ 32*4, &adj, &multipath, 0);
+ if (ret < 0 || adj < 0) {
+ if (!g_allow_regfunc)
+ ferr(po, "non-__cdecl indirect call unhandled yet\n");
+ pp->is_unresolved = 1;
+ adj = 0;
+ }
+ adj /= 4;
+ if (adj > ARRAY_SIZE(pp->arg))
+ ferr(po, "esp adjust too large: %d\n", adj);
+ pp->ret_type.name = strdup("int");
+ pp->argc = pp->argc_stack = adj;
+ 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
+ multipath = 0;
+ ret = -1;
+ if (!pp->is_stdcall && pp->argc_stack > 0)
+ ret = scan_for_esp_adjust(i + 1, opcnt,
+ pp->argc_stack * 4, &adj, &multipath, 0);
+ if (ret >= 0) {
+ if (pp->is_vararg) {
+ if (adj / 4 < pp->argc_stack) {
+ fnote(po, "(this call)\n");
+ ferr(&ops[ret], "esp adjust is too small: %x < %x\n",
+ adj, pp->argc_stack * 4);
+ }
+ // modify pp to make it have varargs as normal args
+ arg = pp->argc;
+ pp->argc += adj / 4 - pp->argc_stack;
+ for (; arg < pp->argc; arg++) {
+ pp->arg[arg].type.name = strdup("int");
+ pp->argc_stack++;
+ }
+ if (pp->argc > ARRAY_SIZE(pp->arg))
+ ferr(po, "too many args for '%s'\n", tmpname);
+ }
+ if (pp->argc_stack > adj / 4) {
+ fnote(po, "(this call)\n");
+ ferr(&ops[ret], "stack tracking failed for '%s': %x %x\n",
+ tmpname, pp->argc_stack * 4, adj);
+ }
+
+ scan_for_esp_adjust(i + 1, opcnt,
+ pp->argc_stack * 4, &adj, &multipath, 1);
+ }
+ else if (pp->is_vararg)
+ ferr(po, "missing esp_adjust for vararg func '%s'\n",
+ pp->name);
+
+ return pp;
+}
+
+static int collect_call_args_early(struct parsed_op *po, int i,
+ struct parsed_proto *pp, int *regmask)
+{
+ int arg, ret;
+ int j;
+
+ for (arg = 0; arg < pp->argc; arg++)
+ if (pp->arg[arg].reg == NULL)
+ break;
+
+ // first see if it can be easily done
+ for (j = i; j > 0 && arg < pp->argc; )
+ {
+ if (g_labels[j] != NULL)
+ return -1;
+ j--;
+
+ if (ops[j].op == OP_CALL)
+ return -1;
+ else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP)
+ return -1;
+ else if (ops[j].op == OP_POP)
+ return -1;
+ else if (ops[j].flags & OPF_CJMP)
+ return -1;
+ else if (ops[j].op == OP_PUSH) {
+ if (ops[j].flags & (OPF_FARG|OPF_FARGNR))
+ return -1;
+ ret = scan_for_mod(&ops[j], j + 1, i, 1);
+ if (ret >= 0)
+ return -1;
+
+ if (pp->arg[arg].type.is_va_list)
+ return -1;
+
+ // next arg
+ for (arg++; arg < pp->argc; arg++)
+ if (pp->arg[arg].reg == NULL)
+ break;
+ }
+ }
+
+ if (arg < pp->argc)
+ return -1;
+
+ // now do it
+ for (arg = 0; arg < pp->argc; arg++)
+ if (pp->arg[arg].reg == NULL)
+ break;
+
+ for (j = i; j > 0 && arg < pp->argc; )
+ {
+ j--;
+
+ if (ops[j].op == OP_PUSH)
+ {
+ ops[j].p_argnext = -1;
+ ferr_assert(&ops[j], pp->arg[arg].datap == NULL);
+ pp->arg[arg].datap = &ops[j];
+
+ if (ops[j].operand[0].type == OPT_REG)
+ *regmask |= 1 << ops[j].operand[0].reg;
+
+ ops[j].flags |= OPF_RMD | OPF_DONE | OPF_FARGNR | OPF_FARG;
+ ops[j].flags &= ~OPF_RSAVE;
+
+ // next arg
+ for (arg++; arg < pp->argc; arg++)
+ if (pp->arg[arg].reg == NULL)
+ break;
+ }
+ }
+
+ return 0;
+}
+
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, int *regmask, int *save_arg_vars,
+ int *arg_grp, int arg, int magic, int need_op_saving, int may_reuse)
{
struct parsed_proto *pp_tmp;
+ struct parsed_op *po_tmp;
struct label_ref *lr;
int need_to_save_current;
+ int arg_grp_current = 0;
+ int save_args_seen = 0;
int save_args;
int ret = 0;
int reg;
}
ops[j].cc_scratch = magic;
- if (g_labels[j][0] != 0 && g_label_refs[j].i != -1) {
+ if (g_labels[j] != NULL && 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) {
+ check_i(&ops[j], lr->i);
if ((ops[lr->i].flags & (OPF_JMP|OPF_CJMP)) != OPF_JMP)
may_reuse = 1;
ret = collect_call_args_r(po, lr->i, pp, regmask, save_arg_vars,
- arg, magic, need_op_saving, may_reuse);
+ arg_grp, arg, magic, need_op_saving, may_reuse);
if (ret < 0)
return ret;
}
+ check_i(&ops[j], lr->i);
if ((ops[lr->i].flags & (OPF_JMP|OPF_CJMP)) != OPF_JMP)
may_reuse = 1;
if (j > 0 && LAST_OP(j - 1)) {
}
need_op_saving = 1;
ret = collect_call_args_r(po, lr->i, pp, regmask, save_arg_vars,
- arg, magic, need_op_saving, may_reuse);
+ arg_grp, arg, magic, need_op_saving, may_reuse);
if (ret < 0)
return ret;
}
ferr(po, "arg collect %d/%d hit '%s' with %d stack args\n",
arg, pp->argc, opr_name(&ops[j], 0), pp_tmp->argc_stack);
}
- else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP) {
+ // esp adjust of 0 means we collected it before
+ else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP
+ && (ops[j].operand[1].type != OPT_CONST
+ || ops[j].operand[1].val != 0))
+ {
if (pp->is_unresolved)
break;
- ferr(po, "arg collect %d/%d hit esp adjust\n",
- arg, pp->argc);
+ ferr(po, "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) {
+ else if (ops[j].op == OP_POP && !(ops[j].flags & OPF_DONE))
+ {
if (pp->is_unresolved)
break;
may_reuse = 1;
}
- else if (ops[j].op == OP_PUSH && !(ops[j].flags & OPF_FARG))
+ else if (ops[j].op == OP_PUSH
+ && !(ops[j].flags & (OPF_FARGNR|OPF_DONE)))
{
if (pp->is_unresolved && (ops[j].flags & OPF_RMD))
break;
+ ops[j].p_argnext = -1;
+ po_tmp = pp->arg[arg].datap;
+ if (po_tmp != NULL)
+ ops[j].p_argnext = po_tmp - ops;
pp->arg[arg].datap = &ops[j];
+
need_to_save_current = 0;
save_args = 0;
reg = -1;
ops[j].p_argnum = arg + 1;
save_args |= 1 << arg;
}
- else if (ops[j].p_argnum < arg + 1)
- ferr(&ops[j], "p_argnum conflict (%d<%d) for '%s'\n",
- ops[j].p_argnum, arg + 1, pp->name);
+ else if (ops[j].p_argnum < arg + 1) {
+ // XXX: might kill valid var..
+ //*save_arg_vars &= ~(1 << (ops[j].p_argnum - 1));
+ ops[j].p_argnum = arg + 1;
+ save_args |= 1 << arg;
+ }
+
+ if (save_args_seen & (1 << (ops[j].p_argnum - 1))) {
+ save_args_seen = 0;
+ arg_grp_current++;
+ if (arg_grp_current >= MAX_ARG_GRP)
+ ferr(&ops[j], "out of arg groups (arg%d), f %s\n",
+ ops[j].p_argnum, pp->name);
+ }
}
else if (ops[j].p_argnum == 0)
ops[j].flags |= OPF_RMD;
// but that can't happen if we didn't branch, so they
// can be removed from future searches (handles nested calls)
if (!may_reuse)
- ops[j].flags |= OPF_FARG;
+ ops[j].flags |= OPF_FARGNR;
+ ops[j].flags |= OPF_FARG;
ops[j].flags &= ~OPF_RSAVE;
// check for __VALIST
if (!pp->is_unresolved && pp->arg[arg].type.is_va_list) {
k = -1;
- ret = resolve_origin(j, &ops[j].operand[0], magic + 1, &k);
+ ret = resolve_origin(j, &ops[j].operand[0],
+ magic + 1, &k, NULL);
if (ret == 1 && k >= 0)
{
if (ops[k].op == OP_LEA) {
if (!g_func_pp->is_vararg
|| strstr(ops[k].operand[1].name, buf))
{
- ops[k].flags |= OPF_RMD;
+ ops[k].flags |= OPF_RMD | OPF_DONE;
ops[j].flags |= OPF_RMD | OPF_VAPUSH;
save_args &= ~(1 << arg);
reg = -1;
ret = stack_frame_access(&ops[k], &ops[k].operand[1],
buf, sizeof(buf), ops[k].operand[1].name, "", 1, 0);
if (ret >= 0) {
- ops[k].flags |= OPF_RMD;
+ ops[k].flags |= OPF_RMD | OPF_DONE;
ops[j].flags |= OPF_RMD;
ops[j].p_argpass = ret + 1;
save_args &= ~(1 << arg);
}
magic = (magic & 0xffffff) | (arg << 24);
}
+
+ if (ops[j].p_arggrp > arg_grp_current) {
+ save_args_seen = 0;
+ arg_grp_current = ops[j].p_arggrp;
+ }
+ if (ops[j].p_argnum > 0)
+ save_args_seen |= 1 << (ops[j].p_argnum - 1);
}
if (arg < pp->argc) {
return -1;
}
+ if (arg_grp_current > *arg_grp)
+ *arg_grp = arg_grp_current;
+
return arg;
}
struct parsed_proto *pp, int *regmask, int *save_arg_vars,
int magic)
{
+ // arg group is for cases when pushes for
+ // multiple funcs are going on
+ struct parsed_op *po_tmp;
+ int save_arg_vars_current = 0;
+ int arg_grp = 0;
int ret;
int a;
- ret = collect_call_args_r(po, i, pp, regmask, save_arg_vars,
- 0, magic, 0, 0);
+ ret = collect_call_args_r(po, i, pp, regmask,
+ &save_arg_vars_current, &arg_grp, 0, magic, 0, 0);
if (ret < 0)
return ret;
+ if (arg_grp != 0) {
+ // propagate arg_grp
+ for (a = 0; a < pp->argc; a++) {
+ if (pp->arg[a].reg != NULL)
+ continue;
+
+ po_tmp = pp->arg[a].datap;
+ while (po_tmp != NULL) {
+ po_tmp->p_arggrp = arg_grp;
+ if (po_tmp->p_argnext > 0)
+ po_tmp = &ops[po_tmp->p_argnext];
+ else
+ po_tmp = NULL;
+ }
+ }
+ }
+ save_arg_vars[arg_grp] |= save_arg_vars_current;
+
if (pp->is_unresolved) {
pp->argc += ret;
pp->argc_stack += ret;
return ret;
}
-// early check for tail call or branch back
-static int is_like_tailjmp(int j)
+static void pp_insert_reg_arg(struct parsed_proto *pp, const char *reg)
{
- 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;
+ int i;
for (i = 0; i < pp->argc; i++)
if (pp->arg[i].reg == NULL)
lr->next = lr_new;
}
+static struct parsed_data *try_resolve_jumptab(int i, int opcnt)
+{
+ struct parsed_op *po = &ops[i];
+ struct parsed_data *pd;
+ char label[NAMELEN], *p;
+ int len, j, l;
+
+ p = strchr(po->operand[0].name, '[');
+ if (p == NULL)
+ return NULL;
+
+ len = p - po->operand[0].name;
+ strncpy(label, po->operand[0].name, len);
+ label[len] = 0;
+
+ for (j = 0, pd = NULL; j < g_func_pd_cnt; j++) {
+ if (IS(g_func_pd[j].label, label)) {
+ pd = &g_func_pd[j];
+ break;
+ }
+ }
+ if (pd == NULL)
+ //ferr(po, "label '%s' not parsed?\n", label);
+ return NULL;
+
+ if (pd->type != OPT_OFFSET)
+ ferr(po, "label '%s' with non-offset data?\n", label);
+
+ // find all labels, link
+ for (j = 0; j < pd->count; j++) {
+ for (l = 0; l < opcnt; l++) {
+ if (g_labels[l] != NULL && IS(g_labels[l], pd->d[j].u.label)) {
+ add_label_ref(&g_label_refs[l], i);
+ pd->d[j].bt_i = l;
+ break;
+ }
+ }
+ }
+
+ return pd;
+}
+
+static void clear_labels(int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (g_labels[i] != NULL) {
+ free(g_labels[i]);
+ g_labels[i] = NULL;
+ }
+ }
+}
+
static void output_std_flags(FILE *fout, struct parsed_op *po,
int *pfomask, const char *dst_opr_text)
{
}
}
+enum {
+ OPP_FORCE_NORETURN = (1 << 0),
+ OPP_SIMPLE_ARGS = (1 << 1),
+ OPP_ALIGN = (1 << 2),
+};
+
static void output_pp_attrs(FILE *fout, const struct parsed_proto *pp,
- int is_noreturn)
+ int flags)
{
+ const char *cconv = "";
+
if (pp->is_fastcall)
- fprintf(fout, "__fastcall ");
+ cconv = "__fastcall ";
else if (pp->is_stdcall && pp->argc_reg == 0)
- fprintf(fout, "__stdcall ");
- if (pp->is_noreturn || is_noreturn)
+ cconv = "__stdcall ";
+
+ fprintf(fout, (flags & OPP_ALIGN) ? "%-16s" : "%s", cconv);
+
+ if (pp->is_noreturn || (flags & OPP_FORCE_NORETURN))
fprintf(fout, "noreturn ");
}
+static void output_pp(FILE *fout, const struct parsed_proto *pp,
+ int flags)
+{
+ int i;
+
+ fprintf(fout, (flags & OPP_ALIGN) ? "%-5s" : "%s ",
+ pp->ret_type.name);
+ if (pp->is_fptr)
+ fprintf(fout, "(");
+ output_pp_attrs(fout, pp, flags);
+ if (pp->is_fptr)
+ fprintf(fout, "*");
+ fprintf(fout, "%s", pp->name);
+ if (pp->is_fptr)
+ fprintf(fout, ")");
+
+ fprintf(fout, "(");
+ for (i = 0; i < pp->argc; i++) {
+ if (i > 0)
+ fprintf(fout, ", ");
+ if (pp->arg[i].fptr != NULL && !(flags & OPP_SIMPLE_ARGS)) {
+ // func pointer
+ output_pp(fout, pp->arg[i].fptr, 0);
+ }
+ else if (pp->arg[i].type.is_retreg) {
+ fprintf(fout, "u32 *r_%s", pp->arg[i].reg);
+ }
+ else {
+ fprintf(fout, "%s", pp->arg[i].type.name);
+ if (!pp->is_fptr)
+ fprintf(fout, " a%d", i + 1);
+ }
+ }
+ if (pp->is_vararg) {
+ if (i > 0)
+ fprintf(fout, ", ");
+ fprintf(fout, "...");
+ }
+ fprintf(fout, ")");
+}
+
+static int get_pp_arg_regmask(const struct parsed_proto *pp)
+{
+ int regmask = 0;
+ int i, reg;
+
+ for (i = 0; i < pp->argc; i++) {
+ if (pp->arg[i].reg != NULL) {
+ reg = char_array_i(regs_r32,
+ ARRAY_SIZE(regs_r32), pp->arg[i].reg);
+ if (reg < 0)
+ ferr(ops, "arg '%s' of func '%s' is not a reg?\n",
+ pp->arg[i].reg, pp->name);
+ regmask |= 1 << reg;
+ }
+ }
+
+ return regmask;
+}
+
+static char *saved_arg_name(char *buf, size_t buf_size, int grp, int num)
+{
+ char buf1[16];
+
+ buf1[0] = 0;
+ if (grp > 0)
+ snprintf(buf1, sizeof(buf1), "%d", grp);
+ snprintf(buf, buf_size, "s%s_a%d", buf1, num);
+
+ return buf;
+}
+
+static void gen_x_cleanup(int opcnt);
+
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_data *pd;
const char *tmpname;
unsigned int uval;
- int save_arg_vars = 0;
+ int save_arg_vars[MAX_ARG_GRP] = { 0, };
int cond_vars = 0;
int need_tmp_var = 0;
int need_tmp64 = 0;
if (g_func_pp == NULL)
ferr(ops, "proto_parse failed for '%s'\n", funcn);
- 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;
- }
- }
+ regmask_arg = get_pp_arg_regmask(g_func_pp);
// pass1:
// - handle ebp/esp frame, remove ops related to it
- if (ops[0].op == OP_PUSH && IS(opr_name(&ops[0], 0), "ebp")
- && ops[1].op == OP_MOV
- && IS(opr_name(&ops[1], 0), "ebp")
- && IS(opr_name(&ops[1], 1), "esp"))
- {
- int ecx_push = 0;
-
- g_bp_frame = 1;
- ops[0].flags |= OPF_RMD;
- ops[1].flags |= OPF_RMD;
- i = 2;
-
- if (ops[2].op == OP_SUB && IS(opr_name(&ops[2], 0), "esp")) {
- g_stack_fsz = opr_const(&ops[2], 1);
- ops[2].flags |= OPF_RMD;
- i++;
- }
- else {
- // another way msvc builds stack frame..
- i = 2;
- while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
- g_stack_fsz += 4;
- ops[i].flags |= OPF_RMD;
- ecx_push++;
- i++;
- }
- // and another way..
- if (i == 2 && ops[i].op == OP_MOV && ops[i].operand[0].reg == xAX
- && ops[i].operand[1].type == OPT_CONST
- && ops[i + 1].op == OP_CALL
- && IS(opr_name(&ops[i + 1], 0), "__alloca_probe"))
- {
- g_stack_fsz += ops[i].operand[1].val;
- ops[i].flags |= OPF_RMD;
- i++;
- ops[i].flags |= OPF_RMD;
- i++;
- }
- }
-
- found = 0;
- do {
- for (; i < opcnt; i++)
- if (ops[i].op == OP_RET)
- break;
- j = i - 1;
- if (i == opcnt && (ops[j].flags & OPF_JMP)) {
- if (found && is_like_tailjmp(j))
- break;
- j--;
- }
-
- 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[j], "'pop ebp' expected\n");
-
- if (g_stack_fsz != 0) {
- 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))
- {
- ferr(&ops[j - 1], "esp restore expected\n");
- }
-
- if (ecx_push && ops[j - 2].op == OP_POP
- && IS(opr_name(&ops[j - 2], 0), "ecx"))
- {
- ferr(&ops[j - 2], "unexpected ecx pop\n");
- }
- }
-
- found = 1;
- i++;
- } while (i < opcnt);
- }
- else {
- for (i = 0; i < opcnt; i++) {
- if (ops[i].op == OP_PUSH || (ops[i].flags & (OPF_JMP|OPF_TAIL)))
- break;
- if (ops[i].op == OP_SUB && ops[i].operand[0].reg == xSP
- && ops[i].operand[1].type == OPT_CONST)
- {
- g_sp_frame = 1;
- break;
- }
- }
-
- found = 0;
- if (g_sp_frame)
- {
- g_stack_fsz = ops[i].operand[1].val;
- ops[i].flags |= OPF_RMD;
-
- i++;
- do {
- for (; i < opcnt; i++)
- if (ops[i].op == OP_RET)
- break;
- 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);
- }
- }
+ scan_prologue_epilogue(opcnt);
// pass2:
// - parse calls with labels
po->bt_i = -1;
po->btj = NULL;
- if (po->flags & OPF_RMD)
+ if (po->flags & (OPF_RMD|OPF_DONE))
continue;
if (po->op == OP_CALL) {
continue;
if (po->operand[0].type == OPT_REGMEM) {
- char *p = strchr(po->operand[0].name, '[');
- if (p == NULL)
- goto tailcall;
- ret = p - po->operand[0].name;
- strncpy(buf1, po->operand[0].name, ret);
- buf1[ret] = 0;
-
- for (j = 0, pd = NULL; j < g_func_pd_cnt; j++) {
- if (IS(g_func_pd[j].label, buf1)) {
- pd = &g_func_pd[j];
- break;
- }
- }
+ pd = try_resolve_jumptab(i, opcnt);
if (pd == NULL)
- //ferr(po, "label '%s' not parsed?\n", buf1);
goto tailcall;
- if (pd->type != OPT_OFFSET)
- ferr(po, "label '%s' with non-offset data?\n", buf1);
-
- // find all labels, link
- for (j = 0; j < pd->count; j++) {
- for (l = 0; l < opcnt; l++) {
- if (g_labels[l][0] && IS(g_labels[l], pd->d[j].u.label)) {
- add_label_ref(&g_label_refs[l], i);
- pd->d[j].bt_i = l;
- break;
- }
- }
- }
po->btj = pd;
continue;
}
for (l = 0; l < opcnt; l++) {
- if (g_labels[l][0] && IS(po->operand[0].name, g_labels[l])) {
+ if (g_labels[l] != NULL
+ && IS(po->operand[0].name, g_labels[l]))
+ {
if (l == i + 1 && po->op == OP_JMP) {
// yet another alignment type..
- po->flags |= OPF_RMD;
+ po->flags |= OPF_RMD|OPF_DONE;
break;
}
add_label_ref(&g_label_refs[l], i);
// pass3:
// - remove dead labels
- // - process calls
+ // - process trivial calls
for (i = 0; i < opcnt; i++)
{
- if (g_labels[i][0] != 0 && g_label_refs[i].i == -1)
- g_labels[i][0] = 0;
+ if (g_labels[i] != NULL && g_label_refs[i].i == -1) {
+ free(g_labels[i]);
+ g_labels[i] = NULL;
+ }
po = &ops[i];
- if (po->flags & OPF_RMD)
+ if (po->flags & (OPF_RMD|OPF_DONE))
continue;
if (po->op == OP_CALL)
{
- tmpname = opr_name(po, 0);
- pp = po->pp;
- if (pp == NULL)
- {
- // indirect call
- pp_c = resolve_icall(i, opcnt, &l);
- if (pp_c != NULL) {
- if (!pp_c->is_func && !pp_c->is_fptr)
- ferr(po, "call to non-func: %s\n", pp_c->name);
- 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, &l);
- if (ret >= 0) {
- if (pp->is_vararg) {
- if (j / 4 < pp->argc_stack)
- ferr(po, "esp adjust is too small: %x < %x\n",
- j, pp->argc_stack * 4);
- // modify pp to make it have varargs as normal args
- arg = pp->argc;
- pp->argc += j / 4 - pp->argc_stack;
- for (; arg < pp->argc; arg++) {
- pp->arg[arg].type.name = strdup("int");
- pp->argc_stack++;
- }
- if (pp->argc > ARRAY_SIZE(pp->arg))
- ferr(po, "too many args for '%s'\n", tmpname);
- }
- if (pp->argc_stack != j / 4)
- ferr(po, "stack tracking failed for '%s': %x %x\n",
- tmpname, pp->argc_stack * 4, j);
-
- ops[ret].flags |= OPF_RMD;
- 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;
+ pp = process_call_early(i, opcnt, &j);
+ if (pp != NULL) {
+ if (!(po->flags & OPF_ATAIL))
+ // since we know the args, try to collect them
+ if (collect_call_args_early(po, i, pp, ®mask) != 0)
+ pp = 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;
}
+
+ if (strstr(pp->ret_type.name, "int64"))
+ need_tmp64 = 1;
+
+ po->flags |= OPF_DONE;
}
- else if (pp->is_vararg)
- ferr(po, "missing esp_adjust for vararg func '%s'\n",
- pp->name);
+ }
+ }
+
+ // pass4:
+ // - process calls
+ // - handle push <const>/pop pairs
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+
+ if (po->op == OP_CALL && !(po->flags & OPF_DONE))
+ {
+ pp = process_call(i, opcnt);
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,
+ collect_call_args(po, i, pp, ®mask, save_arg_vars,
i + opcnt * 2);
}
if (strstr(pp->ret_type.name, "int64"))
need_tmp64 = 1;
}
+ 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);
}
- // pass4:
+ // pass5:
// - 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++) {
+ for (i = 0; i < opcnt; i++)
+ {
po = &ops[i];
- if (po->flags & OPF_RMD)
+ if (po->flags & (OPF_RMD|OPF_DONE))
continue;
if (po->op == OP_PUSH && (po->flags & OPF_RSAVE)) {
regmask_save |= 1 << reg;
}
- if (po->op == OP_PUSH && po->p_argnum == 0
+ if (po->op == OP_PUSH && !(po->flags & OPF_FARG)
&& !(po->flags & OPF_RSAVE) && !g_func_pp->is_userstack)
{
if (po->operand[0].type == OPT_REG)
continue;
}
}
- 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;
- }
- }
- }
- }
+ }
if (po->op == OP_STD) {
- po->flags |= OPF_DF | OPF_RMD;
+ po->flags |= OPF_DF | OPF_RMD | OPF_DONE;
scan_propagate_df(i + 1, opcnt);
}
need_tmp64 = 1;
}
else if (po->op == OP_CALL) {
+ // note: resolved non-reg calls are OPF_DONE already
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,
+ collect_call_args(po, i, pp, ®mask, save_arg_vars,
i + opcnt * 2);
// this is pretty rough guess:
need_tmp64 = 1;
}
else if (po->op == OP_CLD)
- po->flags |= OPF_RMD;
+ po->flags |= OPF_RMD | OPF_DONE;
if (po->op == OP_RCL || po->op == OP_RCR || po->op == OP_XCHG) {
need_tmp_var = 1;
}
}
- // pass4:
+ // pass6:
// - confirm regmask_save, it might have been reduced
if (regmask_save != 0)
{
}
// 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");
+ ferr_assert(ops, !g_func_pp->is_fptr);
+ output_pp(fout, g_func_pp,
+ (g_ida_func_attr & IDAFA_NORETURN) ? OPP_FORCE_NORETURN : 0);
+ fprintf(fout, "\n{\n");
// declare indirect functions
for (i = 0; i < opcnt; i++) {
else
snprintf(pp->name, sizeof(pp->name), "icall%d", i);
- fprintf(fout, " %s (", pp->ret_type.name);
- output_pp_attrs(fout, pp, 0);
- fprintf(fout, "*%s)(", pp->name);
- for (j = 0; j < pp->argc; j++) {
- if (j > 0)
- fprintf(fout, ", ");
- fprintf(fout, "%s a%d", pp->arg[j].type.name, j + 1);
- }
- fprintf(fout, ");\n");
+ fprintf(fout, " ");
+ output_pp(fout, pp, OPP_SIMPLE_ARGS);
+ fprintf(fout, ";\n");
}
}
}
regmask_now = regmask & ~regmask_arg;
regmask_now &= ~(1 << xSP);
- if (regmask_now) {
+ if (regmask_now & 0x00ff) {
for (reg = 0; reg < 8; reg++) {
if (regmask_now & (1 << reg)) {
fprintf(fout, " u32 %s", regs_r32[reg]);
}
}
}
+ if (regmask_now & 0xff00) {
+ for (reg = 8; reg < 16; reg++) {
+ if (regmask_now & (1 << reg)) {
+ fprintf(fout, " mmxr %s", regs_r32[reg]);
+ if (regmask_init & (1 << reg))
+ fprintf(fout, " = { 0, }");
+ fprintf(fout, ";\n");
+ had_decl = 1;
+ }
+ }
+ }
if (regmask_save) {
for (reg = 0; reg < 8; reg++) {
}
}
- if (save_arg_vars) {
+ for (i = 0; i < ARRAY_SIZE(save_arg_vars); i++) {
+ if (save_arg_vars[i] == 0)
+ continue;
for (reg = 0; reg < 32; reg++) {
- if (save_arg_vars & (1 << reg)) {
- fprintf(fout, " u32 s_a%d;\n", reg + 1);
+ if (save_arg_vars[i] & (1 << reg)) {
+ fprintf(fout, " u32 %s;\n",
+ saved_arg_name(buf1, sizeof(buf1), i, reg + 1));
had_decl = 1;
}
}
// output ops
for (i = 0; i < opcnt; i++)
{
- if (g_labels[i][0] != 0) {
+ if (g_labels[i] != NULL) {
fprintf(fout, "\n%s:\n", g_labels[i]);
label_pending = 1;
delayed_flag_op = NULL;
break;
+ case OP_SHRD:
+ assert_operand_cnt(3);
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ l = lmod_bytes(po, po->operand[0].lmod) * 8;
+ 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");
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
case OP_ROL:
case OP_ROR:
assert_operand_cnt(2);
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);
+ if (pfomask & ~((1 << PFO_Z) | (1 << PFO_S))) {
+ for (j = 0; j <= PFO_LE; j++) {
+ if (!(pfomask & (1 << j)))
+ continue;
+ if (j == PFO_Z || j == PFO_S)
+ continue;
+
+ out_cmp_for_cc(buf1, sizeof(buf1), po, j, 0);
+ fprintf(fout, " cond_%s = %s;\n",
+ parsed_flag_op_names[j], buf1);
+ pfomask &= ~(1 << j);
+ }
}
goto dualop_arith;
strcat(g_comment, "bsf");
break;
- case OP_INC:
case OP_DEC:
+ if (pfomask & ~(PFOB_S | PFOB_S | PFOB_C)) {
+ for (j = 0; j <= PFO_LE; j++) {
+ if (!(pfomask & (1 << j)))
+ continue;
+ if (j == PFO_Z || j == PFO_S || j == PFO_C)
+ continue;
+
+ out_cmp_for_cc(buf1, sizeof(buf1), po, j, 0);
+ fprintf(fout, " cond_%s = %s;\n",
+ parsed_flag_op_names[j], buf1);
+ pfomask &= ~(1 << j);
+ }
+ }
+ // fallthrough
+
+ case OP_INC:
+ if (pfomask & (1 << PFO_C))
+ // carry is unaffected by inc/dec.. wtf?
+ ferr(po, "carry propagation needed\n");
+
out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
if (po->operand[0].type == OPT_REG) {
strcpy(buf2, po->op == OP_INC ? "++" : "--");
fprintf(fout, "a%d", tmp_op->p_argpass);
}
else if (tmp_op->p_argnum != 0) {
- fprintf(fout, "%ss_a%d", cast, tmp_op->p_argnum);
+ fprintf(fout, "%s%s", cast,
+ saved_arg_name(buf1, sizeof(buf1),
+ tmp_op->p_arggrp, tmp_op->p_argnum));
}
else {
fprintf(fout, "%s",
if (po->flags & OPF_CC)
strcat(g_comment, " cond");
- if (po->flags & OPF_CC)
- fprintf(fout, "\n }");
+ 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;");
+ }
+ else if (g_func_pp->ret_type.is_ptr) {
+ 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;");
+
+ last_arith_dst = NULL;
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_PUSH:
+ out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
+ if (po->p_argnum != 0) {
+ // special case - saved func arg
+ fprintf(fout, " %s = %s;",
+ saved_arg_name(buf2, sizeof(buf2),
+ po->p_arggrp, po->p_argnum), buf1);
+ break;
+ }
+ else if (po->flags & OPF_RSAVE) {
+ 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;
+ break;
+
+ case OP_POP:
+ if (po->flags & OPF_RSAVE) {
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ fprintf(fout, " %s = s_%s;", buf1, buf1);
+ break;
+ }
+ else if (po->datap != NULL) {
+ // push/pop pair
+ tmp_op = po->datap;
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ fprintf(fout, " %s = %s;", buf1,
+ out_src_opr(buf2, sizeof(buf2),
+ tmp_op, &tmp_op->operand[0],
+ 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:
+ no_output = 1;
+ break;
+
+ // mmx
+ case OP_EMMS:
+ strcpy(g_comment, "(emms)");
+ break;
+
+ default:
+ no_output = 1;
+ ferr(po, "unhandled op type %d, flags %x\n",
+ po->op, po->flags);
+ break;
+ }
+
+ if (g_comment[0] != 0) {
+ char *p = g_comment;
+ while (my_isblank(*p))
+ p++;
+ fprintf(fout, " // %s", p);
+ g_comment[0] = 0;
+ no_output = 0;
+ }
+ if (!no_output)
+ fprintf(fout, "\n");
+
+ // some sanity checking
+ 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)
+ ferr(po, "missed flag calc, pfomask=%x\n", pfomask);
+
+ // 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, 0))
+ delayed_flag_op = NULL;
+ }
+
+ if (last_arith_dst != NULL && last_arith_dst != &po->operand[0]) {
+ if (is_opr_modified(last_arith_dst, po))
+ last_arith_dst = NULL;
+ }
+
+ label_pending = 0;
+ }
+
+ if (g_stack_fsz && !g_stack_frame_used)
+ fprintf(fout, " (void)sf;\n");
+
+ fprintf(fout, "}\n\n");
+
+ gen_x_cleanup(opcnt);
+}
+
+static void gen_x_cleanup(int opcnt)
+{
+ int i;
+
+ for (i = 0; i < opcnt; i++) {
+ struct label_ref *lr, *lr_del;
+
+ lr = g_label_refs[i].next;
+ while (lr != NULL) {
+ lr_del = lr;
+ lr = lr->next;
+ free(lr_del);
+ }
+ g_label_refs[i].i = -1;
+ g_label_refs[i].next = NULL;
+
+ if (ops[i].op == OP_CALL) {
+ if (ops[i].pp)
+ proto_release(ops[i].pp);
+ }
+ }
+ g_func_pp = NULL;
+}
+
+struct func_proto_dep;
+
+struct func_prototype {
+ char name[NAMELEN];
+ int id;
+ int argc_stack;
+ int regmask_dep;
+ int has_ret:3; // -1, 0, 1: unresolved, no, yes
+ unsigned int dep_resolved:1;
+ unsigned int is_stdcall:1;
+ struct func_proto_dep *dep_func;
+ int dep_func_cnt;
+ const struct parsed_proto *pp; // seed pp, if any
+};
+
+struct func_proto_dep {
+ char *name;
+ struct func_prototype *proto;
+ int regmask_live; // .. at the time of call
+ unsigned int ret_dep:1; // return from this is caller's return
+};
+
+static struct func_prototype *hg_fp;
+static int hg_fp_cnt;
+
+static struct scanned_var {
+ char name[NAMELEN];
+ enum opr_lenmod lmod;
+ unsigned int is_seeded:1;
+ unsigned int is_c_str:1;
+ const struct parsed_proto *pp; // seed pp, if any
+} *hg_vars;
+static int hg_var_cnt;
+
+static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
+ int count);
+
+static struct func_proto_dep *hg_fp_find_dep(struct func_prototype *fp,
+ const char *name)
+{
+ int i;
+
+ for (i = 0; i < fp->dep_func_cnt; i++)
+ if (IS(fp->dep_func[i].name, name))
+ return &fp->dep_func[i];
+
+ return NULL;
+}
+
+static void hg_fp_add_dep(struct func_prototype *fp, const char *name)
+{
+ // is it a dupe?
+ if (hg_fp_find_dep(fp, name))
+ return;
+
+ if ((fp->dep_func_cnt & 0xff) == 0) {
+ fp->dep_func = realloc(fp->dep_func,
+ sizeof(fp->dep_func[0]) * (fp->dep_func_cnt + 0x100));
+ my_assert_not(fp->dep_func, NULL);
+ memset(&fp->dep_func[fp->dep_func_cnt], 0,
+ sizeof(fp->dep_func[0]) * 0x100);
+ }
+ fp->dep_func[fp->dep_func_cnt].name = strdup(name);
+ fp->dep_func_cnt++;
+}
+
+static int hg_fp_cmp_name(const void *p1_, const void *p2_)
+{
+ const struct func_prototype *p1 = p1_, *p2 = p2_;
+ return strcmp(p1->name, p2->name);
+}
+
+#if 0
+static int hg_fp_cmp_id(const void *p1_, const void *p2_)
+{
+ const struct func_prototype *p1 = p1_, *p2 = p2_;
+ return p1->id - p2->id;
+}
+#endif
+
+// recursive register dep pass
+// - track saved regs (part 2)
+// - try to figure out arg-regs
+// - calculate reg deps
+static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits,
+ struct func_prototype *fp, int regmask_save, int regmask_dst,
+ int *regmask_dep, int *has_ret)
+{
+ struct func_proto_dep *dep;
+ struct parsed_op *po;
+ int from_caller = 0;
+ int depth;
+ int j, l;
+ int reg;
+ int ret;
+
+ for (; i < opcnt; i++)
+ {
+ if (cbits[i >> 3] & (1 << (i & 7)))
+ return;
+ cbits[i >> 3] |= (1 << (i & 7));
+
+ po = &ops[i];
+
+ if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
+ if (po->btj != NULL) {
+ // jumptable
+ for (j = 0; j < po->btj->count; j++) {
+ gen_hdr_dep_pass(po->btj->d[j].bt_i, opcnt, cbits, fp,
+ regmask_save, regmask_dst, regmask_dep, has_ret);
+ }
+ return;
+ }
+
+ if (po->bt_i < 0) {
+ ferr(po, "dead branch\n");
+ return;
+ }
+
+ if (po->flags & OPF_CJMP) {
+ gen_hdr_dep_pass(po->bt_i, opcnt, cbits, fp,
+ regmask_save, regmask_dst, regmask_dep, has_ret);
+ }
+ else {
+ i = po->bt_i - 1;
+ }
+ continue;
+ }
+
+ if (po->flags & OPF_FARG)
+ /* (just calculate register deps) */;
+ else if (po->op == OP_PUSH && po->operand[0].type == OPT_REG)
+ {
+ reg = po->operand[0].reg;
+ if (reg < 0)
+ ferr(po, "reg not set for push?\n");
+
+ if (po->flags & OPF_RSAVE) {
+ regmask_save |= 1 << reg;
+ continue;
+ }
+ if (po->flags & OPF_DONE)
+ continue;
+
+ depth = 0;
+ ret = scan_for_pop(i + 1, opcnt,
+ po->operand[0].name, i + opcnt * 2, 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);
+ continue;
+ }
+ }
+ else if (po->flags & OPF_RMD)
+ continue;
+ else if (po->op == OP_CALL) {
+ po->regmask_dst |= 1 << xAX;
+
+ dep = hg_fp_find_dep(fp, po->operand[0].name);
+ if (dep != NULL)
+ dep->regmask_live = regmask_save | regmask_dst;
+ }
+ else if (po->op == OP_RET) {
+ if (po->operand_cnt > 0) {
+ fp->is_stdcall = 1;
+ if (fp->argc_stack >= 0
+ && fp->argc_stack != po->operand[0].val / 4)
+ ferr(po, "ret mismatch? (%d)\n", fp->argc_stack * 4);
+ fp->argc_stack = po->operand[0].val / 4;
+ }
+ }
+
+ if (*has_ret != 0 && (po->flags & OPF_TAIL)) {
+ if (po->op == OP_CALL) {
+ j = i;
+ ret = 1;
+ }
+ else {
+ struct parsed_opr opr = { 0, };
+ opr.type = OPT_REG;
+ opr.reg = xAX;
+ j = -1;
+ from_caller = 0;
+ ret = resolve_origin(i, &opr, i + opcnt * 4, &j, &from_caller);
+ }
+
+ if (ret == -1 && from_caller) {
+ // unresolved eax - probably void func
+ *has_ret = 0;
+ }
+ else {
+ if (ops[j].op == OP_CALL) {
+ dep = hg_fp_find_dep(fp, po->operand[0].name);
+ if (dep != NULL)
+ dep->ret_dep = 1;
+ else
+ *has_ret = 1;
+ }
+ else
+ *has_ret = 1;
+ }
+ }
+
+ l = regmask_save | regmask_dst;
+ if (g_bp_frame && !(po->flags & OPF_EBP_S))
+ l |= 1 << xBP;
+
+ l = po->regmask_src & ~l;
+#if 0
+ if (l)
+ fnote(po, "dep |= %04x, dst %04x, save %04x (f %x)\n",
+ l, regmask_dst, regmask_save, po->flags);
+#endif
+ *regmask_dep |= l;
+ regmask_dst |= po->regmask_dst;
+
+ if (po->flags & OPF_TAIL)
+ return;
+ }
+}
+
+static void gen_hdr(const char *funcn, int opcnt)
+{
+ int save_arg_vars[MAX_ARG_GRP] = { 0, };
+ unsigned char cbits[MAX_OPS / 8];
+ const struct parsed_proto *pp_c;
+ struct parsed_proto *pp;
+ struct func_prototype *fp;
+ struct parsed_data *pd;
+ struct parsed_op *po;
+ const char *tmpname;
+ int regmask_dummy = 0;
+ int regmask_dep;
+ int max_bp_offset = 0;
+ int has_ret;
+ int i, j, l, ret;
+
+ if ((hg_fp_cnt & 0xff) == 0) {
+ hg_fp = realloc(hg_fp, sizeof(hg_fp[0]) * (hg_fp_cnt + 0x100));
+ my_assert_not(hg_fp, NULL);
+ memset(hg_fp + hg_fp_cnt, 0, sizeof(hg_fp[0]) * 0x100);
+ }
+
+ fp = &hg_fp[hg_fp_cnt];
+ snprintf(fp->name, sizeof(fp->name), "%s", funcn);
+ fp->id = hg_fp_cnt;
+ fp->argc_stack = -1;
+ hg_fp_cnt++;
+
+ // perhaps already in seed header?
+ fp->pp = proto_parse(g_fhdr, funcn, 1);
+ if (fp->pp != NULL) {
+ fp->argc_stack = fp->pp->argc_stack;
+ fp->is_stdcall = fp->pp->is_stdcall;
+ fp->regmask_dep = get_pp_arg_regmask(fp->pp);
+ fp->has_ret = !IS(fp->pp->ret_type.name, "void");
+ return;
+ }
+
+ g_bp_frame = g_sp_frame = g_stack_fsz = 0;
+ g_stack_frame_used = 0;
+
+ // pass1:
+ // - handle ebp/esp frame, remove ops related to it
+ scan_prologue_epilogue(opcnt);
+
+ // pass2:
+ // - collect calls
+ // - resolve all branches
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ po->bt_i = -1;
+ po->btj = NULL;
+
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+
+ if (po->op == OP_CALL) {
+ tmpname = opr_name(po, 0);
+ pp = NULL;
+ if (po->operand[0].type == OPT_LABEL) {
+ hg_fp_add_dep(fp, tmpname);
+
+ // perhaps a call to already known func?
+ pp_c = proto_parse(g_fhdr, tmpname, 1);
+ if (pp_c != NULL)
+ pp = proto_clone(pp_c);
+ }
+ 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 && pp->is_noreturn)
+ po->flags |= OPF_TAIL;
+
+ po->pp = pp;
+ continue;
+ }
+
+ if (!(po->flags & OPF_JMP) || po->op == OP_RET)
+ continue;
+
+ if (po->operand[0].type == OPT_REGMEM) {
+ pd = try_resolve_jumptab(i, opcnt);
+ if (pd == NULL)
+ goto tailcall;
+
+ po->btj = pd;
+ continue;
+ }
+
+ for (l = 0; l < opcnt; l++) {
+ if (g_labels[l] != NULL
+ && IS(po->operand[0].name, g_labels[l]))
+ {
+ add_label_ref(&g_label_refs[l], i);
+ po->bt_i = l;
+ break;
+ }
+ }
+
+ if (po->bt_i != -1 || (po->flags & OPF_RMD))
+ continue;
+
+ if (po->operand[0].type == OPT_LABEL)
+ // assume tail call
+ goto tailcall;
+
+ ferr(po, "unhandled branch\n");
+
+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
+ // - handle push <const>/pop pairs
+ for (i = 0; i < opcnt; i++)
+ {
+ if (g_labels[i] != NULL && g_label_refs[i].i == -1) {
+ free(g_labels[i]);
+ g_labels[i] = NULL;
+ }
+
+ po = &ops[i];
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+
+ if (po->op == OP_PUSH && po->operand[0].type == OPT_CONST)
+ scan_for_pop_const(i, opcnt);
+ }
+
+ // pass4:
+ // - process trivial calls
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+
+ if (po->op == OP_CALL)
+ {
+ pp = process_call_early(i, opcnt, &j);
+ if (pp != NULL) {
+ if (!(po->flags & OPF_ATAIL))
+ // since we know the args, try to collect them
+ if (collect_call_args_early(po, i, pp, ®mask_dummy) != 0)
+ pp = 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;
+ }
+
+ po->flags |= OPF_DONE;
+ }
+ }
+ }
+
+ // pass5:
+ // - track saved regs (simple)
+ // - process calls
+ for (i = 0; i < opcnt; i++)
+ {
+ po = &ops[i];
+ if (po->flags & (OPF_RMD|OPF_DONE))
+ continue;
+
+ if (po->op == OP_PUSH && po->operand[0].type == OPT_REG)
+ {
+ ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, 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);
+ }
+ }
+ else if (po->op == OP_CALL && !(po->flags & OPF_DONE))
+ {
+ pp = process_call(i, opcnt);
+
+ if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
+ // since we know the args, collect them
+ ret = collect_call_args(po, i, pp, ®mask_dummy, save_arg_vars,
+ i + opcnt * 1);
+ }
+ }
+ }
+
+ // pass6
+ memset(cbits, 0, sizeof(cbits));
+ regmask_dep = 0;
+ has_ret = -1;
+
+ gen_hdr_dep_pass(0, opcnt, cbits, fp, 0, 0, ®mask_dep, &has_ret);
+
+ // find unreachable code - must be fixed in IDA
+ for (i = 0; i < opcnt; i++)
+ {
+ if (cbits[i >> 3] & (1 << (i & 7)))
+ continue;
+
+ if (ops[i].op != OP_NOP)
+ ferr(&ops[i], "unreachable code\n");
+ }
+
+ if (has_ret == -1 && (regmask_dep & (1 << xAX)))
+ has_ret = 1;
+
+ for (i = 0; i < g_eqcnt; i++) {
+ if (g_eqs[i].offset > max_bp_offset && g_eqs[i].offset < 4*32)
+ max_bp_offset = g_eqs[i].offset;
+ }
+
+ if (fp->argc_stack < 0) {
+ max_bp_offset = (max_bp_offset + 3) & ~3;
+ fp->argc_stack = max_bp_offset / 4;
+ if ((g_ida_func_attr & IDAFA_BP_FRAME) && fp->argc_stack > 0)
+ fp->argc_stack--;
+ }
+
+ fp->regmask_dep = regmask_dep & ~(1 << xSP);
+ fp->has_ret = has_ret;
+#if 0
+ printf("// has_ret %d, regmask_dep %x\n",
+ fp->has_ret, fp->regmask_dep);
+ output_hdr_fp(stdout, fp, 1);
+ if (IS(funcn, "sub_100073FD")) exit(1);
+#endif
+
+ gen_x_cleanup(opcnt);
+}
+
+static void hg_fp_resolve_deps(struct func_prototype *fp)
+{
+ struct func_prototype fp_s;
+ int dep;
+ int i;
- delayed_flag_op = NULL;
- last_arith_dst = NULL;
- break;
+ // this thing is recursive, so mark first..
+ fp->dep_resolved = 1;
- 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;");
- }
- else if (g_func_pp->ret_type.is_ptr) {
- 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;");
+ for (i = 0; i < fp->dep_func_cnt; i++) {
+ strcpy(fp_s.name, fp->dep_func[i].name);
+ fp->dep_func[i].proto = bsearch(&fp_s, hg_fp, hg_fp_cnt,
+ sizeof(hg_fp[0]), hg_fp_cmp_name);
+ if (fp->dep_func[i].proto != NULL) {
+ if (!fp->dep_func[i].proto->dep_resolved)
+ hg_fp_resolve_deps(fp->dep_func[i].proto);
- last_arith_dst = NULL;
- delayed_flag_op = NULL;
- break;
+ dep = ~fp->dep_func[i].regmask_live
+ & fp->dep_func[i].proto->regmask_dep;
+ fp->regmask_dep |= dep;
+ // printf("dep %s %s |= %x\n", fp->name,
+ // fp->dep_func[i].name, dep);
- case OP_PUSH:
- out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
- if (po->p_argnum != 0) {
- // special case - saved func arg
- fprintf(fout, " s_a%d = %s;", po->p_argnum, buf1);
- break;
- }
- else if (po->flags & OPF_RSAVE) {
- 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;
- break;
+ if (fp->has_ret == -1)
+ fp->has_ret = fp->dep_func[i].proto->has_ret;
+ }
+ }
+}
- case OP_POP:
- if (po->flags & OPF_RSAVE) {
- out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
- fprintf(fout, " %s = s_%s;", buf1, buf1);
- break;
- }
- else if (po->datap != NULL) {
- // push/pop pair
- tmp_op = po->datap;
- out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
- fprintf(fout, " %s = %s;", buf1,
- out_src_opr(buf2, sizeof(buf2),
- tmp_op, &tmp_op->operand[0],
- 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]));
+static void output_hdr_fp(FILE *fout, const struct func_prototype *fp,
+ int count)
+{
+ const struct parsed_proto *pp;
+ char *p, namebuf[NAMELEN];
+ const char *name;
+ int regmask_dep;
+ int argc_stack;
+ int j, arg;
+
+ for (; count > 0; count--, fp++) {
+ if (fp->has_ret == -1)
+ fprintf(fout, "// ret unresolved\n");
+#if 0
+ fprintf(fout, "// dep:");
+ for (j = 0; j < fp->dep_func_cnt; j++) {
+ fprintf(fout, " %s/", fp->dep_func[j].name);
+ if (fp->dep_func[j].proto != NULL)
+ fprintf(fout, "%04x/%d", fp->dep_func[j].proto->regmask_dep,
+ fp->dep_func[j].proto->has_ret);
+ }
+ fprintf(fout, "\n");
+#endif
+
+ p = strchr(fp->name, '@');
+ if (p != NULL) {
+ memcpy(namebuf, fp->name, p - fp->name);
+ namebuf[p - fp->name] = 0;
+ name = namebuf;
+ }
+ else
+ name = fp->name;
+ if (name[0] == '_')
+ name++;
+
+ pp = proto_parse(g_fhdr, name, 1);
+ if (pp != NULL && pp->is_include)
+ continue;
+
+ if (fp->pp != NULL) {
+ // prefer fp for common style,
+ // only use output_pp if args are complex
+ for (j = 0; j < fp->pp->argc; j++) {
+ if (fp->pp->arg[j].fptr != NULL)
break;
- }
- else
- ferr(po, "stray pop encountered\n");
- break;
+ }
+ if (j != fp->pp->argc) {
+ output_pp(fout, fp->pp, OPP_ALIGN);
+ fprintf(fout, ";\n");
+ continue;
+ }
+ }
- case OP_NOP:
- no_output = 1;
- break;
+ regmask_dep = fp->regmask_dep;
+ argc_stack = fp->argc_stack;
- default:
- no_output = 1;
- ferr(po, "unhandled op type %d, flags %x\n",
- po->op, po->flags);
- break;
+ fprintf(fout, "%-5s", fp->pp ? fp->pp->ret_type.name :
+ (fp->has_ret ? "int" : "void"));
+ if (regmask_dep && (fp->is_stdcall || argc_stack == 0)
+ && (regmask_dep & ~((1 << xCX) | (1 << xDX))) == 0)
+ {
+ fprintf(fout, " __fastcall ");
+ if (!(regmask_dep & (1 << xDX)) && argc_stack == 0)
+ argc_stack = 1;
+ else
+ argc_stack += 2;
+ regmask_dep = 0;
}
+ else if (regmask_dep && !fp->is_stdcall) {
+ fprintf(fout, "/*__usercall*/ ");
+ }
+ else if (regmask_dep) {
+ fprintf(fout, "/*__userpurge*/ ");
+ }
+ else if (fp->is_stdcall)
+ fprintf(fout, " __stdcall ");
+ else
+ fprintf(fout, " __cdecl ");
- if (g_comment[0] != 0) {
- char *p = g_comment;
- while (my_isblank(*p))
- p++;
- fprintf(fout, " // %s", p);
- g_comment[0] = 0;
- no_output = 0;
+ fprintf(fout, "%s(", name);
+
+ arg = 0;
+ for (j = 0; j < xSP; j++) {
+ if (regmask_dep & (1 << j)) {
+ arg++;
+ if (arg != 1)
+ fprintf(fout, ", ");
+ if (fp->pp != NULL)
+ fprintf(fout, "%s", fp->pp->arg[arg - 1].type.name);
+ else
+ fprintf(fout, "int");
+ fprintf(fout, " a%d/*<%s>*/", arg, regs_r32[j]);
+ }
}
- if (!no_output)
- fprintf(fout, "\n");
- // some sanity checking
- 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");
+ for (j = 0; j < argc_stack; j++) {
+ arg++;
+ if (arg != 1)
+ fprintf(fout, ", ");
+ if (fp->pp != NULL) {
+ fprintf(fout, "%s", fp->pp->arg[arg - 1].type.name);
+ if (!fp->pp->arg[arg - 1].type.is_ptr)
+ fprintf(fout, " ");
+ }
+ else
+ fprintf(fout, "int ");
+ fprintf(fout, "a%d", arg);
}
- if ((po->flags & (OPF_REPZ|OPF_REPNZ))
- && po->op != OP_CMPS && po->op != OP_SCAS)
- ferr(po, "unexpected repz/repnz\n");
- if (pfomask != 0)
- ferr(po, "missed flag calc, pfomask=%x\n", pfomask);
+ fprintf(fout, ");\n");
+ }
+}
- // 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, 0))
- delayed_flag_op = NULL;
+static void output_hdr(FILE *fout)
+{
+ static const char *lmod_c_names[] = {
+ [OPLM_UNSPEC] = "???",
+ [OPLM_BYTE] = "uint8_t",
+ [OPLM_WORD] = "uint16_t",
+ [OPLM_DWORD] = "uint32_t",
+ [OPLM_QWORD] = "uint64_t",
+ };
+ const struct scanned_var *var;
+ char line[256] = { 0, };
+ int i;
+
+ // resolve deps
+ qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_name);
+ for (i = 0; i < hg_fp_cnt; i++)
+ hg_fp_resolve_deps(&hg_fp[i]);
+
+ // note: messes up .proto ptr, don't use
+ //qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_id);
+
+ // output variables
+ for (i = 0; i < hg_var_cnt; i++) {
+ var = &hg_vars[i];
+
+ if (var->pp != NULL && var->pp->is_fptr) {
+ fprintf(fout, "extern ");
+ output_pp(fout, var->pp, 0);
+ fprintf(fout, ";");
}
+ else if (var->is_c_str)
+ fprintf(fout, "extern %-8s %s[];", "char", var->name);
+ else
+ fprintf(fout, "extern %-8s %s;",
+ lmod_c_names[var->lmod], var->name);
- if (last_arith_dst != NULL && last_arith_dst != &po->operand[0]) {
- if (is_opr_modified(last_arith_dst, po))
- last_arith_dst = NULL;
+ if (var->is_seeded)
+ fprintf(fout, " // seeded");
+ fprintf(fout, "\n");
+ }
+
+ fprintf(fout, "\n");
+
+ // output function prototypes
+ output_hdr_fp(fout, hg_fp, hg_fp_cnt);
+
+ // include passthrough
+ fprintf(fout, "\n// for translate\n");
+
+ rewind(g_fhdr);
+ while (fgets(line, sizeof(line), g_fhdr)) {
+ if (IS_START(line, "//#"))
+ 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');
+ }
- label_pending = 0;
+ return ret;
+}
+
+// '=' needs special treatment
+// also ' quote
+static char *next_word_s(char *w, size_t wsize, char *s)
+{
+ size_t i;
+
+ s = sskip(s);
+
+ i = 0;
+ if (*s == '\'') {
+ w[0] = s[0];
+ for (i = 1; i < wsize - 1; i++) {
+ if (s[i] == 0) {
+ printf("warning: missing closing quote: \"%s\"\n", s);
+ break;
+ }
+ if (s[i] == '\'')
+ break;
+ w[i] = s[i];
+ }
}
- if (g_stack_fsz && !g_stack_frame_used)
- fprintf(fout, " (void)sf;\n");
+ for (; i < wsize - 1; i++) {
+ if (s[i] == 0 || my_isblank(s[i]) || (s[i] == '=' && i > 0))
+ break;
+ w[i] = s[i];
+ }
+ w[i] = 0;
- fprintf(fout, "}\n\n");
+ if (s[i] != 0 && !my_isblank(s[i]) && s[i] != '=')
+ printf("warning: '%s' truncated\n", w);
- // cleanup
- for (i = 0; i < opcnt; i++) {
- struct label_ref *lr, *lr_del;
+ return s + i;
+}
- lr = g_label_refs[i].next;
- while (lr != NULL) {
- lr_del = lr;
- lr = lr->next;
- free(lr_del);
+static void scan_variables(FILE *fasm)
+{
+ struct scanned_var *var;
+ char line[256] = { 0, };
+ char words[3][256];
+ char *p = NULL;
+ int wordc;
+ int l;
+
+ while (!feof(fasm))
+ {
+ // skip to next data section
+ while (my_fgets(line, sizeof(line), fasm))
+ {
+ asmln++;
+
+ p = sskip(line);
+ if (*p == 0 || *p == ';')
+ continue;
+
+ p = sskip(next_word_s(words[0], sizeof(words[0]), p));
+ if (*p == 0 || *p == ';')
+ continue;
+
+ if (*p != 's' || !IS_START(p, "segment para public"))
+ continue;
+
+ break;
}
- g_label_refs[i].i = -1;
- g_label_refs[i].next = NULL;
- if (ops[i].op == OP_CALL) {
- if (ops[i].pp)
- proto_release(ops[i].pp);
+ if (p == NULL || !IS_START(p, "segment para public"))
+ break;
+ p = sskip(p + 19);
+
+ if (!IS_START(p, "'DATA'"))
+ continue;
+
+ // now process it
+ while (my_fgets(line, sizeof(line), fasm))
+ {
+ asmln++;
+
+ p = line;
+ if (my_isblank(*p))
+ continue;
+
+ p = sskip(p);
+ if (*p == 0 || *p == ';')
+ continue;
+
+ for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) {
+ words[wordc][0] = 0;
+ p = sskip(next_word_s(words[wordc], sizeof(words[0]), p));
+ if (*p == 0 || *p == ';') {
+ wordc++;
+ break;
+ }
+ }
+
+ if (wordc == 2 && IS(words[1], "ends"))
+ break;
+ if (wordc < 2)
+ continue;
+
+ if ((hg_var_cnt & 0xff) == 0) {
+ hg_vars = realloc(hg_vars, sizeof(hg_vars[0])
+ * (hg_var_cnt + 0x100));
+ my_assert_not(hg_vars, NULL);
+ memset(hg_vars + hg_var_cnt, 0, sizeof(hg_vars[0]) * 0x100);
+ }
+
+ var = &hg_vars[hg_var_cnt++];
+ snprintf(var->name, sizeof(var->name), "%s", words[0]);
+
+ // maybe already in seed header?
+ var->pp = proto_parse(g_fhdr, var->name, 1);
+ if (var->pp != NULL) {
+ if (var->pp->is_fptr) {
+ var->lmod = OPLM_DWORD;
+ //var->is_ptr = 1;
+ }
+ else if (var->pp->is_func)
+ aerr("func?\n");
+ else if (!guess_lmod_from_c_type(&var->lmod, &var->pp->type))
+ aerr("unhandled C type '%s' for '%s'\n",
+ var->pp->type.name, var->name);
+
+ var->is_seeded = 1;
+ continue;
+ }
+
+ if (IS(words[1], "dd"))
+ var->lmod = OPLM_DWORD;
+ else if (IS(words[1], "dw"))
+ var->lmod = OPLM_WORD;
+ else if (IS(words[1], "db")) {
+ var->lmod = OPLM_BYTE;
+ if (wordc >= 3 && (l = strlen(words[2])) > 4) {
+ if (words[2][0] == '\'' && IS(words[2] + l - 2, ",0"))
+ var->is_c_str = 1;
+ }
+ }
+ else if (IS(words[1], "dq"))
+ var->lmod = OPLM_QWORD;
+ //else if (IS(words[1], "dt"))
+ else
+ aerr("type '%s' not known\n", words[1]);
}
}
- g_func_pp = NULL;
+
+ rewind(fasm);
+ asmln = 0;
}
static void set_label(int i, const char *name)
if (p != NULL)
len = p - name;
- if (len > sizeof(g_labels[0]) - 1)
- aerr("label '%s' too long: %d\n", name, len);
- if (g_labels[i][0] != 0 && !IS_START(g_labels[i], "algn_"))
+ if (g_labels[i] != NULL && !IS_START(g_labels[i], "algn_"))
aerr("dupe label '%s' vs '%s'?\n", name, g_labels[i]);
+ g_labels[i] = realloc(g_labels[i], len + 1);
+ my_assert_not(g_labels[i], NULL);
memcpy(g_labels[i], name, len);
g_labels[i][len] = 0;
}
-// '=' needs special treatment..
-static char *next_word_s(char *w, size_t wsize, char *s)
-{
- size_t i;
-
- s = sskip(s);
-
- for (i = 0; i < wsize - 1; i++) {
- if (s[i] == 0 || my_isblank(s[i]) || (s[i] == '=' && i > 0))
- break;
- w[i] = s[i];
- }
- w[i] = 0;
-
- if (s[i] != 0 && !my_isblank(s[i]) && s[i] != '=')
- printf("warning: '%s' truncated\n", w);
-
- return s + i;
-}
-
struct chunk_item {
char *name;
long fptr;
oldpos = ftell(fasm);
oldasmln = asmln;
- while (fgets(line, sizeof(line), fasm))
+ while (my_fgets(line, sizeof(line), fasm))
{
wordc = 0;
asmln++;
g_allow_regfunc = 1;
else if (IS(argv[arg], "-m"))
multi_seg = 1;
+ else if (IS(argv[arg], "-hdr"))
+ g_header_mode = g_quiet_pp = g_allow_regfunc = 1;
else
break;
}
if (argc < arg + 3) {
- printf("usage:\n%s [-v] [-rf] [-m] <.c> <.asm> <hdrf> [rlist]*\n",
- argv[0]);
+ printf("usage:\n%s [-v] [-rf] [-m] <.c> <.asm> <hdr.h> [rlist]*\n"
+ "%s -hdr <out.h> <.asm> <seed.h> [rlist]*\n"
+ "options:\n"
+ " -hdr - header generation mode\n"
+ " -rf - allow unannotated indirect calls\n"
+ " -m - allow multiple .text sections\n"
+ "[rlist] is a file with function names to skip,"
+ " one per line\n",
+ argv[0], argv[0]);
return 1;
}
frlist = fopen(argv[arg], "r");
my_assert_not(frlist, NULL);
- while (fgets(line, sizeof(line), frlist)) {
+ while (my_fgets(line, sizeof(line), frlist)) {
p = sskip(line);
if (*p == 0 || *p == ';')
continue;
g_label_refs[i].next = NULL;
}
- while (fgets(line, sizeof(line), fasm))
+ if (g_header_mode)
+ scan_variables(fasm);
+
+ while (my_fgets(line, sizeof(line), fasm))
{
wordc = 0;
asmln++;
do_pending_endp:
// do delayed endp processing to collect switch jumptables
if (pending_endp) {
- if (in_func && !skip_func && !end && wordc >= 2
+ if (in_func && !g_skip_func && !end && wordc >= 2
&& ((words[0][0] == 'd' && words[0][2] == 0)
|| (words[1][0] == 'd' && words[1][2] == 0)))
{
continue;
}
- if (in_func && !skip_func)
- gen_func(fout, g_fhdr, g_func, pi);
+ if (in_func && !g_skip_func) {
+ if (g_header_mode)
+ gen_hdr(g_func, pi);
+ else
+ gen_func(fout, g_fhdr, g_func, pi);
+ }
pending_endp = 0;
in_func = 0;
g_ida_func_attr = 0;
skip_warned = 0;
- skip_func = 0;
+ g_skip_func = 0;
g_func[0] = 0;
func_chunks_used = 0;
func_chunk_i = -1;
if (pi != 0) {
memset(&ops, 0, pi * sizeof(ops[0]));
- memset(g_labels, 0, pi * sizeof(g_labels[0]));
+ clear_labels(pi);
pi = 0;
}
g_eqcnt = 0;
words[0], g_func);
p = words[0];
if (bsearch(&p, rlist, rlist_len, sizeof(rlist[0]), cmpstringp))
- skip_func = 1;
+ g_skip_func = 1;
strcpy(g_func, words[0]);
set_label(0, words[0]);
in_func = 1;
&& ops[0].op == OP_JMP && ops[0].operand[0].had_ds)
{
// import jump
- skip_func = 1;
+ g_skip_func = 1;
}
- if (!skip_func && func_chunks_used) {
+ if (!g_skip_func && func_chunks_used) {
// start processing chunks
struct chunk_item *ci, key = { g_func, 0 };
}
// scan for next text segment
- while (fgets(line, sizeof(line), fasm)) {
+ while (my_fgets(line, sizeof(line), fasm)) {
asmln++;
p = sskip(line);
if (*p == 0 || *p == ';')
continue;
}
- if (!in_func || skip_func) {
- if (!skip_warned && !skip_func && g_labels[pi][0] != 0) {
+ if (!in_func || g_skip_func) {
+ if (!skip_warned && !g_skip_func && g_labels[pi] != NULL) {
if (verbose)
anote("skipping from '%s'\n", g_labels[pi]);
skip_warned = 1;
}
- g_labels[pi][0] = 0;
+ free(g_labels[pi]);
+ g_labels[pi] = NULL;
continue;
}
g_eqs[g_eqcnt].lmod = OPLM_WORD;
else if (IS(words[2], "byte"))
g_eqs[g_eqcnt].lmod = OPLM_BYTE;
+ else if (IS(words[2], "qword"))
+ g_eqs[g_eqcnt].lmod = OPLM_QWORD;
else
aerr("bad lmod: '%s'\n", words[2]);
pi++;
}
+ if (g_header_mode)
+ output_hdr(fout);
+
fclose(fout);
fclose(fasm);
fclose(g_fhdr);