From: Søren Sandmann Date: Mon, 2 Jun 2008 04:54:15 +0000 (+0000) Subject: Keep a list of elf files for each binary file. In some cases, notably X-Git-Tag: 1.1.2~72 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=7cd38113c823c7663e012ad539ab513ff3262115;p=platform%2Fupstream%2Fsysprof.git Keep a list of elf files for each binary file. In some cases, notably Mon Jun 2 00:51:46 2008 Søren Sandmann * binfile.c (get_debug_binaries): Keep a list of elf files for each binary file. In some cases, notably glibc on Fedora, the debug binary does not contain a symbol table, but the original file does. svn path=/trunk/; revision=428 --- diff --git a/ChangeLog b/ChangeLog index ed7bb4a..3034ff2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Mon Jun 2 00:51:46 2008 Søren Sandmann + + * binfile.c (get_debug_binaries): Keep a list of elf files for + each binary file. In some cases, notably glibc on Fedora, the + debug binary does not contain a symbol table, but the original + file does. + Sun Jun 1 23:03:06 2008 Søren Sandmann * profile.c (add_trace_to_tree): Do not fold recursions for diff --git a/binfile.c b/binfile.c index 8c95641..035b731 100644 --- a/binfile.c +++ b/binfile.c @@ -41,8 +41,8 @@ struct BinFile { int ref_count; - - ElfParser * elf; + + GList * elf_files; char * filename; @@ -91,6 +91,62 @@ already_warned (const char *name) static const char *const debug_file_directory = DEBUGDIR; static ElfParser * +get_build_id_file (ElfParser *elf) +{ + const char *build_id; + GList *tries = NULL, *list; + char *init, *rest; + ElfParser *result = NULL; + char *tmp; + + build_id = elf_parser_get_build_id (elf); + + if (!build_id) + return NULL; + + if (strlen (build_id) < 4) + return NULL; + + init = g_strndup (build_id, 2); + rest = g_strdup_printf ("%s%s", build_id + 2, ".debug"); + + tmp = g_build_filename ( + "/usr", "lib", "debug", ".build-id", init, rest, NULL); + tries = g_list_append (tries, tmp); + + tmp = g_build_filename ( + debug_file_directory, ".build-id", init, rest, NULL); + tries = g_list_append (tries, tmp); + + for (list = tries; list != NULL; list = list->next) + { + char *name = list->data; + ElfParser *parser = elf_parser_new (name, NULL); + + if (parser) + { + const char *file_id = elf_parser_get_build_id (parser); + + if (file_id && strcmp (build_id, file_id) == 0) + { + result = parser; + break; + } + + elf_parser_free (parser); + } + } + + g_list_foreach (tries, (GFunc)g_free, NULL); + g_list_free (tries); + + g_free (init); + g_free (rest); + + return result; +} + +static ElfParser * get_debuglink_file (ElfParser *elf, const char *filename, char **new_name) @@ -155,116 +211,51 @@ get_debuglink_file (ElfParser *elf, return result; } -static ElfParser * -get_build_id_file (ElfParser *elf, - const char *filename, - char **new_name) +static GList * +get_debug_binaries (GList *files, + ElfParser *elf, + const char *filename) { - const char *build_id = elf_parser_get_build_id (elf); - GList *tries = NULL, *list; - char *init, *rest; - ElfParser *result = NULL; - - if (!build_id) - return NULL; - - if (strlen (build_id) < 4) - return NULL; - - init = g_strndup (build_id, 2); - rest = g_strdup_printf ("%s%s", build_id + 2, ".debug"); - - tries = g_list_append (tries, g_build_filename ("/usr", "lib", "debug", ".build-id", init, rest, NULL)); - tries = g_list_append (tries, g_build_filename (debug_file_directory, ".build-id", init, rest, NULL)); - - for (list = tries; list != NULL; list = list->next) - { - const char *name = list->data; - ElfParser *parser = elf_parser_new (name, NULL); - - if (parser) - { - const char *file_id = elf_parser_get_build_id (parser); + ElfParser *build_id_file; + ElfParser *debuglink_file; + GHashTable *seen_names; + GList *free_us = NULL; - if (file_id && strcmp (build_id, file_id) == 0) - { - *new_name = g_strdup (name); - result = parser; - break; - } - - elf_parser_free (parser); - } - } - - g_list_foreach (tries, (GFunc)g_free, NULL); - g_list_free (tries); + build_id_file = get_build_id_file (elf); - g_free (init); - g_free (rest); - - return result; -} - -static ElfParser * -get_debug_file (ElfParser *elf, - const char *filename, - char **new_name) -{ - ElfParser *t; + if (build_id_file) + return g_list_prepend (files, build_id_file); - if ((t = get_build_id_file (elf, filename, new_name))) - return t; - else - return get_debuglink_file (elf, filename, new_name); -} - -static ElfParser * -find_separate_debug_file (ElfParser *elf, - const char *filename) -{ - ElfParser *debug; - char *debug_name = NULL; - char *fname; - GHashTable *seen_names = - g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - fname = g_strdup (filename); + /* .gnu_debuglink is actually a chain of debuglinks, and + * there have been cases where you'd have to follow it. + */ + seen_names = g_hash_table_new (g_str_hash, g_str_equal); do { - if (g_hash_table_lookup (seen_names, fname)) - { -#if 0 - g_print (" cycle detected\n"); -#endif - /* cycle detected, just return the original elf file itself */ + char *debug_name; + + if (g_hash_table_lookup (seen_names, filename)) break; - } - - debug = get_debug_file (elf, fname, &debug_name); + + g_hash_table_insert (seen_names, (char *)filename, (char *)filename); - if (debug) - { - elf_parser_free (elf); - elf = debug; - - g_hash_table_insert (seen_names, fname, fname); - fname = debug_name; - } -#if 0 - else + debuglink_file = get_debuglink_file (elf, filename, &debug_name); + + if (debuglink_file) { - g_print (" no debug info file for %s\n", fname); + free_us = g_list_prepend (free_us, debug_name); + files = g_list_prepend (files, debuglink_file); + + filename = debug_name; } -#endif } - while (debug); + while (debuglink_file); - g_free (fname); - g_hash_table_destroy (seen_names); - - return elf; + g_list_foreach (free_us, (GFunc)g_free, NULL); + g_list_free (free_us); + + return files; } static GHashTable *bin_files; @@ -286,67 +277,43 @@ bin_file_new (const char *filename) } else { + ElfParser *elf = NULL; + bf = g_new0 (BinFile, 1); bf->inode_check = FALSE; bf->filename = g_strdup (filename); bf->undefined_name = g_strdup_printf ("In file %s", filename); bf->ref_count = 1; + bf->elf_files = NULL; g_hash_table_insert (bin_files, bf->filename, bf); if (strcmp (filename, "[vdso]") == 0) { - gsize length; const guint8 *vdso_bytes; + gsize length; vdso_bytes = process_get_vdso_bytes (&length); + if (vdso_bytes) - { - bf->elf = elf_parser_new_from_data (vdso_bytes, length); -#if 0 - g_print ("got vdso elf: %p (%d)\n", bf->elf, length); -#endif - } - else - bf->elf = NULL; + elf = elf_parser_new_from_data (vdso_bytes, length); } else { - bf->elf = elf_parser_new (filename, NULL); -#if 0 - if (!bf->elf) - g_print ("Could not parse file %s\n", filename); -#endif + elf = elf_parser_new (filename, NULL); } - - /* We need the text offset of the actual binary, not the - * (potential) debug binary - */ - if (bf->elf) + + if (elf) { - ElfParser *oldelf; - - bf->text_offset = elf_parser_get_text_offset (bf->elf); -#if 0 - g_print ("text offset: %d\n", bf->text_offset); -#endif + /* We need the text offset of the actual binary, not the + * (potential) debug binaries + */ + bf->text_offset = elf_parser_get_text_offset (elf); - oldelf = bf->elf; -#if 0 - if (bf->elf) - g_print ("trying to find separate debug file for %s\n", filename); -#endif - bf->elf = find_separate_debug_file (bf->elf, filename); + bf->elf_files = get_debug_binaries (bf->elf_files, elf, filename); + bf->elf_files = g_list_append (bf->elf_files, elf); -#if 0 - if (!bf->elf) - g_print (" returned NULL\n"); - else if (bf->elf != oldelf) - g_print (" successfully opened a different elf file than the original\n"); - else - g_print (" opened the original elf file\n"); -#endif bf->inode = read_inode (filename); } } @@ -360,9 +327,9 @@ bin_file_free (BinFile *bin_file) if (--bin_file->ref_count == 0) { g_hash_table_remove (bin_files, bin_file->filename); - - if (bin_file->elf) - elf_parser_free (bin_file->elf); + + g_list_foreach (bin_file->elf_files, (GFunc)elf_parser_free, NULL); + g_list_free (bin_file->elf_files); g_free (bin_file->filename); g_free (bin_file->undefined_name); @@ -374,39 +341,40 @@ const BinSymbol * bin_file_lookup_symbol (BinFile *bin_file, gulong address) { + GList *list; + #if 0 g_print ("-=-=-=- \n"); + + g_print ("bin file lookup lookup %d\n", address); #endif - - if (bin_file->elf) - { -#if 0 - g_print ("bin file lookup lookup %d\n", address); -#endif - - address -= bin_file->text_offset; + address -= bin_file->text_offset; + #if 0 - g_print ("lookup %d in %s\n", address, bin_file->filename); + g_print ("lookup %d in %s\n", address, bin_file->filename); #endif - const ElfSym *sym = elf_parser_lookup_symbol (bin_file->elf, address); + for (list = bin_file->elf_files; list != NULL; list = list->next) + { + ElfParser *elf = list->data; + const ElfSym *sym = elf_parser_lookup_symbol (elf, address); if (sym) { #if 0 - g_print ("found %lx => %s\n", address, bin_symbol_get_name (bin_file, sym)); + g_print ("found %lx => %s\n", address, + bin_symbol_get_name (bin_file, sym)); #endif return (const BinSymbol *)sym; } } -#if 0 - else - g_print ("no elf file for %s\n", bin_file->filename); -#endif #if 0 - g_print ("%lx undefined in %s (textoffset %x)\n", address + bin_file->text_offset, bin_file->filename, bin_file->text_offset); + g_print ("%lx undefined in %s (textoffset %x)\n", + address + bin_file->text_offset, + bin_file->filename, + bin_file->text_offset); #endif return (const BinSymbol *)bin_file->undefined_name; @@ -419,7 +387,7 @@ bin_file_check_inode (BinFile *bin_file, if (bin_file->inode == inode) return TRUE; - if (!bin_file->elf) + if (!bin_file->elf_files) return FALSE; if (!bin_file->inode_check) @@ -434,14 +402,48 @@ bin_file_check_inode (BinFile *bin_file, return FALSE; } +static const ElfSym * +get_elf_sym (BinFile *file, + const BinSymbol *symbol, + ElfParser **elf_ret) +{ + GList *list; + + for (list = file->elf_files; list != NULL; list = list->next) + { + const ElfSym *sym = (const ElfSym *)symbol; + ElfParser *elf = list->data; + + if (elf_parser_owns_symbol (elf, sym)) + { + *elf_ret = elf; + return sym; + } + } + + g_critical ("Internal error: unrecognized symbol pointer"); + + *elf_ret = NULL; + return NULL; +} + const char * bin_symbol_get_name (BinFile *file, const BinSymbol *symbol) { if (file->undefined_name == (char *)symbol) + { return file->undefined_name; + } else - return elf_parser_get_sym_name (file->elf, (const ElfSym *)symbol); + { + ElfParser *elf; + const ElfSym *sym; + + sym = get_elf_sym (file, symbol, &elf); + + return elf_parser_get_sym_name (elf, sym); + } } gulong @@ -449,7 +451,16 @@ bin_symbol_get_address (BinFile *file, const BinSymbol *symbol) { if (file->undefined_name == (char *)symbol) + { return 0x0; + } else - return file->text_offset + elf_parser_get_sym_address (file->elf, (const ElfSym *)symbol); + { + ElfParser *elf; + const ElfSym *sym; + + sym = get_elf_sym (file, symbol, &elf); + + return elf_parser_get_sym_address (elf, sym); + } } diff --git a/elfparser.c b/elfparser.c index ccd9254..5f839cd 100644 --- a/elfparser.c +++ b/elfparser.c @@ -766,6 +766,21 @@ elf_parser_get_sym_name (ElfParser *parser, return result; } +gboolean +elf_parser_owns_symbol (ElfParser *parser, + const ElfSym *sym) +{ + ElfSym *first, *last; + + if (!parser->n_symbols) + return FALSE; + + first = parser->symbols; + last = parser->symbols + parser->n_symbols - 1; + + return first <= sym && sym <= last; +} + gulong elf_parser_get_sym_address (ElfParser *parser, const ElfSym *sym) diff --git a/elfparser.h b/elfparser.h index 842f85c..53d4cbf 100644 --- a/elfparser.h +++ b/elfparser.h @@ -51,4 +51,6 @@ const char *elf_parser_get_sym_name (ElfParser *parser, const ElfSym *sym); gulong elf_parser_get_sym_address (ElfParser *parser, const ElfSym *sym); +gboolean elf_parser_owns_symbol (ElfParser *parser, + const ElfSym *sym); char *elf_demangle (const char *name);