translate: improve call arg collection
[ia32rtools.git] / tools / protoparse.h
... / ...
CommitLineData
1/*
2 * ia32rtools
3 * (C) notaz, 2013,2014
4 *
5 * This work is licensed under the terms of 3-clause BSD license.
6 * See COPYING file in the top-level directory.
7 */
8
9struct parsed_proto;
10
11struct parsed_type {
12 char *name;
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
20};
21
22struct parsed_proto_arg {
23 char *reg;
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
27 void **push_refs;
28 int push_ref_cnt;
29};
30
31struct parsed_proto {
32 char name[256];
33 union {
34 struct parsed_type ret_type;
35 struct parsed_type type;
36 };
37 struct parsed_proto_arg arg[32];
38 int argc;
39 int argc_stack;
40 int argc_reg;
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;
57};
58
59struct parsed_struct {
60 char name[256];
61 struct {
62 int offset;
63 struct parsed_proto pp;
64 } members[64];
65 int member_count;
66};
67
68static const char *hdrfn;
69static int hdrfline = 0;
70
71static void pp_copy_arg(struct parsed_proto_arg *d,
72 const struct parsed_proto_arg *s);
73
74static int b_pp_c_handler(char *proto, const char *fname,
75 int is_include, int is_osinc, int is_cinc);
76static int struct_handler(FILE *fhdr, char *proto, int *line);
77
78static int do_protostrs(FILE *fhdr, const char *fname, int is_include)
79{
80 const char *finc_name;
81 const char *hdrfn_saved;
82 char protostr[256];
83 char path[256];
84 char fname_inc[256];
85 int is_osinc;
86 int is_cinc;
87 FILE *finc;
88 int line = 0;
89 int ret;
90 char *p;
91
92 hdrfn_saved = hdrfn;
93 hdrfn = fname;
94
95 is_cinc = strstr(fname, "stdc.hlist") != NULL;
96 is_osinc = is_cinc || strstr(fname, "win32.hlist") != NULL;
97
98 while (fgets(protostr, sizeof(protostr), fhdr))
99 {
100 line++;
101 if (strncmp(protostr, "//#include ", 11) == 0) {
102 finc_name = protostr + 11;
103 p = strpbrk(finc_name, "\r\n ");
104 if (p != NULL)
105 *p = 0;
106
107 path[0] = 0;
108 p = strrchr(hdrfn_saved, '/');
109 if (p) {
110 memcpy(path, hdrfn_saved,
111 p - hdrfn_saved + 1);
112 path[p - hdrfn_saved + 1] = 0;
113 }
114 snprintf(fname_inc, sizeof(fname_inc), "%s%s",
115 path, finc_name);
116 finc = fopen(fname_inc, "r");
117 if (finc == NULL) {
118 printf("%s:%d: can't open '%s'\n",
119 fname_inc, line, finc_name);
120 continue;
121 }
122 ret = do_protostrs(finc, finc_name, 1);
123 fclose(finc);
124 if (ret < 0)
125 break;
126 continue;
127 }
128 if (strncmp(sskip(protostr), "//", 2) == 0)
129 continue;
130
131 p = protostr + strlen(protostr);
132 for (p--; p >= protostr && my_isblank(*p); --p)
133 *p = 0;
134 if (p < protostr)
135 continue;
136
137 hdrfline = line;
138
139 if (!strncmp(protostr, "struct", 6)
140 && strchr(protostr, '{') != NULL)
141 ret = struct_handler(fhdr, protostr, &line);
142 else
143 ret = b_pp_c_handler(protostr, hdrfn,
144 is_include, is_osinc, is_cinc);
145 if (ret < 0)
146 break;
147 }
148
149 hdrfn = hdrfn_saved;
150
151 if (feof(fhdr))
152 return 0;
153
154 return -1;
155}
156
157static int get_regparm(char *dst, size_t dlen, char *p, int *retreg)
158{
159 int i = 0, o;
160
161 *retreg = 0;
162
163 if (*p != '<')
164 return 0;
165
166 i++;
167 if (p[i] == '*') {
168 *retreg = 1;
169 i++;
170 }
171
172 for (o = 0; o < dlen; i++) {
173 if (p[i] == 0)
174 return 0;
175 if (p[i] == '>')
176 break;
177 dst[o++] = p[i];
178 }
179 dst[o] = 0;
180 return i + 1;
181}
182
183// hmh..
184static const char *known_type_mod[] = {
185 "const",
186 "signed",
187 "unsigned",
188 "enum",
189 "CONST",
190 "volatile",
191};
192
193static const char *known_ptr_types[] = {
194 "FARPROC",
195 "WNDPROC",
196 "LINECALLBACK",
197 "HACCEL",
198 "HANDLE",
199 "HBITMAP",
200 "HBRUSH",
201 "HCALL",
202 "HCURSOR",
203 "HDC",
204 "HFONT",
205 "HGDIOBJ",
206 "HGLOBAL",
207 "HHOOK",
208 "HICON",
209 "HINSTANCE",
210 "HIMC", // DWORD in mingw, ptr in wine..
211 "HLINE",
212 "HLINEAPP",
213 "HLOCAL",
214 "HMODULE",
215 "HPALETTE",
216 "HRGN",
217 "HRSRC",
218 "HKEY",
219 "HMENU",
220 "HMONITOR",
221 "HWAVEOUT",
222 "HWND",
223 "PAPPBARDATA",
224 "PBYTE",
225 "PCRITICAL_SECTION",
226 "PDEVMODEA",
227 "PDWORD",
228 "PFILETIME",
229 "PLARGE_INTEGER",
230 "PHANDLE",
231 "PHKEY",
232 "PLONG",
233 "PMEMORY_BASIC_INFORMATION",
234 "PUINT",
235 "PULARGE_INTEGER",
236 "PULONG_PTR",
237 "PVOID",
238 "PCVOID",
239 "PWORD",
240 "REFCLSID",
241 "REFGUID",
242 "REFIID",
243 "SC_HANDLE",
244 "SERVICE_STATUS_HANDLE",
245 "HOOKPROC",
246 "DLGPROC",
247 "TIMERPROC",
248 "WNDENUMPROC",
249 "va_list",
250 "__VALIST",
251};
252
253static const char *ignored_keywords[] = {
254 "extern",
255 "WINBASEAPI",
256 "WINUSERAPI",
257 "WINGDIAPI",
258 "WINADVAPI",
259};
260
261static int typecmp(const char *n, const char *t)
262{
263 for (; *t != 0; n++, t++) {
264 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
265 n++;
266 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
267 t++;
268 if (*n != *t)
269 return *n - *t;
270 }
271
272 return 0;
273}
274
275static const char *skip_type_mod(const char *n)
276{
277 int len;
278 int i;
279
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)
283 continue;
284 if (!my_isblank(n[len]))
285 continue;
286
287 n += len;
288 while (my_isblank(*n))
289 n++;
290 i = 0;
291 }
292
293 return n;
294}
295
296static int check_type(const char *name, struct parsed_type *type)
297{
298 const char *n, *n1;
299 int ret = -1;
300 int i;
301
302 n = skip_type_mod(name);
303
304 if (!strncmp(n, "struct", 6) && my_isblank(n[6])) {
305 type->is_struct = 1;
306
307 n += 6;
308 while (my_isblank(*n))
309 n++;
310 }
311
312 for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
313 if (typecmp(n, known_ptr_types[i]))
314 continue;
315
316 type->is_ptr = 1;
317 break;
318 }
319
320 if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
321 type->is_ptr = 1;
322
323 // assume single word
324 while (!my_isblank(*n) && !my_issep(*n))
325 n++;
326
327 while (1) {
328 n1 = n;
329 while (my_isblank(*n))
330 n++;
331 if (*n == '*') {
332 type->is_ptr = 1;
333 n++;
334 continue;
335 }
336 break;
337 }
338
339 ret = n1 - name;
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);
345
346 return ret;
347}
348
349/* args are always expanded to 32bit */
350static const char *map_reg(const char *reg)
351{
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" };
355 int i;
356
357 for (i = 0; i < ARRAY_SIZE(regs_w); i++)
358 if (IS(reg, regs_w[i]))
359 return regs_f[i];
360
361 for (i = 0; i < ARRAY_SIZE(regs_b); i++)
362 if (IS(reg, regs_b[i]))
363 return regs_f[i];
364
365 return reg;
366}
367
368static int check_struct_arg(struct parsed_proto_arg *arg)
369{
370 if (IS(arg->type.name, "POINT"))
371 return 2 - 1;
372
373 return 0;
374}
375
376static int parse_protostr(char *protostr, struct parsed_proto *pp);
377
378static int parse_arg(char **p_, struct parsed_proto_arg *arg, int xarg)
379{
380 char buf[256];
381 char *p = *p_;
382 char *pe;
383 int ret;
384
385 arg->pp = calloc(1, sizeof(*arg->pp));
386 my_assert_not(arg->pp, NULL);
387 arg->pp->is_arg = 1;
388
389 pe = p;
390 while (1) {
391 pe = strpbrk(pe, ",()");
392 if (pe == NULL)
393 return -1;
394 if (*pe == ',' || *pe == ')')
395 break;
396 pe = strchr(pe, ')');
397 if (pe == NULL)
398 return -1;
399 pe++;
400 }
401
402 if (pe - p > sizeof(buf) - 1)
403 return -1;
404 memcpy(buf, p, pe - p);
405 buf[pe - p] = 0;
406
407 ret = parse_protostr(buf, arg->pp);
408 if (ret < 0)
409 return -1;
410
411 if (IS_START(arg->pp->name, "guess"))
412 arg->pp->is_guessed = 1;
413
414 // we don't use actual names right now...
415 snprintf(arg->pp->name, sizeof(arg->pp->name), "a%d", xarg);
416
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;
421
422 p += ret;
423 *p_ = p;
424 return 0;
425}
426
427static int parse_protostr(char *protostr, struct parsed_proto *pp)
428{
429 struct parsed_proto_arg *arg;
430 char regparm[16];
431 char buf[256];
432 char cconv[32];
433 int is_retreg;
434 char *p, *p1;
435 int xarg = 0;
436 int i, l;
437 int ret;
438
439 p = sskip(protostr);
440 if (p[0] == '/' && p[1] == '/') {
441 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
442 p = sskip(p + 2);
443 }
444
445 // allow start of line comment
446 if (p[0] == '/' && p[1] == '*') {
447 p = strstr(p + 2, "*/");
448 if (p == NULL) {
449 printf("%s:%d: multiline comments unsupported\n",
450 hdrfn, hdrfline);
451 return -1;
452 }
453 p = sskip(p + 2);
454 }
455
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] == '/'))
460 p1[0] = p1[1] = ' ';
461 }
462
463 if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
464 pp->is_noreturn = 1;
465 p = sskip(p + 18);
466 }
467
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);
472 }
473
474 if (IS_START(p, "DECL_IMPORT ")) {
475 pp->is_import = 1;
476 p = sskip(p + 12);
477 }
478
479 ret = check_type(p, &pp->ret_type);
480 if (ret <= 0) {
481 printf("%s:%d:%zd: unhandled return in '%s'\n",
482 hdrfn, hdrfline, (p - protostr) + 1, protostr);
483 return -1;
484 }
485 p = sskip(p + ret);
486
487 if (!strncmp(p, "noreturn ", 9)) {
488 pp->is_noreturn = 1;
489 p = sskip(p + 9);
490 }
491
492 if (!strchr(p, ')')) {
493 p = next_idt(buf, sizeof(buf), p);
494 p = sskip(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);
498 return -1;
499 }
500 strcpy(pp->name, buf);
501
502 p1 = strchr(p, ']');
503 if (p1 != NULL) {
504 p = p1 + 1;
505 pp->ret_type.is_array = 1;
506 }
507 return p - protostr;
508 }
509
510 pp->is_func = 1;
511
512 if (*p == '(') {
513 pp->is_fptr = 1;
514 p = sskip(p + 1);
515 }
516
517 p = next_word(cconv, sizeof(cconv), p);
518 p = sskip(p);
519 if (cconv[0] == 0) {
520 printf("%s:%d:%zd: cconv missing\n",
521 hdrfn, hdrfline, (p - protostr) + 1);
522 return -1;
523 }
524 if (IS(cconv, "__cdecl"))
525 pp->is_stdcall = 0;
526 else if (IS(cconv, "__stdcall"))
527 pp->is_stdcall = 1;
528 else if (IS(cconv, "__fastcall")) {
529 pp->is_fastcall = 1;
530 pp->is_stdcall = 1; // sort of..
531 }
532 else if (IS(cconv, "__thiscall"))
533 pp->is_stdcall = 1;
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;
541 }
542 else if (IS(cconv, "WINAPI") || IS(cconv, "PASCAL"))
543 pp->is_stdcall = 1;
544 else {
545 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
546 hdrfn, hdrfline, (p - protostr) + 1, cconv);
547 return -1;
548 }
549
550 if (pp->is_fptr) {
551 if (*p != '*') {
552 printf("%s:%d:%zd: '*' expected\n",
553 hdrfn, hdrfline, (p - protostr) + 1);
554 return -1;
555 }
556 p++;
557 // XXX: skipping extra asterisks, for now
558 while (*p == '*')
559 p++;
560 p = sskip(p);
561 }
562
563 p = next_idt(buf, sizeof(buf), p);
564 p = sskip(p);
565 if (buf[0] == 0) {
566 //printf("%s:%d:%zd: func name missing\n",
567 // hdrfn, hdrfline, (p - protostr) + 1);
568 //return -1;
569 }
570 strcpy(pp->name, buf);
571
572 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
573 if (ret > 0) {
574 if (!IS(regparm, "eax") && !IS(regparm, "ax")
575 && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
576 {
577 printf("%s:%d:%zd: bad regparm: %s\n",
578 hdrfn, hdrfline, (p - protostr) + 1, regparm);
579 return -1;
580 }
581 p += ret;
582 p = sskip(p);
583 }
584
585 if (pp->is_fptr) {
586 if (*p == '[') {
587 // not really ret_type is array, but ohwell
588 pp->ret_type.is_array = 1;
589 p = strchr(p + 1, ']');
590 if (p == NULL) {
591 printf("%s:%d:%zd: ']' expected\n",
592 hdrfn, hdrfline, (p - protostr) + 1);
593 return -1;
594 }
595 p = sskip(p + 1);
596 }
597 if (*p != ')') {
598 printf("%s:%d:%zd: ')' expected\n",
599 hdrfn, hdrfline, (p - protostr) + 1);
600 return -1;
601 }
602 p = sskip(p + 1);
603 }
604
605 if (*p != '(') {
606 printf("%s:%d:%zd: '(' expected, got '%c'\n",
607 hdrfn, hdrfline, (p - protostr) + 1, *p);
608 return -1;
609 }
610 p++;
611
612 // check for x(void)
613 p = sskip(p);
614 if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
615 && *sskip(p + 4) == ')')
616 p += 4;
617
618 while (1) {
619 p = sskip(p);
620 if (*p == ')') {
621 p++;
622 break;
623 }
624 if (xarg > 0) {
625 if (*p != ',') {
626 printf("%s:%d:%zd: ',' expected\n",
627 hdrfn, hdrfline, (p - protostr) + 1);
628 return -1;
629 }
630 p = sskip(p + 1);
631 }
632
633 if (!strncmp(p, "...", 3)) {
634 pp->is_vararg = 1;
635 p = sskip(p + 3);
636 if (*p == ')') {
637 p++;
638 break;
639 }
640 printf("%s:%d:%zd: ')' expected\n",
641 hdrfn, hdrfline, (p - protostr) + 1);
642 return -1;
643 }
644
645 if (xarg >= ARRAY_SIZE(pp->arg)) {
646 printf("%s:%d:%zd: too many args\n",
647 hdrfn, hdrfline, (p - protostr) + 1);
648 return -1;
649 }
650
651 arg = &pp->arg[xarg];
652 xarg++;
653
654 p1 = p;
655 ret = check_type(p, &arg->type);
656 if (ret <= 0) {
657 printf("%s:%d:%zd: unhandled type for arg%d\n",
658 hdrfn, hdrfline, (p - protostr) + 1, xarg);
659 return -1;
660 }
661 p = sskip(p + ret);
662
663 if (*p == '(' || arg->type.is_struct) {
664 // func ptr or struct
665 ret = parse_arg(&p1, arg, xarg);
666 if (ret < 0) {
667 printf("%s:%d:%zd: funcarg parse failed\n",
668 hdrfn, hdrfline, p1 - protostr);
669 return -1;
670 }
671 p = p1;
672 }
673
674 p = next_idt(buf, sizeof(buf), p);
675 p = sskip(p);
676#if 0
677 if (buf[0] == 0) {
678 printf("%s:%d:%zd: idt missing for arg%d\n",
679 hdrfn, hdrfline, (p - protostr) + 1, xarg);
680 return -1;
681 }
682#endif
683 arg->reg = NULL;
684
685 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
686 if (ret > 0) {
687 p += ret;
688 p = sskip(p);
689
690 arg->reg = strdup(map_reg(regparm));
691 arg->type.is_retreg = is_retreg;
692 pp->has_retreg |= is_retreg;
693 }
694
695 if (IS(arg->type.name, "float")
696 || IS(arg->type.name, "double"))
697 {
698 arg->type.is_float = 1;
699 }
700
701 if (!arg->type.is_ptr && (strstr(arg->type.name, "int64")
702 || IS(arg->type.name, "double")))
703 {
704 arg->type.is_64bit = 1;
705 // hack..
706 pp_copy_arg(&pp->arg[xarg], arg);
707 arg = &pp->arg[xarg];
708 xarg++;
709 free(arg->type.name);
710 arg->type.name = strdup("dummy");
711 }
712
713 ret = check_struct_arg(arg);
714 if (ret > 0) {
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);
721 xarg++;
722 }
723 }
724 }
725
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);
730 }
731 pp->arg[0].reg = strdup("ecx");
732 }
733
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);
738 }
739 pp->arg[1].reg = strdup("edx");
740 }
741
742 pp->argc = xarg;
743
744 for (i = 0; i < pp->argc; i++) {
745 if (pp->arg[i].reg == NULL)
746 pp->argc_stack++;
747 else
748 pp->argc_reg++;
749 }
750
751 if (pp->argc == 1 && pp->arg[0].reg != NULL
752 && IS(pp->arg[0].reg, "ecx"))
753 {
754 pp->is_fastcall = 1;
755 }
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"))
759 {
760 pp->is_fastcall = 1;
761 }
762
763 if (pp->is_vararg && (pp->is_stdcall || pp->is_fastcall)) {
764 printf("%s:%d: vararg %s?\n", hdrfn, hdrfline, cconv);
765 return -1;
766 }
767
768 return p - protostr;
769}
770
771static int pp_name_cmp(const void *p1, const void *p2)
772{
773 const struct parsed_proto *pp1 = p1, *pp2 = p2;
774 return strcmp(pp1->name, pp2->name);
775}
776
777static int ps_name_cmp(const void *p1, const void *p2)
778{
779 const struct parsed_struct *ps1 = p1, *ps2 = p2;
780 return strcmp(ps1->name, ps2->name);
781}
782
783// parsed struct cache
784static struct parsed_struct *ps_cache;
785static int ps_cache_size;
786static int ps_cache_alloc;
787
788static int struct_handler(FILE *fhdr, char *proto, int *line)
789{
790 struct parsed_struct *ps;
791 char lstr[256], *p;
792 int offset = 0;
793 int m = 0;
794 int ret;
795
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]));
804 }
805
806 ps = &ps_cache[ps_cache_size++];
807 ret = sscanf(proto, "struct %255s {", ps->name);
808 if (ret != 1) {
809 printf("%s:%d: struct parse failed\n", hdrfn, *line);
810 return -1;
811 }
812
813 while (fgets(lstr, sizeof(lstr), fhdr))
814 {
815 (*line)++;
816
817 p = sskip(lstr);
818 if (p[0] == '/' && p[1] == '/')
819 continue;
820 if (p[0] == '}')
821 break;
822
823 if (m >= ARRAY_SIZE(ps->members)) {
824 printf("%s:%d: too many struct members\n",
825 hdrfn, *line);
826 return -1;
827 }
828
829 hdrfline = *line;
830 ret = parse_protostr(p, &ps->members[m].pp);
831 if (ret < 0) {
832 printf("%s:%d: struct member #%d/%02x "
833 "doesn't parse\n", hdrfn, *line,
834 m, offset);
835 return -1;
836 }
837 ps->members[m].offset = offset;
838 offset += 4;
839 m++;
840 }
841
842 ps->member_count = m;
843
844 return 0;
845}
846
847// parsed proto cache
848static struct parsed_proto *pp_cache;
849static int pp_cache_size;
850static int pp_cache_alloc;
851
852static int b_pp_c_handler(char *proto, const char *fname,
853 int is_include, int is_osinc, int is_cinc)
854{
855 int ret;
856
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]));
865 }
866
867 ret = parse_protostr(proto, &pp_cache[pp_cache_size]);
868 if (ret < 0)
869 return -1;
870
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;
874 pp_cache_size++;
875 return 0;
876}
877
878static void build_caches(FILE *fhdr)
879{
880 long pos;
881 int ret;
882
883 pos = ftell(fhdr);
884 rewind(fhdr);
885
886 ret = do_protostrs(fhdr, hdrfn, 0);
887 if (ret < 0)
888 exit(1);
889
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);
893}
894
895static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
896 int quiet)
897{
898 const struct parsed_proto *pp_ret;
899 struct parsed_proto pp_search;
900 char *p;
901
902 if (pp_cache == NULL)
903 build_caches(fhdr);
904
905 // ugh...
906 if (sym[0] == '_' && !IS_START(sym, "__W"))
907 sym++;
908
909 strcpy(pp_search.name, sym);
910 p = strchr(pp_search.name, '@');
911 if (p != NULL)
912 *p = 0;
913
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);
918
919 return pp_ret;
920}
921
922static const struct parsed_proto *proto_lookup_struct(FILE *fhdr,
923 const char *type, int offset)
924{
925 struct parsed_struct ps_search, *ps;
926 int m;
927
928 if (pp_cache == NULL)
929 build_caches(fhdr);
930 if (ps_cache_size == 0)
931 return NULL;
932
933 while (my_isblank(*type))
934 type++;
935 if (!strncmp(type, "struct", 6) && my_isblank(type[6]))
936 type += 7;
937
938 if (sscanf(type, "%255s", ps_search.name) != 1)
939 return NULL;
940
941 ps = bsearch(&ps_search, ps_cache, ps_cache_size,
942 sizeof(ps_cache[0]), ps_name_cmp);
943 if (ps == NULL) {
944 printf("%s: struct '%s' is missing\n",
945 hdrfn, ps_search.name);
946 return NULL;
947 }
948
949 for (m = 0; m < ps->member_count; m++) {
950 if (ps->members[m].offset == offset)
951 return &ps->members[m].pp;
952 }
953
954 return NULL;
955}
956
957static void pp_copy_arg(struct parsed_proto_arg *d,
958 const struct parsed_proto_arg *s)
959{
960 memcpy(d, s, sizeof(*d));
961
962 if (s->reg != NULL) {
963 d->reg = strdup(s->reg);
964 my_assert_not(d->reg, NULL);
965 }
966 if (s->type.name != NULL) {
967 d->type.name = strdup(s->type.name);
968 my_assert_not(d->type.name, NULL);
969 }
970 if (s->pp != NULL) {
971 d->pp = malloc(sizeof(*d->pp));
972 my_assert_not(d->pp, NULL);
973 memcpy(d->pp, s->pp, sizeof(*d->pp));
974 }
975}
976
977struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
978{
979 struct parsed_proto *pp;
980 int i;
981
982 pp = malloc(sizeof(*pp));
983 my_assert_not(pp, NULL);
984 memcpy(pp, pp_c, sizeof(*pp)); // lazy..
985
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);
991
992 return pp;
993}
994
995
996static inline int pp_cmp_func(const struct parsed_proto *pp1,
997 const struct parsed_proto *pp2)
998{
999 int i;
1000
1001 if (pp1->argc != pp2->argc || pp1->argc_reg != pp2->argc_reg)
1002 return 1;
1003 if (pp1->is_stdcall != pp2->is_stdcall)
1004 return 1;
1005
1006 // because of poor void return detection, return is not
1007 // checked for now to avoid heaps of false positives
1008
1009 for (i = 0; i < pp1->argc; i++) {
1010 if ((pp1->arg[i].reg != NULL) != (pp2->arg[i].reg != NULL))
1011 return 1;
1012
1013 if ((pp1->arg[i].reg != NULL)
1014 && !IS(pp1->arg[i].reg, pp2->arg[i].reg))
1015 {
1016 return 1;
1017 }
1018 }
1019
1020 return 0;
1021}
1022
1023static inline int pp_compatible_func(
1024 const struct parsed_proto *pp_site,
1025 const struct parsed_proto *pp_callee)
1026{
1027 if (pp_cmp_func(pp_site, pp_callee) == 0)
1028 return 1;
1029
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 */
1035 return 1;
1036
1037 return 0;
1038}
1039
1040static inline void pp_print(char *buf, size_t buf_size,
1041 const struct parsed_proto *pp)
1042{
1043 size_t l;
1044 int i;
1045
1046 snprintf(buf, buf_size, "%s %s(", pp->ret_type.name, pp->name);
1047 l = strlen(buf);
1048
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);
1052 l = strlen(buf);
1053 }
1054 if (pp->argc_stack > 0) {
1055 snprintf(buf + l, buf_size - l, "%s{%d stack}",
1056 i == 0 ? "" : ", ", pp->argc_stack);
1057 l = strlen(buf);
1058 }
1059 snprintf(buf + l, buf_size - l, ")");
1060}
1061
1062static inline void proto_release(struct parsed_proto *pp)
1063{
1064 int i;
1065
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);
1071 }
1072 if (pp->ret_type.name != NULL)
1073 free(pp->ret_type.name);
1074 free(pp);
1075
1076 (void)proto_lookup_struct;
1077}