1 /* Sysprof -- Sampling, systemwide CPU profiler
2 * Copyright 2006, 2007, Soeren Sandmann
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.
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.
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.
22 #include "binparser.h"
23 #include "elfparser.h"
25 typedef struct Section Section
;
48 BinRecord
* strtab_format
;
49 BinRecord
* shn_entry
;
50 BinRecord
* sym_format
;
61 const Section
* text_section
;
64 static gboolean
parse_elf_signature (const guchar
*data
, gsize length
,
65 gboolean
*is_64
, gboolean
*is_be
);
66 static void make_formats (ElfParser
*parser
, gboolean is_64
);
69 get_string_indirect (BinParser
*parser
,
74 const char *result
= NULL
;
77 bin_parser_save (parser
);
79 index
= bin_parser_get_uint_field (parser
, record
, name
);
81 bin_parser_set_offset (parser
, str_table
+ index
);
83 result
= bin_parser_get_string (parser
);
85 bin_parser_restore (parser
);
91 section_new (BinParser
*parser
,
95 Section
*section
= g_new (Section
, 1);
98 section
->name
= get_string_indirect (parser
, record
, "sh_name", name_table
);
99 section
->size
= bin_parser_get_uint_field (parser
, record
, "sh_size");
100 section
->offset
= bin_parser_get_uint_field (parser
, record
, "sh_offset");
102 flags
= bin_parser_get_uint_field (parser
, record
, "sh_flags");
103 section
->allocated
= !!(flags
& SHF_ALLOC
);
105 if (section
->allocated
)
107 section
->load_address
= bin_parser_get_uint_field (
108 parser
, record
, "sh_addr");
112 section
->load_address
= 0;
115 section
->type
= bin_parser_get_uint_field (parser
, record
, "sh_type");
121 section_free (Section
*section
)
126 static const Section
*
127 find_section (ElfParser
*parser
,
133 for (i
= 0; i
< parser
->n_sections
; ++i
)
135 Section
*section
= parser
->sections
[i
];
137 if (strcmp (section
->name
, name
) == 0 && section
->type
== type
)
145 elf_parser_new_from_data (const guchar
*data
,
149 gboolean is_64
, is_big_endian
;
150 int section_names_idx
;
152 gsize section_headers
;
155 if (!parse_elf_signature (data
, length
, &is_64
, &is_big_endian
))
157 /* FIXME: set error */
161 parser
= g_new0 (ElfParser
, 1);
163 parser
->parser
= bin_parser_new (data
, length
);
166 bin_parser_set_endian (parser
->parser
, BIN_BIG_ENDIAN
);
168 bin_parser_set_endian (parser
->parser
, BIN_LITTLE_ENDIAN
);
170 make_formats (parser
, is_64
);
173 /* Read ELF header */
175 bin_parser_set_offset (parser
->parser
, 0);
177 parser
->n_sections
= bin_parser_get_uint_field (
178 parser
->parser
, parser
->header
, "e_shnum");
179 section_names_idx
= bin_parser_get_uint_field (
180 parser
->parser
, parser
->header
, "e_shstrndx");
181 section_headers
= bin_parser_get_uint_field (
182 parser
->parser
, parser
->header
, "e_shoff");
184 /* Read section headers */
185 parser
->sections
= g_new0 (Section
*, parser
->n_sections
);
187 bin_parser_set_offset (parser
->parser
, section_headers
);
189 bin_parser_save (parser
->parser
);
191 bin_parser_seek_record (parser
->parser
, parser
->shn_entry
,
194 section_names
= bin_parser_get_uint_field (
195 parser
->parser
, parser
->shn_entry
, "sh_offset");
197 bin_parser_restore (parser
->parser
);
199 for (i
= 0; i
< parser
->n_sections
; ++i
)
201 parser
->sections
[i
] = section_new (parser
->parser
,
205 bin_parser_seek_record (parser
->parser
, parser
->shn_entry
, 1);
208 /* Cache the text section */
209 parser
->text_section
= find_section (parser
, ".text", SHT_PROGBITS
);
210 if (!parser
->text_section
)
211 parser
->text_section
= find_section (parser
, ".text", SHT_NOBITS
);
217 elf_parser_new (const char *filename
,
224 GMappedFile
*file
= g_mapped_file_new (filename
, FALSE
, NULL
);
230 g_print ("elf parser new : %s\n", filename
);
233 data
= (guchar
*)g_mapped_file_get_contents (file
);
234 length
= g_mapped_file_get_length (file
);
237 g_print ("data %p: for %s\n", data
, filename
);
240 parser
= elf_parser_new_from_data (data
, length
);
244 g_mapped_file_free (file
);
251 g_print ("Elf file: %s (debug: %s)\n",
252 filename
, elf_parser_get_debug_link (parser
, NULL
));
258 if (!parser
->symbols
)
259 g_print ("at this point %s has no symbols\n", filename
);
266 elf_parser_get_crc32 (ElfParser
*parser
)
268 static const unsigned long crc32_table
[256] = {
269 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
270 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
271 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
272 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
273 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
274 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
275 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
276 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
277 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
278 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
279 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
280 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
281 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
282 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
283 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
284 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
285 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
286 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
287 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
288 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
289 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
290 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
291 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
292 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
293 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
294 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
295 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
296 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
297 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
298 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
299 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
300 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
301 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
302 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
303 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
304 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
305 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
306 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
307 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
308 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
309 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
310 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
311 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
318 data
= bin_parser_get_data (parser
->parser
);
319 length
= bin_parser_get_length (parser
->parser
);
323 madvise ((char *)data
, length
, MADV_SEQUENTIAL
);
325 for (i
= 0; i
< length
; ++i
)
326 crc
= crc32_table
[(crc
^ data
[i
]) & 0xff] ^ (crc
>> 8);
328 /* We just read the entire file into memory, but we only really
329 * need the symbol table, so swap the whole thing out.
331 * We could be more exact here, but it's only a few minor
334 madvise ((char *)data
, length
, MADV_DONTNEED
);
336 return ~crc
& 0xffffffff;
340 elf_parser_free (ElfParser
*parser
)
344 for (i
= 0; i
< parser
->n_sections
; ++i
)
345 section_free (parser
->sections
[i
]);
346 g_free (parser
->sections
);
349 g_mapped_file_free (parser
->file
);
351 g_free (parser
->symbols
);
353 bin_parser_free (parser
->parser
);
358 extern char *sysprof_cplus_demangle (const char *name
, int options
);
361 elf_demangle (const char *name
)
363 #define DMGL_PARAMS (1 << 0) /* Include function args */
364 #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
366 char *demangled
= sysprof_cplus_demangle (name
, DMGL_PARAMS
| DMGL_ANSI
);
371 return g_strdup (name
);
378 compare_sym (const void *a
, const void *b
)
380 const ElfSym
*sym_a
= a
;
381 const ElfSym
*sym_b
= b
;
383 if (sym_a
->address
< sym_b
->address
)
385 else if (sym_a
->address
== sym_b
->address
)
393 dump_symbols (ElfParser
*parser
, ElfSym
*syms
, guint n_syms
)
397 for (i
= 0; i
< n_syms
; ++i
)
399 ElfSym
*s
= &(syms
[i
]);
401 g_print (" %s: %lx\n", elf_parser_get_sym_name (parser
, s
), s
->address
);
407 read_table (ElfParser
*parser
,
408 const Section
*sym_table
,
409 const Section
*str_table
)
411 int sym_size
= bin_record_get_size (parser
->sym_format
);
415 parser
->n_symbols
= sym_table
->size
/ sym_size
;
416 parser
->symbols
= g_new (ElfSym
, parser
->n_symbols
);
419 g_print ("sym table offset: %d\n", sym_table
->offset
);
422 bin_parser_set_offset (parser
->parser
, sym_table
->offset
);
426 g_print ("n syms: %d\n", parser
->n_symbols
);
428 for (i
= 0; i
< parser
->n_symbols
; ++i
)
434 info
= bin_parser_get_uint_field (
435 parser
->parser
, parser
->sym_format
, "st_info");
436 addr
= bin_parser_get_uint_field (
437 parser
->parser
, parser
->sym_format
, "st_value");
438 offset
= bin_parser_get_offset (parser
->parser
);
441 (info
& 0xf) == STT_FUNC
&&
442 ((info
>> 4) == STB_GLOBAL
||
443 (info
>> 4) == STB_LOCAL
))
445 parser
->symbols
[n_functions
].address
= addr
;
446 parser
->symbols
[n_functions
].offset
= offset
;
452 g_print ("read symbol: %s\n", get_string_indirect (parser
->parser
,
453 parser
->sym_format
, "st_name",
458 bin_parser_seek_record (parser
->parser
, parser
->sym_format
, 1);
461 parser
->sym_strings
= str_table
->offset
;
462 parser
->n_symbols
= n_functions
;
463 parser
->symbols
= g_renew (ElfSym
, parser
->symbols
, parser
->n_symbols
);
465 qsort (parser
->symbols
, parser
->n_symbols
, sizeof (ElfSym
), compare_sym
);
469 read_symbols (ElfParser
*parser
)
471 const Section
*symtab
= find_section (parser
, ".symtab", SHT_SYMTAB
);
472 const Section
*strtab
= find_section (parser
, ".strtab", SHT_STRTAB
);
473 const Section
*dynsym
= find_section (parser
, ".dynsym", SHT_DYNSYM
);
474 const Section
*dynstr
= find_section (parser
, ".dynstr", SHT_STRTAB
);
476 if (symtab
&& strtab
)
478 read_table (parser
, symtab
, strtab
);
480 else if (dynsym
&& dynstr
)
482 read_table (parser
, dynsym
, dynstr
);
486 /* To make sure parser->symbols is non-NULL */
487 parser
->n_symbols
= 0;
488 parser
->symbols
= g_new (ElfSym
, 1);
493 do_lookup (ElfSym
*symbols
,
498 if (address
>= symbols
[last
].address
)
500 return &(symbols
[last
]);
502 else if (last
- first
< 3)
504 while (last
>= first
)
506 if (address
>= symbols
[last
].address
)
507 return &(symbols
[last
]);
516 int mid
= (first
+ last
) / 2;
518 if (symbols
[mid
].address
> address
)
519 return do_lookup (symbols
, address
, first
, mid
);
521 return do_lookup (symbols
, address
, mid
, last
);
525 /* Address should be given in 'offset into text segment' */
527 elf_parser_lookup_symbol (ElfParser
*parser
,
530 const ElfSym
*result
;
532 if (!parser
->symbols
)
535 g_print ("reading symbols\n");
537 read_symbols (parser
);
540 if (parser
->n_symbols
== 0)
543 if (!parser
->text_section
)
546 address
+= parser
->text_section
->load_address
;
549 g_print ("the address we are looking up is %p\n", address
);
552 result
= do_lookup (parser
->symbols
, address
, 0, parser
->n_symbols
- 1);
557 g_print ("found %s at %lx\n", elf_parser_get_sym_name (parser
, result
), result
->address
);
565 bin_parser_set_offset (parser
->parser
, result
->offset
);
567 size
= bin_parser_get_uint_field (
568 parser
->parser
, parser
->sym_format
, "st_size");
570 if (result
->address
+ size
<= address
)
578 elf_parser_get_text_offset (ElfParser
*parser
)
580 g_return_val_if_fail (parser
!= NULL
, (gulong
)-1);
582 if (!parser
->text_section
)
585 return parser
->text_section
->offset
;
589 elf_parser_get_debug_link (ElfParser
*parser
, guint32
*crc32
)
591 const Section
*debug_link
= find_section (parser
, ".gnu_debuglink",
598 bin_parser_set_offset (parser
->parser
, debug_link
->offset
);
600 result
= bin_parser_get_string (parser
->parser
);
602 bin_parser_align (parser
->parser
, 4);
605 *crc32
= bin_parser_get_uint (parser
->parser
, 4);
611 elf_parser_get_eh_frame (ElfParser
*parser
)
613 const Section
*eh_frame
= find_section (parser
, ".eh_frame", SHT_PROGBITS
);
616 return bin_parser_get_data (parser
->parser
) + eh_frame
->offset
;
622 elf_parser_get_sym_name (ElfParser
*parser
,
627 g_return_val_if_fail (parser
!= NULL
, NULL
);
629 bin_parser_set_offset (parser
->parser
, sym
->offset
);
630 result
= get_string_indirect (
631 parser
->parser
, parser
->sym_format
, "st_name", parser
->sym_strings
);
637 elf_parser_get_sym_address (ElfParser
*parser
,
647 parse_elf_signature (const guchar
*data
,
652 /* FIXME: this function should be able to return an error */
653 if (length
< EI_NIDENT
)
655 /* FIXME set error */
659 if (data
[EI_CLASS
] != ELFCLASS32
&&
660 data
[EI_CLASS
] != ELFCLASS64
)
662 /* FIXME set error */
666 if (data
[EI_DATA
] != ELFDATA2LSB
&&
667 data
[EI_DATA
] != ELFDATA2MSB
)
669 /* FIXME set error */
674 *is_64
= (data
[EI_CLASS
] == ELFCLASS64
);
677 *is_be
= (data
[EI_DATA
] == ELFDATA2MSB
);
683 get_formats (gboolean is_64
,
684 const BinField
**elf_header
,
685 const BinField
**shn_entry
,
686 const BinField
**sym_format
)
688 static const BinField elf64_header
[] = {
689 { "e_ident", BIN_UNINTERPRETED
, EI_NIDENT
},
690 { "e_type", BIN_UINT
, 2 },
691 { "e_machine", BIN_UINT
, 2 },
692 { "e_version", BIN_UINT
, 4 },
693 { "e_entry", BIN_UINT
, 8 },
694 { "e_phoff", BIN_UINT
, 8 },
695 { "e_shoff", BIN_UINT
, 8 },
696 { "e_flags", BIN_UINT
, 4 },
697 { "e_ehsize", BIN_UINT
, 2 },
698 { "e_phentsize", BIN_UINT
, 2 },
699 { "e_phnum", BIN_UINT
, 2 },
700 { "e_shentsize", BIN_UINT
, 2 },
701 { "e_shnum", BIN_UINT
, 2 },
702 { "e_shstrndx", BIN_UINT
, 2 },
706 static const BinField elf32_header
[] = {
707 { "e_ident", BIN_UNINTERPRETED
, EI_NIDENT
},
708 { "e_type", BIN_UINT
, 2 },
709 { "e_machine", BIN_UINT
, 2 },
710 { "e_version", BIN_UINT
, 4 },
711 { "e_entry", BIN_UINT
, 4 },
712 { "e_phoff", BIN_UINT
, 4 },
713 { "e_shoff", BIN_UINT
, 4 },
714 { "e_flags", BIN_UINT
, 4 },
715 { "e_ehsize", BIN_UINT
, 2 },
716 { "e_phentsize", BIN_UINT
, 2 },
717 { "e_phnum", BIN_UINT
, 2 },
718 { "e_shentsize", BIN_UINT
, 2 },
719 { "e_shnum", BIN_UINT
, 2 },
720 { "e_shstrndx", BIN_UINT
, 2 },
724 static const BinField shn64_entry
[] = {
725 { "sh_name", BIN_UINT
, 4 },
726 { "sh_type", BIN_UINT
, 4 },
727 { "sh_flags", BIN_UINT
, 8 },
728 { "sh_addr", BIN_UINT
, 8 },
729 { "sh_offset", BIN_UINT
, 8 },
730 { "sh_size", BIN_UINT
, 8 },
731 { "sh_link", BIN_UINT
, 4 },
732 { "sh_info", BIN_UINT
, 4 },
733 { "sh_addralign", BIN_UINT
, 8 },
734 { "sh_entsize", BIN_UINT
, 8 },
738 static const BinField shn32_entry
[] = {
739 { "sh_name", BIN_UINT
, 4 },
740 { "sh_type", BIN_UINT
, 4 },
741 { "sh_flags", BIN_UINT
, 4 },
742 { "sh_addr", BIN_UINT
, 4 },
743 { "sh_offset", BIN_UINT
, 4 },
744 { "sh_size", BIN_UINT
, 4 },
745 { "sh_link", BIN_UINT
, 4 },
746 { "sh_info", BIN_UINT
, 4 },
747 { "sh_addralign", BIN_UINT
, 4 },
748 { "sh_entsize", BIN_UINT
, 4 },
752 static const BinField sym64_format
[] = {
753 { "st_name", BIN_UINT
, 4 },
754 { "st_info", BIN_UINT
, 1 },
755 { "st_other", BIN_UINT
, 1 },
756 { "st_shndx", BIN_UINT
, 2 },
757 { "st_value", BIN_UINT
, 8 },
758 { "st_size", BIN_UINT
, 8 },
762 static const BinField sym32_format
[] = {
763 { "st_name", BIN_UINT
, 4 },
764 { "st_value", BIN_UINT
, 4 },
765 { "st_size", BIN_UINT
, 4 },
766 { "st_info", BIN_UINT
, 1 },
767 { "st_other", BIN_UINT
, 1 },
768 { "st_shndx", BIN_UINT
, 2 },
774 *elf_header
= elf64_header
;
775 *shn_entry
= shn64_entry
;
776 *sym_format
= sym64_format
;
780 *elf_header
= elf32_header
;
781 *shn_entry
= shn32_entry
;
782 *sym_format
= sym32_format
;
787 make_formats (ElfParser
*parser
, gboolean is_64
)
789 const BinField
*elf_header
, *shn_entry
, *sym_format
;
791 get_formats (is_64
, &elf_header
, &shn_entry
, &sym_format
);
793 parser
->header
= bin_parser_create_record (parser
->parser
, elf_header
);
794 parser
->shn_entry
= bin_parser_create_record (parser
->parser
, shn_entry
);
795 parser
->sym_format
= bin_parser_create_record (parser
->parser
, sym_format
);