translate: add fptr-arg checking
[ia32rtools.git] / tools / protoparse.h
CommitLineData
7637b6cc 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 */
c36e914d 8
39b168b8 9struct parsed_proto;
10
3ebea2cf 11struct parsed_type {
12 char *name;
13 unsigned int is_array:1;
14 unsigned int is_ptr:1;
a652aa9f 15 unsigned int is_struct:1; // split for args
5f70a34f 16 unsigned int is_retreg:1; // register to return to caller
17 unsigned int is_va_list:1;
2c31fb4c 18 unsigned int is_64bit:1;
b62264bc 19 unsigned int is_float:1; // float, double
3ebea2cf 20};
21
39b168b8 22struct parsed_proto_arg {
23 char *reg;
3ebea2cf 24 struct parsed_type type;
93b5bd18 25 struct parsed_proto *pp; // fptr or struct
39b168b8 26 void *datap;
8c83cc48 27 unsigned int is_saved:1; // not set here, for tool use
39b168b8 28};
29
c36e914d 30struct parsed_proto {
39b168b8 31 char name[256];
3ebea2cf 32 union {
33 struct parsed_type ret_type;
34 struct parsed_type type;
35 };
57b1b785 36 struct parsed_proto_arg arg[32];
c36e914d 37 int argc;
38 int argc_stack;
39 int argc_reg;
06c5d854 40 unsigned int is_func:1;
39b168b8 41 unsigned int is_stdcall:1;
c0050df6 42 unsigned int is_fastcall:1;
efea2951 43 unsigned int is_vararg:1; // vararg func
39b168b8 44 unsigned int is_fptr:1;
1fe8d40e 45 unsigned int is_import:1; // data import
e56ab892 46 unsigned int is_noreturn:1;
89ff3147 47 unsigned int is_unresolved:1;
1fe8d40e 48 unsigned int is_guessed:1; // for extra checking
1f84f6b3 49 unsigned int is_userstack:1;
61e29183 50 unsigned int is_include:1; // not from top-level header
51 unsigned int is_osinc:1; // OS/system library func
ebc4dc43 52 unsigned int is_cinc:1; // crt library func
179b79a9 53 unsigned int is_arg:1; // declared in some func arg
a652aa9f 54 unsigned int has_structarg:1;
1f84f6b3 55 unsigned int has_retreg:1;
c36e914d 56};
57
865f1aca 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
c36e914d 67static const char *hdrfn;
68static int hdrfline = 0;
69
a652aa9f 70static void pp_copy_arg(struct parsed_proto_arg *d,
71 const struct parsed_proto_arg *s);
72
61e29183 73static int b_pp_c_handler(char *proto, const char *fname,
ebc4dc43 74 int is_include, int is_osinc, int is_cinc);
865f1aca 75static int struct_handler(FILE *fhdr, char *proto, int *line);
bd96f656 76
61e29183 77static int do_protostrs(FILE *fhdr, const char *fname, int is_include)
c36e914d 78{
06c5d854 79 const char *finc_name;
bd96f656 80 const char *hdrfn_saved;
81 char protostr[256];
36595fd2 82 char path[256];
83 char fname_inc[256];
61e29183 84 int is_osinc;
ebc4dc43 85 int is_cinc;
39b168b8 86 FILE *finc;
c36e914d 87 int line = 0;
39b168b8 88 int ret;
c36e914d 89 char *p;
90
bd96f656 91 hdrfn_saved = hdrfn;
92 hdrfn = fname;
39b168b8 93
ebc4dc43 94 is_cinc = strstr(fname, "stdc.hlist") != NULL;
edeadc28 95 is_osinc = is_cinc || strstr(fname, "win32.hlist") != NULL;
179b79a9 96
bd96f656 97 while (fgets(protostr, sizeof(protostr), fhdr))
c36e914d 98 {
99 line++;
bd96f656 100 if (strncmp(protostr, "//#include ", 11) == 0) {
101 finc_name = protostr + 11;
06c5d854 102 p = strpbrk(finc_name, "\r\n ");
39b168b8 103 if (p != NULL)
104 *p = 0;
105
36595fd2 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");
39b168b8 116 if (finc == NULL) {
117 printf("%s:%d: can't open '%s'\n",
36595fd2 118 fname_inc, line, finc_name);
39b168b8 119 continue;
120 }
61e29183 121 ret = do_protostrs(finc, finc_name, 1);
39b168b8 122 fclose(finc);
bd96f656 123 if (ret < 0)
39b168b8 124 break;
125 continue;
126 }
bd96f656 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)
06c5d854 134 continue;
39b168b8 135
bd96f656 136 hdrfline = line;
137
865f1aca 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);
bd96f656 144 if (ret < 0)
c36e914d 145 break;
146 }
c36e914d 147
bd96f656 148 hdrfn = hdrfn_saved;
c36e914d 149
bd96f656 150 if (feof(fhdr))
151 return 0;
c36e914d 152
bd96f656 153 return -1;
c36e914d 154}
155
1f84f6b3 156static int get_regparm(char *dst, size_t dlen, char *p, int *retreg)
c36e914d 157{
1f84f6b3 158 int i = 0, o;
159
160 *retreg = 0;
c36e914d 161
162 if (*p != '<')
163 return 0;
164
1f84f6b3 165 i++;
166 if (p[i] == '*') {
167 *retreg = 1;
168 i++;
169 }
170
171 for (o = 0; o < dlen; i++) {
c36e914d 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..
64c59faf 183static const char *known_type_mod[] = {
184 "const",
185 "signed",
186 "unsigned",
3ebea2cf 187 "enum",
840257f6 188 "CONST",
92804a48 189 "volatile",
64c59faf 190};
191
3ebea2cf 192static const char *known_ptr_types[] = {
da87ae38 193 "FARPROC",
92804a48 194 "WNDPROC",
bf2471e9 195 "LINECALLBACK",
e56ab892 196 "HACCEL",
c36e914d 197 "HANDLE",
e56ab892 198 "HBITMAP",
79af284c 199 "HBRUSH",
bf2471e9 200 "HCALL",
e56ab892 201 "HCURSOR",
3ebea2cf 202 "HDC",
a652aa9f 203 "HFONT",
3ebea2cf 204 "HGDIOBJ",
e56ab892 205 "HGLOBAL",
79af284c 206 "HHOOK",
da87ae38 207 "HICON",
e56ab892 208 "HINSTANCE",
c7ed83dd 209 "HIMC", // DWORD in mingw, ptr in wine..
bf2471e9 210 "HLINE",
211 "HLINEAPP",
212 "HLOCAL",
e56ab892 213 "HMODULE",
da87ae38 214 "HPALETTE",
e56ab892 215 "HRGN",
216 "HRSRC",
217 "HKEY",
218 "HMENU",
383dff16 219 "HMONITOR",
7aca4698 220 "HWAVEOUT",
e56ab892 221 "HWND",
b62264bc 222 "PAPPBARDATA",
ef6b315d 223 "PBYTE",
a2c1d768 224 "PCRITICAL_SECTION",
b62264bc 225 "PDEVMODEA",
3ebea2cf 226 "PDWORD",
92804a48 227 "PFILETIME",
bf2471e9 228 "PLARGE_INTEGER",
79af284c 229 "PHANDLE",
94d447fb 230 "PHKEY",
231 "PLONG",
a2c1d768 232 "PMEMORY_BASIC_INFORMATION",
46411e6c 233 "PUINT",
f9327ad4 234 "PULARGE_INTEGER",
235 "PULONG_PTR",
3ebea2cf 236 "PVOID",
237 "PCVOID",
ef6b315d 238 "PWORD",
79af284c 239 "REFCLSID",
ead38b9e 240 "REFGUID",
79af284c 241 "REFIID",
354f5504 242 "SC_HANDLE",
243 "SERVICE_STATUS_HANDLE",
79af284c 244 "HOOKPROC",
e56ab892 245 "DLGPROC",
cdfaeed7 246 "TIMERPROC",
247 "WNDENUMPROC",
4f12f671 248 "va_list",
249 "__VALIST",
3ebea2cf 250};
251
252static const char *ignored_keywords[] = {
253 "extern",
254 "WINBASEAPI",
255 "WINUSERAPI",
4f12f671 256 "WINGDIAPI",
257 "WINADVAPI",
c36e914d 258};
259
3ebea2cf 260static int typecmp(const char *n, const char *t)
39b168b8 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)
3ebea2cf 268 return *n - *t;
39b168b8 269 }
270
3ebea2cf 271 return 0;
39b168b8 272}
273
3ebea2cf 274static const char *skip_type_mod(const char *n)
c36e914d 275{
64c59faf 276 int len;
39b168b8 277 int i;
c36e914d 278
64c59faf 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;
bd96f656 283 if (!my_isblank(n[len]))
284 continue;
64c59faf 285
286 n += len;
287 while (my_isblank(*n))
288 n++;
289 i = 0;
290 }
291
3ebea2cf 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
865f1aca 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
3ebea2cf 311 for (i = 0; i < ARRAY_SIZE(known_ptr_types); i++) {
312 if (typecmp(n, known_ptr_types[i]))
64c59faf 313 continue;
314
3ebea2cf 315 type->is_ptr = 1;
316 break;
c36e914d 317 }
318
a652aa9f 319 if (n[0] == 'L' && n[1] == 'P' && strncmp(n, "LPARAM", 6))
3ebea2cf 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);
5f70a34f 340 if (IS(type->name, "__VALIST") || IS(type->name, "va_list"))
341 type->is_va_list = 1;
a2c1d768 342 if (IS(type->name, "VOID"))
343 memcpy(type->name, "void", 4);
344
3ebea2cf 345 return ret;
c36e914d 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
a652aa9f 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
93b5bd18 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
1fe8d40e 410 if (IS_START(arg->pp->name, "guess"))
411 arg->pp->is_guessed = 1;
412
93b5bd18 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
c36e914d 426static int parse_protostr(char *protostr, struct parsed_proto *pp)
427{
39b168b8 428 struct parsed_proto_arg *arg;
c36e914d 429 char regparm[16];
430 char buf[256];
431 char cconv[32];
1f84f6b3 432 int is_retreg;
39b168b8 433 char *p, *p1;
93b5bd18 434 int xarg = 0;
3ebea2cf 435 int i, l;
c36e914d 436 int ret;
c36e914d 437
06c5d854 438 p = sskip(protostr);
c36e914d 439 if (p[0] == '/' && p[1] == '/') {
06c5d854 440 printf("%s:%d: commented out?\n", hdrfn, hdrfline);
c36e914d 441 p = sskip(p + 2);
442 }
443
865f1aca 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 / *
06c5d854 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
e56ab892 462 if (!strncmp(p, "DECLSPEC_NORETURN ", 18)) {
463 pp->is_noreturn = 1;
464 p = sskip(p + 18);
465 }
466
3ebea2cf 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 }
06c5d854 472
1fe8d40e 473 if (IS_START(p, "DECL_IMPORT ")) {
474 pp->is_import = 1;
475 p = sskip(p + 12);
476 }
477
3ebea2cf 478 ret = check_type(p, &pp->ret_type);
479 if (ret <= 0) {
63df67be 480 printf("%s:%d:%zd: unhandled return in '%s'\n",
c36e914d 481 hdrfn, hdrfline, (p - protostr) + 1, protostr);
39b168b8 482 return -1;
c36e914d 483 }
64c59faf 484 p = sskip(p + ret);
c36e914d 485
da87ae38 486 if (!strncmp(p, "noreturn ", 9)) {
487 pp->is_noreturn = 1;
488 p = sskip(p + 9);
489 }
490
06c5d854 491 if (!strchr(p, ')')) {
492 p = next_idt(buf, sizeof(buf), p);
493 p = sskip(p);
93b5bd18 494 if (!pp->is_arg && buf[0] == 0) {
495 printf("%s:%d:%zd: var name is missing\n",
06c5d854 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;
3ebea2cf 504 pp->ret_type.is_array = 1;
06c5d854 505 }
506 return p - protostr;
507 }
508
509 pp->is_func = 1;
510
39b168b8 511 if (*p == '(') {
512 pp->is_fptr = 1;
513 p = sskip(p + 1);
514 }
515
c36e914d 516 p = next_word(cconv, sizeof(cconv), p);
517 p = sskip(p);
518 if (cconv[0] == 0) {
63df67be 519 printf("%s:%d:%zd: cconv missing\n",
c36e914d 520 hdrfn, hdrfline, (p - protostr) + 1);
39b168b8 521 return -1;
c36e914d 522 }
523 if (IS(cconv, "__cdecl"))
524 pp->is_stdcall = 0;
525 else if (IS(cconv, "__stdcall"))
526 pp->is_stdcall = 1;
c0050df6 527 else if (IS(cconv, "__fastcall")) {
528 pp->is_fastcall = 1;
529 pp->is_stdcall = 1; // sort of..
530 }
c36e914d 531 else if (IS(cconv, "__thiscall"))
532 pp->is_stdcall = 1;
533 else if (IS(cconv, "__userpurge"))
de50b98b 534 pp->is_stdcall = 1; // IDA
c36e914d 535 else if (IS(cconv, "__usercall"))
de50b98b 536 pp->is_stdcall = 0; // IDA
1f84f6b3 537 else if (IS(cconv, "__userstack")) {
538 pp->is_stdcall = 0; // custom
539 pp->is_userstack = 1;
540 }
79af284c 541 else if (IS(cconv, "WINAPI") || IS(cconv, "PASCAL"))
64c59faf 542 pp->is_stdcall = 1;
c36e914d 543 else {
63df67be 544 printf("%s:%d:%zd: unhandled cconv: '%s'\n",
c36e914d 545 hdrfn, hdrfline, (p - protostr) + 1, cconv);
39b168b8 546 return -1;
547 }
548
549 if (pp->is_fptr) {
550 if (*p != '*') {
63df67be 551 printf("%s:%d:%zd: '*' expected\n",
39b168b8 552 hdrfn, hdrfline, (p - protostr) + 1);
553 return -1;
554 }
bd96f656 555 p++;
556 // XXX: skipping extra asterisks, for now
557 while (*p == '*')
558 p++;
559 p = sskip(p);
c36e914d 560 }
561
562 p = next_idt(buf, sizeof(buf), p);
563 p = sskip(p);
564 if (buf[0] == 0) {
de50b98b 565 //printf("%s:%d:%zd: func name missing\n",
566 // hdrfn, hdrfline, (p - protostr) + 1);
567 //return -1;
c36e914d 568 }
39b168b8 569 strcpy(pp->name, buf);
c36e914d 570
1f84f6b3 571 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
c36e914d 572 if (ret > 0) {
573 if (!IS(regparm, "eax") && !IS(regparm, "ax")
2b43685d 574 && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
c36e914d 575 {
63df67be 576 printf("%s:%d:%zd: bad regparm: %s\n",
c36e914d 577 hdrfn, hdrfline, (p - protostr) + 1, regparm);
39b168b8 578 return -1;
c36e914d 579 }
580 p += ret;
581 p = sskip(p);
582 }
583
39b168b8 584 if (pp->is_fptr) {
bd96f656 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 }
39b168b8 596 if (*p != ')') {
63df67be 597 printf("%s:%d:%zd: ')' expected\n",
39b168b8 598 hdrfn, hdrfline, (p - protostr) + 1);
599 return -1;
600 }
601 p = sskip(p + 1);
602 }
603
c36e914d 604 if (*p != '(') {
63df67be 605 printf("%s:%d:%zd: '(' expected, got '%c'\n",
c36e914d 606 hdrfn, hdrfline, (p - protostr) + 1, *p);
39b168b8 607 return -1;
c36e914d 608 }
609 p++;
610
39b168b8 611 // check for x(void)
612 p = sskip(p);
06c5d854 613 if ((!strncmp(p, "void", 4) || !strncmp(p, "VOID", 4))
614 && *sskip(p + 4) == ')')
39b168b8 615 p += 4;
616
c36e914d 617 while (1) {
618 p = sskip(p);
39b168b8 619 if (*p == ')') {
620 p++;
c36e914d 621 break;
39b168b8 622 }
840257f6 623 if (xarg > 0) {
624 if (*p != ',') {
625 printf("%s:%d:%zd: ',' expected\n",
626 hdrfn, hdrfline, (p - protostr) + 1);
627 return -1;
628 }
c36e914d 629 p = sskip(p + 1);
840257f6 630 }
c36e914d 631
7ba45c34 632 if (!strncmp(p, "...", 3)) {
633 pp->is_vararg = 1;
634 p = sskip(p + 3);
635 if (*p == ')') {
636 p++;
637 break;
638 }
63df67be 639 printf("%s:%d:%zd: ')' expected\n",
7ba45c34 640 hdrfn, hdrfline, (p - protostr) + 1);
641 return -1;
642 }
643
57b1b785 644 if (xarg >= ARRAY_SIZE(pp->arg)) {
645 printf("%s:%d:%zd: too many args\n",
646 hdrfn, hdrfline, (p - protostr) + 1);
647 return -1;
648 }
649
39b168b8 650 arg = &pp->arg[xarg];
c36e914d 651 xarg++;
652
39b168b8 653 p1 = p;
3ebea2cf 654 ret = check_type(p, &arg->type);
655 if (ret <= 0) {
63df67be 656 printf("%s:%d:%zd: unhandled type for arg%d\n",
c36e914d 657 hdrfn, hdrfline, (p - protostr) + 1, xarg);
39b168b8 658 return -1;
c36e914d 659 }
64c59faf 660 p = sskip(p + ret);
c36e914d 661
93b5bd18 662 if (*p == '(' || arg->type.is_struct) {
663 // func ptr or struct
664 ret = parse_arg(&p1, arg, xarg);
39b168b8 665 if (ret < 0) {
63df67be 666 printf("%s:%d:%zd: funcarg parse failed\n",
39b168b8 667 hdrfn, hdrfline, p1 - protostr);
668 return -1;
669 }
93b5bd18 670 p = p1;
39b168b8 671 }
672
c36e914d 673 p = next_idt(buf, sizeof(buf), p);
674 p = sskip(p);
675#if 0
676 if (buf[0] == 0) {
63df67be 677 printf("%s:%d:%zd: idt missing for arg%d\n",
c36e914d 678 hdrfn, hdrfline, (p - protostr) + 1, xarg);
39b168b8 679 return -1;
c36e914d 680 }
681#endif
39b168b8 682 arg->reg = NULL;
c36e914d 683
1f84f6b3 684 ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
c36e914d 685 if (ret > 0) {
686 p += ret;
687 p = sskip(p);
688
39b168b8 689 arg->reg = strdup(map_reg(regparm));
1f84f6b3 690 arg->type.is_retreg = is_retreg;
691 pp->has_retreg |= is_retreg;
c36e914d 692 }
a652aa9f 693
b62264bc 694 if (IS(arg->type.name, "float")
695 || IS(arg->type.name, "double"))
696 {
697 arg->type.is_float = 1;
698 }
699
f9327ad4 700 if (!arg->type.is_ptr && (strstr(arg->type.name, "int64")
701 || IS(arg->type.name, "double")))
7e50b291 702 {
2c31fb4c 703 arg->type.is_64bit = 1;
7e50b291 704 // hack..
7e50b291 705 pp_copy_arg(&pp->arg[xarg], arg);
2c31fb4c 706 arg = &pp->arg[xarg];
7e50b291 707 xarg++;
2c31fb4c 708 free(arg->type.name);
709 arg->type.name = strdup("dummy");
7e50b291 710 }
711
a652aa9f 712 ret = check_struct_arg(arg);
713 if (ret > 0) {
714 pp->has_structarg = 1;
715 arg->type.is_struct = 1;
716 free(arg->type.name);
717 arg->type.name = strdup("int");
718 for (l = 0; l < ret; l++) {
719 pp_copy_arg(&pp->arg[xarg], arg);
720 xarg++;
721 }
722 }
c36e914d 723 }
724
725 if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
726 if (pp->arg[0].reg != NULL) {
727 printf("%s:%d: %s with arg1 spec %s?\n",
728 hdrfn, hdrfline, cconv, pp->arg[0].reg);
729 }
730 pp->arg[0].reg = strdup("ecx");
731 }
732
733 if (xarg > 1 && IS(cconv, "__fastcall")) {
734 if (pp->arg[1].reg != NULL) {
735 printf("%s:%d: %s with arg2 spec %s?\n",
736 hdrfn, hdrfline, cconv, pp->arg[1].reg);
737 }
738 pp->arg[1].reg = strdup("edx");
739 }
740
741 pp->argc = xarg;
742
743 for (i = 0; i < pp->argc; i++) {
744 if (pp->arg[i].reg == NULL)
745 pp->argc_stack++;
746 else
747 pp->argc_reg++;
748 }
749
c0050df6 750 if (pp->argc == 1 && pp->arg[0].reg != NULL
751 && IS(pp->arg[0].reg, "ecx"))
752 {
753 pp->is_fastcall = 1;
754 }
755 else if (pp->argc_reg == 2
756 && pp->arg[0].reg != NULL && IS(pp->arg[0].reg, "ecx")
757 && pp->arg[1].reg != NULL && IS(pp->arg[1].reg, "edx"))
758 {
759 pp->is_fastcall = 1;
760 }
761
762 if (pp->is_vararg && (pp->is_stdcall || pp->is_fastcall)) {
763 printf("%s:%d: vararg %s?\n", hdrfn, hdrfline, cconv);
764 return -1;
765 }
766
39b168b8 767 return p - protostr;
c36e914d 768}
769
bd96f656 770static int pp_name_cmp(const void *p1, const void *p2)
771{
772 const struct parsed_proto *pp1 = p1, *pp2 = p2;
773 return strcmp(pp1->name, pp2->name);
774}
775
865f1aca 776static int ps_name_cmp(const void *p1, const void *p2)
777{
778 const struct parsed_struct *ps1 = p1, *ps2 = p2;
779 return strcmp(ps1->name, ps2->name);
780}
781
782// parsed struct cache
783static struct parsed_struct *ps_cache;
784static int ps_cache_size;
785static int ps_cache_alloc;
786
787static int struct_handler(FILE *fhdr, char *proto, int *line)
788{
789 struct parsed_struct *ps;
790 char lstr[256], *p;
791 int offset = 0;
792 int m = 0;
793 int ret;
794
795 if (ps_cache_size >= ps_cache_alloc) {
796 ps_cache_alloc = ps_cache_alloc * 2 + 64;
797 ps_cache = realloc(ps_cache, ps_cache_alloc
798 * sizeof(ps_cache[0]));
799 my_assert_not(ps_cache, NULL);
800 memset(ps_cache + ps_cache_size, 0,
801 (ps_cache_alloc - ps_cache_size)
802 * sizeof(ps_cache[0]));
803 }
804
805 ps = &ps_cache[ps_cache_size++];
806 ret = sscanf(proto, "struct %255s {", ps->name);
807 if (ret != 1) {
808 printf("%s:%d: struct parse failed\n", hdrfn, *line);
809 return -1;
810 }
811
812 while (fgets(lstr, sizeof(lstr), fhdr))
813 {
814 (*line)++;
815
816 p = sskip(lstr);
817 if (p[0] == '/' && p[1] == '/')
818 continue;
819 if (p[0] == '}')
820 break;
821
822 if (m >= ARRAY_SIZE(ps->members)) {
823 printf("%s:%d: too many struct members\n",
824 hdrfn, *line);
825 return -1;
826 }
827
828 hdrfline = *line;
829 ret = parse_protostr(p, &ps->members[m].pp);
830 if (ret < 0) {
831 printf("%s:%d: struct member #%d/%02x "
832 "doesn't parse\n", hdrfn, *line,
833 m, offset);
834 return -1;
835 }
836 ps->members[m].offset = offset;
837 offset += 4;
838 m++;
839 }
840
841 ps->member_count = m;
842
843 return 0;
844}
845
846// parsed proto cache
bd96f656 847static struct parsed_proto *pp_cache;
848static int pp_cache_size;
849static int pp_cache_alloc;
850
61e29183 851static int b_pp_c_handler(char *proto, const char *fname,
ebc4dc43 852 int is_include, int is_osinc, int is_cinc)
bd96f656 853{
854 int ret;
855
856 if (pp_cache_size >= pp_cache_alloc) {
857 pp_cache_alloc = pp_cache_alloc * 2 + 64;
858 pp_cache = realloc(pp_cache, pp_cache_alloc
859 * sizeof(pp_cache[0]));
860 my_assert_not(pp_cache, NULL);
861 memset(pp_cache + pp_cache_size, 0,
862 (pp_cache_alloc - pp_cache_size)
863 * sizeof(pp_cache[0]));
864 }
865
866 ret = parse_protostr(proto, &pp_cache[pp_cache_size]);
867 if (ret < 0)
868 return -1;
869
61e29183 870 pp_cache[pp_cache_size].is_include = is_include;
871 pp_cache[pp_cache_size].is_osinc = is_osinc;
ebc4dc43 872 pp_cache[pp_cache_size].is_cinc = is_cinc;
bd96f656 873 pp_cache_size++;
874 return 0;
875}
876
865f1aca 877static void build_caches(FILE *fhdr)
c36e914d 878{
1f906263 879 long pos;
c36e914d 880 int ret;
881
1f906263 882 pos = ftell(fhdr);
bd96f656 883 rewind(fhdr);
884
61e29183 885 ret = do_protostrs(fhdr, hdrfn, 0);
bd96f656 886 if (ret < 0)
887 exit(1);
888
889 qsort(pp_cache, pp_cache_size, sizeof(pp_cache[0]), pp_name_cmp);
865f1aca 890 qsort(ps_cache, ps_cache_size, sizeof(ps_cache[0]), ps_name_cmp);
1f906263 891 fseek(fhdr, pos, SEEK_SET);
bd96f656 892}
893
36595fd2 894static const struct parsed_proto *proto_parse(FILE *fhdr, const char *sym,
895 int quiet)
bd96f656 896{
897 const struct parsed_proto *pp_ret;
898 struct parsed_proto pp_search;
7aca4698 899 char *p;
bd96f656 900
901 if (pp_cache == NULL)
865f1aca 902 build_caches(fhdr);
bd96f656 903
f9327ad4 904 // ugh...
905 if (sym[0] == '_' && !IS_START(sym, "__W"))
bd96f656 906 sym++;
c36e914d 907
bd96f656 908 strcpy(pp_search.name, sym);
7aca4698 909 p = strchr(pp_search.name, '@');
910 if (p != NULL)
911 *p = 0;
912
bd96f656 913 pp_ret = bsearch(&pp_search, pp_cache, pp_cache_size,
914 sizeof(pp_cache[0]), pp_name_cmp);
36595fd2 915 if (pp_ret == NULL && !quiet)
c36e914d 916 printf("%s: sym '%s' is missing\n", hdrfn, sym);
bd96f656 917
918 return pp_ret;
919}
920
865f1aca 921static const struct parsed_proto *proto_lookup_struct(FILE *fhdr,
922 const char *type, int offset)
923{
924 struct parsed_struct ps_search, *ps;
925 int m;
926
927 if (pp_cache == NULL)
928 build_caches(fhdr);
929 if (ps_cache_size == 0)
930 return NULL;
931
932 while (my_isblank(*type))
933 type++;
934 if (!strncmp(type, "struct", 6) && my_isblank(type[6]))
935 type += 7;
936
937 if (sscanf(type, "%255s", ps_search.name) != 1)
938 return NULL;
939
940 ps = bsearch(&ps_search, ps_cache, ps_cache_size,
941 sizeof(ps_cache[0]), ps_name_cmp);
942 if (ps == NULL) {
943 printf("%s: struct '%s' is missing\n",
944 hdrfn, ps_search.name);
945 return NULL;
946 }
947
948 for (m = 0; m < ps->member_count; m++) {
949 if (ps->members[m].offset == offset)
950 return &ps->members[m].pp;
951 }
952
953 return NULL;
954}
955
a652aa9f 956static void pp_copy_arg(struct parsed_proto_arg *d,
957 const struct parsed_proto_arg *s)
958{
959 memcpy(d, s, sizeof(*d));
960
961 if (s->reg != NULL) {
962 d->reg = strdup(s->reg);
963 my_assert_not(d->reg, NULL);
964 }
965 if (s->type.name != NULL) {
966 d->type.name = strdup(s->type.name);
967 my_assert_not(d->type.name, NULL);
968 }
93b5bd18 969 if (s->pp != NULL) {
970 d->pp = malloc(sizeof(*d->pp));
971 my_assert_not(d->pp, NULL);
972 memcpy(d->pp, s->pp, sizeof(*d->pp));
a652aa9f 973 }
974}
975
bd96f656 976struct parsed_proto *proto_clone(const struct parsed_proto *pp_c)
977{
978 struct parsed_proto *pp;
979 int i;
980
981 pp = malloc(sizeof(*pp));
982 my_assert_not(pp, NULL);
983 memcpy(pp, pp_c, sizeof(*pp)); // lazy..
984
985 // do the actual deep copy..
a652aa9f 986 for (i = 0; i < pp_c->argc; i++)
987 pp_copy_arg(&pp->arg[i], &pp_c->arg[i]);
bd96f656 988 if (pp_c->ret_type.name != NULL)
989 pp->ret_type.name = strdup(pp_c->ret_type.name);
c36e914d 990
bd96f656 991 return pp;
c36e914d 992}
993
27ebfaed 994
995static inline int pp_cmp_func(const struct parsed_proto *pp1,
996 const struct parsed_proto *pp2)
997{
998 int i;
999
1000 if (pp1->argc != pp2->argc || pp1->argc_reg != pp2->argc_reg)
1001 return 1;
17ed469e 1002 if (pp1->is_stdcall != pp2->is_stdcall)
1003 return 1;
1004
1005 // because of poor void return detection, return is not
1006 // checked for now to avoid heaps of false positives
1007
1008 for (i = 0; i < pp1->argc; i++) {
1009 if ((pp1->arg[i].reg != NULL) != (pp2->arg[i].reg != NULL))
1010 return 1;
1011
1012 if ((pp1->arg[i].reg != NULL)
1013 && !IS(pp1->arg[i].reg, pp2->arg[i].reg))
1014 {
1015 return 1;
27ebfaed 1016 }
1017 }
1018
1019 return 0;
1020}
1021
17ed469e 1022static inline int pp_compatible_func(
1023 const struct parsed_proto *pp_site,
1024 const struct parsed_proto *pp_callee)
1025{
1026 if (pp_cmp_func(pp_site, pp_callee) == 0)
1027 return 1;
1028
1029 if (pp_site->argc_stack == 0 && pp_site->is_fastcall
1030 && pp_callee->argc_stack == 0
1031 && (pp_callee->is_fastcall || pp_callee->argc_reg == 0)
1032 && pp_site->argc_reg > pp_callee->argc_reg)
1033 /* fascall compatible callee doesn't use all args -> ok */
1034 return 1;
1035
1036 return 0;
1037}
1038
b74c31e3 1039static inline void pp_print(char *buf, size_t buf_size,
1040 const struct parsed_proto *pp)
1041{
1042 size_t l;
1043 int i;
1044
1045 snprintf(buf, buf_size, "%s %s(", pp->ret_type.name, pp->name);
1046 l = strlen(buf);
1047
1048 for (i = 0; i < pp->argc_reg; i++) {
1049 snprintf(buf + l, buf_size - l, "%s%s",
1050 i == 0 ? "" : ", ", pp->arg[i].reg);
1051 l = strlen(buf);
1052 }
1053 if (pp->argc_stack > 0) {
1054 snprintf(buf + l, buf_size - l, "%s{%d stack}",
1055 i == 0 ? "" : ", ", pp->argc_stack);
1056 l = strlen(buf);
1057 }
1058 snprintf(buf + l, buf_size - l, ")");
1059}
1060
bd96f656 1061static inline void proto_release(struct parsed_proto *pp)
c36e914d 1062{
1063 int i;
1064
1065 for (i = 0; i < pp->argc; i++) {
39b168b8 1066 if (pp->arg[i].reg != NULL)
c36e914d 1067 free(pp->arg[i].reg);
3ebea2cf 1068 if (pp->arg[i].type.name != NULL)
1069 free(pp->arg[i].type.name);
93b5bd18 1070 if (pp->arg[i].pp != NULL)
1071 free(pp->arg[i].pp);
c36e914d 1072 }
3ebea2cf 1073 if (pp->ret_type.name != NULL)
1074 free(pp->ret_type.name);
bd96f656 1075 free(pp);
865f1aca 1076
1077 (void)proto_lookup_struct;
c36e914d 1078}