translate: tune float i/o, indirect tailcalls
[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 "HKL",
220 "HMENU",
221 "HMONITOR",
222 "HWAVEOUT",
223 "HWND",
224 "PAPPBARDATA",
225 "PBYTE",
226 "PCRITICAL_SECTION",
227 "PDEVMODEA",
228 "PDWORD",
229 "PFILETIME",
230 "PLARGE_INTEGER",
231 "PHANDLE",
232 "PHKEY",
233 "PLONG",
234 "PMEMORY_BASIC_INFORMATION",
235 "PUINT",
236 "PULARGE_INTEGER",
237 "PULONG_PTR",
238 "PVOID",
239 "PCVOID",
240 "PWORD",
241 "REFCLSID",
242 "REFGUID",
243 "REFIID",
244 "SC_HANDLE",
245 "SERVICE_STATUS_HANDLE",
246 "HOOKPROC",
247 "DLGPROC",
248 "TIMERPROC",
249 "WNDENUMPROC",
250 "va_list",
251 "__VALIST",
252};
253
254static const char *ignored_keywords[] = {
255 "extern",
256 "WINBASEAPI",
257 "WINUSERAPI",
258 "WINGDIAPI",
259 "WINADVAPI",
260};
261
262static int typecmp(const char *n, const char *t)
263{
264 for (; *t != 0; n++, t++) {
265 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
266 n++;
267 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
268 t++;
269 if (*n != *t)
270 return *n - *t;
271 }
272
273 return 0;
274}
275
276static const char *skip_type_mod(const char *n)
277{
278 int len;
279 int i;
280
281 for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
282 len = strlen(known_type_mod[i]);
283 if (strncmp(n, known_type_mod[i], len) != 0)
284 continue;
285 if (!my_isblank(n[len]))
286 continue;
287
288 n += len;
289 while (my_isblank(*n))
290 n++;
291 i = 0;
292 }
293
294 return n;
295}
296
297static int check_type(const char *name, struct parsed_type *type)
298{
299 const char *n, *n1;
300 int ret = -1;
301 int i;
302
303 n = skip_type_mod(name);
304
305 if (!strncmp(n, "struct", 6) && my_isblank(n[6])) {
306 type->is_struct = 1;
307
308 n += 6;
309 while (my_isblank(*n))
310 n++;
311 }
312
313 for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
314 if (typecmp(n, known_ptr_types[i]))
315 continue;
316
317 type->is_ptr = 1;
318 break;
319 }
320
321 if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
322 type->is_ptr = 1;
323
324 // assume single word
325 while (!my_isblank(*n) && !my_issep(*n))
326 n++;
327
328 while (1) {
329 n1 = n;
330 while (my_isblank(*n))
331 n++;
332 if (*n == '*') {
333 type->is_ptr = 1;
334 n++;
335 continue;
336 }
337 break;
338 }
339
340 ret = n1 - name;
341 type->name = strndup(name, ret);
342 if (IS(type->name, "__VALIST") || IS(type->name, "va_list"))
343 type->is_va_list = 1;
344 if (IS(type->name, "VOID"))
345 memcpy(type->name, "void", 4);
346
347 return ret;
348}
349
350/* args are always expanded to 32bit */
351static const char *map_reg(const char *reg)
352{
353 const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
354 const char *regs_w[] = { "ax", "bx", "cx", "dx", "si", "di" };
355 const char *regs_b[] = { "al", "bl", "cl", "dl" };
356 int i;
357
358 for (i = 0; i < ARRAY_SIZE(regs_w); i++)
359 if (IS(reg, regs_w[i]))
360 return regs_f[i];
361
362 for (i = 0; i < ARRAY_SIZE(regs_b); i++)
363 if (IS(reg, regs_b[i]))
364 return regs_f[i];
365
366 return reg;
367}
368
369static int check_struct_arg(struct parsed_proto_arg *arg)
370{
371 if (IS(arg->type.name, "POINT"))
372 return 2 - 1;
373
374 return 0;
375}
376
377static int parse_protostr(char *protostr, struct parsed_proto *pp);
378
379static int parse_arg(char **p_, struct parsed_proto_arg *arg, int xarg)
380{
381 char buf[256];
382 char *p = *p_;
383 char *pe;
384 int ret;
385
386 arg->pp = calloc(1, sizeof(*arg->pp));
387 my_assert_not(arg->pp, NULL);
388 arg->pp->is_arg = 1;
389
390 pe = p;
391 while (1) {
392 pe = strpbrk(pe, ",()");
393 if (pe == NULL)
394 return -1;
395 if (*pe == ',' || *pe == ')')
396 break;
397 pe = strchr(pe, ')');
398 if (pe == NULL)
399 return -1;
400 pe++;
401 }
402
403 if (pe - p > sizeof(buf) - 1)
404 return -1;
405 memcpy(buf, p, pe - p);
406 buf[pe - p] = 0;
407
408 ret = parse_protostr(buf, arg->pp);
409 if (ret < 0)
410 return -1;
411
412 if (IS_START(arg->pp->name, "guess"))
413 arg->pp->is_guessed = 1;
414
415 // we don't use actual names right now...
416 snprintf(arg->pp->name, sizeof(arg->pp->name), "a%d", xarg);
417
418 if (!arg->type.is_struct)
419 // we'll treat it as void * for non-calls
420 arg->type.name = strdup("void *");
421 arg->type.is_ptr = 1;
422
423 p += ret;
424 *p_ = p;
425 return 0;
426}
427
428static int parse_protostr(char *protostr, struct parsed_proto *pp)
429{
430 struct parsed_proto_arg *arg;
431 char regparm[16];
432 char buf[256];
433 char cconv[32];
434 int is_retreg;
435 char *p, *p1;
436 int xarg = 0;
437 int i, l;
438 int ret;
439
440 p = sskip(protostr);
441 if (p[0] == '/' && p[1] == '/') {
442 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
443 p = sskip(p + 2);
444 }
445
446 // allow start of line comment
447 if (p[0] == '/' && p[1] == '*') {
448 p = strstr(p + 2, "*/");
449 if (p == NULL) {
450 printf("%s:%d: multiline comments unsupported\n",
451 hdrfn, hdrfline);
452 return -1;
453 }
454 p = sskip(p + 2);
455 }
456
457 // we need remaining hints in comments, so strip / *
458 for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
459 if ((p1[0] == '/' && p1[1] == '*')
460 || (p1[0] == '*' && p1[1] == '/'))
461 p1[0] = p1[1] = ' ';
462 }
463
464 if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
465 pp->is_noreturn = 1;
466 p = sskip(p + 18);
467 }
468
469 for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
470 l = strlen(ignored_keywords[i]);
471 if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
472 p = sskip(p + l + 1);
473 }
474
475 if (IS_START(p, "DECL_IMPORT ")) {
476 pp->is_import = 1;
477 p = sskip(p + 12);
478 }
479
480 ret = check_type(p, &pp->ret_type);
481 if (ret <= 0) {
482 printf("%s:%d:%zd: unhandled return in '%s'\n",
483 hdrfn, hdrfline, (p - protostr) + 1, protostr);
484 return -1;
485 }
486 p = sskip(p + ret);
487
488 if (!strncmp(p, "noreturn ", 9)) {
489 pp->is_noreturn = 1;
490 p = sskip(p + 9);
491 }
492
493 if (!strchr(p, ')')) {
494 p = next_idt(buf, sizeof(buf), p);
495 p = sskip(p);
496 if (!pp->is_arg && buf[0] == 0) {
497 printf("%s:%d:%zd: var name is missing\n",
498 hdrfn, hdrfline, (p - protostr) + 1);
499 return -1;
500 }
501 strcpy(pp->name, buf);
502
503 p1 = strchr(p, ']');
504 if (p1 != NULL) {
505 p = p1 + 1;
506 pp->ret_type.is_array = 1;
507 }
508 return p - protostr;
509 }
510
511 pp->is_func = 1;
512
513 if (*p == '(') {
514 pp->is_fptr = 1;
515 p = sskip(p + 1);
516 }
517
518 p = next_word(cconv, sizeof(cconv), p);
519 p = sskip(p);
520 if (cconv[0] == 0) {
521 printf("%s:%d:%zd: cconv missing\n",
522 hdrfn, hdrfline, (p - protostr) + 1);
523 return -1;
524 }
525 if (IS(cconv, "__cdecl"))
526 pp->is_stdcall = 0;
527 else if (IS(cconv, "__stdcall"))
528 pp->is_stdcall = 1;
529 else if (IS(cconv, "__fastcall")) {
530 pp->is_fastcall = 1;
531 pp->is_stdcall = 1; // sort of..
532 }
533 else if (IS(cconv, "__thiscall"))
534 pp->is_stdcall = 1;
535 else if (IS(cconv, "__userpurge"))
536 pp->is_stdcall = 1; // IDA
537 else if (IS(cconv, "__usercall"))
538 pp->is_stdcall = 0; // IDA
539 else if (IS(cconv, "__userstack")) {
540 pp->is_stdcall = 0; // custom
541 pp->is_userstack = 1;
542 }
543 else if (IS(cconv, "WINAPI") || IS(cconv, "PASCAL"))
544 pp->is_stdcall = 1;
545 else {
546 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
547 hdrfn, hdrfline, (p - protostr) + 1, cconv);
548 return -1;
549 }
550
551 if (pp->is_fptr) {
552 if (*p != '*') {
553 printf("%s:%d:%zd: '*' expected\n",
554 hdrfn, hdrfline, (p - protostr) + 1);
555 return -1;
556 }
557 p++;
558 // XXX: skipping extra asterisks, for now
559 while (*p == '*')
560 p++;
561 p = sskip(p);
562 }
563
564 p = next_idt(buf, sizeof(buf), p);
565 p = sskip(p);
566 if (buf[0] == 0) {
567 //printf("%s:%d:%zd: func name missing\n",
568 // hdrfn, hdrfline, (p - protostr) + 1);
569 //return -1;
570 }
571 strcpy(pp->name, buf);
572
573 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
574 if (ret > 0) {
575 if (!IS(regparm, "eax") && !IS(regparm, "ax")
576 && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
577 {
578 printf("%s:%d:%zd: bad regparm: %s\n",
579 hdrfn, hdrfline, (p - protostr) + 1, regparm);
580 return -1;
581 }
582 p += ret;
583 p = sskip(p);
584 }
585
586 if (pp->is_fptr) {
587 if (*p == '[') {
588 // not really ret_type is array, but ohwell
589 pp->ret_type.is_array = 1;
590 p = strchr(p + 1, ']');
591 if (p == NULL) {
592 printf("%s:%d:%zd: ']' expected\n",
593 hdrfn, hdrfline, (p - protostr) + 1);
594 return -1;
595 }
596 p = sskip(p + 1);
597 }
598 if (*p != ')') {
599 printf("%s:%d:%zd: ')' expected\n",
600 hdrfn, hdrfline, (p - protostr) + 1);
601 return -1;
602 }
603 p = sskip(p + 1);
604 }
605
606 if (*p != '(') {
607 printf("%s:%d:%zd: '(' expected, got '%c'\n",
608 hdrfn, hdrfline, (p - protostr) + 1, *p);
609 return -1;
610 }
611 p++;
612
613 // check for x(void)
614 p = sskip(p);
615 if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
616 && *sskip(p + 4) == ')')
617 p += 4;
618
619 while (1) {
620 p = sskip(p);
621 if (*p == ')') {
622 p++;
623 break;
624 }
625 if (xarg > 0) {
626 if (*p != ',') {
627 printf("%s:%d:%zd: ',' expected\n",
628 hdrfn, hdrfline, (p - protostr) + 1);
629 return -1;
630 }
631 p = sskip(p + 1);
632 }
633
634 if (!strncmp(p, "...", 3)) {
635 pp->is_vararg = 1;
636 p = sskip(p + 3);
637 if (*p == ')') {
638 p++;
639 break;
640 }
641 printf("%s:%d:%zd: ')' expected\n",
642 hdrfn, hdrfline, (p - protostr) + 1);
643 return -1;
644 }
645
646 if (xarg >= ARRAY_SIZE(pp->arg)) {
647 printf("%s:%d:%zd: too many args\n",
648 hdrfn, hdrfline, (p - protostr) + 1);
649 return -1;
650 }
651
652 arg = &pp->arg[xarg];
653 xarg++;
654
655 p1 = p;
656 ret = check_type(p, &arg->type);
657 if (ret <= 0) {
658 printf("%s:%d:%zd: unhandled type for arg%d\n",
659 hdrfn, hdrfline, (p - protostr) + 1, xarg);
660 return -1;
661 }
662 p = sskip(p + ret);
663
664 if (*p == '(' || arg->type.is_struct) {
665 // func ptr or struct
666 ret = parse_arg(&p1, arg, xarg);
667 if (ret < 0) {
668 printf("%s:%d:%zd: funcarg parse failed\n",
669 hdrfn, hdrfline, p1 - protostr);
670 return -1;
671 }
672 p = p1;
673 }
674
675 p = next_idt(buf, sizeof(buf), p);
676 p = sskip(p);
677#if 0
678 if (buf[0] == 0) {
679 printf("%s:%d:%zd: idt missing for arg%d\n",
680 hdrfn, hdrfline, (p - protostr) + 1, xarg);
681 return -1;
682 }
683#endif
684 arg->reg = NULL;
685
686 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
687 if (ret > 0) {
688 p += ret;
689 p = sskip(p);
690
691 arg->reg = strdup(map_reg(regparm));
692 arg->type.is_retreg = is_retreg;
693 pp->has_retreg |= is_retreg;
694 }
695
696 if (IS(arg->type.name, "float")
697 || IS(arg->type.name, "double"))
698 {
699 arg->type.is_float = 1;
700 }
701
702 if (!arg->type.is_ptr && (strstr(arg->type.name, "int64")
703 || IS(arg->type.name, "double")))
704 {
705 arg->type.is_64bit = 1;
706 // hack..
707 pp_copy_arg(&pp->arg[xarg], arg);
708 arg = &pp->arg[xarg];
709 xarg++;
710 free(arg->type.name);
711 arg->type.name = strdup("dummy");
712 }
713
714 ret = check_struct_arg(arg);
715 if (ret > 0) {
716 pp->has_structarg = 1;
717 arg->type.is_struct = 1;
718 free(arg->type.name);
719 arg->type.name = strdup("int");
720 for (l = 0; l < ret; l++) {
721 pp_copy_arg(&pp->arg[xarg], arg);
722 xarg++;
723 }
724 }
725 }
726
727 if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
728 if (pp->arg[0].reg != NULL) {
729 printf("%s:%d: %s with arg1 spec %s?\n",
730 hdrfn, hdrfline, cconv, pp->arg[0].reg);
731 }
732 pp->arg[0].reg = strdup("ecx");
733 }
734
735 if (xarg > 1 && IS(cconv, "__fastcall")) {
736 if (pp->arg[1].reg != NULL) {
737 printf("%s:%d: %s with arg2 spec %s?\n",
738 hdrfn, hdrfline, cconv, pp->arg[1].reg);
739 }
740 pp->arg[1].reg = strdup("edx");
741 }
742
743 pp->argc = xarg;
744
745 for (i = 0; i < pp->argc; i++) {
746 if (pp->arg[i].reg == NULL)
747 pp->argc_stack++;
748 else
749 pp->argc_reg++;
750 }
751
752 if (pp->argc == 1 && pp->arg[0].reg != NULL
753 && IS(pp->arg[0].reg, "ecx"))
754 {
755 pp->is_fastcall = 1;
756 }
757 else if (pp->argc_reg == 2
758 && pp->arg[0].reg != NULL && IS(pp->arg[0].reg, "ecx")
759 && pp->arg[1].reg != NULL && IS(pp->arg[1].reg, "edx"))
760 {
761 pp->is_fastcall = 1;
762 }
763
764 if (pp->is_vararg && (pp->is_stdcall || pp->is_fastcall)) {
765 printf("%s:%d: vararg %s?\n", hdrfn, hdrfline, cconv);
766 return -1;
767 }
768
769 return p - protostr;
770}
771
772static int pp_name_cmp(const void *p1, const void *p2)
773{
774 const struct parsed_proto *pp1 = p1, *pp2 = p2;
775 return strcmp(pp1->name, pp2->name);
776}
777
778static int ps_name_cmp(const void *p1, const void *p2)
779{
780 const struct parsed_struct *ps1 = p1, *ps2 = p2;
781 return strcmp(ps1->name, ps2->name);
782}
783
784// parsed struct cache
785static struct parsed_struct *ps_cache;
786static int ps_cache_size;
787static int ps_cache_alloc;
788
789static int struct_handler(FILE *fhdr, char *proto, int *line)
790{
791 struct parsed_struct *ps;
792 char lstr[256], *p;
793 int offset = 0;
794 int m = 0;
795 int ret;
796
797 if (ps_cache_size >= ps_cache_alloc) {
798 ps_cache_alloc = ps_cache_alloc * 2 + 64;
799 ps_cache = realloc(ps_cache, ps_cache_alloc
800 * sizeof(ps_cache[0]));
801 my_assert_not(ps_cache, NULL);
802 memset(ps_cache + ps_cache_size, 0,
803 (ps_cache_alloc - ps_cache_size)
804 * sizeof(ps_cache[0]));
805 }
806
807 ps = &ps_cache[ps_cache_size++];
808 ret = sscanf(proto, "struct %255s {", ps->name);
809 if (ret != 1) {
810 printf("%s:%d: struct parse failed\n", hdrfn, *line);
811 return -1;
812 }
813
814 while (fgets(lstr, sizeof(lstr), fhdr))
815 {
816 (*line)++;
817
818 p = sskip(lstr);
819 if (p[0] == '/' && p[1] == '/')
820 continue;
821 if (p[0] == '}')
822 break;
823
824 if (m >= ARRAY_SIZE(ps->members)) {
825 printf("%s:%d: too many struct members\n",
826 hdrfn, *line);
827 return -1;
828 }
829
830 hdrfline = *line;
831 ret = parse_protostr(p, &ps->members[m].pp);
832 if (ret < 0) {
833 printf("%s:%d: struct member #%d/%02x "
834 "doesn't parse\n", hdrfn, *line,
835 m, offset);
836 return -1;
837 }
838 ps->members[m].offset = offset;
839 offset += 4;
840 m++;
841 }
842
843 ps->member_count = m;
844
845 return 0;
846}
847
848// parsed proto cache
849static struct parsed_proto *pp_cache;
850static int pp_cache_size;
851static int pp_cache_alloc;
852
853static int b_pp_c_handler(char *proto, const char *fname,
854 int is_include, int is_osinc, int is_cinc)
855{
856 int ret;
857
858 if (pp_cache_size >= pp_cache_alloc) {
859 pp_cache_alloc = pp_cache_alloc * 2 + 64;
860 pp_cache = realloc(pp_cache, pp_cache_alloc
861 * sizeof(pp_cache[0]));
862 my_assert_not(pp_cache, NULL);
863 memset(pp_cache + pp_cache_size, 0,
864 (pp_cache_alloc - pp_cache_size)
865 * sizeof(pp_cache[0]));
866 }
867
868 ret = parse_protostr(proto, &pp_cache[pp_cache_size]);
869 if (ret < 0)
870 return -1;
871
872 pp_cache[pp_cache_size].is_include = is_include;
873 pp_cache[pp_cache_size].is_osinc = is_osinc;
874 pp_cache[pp_cache_size].is_cinc = is_cinc;
875 pp_cache_size++;
876 return 0;
877}
878
879static void build_caches(FILE *fhdr)
880{
881 long pos;
882 int ret;
883
884 pos = ftell(fhdr);
885 rewind(fhdr);
886
887 ret = do_protostrs(fhdr, hdrfn, 0);
888 if (ret < 0)
889 exit(1);
890
891 qsort(pp_cache, pp_cache_size, sizeof(pp_cache[0]), pp_name_cmp);
892 qsort(ps_cache, ps_cache_size, sizeof(ps_cache[0]), ps_name_cmp);
893 fseek(fhdr, pos, SEEK_SET);
894}
895
896static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
897 int quiet)
898{
899 const struct parsed_proto *pp_ret;
900 struct parsed_proto pp_search;
901 char *p;
902
903 if (pp_cache == NULL)
904 build_caches(fhdr);
905
906 // ugh...
907 if (sym[0] == '_' && !IS_START(sym, "__W"))
908 sym++;
909
910 strcpy(pp_search.name, sym);
911 p = strchr(pp_search.name, '@');
912 if (p != NULL)
913 *p = 0;
914
915 pp_ret = bsearch(&pp_search, pp_cache, pp_cache_size,
916 sizeof(pp_cache[0]), pp_name_cmp);
917 if (pp_ret == NULL && !quiet)
918 printf("%s: sym '%s' is missing\n", hdrfn, sym);
919
920 return pp_ret;
921}
922
923static const struct parsed_proto *proto_lookup_struct(FILE *fhdr,
924 const char *type, int offset)
925{
926 struct parsed_struct ps_search, *ps;
927 int m;
928
929 if (pp_cache == NULL)
930 build_caches(fhdr);
931 if (ps_cache_size == 0)
932 return NULL;
933
934 while (my_isblank(*type))
935 type++;
936 if (!strncmp(type, "struct", 6) && my_isblank(type[6]))
937 type += 7;
938
939 if (sscanf(type, "%255s", ps_search.name) != 1)
940 return NULL;
941
942 ps = bsearch(&ps_search, ps_cache, ps_cache_size,
943 sizeof(ps_cache[0]), ps_name_cmp);
944 if (ps == NULL) {
945 printf("%s: struct '%s' is missing\n",
946 hdrfn, ps_search.name);
947 return NULL;
948 }
949
950 for (m = 0; m < ps->member_count; m++) {
951 if (ps->members[m].offset == offset)
952 return &ps->members[m].pp;
953 }
954
955 return NULL;
956}
957
958static void pp_copy_arg(struct parsed_proto_arg *d,
959 const struct parsed_proto_arg *s)
960{
961 memcpy(d, s, sizeof(*d));
962
963 if (s->reg != NULL) {
964 d->reg = strdup(s->reg);
965 my_assert_not(d->reg, NULL);
966 }
967 if (s->type.name != NULL) {
968 d->type.name = strdup(s->type.name);
969 my_assert_not(d->type.name, NULL);
970 }
971 if (s->pp != NULL) {
972 d->pp = malloc(sizeof(*d->pp));
973 my_assert_not(d->pp, NULL);
974 memcpy(d->pp, s->pp, sizeof(*d->pp));
975 }
976}
977
978struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
979{
980 struct parsed_proto *pp;
981 int i;
982
983 pp = malloc(sizeof(*pp));
984 my_assert_not(pp, NULL);
985 memcpy(pp, pp_c, sizeof(*pp)); // lazy..
986
987 // do the actual deep copy..
988 for (i = 0; i < pp_c->argc; i++)
989 pp_copy_arg(&pp->arg[i], &pp_c->arg[i]);
990 if (pp_c->ret_type.name != NULL)
991 pp->ret_type.name = strdup(pp_c->ret_type.name);
992
993 return pp;
994}
995
996
997static inline int pp_cmp_func(const struct parsed_proto *pp1,
998 const struct parsed_proto *pp2)
999{
1000 int i;
1001
1002 if (pp1->argc != pp2->argc || pp1->argc_reg != pp2->argc_reg)
1003 return 1;
1004 if (pp1->is_stdcall != pp2->is_stdcall)
1005 return 1;
1006
1007 // because of poor void return detection, return is not
1008 // checked for now to avoid heaps of false positives
1009
1010 for (i = 0; i < pp1->argc; i++) {
1011 if ((pp1->arg[i].reg != NULL) != (pp2->arg[i].reg != NULL))
1012 return 1;
1013
1014 if ((pp1->arg[i].reg != NULL)
1015 && !IS(pp1->arg[i].reg, pp2->arg[i].reg))
1016 {
1017 return 1;
1018 }
1019 }
1020
1021 return 0;
1022}
1023
1024static inline int pp_compatible_func(
1025 const struct parsed_proto *pp_site,
1026 const struct parsed_proto *pp_callee)
1027{
1028 if (pp_cmp_func(pp_site, pp_callee) == 0)
1029 return 1;
1030
1031 if (pp_site->argc_stack == 0 && pp_site->is_fastcall
1032 && pp_callee->argc_stack == 0
1033 && (pp_callee->is_fastcall || pp_callee->argc_reg == 0)
1034 && pp_site->argc_reg > pp_callee->argc_reg)
1035 /* fascall compatible callee doesn't use all args -> ok */
1036 return 1;
1037
1038 return 0;
1039}
1040
1041static inline void pp_print(char *buf, size_t buf_size,
1042 const struct parsed_proto *pp)
1043{
1044 size_t l;
1045 int i;
1046
1047 snprintf(buf, buf_size, "%s %s(", pp->ret_type.name, pp->name);
1048 l = strlen(buf);
1049
1050 for (i = 0; i < pp->argc_reg; i++) {
1051 snprintf(buf + l, buf_size - l, "%s%s",
1052 i == 0 ? "" : ", ", pp->arg[i].reg);
1053 l = strlen(buf);
1054 }
1055 if (pp->argc_stack > 0) {
1056 snprintf(buf + l, buf_size - l, "%s{%d stack}",
1057 i == 0 ? "" : ", ", pp->argc_stack);
1058 l = strlen(buf);
1059 }
1060 snprintf(buf + l, buf_size - l, ")");
1061}
1062
1063static inline void proto_release(struct parsed_proto *pp)
1064{
1065 int i;
1066
1067 for (i = 0; i < pp->argc; i++) {
1068 free(pp->arg[i].reg);
1069 free(pp->arg[i].type.name);
1070 free(pp->arg[i].pp);
1071 free(pp->arg[i].push_refs);
1072 }
1073 if (pp->ret_type.name != NULL)
1074 free(pp->ret_type.name);
1075 free(pp);
1076
1077 (void)proto_lookup_struct;
1078}