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