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