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