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