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