64d782020db3c61d2ed58a31d27369253ba9e52e
[platform/upstream/ltrace.git] / dwarf_prototypes.c
1 /* Most of this is Copyright Dima Kogan <dima@secretsauce.net>
2  *
3  * Pieces of this were taken from dwarf_prototypes.c in the dwarves project.
4  * Those are Copyright (C) 2008 Arnaldo Carvalho de Melo <acme@redhat.com>.
5  *
6  * This program is free software; you can redistribute it and/or modify it under
7  * the terms of version 2 of the GNU General Public License as published by the
8  * Free Software Foundation.
9  *
10  */
11 #include <stdio.h>
12 #include <elfutils/libdwfl.h>
13 #include <dwarf.h>
14 #include <stdlib.h>
15 #include <errno.h>
16 #include <string.h>
17
18 #include "config.h"
19 #include "prototype.h"
20 #include "type.h"
21 #include "param.h"
22 #include "dict.h"
23 #include "lens.h"
24 #include "lens_enum.h"
25 #include "value.h"
26 #include "expr.h"
27 #include "library.h"
28 #include "options.h"
29 #include "filter.h"
30
31
32 //#define DUMP_PROTOTYPES
33
34 #if 1
35 #define complain( die, format, ... )                                                    \
36         fprintf(stderr, "%s() die '%s' @ 0x%lx: " format "\n",          \
37                         __func__, dwarf_diename(die), dwarf_dieoffset(die),     \
38                         ##__VA_ARGS__ )
39 #else
40 #define complain( die, format, ... )
41 #endif
42
43 // A map from DIE addresses (Dwarf_Off) to type structures (struct
44 // arg_type_info*). This is created and filled in at the start of each import,
45 // and deleted when the import is complete
46 static struct dict type_hash;
47
48
49 static bool get_type(struct arg_type_info** info, Dwarf_Die* type_die);
50
51
52 #if 0
53 static bool _dump_dwarf_tree(Dwarf_Die* die, int indent)
54 {
55     while (1) {
56         fprintf(stderr, "%*sprocessing unit: 0x%02x/'%s'\n", indent*4, "",
57                dwarf_tag(die), dwarf_diename(die));
58
59         Dwarf_Die child;
60         if (dwarf_child(die, &child) == 0) {
61                         if (!_dump_dwarf_tree(&child, indent+1))
62                                 return false;
63         }
64
65         int res = dwarf_siblingof(die, die);
66         if (res == 0 ) continue;     // sibling exists
67         if (res < 0 )  return false; // error
68         break;                       // no sibling exists
69     }
70
71     return true;
72 }
73
74 static bool dump_dwarf_tree(Dwarf_Die* die)
75 {
76     return _dump_dwarf_tree( die, 0 );
77 }
78 #endif
79
80 #ifdef DUMP_PROTOTYPES
81 static bool _dump_ltrace_tree(const struct arg_type_info* info, int indent)
82 {
83         if (indent > 7) {
84                 fprintf(stderr, "%*s%p ...\n", indent*4, "", (void*)info);
85                 return true;
86         }
87
88         if (info == NULL) {
89                 fprintf(stderr, "%*s%p NULL\n", indent*4, "", (void*)info);
90                 return true;
91         }
92
93         switch (info->type) {
94         case ARGTYPE_VOID:
95                 fprintf(stderr, "%*s%p void\n", indent*4, "", (void*)info);
96                 break;
97
98         case ARGTYPE_INT:
99         case ARGTYPE_UINT:
100         case ARGTYPE_LONG:
101         case ARGTYPE_ULONG:
102         case ARGTYPE_CHAR:
103         case ARGTYPE_SHORT:
104         case ARGTYPE_USHORT:
105         case ARGTYPE_FLOAT:
106         case ARGTYPE_DOUBLE:
107                 fprintf(stderr, "%*s%p base\n", indent*4, "", (void*)info);
108                 break;
109
110         case ARGTYPE_ARRAY:
111                 fprintf(stderr, "%*s%p array. elements not printed\n", indent*4, "",
112                                 (void*)info);
113                 break;
114
115         case ARGTYPE_POINTER:
116                 fprintf(stderr, "%*s%p pointer to...\n", indent*4, "", (void*)info);
117                 _dump_ltrace_tree( info->u.ptr_info.info, indent+1 );
118                 break;
119
120         case ARGTYPE_STRUCT:
121                 fprintf(stderr, "%*s%p struct...\n", indent*4, "", (void*)info);
122                 struct struct_field
123                 {
124                         struct arg_type_info *info;
125                         int own_info;
126                 }* elements = (struct struct_field*)info->u.entries.data;
127                 unsigned int i;
128                 for(i=0; i<info->u.entries.size; i++)
129                         _dump_ltrace_tree( elements[i].info, indent+1 );
130                 break;
131
132         default:
133                 fprintf(stderr, "%*s%p unknown type\n", indent*4, "", (void*)info);
134                 return false;;
135         }
136
137         return true;
138 }
139
140 static bool dump_ltrace_tree(const struct arg_type_info* info)
141 {
142         return _dump_ltrace_tree( info, 0 );
143 }
144 #endif
145
146
147
148 static uint64_t attr_numeric(Dwarf_Die *die, uint32_t name)
149 {
150         Dwarf_Attribute attr;
151         uint32_t form;
152
153         if (dwarf_attr(die, name, &attr) == NULL)
154                 return 0;
155
156         form = dwarf_whatform(&attr);
157
158         switch (form) {
159         case DW_FORM_addr: {
160                 Dwarf_Addr addr;
161                 if (dwarf_formaddr(&attr, &addr) == 0)
162                         return addr;
163         }
164                 break;
165         case DW_FORM_data1:
166         case DW_FORM_data2:
167         case DW_FORM_data4:
168         case DW_FORM_data8:
169         case DW_FORM_sdata:
170         case DW_FORM_udata: {
171                 Dwarf_Word value;
172                 if (dwarf_formudata(&attr, &value) == 0)
173                         return value;
174         }
175                 break;
176         case DW_FORM_flag:
177         case DW_FORM_flag_present: {
178                 bool value;
179                 if (dwarf_formflag(&attr, &value) == 0)
180                         return value;
181         }
182                 break;
183         default:
184                 complain(die, "DW_AT_<0x%x>=0x%x", name, form);
185                 break;
186         }
187
188         return 0;
189 }
190
191 static enum arg_type get_base_type(Dwarf_Die* die)
192 {
193         int encoding = attr_numeric(die, DW_AT_encoding);
194
195         if (encoding == DW_ATE_void )
196                 return ARGTYPE_VOID;
197
198         if (encoding == DW_ATE_signed_char || encoding == DW_ATE_unsigned_char )
199                 return ARGTYPE_CHAR;
200
201         if (encoding == DW_ATE_signed   ||
202                 encoding == DW_ATE_unsigned ||
203                 encoding == DW_ATE_boolean) {
204                 bool is_signed = (encoding == DW_ATE_signed);
205                 switch (attr_numeric(die, DW_AT_byte_size)) {
206                 case sizeof(char):
207                         return ARGTYPE_CHAR;
208
209                 case sizeof(short):
210                         return is_signed ? ARGTYPE_SHORT : ARGTYPE_USHORT;
211
212                 case sizeof(int):
213                         return is_signed ? ARGTYPE_INT : ARGTYPE_UINT;
214
215                 case sizeof(long):
216                         return is_signed ? ARGTYPE_LONG : ARGTYPE_ULONG;
217
218                 default:
219                         complain(die, "");
220                         exit(1);
221                 }
222         }
223
224         if (encoding == DW_ATE_float) {
225                 switch (attr_numeric(die, DW_AT_byte_size)) {
226                 case sizeof(float):
227                         return ARGTYPE_FLOAT;
228
229                 case sizeof(double):
230                         return ARGTYPE_DOUBLE;
231
232                 default:
233                         // things like long doubles. ltrace has no support yet, so I just
234                         // say "void"
235                         return ARGTYPE_VOID;
236                 }
237         }
238
239 #if 0
240         if (encoding == DW_ATE_complex_float) {
241                 switch (attr_numeric(die, DW_AT_byte_size)) {
242                 case 2*sizeof(float):
243                         return ARGTYPE_FLOAT;
244
245                 case 2*sizeof(double):
246                         return ARGTYPE_DOUBLE;
247
248                 default:
249                         // things like long doubles. ltrace has no support yet, so I just
250                         // say "void"
251                         return ARGTYPE_VOID;
252                 }
253         }
254 #endif
255
256         // Unknown encoding. I just say void
257         complain(die, "Unknown base type. Returning 'void'");
258         return ARGTYPE_VOID;
259 }
260
261 static bool get_type_die(Dwarf_Die* type_die, Dwarf_Die* die)
262 {
263         Dwarf_Attribute attr;
264         return
265                 dwarf_attr(die, DW_AT_type, &attr) != NULL &&
266                 dwarf_formref_die(&attr, type_die) != NULL;
267 }
268
269 static size_t dwarf_die_hash(const void* x)
270 {
271         return *(const Dwarf_Off*)x;
272 }
273 static int dwarf_die_eq(const void* a, const void* b)
274 {
275         return *(const Dwarf_Off*)a == *(const Dwarf_Off*)b;
276 }
277
278 static bool get_enum(struct arg_type_info* enum_info, Dwarf_Die* parent)
279 {
280         enum_info->type = ARGTYPE_INT;
281
282         struct enum_lens *lens = calloc(1, sizeof(struct enum_lens));
283         if (lens == NULL) {
284                 complain(parent, "alloc error");
285                 return false;
286         }
287         lens_init_enum(lens);
288         enum_info->lens = &lens->super;
289
290         Dwarf_Die die;
291         if (dwarf_child(parent, &die) != 0) {
292                 // empty enum. we're done
293                 return true;
294         }
295
296         while(1) {
297                 complain(&die, "enum element: 0x%02x/'%s'", dwarf_tag(&die),
298                                  dwarf_diename(&die));
299
300                 if (dwarf_tag(&die) != DW_TAG_enumerator) {
301                         complain(&die, "Enums can have ONLY DW_TAG_enumerator elements");
302                         return false;
303                 }
304
305                 if (!dwarf_hasattr(&die, DW_AT_const_value)) {
306                         complain(&die, "Enums MUST have DW_AT_const_value values");
307                         return false;
308                 }
309
310                 const char* key = dwarf_diename(&die);
311                 if (key == NULL) {
312                         complain(&die, "Enums must have a DW_AT_name key");
313                         return false;
314                 }
315                 const char* dupkey = strdup(key);
316                 if (dupkey == NULL) {
317                         complain(&die, "Couldn't duplicate enum key");
318                         return false;
319                 }
320
321                 struct value* value = calloc( 1, sizeof(struct value));
322                 if (value == NULL) {
323                         complain(&die, "Couldn't alloc enum value");
324                         return false;
325                 }
326
327                 value_init_detached(value, NULL, type_get_simple( ARGTYPE_INT ), 0);
328                 value_set_word(value, attr_numeric(&die, DW_AT_const_value));
329
330                 if (lens_enum_add( lens, dupkey, 0, value, 0 )) {
331                         complain(&die, "Couldn't add enum element");
332                         return false;
333                 }
334
335                 int res = dwarf_siblingof(&die, &die);
336                 if (res == 0) continue;     /* sibling exists    */
337                 if (res < 0)  return false; /* error             */
338                 break;                      /* no sibling exists */
339         }
340
341         return true;
342 }
343
344 static bool get_array(struct arg_type_info* array_info, Dwarf_Die* parent)
345 {
346         Dwarf_Die type_die;
347         if (!get_type_die( &type_die, parent )) {
348                 complain( parent, "Array has unknown type" );
349                 return false;
350         }
351
352         struct arg_type_info* info;
353         if (!get_type( &info, &type_die )) {
354                 complain( parent, "Couldn't figure out array's type" );
355                 return false;
356         }
357
358         Dwarf_Die subrange;
359         if (dwarf_child(parent, &subrange) != 0) {
360                 complain(parent,
361                                  "Array must have a DW_TAG_subrange_type child, but has none");
362                 return false;
363         }
364
365         Dwarf_Die next_subrange;
366         if (dwarf_siblingof(&subrange, &next_subrange) <= 0) {
367                 complain(parent,
368                                  "Array must have exactly one DW_TAG_subrange_type child");
369                 return false;
370         }
371
372         if (dwarf_hasattr(&subrange, DW_AT_lower_bound)) {
373                 if (attr_numeric(&subrange, DW_AT_lower_bound) != 0) {
374                         complain( parent,
375                                           "Array subrange has a nonzero lower bound. Don't know what to do");
376                         return false;
377                 }
378         }
379
380         int N;
381         if (!dwarf_hasattr(&subrange, DW_AT_upper_bound)) {
382                 // no upper bound is defined. This is probably a variable-width array,
383                 // and I don't know how long it is. Let's say 0 to be safe
384                 N = 0;
385         }
386         else
387                 N = attr_numeric(&subrange, DW_AT_upper_bound)+1;
388
389         // I'm not checking the subrange type. It should be some sort of integer,
390         // and I don't know what it would mean for it to be something else
391
392         struct value* value = calloc( 1, sizeof(struct value));
393         if (value == NULL) {
394                 complain(&subrange, "Couldn't alloc length value");
395                 return false;
396         }
397         value_init_detached(value, NULL, type_get_simple( ARGTYPE_INT ), 0);
398         value_set_word(value, N );
399
400         struct expr_node* length = calloc( 1, sizeof(struct expr_node));
401         if (length == NULL) {
402                 complain(&subrange, "Couldn't alloc length expr");
403                 return false;
404         }
405         expr_init_const(length, value);
406
407         type_init_array(array_info, info, 0, length, 0 );
408
409         return true;
410 }
411
412 static bool get_structure(struct arg_type_info* struct_info, Dwarf_Die* parent)
413 {
414         type_init_struct(struct_info);
415
416         Dwarf_Die die;
417         if (dwarf_child(parent, &die) != 0) {
418                 // no elements; we're done
419                 return true;
420         }
421
422         while(1) {
423                 complain(&die, "member: 0x%02x", dwarf_tag(&die));
424
425                 if (dwarf_tag(&die) != DW_TAG_member) {
426                         complain(&die, "Structure can have ONLY DW_TAG_member");
427                         return false;
428                 }
429
430                 Dwarf_Die type_die;
431                 if (!get_type_die( &type_die, &die )) {
432                         complain( &die, "Couldn't get type of element");
433                         return false;
434                 }
435
436                 struct arg_type_info* member_info = NULL;
437                 if (!get_type( &member_info, &type_die )) {
438                         complain(&die, "Couldn't parse type from DWARF data");
439                         return false;
440                 }
441                 type_struct_add( struct_info, member_info, 0 );
442
443                 int res = dwarf_siblingof(&die, &die);
444                 if (res == 0) continue;     /* sibling exists    */
445                 if (res < 0)  return false; /* error             */
446                 break;                      /* no sibling exists */
447         }
448
449         return true;
450 }
451
452 // Reads the type in the die into the given structure
453 // Returns true on sucess
454 static bool get_type(struct arg_type_info** info, Dwarf_Die* type_die)
455 {
456         Dwarf_Off die_offset = dwarf_dieoffset(type_die);
457         struct arg_type_info** found_type = dict_find(&type_hash, &die_offset );
458         if (found_type != NULL) {
459                 *info = *found_type;
460                 complain(type_die, "Read pre-computed type: %p", *info);
461                 return true;
462         }
463
464         Dwarf_Die next_die;
465
466         switch (dwarf_tag(type_die)) {
467         case DW_TAG_base_type:
468                 *info = type_get_simple( get_base_type( type_die ));
469                 complain(type_die, "Storing base type: %p", *info);
470                 dict_insert( &type_hash, &die_offset, info );
471                 return true;
472
473         case DW_TAG_subroutine_type:
474         case DW_TAG_inlined_subroutine:
475                 // function pointers are stored as void*. If ltrace tries to dereference
476                 // these, it'll get a segfault
477                 *info = type_get_simple( ARGTYPE_VOID );
478                 complain(type_die, "Storing subroutine type: %p", *info);
479                 dict_insert( &type_hash, &die_offset, info );
480                 return true;
481
482         case DW_TAG_pointer_type:
483
484                 if (!get_type_die(&next_die, type_die )) {
485                         // the pointed-to type isn't defined, so I report a void*
486                         *info = type_get_simple( ARGTYPE_VOID );
487                         complain(type_die, "Storing void-pointer type: %p", *info);
488                         dict_insert( &type_hash, &die_offset, info );
489                         return true;
490                 }
491
492                 *info = calloc( 1, sizeof(struct arg_type_info));
493                 if (*info == NULL) {
494                         complain(type_die, "alloc error");
495                         return false;
496                 }
497                 type_init_pointer(*info, NULL, 0);
498
499                 complain(type_die, "Storing pointer type: %p", *info);
500                 dict_insert( &type_hash, &die_offset, info );
501                 return get_type( &(*info)->u.ptr_info.info, &next_die );
502
503         case DW_TAG_structure_type:
504                 *info = calloc( 1, sizeof(struct arg_type_info));
505                 if (*info == NULL) {
506                         complain(type_die, "alloc error");
507                         return false;
508                 }
509
510                 complain(type_die, "Storing struct type: %p", *info);
511                 dict_insert( &type_hash, &die_offset, info );
512                 return get_structure( *info, type_die );
513
514
515         case DW_TAG_typedef: ;
516         case DW_TAG_const_type: ;
517         case DW_TAG_volatile_type: ;
518                 // Various tags are simply pass-through, so I just keep going
519                 bool res = true;
520                 if (get_type_die(&next_die, type_die )) {
521                         complain(type_die, "Storing const/typedef type: %p", *info);
522                         res = get_type( info, &next_die );
523                 } else {
524                         // no type. Use 'void'. Normally I'd think this is bogus, but stdio
525                         // typedefs something to void
526                         *info = type_get_simple( ARGTYPE_VOID );
527                         complain(type_die, "Storing void type: %p", *info);
528                 }
529                 if (res )
530                         dict_insert( &type_hash, &die_offset, info );
531                 return res;
532
533         case DW_TAG_enumeration_type:
534                 // We have an enumeration. This has type "int", but has a particular
535                 // lens to handle the enum
536                 *info = calloc( 1, sizeof(struct arg_type_info));
537                 if (*info == NULL) {
538                         complain(type_die, "alloc error");
539                         return false;
540                 }
541
542                 complain(type_die, "Storing enum int: %p", *info);
543                 dict_insert( &type_hash, &die_offset, info );
544                 return get_enum( *info, type_die );
545
546         case DW_TAG_array_type:
547                 *info = calloc( 1, sizeof(struct arg_type_info));
548                 if (*info == NULL) {
549                         complain(type_die, "alloc error");
550                         return false;
551                 }
552
553                 complain(type_die, "Storing array: %p", *info);
554                 dict_insert( &type_hash, &die_offset, info );
555                 return get_array( *info, type_die );
556
557         case DW_TAG_union_type:
558                 *info = type_get_simple( ARGTYPE_VOID );
559                 complain(type_die, "Storing union-as-void type: %p", *info);
560                 return true;
561
562         default:
563                 complain(type_die, "Unknown type tag 0x%x", dwarf_tag(type_die));
564                 break;
565         }
566
567         return false;
568 }
569
570 static bool get_prototype(struct prototype* proto, Dwarf_Die* subroutine)
571 {
572         // First, look at the return type. This is stored in a DW_AT_type tag in the
573         // subroutine DIE. If there is no such tag, this function returns void
574         Dwarf_Die return_type_die;
575         if (!get_type_die(&return_type_die, subroutine )) {
576                 proto->return_info = type_get_simple( ARGTYPE_VOID );
577                 proto->own_return_info = 0;
578         } else {
579                 proto->return_info = calloc( 1, sizeof( struct arg_type_info ));
580                 if (proto->return_info == NULL) {
581                         complain(subroutine, "Couldn't alloc return type");
582                         return false;
583                 }
584                 proto->own_return_info = 0;
585
586                 if (!get_type( &proto->return_info, &return_type_die )) {
587                         complain(subroutine, "Couldn't get return type");
588                         return false;
589                 }
590         }
591
592
593         // Now look at the arguments
594         Dwarf_Die arg_die;
595         if (dwarf_child(subroutine, &arg_die) != 0) {
596                 // no args. We're done
597                 return true;
598         }
599
600         while(1) {
601                 if (dwarf_tag(&arg_die) != DW_TAG_formal_parameter )
602                         goto next_prototype_argument;
603
604                 complain(&arg_die, "arg: 0x%02x", dwarf_tag(&arg_die));
605
606                 Dwarf_Die type_die;
607                 if (!get_type_die(&type_die, &arg_die )) {
608                         complain(&arg_die, "Couldn't get the argument type die");
609                         return false;
610                 }
611
612                 struct arg_type_info* arg_type_info = NULL;
613                 if (!get_type( &arg_type_info, &type_die )) {
614                         complain(&arg_die, "Couldn't parse arg type from DWARF data");
615                         return false;
616                 }
617
618                 struct param param;
619                 param_init_type(&param, arg_type_info, 0);
620                 if (prototype_push_param(proto, &param) <0) {
621                         complain(&arg_die, "couldn't add argument to the prototype");
622                         return false;
623                 }
624
625 #ifdef DUMP_PROTOTYPES
626                 fprintf(stderr, "Adding argument:\n");
627                 dump_ltrace_tree(arg_type_info);
628 #endif
629
630         next_prototype_argument: ;
631                 int res = dwarf_siblingof(&arg_die, &arg_die);
632                 if (res == 0) continue;     /* sibling exists    */
633                 if (res < 0)  return false; /* error             */
634                 break;                      /* no sibling exists */
635         }
636
637         return true;
638 }
639
640 static bool process_die_compileunit(struct protolib* plib, struct library* lib,
641                                                                         Dwarf_Die* parent)
642 {
643         Dwarf_Die die;
644         if (dwarf_child(parent, &die) != 0) {
645                 // no child nodes, so nothing to do
646                 return true;
647         }
648
649         while (1) {
650                 if (dwarf_tag(&die) == DW_TAG_subprogram) {
651                         const char* function_name = dwarf_diename(&die);
652
653                         complain(&die, "subroutine_type: 0x%02x; function '%s'",
654                                          dwarf_tag(&die), function_name);
655
656                         struct prototype* proto =
657                                 protolib_lookup_prototype(plib, function_name, true );
658
659                         if (proto != NULL) {
660                                 complain(&die, "Prototype already exists. Skipping");
661                                 goto next_prototype;
662                         }
663
664                         if (!filter_matches_symbol(options.plt_filter,    function_name, lib) &&
665                                 !filter_matches_symbol(options.static_filter, function_name, lib) &&
666                                 !filter_matches_symbol(options.export_filter, function_name, lib)) {
667                                 complain(&die, "Prototype not requested by any filter");
668                                 goto next_prototype;
669                         }
670
671                         proto = malloc(sizeof(struct prototype));
672                         if (proto == NULL) {
673                                 complain(&die, "couldn't alloc prototype");
674                                 return false;
675                         }
676                         prototype_init( proto );
677
678                         if (!get_prototype(proto, &die )) {
679                                 complain(&die, "couldn't get prototype");
680                                 return false;
681                         }
682
683                         protolib_add_prototype(plib, function_name, 0, proto);
684                 }
685
686                 next_prototype:;
687                 int res = dwarf_siblingof(&die, &die);
688                 if (res == 0) continue;     /* sibling exists    */
689                 if (res < 0)  return false; /* error             */
690                 break;                      /* no sibling exists */
691         }
692
693         return true;
694 }
695
696 static bool import(struct protolib* plib, struct library* lib, Dwfl* dwfl)
697 {
698         dict_init(&type_hash, sizeof(Dwarf_Off), sizeof(struct arg_type_info*),
699                           dwarf_die_hash, dwarf_die_eq, NULL );
700
701         Dwarf_Addr bias;
702     Dwarf_Die* die = NULL;
703     while ((die = dwfl_nextcu(dwfl, die, &bias)) != NULL) {
704         if (dwarf_tag(die) == DW_TAG_compile_unit) {
705             if (!process_die_compileunit(plib, lib, die)) {
706                 complain(die, "Error reading compile unit");
707                                 exit(1);
708                                 return false;
709             }
710         } else {
711             complain(die, "DW_TAG_compile_unit expected");
712                         exit(1);
713             return false;
714         }
715     }
716
717         dict_destroy( &type_hash, NULL, NULL, NULL );
718         return true;
719 }
720
721 bool import_DWARF_prototypes(struct protolib* plib, struct library* lib,
722                                                          Dwfl *dwfl)
723 {
724         if (plib == NULL) {
725                 plib = protolib_cache_default(&g_protocache, lib->soname, 0);
726                 if (plib == NULL) {
727                         fprintf(stderr, "Error loading protolib %s: %s.\n",
728                                         lib->soname, strerror(errno));
729                 }
730         }
731
732         return import(plib, lib, dwfl);
733 }
734
735 /*
736 - I handle static functions now. Should I? Those do not have DW_AT_external==1
737
738 - should process existing prototypes to make sure they match
739
740 - what do function pointers look like? I'm doing void*
741
742 - unions
743
744 - all my *allocs leak
745
746 - protolib_lookup_prototype should look for imports?
747
748 */