Logo Search packages:      
Sourcecode: radare2 version File versions  Download package

rarc2.c

/* GPLv3 -- Copyright 2009-2010 -- pancake /at/ nopcode.org */

#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#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)
                        strcpy (str+i+1, str+i+2); // Add STRBCPY macro
            }
      }

      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; i<nsyscalls; i++)
                                    if (!strcmp (syscalls[i].name, callname))
                                          return syscalls[i].arg;
                              eprintf ("Unknown arg for syscall '%s'\n", callname);
                        }
                  }
            } else
            if (!memcmp (str+1, "reg", 3)) {
                  sprintf (out, "%%%s", emit->regs (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';
            sprintf (foo, ".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; i<nsyscalls; i++) {
                        if (!strcmp (str, syscalls[i].name)) {
                              p = syscallbody;
                              emit->comment ("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; i<ninlines; i++) {
                        if (!strcmp (str, inlines[i].name)) {
                              p = inlines[i].body;
                              docall = 0;
                              emit->comment ("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;i<argc;i++) {
            if (argv[i][0]=='-')
                  parseflag (argv[i]+1);
            else break;
      }
      do {
            if (i!=argc)
                  fd = open ((file=argv[i++]), O_RDONLY);
            if (fd == -1) {
                  eprintf ("Cannot open '%s'.\n", file);
                  return 1;
            }
            if (!once) {
                  once++;
                  if (!attsyntax && (emit==&emit_x86 || emit==&emit_x64))
                        rcc_printf (".intel_syntax noprefix\n");
                  if (showmain) {
                        emit->call ("main", 0);
                        emit->trap ();
                  }
            }
            for (line=1; read (fd, &ch, 1)==1; )
                  parsechar (ch);
            close (fd);
      } while (i<argc);
      if (commentmode) {
            eprintf("ERROR: non-closed /**/ comment\n");
            return 1;
      }
      rcc_flush ();
      return 0;
}

Generated by  Doxygen 1.6.0   Back to index