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;
18 unsigned int is_64bit:1;
19 unsigned int is_float:1; // float, double
22 struct parsed_proto_arg {
24 struct parsed_type type;
25 struct parsed_proto *pp; // fptr or struct
26 unsigned int is_saved:1; // not set here, for tool use
34 struct parsed_type ret_type;
35 struct parsed_type type;
37 struct parsed_proto_arg arg[32];
41 unsigned int is_func:1;
42 unsigned int is_stdcall:1;
43 unsigned int is_fastcall:1;
44 unsigned int is_vararg:1; // vararg func
45 unsigned int is_fptr:1;
46 unsigned int is_import:1; // data import
47 unsigned int is_noreturn:1;
48 unsigned int is_unresolved:1;
49 unsigned int is_guessed:1; // for extra checking
50 unsigned int is_userstack:1;
51 unsigned int is_include:1; // not from top-level header
52 unsigned int is_osinc:1; // OS/system library func
53 unsigned int is_cinc:1; // crt library func
54 unsigned int is_arg:1; // declared in some func arg
55 unsigned int has_structarg:1;
56 unsigned int has_retreg:1;
59 struct parsed_struct {
63 struct parsed_proto pp;
68 static const char *hdrfn;
69 static int hdrfline = 0;
71 static void pp_copy_arg(struct parsed_proto_arg *d,
72 const struct parsed_proto_arg *s);
74 static int b_pp_c_handler(char *proto, const char *fname,
75 int is_include, int is_osinc, int is_cinc);
76 static int struct_handler(FILE *fhdr, char *proto, int *line);
78 static int do_protostrs(FILE *fhdr, const char *fname, int is_include)
80 const char *finc_name;
81 const char *hdrfn_saved;
95 is_cinc = strstr(fname, "stdc.hlist") != NULL;
96 is_osinc = is_cinc || strstr(fname, "win32.hlist") != NULL;
98 while (fgets(protostr, sizeof(protostr), fhdr))
101 if (strncmp(protostr, "//#include ", 11) == 0) {
102 finc_name = protostr + 11;
103 p = strpbrk(finc_name, "\r\n ");
108 p = strrchr(hdrfn_saved, '/');
110 memcpy(path, hdrfn_saved,
111 p - hdrfn_saved + 1);
112 path[p - hdrfn_saved + 1] = 0;
114 snprintf(fname_inc, sizeof(fname_inc), "%s%s",
116 finc = fopen(fname_inc, "r");
118 printf("%s:%d: can't open '%s'\n",
119 fname_inc, line, finc_name);
122 ret = do_protostrs(finc, finc_name, 1);
128 if (strncmp(sskip(protostr), "//", 2) == 0)
131 p = protostr + strlen(protostr);
132 for (p--; p >= protostr && my_isblank(*p); --p)
139 if (!strncmp(protostr, "struct", 6)
140 && strchr(protostr, '{') != NULL)
141 ret = struct_handler(fhdr, protostr, &line);
143 ret = b_pp_c_handler(protostr, hdrfn,
144 is_include, is_osinc, is_cinc);
157 static int get_regparm(char *dst, size_t dlen, char *p, int *retreg)
172 for (o = 0; o < dlen; i++) {
184 static const char *known_type_mod[] = {
193 static const char *known_ptr_types[] = {
210 "HIMC", // DWORD in mingw, ptr in wine..
233 "PMEMORY_BASIC_INFORMATION",
244 "SERVICE_STATUS_HANDLE",
253 static const char *ignored_keywords[] = {
261 static int typecmp(const char *n, const char *t)
263 for (; *t != 0; n++, t++) {
264 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
266 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
275 static const char *skip_type_mod(const char *n)
280 for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
281 len = strlen(known_type_mod[i]);
282 if (strncmp(n, known_type_mod[i], len) != 0)
284 if (!my_isblank(n[len]))
288 while (my_isblank(*n))
296 static int check_type(const char *name, struct parsed_type *type)
302 n = skip_type_mod(name);
304 if (!strncmp(n, "struct", 6) && my_isblank(n[6])) {
308 while (my_isblank(*n))
312 for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
313 if (typecmp(n, known_ptr_types[i]))
320 if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
323 // assume single word
324 while (!my_isblank(*n) && !my_issep(*n))
329 while (my_isblank(*n))
340 type->name = strndup(name, ret);
341 if (IS(type->name, "__VALIST") || IS(type->name, "va_list"))
342 type->is_va_list = 1;
343 if (IS(type->name, "VOID"))
344 memcpy(type->name, "void", 4);
349 /* args are always expanded to 32bit */
350 static const char *map_reg(const char *reg)
352 const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
353 const char *regs_w[] = { "ax", "bx", "cx", "dx", "si", "di" };
354 const char *regs_b[] = { "al", "bl", "cl", "dl" };
357 for (i = 0; i < ARRAY_SIZE(regs_w); i++)
358 if (IS(reg, regs_w[i]))
361 for (i = 0; i < ARRAY_SIZE(regs_b); i++)
362 if (IS(reg, regs_b[i]))
368 static int check_struct_arg(struct parsed_proto_arg *arg)
370 if (IS(arg->type.name, "POINT"))
376 static int parse_protostr(char *protostr, struct parsed_proto *pp);
378 static int parse_arg(char **p_, struct parsed_proto_arg *arg, int xarg)
385 arg->pp = calloc(1, sizeof(*arg->pp));
386 my_assert_not(arg->pp, NULL);
391 pe = strpbrk(pe, ",()");
394 if (*pe == ',' || *pe == ')')
396 pe = strchr(pe, ')');
402 if (pe - p > sizeof(buf) - 1)
404 memcpy(buf, p, pe - p);
407 ret = parse_protostr(buf, arg->pp);
411 if (IS_START(arg->pp->name, "guess"))
412 arg->pp->is_guessed = 1;
414 // we don't use actual names right now...
415 snprintf(arg->pp->name, sizeof(arg->pp->name), "a%d", xarg);
417 if (!arg->type.is_struct)
418 // we'll treat it as void * for non-calls
419 arg->type.name = strdup("void *");
420 arg->type.is_ptr = 1;
427 static int parse_protostr(char *protostr, struct parsed_proto *pp)
429 struct parsed_proto_arg *arg;
440 if (p[0] == '/' && p[1] == '/') {
441 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
445 // allow start of line comment
446 if (p[0] == '/' && p[1] == '*') {
447 p = strstr(p + 2, "*/");
449 printf("%s:%d: multiline comments unsupported\n",
456 // we need remaining hints in comments, so strip / *
457 for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
458 if ((p1[0] == '/' && p1[1] == '*')
459 || (p1[0] == '*' && p1[1] == '/'))
463 if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
468 for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
469 l = strlen(ignored_keywords[i]);
470 if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
471 p = sskip(p + l + 1);
474 if (IS_START(p, "DECL_IMPORT ")) {
479 ret = check_type(p, &pp->ret_type);
481 printf("%s:%d:%zd: unhandled return in '%s'\n",
482 hdrfn, hdrfline, (p - protostr) + 1, protostr);
487 if (!strncmp(p, "noreturn ", 9)) {
492 if (!strchr(p, ')')) {
493 p = next_idt(buf, sizeof(buf), p);
495 if (!pp->is_arg && buf[0] == 0) {
496 printf("%s:%d:%zd: var name is missing\n",
497 hdrfn, hdrfline, (p - protostr) + 1);
500 strcpy(pp->name, buf);
505 pp->ret_type.is_array = 1;
517 p = next_word(cconv, sizeof(cconv), p);
520 printf("%s:%d:%zd: cconv missing\n",
521 hdrfn, hdrfline, (p - protostr) + 1);
524 if (IS(cconv, "__cdecl"))
526 else if (IS(cconv, "__stdcall"))
528 else if (IS(cconv, "__fastcall")) {
530 pp->is_stdcall = 1; // sort of..
532 else if (IS(cconv, "__thiscall"))
534 else if (IS(cconv, "__userpurge"))
535 pp->is_stdcall = 1; // IDA
536 else if (IS(cconv, "__usercall"))
537 pp->is_stdcall = 0; // IDA
538 else if (IS(cconv, "__userstack")) {
539 pp->is_stdcall = 0; // custom
540 pp->is_userstack = 1;
542 else if (IS(cconv, "WINAPI") || IS(cconv, "PASCAL"))
545 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
546 hdrfn, hdrfline, (p - protostr) + 1, cconv);
552 printf("%s:%d:%zd: '*' expected\n",
553 hdrfn, hdrfline, (p - protostr) + 1);
557 // XXX: skipping extra asterisks, for now
563 p = next_idt(buf, sizeof(buf), p);
566 //printf("%s:%d:%zd: func name missing\n",
567 // hdrfn, hdrfline, (p - protostr) + 1);
570 strcpy(pp->name, buf);
572 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
574 if (!IS(regparm, "eax") && !IS(regparm, "ax")
575 && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
577 printf("%s:%d:%zd: bad regparm: %s\n",
578 hdrfn, hdrfline, (p - protostr) + 1, regparm);
587 // not really ret_type is array, but ohwell
588 pp->ret_type.is_array = 1;
589 p = strchr(p + 1, ']');
591 printf("%s:%d:%zd: ']' expected\n",
592 hdrfn, hdrfline, (p - protostr) + 1);
598 printf("%s:%d:%zd: ')' expected\n",
599 hdrfn, hdrfline, (p - protostr) + 1);
606 printf("%s:%d:%zd: '(' expected, got '%c'\n",
607 hdrfn, hdrfline, (p - protostr) + 1, *p);
614 if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
615 && *sskip(p + 4) == ')')
626 printf("%s:%d:%zd: ',' expected\n",
627 hdrfn, hdrfline, (p - protostr) + 1);
633 if (!strncmp(p, "...", 3)) {
640 printf("%s:%d:%zd: ')' expected\n",
641 hdrfn, hdrfline, (p - protostr) + 1);
645 if (xarg >= ARRAY_SIZE(pp->arg)) {
646 printf("%s:%d:%zd: too many args\n",
647 hdrfn, hdrfline, (p - protostr) + 1);
651 arg = &pp->arg[xarg];
655 ret = check_type(p, &arg->type);
657 printf("%s:%d:%zd: unhandled type for arg%d\n",
658 hdrfn, hdrfline, (p - protostr) + 1, xarg);
663 if (*p == '(' || arg->type.is_struct) {
664 // func ptr or struct
665 ret = parse_arg(&p1, arg, xarg);
667 printf("%s:%d:%zd: funcarg parse failed\n",
668 hdrfn, hdrfline, p1 - protostr);
674 p = next_idt(buf, sizeof(buf), p);
678 printf("%s:%d:%zd: idt missing for arg%d\n",
679 hdrfn, hdrfline, (p - protostr) + 1, xarg);
685 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
690 arg->reg = strdup(map_reg(regparm));
691 arg->type.is_retreg = is_retreg;
692 pp->has_retreg |= is_retreg;
695 if (IS(arg->type.name, "float")
696 || IS(arg->type.name, "double"))
698 arg->type.is_float = 1;
701 if (!arg->type.is_ptr && (strstr(arg->type.name, "int64")
702 || IS(arg->type.name, "double")))
704 arg->type.is_64bit = 1;
706 pp_copy_arg(&pp->arg[xarg], arg);
707 arg = &pp->arg[xarg];
709 free(arg->type.name);
710 arg->type.name = strdup("dummy");
713 ret = check_struct_arg(arg);
715 pp->has_structarg = 1;
716 arg->type.is_struct = 1;
717 free(arg->type.name);
718 arg->type.name = strdup("int");
719 for (l = 0; l < ret; l++) {
720 pp_copy_arg(&pp->arg[xarg], arg);
726 if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
727 if (pp->arg[0].reg != NULL) {
728 printf("%s:%d: %s with arg1 spec %s?\n",
729 hdrfn, hdrfline, cconv, pp->arg[0].reg);
731 pp->arg[0].reg = strdup("ecx");
734 if (xarg > 1 && IS(cconv, "__fastcall")) {
735 if (pp->arg[1].reg != NULL) {
736 printf("%s:%d: %s with arg2 spec %s?\n",
737 hdrfn, hdrfline, cconv, pp->arg[1].reg);
739 pp->arg[1].reg = strdup("edx");
744 for (i = 0; i < pp->argc; i++) {
745 if (pp->arg[i].reg == NULL)
751 if (pp->argc == 1 && pp->arg[0].reg != NULL
752 && IS(pp->arg[0].reg, "ecx"))
756 else if (pp->argc_reg == 2
757 && pp->arg[0].reg != NULL && IS(pp->arg[0].reg, "ecx")
758 && pp->arg[1].reg != NULL && IS(pp->arg[1].reg, "edx"))
763 if (pp->is_vararg && (pp->is_stdcall || pp->is_fastcall)) {
764 printf("%s:%d: vararg %s?\n", hdrfn, hdrfline, cconv);
771 static int pp_name_cmp(const void *p1, const void *p2)
773 const struct parsed_proto *pp1 = p1, *pp2 = p2;
774 return strcmp(pp1->name, pp2->name);
777 static int ps_name_cmp(const void *p1, const void *p2)
779 const struct parsed_struct *ps1 = p1, *ps2 = p2;
780 return strcmp(ps1->name, ps2->name);
783 // parsed struct cache
784 static struct parsed_struct *ps_cache;
785 static int ps_cache_size;
786 static int ps_cache_alloc;
788 static int struct_handler(FILE *fhdr, char *proto, int *line)
790 struct parsed_struct *ps;
796 if (ps_cache_size >= ps_cache_alloc) {
797 ps_cache_alloc = ps_cache_alloc * 2 + 64;
798 ps_cache = realloc(ps_cache, ps_cache_alloc
799 * sizeof(ps_cache[0]));
800 my_assert_not(ps_cache, NULL);
801 memset(ps_cache + ps_cache_size, 0,
802 (ps_cache_alloc - ps_cache_size)
803 * sizeof(ps_cache[0]));
806 ps = &ps_cache[ps_cache_size++];
807 ret = sscanf(proto, "struct %255s {", ps->name);
809 printf("%s:%d: struct parse failed\n", hdrfn, *line);
813 while (fgets(lstr, sizeof(lstr), fhdr))
818 if (p[0] == '/' && p[1] == '/')
823 if (m >= ARRAY_SIZE(ps->members)) {
824 printf("%s:%d: too many struct members\n",
830 ret = parse_protostr(p, &ps->members[m].pp);
832 printf("%s:%d: struct member #%d/%02x "
833 "doesn't parse\n", hdrfn, *line,
837 ps->members[m].offset = offset;
842 ps->member_count = m;
847 // parsed proto cache
848 static struct parsed_proto *pp_cache;
849 static int pp_cache_size;
850 static int pp_cache_alloc;
852 static int b_pp_c_handler(char *proto, const char *fname,
853 int is_include, int is_osinc, int is_cinc)
857 if (pp_cache_size >= pp_cache_alloc) {
858 pp_cache_alloc = pp_cache_alloc * 2 + 64;
859 pp_cache = realloc(pp_cache, pp_cache_alloc
860 * sizeof(pp_cache[0]));
861 my_assert_not(pp_cache, NULL);
862 memset(pp_cache + pp_cache_size, 0,
863 (pp_cache_alloc - pp_cache_size)
864 * sizeof(pp_cache[0]));
867 ret = parse_protostr(proto, &pp_cache[pp_cache_size]);
871 pp_cache[pp_cache_size].is_include = is_include;
872 pp_cache[pp_cache_size].is_osinc = is_osinc;
873 pp_cache[pp_cache_size].is_cinc = is_cinc;
878 static void build_caches(FILE *fhdr)
886 ret = do_protostrs(fhdr, hdrfn, 0);
890 qsort(pp_cache, pp_cache_size, sizeof(pp_cache[0]), pp_name_cmp);
891 qsort(ps_cache, ps_cache_size, sizeof(ps_cache[0]), ps_name_cmp);
892 fseek(fhdr, pos, SEEK_SET);
895 static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
898 const struct parsed_proto *pp_ret;
899 struct parsed_proto pp_search;
902 if (pp_cache == NULL)
906 if (sym[0] == '_' && !IS_START(sym, "__W"))
909 strcpy(pp_search.name, sym);
910 p = strchr(pp_search.name, '@');
914 pp_ret = bsearch(&pp_search, pp_cache, pp_cache_size,
915 sizeof(pp_cache[0]), pp_name_cmp);
916 if (pp_ret == NULL && !quiet)
917 printf("%s: sym '%s' is missing\n", hdrfn, sym);
922 static const struct parsed_proto *proto_lookup_struct(FILE *fhdr,
923 const char *type, int offset)
925 struct parsed_struct ps_search, *ps;
928 if (pp_cache == NULL)
930 if (ps_cache_size == 0)
933 while (my_isblank(*type))
935 if (!strncmp(type, "struct", 6) && my_isblank(type[6]))
938 if (sscanf(type, "%255s", ps_search.name) != 1)
941 ps = bsearch(&ps_search, ps_cache, ps_cache_size,
942 sizeof(ps_cache[0]), ps_name_cmp);
944 printf("%s: struct '%s' is missing\n",
945 hdrfn, ps_search.name);
949 for (m = 0; m < ps->member_count; m++) {
950 if (ps->members[m].offset == offset)
951 return &ps->members[m].pp;
957 static void pp_copy_arg(struct parsed_proto_arg *d,
958 const struct parsed_proto_arg *s)
960 memcpy(d, s, sizeof(*d));
962 if (s->reg != NULL) {
963 d->reg = strdup(s->reg);
964 my_assert_not(d->reg, NULL);
966 if (s->type.name != NULL) {
967 d->type.name = strdup(s->type.name);
968 my_assert_not(d->type.name, NULL);
971 d->pp = malloc(sizeof(*d->pp));
972 my_assert_not(d->pp, NULL);
973 memcpy(d->pp, s->pp, sizeof(*d->pp));
977 struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
979 struct parsed_proto *pp;
982 pp = malloc(sizeof(*pp));
983 my_assert_not(pp, NULL);
984 memcpy(pp, pp_c, sizeof(*pp)); // lazy..
986 // do the actual deep copy..
987 for (i = 0; i < pp_c->argc; i++)
988 pp_copy_arg(&pp->arg[i], &pp_c->arg[i]);
989 if (pp_c->ret_type.name != NULL)
990 pp->ret_type.name = strdup(pp_c->ret_type.name);
996 static inline int pp_cmp_func(const struct parsed_proto *pp1,
997 const struct parsed_proto *pp2)
1001 if (pp1->argc != pp2->argc || pp1->argc_reg != pp2->argc_reg)
1003 if (pp1->is_stdcall != pp2->is_stdcall)
1006 // because of poor void return detection, return is not
1007 // checked for now to avoid heaps of false positives
1009 for (i = 0; i < pp1->argc; i++) {
1010 if ((pp1->arg[i].reg != NULL) != (pp2->arg[i].reg != NULL))
1013 if ((pp1->arg[i].reg != NULL)
1014 && !IS(pp1->arg[i].reg, pp2->arg[i].reg))
1023 static inline int pp_compatible_func(
1024 const struct parsed_proto *pp_site,
1025 const struct parsed_proto *pp_callee)
1027 if (pp_cmp_func(pp_site, pp_callee) == 0)
1030 if (pp_site->argc_stack == 0 && pp_site->is_fastcall
1031 && pp_callee->argc_stack == 0
1032 && (pp_callee->is_fastcall || pp_callee->argc_reg == 0)
1033 && pp_site->argc_reg > pp_callee->argc_reg)
1034 /* fascall compatible callee doesn't use all args -> ok */
1040 static inline void pp_print(char *buf, size_t buf_size,
1041 const struct parsed_proto *pp)
1046 snprintf(buf, buf_size, "%s %s(", pp->ret_type.name, pp->name);
1049 for (i = 0; i < pp->argc_reg; i++) {
1050 snprintf(buf + l, buf_size - l, "%s%s",
1051 i == 0 ? "" : ", ", pp->arg[i].reg);
1054 if (pp->argc_stack > 0) {
1055 snprintf(buf + l, buf_size - l, "%s{%d stack}",
1056 i == 0 ? "" : ", ", pp->argc_stack);
1059 snprintf(buf + l, buf_size - l, ")");
1062 static inline void proto_release(struct parsed_proto *pp)
1066 for (i = 0; i < pp->argc; i++) {
1067 free(pp->arg[i].reg);
1068 free(pp->arg[i].type.name);
1069 free(pp->arg[i].pp);
1070 free(pp->arg[i].push_refs);
1072 if (pp->ret_type.name != NULL)
1073 free(pp->ret_type.name);
1076 (void)proto_lookup_struct;