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