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