indirect call stuff, fixes
[ia32rtools.git] / tools / protoparse.h
... / ...
CommitLineData
1
2struct parsed_proto;
3
4struct parsed_type {
5 char *name;
6 unsigned int is_array:1;
7 unsigned int is_ptr:1;
8 unsigned int is_struct:1; // split for args
9};
10
11struct parsed_proto_arg {
12 char *reg;
13 struct parsed_type type;
14 struct parsed_proto *fptr;
15 void *datap;
16};
17
18struct parsed_proto {
19 char name[256];
20 union {
21 struct parsed_type ret_type;
22 struct parsed_type type;
23 };
24 struct parsed_proto_arg arg[16];
25 int argc;
26 int argc_stack;
27 int argc_reg;
28 unsigned int is_func:1;
29 unsigned int is_stdcall:1;
30 unsigned int is_vararg:1;
31 unsigned int is_fptr:1;
32 unsigned int is_noreturn:1;
33 unsigned int is_unresolved:1;
34 unsigned int has_structarg:1;
35};
36
37static const char *hdrfn;
38static int hdrfline = 0;
39
40static void pp_copy_arg(struct parsed_proto_arg *d,
41 const struct parsed_proto_arg *s);
42
43static int b_pp_c_handler(char *proto, const char *fname);
44
45static int do_protostrs(FILE *fhdr, const char *fname)
46{
47 const char *finc_name;
48 const char *hdrfn_saved;
49 char protostr[256];
50 char path[256];
51 char fname_inc[256];
52 FILE *finc;
53 int line = 0;
54 int ret;
55 char *p;
56
57 hdrfn_saved = hdrfn;
58 hdrfn = fname;
59
60 while (fgets(protostr, sizeof(protostr), fhdr))
61 {
62 line++;
63 if (strncmp(protostr, "//#include ", 11) == 0) {
64 finc_name = protostr + 11;
65 p = strpbrk(finc_name, "\r\n ");
66 if (p != NULL)
67 *p = 0;
68
69 path[0] = 0;
70 p = strrchr(hdrfn_saved, '/');
71 if (p) {
72 memcpy(path, hdrfn_saved,
73 p - hdrfn_saved + 1);
74 path[p - hdrfn_saved + 1] = 0;
75 }
76 snprintf(fname_inc, sizeof(fname_inc), "%s%s",
77 path, finc_name);
78 finc = fopen(fname_inc, "r");
79 if (finc == NULL) {
80 printf("%s:%d: can't open '%s'\n",
81 fname_inc, line, finc_name);
82 continue;
83 }
84 ret = do_protostrs(finc, finc_name);
85 fclose(finc);
86 if (ret < 0)
87 break;
88 continue;
89 }
90 if (strncmp(sskip(protostr), "//", 2) == 0)
91 continue;
92
93 p = protostr + strlen(protostr);
94 for (p--; p >= protostr && my_isblank(*p); --p)
95 *p = 0;
96 if (p < protostr)
97 continue;
98
99 hdrfline = line;
100
101 ret = b_pp_c_handler(protostr, hdrfn);
102 if (ret < 0)
103 break;
104 }
105
106 hdrfn = hdrfn_saved;
107
108 if (feof(fhdr))
109 return 0;
110
111 return -1;
112}
113
114static int get_regparm(char *dst, size_t dlen, char *p)
115{
116 int i, o;
117
118 if (*p != '<')
119 return 0;
120
121 for (o = 0, i = 1; o < dlen; i++) {
122 if (p[i] == 0)
123 return 0;
124 if (p[i] == '>')
125 break;
126 dst[o++] = p[i];
127 }
128 dst[o] = 0;
129 return i + 1;
130}
131
132// hmh..
133static const char *known_type_mod[] = {
134 "const",
135 "signed",
136 "unsigned",
137 "struct",
138 "enum",
139 "CONST",
140};
141
142static const char *known_ptr_types[] = {
143 "HACCEL",
144 "HANDLE",
145 "HBITMAP",
146 "HCURSOR",
147 "HDC",
148 "HFONT",
149 "HGDIOBJ",
150 "HGLOBAL",
151 "HINSTANCE",
152 "HIMC",
153 "HMODULE",
154 "HRGN",
155 "HRSRC",
156 "HKEY",
157 "HMENU",
158 "HWND",
159 "PCRITICAL_SECTION",
160 "PDWORD",
161 "PHKEY",
162 "PLONG",
163 "PMEMORY_BASIC_INFORMATION",
164 "PUINT",
165 "PVOID",
166 "PCVOID",
167 "DLGPROC",
168 "TIMERPROC",
169 "WNDENUMPROC",
170 "va_list",
171 "__VALIST",
172};
173
174static const char *ignored_keywords[] = {
175 "extern",
176 "WINBASEAPI",
177 "WINUSERAPI",
178 "WINGDIAPI",
179 "WINADVAPI",
180};
181
182// returns ptr to char after type ends
183static int typecmp(const char *n, const char *t)
184{
185 for (; *t != 0; n++, t++) {
186 while (n[0] == ' ' && (n[1] == ' ' || n[1] == '*'))
187 n++;
188 while (t[0] == ' ' && (t[1] == ' ' || t[1] == '*'))
189 t++;
190 if (*n != *t)
191 return *n - *t;
192 }
193
194 return 0;
195}
196
197static const char *skip_type_mod(const char *n)
198{
199 int len;
200 int i;
201
202 for (i = 0; i < ARRAY_SIZE(known_type_mod); i++) {
203 len = strlen(known_type_mod[i]);
204 if (strncmp(n, known_type_mod[i], len) != 0)
205 continue;
206 if (!my_isblank(n[len]))
207 continue;
208
209 n += len;
210 while (my_isblank(*n))
211 n++;
212 i = 0;
213 }
214
215 return n;
216}
217
218static int check_type(const char *name, struct parsed_type *type)
219{
220 const char *n, *n1;
221 int ret = -1;
222 int i;
223
224 n = skip_type_mod(name);
225
226 for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
227 if (typecmp(n, known_ptr_types[i]))
228 continue;
229
230 type->is_ptr = 1;
231 break;
232 }
233
234 if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
235 type->is_ptr = 1;
236
237 // assume single word
238 while (!my_isblank(*n) && !my_issep(*n))
239 n++;
240
241 while (1) {
242 n1 = n;
243 while (my_isblank(*n))
244 n++;
245 if (*n == '*') {
246 type->is_ptr = 1;
247 n++;
248 continue;
249 }
250 break;
251 }
252
253 ret = n1 - name;
254 type->name = strndup(name, ret);
255 if (IS(type->name, "VOID"))
256 memcpy(type->name, "void", 4);
257
258 return ret;
259}
260
261/* args are always expanded to 32bit */
262static const char *map_reg(const char *reg)
263{
264 const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
265 const char *regs_w[] = { "ax", "bx", "cx", "dx", "si", "di" };
266 const char *regs_b[] = { "al", "bl", "cl", "dl" };
267 int i;
268
269 for (i = 0; i < ARRAY_SIZE(regs_w); i++)
270 if (IS(reg, regs_w[i]))
271 return regs_f[i];
272
273 for (i = 0; i < ARRAY_SIZE(regs_b); i++)
274 if (IS(reg, regs_b[i]))
275 return regs_f[i];
276
277 return reg;
278}
279
280static int check_struct_arg(struct parsed_proto_arg *arg)
281{
282 if (IS(arg->type.name, "POINT"))
283 return 2 - 1;
284
285 return 0;
286}
287
288static int parse_protostr(char *protostr, struct parsed_proto *pp)
289{
290 struct parsed_proto_arg *arg;
291 char regparm[16];
292 char buf[256];
293 char cconv[32];
294 int xarg = 0;
295 char *p, *p1;
296 int i, l;
297 int ret;
298
299 p = sskip(protostr);
300 if (p[0] == '/' && p[1] == '/') {
301 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
302 p = sskip(p + 2);
303 }
304
305 // strip unneeded stuff
306 for (p1 = p; p1[0] != 0 && p1[1] != 0; p1++) {
307 if ((p1[0] == '/' && p1[1] == '*')
308 || (p1[0] == '*' && p1[1] == '/'))
309 p1[0] = p1[1] = ' ';
310 }
311
312 if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
313 pp->is_noreturn = 1;
314 p = sskip(p + 18);
315 }
316 else if (!strncmp(p, "noreturn ", 9)) {
317 pp->is_noreturn = 1;
318 p = sskip(p + 9);
319 }
320
321 for (i = 0; i < ARRAY_SIZE(ignored_keywords); i++) {
322 l = strlen(ignored_keywords[i]);
323 if (!strncmp(p, ignored_keywords[i], l) && my_isblank(p[l]))
324 p = sskip(p + l + 1);
325 }
326
327 ret = check_type(p, &pp->ret_type);
328 if (ret <= 0) {
329 printf("%s:%d:%zd: unhandled return in '%s'\n",
330 hdrfn, hdrfline, (p - protostr) + 1, protostr);
331 return -1;
332 }
333 p = sskip(p + ret);
334
335 if (!strchr(p, ')')) {
336 p = next_idt(buf, sizeof(buf), p);
337 p = sskip(p);
338 if (buf[0] == 0) {
339 printf("%s:%d:%zd: var name missing\n",
340 hdrfn, hdrfline, (p - protostr) + 1);
341 return -1;
342 }
343 strcpy(pp->name, buf);
344
345 p1 = strchr(p, ']');
346 if (p1 != NULL) {
347 p = p1 + 1;
348 pp->ret_type.is_array = 1;
349 }
350 return p - protostr;
351 }
352
353 pp->is_func = 1;
354
355 if (*p == '(') {
356 pp->is_fptr = 1;
357 p = sskip(p + 1);
358 }
359
360 p = next_word(cconv, sizeof(cconv), p);
361 p = sskip(p);
362 if (cconv[0] == 0) {
363 printf("%s:%d:%zd: cconv missing\n",
364 hdrfn, hdrfline, (p - protostr) + 1);
365 return -1;
366 }
367 if (IS(cconv, "__cdecl"))
368 pp->is_stdcall = 0;
369 else if (IS(cconv, "__stdcall"))
370 pp->is_stdcall = 1;
371 else if (IS(cconv, "__fastcall"))
372 pp->is_stdcall = 1;
373 else if (IS(cconv, "__thiscall"))
374 pp->is_stdcall = 1;
375 else if (IS(cconv, "__userpurge"))
376 pp->is_stdcall = 1; // IDA
377 else if (IS(cconv, "__usercall"))
378 pp->is_stdcall = 0; // IDA
379 else if (IS(cconv, "WINAPI"))
380 pp->is_stdcall = 1;
381 else {
382 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
383 hdrfn, hdrfline, (p - protostr) + 1, cconv);
384 return -1;
385 }
386
387 if (pp->is_fptr) {
388 if (*p != '*') {
389 printf("%s:%d:%zd: '*' expected\n",
390 hdrfn, hdrfline, (p - protostr) + 1);
391 return -1;
392 }
393 p++;
394 // XXX: skipping extra asterisks, for now
395 while (*p == '*')
396 p++;
397 p = sskip(p);
398 }
399
400 p = next_idt(buf, sizeof(buf), p);
401 p = sskip(p);
402 if (buf[0] == 0) {
403 //printf("%s:%d:%zd: func name missing\n",
404 // hdrfn, hdrfline, (p - protostr) + 1);
405 //return -1;
406 }
407 strcpy(pp->name, buf);
408
409 ret = get_regparm(regparm, sizeof(regparm), p);
410 if (ret > 0) {
411 if (!IS(regparm, "eax") && !IS(regparm, "ax")
412 && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
413 {
414 printf("%s:%d:%zd: bad regparm: %s\n",
415 hdrfn, hdrfline, (p - protostr) + 1, regparm);
416 return -1;
417 }
418 p += ret;
419 p = sskip(p);
420 }
421
422 if (pp->is_fptr) {
423 if (*p == '[') {
424 // not really ret_type is array, but ohwell
425 pp->ret_type.is_array = 1;
426 p = strchr(p + 1, ']');
427 if (p == NULL) {
428 printf("%s:%d:%zd: ']' expected\n",
429 hdrfn, hdrfline, (p - protostr) + 1);
430 return -1;
431 }
432 p = sskip(p + 1);
433 }
434 if (*p != ')') {
435 printf("%s:%d:%zd: ')' expected\n",
436 hdrfn, hdrfline, (p - protostr) + 1);
437 return -1;
438 }
439 p = sskip(p + 1);
440 }
441
442 if (*p != '(') {
443 printf("%s:%d:%zd: '(' expected, got '%c'\n",
444 hdrfn, hdrfline, (p - protostr) + 1, *p);
445 return -1;
446 }
447 p++;
448
449 // check for x(void)
450 p = sskip(p);
451 if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
452 && *sskip(p + 4) == ')')
453 p += 4;
454
455 while (1) {
456 p = sskip(p);
457 if (*p == ')') {
458 p++;
459 break;
460 }
461 if (xarg > 0) {
462 if (*p != ',') {
463 printf("%s:%d:%zd: ',' expected\n",
464 hdrfn, hdrfline, (p - protostr) + 1);
465 return -1;
466 }
467 p = sskip(p + 1);
468 }
469
470 if (!strncmp(p, "...", 3)) {
471 pp->is_vararg = 1;
472 p = sskip(p + 3);
473 if (*p == ')') {
474 p++;
475 break;
476 }
477 printf("%s:%d:%zd: ')' expected\n",
478 hdrfn, hdrfline, (p - protostr) + 1);
479 return -1;
480 }
481
482 arg = &pp->arg[xarg];
483 xarg++;
484
485 p1 = p;
486 ret = check_type(p, &arg->type);
487 if (ret <= 0) {
488 printf("%s:%d:%zd: unhandled type for arg%d\n",
489 hdrfn, hdrfline, (p - protostr) + 1, xarg);
490 return -1;
491 }
492 p = sskip(p + ret);
493
494 if (*p == '(') {
495 // func ptr
496 arg->fptr = calloc(1, sizeof(*arg->fptr));
497 ret = parse_protostr(p1, arg->fptr);
498 if (ret < 0) {
499 printf("%s:%d:%zd: funcarg parse failed\n",
500 hdrfn, hdrfline, p1 - protostr);
501 return -1;
502 }
503 // we'll treat it as void * for non-calls
504 arg->type.name = strdup("void *");
505 arg->type.is_ptr = 1;
506
507 p = p1 + ret;
508 }
509
510 p = next_idt(buf, sizeof(buf), p);
511 p = sskip(p);
512#if 0
513 if (buf[0] == 0) {
514 printf("%s:%d:%zd: idt missing for arg%d\n",
515 hdrfn, hdrfline, (p - protostr) + 1, xarg);
516 return -1;
517 }
518#endif
519 arg->reg = NULL;
520
521 ret = get_regparm(regparm, sizeof(regparm), p);
522 if (ret > 0) {
523 p += ret;
524 p = sskip(p);
525
526 arg->reg = strdup(map_reg(regparm));
527 }
528
529 ret = check_struct_arg(arg);
530 if (ret > 0) {
531 pp->has_structarg = 1;
532 arg->type.is_struct = 1;
533 free(arg->type.name);
534 arg->type.name = strdup("int");
535 for (l = 0; l < ret; l++) {
536 pp_copy_arg(&pp->arg[xarg], arg);
537 xarg++;
538 }
539 }
540 }
541
542 if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
543 if (pp->arg[0].reg != NULL) {
544 printf("%s:%d: %s with arg1 spec %s?\n",
545 hdrfn, hdrfline, cconv, pp->arg[0].reg);
546 }
547 pp->arg[0].reg = strdup("ecx");
548 }
549
550 if (xarg > 1 && IS(cconv, "__fastcall")) {
551 if (pp->arg[1].reg != NULL) {
552 printf("%s:%d: %s with arg2 spec %s?\n",
553 hdrfn, hdrfline, cconv, pp->arg[1].reg);
554 }
555 pp->arg[1].reg = strdup("edx");
556 }
557
558 if (pp->is_vararg && pp->is_stdcall) {
559 printf("%s:%d: vararg stdcall?\n", hdrfn, hdrfline);
560 return -1;
561 }
562
563 pp->argc = xarg;
564
565 for (i = 0; i < pp->argc; i++) {
566 if (pp->arg[i].reg == NULL)
567 pp->argc_stack++;
568 else
569 pp->argc_reg++;
570 }
571
572 return p - protostr;
573}
574
575static int pp_name_cmp(const void *p1, const void *p2)
576{
577 const struct parsed_proto *pp1 = p1, *pp2 = p2;
578 return strcmp(pp1->name, pp2->name);
579}
580
581static struct parsed_proto *pp_cache;
582static int pp_cache_size;
583static int pp_cache_alloc;
584
585static int b_pp_c_handler(char *proto, const char *fname)
586{
587 int ret;
588
589 if (pp_cache_size >= pp_cache_alloc) {
590 pp_cache_alloc = pp_cache_alloc * 2 + 64;
591 pp_cache = realloc(pp_cache, pp_cache_alloc
592 * sizeof(pp_cache[0]));
593 my_assert_not(pp_cache, NULL);
594 memset(pp_cache + pp_cache_size, 0,
595 (pp_cache_alloc - pp_cache_size)
596 * sizeof(pp_cache[0]));
597 }
598
599 ret = parse_protostr(proto, &pp_cache[pp_cache_size]);
600 if (ret < 0)
601 return -1;
602
603 pp_cache_size++;
604 return 0;
605}
606
607static void build_pp_cache(FILE *fhdr)
608{
609 int ret;
610
611 rewind(fhdr);
612
613 ret = do_protostrs(fhdr, hdrfn);
614 if (ret < 0)
615 exit(1);
616
617 qsort(pp_cache, pp_cache_size, sizeof(pp_cache[0]), pp_name_cmp);
618}
619
620static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
621 int quiet)
622{
623 const struct parsed_proto *pp_ret;
624 struct parsed_proto pp_search;
625
626 if (pp_cache == NULL)
627 build_pp_cache(fhdr);
628
629 if (sym[0] == '_') // && strncmp(fname, "stdc", 4) == 0)
630 sym++;
631
632 strcpy(pp_search.name, sym);
633 pp_ret = bsearch(&pp_search, pp_cache, pp_cache_size,
634 sizeof(pp_cache[0]), pp_name_cmp);
635 if (pp_ret == NULL && !quiet)
636 printf("%s: sym '%s' is missing\n", hdrfn, sym);
637
638 return pp_ret;
639}
640
641static void pp_copy_arg(struct parsed_proto_arg *d,
642 const struct parsed_proto_arg *s)
643{
644 memcpy(d, s, sizeof(*d));
645
646 if (s->reg != NULL) {
647 d->reg = strdup(s->reg);
648 my_assert_not(d->reg, NULL);
649 }
650 if (s->type.name != NULL) {
651 d->type.name = strdup(s->type.name);
652 my_assert_not(d->type.name, NULL);
653 }
654 if (s->fptr != NULL) {
655 d->fptr = malloc(sizeof(*d->fptr));
656 my_assert_not(d->fptr, NULL);
657 memcpy(d->fptr, s->fptr, sizeof(*d->fptr));
658 }
659}
660
661struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
662{
663 struct parsed_proto *pp;
664 int i;
665
666 pp = malloc(sizeof(*pp));
667 my_assert_not(pp, NULL);
668 memcpy(pp, pp_c, sizeof(*pp)); // lazy..
669
670 // do the actual deep copy..
671 for (i = 0; i < pp_c->argc; i++)
672 pp_copy_arg(&pp->arg[i], &pp_c->arg[i]);
673 if (pp_c->ret_type.name != NULL)
674 pp->ret_type.name = strdup(pp_c->ret_type.name);
675
676 return pp;
677}
678
679static inline void proto_release(struct parsed_proto *pp)
680{
681 int i;
682
683 for (i = 0; i < pp->argc; i++) {
684 if (pp->arg[i].reg != NULL)
685 free(pp->arg[i].reg);
686 if (pp->arg[i].type.name != NULL)
687 free(pp->arg[i].type.name);
688 if (pp->arg[i].fptr != NULL)
689 free(pp->arg[i].fptr);
690 }
691 if (pp->ret_type.name != NULL)
692 free(pp->ret_type.name);
693 free(pp);
694}