Keep a list of elf files for each binary file. In some cases, notably

Mon Jun  2 00:51:46 2008  Søren Sandmann  <sandmann@redhat.com>

	* 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
This commit is contained in:
Søren Sandmann
2008-06-02 04:54:15 +00:00
committed by Søren Sandmann Pedersen
parent 736806aaa6
commit 7cd38113c8
4 changed files with 197 additions and 162 deletions

View File

@ -1,3 +1,10 @@
Mon Jun 2 00:51:46 2008 Søren Sandmann <sandmann@redhat.com>
* 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 <sandmann@redhat.com> Sun Jun 1 23:03:06 2008 Søren Sandmann <sandmann@redhat.com>
* profile.c (add_trace_to_tree): Do not fold recursions for * profile.c (add_trace_to_tree): Do not fold recursions for

335
binfile.c
View File

@ -41,8 +41,8 @@
struct BinFile struct BinFile
{ {
int ref_count; int ref_count;
ElfParser * elf; GList * elf_files;
char * filename; char * filename;
@ -90,6 +90,62 @@ already_warned (const char *name)
static const char *const debug_file_directory = DEBUGDIR; 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 * static ElfParser *
get_debuglink_file (ElfParser *elf, get_debuglink_file (ElfParser *elf,
const char *filename, const char *filename,
@ -155,116 +211,51 @@ get_debuglink_file (ElfParser *elf,
return result; return result;
} }
static ElfParser * static GList *
get_build_id_file (ElfParser *elf, get_debug_binaries (GList *files,
const char *filename, ElfParser *elf,
char **new_name) const char *filename)
{ {
const char *build_id = elf_parser_get_build_id (elf); ElfParser *build_id_file;
GList *tries = NULL, *list; ElfParser *debuglink_file;
char *init, *rest; GHashTable *seen_names;
ElfParser *result = NULL; GList *free_us = NULL;
if (!build_id) build_id_file = get_build_id_file (elf);
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);
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);
g_free (init); if (build_id_file)
g_free (rest); return g_list_prepend (files, build_id_file);
return result;
}
static ElfParser * /* .gnu_debuglink is actually a chain of debuglinks, and
get_debug_file (ElfParser *elf, * there have been cases where you'd have to follow it.
const char *filename, */
char **new_name) seen_names = g_hash_table_new (g_str_hash, g_str_equal);
{
ElfParser *t;
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);
do do
{ {
if (g_hash_table_lookup (seen_names, fname)) char *debug_name;
{
#if 0 if (g_hash_table_lookup (seen_names, filename))
g_print (" cycle detected\n");
#endif
/* cycle detected, just return the original elf file itself */
break; break;
}
g_hash_table_insert (seen_names, (char *)filename, (char *)filename);
debug = get_debug_file (elf, fname, &debug_name);
if (debug) debuglink_file = get_debuglink_file (elf, filename, &debug_name);
if (debuglink_file)
{ {
elf_parser_free (elf); free_us = g_list_prepend (free_us, debug_name);
elf = debug; files = g_list_prepend (files, debuglink_file);
g_hash_table_insert (seen_names, fname, fname); filename = debug_name;
fname = debug_name;
} }
#if 0
else
{
g_print (" no debug info file for %s\n", fname);
}
#endif
} }
while (debug); while (debuglink_file);
g_free (fname); g_list_foreach (free_us, (GFunc)g_free, NULL);
g_hash_table_destroy (seen_names); g_list_free (free_us);
return elf; return files;
} }
static GHashTable *bin_files; static GHashTable *bin_files;
@ -286,67 +277,43 @@ bin_file_new (const char *filename)
} }
else else
{ {
ElfParser *elf = NULL;
bf = g_new0 (BinFile, 1); bf = g_new0 (BinFile, 1);
bf->inode_check = FALSE; bf->inode_check = FALSE;
bf->filename = g_strdup (filename); bf->filename = g_strdup (filename);
bf->undefined_name = g_strdup_printf ("In file %s", filename); bf->undefined_name = g_strdup_printf ("In file %s", filename);
bf->ref_count = 1; bf->ref_count = 1;
bf->elf_files = NULL;
g_hash_table_insert (bin_files, bf->filename, bf); g_hash_table_insert (bin_files, bf->filename, bf);
if (strcmp (filename, "[vdso]") == 0) if (strcmp (filename, "[vdso]") == 0)
{ {
gsize length;
const guint8 *vdso_bytes; const guint8 *vdso_bytes;
gsize length;
vdso_bytes = process_get_vdso_bytes (&length); vdso_bytes = process_get_vdso_bytes (&length);
if (vdso_bytes) if (vdso_bytes)
{ elf = elf_parser_new_from_data (vdso_bytes, length);
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;
} }
else else
{ {
bf->elf = elf_parser_new (filename, NULL); elf = elf_parser_new (filename, NULL);
#if 0
if (!bf->elf)
g_print ("Could not parse file %s\n", filename);
#endif
} }
/* We need the text offset of the actual binary, not the if (elf)
* (potential) debug binary
*/
if (bf->elf)
{ {
ElfParser *oldelf; /* We need the text offset of the actual binary, not the
* (potential) debug binaries
bf->text_offset = elf_parser_get_text_offset (bf->elf); */
#if 0 bf->text_offset = elf_parser_get_text_offset (elf);
g_print ("text offset: %d\n", bf->text_offset);
#endif
oldelf = bf->elf; bf->elf_files = get_debug_binaries (bf->elf_files, elf, filename);
#if 0 bf->elf_files = g_list_append (bf->elf_files, elf);
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);
#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); bf->inode = read_inode (filename);
} }
} }
@ -360,9 +327,9 @@ bin_file_free (BinFile *bin_file)
if (--bin_file->ref_count == 0) if (--bin_file->ref_count == 0)
{ {
g_hash_table_remove (bin_files, bin_file->filename); g_hash_table_remove (bin_files, bin_file->filename);
if (bin_file->elf) g_list_foreach (bin_file->elf_files, (GFunc)elf_parser_free, NULL);
elf_parser_free (bin_file->elf); g_list_free (bin_file->elf_files);
g_free (bin_file->filename); g_free (bin_file->filename);
g_free (bin_file->undefined_name); g_free (bin_file->undefined_name);
@ -374,39 +341,40 @@ const BinSymbol *
bin_file_lookup_symbol (BinFile *bin_file, bin_file_lookup_symbol (BinFile *bin_file,
gulong address) gulong address)
{ {
GList *list;
#if 0 #if 0
g_print ("-=-=-=- \n"); g_print ("-=-=-=- \n");
g_print ("bin file lookup lookup %d\n", address);
#endif #endif
address -= bin_file->text_offset;
if (bin_file->elf) #if 0
g_print ("lookup %d in %s\n", address, bin_file->filename);
#endif
for (list = bin_file->elf_files; list != NULL; list = list->next)
{ {
#if 0 ElfParser *elf = list->data;
g_print ("bin file lookup lookup %d\n", address); const ElfSym *sym = elf_parser_lookup_symbol (elf, address);
#endif
address -= bin_file->text_offset;
#if 0
g_print ("lookup %d in %s\n", address, bin_file->filename);
#endif
const ElfSym *sym = elf_parser_lookup_symbol (bin_file->elf, address);
if (sym) if (sym)
{ {
#if 0 #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 #endif
return (const BinSymbol *)sym; return (const BinSymbol *)sym;
} }
} }
#if 0
else
g_print ("no elf file for %s\n", bin_file->filename);
#endif
#if 0 #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 #endif
return (const BinSymbol *)bin_file->undefined_name; return (const BinSymbol *)bin_file->undefined_name;
@ -419,7 +387,7 @@ bin_file_check_inode (BinFile *bin_file,
if (bin_file->inode == inode) if (bin_file->inode == inode)
return TRUE; return TRUE;
if (!bin_file->elf) if (!bin_file->elf_files)
return FALSE; return FALSE;
if (!bin_file->inode_check) if (!bin_file->inode_check)
@ -434,14 +402,48 @@ bin_file_check_inode (BinFile *bin_file,
return FALSE; 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 * const char *
bin_symbol_get_name (BinFile *file, bin_symbol_get_name (BinFile *file,
const BinSymbol *symbol) const BinSymbol *symbol)
{ {
if (file->undefined_name == (char *)symbol) if (file->undefined_name == (char *)symbol)
{
return file->undefined_name; return file->undefined_name;
}
else 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 gulong
@ -449,7 +451,16 @@ bin_symbol_get_address (BinFile *file,
const BinSymbol *symbol) const BinSymbol *symbol)
{ {
if (file->undefined_name == (char *)symbol) if (file->undefined_name == (char *)symbol)
{
return 0x0; return 0x0;
}
else 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);
}
} }

View File

@ -766,6 +766,21 @@ elf_parser_get_sym_name (ElfParser *parser,
return result; 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 gulong
elf_parser_get_sym_address (ElfParser *parser, elf_parser_get_sym_address (ElfParser *parser,
const ElfSym *sym) const ElfSym *sym)

View File

@ -51,4 +51,6 @@ const char *elf_parser_get_sym_name (ElfParser *parser,
const ElfSym *sym); const ElfSym *sym);
gulong elf_parser_get_sym_address (ElfParser *parser, gulong elf_parser_get_sym_address (ElfParser *parser,
const ElfSym *sym); const ElfSym *sym);
gboolean elf_parser_owns_symbol (ElfParser *parser,
const ElfSym *sym);
char *elf_demangle (const char *name); char *elf_demangle (const char *name);