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