support __fastcall
[ia32rtools.git] / tools / protoparse.h
1
2 struct parsed_proto;
3
4 struct parsed_type {
5         char *name;
6         unsigned int is_array:1;
7         unsigned int is_ptr:1;
8         unsigned int is_struct:1; // split for args
9 };
10
11 struct parsed_proto_arg {
12         char *reg;
13         struct parsed_type type;
14         struct parsed_proto *fptr;
15         void *datap;
16 };
17
18 struct parsed_proto {
19         char name[256];
20         union {
21                 struct parsed_type ret_type;
22                 struct parsed_type type;
23         };
24         struct parsed_proto_arg arg[16];
25         int argc;
26         int argc_stack;
27         int argc_reg;
28         unsigned int is_func:1;
29         unsigned int is_stdcall:1;
30         unsigned int is_fastcall:1;
31         unsigned int is_vararg:1;
32         unsigned int is_fptr:1;
33         unsigned int is_noreturn:1;
34         unsigned int is_unresolved:1;
35         unsigned int has_structarg:1;
36 };
37
38 static const char *hdrfn;
39 static int hdrfline = 0;
40
41 static void pp_copy_arg(struct parsed_proto_arg *d,
42         const struct parsed_proto_arg *s);
43
44 static int b_pp_c_handler(char *proto, const char *fname);
45
46 static int do_protostrs(FILE *fhdr, const char *fname)
47 {
48         const char *finc_name;
49         const char *hdrfn_saved;
50         char protostr[256];
51         char path[256];
52         char fname_inc[256];
53         FILE *finc;
54         int line = 0;
55         int ret;
56         char *p;
57
58         hdrfn_saved = hdrfn;
59         hdrfn = fname;
60
61         while (fgets(protostr, sizeof(protostr), fhdr))
62         {
63                 line++;
64                 if (strncmp(protostr, "//#include ", 11) == 0) {
65                         finc_name = protostr + 11;
66                         p = strpbrk(finc_name, "\r\n ");
67                         if (p != NULL)
68                                 *p = 0;
69
70                         path[0] = 0;
71                         p = strrchr(hdrfn_saved, '/');
72                         if (p) {
73                                 memcpy(path, hdrfn_saved,
74                                         p - hdrfn_saved + 1);
75                                 path[p - hdrfn_saved + 1] = 0;
76                         }
77                         snprintf(fname_inc, sizeof(fname_inc), "%s%s", 
78                                 path, finc_name);
79                         finc = fopen(fname_inc, "r");
80                         if (finc == NULL) {
81                                 printf("%s:%d: can't open '%s'\n",
82                                         fname_inc, line, finc_name);
83                                 continue;
84                         }
85                         ret = do_protostrs(finc, finc_name);
86                         fclose(finc);
87                         if (ret < 0)
88                                 break;
89                         continue;
90                 }
91                 if (strncmp(sskip(protostr), "//", 2) == 0)
92                         continue;
93
94                 p = protostr + strlen(protostr);
95                 for (p--; p >= protostr && my_isblank(*p); --p)
96                         *p = 0;
97                 if (p < protostr)
98                         continue;
99
100                 hdrfline = line;
101
102                 ret = b_pp_c_handler(protostr, hdrfn);
103                 if (ret < 0)
104                         break;
105         }
106
107         hdrfn = hdrfn_saved;
108
109         if (feof(fhdr))
110                 return 0;
111
112         return -1;
113 }
114
115 static int get_regparm(char *dst, size_t dlen, char *p)
116 {
117         int i, o;
118
119         if (*p != '<')
120                 return 0;
121
122         for (o = 0, i = 1; o < dlen; i++) {
123                 if (p[i] == 0)
124                         return 0;
125                 if (p[i] == '>')
126                         break;
127                 dst[o++] = p[i];
128         }
129         dst[o] = 0;
130         return i + 1;
131 }
132
133 // hmh..
134 static const char *known_type_mod[] = {
135         "const",
136         "signed",
137         "unsigned",
138         "struct",
139         "enum",
140         "CONST",
141 };
142
143 static const char *known_ptr_types[] = {
144         "FARPROC",
145         "HACCEL",
146         "HANDLE",
147         "HBITMAP",
148         "HCURSOR",
149         "HDC",
150         "HFONT",
151         "HGDIOBJ",
152         "HGLOBAL",
153         "HICON",
154         "HINSTANCE",
155         //"HIMC", // DWORD
156         "HMODULE",
157         "HPALETTE",
158         "HRGN",
159         "HRSRC",
160         "HKEY",
161         "HMENU",
162         "HWND",
163         "PCRITICAL_SECTION",
164         "PDWORD",
165         "PHKEY",
166         "PLONG",
167         "PMEMORY_BASIC_INFORMATION",
168         "PUINT",
169         "PVOID",
170         "PCVOID",
171         "DLGPROC",
172         "TIMERPROC",
173         "WNDENUMPROC",
174         "va_list",
175         "__VALIST",
176 };
177
178 static const char *ignored_keywords[] = {
179         "extern",
180         "WINBASEAPI",
181         "WINUSERAPI",
182         "WINGDIAPI",
183         "WINADVAPI",
184 };
185
186 // returns ptr to char after type ends
187 static int typecmp(const char *n, const char *t)
188 {
189         for (; *t != 0; n++, t++) {
190                 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
191                         n++;
192                 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
193                         t++;
194                 if (*n != *t)
195                         return *n - *t;
196         }
197
198         return 0;
199 }
200
201 static const char *skip_type_mod(const char *n)
202 {
203         int len;
204         int i;
205
206         for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
207                 len = strlen(known_type_mod[i]);
208                 if (strncmp(n, known_type_mod[i], len) != 0)
209                         continue;
210                 if (!my_isblank(n[len]))
211                         continue;
212
213                 n += len;
214                 while (my_isblank(*n))
215                         n++;
216                 i = 0;
217         }
218
219         return n;
220 }
221
222 static int check_type(const char *name, struct parsed_type *type)
223 {
224         const char *n, *n1;
225         int ret = -1;
226         int i;
227
228         n = skip_type_mod(name);
229
230         for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
231                 if (typecmp(n, known_ptr_types[i]))
232                         continue;
233
234                 type->is_ptr = 1;
235                 break;
236         }
237
238         if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
239                 type->is_ptr = 1;
240
241         // assume single word
242         while (!my_isblank(*n) && !my_issep(*n))
243                 n++;
244
245         while (1) {
246                 n1 = n;
247                 while (my_isblank(*n))
248                         n++;
249                 if (*n == '*') {
250                         type->is_ptr = 1;
251                         n++;
252                         continue;
253                 }
254                 break;
255         }
256
257         ret = n1 - name;
258         type->name = strndup(name, ret);
259         if (IS(type->name, "VOID"))
260                 memcpy(type->name, "void", 4);
261
262         return ret;
263 }
264
265 /* args are always expanded to 32bit */
266 static const char *map_reg(const char *reg)
267 {
268         const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
269         const char *regs_w[] = { "ax",  "bx",  "cx",  "dx",  "si",  "di" };
270         const char *regs_b[] = { "al",  "bl",  "cl",  "dl" };
271         int i;
272
273         for (i = 0; i < ARRAY_SIZE(regs_w); i++)
274                 if (IS(reg, regs_w[i]))
275                         return regs_f[i];
276
277         for (i = 0; i < ARRAY_SIZE(regs_b); i++)
278                 if (IS(reg, regs_b[i]))
279                         return regs_f[i];
280
281         return reg;
282 }
283
284 static int check_struct_arg(struct parsed_proto_arg *arg)
285 {
286         if (IS(arg->type.name, "POINT"))
287                 return 2 - 1;
288
289         return 0;
290 }
291
292 static int parse_protostr(char *protostr, struct parsed_proto *pp)
293 {
294         struct parsed_proto_arg *arg;
295         char regparm[16];
296         char buf[256];
297         char cconv[32];
298         int xarg = 0;
299         char *p, *p1;
300         int i, l;
301         int ret;
302
303         p = sskip(protostr);
304         if (p[0] == '/' && p[1] == '/') {
305                 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
306                 p = sskip(p + 2);
307         }
308
309         // strip unneeded stuff
310         for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
311                 if ((p1[0] == '/' && p1[1] == '*')
312                  || (p1[0] == '*' && p1[1] == '/'))
313                         p1[0] = p1[1] = ' ';
314         }
315
316         if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
317                 pp->is_noreturn = 1;
318                 p = sskip(p + 18);
319         }
320
321         for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
322                 l = strlen(ignored_keywords[i]);
323                 if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
324                         p = sskip(p + l + 1);
325         }
326
327         ret = check_type(p, &pp->ret_type);
328         if (ret <= 0) {
329                 printf("%s:%d:%zd: unhandled return in '%s'\n",
330                         hdrfn, hdrfline, (p - protostr) + 1, protostr);
331                 return -1;
332         }
333         p = sskip(p + ret);
334
335         if (!strncmp(p, "noreturn ", 9)) {
336                 pp->is_noreturn = 1;
337                 p = sskip(p + 9);
338         }
339
340         if (!strchr(p, ')')) {
341                 p = next_idt(buf, sizeof(buf), p);
342                 p = sskip(p);
343                 if (buf[0] == 0) {
344                         printf("%s:%d:%zd: var name missing\n",
345                                 hdrfn, hdrfline, (p - protostr) + 1);
346                         return -1;
347                 }
348                 strcpy(pp->name, buf);
349
350                 p1 = strchr(p, ']');
351                 if (p1 != NULL) {
352                         p = p1 + 1;
353                         pp->ret_type.is_array = 1;
354                 }
355                 return p - protostr;
356         }
357
358         pp->is_func = 1;
359
360         if (*p == '(') {
361                 pp->is_fptr = 1;
362                 p = sskip(p + 1);
363         }
364
365         p = next_word(cconv, sizeof(cconv), p);
366         p = sskip(p);
367         if (cconv[0] == 0) {
368                 printf("%s:%d:%zd: cconv missing\n",
369                         hdrfn, hdrfline, (p - protostr) + 1);
370                 return -1;
371         }
372         if      (IS(cconv, "__cdecl"))
373                 pp->is_stdcall = 0;
374         else if (IS(cconv, "__stdcall"))
375                 pp->is_stdcall = 1;
376         else if (IS(cconv, "__fastcall")) {
377                 pp->is_fastcall = 1;
378                 pp->is_stdcall = 1; // sort of..
379         }
380         else if (IS(cconv, "__thiscall"))
381                 pp->is_stdcall = 1;
382         else if (IS(cconv, "__userpurge"))
383                 pp->is_stdcall = 1; // IDA
384         else if (IS(cconv, "__usercall"))
385                 pp->is_stdcall = 0; // IDA
386         else if (IS(cconv, "WINAPI"))
387                 pp->is_stdcall = 1;
388         else {
389                 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
390                         hdrfn, hdrfline, (p - protostr) + 1, cconv);
391                 return -1;
392         }
393
394         if (pp->is_fptr) {
395                 if (*p != '*') {
396                         printf("%s:%d:%zd: '*' expected\n",
397                                 hdrfn, hdrfline, (p - protostr) + 1);
398                         return -1;
399                 }
400                 p++;
401                 // XXX: skipping extra asterisks, for now
402                 while (*p == '*')
403                         p++;
404                 p = sskip(p);
405         }
406
407         p = next_idt(buf, sizeof(buf), p);
408         p = sskip(p);
409         if (buf[0] == 0) {
410                 //printf("%s:%d:%zd: func name missing\n",
411                 //      hdrfn, hdrfline, (p - protostr) + 1);
412                 //return -1;
413         }
414         strcpy(pp->name, buf);
415
416         ret = get_regparm(regparm, sizeof(regparm), p);
417         if (ret > 0) {
418                 if (!IS(regparm, "eax") && !IS(regparm, "ax")
419                  && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
420                 {
421                         printf("%s:%d:%zd: bad regparm: %s\n",
422                                 hdrfn, hdrfline, (p - protostr) + 1, regparm);
423                         return -1;
424                 }
425                 p += ret;
426                 p = sskip(p);
427         }
428
429         if (pp->is_fptr) {
430                 if (*p == '[') {
431                         // not really ret_type is array, but ohwell
432                         pp->ret_type.is_array = 1;
433                         p = strchr(p + 1, ']');
434                         if (p == NULL) {
435                                 printf("%s:%d:%zd: ']' expected\n",
436                                  hdrfn, hdrfline, (p - protostr) + 1);
437                                 return -1;
438                         }
439                         p = sskip(p + 1);
440                 }
441                 if (*p != ')') {
442                         printf("%s:%d:%zd: ')' expected\n",
443                                 hdrfn, hdrfline, (p - protostr) + 1);
444                         return -1;
445                 }
446                 p = sskip(p + 1);
447         }
448
449         if (*p != '(') {
450                 printf("%s:%d:%zd: '(' expected, got '%c'\n",
451                                 hdrfn, hdrfline, (p - protostr) + 1, *p);
452                 return -1;
453         }
454         p++;
455
456         // check for x(void)
457         p = sskip(p);
458         if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
459            && *sskip(p + 4) == ')')
460                 p += 4;
461
462         while (1) {
463                 p = sskip(p);
464                 if (*p == ')') {
465                         p++;
466                         break;
467                 }
468                 if (xarg > 0) {
469                         if (*p != ',') {
470                                 printf("%s:%d:%zd: ',' expected\n",
471                                  hdrfn, hdrfline, (p - protostr) + 1);
472                                 return -1;
473                         }
474                         p = sskip(p + 1);
475                 }
476
477                 if (!strncmp(p, "...", 3)) {
478                         pp->is_vararg = 1;
479                         p = sskip(p + 3);
480                         if (*p == ')') {
481                                 p++;
482                                 break;
483                         }
484                         printf("%s:%d:%zd: ')' expected\n",
485                                 hdrfn, hdrfline, (p - protostr) + 1);
486                         return -1;
487                 }
488
489                 arg = &pp->arg[xarg];
490                 xarg++;
491
492                 p1 = p;
493                 ret = check_type(p, &arg->type);
494                 if (ret <= 0) {
495                         printf("%s:%d:%zd: unhandled type for arg%d\n",
496                                 hdrfn, hdrfline, (p - protostr) + 1, xarg);
497                         return -1;
498                 }
499                 p = sskip(p + ret);
500
501                 if (*p == '(') {
502                         // func ptr
503                         arg->fptr = calloc(1, sizeof(*arg->fptr));
504                         ret = parse_protostr(p1, arg->fptr);
505                         if (ret < 0) {
506                                 printf("%s:%d:%zd: funcarg parse failed\n",
507                                         hdrfn, hdrfline, p1 - protostr);
508                                 return -1;
509                         }
510                         // we'll treat it as void * for non-calls
511                         arg->type.name = strdup("void *");
512                         arg->type.is_ptr = 1;
513
514                         p = p1 + ret;
515                 }
516
517                 p = next_idt(buf, sizeof(buf), p);
518                 p = sskip(p);
519 #if 0
520                 if (buf[0] == 0) {
521                         printf("%s:%d:%zd: idt missing for arg%d\n",
522                                 hdrfn, hdrfline, (p - protostr) + 1, xarg);
523                         return -1;
524                 }
525 #endif
526                 arg->reg = NULL;
527
528                 ret = get_regparm(regparm, sizeof(regparm), p);
529                 if (ret > 0) {
530                         p += ret;
531                         p = sskip(p);
532
533                         arg->reg = strdup(map_reg(regparm));
534                 }
535
536                 ret = check_struct_arg(arg);
537                 if (ret > 0) {
538                         pp->has_structarg = 1;
539                         arg->type.is_struct = 1;
540                         free(arg->type.name);
541                         arg->type.name = strdup("int");
542                         for (l = 0; l < ret; l++) {
543                                 pp_copy_arg(&pp->arg[xarg], arg);
544                                 xarg++;
545                         }
546                 }
547         }
548
549         if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
550                 if (pp->arg[0].reg != NULL) {
551                         printf("%s:%d: %s with arg1 spec %s?\n",
552                                 hdrfn, hdrfline, cconv, pp->arg[0].reg);
553                 }
554                 pp->arg[0].reg = strdup("ecx");
555         }
556
557         if (xarg > 1 && IS(cconv, "__fastcall")) {
558                 if (pp->arg[1].reg != NULL) {
559                         printf("%s:%d: %s with arg2 spec %s?\n",
560                                 hdrfn, hdrfline, cconv, pp->arg[1].reg);
561                 }
562                 pp->arg[1].reg = strdup("edx");
563         }
564
565         pp->argc = xarg;
566
567         for (i = 0; i < pp->argc; i++) {
568                 if (pp->arg[i].reg == NULL)
569                         pp->argc_stack++;
570                 else
571                         pp->argc_reg++;
572         }
573
574         if (pp->argc == 1 && pp->arg[0].reg != NULL
575             && IS(pp->arg[0].reg, "ecx"))
576         {
577                 pp->is_fastcall = 1;
578         }
579         else if (pp->argc_reg == 2
580           && pp->arg[0].reg != NULL && IS(pp->arg[0].reg, "ecx")
581           && pp->arg[1].reg != NULL && IS(pp->arg[1].reg, "edx"))
582         {
583                 pp->is_fastcall = 1;
584         }
585
586         if (pp->is_vararg && (pp->is_stdcall || pp->is_fastcall)) {
587                 printf("%s:%d: vararg %s?\n", hdrfn, hdrfline, cconv);
588                 return -1;
589         }
590
591         return p - protostr;
592 }
593
594 static int pp_name_cmp(const void *p1, const void *p2)
595 {
596         const struct parsed_proto *pp1 = p1, *pp2 = p2;
597         return strcmp(pp1->name, pp2->name);
598 }
599
600 static struct parsed_proto *pp_cache;
601 static int pp_cache_size;
602 static int pp_cache_alloc;
603
604 static int b_pp_c_handler(char *proto, const char *fname)
605 {
606         int ret;
607
608         if (pp_cache_size >= pp_cache_alloc) {
609                 pp_cache_alloc = pp_cache_alloc * 2 + 64;
610                 pp_cache = realloc(pp_cache, pp_cache_alloc
611                                 * sizeof(pp_cache[0]));
612                 my_assert_not(pp_cache, NULL);
613                 memset(pp_cache + pp_cache_size, 0,
614                         (pp_cache_alloc - pp_cache_size)
615                          * sizeof(pp_cache[0]));
616         }
617
618         ret = parse_protostr(proto, &pp_cache[pp_cache_size]);
619         if (ret < 0)
620                 return -1;
621
622         pp_cache_size++;
623         return 0;
624 }
625
626 static void build_pp_cache(FILE *fhdr)
627 {
628         long pos;
629         int ret;
630
631         pos = ftell(fhdr);
632         rewind(fhdr);
633
634         ret = do_protostrs(fhdr, hdrfn);
635         if (ret < 0)
636                 exit(1);
637
638         qsort(pp_cache, pp_cache_size, sizeof(pp_cache[0]), pp_name_cmp);
639         fseek(fhdr, pos, SEEK_SET);
640 }
641
642 static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
643         int quiet)
644 {
645         const struct parsed_proto *pp_ret;
646         struct parsed_proto pp_search;
647
648         if (pp_cache == NULL)
649                 build_pp_cache(fhdr);
650
651         if (sym[0] == '_') // && strncmp(fname, "stdc", 4) == 0)
652                 sym++;
653
654         strcpy(pp_search.name, sym);
655         pp_ret = bsearch(&pp_search, pp_cache, pp_cache_size,
656                         sizeof(pp_cache[0]), pp_name_cmp);
657         if (pp_ret == NULL && !quiet)
658                 printf("%s: sym '%s' is missing\n", hdrfn, sym);
659
660         return pp_ret;
661 }
662
663 static void pp_copy_arg(struct parsed_proto_arg *d,
664         const struct parsed_proto_arg *s)
665 {
666         memcpy(d, s, sizeof(*d));
667
668         if (s->reg != NULL) {
669                 d->reg = strdup(s->reg);
670                 my_assert_not(d->reg, NULL);
671         }
672         if (s->type.name != NULL) {
673                 d->type.name = strdup(s->type.name);
674                 my_assert_not(d->type.name, NULL);
675         }
676         if (s->fptr != NULL) {
677                 d->fptr = malloc(sizeof(*d->fptr));
678                 my_assert_not(d->fptr, NULL);
679                 memcpy(d->fptr, s->fptr, sizeof(*d->fptr));
680         }
681 }
682
683 struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
684 {
685         struct parsed_proto *pp;
686         int i;
687
688         pp = malloc(sizeof(*pp));
689         my_assert_not(pp, NULL);
690         memcpy(pp, pp_c, sizeof(*pp)); // lazy..
691
692         // do the actual deep copy..
693         for (i = 0; i < pp_c->argc; i++)
694                 pp_copy_arg(&pp->arg[i], &pp_c->arg[i]);
695         if (pp_c->ret_type.name != NULL)
696                 pp->ret_type.name = strdup(pp_c->ret_type.name);
697
698         return pp;
699 }
700
701 static inline void proto_release(struct parsed_proto *pp)
702 {
703         int i;
704
705         for (i = 0; i < pp->argc; i++) {
706                 if (pp->arg[i].reg != NULL)
707                         free(pp->arg[i].reg);
708                 if (pp->arg[i].type.name != NULL)
709                         free(pp->arg[i].type.name);
710                 if (pp->arg[i].fptr != NULL)
711                         free(pp->arg[i].fptr);
712         }
713         if (pp->ret_type.name != NULL)
714                 free(pp->ret_type.name);
715         free(pp);
716 }