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