5 * This work is licensed under the terms of 3-clause BSD license.
6 * See COPYING file in the top-level directory.
13 unsigned int is_array:1;
14 unsigned int is_ptr:1;
15 unsigned int is_struct:1; // split for args
16 unsigned int is_retreg:1; // register to return to caller
17 unsigned int is_va_list:1;
20 struct parsed_proto_arg {
22 struct parsed_type type;
23 struct parsed_proto *fptr;
30 struct parsed_type ret_type;
31 struct parsed_type type;
33 struct parsed_proto_arg arg[16];
37 unsigned int is_func:1;
38 unsigned int is_stdcall:1;
39 unsigned int is_fastcall:1;
40 unsigned int is_vararg:1; // vararg func
41 unsigned int is_fptr:1;
42 unsigned int is_noreturn:1;
43 unsigned int is_unresolved:1;
44 unsigned int is_userstack:1;
45 unsigned int is_include:1; // not from top-level header
46 unsigned int is_osinc:1; // OS/system library func
47 unsigned int is_cinc:1; // crt library func
48 unsigned int is_arg:1; // declared in some func arg
49 unsigned int has_structarg:1;
50 unsigned int has_retreg:1;
53 static const char *hdrfn;
54 static int hdrfline = 0;
56 static void pp_copy_arg(struct parsed_proto_arg *d,
57 const struct parsed_proto_arg *s);
59 static int b_pp_c_handler(char *proto, const char *fname,
60 int is_include, int is_osinc, int is_cinc);
62 static int do_protostrs(FILE *fhdr, const char *fname, int is_include)
64 const char *finc_name;
65 const char *hdrfn_saved;
79 is_cinc = strstr(fname, "stdc.hlist") != NULL;
80 is_osinc = is_cinc || strstr(fname, "stdc.hlist") != NULL;
82 while (fgets(protostr, sizeof(protostr), fhdr))
85 if (strncmp(protostr, "//#include ", 11) == 0) {
86 finc_name = protostr + 11;
87 p = strpbrk(finc_name, "\r\n ");
92 p = strrchr(hdrfn_saved, '/');
94 memcpy(path, hdrfn_saved,
96 path[p - hdrfn_saved + 1] = 0;
98 snprintf(fname_inc, sizeof(fname_inc), "%s%s",
100 finc = fopen(fname_inc, "r");
102 printf("%s:%d: can't open '%s'\n",
103 fname_inc, line, finc_name);
106 ret = do_protostrs(finc, finc_name, 1);
112 if (strncmp(sskip(protostr), "//", 2) == 0)
115 p = protostr + strlen(protostr);
116 for (p--; p >= protostr && my_isblank(*p); --p)
123 ret = b_pp_c_handler(protostr, hdrfn, is_include,
137 static int get_regparm(char *dst, size_t dlen, char *p, int *retreg)
152 for (o = 0; o < dlen; i++) {
164 static const char *known_type_mod[] = {
174 static const char *known_ptr_types[] = {
189 "HIMC", // DWORD in mingw, ptr in wine..
208 "PMEMORY_BASIC_INFORMATION",
220 static const char *ignored_keywords[] = {
228 // returns ptr to char after type ends
229 static int typecmp(const char *n, const char *t)
231 for (; *t != 0; n++, t++) {
232 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
234 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
243 static const char *skip_type_mod(const char *n)
248 for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
249 len = strlen(known_type_mod[i]);
250 if (strncmp(n, known_type_mod[i], len) != 0)
252 if (!my_isblank(n[len]))
256 while (my_isblank(*n))
264 static int check_type(const char *name, struct parsed_type *type)
270 n = skip_type_mod(name);
272 for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
273 if (typecmp(n, known_ptr_types[i]))
280 if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
283 // assume single word
284 while (!my_isblank(*n) && !my_issep(*n))
289 while (my_isblank(*n))
300 type->name = strndup(name, ret);
301 if (IS(type->name, "__VALIST") || IS(type->name, "va_list"))
302 type->is_va_list = 1;
303 if (IS(type->name, "VOID"))
304 memcpy(type->name, "void", 4);
309 /* args are always expanded to 32bit */
310 static const char *map_reg(const char *reg)
312 const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
313 const char *regs_w[] = { "ax", "bx", "cx", "dx", "si", "di" };
314 const char *regs_b[] = { "al", "bl", "cl", "dl" };
317 for (i = 0; i < ARRAY_SIZE(regs_w); i++)
318 if (IS(reg, regs_w[i]))
321 for (i = 0; i < ARRAY_SIZE(regs_b); i++)
322 if (IS(reg, regs_b[i]))
328 static int check_struct_arg(struct parsed_proto_arg *arg)
330 if (IS(arg->type.name, "POINT"))
336 static int parse_protostr(char *protostr, struct parsed_proto *pp)
338 struct parsed_proto_arg *arg;
349 if (p[0] == '/' && p[1] == '/') {
350 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
354 // strip unneeded stuff
355 for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
356 if ((p1[0] == '/' && p1[1] == '*')
357 || (p1[0] == '*' && p1[1] == '/'))
361 if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
366 for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
367 l = strlen(ignored_keywords[i]);
368 if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
369 p = sskip(p + l + 1);
372 ret = check_type(p, &pp->ret_type);
374 printf("%s:%d:%zd: unhandled return in '%s'\n",
375 hdrfn, hdrfline, (p - protostr) + 1, protostr);
380 if (!strncmp(p, "noreturn ", 9)) {
385 if (!strchr(p, ')')) {
386 p = next_idt(buf, sizeof(buf), p);
389 printf("%s:%d:%zd: var name missing\n",
390 hdrfn, hdrfline, (p - protostr) + 1);
393 strcpy(pp->name, buf);
398 pp->ret_type.is_array = 1;
410 p = next_word(cconv, sizeof(cconv), p);
413 printf("%s:%d:%zd: cconv missing\n",
414 hdrfn, hdrfline, (p - protostr) + 1);
417 if (IS(cconv, "__cdecl"))
419 else if (IS(cconv, "__stdcall"))
421 else if (IS(cconv, "__fastcall")) {
423 pp->is_stdcall = 1; // sort of..
425 else if (IS(cconv, "__thiscall"))
427 else if (IS(cconv, "__userpurge"))
428 pp->is_stdcall = 1; // IDA
429 else if (IS(cconv, "__usercall"))
430 pp->is_stdcall = 0; // IDA
431 else if (IS(cconv, "__userstack")) {
432 pp->is_stdcall = 0; // custom
433 pp->is_userstack = 1;
435 else if (IS(cconv, "WINAPI"))
438 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
439 hdrfn, hdrfline, (p - protostr) + 1, cconv);
445 printf("%s:%d:%zd: '*' expected\n",
446 hdrfn, hdrfline, (p - protostr) + 1);
450 // XXX: skipping extra asterisks, for now
456 p = next_idt(buf, sizeof(buf), p);
459 //printf("%s:%d:%zd: func name missing\n",
460 // hdrfn, hdrfline, (p - protostr) + 1);
463 strcpy(pp->name, buf);
465 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
467 if (!IS(regparm, "eax") && !IS(regparm, "ax")
468 && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
470 printf("%s:%d:%zd: bad regparm: %s\n",
471 hdrfn, hdrfline, (p - protostr) + 1, regparm);
480 // not really ret_type is array, but ohwell
481 pp->ret_type.is_array = 1;
482 p = strchr(p + 1, ']');
484 printf("%s:%d:%zd: ']' expected\n",
485 hdrfn, hdrfline, (p - protostr) + 1);
491 printf("%s:%d:%zd: ')' expected\n",
492 hdrfn, hdrfline, (p - protostr) + 1);
499 printf("%s:%d:%zd: '(' expected, got '%c'\n",
500 hdrfn, hdrfline, (p - protostr) + 1, *p);
507 if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
508 && *sskip(p + 4) == ')')
519 printf("%s:%d:%zd: ',' expected\n",
520 hdrfn, hdrfline, (p - protostr) + 1);
526 if (!strncmp(p, "...", 3)) {
533 printf("%s:%d:%zd: ')' expected\n",
534 hdrfn, hdrfline, (p - protostr) + 1);
538 arg = &pp->arg[xarg];
542 ret = check_type(p, &arg->type);
544 printf("%s:%d:%zd: unhandled type for arg%d\n",
545 hdrfn, hdrfline, (p - protostr) + 1, xarg);
552 arg->fptr = calloc(1, sizeof(*arg->fptr));
553 ret = parse_protostr(p1, arg->fptr);
555 printf("%s:%d:%zd: funcarg parse failed\n",
556 hdrfn, hdrfline, p1 - protostr);
559 arg->fptr->is_arg = 1;
560 // we don't use actual names right now..
561 snprintf(arg->fptr->name,
562 sizeof(arg->fptr->name), "a%d", xarg);
563 // we'll treat it as void * for non-calls
564 arg->type.name = strdup("void *");
565 arg->type.is_ptr = 1;
570 p = next_idt(buf, sizeof(buf), p);
574 printf("%s:%d:%zd: idt missing for arg%d\n",
575 hdrfn, hdrfline, (p - protostr) + 1, xarg);
581 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
586 arg->reg = strdup(map_reg(regparm));
587 arg->type.is_retreg = is_retreg;
588 pp->has_retreg |= is_retreg;
591 if (strstr(arg->type.name, "int64")
592 || IS(arg->type.name, "double"))
595 free(arg->type.name);
596 arg->type.name = strdup("int");
597 pp_copy_arg(&pp->arg[xarg], arg);
601 ret = check_struct_arg(arg);
603 pp->has_structarg = 1;
604 arg->type.is_struct = 1;
605 free(arg->type.name);
606 arg->type.name = strdup("int");
607 for (l = 0; l < ret; l++) {
608 pp_copy_arg(&pp->arg[xarg], arg);
614 if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
615 if (pp->arg[0].reg != NULL) {
616 printf("%s:%d: %s with arg1 spec %s?\n",
617 hdrfn, hdrfline, cconv, pp->arg[0].reg);
619 pp->arg[0].reg = strdup("ecx");
622 if (xarg > 1 && IS(cconv, "__fastcall")) {
623 if (pp->arg[1].reg != NULL) {
624 printf("%s:%d: %s with arg2 spec %s?\n",
625 hdrfn, hdrfline, cconv, pp->arg[1].reg);
627 pp->arg[1].reg = strdup("edx");
632 for (i = 0; i < pp->argc; i++) {
633 if (pp->arg[i].reg == NULL)
639 if (pp->argc == 1 && pp->arg[0].reg != NULL
640 && IS(pp->arg[0].reg, "ecx"))
644 else if (pp->argc_reg == 2
645 && pp->arg[0].reg != NULL && IS(pp->arg[0].reg, "ecx")
646 && pp->arg[1].reg != NULL && IS(pp->arg[1].reg, "edx"))
651 if (pp->is_vararg && (pp->is_stdcall || pp->is_fastcall)) {
652 printf("%s:%d: vararg %s?\n", hdrfn, hdrfline, cconv);
659 static int pp_name_cmp(const void *p1, const void *p2)
661 const struct parsed_proto *pp1 = p1, *pp2 = p2;
662 return strcmp(pp1->name, pp2->name);
665 static struct parsed_proto *pp_cache;
666 static int pp_cache_size;
667 static int pp_cache_alloc;
669 static int b_pp_c_handler(char *proto, const char *fname,
670 int is_include, int is_osinc, int is_cinc)
674 if (pp_cache_size >= pp_cache_alloc) {
675 pp_cache_alloc = pp_cache_alloc * 2 + 64;
676 pp_cache = realloc(pp_cache, pp_cache_alloc
677 * sizeof(pp_cache[0]));
678 my_assert_not(pp_cache, NULL);
679 memset(pp_cache + pp_cache_size, 0,
680 (pp_cache_alloc - pp_cache_size)
681 * sizeof(pp_cache[0]));
684 ret = parse_protostr(proto, &pp_cache[pp_cache_size]);
688 pp_cache[pp_cache_size].is_include = is_include;
689 pp_cache[pp_cache_size].is_osinc = is_osinc;
690 pp_cache[pp_cache_size].is_cinc = is_cinc;
695 static void build_pp_cache(FILE *fhdr)
703 ret = do_protostrs(fhdr, hdrfn, 0);
707 qsort(pp_cache, pp_cache_size, sizeof(pp_cache[0]), pp_name_cmp);
708 fseek(fhdr, pos, SEEK_SET);
711 static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
714 const struct parsed_proto *pp_ret;
715 struct parsed_proto pp_search;
718 if (pp_cache == NULL)
719 build_pp_cache(fhdr);
721 if (sym[0] == '_') // && strncmp(fname, "stdc", 4) == 0)
724 strcpy(pp_search.name, sym);
725 p = strchr(pp_search.name, '@');
729 pp_ret = bsearch(&pp_search, pp_cache, pp_cache_size,
730 sizeof(pp_cache[0]), pp_name_cmp);
731 if (pp_ret == NULL && !quiet)
732 printf("%s: sym '%s' is missing\n", hdrfn, sym);
737 static void pp_copy_arg(struct parsed_proto_arg *d,
738 const struct parsed_proto_arg *s)
740 memcpy(d, s, sizeof(*d));
742 if (s->reg != NULL) {
743 d->reg = strdup(s->reg);
744 my_assert_not(d->reg, NULL);
746 if (s->type.name != NULL) {
747 d->type.name = strdup(s->type.name);
748 my_assert_not(d->type.name, NULL);
750 if (s->fptr != NULL) {
751 d->fptr = malloc(sizeof(*d->fptr));
752 my_assert_not(d->fptr, NULL);
753 memcpy(d->fptr, s->fptr, sizeof(*d->fptr));
757 struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
759 struct parsed_proto *pp;
762 pp = malloc(sizeof(*pp));
763 my_assert_not(pp, NULL);
764 memcpy(pp, pp_c, sizeof(*pp)); // lazy..
766 // do the actual deep copy..
767 for (i = 0; i < pp_c->argc; i++)
768 pp_copy_arg(&pp->arg[i], &pp_c->arg[i]);
769 if (pp_c->ret_type.name != NULL)
770 pp->ret_type.name = strdup(pp_c->ret_type.name);
776 static inline int pp_cmp_func(const struct parsed_proto *pp1,
777 const struct parsed_proto *pp2)
781 if (pp1->argc != pp2->argc || pp1->argc_reg != pp2->argc_reg)
784 for (i = 0; i < pp1->argc; i++) {
785 if ((pp1->arg[i].reg != NULL) != (pp2->arg[i].reg != NULL))
788 if ((pp1->arg[i].reg != NULL)
789 && !IS(pp1->arg[i].reg, pp2->arg[i].reg))
799 static inline void pp_print(char *buf, size_t buf_size,
800 const struct parsed_proto *pp)
805 snprintf(buf, buf_size, "%s %s(", pp->ret_type.name, pp->name);
808 for (i = 0; i < pp->argc_reg; i++) {
809 snprintf(buf + l, buf_size - l, "%s%s",
810 i == 0 ? "" : ", ", pp->arg[i].reg);
813 if (pp->argc_stack > 0) {
814 snprintf(buf + l, buf_size - l, "%s{%d stack}",
815 i == 0 ? "" : ", ", pp->argc_stack);
818 snprintf(buf + l, buf_size - l, ")");
821 static inline void proto_release(struct parsed_proto *pp)
825 for (i = 0; i < pp->argc; i++) {
826 if (pp->arg[i].reg != NULL)
827 free(pp->arg[i].reg);
828 if (pp->arg[i].type.name != NULL)
829 free(pp->arg[i].type.name);
830 if (pp->arg[i].fptr != NULL)
831 free(pp->arg[i].fptr);
833 if (pp->ret_type.name != NULL)
834 free(pp->ret_type.name);