radare2/libr/egg/emit_x86.c
2022-08-18 18:35:22 +02:00

576 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* pancake // nopcode.org 2010-2013 -- emit module for rcc */
#include <r_egg.h>
#include <r_types.h>
/* hardcoded */
#define attsyntax 0
#ifdef ARCH_X86_64
# define EMIT_NAME emit_x64
# define R_ARCH "x64"
# define R_SZ 8
# define R_SP "rsp"
# define R_BP "rbp"
# define R_AX "rax"
# define SYSCALL_ATT "syscall"
# define SYSCALL_INTEL "syscall"
# define R_REG_AR_OFF 1
static char *regs[] = { "rax", "rdi", "rsi", "rdx", "r10", "r8", "r9" };
#else
# define EMIT_NAME emit_x86
# define R_ARCH "x86"
# define R_SZ 4
# define R_SP "esp"
# define R_BP "ebp"
# define R_AX "eax"
# define SYSCALL_ATT "int $0x80"
# define SYSCALL_INTEL "int 0x80"
# define R_REG_AR_OFF 0
static char *regs[] = { "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp" };
#endif
# define R_NGP (sizeof (regs)/sizeof (char *))
static void emit_init(REgg *egg) {
// TODO: add 'andb rsp, 0xf0'
if (attsyntax) {
r_egg_printf (egg, "mov %%" R_SP ", %%" R_BP "\n");
} else {
r_egg_printf (egg, "mov " R_BP ", " R_SP "\n");
}
}
static char *emit_syscall(REgg *egg, int nargs) {
char p[512];
if (attsyntax) {
return strdup (": mov $`.arg`, %" R_AX "\n: " SYSCALL_ATT "\n");
}
switch (egg->os) {
case R_EGG_OS_LINUX:
strcpy (p, "\n : mov "R_AX", `.arg`\n : "SYSCALL_INTEL "\n");
break;
case R_EGG_OS_OSX:
case R_EGG_OS_MACOS:
case R_EGG_OS_DARWIN:
#if ARCH_X86_64
snprintf (p, sizeof (p), "\n"
" : mov rax, `.arg`\n"
" : syscall\n");
#else
snprintf (p, sizeof (p), "\n"
" : mov eax, `.arg`\n"
" : push eax\n"
" : int 0x80\n"
" : add esp, %d\n",
4); //(nargs+2)*(egg->bits/8));
#endif
break;
default:
return NULL;
}
return strdup (p);
}
static void emit_frame(REgg *egg, int sz) {
if (sz < 1) {
return;
}
if (attsyntax) {
r_egg_printf (egg,
" push %%"R_BP"\n"
" mov %%"R_SP", %%"R_BP"\n"
" sub $%d, %%"R_SP"\n", sz);
} else {
r_egg_printf (egg,
" push " R_BP "\n"
" mov " R_BP ", " R_SP "\n"
" sub " R_SP ", %d\n",
sz);
}
}
static void emit_frame_end(REgg *egg, int sz, int ctx) {
if (sz>0) {
if (attsyntax) {
r_egg_printf (egg, " add $%d, %%"R_SP"\n", sz);
r_egg_printf (egg, " pop %%"R_BP"\n");
} else {
r_egg_printf (egg, " add "R_SP", %d\n", sz);
r_egg_printf (egg, " pop "R_BP"\n");
}
}
if (ctx > 0) {
r_egg_printf (egg, " ret\n");
}
}
static void emit_comment(REgg *egg, const char *fmt, ...) {
va_list ap;
char buf[1024];
va_start (ap, fmt);
vsnprintf (buf, sizeof (buf), fmt, ap);
if (attsyntax) {
r_egg_printf (egg, " /* %s */\n", buf);
} else {
r_egg_printf (egg, "# %s\n", buf);
}
va_end (ap);
}
static void emit_equ(REgg *egg, const char *key, const char *value) {
r_egg_printf (egg, ".equ %s,%s\n", key, value);
}
static const char *getreg(int i) {
if (i < 0 || i >= R_NGP) {
return NULL;
}
return regs[i];
}
static void emit_syscall_args(REgg *egg, int nargs) {
int j, k;
for (j = 0; j < nargs; j++) {
k = j * R_SZ;
const char *reg = getreg (j + 1);
if (!reg) {
R_LOG_ERROR ("Cannot find gpr %d", j + 1);
break;
}
if (attsyntax) {
r_egg_printf (egg, " mov %d(%%"R_SP"), %%%s\n", k, reg);
} else {
if (k > 0) {
r_egg_printf (egg, " mov %s, ["R_SP"+%d]\n", reg, k);
} else if (k < 0) {
r_egg_printf (egg, " mov %s, ["R_SP"%d]\n", reg, k);
} else {
r_egg_printf (egg, " mov %s, ["R_SP"]\n", reg);
}
}
}
}
static void emit_string(REgg *egg, const char *dstvar, const char *str, int j) {
char *p, str2[64];
int i, oj = j;
int len = strlen (str);
char *s = calloc (1, len + 8);
if (!s) {
return;
}
memcpy (s, str, len);
memset (s + len, 0, 4);
/* XXX: Hack: Adjust offset in R_BP correctly for 64b addresses */
#define BPOFF (R_SZ-4)
#define M32(x) (unsigned int)((x) & 0xffffffff)
/* XXX: Assumes sizeof (ut32) == 4 */
for (i = 4; i <= oj; i += 4) {
/* XXX endian issues (non-portable asm) */
ut32 *n = (ut32 *)(s + i - 4);
p = r_egg_mkvar (egg, str2, dstvar, i+BPOFF);
if (attsyntax) {
r_egg_printf (egg, " movl $0x%x, %s\n", M32 (*n), p);
} else {
r_egg_printf (egg, " mov dword %s, 0x%x\n", p, M32 (*n));
}
free (p);
j -= 4;
}
#undef M32
/* zero */
p = r_egg_mkvar (egg, str2, dstvar, i+BPOFF);
if (attsyntax) {
r_egg_printf (egg, " movl $0, %s\n", p);
} else {
r_egg_printf (egg, " mov dword %s, 0\n", p);
}
free (p);
/* store pointer */
p = r_egg_mkvar (egg, str2, dstvar, j+4+BPOFF);
if (attsyntax) {
r_egg_printf (egg, " lea %s, %%" R_AX "\n", p);
} else {
r_egg_printf (egg, " lea " R_AX ", %s\n", p);
}
free (p);
p = r_egg_mkvar (egg, str2, dstvar, 0);
if (attsyntax) {
r_egg_printf (egg, " mov %%" R_AX ", %s\n", p);
} else {
r_egg_printf (egg, " mov %s, " R_AX "\n", p);
}
free (p);
#undef BPOFF
#if 0
char *p, str2[64];
int i, oj = j;
for (i = 0; i < oj; i += 4) {
/* XXX endian and 32/64bit issues */
int *n = (int *)(str+i);
p = r_egg_mkvar (egg, str2, dstvar, j);
if (attsyntax) r_egg_printf (egg, " movl $0x%x, %s\n", *n, p);
else r_egg_printf (egg, " mov %s, 0x%x\n", p, *n);
j -= 4;
}
p = r_egg_mkvar (egg, str2, dstvar, oj);
if (attsyntax) r_egg_printf (egg, " lea %s, %%"R_AX"\n", p);
else r_egg_printf (egg, " lea "R_AX", %s\n", p);
p = r_egg_mkvar (egg, str2, dstvar, 0);
if (attsyntax) r_egg_printf (egg, " mov %%"R_AX", %s\n", p);
else r_egg_printf (egg, " mov %s, "R_AX"\n", p);
#endif
free (s);
}
static void emit_call(REgg *egg, const char *str, int atr) {
if (atr) {
if (attsyntax) {
r_egg_printf (egg, " call *%s\n", str);
} else {
r_egg_printf (egg, " call [%s]\n", str);
}
} else {
r_egg_printf (egg, " call %s\n", str);
}
}
static void emit_jmp(REgg *egg, const char *str, int atr) {
if (str) {
if (atr) {
if (attsyntax) {
r_egg_printf (egg, " jmp *%s\n", str);
} else {
r_egg_printf (egg, " jmp [%s]\n", str);
}
} else {
r_egg_printf (egg, " jmp %s\n", str);
}
} else {
eprintf ("Jump without destination\n");
}
}
static void emit_arg(REgg *egg, int xs, int num, const char *str) {
int d = atoi (str);
if (!attsyntax && (*str == '$')) {
str = str + 1;
}
switch (xs) {
case 0:
#ifdef ARCH_X86_64
/* push imm64 instruction not exist, it´s translated to:
mov rax, 0x0102030405060708
push rax
*/
if (attsyntax) {
r_egg_printf (egg, " mov %s, %%"R_AX "\n", str);
r_egg_printf (egg, " push %%"R_AX "\n");
} else {
r_egg_printf (egg, " mov "R_AX ", %s\n", str);
r_egg_printf (egg, " push "R_AX "\n");
}
#else
r_egg_printf (egg, " push %s\n", str);
#endif
break;
case '*':
if (attsyntax) {
r_egg_printf (egg, " push (%s)\n", str);
} else {
r_egg_printf (egg, " push [%s]\n", str);
}
break;
case '&':
if (attsyntax) {
if (d != 0) {
r_egg_printf (egg, " addl $%d, %%" R_BP "\n", d);
}
r_egg_printf (egg, " pushl %%"R_BP"\n");
if (d != 0) {
r_egg_printf (egg, " subl $%d, %%" R_BP "\n", d);
}
} else {
if (d != 0) {
r_egg_printf (egg, " add " R_BP ", %d\n", d);
}
r_egg_printf (egg, " push "R_BP"\n");
if (d != 0) {
r_egg_printf (egg, " sub " R_BP ", %d\n", d);
}
}
break;
}
}
static void emit_get_result(REgg *egg, const char *ocn) {
if (attsyntax) {
r_egg_printf (egg, " mov %%" R_AX ", %s\n", ocn);
} else {
r_egg_printf (egg, " mov %s, " R_AX "\n", ocn);
}
}
static void emit_restore_stack(REgg *egg, int size) {
if (attsyntax) {
r_egg_printf (egg, " add $%d, %%" R_SP " /* args */\n", size);
} else {
r_egg_printf (egg, " add " R_SP ", %d\n", size);
}
}
static void emit_get_while_end(REgg *egg, char *str, const char *ctxpush, const char *label) {
sprintf (str, " push %s\n jmp %s\n", ctxpush, label);
}
static void emit_while_end(REgg *egg, const char *labelback) {
#if 0
if (attsyntax) {
r_egg_printf (egg, " pop %%"R_AX"\n");
r_egg_printf (egg, " cmp $0, %%"R_AX"\n"); // XXX MUST SUPPORT != 0 COMPARE HERE
r_egg_printf (egg, " jnz %s\n", labelback);
} else {
#endif
r_egg_printf (egg, " pop "R_AX"\n");
r_egg_printf (egg, " test "R_AX", "R_AX"\n"); // XXX MUST SUPPORT != 0 COMPARE HERE
r_egg_printf (egg, " jnz %s\n", labelback);
// }
}
// XXX: this is wrong
static void emit_get_var(REgg *egg, int type, char *out, int idx) {
switch (type) {
case 0: /* variable */
if (idx > 0) {
sprintf (out, "[" R_BP "+%d]", idx);
} else if (idx < 0) {
sprintf (out, "[" R_BP "%d]", idx);
} else {
strcpy (out, "[" R_BP "]");
}
break;
case 1: /* argument */
// OMG WE CAN'T stuff found in relative address in stack in the stack
R_LOG_WARN ("Using stack vars in naked functions");
idx = 8; // HACK to make arg0, arg4, ... work
if (idx > 0) {
sprintf (out, "[" R_SP "+%d]", idx);
} else if (idx < 0) {
sprintf (out, "[" R_SP "%d]", idx);
} else {
strcpy (out, "[" R_SP "]");
}
break;
case 2:
if (idx > 0) {
sprintf (out, "[" R_BP "+%d]", idx);
} else if (idx < 0) {
sprintf (out, "[" R_BP "%d]", idx);
} else {
strcpy (out, "[" R_BP "]");
}
break;
}
}
static void emit_trap(REgg *egg) {
r_egg_printf (egg, " int3\n");
}
static void emit_load_ptr(REgg *egg, const char *dst) {
int d = atoi (dst);
if (d == 0) { // hack to handle stackvarptrz
char *p = strchr (dst, '+');
if (p) {
d = atoi (p + 1);
}
}
//eprintf ("emit_load_ptr: HACK\n");
// XXX: 32/64bit care
//r_egg_printf (egg, "# DELTA IS (%s)\n", dst);
if (attsyntax) {
r_egg_printf (egg, " leal %d(%%"R_BP"), %%"R_AX"\n", d);
} else {
r_egg_printf (egg, " lea "R_AX", ["R_BP"+%d]\n", d);
}
//r_egg_printf (egg, " movl %%"R_BP", %%"R_AX"\n");
//r_egg_printf (egg, " addl $%d, %%"R_AX"\n", d);
}
static void emit_branch(REgg *egg, char *b, char *g, char *e, char *n, int sz, const char *dst) {
char *p, str[64];
char *arg = NULL;
char *op = "jz";
int signed_value = 1; // XXX: add support for signed/unsigned variables
/* NOTE that jb/ja are inverted to fit cmp opcode */
if (b) {
*b = '\0';
if (signed_value) {
op = e? "jge": "jg";
} else {
op = e? "jae": "ja";
}
arg = b+1;
} else if (g) {
*g = '\0';
if (signed_value) {
op = e? "jle": "jl";
} else {
op = e? "jbe": "jb";
}
arg = g + 1;
}
if (!arg) {
if (e) {
arg = e+1;
op = "jne";
} else {
arg = attsyntax? "$0": "0";
if (n) {
op = "jnz";
} else {
op = "jz";
}
}
}
if (*arg == '=') {
arg++; /* for <=, >=, ... */
}
p = r_egg_mkvar (egg, str, arg, 0);
if (attsyntax) {
r_egg_printf (egg, " pop %%"R_AX"\n"); /* TODO: add support for more than one arg get arg0 */
r_egg_printf (egg, " cmp%c %s, %%"R_AX"\n", sz, p);
} else {
r_egg_printf (egg, " pop "R_AX"\n"); /* TODO: add support for more than one arg get arg0 */
r_egg_printf (egg, " cmp "R_AX", %s\n", p);
}
// if (context>0)
free (p);
r_egg_printf (egg, " %s %s\n", op, dst);
}
static void emit_load(REgg *egg, const char *dst, int sz) {
if (attsyntax) {
switch (sz) {
case 'l':
r_egg_printf (egg, " movl %s, %%"R_AX"\n", dst);
r_egg_printf (egg, " movl (%%"R_AX"), %%"R_AX"\n");
break;
case 'b':
r_egg_printf (egg, " movl %s, %%"R_AX"\n", dst);
r_egg_printf (egg, " movzb (%%"R_AX"), %%"R_AX"\n");
break;
default:
// TODO: unhandled?!?
r_egg_printf (egg, " mov%c %s, %%"R_AX"\n", sz, dst);
r_egg_printf (egg, " mov%c (%%"R_AX"), %%"R_AX"\n", sz);
}
} else {
switch (sz) {
case 'l':
r_egg_printf (egg, " mov "R_AX", %s\n", dst);
r_egg_printf (egg, " mov "R_AX", ["R_AX"]\n");
break;
case 'b':
r_egg_printf (egg, " mov "R_AX", %s\n", dst);
r_egg_printf (egg, " movz "R_AX", ["R_AX"]\n");
break;
default:
// TODO: unhandled?!?
r_egg_printf (egg, " mov "R_AX", %s\n", dst);
r_egg_printf (egg, " mov "R_AX", ["R_AX"]\n");
}
}
}
static void emit_mathop(REgg *egg, int ch, int vs, int type, const char *eq, const char *p) {
char *op;
switch(ch) {
case '^': op = "xor"; break;
case '&': op = "and"; break;
case '|': op = "or"; break;
case '-': op = "sub"; break;
case '+': op = "add"; break;
case '*': op = "mul"; break;
case '/': op = "div"; break;
default: op = "mov"; break;
}
if (attsyntax) {
if (!eq) {
eq = "%" R_AX;
}
if (!p) {
p = "%" R_AX;
}
r_egg_printf (egg, " %s%c %c%s, %s\n", op, vs, type, eq, p);
} else {
if (!eq) {
eq = R_AX;
}
if (!p) {
p = R_AX;
}
// TODO:
#if 0
eprintf ("TYPE = %c\n", type);
eprintf (" %s%c %c%s, %s\n", op, vs, type, eq, p);
eprintf (" %s %s, [%s]\n", op, p, eq);
#endif
if (type == '*') {
r_egg_printf (egg, " %s %s, [%s]\n", op, p, eq);
} else {
r_egg_printf (egg, " %s %s, %s\n", op, p, eq);
}
}
}
static const char* emit_regs(REgg *egg, int idx) {
return regs[idx%R_NGP];
}
static void emit_get_ar(REgg *egg, char *out, int idx) {
const char *reg = emit_regs (egg, R_REG_AR_OFF + idx);
if (reg) {
strcpy (out, reg);
}
}
REggEmit EMIT_NAME = {
.retvar = R_AX,
.arch = R_ARCH,
.size = R_SZ,
.init = emit_init,
.jmp = emit_jmp,
.call = emit_call,
.equ = emit_equ,
.regs = emit_regs,
//.sc = emit_sc,
.trap = emit_trap,
.frame = emit_frame,
.frame_end = emit_frame_end,
.comment = emit_comment,
.push_arg = emit_arg,
.restore_stack = emit_restore_stack,
.get_result = emit_get_result,
.syscall_args = emit_syscall_args,
.set_string = emit_string,
.get_ar = emit_get_ar,
.get_var = emit_get_var,
.while_end = emit_while_end,
.get_while_end = emit_get_while_end,
.branch = emit_branch,
.load = emit_load,
.load_ptr = emit_load_ptr,
.mathop = emit_mathop,
.syscall = emit_syscall,
};