translate: initial struct parsing for member calls
[ia32rtools.git] / tools / protoparse.h
index 0900243..511e337 100644 (file)
@@ -1,3 +1,10 @@
+/*
+ * ia32rtools
+ * (C) notaz, 2013,2014
+ *
+ * This work is licensed under the terms of 3-clause BSD license.
+ * See COPYING file in the top-level directory.
+ */
 
 struct parsed_proto;
 
@@ -6,6 +13,8 @@ struct parsed_type {
        unsigned int is_array:1;
        unsigned int is_ptr:1;
        unsigned int is_struct:1; // split for args
+       unsigned int is_retreg:1; // register to return to caller
+       unsigned int is_va_list:1;
 };
 
 struct parsed_proto_arg {
@@ -28,11 +37,26 @@ struct parsed_proto {
        unsigned int is_func:1;
        unsigned int is_stdcall:1;
        unsigned int is_fastcall:1;
-       unsigned int is_vararg:1;
+       unsigned int is_vararg:1;     // vararg func
        unsigned int is_fptr:1;
        unsigned int is_noreturn:1;
        unsigned int is_unresolved:1;
+       unsigned int is_userstack:1;
+       unsigned int is_include:1;    // not from top-level header
+       unsigned int is_osinc:1;      // OS/system library func
+       unsigned int is_cinc:1;       // crt library func
+       unsigned int is_arg:1;        // declared in some func arg
        unsigned int has_structarg:1;
+       unsigned int has_retreg:1;
+};
+
+struct parsed_struct {
+       char name[256];
+       struct {
+               int offset;
+               struct parsed_proto pp;
+       } members[64];
+       int member_count;
 };
 
 static const char *hdrfn;
@@ -41,15 +65,19 @@ static int hdrfline = 0;
 static void pp_copy_arg(struct parsed_proto_arg *d,
        const struct parsed_proto_arg *s);
 
-static int b_pp_c_handler(char *proto, const char *fname);
+static int b_pp_c_handler(char *proto, const char *fname,
+       int is_include, int is_osinc, int is_cinc);
+static int struct_handler(FILE *fhdr, char *proto, int *line);
 
-static int do_protostrs(FILE *fhdr, const char *fname)
+static int do_protostrs(FILE *fhdr, const char *fname, int is_include)
 {
        const char *finc_name;
        const char *hdrfn_saved;
        char protostr[256];
        char path[256];
        char fname_inc[256];
+       int is_osinc;
+       int is_cinc;
        FILE *finc;
        int line = 0;
        int ret;
@@ -58,6 +86,9 @@ static int do_protostrs(FILE *fhdr, const char *fname)
        hdrfn_saved = hdrfn;
        hdrfn = fname;
 
+       is_cinc = strstr(fname, "stdc.hlist") != NULL;
+       is_osinc = is_cinc || strstr(fname, "stdc.hlist") != NULL;
+
        while (fgets(protostr, sizeof(protostr), fhdr))
        {
                line++;
@@ -82,7 +113,7 @@ static int do_protostrs(FILE *fhdr, const char *fname)
                                        fname_inc, line, finc_name);
                                continue;
                        }
-                       ret = do_protostrs(finc, finc_name);
+                       ret = do_protostrs(finc, finc_name, 1);
                        fclose(finc);
                        if (ret < 0)
                                break;
@@ -99,7 +130,12 @@ static int do_protostrs(FILE *fhdr, const char *fname)
 
                hdrfline = line;
 
-               ret = b_pp_c_handler(protostr, hdrfn);
+               if (!strncmp(protostr, "struct", 6)
+                   && strchr(protostr, '{') != NULL)
+                       ret = struct_handler(fhdr, protostr, &line);
+               else
+                       ret = b_pp_c_handler(protostr, hdrfn,
+                               is_include, is_osinc, is_cinc);
                if (ret < 0)
                        break;
        }
@@ -112,14 +148,22 @@ static int do_protostrs(FILE *fhdr, const char *fname)
        return -1;
 }
 
-static int get_regparm(char *dst, size_t dlen, char *p)
+static int get_regparm(char *dst, size_t dlen, char *p, int *retreg)
 {
-       int i, o;
+       int i = 0, o;
+
+       *retreg = 0;
 
        if (*p != '<')
                return 0;
 
-       for (o = 0, i = 1; o < dlen; i++) {
+       i++;
+       if (p[i] == '*') {
+               *retreg = 1;
+               i++;
+       }
+
+       for (o = 0; o < dlen; i++) {
                if (p[i] == 0)
                        return 0;
                if (p[i] == '>')
@@ -135,7 +179,6 @@ static const char *known_type_mod[] = {
        "const",
        "signed",
        "unsigned",
-       "struct",
        "enum",
        "CONST",
        "volatile",
@@ -144,9 +187,11 @@ static const char *known_type_mod[] = {
 static const char *known_ptr_types[] = {
        "FARPROC",
        "WNDPROC",
+       "LINECALLBACK",
        "HACCEL",
        "HANDLE",
        "HBITMAP",
+       "HCALL",
        "HCURSOR",
        "HDC",
        "HFONT",
@@ -154,18 +199,23 @@ static const char *known_ptr_types[] = {
        "HGLOBAL",
        "HICON",
        "HINSTANCE",
-       //"HIMC", // DWORD
+       "HIMC", // DWORD in mingw, ptr in wine..
+       "HLINE",
+       "HLINEAPP",
+       "HLOCAL",
        "HMODULE",
        "HPALETTE",
        "HRGN",
        "HRSRC",
        "HKEY",
        "HMENU",
+       "HWAVEOUT",
        "HWND",
        "PBYTE",
        "PCRITICAL_SECTION",
        "PDWORD",
        "PFILETIME",
+       "PLARGE_INTEGER",
        "PHKEY",
        "PLONG",
        "PMEMORY_BASIC_INFORMATION",
@@ -188,7 +238,6 @@ static const char *ignored_keywords[] = {
        "WINADVAPI",
 };
 
-// returns ptr to char after type ends
 static int typecmp(const char *n, const char *t)
 {
        for (; *t != 0; n++, t++) {
@@ -232,6 +281,14 @@ static int check_type(const char *name, struct parsed_type *type)
 
        n = skip_type_mod(name);
 
+       if (!strncmp(n, "struct", 6) && my_isblank(n[6])) {
+               type->is_struct = 1;
+
+               n += 6;
+               while (my_isblank(*n))
+                       n++;
+       }
+
        for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
                if (typecmp(n, known_ptr_types[i]))
                        continue;
@@ -261,6 +318,8 @@ static int check_type(const char *name, struct parsed_type *type)
 
        ret = n1 - name;
        type->name = strndup(name, ret);
+       if (IS(type->name, "__VALIST") || IS(type->name, "va_list"))
+               type->is_va_list = 1;
        if (IS(type->name, "VOID"))
                memcpy(type->name, "void", 4);
 
@@ -300,6 +359,7 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp)
        char regparm[16];
        char buf[256];
        char cconv[32];
+       int is_retreg;
        int xarg = 0;
        char *p, *p1;
        int i, l;
@@ -311,7 +371,18 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp)
                p = sskip(p + 2);
        }
 
-       // strip unneeded stuff
+       // allow start of line comment
+       if (p[0] == '/' && p[1] == '*') {
+               p = strstr(p + 2, "*/");
+               if (p == NULL) {
+                       printf("%s:%d: multiline comments unsupported\n",
+                               hdrfn, hdrfline);
+                       return -1;
+               }
+               p = sskip(p + 2);
+       }
+
+       // we need remaining hints in comments, so strip / *
        for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
                if ((p1[0] == '/' && p1[1] == '*')
                 || (p1[0] == '*' && p1[1] == '/'))
@@ -388,6 +459,10 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp)
                pp->is_stdcall = 1; // IDA
        else if (IS(cconv, "__usercall"))
                pp->is_stdcall = 0; // IDA
+       else if (IS(cconv, "__userstack")) {
+               pp->is_stdcall = 0; // custom
+               pp->is_userstack = 1;
+       }
        else if (IS(cconv, "WINAPI"))
                pp->is_stdcall = 1;
        else {
@@ -418,7 +493,7 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp)
        }
        strcpy(pp->name, buf);
 
-       ret = get_regparm(regparm, sizeof(regparm), p);
+       ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
        if (ret > 0) {
                if (!IS(regparm, "eax") && !IS(regparm, "ax")
                 && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
@@ -512,6 +587,10 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp)
                                        hdrfn, hdrfline, p1 - protostr);
                                return -1;
                        }
+                       arg->fptr->is_arg = 1;
+                       // we don't use actual names right now..
+                       snprintf(arg->fptr->name,
+                               sizeof(arg->fptr->name), "a%d", xarg);
                        // we'll treat it as void * for non-calls
                        arg->type.name = strdup("void *");
                        arg->type.is_ptr = 1;
@@ -530,12 +609,14 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp)
 #endif
                arg->reg = NULL;
 
-               ret = get_regparm(regparm, sizeof(regparm), p);
+               ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
                if (ret > 0) {
                        p += ret;
                        p = sskip(p);
 
                        arg->reg = strdup(map_reg(regparm));
+                       arg->type.is_retreg = is_retreg;
+                       pp->has_retreg |= is_retreg;
                }
 
                if (strstr(arg->type.name, "int64")
@@ -612,11 +693,83 @@ static int pp_name_cmp(const void *p1, const void *p2)
        return strcmp(pp1->name, pp2->name);
 }
 
+static int ps_name_cmp(const void *p1, const void *p2)
+{
+       const struct parsed_struct *ps1 = p1, *ps2 = p2;
+       return strcmp(ps1->name, ps2->name);
+}
+
+// parsed struct cache
+static struct parsed_struct *ps_cache;
+static int ps_cache_size;
+static int ps_cache_alloc;
+
+static int struct_handler(FILE *fhdr, char *proto, int *line)
+{
+       struct parsed_struct *ps;
+       char lstr[256], *p;
+       int offset = 0;
+       int m = 0;
+       int ret;
+
+       if (ps_cache_size >= ps_cache_alloc) {
+               ps_cache_alloc = ps_cache_alloc * 2 + 64;
+               ps_cache = realloc(ps_cache, ps_cache_alloc
+                               * sizeof(ps_cache[0]));
+               my_assert_not(ps_cache, NULL);
+               memset(ps_cache + ps_cache_size, 0,
+                       (ps_cache_alloc - ps_cache_size)
+                        * sizeof(ps_cache[0]));
+       }
+
+       ps = &ps_cache[ps_cache_size++];
+       ret = sscanf(proto, "struct %255s {", ps->name);
+       if (ret != 1) {
+               printf("%s:%d: struct parse failed\n", hdrfn, *line);
+               return -1;
+       }
+
+       while (fgets(lstr, sizeof(lstr), fhdr))
+       {
+               (*line)++;
+
+               p = sskip(lstr);
+               if (p[0] == '/' && p[1] == '/')
+                       continue;
+               if (p[0] == '}')
+                       break;
+
+               if (m >= ARRAY_SIZE(ps->members)) {
+                       printf("%s:%d: too many struct members\n",
+                               hdrfn, *line);
+                       return -1;
+               }
+
+               hdrfline = *line;
+               ret = parse_protostr(p, &ps->members[m].pp);
+               if (ret < 0) {
+                       printf("%s:%d: struct member #%d/%02x "
+                               "doesn't parse\n", hdrfn, *line,
+                               m, offset);
+                       return -1;
+               }
+               ps->members[m].offset = offset;
+               offset += 4;
+               m++;
+       }
+
+       ps->member_count = m;
+
+       return 0;
+}
+
+// parsed proto cache
 static struct parsed_proto *pp_cache;
 static int pp_cache_size;
 static int pp_cache_alloc;
 
-static int b_pp_c_handler(char *proto, const char *fname)
+static int b_pp_c_handler(char *proto, const char *fname,
+       int is_include, int is_osinc, int is_cinc)
 {
        int ret;
 
@@ -634,11 +787,14 @@ static int b_pp_c_handler(char *proto, const char *fname)
        if (ret < 0)
                return -1;
 
+       pp_cache[pp_cache_size].is_include = is_include;
+       pp_cache[pp_cache_size].is_osinc = is_osinc;
+       pp_cache[pp_cache_size].is_cinc = is_cinc;
        pp_cache_size++;
        return 0;
 }
 
-static void build_pp_cache(FILE *fhdr)
+static void build_caches(FILE *fhdr)
 {
        long pos;
        int ret;
@@ -646,11 +802,12 @@ static void build_pp_cache(FILE *fhdr)
        pos = ftell(fhdr);
        rewind(fhdr);
 
-       ret = do_protostrs(fhdr, hdrfn);
+       ret = do_protostrs(fhdr, hdrfn, 0);
        if (ret < 0)
                exit(1);
 
        qsort(pp_cache, pp_cache_size, sizeof(pp_cache[0]), pp_name_cmp);
+       qsort(ps_cache, ps_cache_size, sizeof(ps_cache[0]), ps_name_cmp);
        fseek(fhdr, pos, SEEK_SET);
 }
 
@@ -659,14 +816,19 @@ static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
 {
        const struct parsed_proto *pp_ret;
        struct parsed_proto pp_search;
+       char *p;
 
        if (pp_cache == NULL)
-               build_pp_cache(fhdr);
+               build_caches(fhdr);
 
        if (sym[0] == '_') // && strncmp(fname, "stdc", 4) == 0)
                sym++;
 
        strcpy(pp_search.name, sym);
+       p = strchr(pp_search.name, '@');
+       if (p != NULL)
+               *p = 0;
+
        pp_ret = bsearch(&pp_search, pp_cache, pp_cache_size,
                        sizeof(pp_cache[0]), pp_name_cmp);
        if (pp_ret == NULL && !quiet)
@@ -675,6 +837,41 @@ static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
        return pp_ret;
 }
 
+static const struct parsed_proto *proto_lookup_struct(FILE *fhdr,
+       const char *type, int offset)
+{
+       struct parsed_struct ps_search, *ps;
+       int m;
+
+       if (pp_cache == NULL)
+               build_caches(fhdr);
+       if (ps_cache_size == 0)
+               return NULL;
+
+       while (my_isblank(*type))
+               type++;
+       if (!strncmp(type, "struct", 6) && my_isblank(type[6]))
+               type += 7;
+
+       if (sscanf(type, "%255s", ps_search.name) != 1)
+               return NULL;
+
+       ps = bsearch(&ps_search, ps_cache, ps_cache_size,
+                       sizeof(ps_cache[0]), ps_name_cmp);
+       if (ps == NULL) {
+               printf("%s: struct '%s' is missing\n",
+                       hdrfn, ps_search.name);
+               return NULL;
+       }
+
+       for (m = 0; m < ps->member_count; m++) {
+               if (ps->members[m].offset == offset)
+                       return &ps->members[m].pp;
+       }
+
+       return NULL;
+}
+
 static void pp_copy_arg(struct parsed_proto_arg *d,
        const struct parsed_proto_arg *s)
 {
@@ -713,6 +910,30 @@ struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
        return pp;
 }
 
+
+static inline int pp_cmp_func(const struct parsed_proto *pp1,
+  const struct parsed_proto *pp2)
+{
+  int i;
+
+  if (pp1->argc != pp2->argc || pp1->argc_reg != pp2->argc_reg)
+    return 1;
+  else {
+    for (i = 0; i < pp1->argc; i++) {
+      if ((pp1->arg[i].reg != NULL) != (pp2->arg[i].reg != NULL))
+        return 1;
+
+      if ((pp1->arg[i].reg != NULL)
+        && !IS(pp1->arg[i].reg, pp2->arg[i].reg))
+      {
+        return 1;
+      }
+    }
+  }
+
+  return 0;
+}
+
 static inline void pp_print(char *buf, size_t buf_size,
   const struct parsed_proto *pp)
 {
@@ -750,4 +971,6 @@ static inline void proto_release(struct parsed_proto *pp)
        if (pp->ret_type.name != NULL)
                free(pp->ret_type.name);
        free(pp);
+
+       (void)proto_lookup_struct;
 }