/* GPLv3 -- Copyright 2009-2010 -- pancake /at/ nopcode.org */ #include #include #include #include #include #include #include "rarc2.h" static int parsechar(char c); static struct { char *name; char *body; //int fastcall; /* TODO: NOT YET USED */ } inlines[MAX]; static int ninlines = 0; static struct { char *name; char *arg; //int sysnum; /* TODO: NOT YET USED */ } syscalls[MAX]; static int nsyscalls = 0; static char *syscallbody = NULL; static int commentmode = 0; static int varsize = 'l'; static int varxs = 0; static int lastctxdelta = 0; static int nargs = 0; static int docall = 1; /* do call or inline it ? */ // BOOL static int nfunctions = 0; static int nbrackets = 0; static int slurpin = 0; static int slurp = 0; static int line = 1; static char elem[1024]; int attsyntax = 0; static int elem_n = 0; static int context = 0; static int showmain = 0; static char *callname = NULL; static char *endframe = NULL; static char *ctxpush[32]; static char *file = "stdin"; static char *dstvar = NULL; static char *dstval = NULL; static int ndstval = 0; static int skipline = 0; // BOOL static int quoteline = 0; static int quotelinevar = 0; static int stackframe = 0; static int stackfixed = 0; static int oc = '\n'; static int mode = NORMAL; extern struct emit_t emit_x86; extern struct emit_t emit_x64; extern struct emit_t emit_arm; struct emit_t *emits[4] = { &emit_x86, &emit_x64, &emit_arm, NULL }; #if __arm__ static struct emit_t *emit = &emit_arm; #elif __i386__ static struct emit_t *emit = &emit_x86; #else static struct emit_t *emit = &emit_x64; #endif #if SYNTAX_ATT #define FRAME_FMT ".LC%d_%d_frame%d" #define FRAME_END_FMT ".LC%d_%d_end_frame%d" #else #define FRAME_FMT "__%d_%d_frame%d" #define FRAME_END_FMT "__%d_%d_end_frame%d" #endif static char *get_end_frame_label() { static char label[128]; /* THIS IS GAS_ONLY */ snprintf (label, sizeof(label), FRAME_END_FMT, nfunctions, nbrackets, context-1); return label; } static char *get_frame_label(int type) { static char label[128]; int nf = nfunctions; int nb = nbrackets; int ct = context; /* TODO: this type hack to substruct nb and ctx looks weird */ if (type == 1) nb--; else if (type == 2) ct--; /* THIS IS GAS_ONLY */ snprintf (label, sizeof (label), FRAME_FMT, nf, nb, ct); return label; } static void rcc_pushstr(char *str, int filter) { int dotrim = 1; int i, j, len; emit->comment ("encode %s string (%s) (%s)", filter?"filtered":"unfiltered", str, callname); if (filter) for (i=0; str[i]; i++) { if (str[i]=='\\') { switch (str[i+1]) { case 't': str[i]='\t'; break; case 'n': str[i]='\n'; break; case 'e': str[i]='\x1b'; break; default: dotrim = 0; break; } if (dotrim) memmove (str+i+1, str+i+2, strlen (str+i+2)); } } len = strlen (str); j = (len-len%emit->size)+emit->size; emit->set_string (dstvar, str, j); FREE (dstvar); } char *mk_var(char *out, const char *_str, int delta) { int i, idx, len, qi; char *str, foo[32], *q, *ret; delta += stackfixed; // XXX can be problematic if (_str == NULL) return NULL; /* fix segfault, but not badparsing */ /* XXX memory leak */ ret = str = strdup (_str); while (*str==' ') str++; /* skip spaces ...also tabs isspace()? */ //if (num || str[0]=='0') { sprintf(out, "$%d", num); ret = out; } if ( (q = strchr (str, ':')) ) { *q = '\0'; qi = atoi (q+1); varsize = (qi==1)? 'b':'l'; } else varsize='l'; if (*str=='*'||*str=='&') { varxs = *str; str++; } else varxs = 0; if (str[0]=='.') { ret = out; idx = atoi (str+4) + delta + emit->size; if (!memcmp (str+1, "fix", 3)) { emit->get_var (0, out, idx-stackfixed); //sprintf(out, "%d(%%"R_BP")", -(atoi(str+4)+delta+R_SZ-stackfixed)); } else if (!memcmp (str+1, "var", 3)) { emit->get_var (0, out, idx); //sprintf(out, "%d(%%"R_BP")", -(atoi(str+4)+delta+R_SZ)); } else if (!memcmp(str+1, "arg", 3)) { if (str[4]) { if (stackframe == 0) emit->get_var (1, out, idx); else { /* XXX: must simplify */ if (docall) emit->get_var (0, out, -(delta+emit->size*2+(emit->size*(atoi(str+4))))); else emit->get_var (1, out, delta+(emit->size*(atoi(str+4)))); } } else { /* TODO: return size of syscall */ if (callname==NULL) { eprintf ("NO CALLNAME'%s'\n", callname); } else { for (i=0; iregs (atoi (str+4))); } else { ret = str; /* TODO: show error, invalid var name? */ eprintf ("FUCKED UP\n"); } } else if (*str=='"' || *str=='\'') { int mustfilter = *str=='"'; if (!stackfixed) eprintf ("WARNING: No room in the static stackframe!\n"); /* TODO: check for room in stackfixed area */ str++; len = strlen (str)-1; str[len]='\0'; snprintf (foo, sizeof (foo)-1, ".fix%d", nargs*16); /* XXX FIX DELTA !!!1 */ dstvar = strdup (foo); rcc_pushstr (str, mustfilter); ret = mk_var (out, foo, 0); } //free ((void *)_str); return ret; } static void rcc_fun(char *str) { char *ptr, *ptr2; if (context) { ptr = strchr(str, '='); if (ptr) { *ptr = '\0'; free (dstvar); dstvar = strdup (str); for (ptr2=ptr+1; isspace (*ptr2); ptr2++); if (*ptr2) callname = strdup (ptr+1); } else { emit->comment ("rcc_fun %d (%s)", context, str); free (callname); callname = strdup (str); } } else { ptr = strchr (str, '@'); if (ptr) { ptr[0]='\0'; mode = NORMAL; if (strstr (ptr+1, "fastcall")) { /* TODO : not yet implemented */ } else if (strstr (ptr+1, "syscall")) { if (str[0]) { mode = SYSCALL; dstvar = strdup (str); } else { mode = INLINE; free (syscallbody); syscallbody = malloc (4096); // XXX hardcoded size dstval = syscallbody; FREE (dstvar); ndstval = 0; syscallbody[0] = '\0'; } } else if (strstr(ptr+1, "alias")) { mode = ALIAS; dstvar = strdup (str); } else if (strstr(ptr+1, "data")) { mode = DATA; ndstval = 0; dstvar = strdup (str); dstval = malloc (4096); } else if (strstr (ptr+1, "inline")) { mode = INLINE; free (dstvar); dstvar = strdup (str); dstval = malloc (4096); ndstval = 0; } else rcc_printf ("\n.%s %s\n%s:\n", ptr+1, str, str); } else rcc_printf ("\n%s:\n", str); } } static void rcc_pusharg(char *str) { char buf[64], *p = mk_var (buf, str, 0); ctxpush[context] = strdup (p); // INDEX IT WITH NARGS OR CONTEXT?!? nargs++; emit->push_arg (varxs, nargs, p); //ctxpush[context+nbrackets] = strdup(str); // use nargs??? (in callname) } static void rcc_element(char *str) { int num, num2; char *p = strrchr (str, ','); if (context) { nargs = 0; while (p) { *p = '\0'; for (p=p+1; *p==' '; p=p+1); rcc_pusharg (p); p = strrchr (str, ','); } if (callname) rcc_pusharg (str); else if (mode == NORMAL) { if (!atoi (str)) { if (dstvar == NULL) /* return string */ dstvar = strdup (".fix0"); rcc_pushstr (str, 1); } } } else { switch (mode) { case ALIAS: emit->equ (dstvar, str); FREE (dstvar); mode = NORMAL; break; case SYSCALL: syscalls[nsyscalls].name = strdup (dstvar); syscalls[nsyscalls].arg = strdup (str); nsyscalls++; FREE (dstvar); break; default: p = strchr (str, ','); if (p) { *p='\0'; num2 = atoi (p+1); } else num2 = 0; num = atoi (str) + num2; stackframe = num; stackfixed = num2; emit->frame (stackframe+stackfixed); } elem[0] = 0; elem_n = 0; } } /* TODO: split this function into several ones..quite long fun */ static void rcc_next() { int i; char *p, buf[64]; docall = 1; if (callname) { char *str, *ocn, *ptr = strchr (callname, '='); if (ptr) { *ptr = '\0'; ocn = ptr+1; } for (ocn=callname; *ocn==' '; ocn++); str = mk_var (buf, ocn, 0); if (ocn[0]=='.') emit->call (str, 1); else if (!strcmp (str, "while")) { if (lastctxdelta>=0) exit (eprintf ("ERROR: Unsupported while syntax\n")); emit->while_end (get_frame_label (1)); #if 0 eprintf ("------------------------------------------ lastctx: %d\n", lastctxdelta); // TODO: the pushvar is required for the if(){}while(); constructions //char *pushvar = ctxpush[context+nbrackets-1]; /* TODO: support to compare more than one expression (LOGICAL OR) */ rcc_printf (" pop %%eax\n"); rcc_printf (" cmp $0, %%eax\n"); // XXX MUST SUPPORT != 0 COMPARE HERE /* TODO : Simplify!! */ //if (pushvar) // printf(" push %s /* wihle push */\n", pushvar); if (lastctxdelta<0) rcc_printf (" jnz %s\n", get_frame_label (1)); else rcc_printf (" jnz %s\n", get_frame_label (0)); //if (pushvar) // printf(" pop %%"R_AX" /* while pop */\n"); #endif nargs = 0; } else { for (i=0; icomment ("set syscall args"); emit->syscall_args (nargs); docall = 0; emit->comment ("syscall"); parsechar ('\n'); /* FIX parsing issue */ for (; *p; p++) parsechar (*p); docall = 0; break; } } if (docall) for (i=0; icomment ("inline"); parsechar ('\n'); /* FIX parsing issue */ for (; *p; p++) parsechar (*p); docall = 0; break; } } if (docall) { emit->comment ("call in mode %d", mode); emit->call (str, 0); } } if (nargs>0) emit->restore_stack (nargs*emit->size); if (ocn) { // Used to call .var0() /* XXX: Probably buggy and wrong */ *buf = 0; str = mk_var (buf, ocn, 0); if (*buf) emit->get_result (buf); //else { eprintf("external symbol %s\n", ocn); } } /* store result of call */ if (dstvar) { *buf = 0; str = mk_var (buf, dstvar, 0); if (*buf == 0) eprintf ("Cannot resolve variable '%s'\n", dstvar); else emit->get_result (buf); FREE (dstvar); } FREE (callname); nargs = 0; } else { int vs = 'l'; char type, *eq, *ptr = elem; elem[elem_n] = '\0'; while (isspace (ptr[0])) ptr=ptr+1; /* skip spaces */ if (*ptr) { eq = strchr (ptr, '='); if (eq) { char str2[64], *p, ch = *(eq-1); *eq = '\0'; for (eq=eq+1; *eq==' '; eq++); p = mk_var (str2, ptr, 0); vs = varsize; if (IS_VAR (eq)) { eq = mk_var (buf, eq, 0); if (varxs=='*') emit->load (eq, varsize); else /* XXX this is a hack .. must be integrated with pusharg */ if (varxs=='&') emit->load_ptr (eq); eq = NULL; type = ' '; } else type = '$'; vs = 'l'; // XXX: add support for != 'l' size emit->mathop (ch, vs, type, eq, p); } else emit->mathop ('=', vs, '$', ptr, NULL); } } } static void rcc_context(int delta) { char str[64]; context += delta; lastctxdelta = delta; if (context == 0 && delta < 0) { emit->frame_end (stackframe+stackfixed, nbrackets); if (mode == NORMAL) /* XXX : commenting this makes hello.r unhappy! TODO: find a cleaner alternative */ stackframe = 0; mode = NORMAL; } else { if (callname) { /* TODO: this must be an array */ char *b = NULL; /* below */ char *g = NULL; /* greater */ char *e = NULL; /* equal */ char *n = NULL; /* negate */ /* conditional block */ emit->comment ("cond frame %s (%s)", callname, elem); /* TODO: simplify with a single for */ b = strchr (elem, '<'); g = strchr (elem, '>'); e = strchr (elem, '='); n = strchr (elem, '!'); if (strstr (callname, "while")) { emit->get_while_end (str, ctxpush[context-1], get_frame_label (2)); free (endframe); endframe = strdup (str); free (callname); callname = strdup ("if"); } if (strstr (callname, "if")) { emit->branch (b, g, e, n, varsize, get_end_frame_label ()); if (context>0) { /* XXX .. */ } else eprintf ("FUCKING CASE\n"); FREE (callname); } else eprintf ("Unknown statement (%s)(%s)\n", callname, elem); } } } static int parseinlinechar(char c) { static int inlinectr = 0; /* skip until '{' */ if (c == '{') { /* XXX: repeated code!! */ rcc_context (1); inlinectr++; if (inlinectr==1) return 0; } else if (inlinectr == 0) return 0; /* capture body */ if (c == '}') { /* XXX: repeated code!! */ if (context < 2) { rcc_context (-1); slurp = 0; mode = NORMAL; inlinectr = 0; if (dstvar == NULL && dstval == syscallbody) { dstval = NULL; return 1; } else /* register */ if (dstval != NULL && dstvar != NULL) { dstval[ndstval]='\0'; //printf(" /* END OF INLINE (%s)(%s) */\n", dstvar, dstval); inlines[ninlines].name = strdup (dstvar); inlines[ninlines].body = strdup (dstval); ninlines++; FREE (dstvar); FREE (dstval); return 1; } else eprintf ("FUCK FUCK\n"); } } dstval[ndstval++] = c; return 0; } static int parsedatachar(char c) { static int inlinectr = 0; char *str; int i,j; /* skip until '{' */ if (c == '{') { /* XXX: repeated code!! */ rcc_context (1); if (++inlinectr==1) return (ndstval = 0); } else if (inlinectr == 0) { /* capture value between parenthesis foo@data(NNN) { ... } */ if (c==')') { stackframe = atoi (dstval); ndstval=0; } else dstval[ndstval++] = c; return 0; } /* capture body */ if (c == '}') { /* XXX: repeated code!! */ if (context < 2) { inlinectr = 0; rcc_context (-1); slurp = 0; mode = NORMAL; /* register */ if (dstval == NULL || dstvar == NULL) { eprintf ("FUCK FUCK\n"); } else { dstval[ndstval]='\0'; emit->comment ("data (%s)(%s)size=(%d)\n", dstvar, dstval, stackframe); rcc_printf (".data\n"); for (str=dstval; isspace (*str); str++); j = (stackframe)? stackframe:1; /* emit label */ rcc_printf ("%s:\n", dstvar); for(i=1;i<=j;i++) { if (str[0]=='"') rcc_printf(".ascii %s%s\n", dstval, (i==j)?"\"\\x00\"":""); else rcc_printf (".long %s\n", dstval); } rcc_printf(".text\n"); FREE (dstvar); FREE (dstval); ndstval = 0; context = 0; return 1; } } } dstval[ndstval++] = c; return 0; } static int parsechar(char c) { char *ptr, str[64]; if (c=='\n') { line++; elem_n = 0; } /* comments */ if (skipline) { if (c != '\n') return 0; skipline = 0; } if (mode == DATA) return parsedatachar (c); if (mode == INLINE) return parseinlinechar (c); /* quotes */ if (quoteline) { if (c != quoteline) { if (quotelinevar == 1) { if (c == '`') { elem[elem_n] = 0; elem_n = 0; rcc_printf ("%s", mk_var (str, elem, 0)); quotelinevar = 0; } else elem[elem_n++] = c; } else { if (c == '`') { elem_n = 0; quotelinevar = 1; } else rcc_printf ("%c", c); } return 0; } else { rcc_printf ("\n"); quoteline = 0; } } if(commentmode) { if (c=='/' && oc == '*') commentmode = 0; return 0; } else if (c=='*' && oc == '/') commentmode = 1; if (slurp) { if (slurp != '"' && c == slurpin) exit (eprintf ( "%s:%d Nesting of expressions not yet supported\n", file, line)); if (c == slurp && oc != '\\') { slurp = 0; elem[elem_n] = '\0'; if (elem_n > 0) rcc_element (elem); else emit->frame (0); elem_n = 0; } else elem[elem_n++] = c; elem[elem_n] = '\0'; } else { switch (c) { case ';': rcc_next (); break; case '"': slurp = '"'; break; case '(': slurpin = '('; slurp = ')'; break; case '{': if (context>0) rcc_printf (" %s:\n", get_frame_label (0)); rcc_context (1); break; case '}': if (endframe) { // XXX: use endframe[context] rcc_printf ("%s\n", endframe); FREE (endframe); } if (context>0) { rcc_printf (" %s:\n", get_end_frame_label ()); nbrackets++; } rcc_context (-1); if (context == 0) { nbrackets = 0; nfunctions++; } break; case ':': if (oc == '\n' || oc == '}') quoteline = '\n'; else elem[elem_n++] = c; break; case '#': if (oc == '\n') skipline = 1; break; case '/': if (oc == '/') skipline = 1; break; default: elem[elem_n++] = c; } if (slurp) { if (elem_n) { ptr = elem; elem[elem_n] = '\0'; while (isspace (*ptr)) ptr++; rcc_fun (ptr); } elem_n = 0; } } if (c!='\t' && c!=' ') oc = c; return 0; } static void showhelp() { fprintf (stderr, "Usage: r2rc [-alh] [files] > file.S\n" " -s use at&t syntax instead of intel\n" " -m add 'call main prefix\n" " -h display this help\n" " -A show default architecture\n" " -a list all supported architectures\n" " -ax86 use x86-32\n" " -ax64 use x86-64\n" " -aarm use ARM\n"); } static void parseflag(const char *arg) { int i; switch (*arg) { case 'a': if (arg[1]) { emit = NULL; for (i=0; emits[i]; i++) if (!strcmp (emits[i]->arch, arg+1)) { emit = emits[i]; syscallbody = emit->syscall (); break; } if (emit == NULL) { eprintf ("Invalid architecture: '%s'\n", arg+1); exit (1); } } else { for (i=0; emits[i]; i++) printf ("%s\n", emits[i]->arch); exit (0); } break; case 'm': showmain = 1; break; case 's': attsyntax = 1; break; case 'A': printf ("%s\n", emit->arch); exit (0); case 'h': showhelp (); exit (0); default: eprintf ("Unknown flag '%c'\n", *arg); } } int main(int argc, char **argv) { int once=0, i, fd = 0; char ch; rcc_init (); for (i=1;icall ("main", 0); emit->trap (); } } for (line=1; read (fd, &ch, 1)==1; ) parsechar (ch); close (fd); } while (i