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