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