TODO
[platform/upstream/sysprof.git] / elfparser.c
1 /* Sysprof -- Sampling, systemwide CPU profiler
2  * Copyright 2006, 2007, Soeren Sandmann
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 #include <stdlib.h>
19 #include <string.h>
20 #include <elf.h>
21 #include <sys/mman.h>
22 #include "binparser.h"
23 #include "elfparser.h"
24
25 typedef struct Section Section;
26
27 struct ElfSym
28 {
29     gulong offset;
30     gulong address;
31 };
32
33 struct Section
34 {
35     const gchar *       name;
36     gsize               offset;
37     gsize               size;
38     gboolean            allocated;
39     gulong              load_address;
40     guint               type;
41 };
42
43 struct ElfParser
44 {
45     BinParser *         parser;
46     
47     BinRecord *         header;
48     BinRecord *         strtab_format;
49     BinRecord *         shn_entry;
50     BinRecord *         sym_format;
51     BinRecord *         note_format;
52     
53     int                 n_sections;
54     Section **          sections;
55     
56     int                 n_symbols;
57     ElfSym *            symbols;
58     gsize               sym_strings;
59     
60     GMappedFile *       file;
61
62     char *              filename;
63
64     gboolean            checked_build_id;
65     char *              build_id;
66     
67     const Section *     text_section;
68 };
69
70 static gboolean parse_elf_signature (const guchar *data, gsize length,
71                                      gboolean *is_64, gboolean *is_be);
72 static void     make_formats        (ElfParser *parser, gboolean is_64);
73
74 static const char *
75 get_string_indirect (BinParser *parser,
76                      BinRecord *record,
77                      const char *name,
78                      gsize str_table)
79 {
80     const char *result = NULL;    
81     gsize index;
82     
83     bin_parser_save (parser);
84     
85     index = bin_parser_get_uint_field (parser, record, name);
86     
87     bin_parser_set_offset (parser, str_table + index);
88     
89     result = bin_parser_get_string (parser);
90     
91     bin_parser_restore (parser);
92     
93     return result;
94 }
95
96 static Section *
97 section_new (BinParser *parser,
98              BinRecord *record,
99              gsize      name_table)
100 {
101     Section *section = g_new (Section, 1);
102     guint64 flags;
103     
104     section->name = get_string_indirect (parser, record, "sh_name", name_table);
105     section->size = bin_parser_get_uint_field (parser, record, "sh_size");
106     section->offset = bin_parser_get_uint_field (parser, record, "sh_offset");
107     
108     flags = bin_parser_get_uint_field (parser, record, "sh_flags");
109     section->allocated = !!(flags & SHF_ALLOC);
110     
111     if (section->allocated)
112     {
113         section->load_address = bin_parser_get_uint_field (
114             parser, record, "sh_addr");
115     }
116     else
117     {
118         section->load_address = 0;
119     }
120     
121     section->type = bin_parser_get_uint_field (parser, record, "sh_type");
122     
123     return section;
124 }
125
126 static void
127 section_free (Section *section)
128 {
129     g_free (section);
130 }
131
132 static const Section *
133 find_section (ElfParser *parser,
134               const char *name,
135               guint       type)
136 {
137     int i;
138     
139     for (i = 0; i < parser->n_sections; ++i)
140     {
141         Section *section = parser->sections[i];
142         
143         if (strcmp (section->name, name) == 0 && section->type == type)
144             return section;
145     }
146     
147     return NULL;
148 }
149
150 ElfParser *
151 elf_parser_new_from_data (const guchar *data,
152                           gsize length)
153 {
154     ElfParser *parser;
155     gboolean is_64, is_big_endian;
156     int section_names_idx;
157     gsize section_names;
158     gsize section_headers;
159     int i;
160     
161     if (!parse_elf_signature (data, length, &is_64, &is_big_endian))
162     {
163         /* FIXME: set error */
164         return NULL;
165     }
166
167     parser = g_new0 (ElfParser, 1);
168     
169 #if 0
170     g_print ("  new parser : %p\n", parser);
171 #endif
172     
173     parser->parser = bin_parser_new (data, length);
174     
175     if (is_big_endian)
176         bin_parser_set_endian (parser->parser, BIN_BIG_ENDIAN);
177     else
178         bin_parser_set_endian (parser->parser, BIN_LITTLE_ENDIAN);
179     
180     make_formats (parser, is_64);
181     
182     
183     /* Read ELF header */
184     
185     bin_parser_set_offset (parser->parser, 0);
186     
187     parser->n_sections = bin_parser_get_uint_field (
188         parser->parser, parser->header, "e_shnum");
189     section_names_idx = bin_parser_get_uint_field (
190         parser->parser, parser->header, "e_shstrndx");
191     section_headers = bin_parser_get_uint_field (
192         parser->parser, parser->header, "e_shoff");
193     
194     /* Read section headers */
195     parser->sections = g_new0 (Section *, parser->n_sections);
196
197     bin_parser_set_offset (parser->parser, section_headers);
198     
199     bin_parser_save (parser->parser);
200     
201     bin_parser_seek_record (parser->parser, parser->shn_entry,
202                             section_names_idx);
203
204     section_names = bin_parser_get_uint_field (
205         parser->parser, parser->shn_entry, "sh_offset");
206
207     bin_parser_restore (parser->parser);
208     
209     for (i = 0; i < parser->n_sections; ++i)
210     {
211         parser->sections[i] = section_new (parser->parser,
212                                            parser->shn_entry,
213                                            section_names);
214
215         bin_parser_seek_record (parser->parser, parser->shn_entry, 1);
216     }
217     
218     /* Cache the text section */
219     parser->text_section = find_section (parser, ".text", SHT_PROGBITS);
220     if (!parser->text_section)
221         parser->text_section = find_section (parser, ".text", SHT_NOBITS);
222
223     parser->filename = NULL;
224     parser->build_id = NULL;
225     
226     return parser;
227 }
228
229 ElfParser *
230 elf_parser_new (const char *filename,
231                 GError **err)
232 {
233     const guchar *data;
234     gsize length;
235     ElfParser *parser;
236
237     GMappedFile *file = g_mapped_file_new (filename, FALSE, NULL);
238     
239     if (!file)
240         return NULL;
241     
242 #if 0
243     g_print ("elf parser new : %s\n", filename);
244 #endif
245     
246     data = (guchar *)g_mapped_file_get_contents (file);
247     length = g_mapped_file_get_length (file);
248     
249 #if 0
250     g_print ("data %p: for %s\n", data, filename);
251 #endif
252     
253     parser = elf_parser_new_from_data (data, length);
254     
255 #if 0
256     g_print ("Parser for %s: %p\n", filename, parser);
257 #endif
258     
259     if (!parser)
260     {
261         g_mapped_file_free (file);
262         return NULL;
263     }
264
265     parser->filename = g_strdup (filename);
266     
267     parser->file = file;
268     
269 #if 0
270     g_print ("Elf file: %s  (debug: %s)\n",
271              filename, elf_parser_get_debug_link (parser, NULL));
272     
273     if (!parser->symbols)
274         g_print ("at this point %s has no symbols\n", filename);
275 #endif
276     
277     return parser;
278 }
279
280 guint32
281 elf_parser_get_crc32 (ElfParser *parser)
282 {
283     static const unsigned long crc32_table[256] = {
284         0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
285         0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
286         0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
287         0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
288         0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
289         0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
290         0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
291         0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
292         0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
293         0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
294         0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
295         0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
296         0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
297         0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
298         0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
299         0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
300         0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
301         0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
302         0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
303         0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
304         0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
305         0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
306         0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
307         0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
308         0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
309         0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
310         0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
311         0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
312         0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
313         0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
314         0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
315         0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
316         0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
317         0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
318         0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
319         0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
320         0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
321         0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
322         0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
323         0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
324         0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
325         0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
326         0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
327     };
328     const guchar *data;
329     gsize length;
330     gulong crc;
331     gsize i;
332     
333     data = bin_parser_get_data (parser->parser);
334     length = bin_parser_get_length (parser->parser);
335     
336     crc = 0xffffffff;
337
338     madvise ((char *)data, length, MADV_SEQUENTIAL);
339     
340     for (i = 0; i < length; ++i)
341         crc = crc32_table[(crc ^ data[i]) & 0xff] ^ (crc >> 8);
342     
343     /* We just read the entire file into memory, but we only really
344      * need the symbol table, so swap the whole thing out.
345      *
346      * We could be more exact here, but it's only a few minor
347      * pagefaults.
348      */
349     if (parser->file)
350         madvise ((char *)data, length, MADV_DONTNEED);
351
352     return ~crc & 0xffffffff;
353 }
354
355 void
356 elf_parser_free (ElfParser *parser)
357 {
358     int i;
359     
360     for (i = 0; i < parser->n_sections; ++i)
361         section_free (parser->sections[i]);
362     g_free (parser->sections);
363     
364     if (parser->file)
365         g_mapped_file_free (parser->file);
366     
367     g_free (parser->symbols);
368     
369     bin_parser_free (parser->parser);
370
371     if (parser->filename)
372         g_free (parser->filename);
373
374     if (parser->build_id)
375         g_free (parser->build_id);
376     
377     g_free (parser);
378 }
379
380 extern char *sysprof_cplus_demangle (const char *name, int options);
381
382 char *
383 elf_demangle (const char *name)
384 {
385 #define DMGL_PARAMS     (1 << 0)        /* Include function args */
386 #define DMGL_ANSI       (1 << 1)        /* Include const, volatile, etc */
387     
388     char *demangled = sysprof_cplus_demangle (name, DMGL_PARAMS | DMGL_ANSI);
389     
390     if (demangled)
391         return demangled;
392     else
393         return g_strdup (name);
394 }
395
396 /*
397  * Looking up symbols
398  */
399 static int
400 compare_sym (const void *a, const void *b)
401 {
402     const ElfSym *sym_a = a;
403     const ElfSym *sym_b = b;
404     
405     if (sym_a->address < sym_b->address)
406         return -1;
407     else if (sym_a->address == sym_b->address)
408         return 0;
409     else
410         return 1;
411 }
412
413 #if 0
414 static void
415 dump_symbols (ElfParser *parser, ElfSym *syms, guint n_syms)
416 {
417     int i;
418     
419     for (i = 0; i < n_syms; ++i)
420     {
421         ElfSym *s = &(syms[i]);
422         
423         g_print ("   %s: %lx\n", elf_parser_get_sym_name (parser, s), s->address);
424     }
425 }
426 #endif
427
428 static void
429 read_table (ElfParser *parser,
430             const Section *sym_table,
431             const Section *str_table)
432 {
433     int sym_size = bin_record_get_size (parser->sym_format);
434     int i, n_symbols;
435     
436     parser->n_symbols = sym_table->size / sym_size;
437     parser->symbols = g_new (ElfSym, parser->n_symbols);
438     
439 #if 0
440     g_print ("sym table offset: %d\n", sym_table->offset);
441 #endif
442     
443     bin_parser_set_offset (parser->parser, sym_table->offset);
444     
445     n_symbols = 0;
446 #if 0
447     g_print ("n syms: %d\n", parser->n_symbols);
448 #endif
449     for (i = 0; i < parser->n_symbols; ++i)
450     {
451         guint info;
452         gulong addr;
453         gulong offset;
454         gulong shndx;
455         
456         info = bin_parser_get_uint_field (
457             parser->parser, parser->sym_format, "st_info");
458         addr = bin_parser_get_uint_field (
459             parser->parser, parser->sym_format, "st_value");
460         shndx = bin_parser_get_uint_field (
461             parser->parser, parser->sym_format, "st_shndx");
462         offset = bin_parser_get_offset (parser->parser);
463         
464 #if 0
465         g_print ("read symbol: %s (section: %d)\n", get_string_indirect (parser->parser,
466                                                                          parser->sym_format, "st_name",
467                                                                          str_table->offset),
468                  shndx);
469 #endif
470
471         if (addr != 0                                           &&
472             shndx < parser->n_sections                          &&
473             parser->sections[shndx] == parser->text_section     &&
474             (info & 0xf) == STT_FUNC            &&
475             ((info >> 4) == STB_GLOBAL ||
476              (info >> 4) == STB_LOCAL  ||
477              (info >> 4) == STB_WEAK)
478             )
479         {
480             parser->symbols[n_symbols].address = addr;
481             parser->symbols[n_symbols].offset = offset;
482
483             n_symbols++;
484             
485 #if 0
486             g_print ("    symbol: %s:   %lx\n",
487                      get_string_indirect (parser->parser,
488                                           parser->sym_format, "st_name",
489                                           str_table->offset),
490                      addr - parser->text_section->load_address);
491             g_print ("        sym %d in %p (info: %d:%d) (func:global  %d:%d)\n",
492                      addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL);
493 #endif
494         }
495         else if (addr != 0)
496         {
497 #if 0
498             g_print ("        rejecting %d in %p (info: %d:%d) (func:global  %d:%d)\n",
499                      addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL);
500 #endif
501         }
502         
503         bin_parser_seek_record (parser->parser, parser->sym_format, 1);
504     }
505     
506     parser->sym_strings = str_table->offset;
507     parser->n_symbols = n_symbols;
508     parser->symbols = g_renew (ElfSym, parser->symbols, parser->n_symbols);
509     
510     qsort (parser->symbols, parser->n_symbols, sizeof (ElfSym), compare_sym);
511 }
512
513 static void
514 read_symbols (ElfParser *parser)
515 {
516     const Section *symtab = find_section (parser, ".symtab", SHT_SYMTAB);
517     const Section *strtab = find_section (parser, ".strtab", SHT_STRTAB);
518     const Section *dynsym = find_section (parser, ".dynsym", SHT_DYNSYM);
519     const Section *dynstr = find_section (parser, ".dynstr", SHT_STRTAB);
520     
521     if (symtab && strtab)
522     {
523 #if 0
524         g_print ("reading symbol table of %s\n", parser->filename);
525 #endif
526         read_table (parser, symtab, strtab);
527     }
528     else if (dynsym && dynstr)
529     {
530 #if 0
531         g_print ("reading dynamic symbol table of %s\n", parser->filename);
532 #endif
533         read_table (parser, dynsym, dynstr);
534     }
535     else
536     {
537         /* To make sure parser->symbols is non-NULL */
538         parser->n_symbols = 0;
539         parser->symbols = g_new (ElfSym, 1);
540     }
541 }
542
543 static ElfSym *
544 do_lookup (ElfSym *symbols,
545            gulong  address,
546            int     first,
547            int     last)
548 {
549     if (address >= symbols[last].address)
550     {
551         return &(symbols[last]);
552     }
553     else if (last - first < 3)
554     {
555         while (last >= first)
556         {
557             if (address >= symbols[last].address)
558                 return &(symbols[last]);
559             
560             last--;
561         }
562         
563         return NULL;
564     }
565     else
566     {
567         int mid = (first + last) / 2;
568         
569         if (symbols[mid].address > address)
570             return do_lookup (symbols, address, first, mid);
571         else
572             return do_lookup (symbols, address, mid, last);
573     }
574 }
575
576 /* Address should be given in 'offset into text segment' */
577 const ElfSym *
578 elf_parser_lookup_symbol (ElfParser *parser,
579                           gulong     address)
580 {
581     const ElfSym *result;
582     
583     if (!parser->symbols)
584     {
585 #if 0
586         g_print ("reading symbols\n");
587 #endif
588         read_symbols (parser);
589     }
590     
591     if (parser->n_symbols == 0)
592         return NULL;
593     
594     if (!parser->text_section)
595         return NULL;
596     
597     address += parser->text_section->load_address;
598
599 #if 0
600     g_print ("elf: the address we are looking up is %p\n", address);
601 #endif
602     
603     result = do_lookup (parser->symbols, address, 0, parser->n_symbols - 1);
604     
605 #if 0
606     if (result)
607     {
608         g_print ("  elf: found %s at %lx\n", elf_parser_get_sym_name (parser, result), result->address);
609     }
610     else
611     {
612         g_print ("elf: not found\n");
613     }
614 #endif
615     
616     if (result)
617     {
618         gulong size;
619         
620         bin_parser_set_offset (parser->parser, result->offset);
621         
622         size = bin_parser_get_uint_field (
623             parser->parser, parser->sym_format, "st_size");
624
625         if (size > 0 && result->address + size <= address)
626         {
627 #if 0
628             g_print ("  elf: ends at %lx, so rejecting\n",
629                      result->address + size);
630 #endif
631             result = NULL;
632         }
633     }
634     
635     if (result)
636     {
637         /* Reject the symbols if the address is outside the text section */
638         if (address > parser->text_section->load_address + parser->text_section->size)
639             result = NULL;
640     }
641
642     return result;
643 }
644
645 gulong
646 elf_parser_get_text_offset (ElfParser *parser)
647 {
648     g_return_val_if_fail (parser != NULL, (gulong)-1);
649     
650     if (!parser->text_section)
651         return (gulong)-1;
652     
653     return parser->text_section->offset;
654 }
655
656 static gchar *
657 make_hex_string (const gchar *data, int n_bytes)
658 {
659     static const char hex_digits[] = {
660         '0', '1', '2', '3', '4', '5', '6', '7',
661         '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
662     };
663     GString *string = g_string_new (NULL);
664     int i;
665     
666     for (i = 0; i < n_bytes; ++i)
667     {
668         char c = data[i];
669             
670         g_string_append_c (string, hex_digits[(c & 0xf0) >> 4]);
671         g_string_append_c (string, hex_digits[(c & 0x0f)]);
672     }
673
674     return g_string_free (string, FALSE);
675 }
676         
677 const gchar *
678 elf_parser_get_build_id (ElfParser *parser)
679 {
680     if (!parser->checked_build_id)
681     {
682         const Section *build_id = find_section (parser, ".note.gnu.build-id", SHT_NOTE);
683         guint64 name_size;
684         guint64 desc_size;
685         guint64 type;
686         const char *name;
687         const gchar *desc;
688             
689         parser->checked_build_id = TRUE;
690
691         if (!build_id)
692             return NULL;
693         
694         bin_parser_set_offset (parser->parser, build_id->offset);
695         
696         name_size = bin_parser_get_uint_field (parser->parser, parser->note_format, "name_size");
697         desc_size = bin_parser_get_uint_field (parser->parser, parser->note_format, "desc_size");
698         type = bin_parser_get_uint_field (parser->parser, parser->note_format, "type");
699
700         bin_parser_seek_record (parser->parser, parser->note_format, 1);
701         name = bin_parser_get_string (parser->parser);
702
703         bin_parser_align (parser->parser, 4);
704         
705         desc = bin_parser_get_string (parser->parser);
706
707         parser->build_id = make_hex_string (desc, desc_size);
708     }
709     
710     return parser->build_id;
711 }
712
713 const char *
714 elf_parser_get_debug_link (ElfParser *parser, guint32 *crc32)
715 {
716     const Section *debug_link = find_section (parser, ".gnu_debuglink",
717                                               SHT_PROGBITS);
718     const gchar *result;
719     
720     if (!debug_link)
721         return NULL;
722     
723     bin_parser_set_offset (parser->parser, debug_link->offset);
724     
725     result = bin_parser_get_string (parser->parser);
726     
727     bin_parser_align (parser->parser, 4);
728     
729     if (crc32)
730         *crc32 = bin_parser_get_uint (parser->parser, 4);
731     
732     return result;
733 }
734
735 const guchar *
736 get_section (ElfParser *parser,
737              const char *name)
738 {
739     const Section *section = find_section (parser, name, SHT_PROGBITS);
740     
741     if (section)
742         return bin_parser_get_data (parser->parser) + section->offset;
743     else
744         return NULL;
745 }
746
747 const guchar *
748 elf_parser_get_eh_frame   (ElfParser   *parser)
749 {
750     return get_section (parser, ".eh_frame");
751 }
752
753 const guchar *
754 elf_parser_get_debug_frame   (ElfParser   *parser)
755 {
756     return get_section (parser, ".debug_frame");
757 }
758
759 const char *
760 elf_parser_get_sym_name (ElfParser *parser,
761                          const ElfSym *sym)
762 {
763     const char *result;
764     
765     g_return_val_if_fail (parser != NULL, NULL);
766     
767     bin_parser_set_offset (parser->parser, sym->offset);
768     result = get_string_indirect (
769         parser->parser, parser->sym_format, "st_name", parser->sym_strings);
770     
771     return result;
772 }
773
774 gboolean
775 elf_parser_owns_symbol (ElfParser *parser,
776                         const ElfSym *sym)
777 {
778     ElfSym *first, *last;
779     
780     if (!parser->n_symbols)
781         return FALSE;
782
783     first = parser->symbols;
784     last = parser->symbols + parser->n_symbols - 1;
785
786     return first <= sym && sym <= last;
787 }
788
789 gulong
790 elf_parser_get_sym_address (ElfParser *parser,
791                             const ElfSym *sym)
792 {
793     return sym->address - parser->text_section->load_address;
794 }
795
796 /*
797  * Utility functions
798  */
799 static gboolean
800 parse_elf_signature (const guchar *data,
801                      gsize         length,
802                      gboolean     *is_64,
803                      gboolean     *is_be)
804 {
805     /* FIXME: this function should be able to return an error */
806     if (length < EI_NIDENT)
807     {
808         /* FIXME set error */
809         return FALSE;
810     }
811     
812     if (data[EI_CLASS] != ELFCLASS32 &&
813         data[EI_CLASS] != ELFCLASS64)
814     {
815         /* FIXME set error */
816         return FALSE;
817     }
818     
819     if (data[EI_DATA] != ELFDATA2LSB &&
820         data[EI_DATA] != ELFDATA2MSB)
821     {
822         /* FIXME set error */
823         return FALSE;
824     }
825     
826     if (is_64)
827         *is_64 = (data[EI_CLASS] == ELFCLASS64);
828     
829     if (is_be)
830         *is_be = (data[EI_DATA] == ELFDATA2MSB);
831     
832     return TRUE;
833 }
834
835 static void
836 get_formats (gboolean is_64,
837              const BinField **elf_header,
838              const BinField **shn_entry,
839              const BinField **sym_format,
840              const BinField **note_format_out)
841 {
842     static const BinField elf64_header[] = {
843         { "e_ident",            BIN_UNINTERPRETED, EI_NIDENT },
844         { "e_type",             BIN_UINT,       2 },
845         { "e_machine",          BIN_UINT,       2 }, 
846         { "e_version",          BIN_UINT,       4 },
847         { "e_entry",            BIN_UINT,       8 },
848         { "e_phoff",            BIN_UINT,       8 },
849         { "e_shoff",            BIN_UINT,       8 },
850         { "e_flags",            BIN_UINT,       4 },
851         { "e_ehsize",           BIN_UINT,       2 },
852         { "e_phentsize",        BIN_UINT,       2 },
853         { "e_phnum",            BIN_UINT,       2 },
854         { "e_shentsize",        BIN_UINT,       2 },
855         { "e_shnum",            BIN_UINT,       2 },
856         { "e_shstrndx",         BIN_UINT,       2 },
857         { "" },
858     };
859     
860     static const BinField elf32_header[] = {
861         { "e_ident",            BIN_UNINTERPRETED, EI_NIDENT },
862         { "e_type",             BIN_UINT,       2 },
863         { "e_machine",          BIN_UINT,       2 }, 
864         { "e_version",          BIN_UINT,       4 },
865         { "e_entry",            BIN_UINT,       4 },
866         { "e_phoff",            BIN_UINT,       4 },
867         { "e_shoff",            BIN_UINT,       4 },
868         { "e_flags",            BIN_UINT,       4 },
869         { "e_ehsize",           BIN_UINT,       2 },
870         { "e_phentsize",        BIN_UINT,       2 },
871         { "e_phnum",            BIN_UINT,       2 },
872         { "e_shentsize",        BIN_UINT,       2 },
873         { "e_shnum",            BIN_UINT,       2 },
874         { "e_shstrndx",         BIN_UINT,       2 },
875         { "" },
876     };
877     
878     static const BinField shn64_entry[] = {
879         { "sh_name",            BIN_UINT,       4 },
880         { "sh_type",            BIN_UINT,       4 },
881         { "sh_flags",           BIN_UINT,       8 },
882         { "sh_addr",            BIN_UINT,       8 },
883         { "sh_offset",          BIN_UINT,       8 },
884         { "sh_size",            BIN_UINT,       8 },
885         { "sh_link",            BIN_UINT,       4 },
886         { "sh_info",            BIN_UINT,       4 },
887         { "sh_addralign",       BIN_UINT,       8 },
888         { "sh_entsize",         BIN_UINT,       8 },
889         { "" }
890     };
891     
892     static const BinField shn32_entry[] = {
893         { "sh_name",            BIN_UINT,       4 },
894         { "sh_type",            BIN_UINT,       4 },
895         { "sh_flags",           BIN_UINT,       4 },
896         { "sh_addr",            BIN_UINT,       4 },
897         { "sh_offset",          BIN_UINT,       4 },
898         { "sh_size",            BIN_UINT,       4 },
899         { "sh_link",            BIN_UINT,       4 },
900         { "sh_info",            BIN_UINT,       4 },
901         { "sh_addralign",       BIN_UINT,       4 },
902         { "sh_entsize",         BIN_UINT,       4 },
903         { "" }
904     };
905     
906     static const BinField sym64_format[] = {
907         { "st_name",            BIN_UINT,       4 },
908         { "st_info",            BIN_UINT,       1 },
909         { "st_other",           BIN_UINT,       1 },
910         { "st_shndx",           BIN_UINT,       2 },
911         { "st_value",           BIN_UINT,       8 },
912         { "st_size",            BIN_UINT,       8 },
913         { "" }
914     };
915
916     static const BinField sym32_format[] = {
917         { "st_name",            BIN_UINT,       4 },
918         { "st_value",           BIN_UINT,       4 },
919         { "st_size",            BIN_UINT,       4 },
920         { "st_info",            BIN_UINT,       1 },
921         { "st_other",           BIN_UINT,       1 },
922         { "st_shndx",           BIN_UINT,       2 },
923         { "" },
924     };
925
926     static const BinField note_format[] = {
927         { "name_size",          BIN_UINT,       4 },
928         { "desc_size",          BIN_UINT,       4 },
929         { "type",               BIN_UINT,       4 },
930     };
931
932     if (is_64)
933     {
934         *elf_header = elf64_header;
935         *shn_entry  = shn64_entry;
936         *sym_format = sym64_format;
937     }
938     else
939     {
940         *elf_header = elf32_header;
941         *shn_entry  = shn32_entry;
942         *sym_format = sym32_format;
943     }
944
945     *note_format_out = note_format;
946 }
947
948 static void
949 make_formats (ElfParser *parser, gboolean is_64)
950 {
951     const BinField *elf_header, *shn_entry, *sym_format, *note_format;
952
953     get_formats (is_64, &elf_header, &shn_entry, &sym_format, &note_format);
954
955     parser->header = bin_parser_create_record (parser->parser, elf_header);
956     parser->shn_entry = bin_parser_create_record (parser->parser, shn_entry);
957     parser->sym_format = bin_parser_create_record (parser->parser, sym_format);
958     parser->note_format = bin_parser_create_record (parser->parser, note_format);
959 }
960