indirect call stuff, fixes
[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;
89ff3147 33 unsigned int is_unresolved:1;
a652aa9f 34 unsigned int has_structarg:1;
c36e914d 35};
36
37static const char *hdrfn;
38static int hdrfline = 0;
39
a652aa9f 40static void pp_copy_arg(struct parsed_proto_arg *d,
41 const struct parsed_proto_arg *s);
42
bd96f656 43static int b_pp_c_handler(char *proto, const char *fname);
44
45static int do_protostrs(FILE *fhdr, const char *fname)
c36e914d 46{
06c5d854 47 const char *finc_name;
bd96f656 48 const char *hdrfn_saved;
49 char protostr[256];
36595fd2 50 char path[256];
51 char fname_inc[256];
39b168b8 52 FILE *finc;
c36e914d 53 int line = 0;
39b168b8 54 int ret;
c36e914d 55 char *p;
56
bd96f656 57 hdrfn_saved = hdrfn;
58 hdrfn = fname;
39b168b8 59
bd96f656 60 while (fgets(protostr, sizeof(protostr), fhdr))
c36e914d 61 {
62 line++;
bd96f656 63 if (strncmp(protostr, "//#include ", 11) == 0) {
64 finc_name = protostr + 11;
06c5d854 65 p = strpbrk(finc_name, "\r\n ");
39b168b8 66 if (p != NULL)
67 *p = 0;
68
36595fd2 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");
39b168b8 79 if (finc == NULL) {
80 printf("%s:%d: can't open '%s'\n",
36595fd2 81 fname_inc, line, finc_name);
39b168b8 82 continue;
83 }
bd96f656 84 ret = do_protostrs(finc, finc_name);
39b168b8 85 fclose(finc);
bd96f656 86 if (ret < 0)
39b168b8 87 break;
88 continue;
89 }
bd96f656 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)
06c5d854 97 continue;
39b168b8 98
bd96f656 99 hdrfline = line;
100
101 ret = b_pp_c_handler(protostr, hdrfn);
102 if (ret < 0)
c36e914d 103 break;
104 }
c36e914d 105
bd96f656 106 hdrfn = hdrfn_saved;
c36e914d 107
bd96f656 108 if (feof(fhdr))
109 return 0;
c36e914d 110
bd96f656 111 return -1;
c36e914d 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..
64c59faf 133static const char *known_type_mod[] = {
134 "const",
135 "signed",
136 "unsigned",
3ebea2cf 137 "struct",
138 "enum",
840257f6 139 "CONST",
64c59faf 140};
141
3ebea2cf 142static const char *known_ptr_types[] = {
e56ab892 143 "HACCEL",
c36e914d 144 "HANDLE",
e56ab892 145 "HBITMAP",
146 "HCURSOR",
3ebea2cf 147 "HDC",
a652aa9f 148 "HFONT",
3ebea2cf 149 "HGDIOBJ",
e56ab892 150 "HGLOBAL",
151 "HINSTANCE",
1cd4a663 152 "HIMC",
e56ab892 153 "HMODULE",
154 "HRGN",
155 "HRSRC",
156 "HKEY",
157 "HMENU",
158 "HWND",
a2c1d768 159 "PCRITICAL_SECTION",
3ebea2cf 160 "PDWORD",
94d447fb 161 "PHKEY",
162 "PLONG",
a2c1d768 163 "PMEMORY_BASIC_INFORMATION",
46411e6c 164 "PUINT",
3ebea2cf 165 "PVOID",
166 "PCVOID",
e56ab892 167 "DLGPROC",
cdfaeed7 168 "TIMERPROC",
169 "WNDENUMPROC",
4f12f671 170 "va_list",
171 "__VALIST",
3ebea2cf 172};
173
174static const char *ignored_keywords[] = {
175 "extern",
176 "WINBASEAPI",
177 "WINUSERAPI",
4f12f671 178 "WINGDIAPI",
179 "WINADVAPI",
c36e914d 180};
181
64c59faf 182// returns ptr to char after type ends
3ebea2cf 183static int typecmp(const char *n, const char *t)
39b168b8 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)
3ebea2cf 191 return *n - *t;
39b168b8 192 }
193
3ebea2cf 194 return 0;
39b168b8 195}
196
3ebea2cf 197static const char *skip_type_mod(const char *n)
c36e914d 198{
64c59faf 199 int len;
39b168b8 200 int i;
c36e914d 201
64c59faf 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;
bd96f656 206 if (!my_isblank(n[len]))
207 continue;
64c59faf 208
209 n += len;
210 while (my_isblank(*n))
211 n++;
212 i = 0;
213 }
214
3ebea2cf 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]))
64c59faf 228 continue;
229
3ebea2cf 230 type->is_ptr = 1;
231 break;
c36e914d 232 }
233
a652aa9f 234 if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
3ebea2cf 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);
a2c1d768 255 if (IS(type->name, "VOID"))
256 memcpy(type->name, "void", 4);
257
3ebea2cf 258 return ret;
c36e914d 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
a652aa9f 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
c36e914d 288static int parse_protostr(char *protostr, struct parsed_proto *pp)
289{
39b168b8 290 struct parsed_proto_arg *arg;
c36e914d 291 char regparm[16];
292 char buf[256];
293 char cconv[32];
c36e914d 294 int xarg = 0;
39b168b8 295 char *p, *p1;
3ebea2cf 296 int i, l;
c36e914d 297 int ret;
c36e914d 298
06c5d854 299 p = sskip(protostr);
c36e914d 300 if (p[0] == '/' && p[1] == '/') {
06c5d854 301 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
c36e914d 302 p = sskip(p + 2);
303 }
304
06c5d854 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
e56ab892 312 if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
313 pp->is_noreturn = 1;
314 p = sskip(p + 18);
315 }
89ff3147 316 else if (!strncmp(p, "noreturn ", 9)) {
317 pp->is_noreturn = 1;
318 p = sskip(p + 9);
319 }
e56ab892 320
3ebea2cf 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 }
06c5d854 326
3ebea2cf 327 ret = check_type(p, &pp->ret_type);
328 if (ret <= 0) {
63df67be 329 printf("%s:%d:%zd: unhandled return in '%s'\n",
c36e914d 330 hdrfn, hdrfline, (p - protostr) + 1, protostr);
39b168b8 331 return -1;
c36e914d 332 }
64c59faf 333 p = sskip(p + ret);
c36e914d 334
06c5d854 335 if (!strchr(p, ')')) {
336 p = next_idt(buf, sizeof(buf), p);
337 p = sskip(p);
338 if (buf[0] == 0) {
63df67be 339 printf("%s:%d:%zd: var name missing\n",
06c5d854 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;
3ebea2cf 348 pp->ret_type.is_array = 1;
06c5d854 349 }
350 return p - protostr;
351 }
352
353 pp->is_func = 1;
354
39b168b8 355 if (*p == '(') {
356 pp->is_fptr = 1;
357 p = sskip(p + 1);
358 }
359
c36e914d 360 p = next_word(cconv, sizeof(cconv), p);
361 p = sskip(p);
362 if (cconv[0] == 0) {
63df67be 363 printf("%s:%d:%zd: cconv missing\n",
c36e914d 364 hdrfn, hdrfline, (p - protostr) + 1);
39b168b8 365 return -1;
c36e914d 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"))
de50b98b 376 pp->is_stdcall = 1; // IDA
c36e914d 377 else if (IS(cconv, "__usercall"))
de50b98b 378 pp->is_stdcall = 0; // IDA
64c59faf 379 else if (IS(cconv, "WINAPI"))
380 pp->is_stdcall = 1;
c36e914d 381 else {
63df67be 382 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
c36e914d 383 hdrfn, hdrfline, (p - protostr) + 1, cconv);
39b168b8 384 return -1;
385 }
386
387 if (pp->is_fptr) {
388 if (*p != '*') {
63df67be 389 printf("%s:%d:%zd: '*' expected\n",
39b168b8 390 hdrfn, hdrfline, (p - protostr) + 1);
391 return -1;
392 }
bd96f656 393 p++;
394 // XXX: skipping extra asterisks, for now
395 while (*p == '*')
396 p++;
397 p = sskip(p);
c36e914d 398 }
399
400 p = next_idt(buf, sizeof(buf), p);
401 p = sskip(p);
402 if (buf[0] == 0) {
de50b98b 403 //printf("%s:%d:%zd: func name missing\n",
404 // hdrfn, hdrfline, (p - protostr) + 1);
405 //return -1;
c36e914d 406 }
39b168b8 407 strcpy(pp->name, buf);
c36e914d 408
409 ret = get_regparm(regparm, sizeof(regparm), p);
410 if (ret > 0) {
411 if (!IS(regparm, "eax") && !IS(regparm, "ax")
2b43685d 412 && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
c36e914d 413 {
63df67be 414 printf("%s:%d:%zd: bad regparm: %s\n",
c36e914d 415 hdrfn, hdrfline, (p - protostr) + 1, regparm);
39b168b8 416 return -1;
c36e914d 417 }
418 p += ret;
419 p = sskip(p);
420 }
421
39b168b8 422 if (pp->is_fptr) {
bd96f656 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 }
39b168b8 434 if (*p != ')') {
63df67be 435 printf("%s:%d:%zd: ')' expected\n",
39b168b8 436 hdrfn, hdrfline, (p - protostr) + 1);
437 return -1;
438 }
439 p = sskip(p + 1);
440 }
441
c36e914d 442 if (*p != '(') {
63df67be 443 printf("%s:%d:%zd: '(' expected, got '%c'\n",
c36e914d 444 hdrfn, hdrfline, (p - protostr) + 1, *p);
39b168b8 445 return -1;
c36e914d 446 }
447 p++;
448
39b168b8 449 // check for x(void)
450 p = sskip(p);
06c5d854 451 if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
452 && *sskip(p + 4) == ')')
39b168b8 453 p += 4;
454
c36e914d 455 while (1) {
456 p = sskip(p);
39b168b8 457 if (*p == ')') {
458 p++;
c36e914d 459 break;
39b168b8 460 }
840257f6 461 if (xarg > 0) {
462 if (*p != ',') {
463 printf("%s:%d:%zd: ',' expected\n",
464 hdrfn, hdrfline, (p - protostr) + 1);
465 return -1;
466 }
c36e914d 467 p = sskip(p + 1);
840257f6 468 }
c36e914d 469
7ba45c34 470 if (!strncmp(p, "...", 3)) {
471 pp->is_vararg = 1;
472 p = sskip(p + 3);
473 if (*p == ')') {
474 p++;
475 break;
476 }
63df67be 477 printf("%s:%d:%zd: ')' expected\n",
7ba45c34 478 hdrfn, hdrfline, (p - protostr) + 1);
479 return -1;
480 }
481
39b168b8 482 arg = &pp->arg[xarg];
c36e914d 483 xarg++;
484
39b168b8 485 p1 = p;
3ebea2cf 486 ret = check_type(p, &arg->type);
487 if (ret <= 0) {
63df67be 488 printf("%s:%d:%zd: unhandled type for arg%d\n",
c36e914d 489 hdrfn, hdrfline, (p - protostr) + 1, xarg);
39b168b8 490 return -1;
c36e914d 491 }
64c59faf 492 p = sskip(p + ret);
c36e914d 493
39b168b8 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) {
63df67be 499 printf("%s:%d:%zd: funcarg parse failed\n",
39b168b8 500 hdrfn, hdrfline, p1 - protostr);
501 return -1;
502 }
503 // we'll treat it as void * for non-calls
a652aa9f 504 arg->type.name = strdup("void *");
3ebea2cf 505 arg->type.is_ptr = 1;
39b168b8 506
507 p = p1 + ret;
508 }
509
c36e914d 510 p = next_idt(buf, sizeof(buf), p);
511 p = sskip(p);
512#if 0
513 if (buf[0] == 0) {
63df67be 514 printf("%s:%d:%zd: idt missing for arg%d\n",
c36e914d 515 hdrfn, hdrfline, (p - protostr) + 1, xarg);
39b168b8 516 return -1;
c36e914d 517 }
518#endif
39b168b8 519 arg->reg = NULL;
c36e914d 520
521 ret = get_regparm(regparm, sizeof(regparm), p);
522 if (ret > 0) {
523 p += ret;
524 p = sskip(p);
525
39b168b8 526 arg->reg = strdup(map_reg(regparm));
c36e914d 527 }
a652aa9f 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 }
c36e914d 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
7ba45c34 558 if (pp->is_vararg && pp->is_stdcall) {
559 printf("%s:%d: vararg stdcall?\n", hdrfn, hdrfline);
560 return -1;
561 }
562
c36e914d 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
39b168b8 572 return p - protostr;
c36e914d 573}
574
bd96f656 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)
c36e914d 608{
c36e914d 609 int ret;
610
bd96f656 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
36595fd2 620static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
621 int quiet)
bd96f656 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++;
c36e914d 631
bd96f656 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);
36595fd2 635 if (pp_ret == NULL && !quiet)
c36e914d 636 printf("%s: sym '%s' is missing\n", hdrfn, sym);
bd96f656 637
638 return pp_ret;
639}
640
a652aa9f 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
bd96f656 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..
a652aa9f 671 for (i = 0; i < pp_c->argc; i++)
672 pp_copy_arg(&pp->arg[i], &pp_c->arg[i]);
bd96f656 673 if (pp_c->ret_type.name != NULL)
674 pp->ret_type.name = strdup(pp_c->ret_type.name);
c36e914d 675
bd96f656 676 return pp;
c36e914d 677}
678
bd96f656 679static inline void proto_release(struct parsed_proto *pp)
c36e914d 680{
681 int i;
682
683 for (i = 0; i < pp->argc; i++) {
39b168b8 684 if (pp->arg[i].reg != NULL)
c36e914d 685 free(pp->arg[i].reg);
3ebea2cf 686 if (pp->arg[i].type.name != NULL)
687 free(pp->arg[i].type.name);
39b168b8 688 if (pp->arg[i].fptr != NULL)
689 free(pp->arg[i].fptr);
c36e914d 690 }
3ebea2cf 691 if (pp->ret_type.name != NULL)
692 free(pp->ret_type.name);
bd96f656 693 free(pp);
c36e914d 694}