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