/* nautilus-leak-symbol-lookup.C - symbol lookup for a leak checking and profiling library Copyright (C) 2000 Eazel The Gnome Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The Gnome Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the Gnome Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Author: Pavel Cisler */ /* Copied from the leakchecker with a few minor tweaks. * FIXME: need to consolidate this with the leakchecker version and * only have one copy for both */ #define _GNU_SOURCE /* need this for dladdr */ #include "nautilus-leak-symbol-lookup.h" #include #include #include #include #include #include #include static GList *symbol_table_list; typedef struct { char *path; bfd *abfd; asymbol **symbol_table; asection *text_section; unsigned long start; unsigned long end; } NautilusLeakSymbolLookupMap; static gboolean nautilus_leak_find_symbol_in_map (const NautilusLeakSymbolLookupMap *map, unsigned long address, char **function_name, char **source_file_name, unsigned int *line) { const char *file; const char *function; address -= map->start; address -= map->text_section->vma; if (address > map->text_section->_cooked_size) { /* not a valid address range for this binary */ return FALSE; } if (!bfd_find_nearest_line (map->abfd, map->text_section, map->symbol_table, address, &file, &function, line)) { printf ("error looking up address in binary %s\n", map->path); return FALSE; } if (file == NULL || function == NULL) { return FALSE; } *function_name = g_strdup (function); *source_file_name = g_strdup (file); return TRUE; } static void nautilus_leak_symbol_map_get_offsets (NautilusLeakSymbolLookupMap *map) { gchar buffer[1024]; FILE *in; gchar perms[26]; gchar file[256]; unsigned long start, end; guint major, minor; ino_t inode; struct stat library_stat; struct stat entry_stat; int count; /* find the library we are looking for in the proc directories * to find out at which addresses it is mapped */ snprintf (buffer, 1023, "/proc/%d/maps", getpid()); in = fopen (buffer, "r"); if (stat (map->path, &library_stat) != 0) { /* we will use st_ino and st_dev to do a file match */ return; } while (fgets(buffer, 1023, in)) { gulong tmp; count = sscanf (buffer, "%lx-%lx %15s %*x %u:%u %lu %255s", &start, &end, perms, &major, &minor, &tmp, file); inode = tmp; if (count >= 6 && strcmp (perms, "r-xp") == 0) { if (stat (file, &entry_stat) != 0) { break; } /* check if this is the library we are loading */ if (library_stat.st_ino == entry_stat.st_ino && library_stat.st_dev == entry_stat.st_dev) { map->start = start; map->end = end; break; } } } fclose (in); } static NautilusLeakSymbolLookupMap * nautilus_leak_symbol_map_load (const char *binary_path, gboolean executable) { NautilusLeakSymbolLookupMap *map; char *target = NULL; size_t storage_needed; int number_of_symbols; map = g_new0 (NautilusLeakSymbolLookupMap, 1); map->abfd = bfd_openr (binary_path, target); if (map->abfd == NULL) { fprintf (stderr, "%s: ", binary_path); bfd_perror (binary_path); return NULL; } if (!bfd_check_format (map->abfd, bfd_object)) { fprintf (stderr, "%s is not an object file\n", binary_path); bfd_close (map->abfd); return NULL; } /* Use the ".text" section. */ map->text_section = bfd_get_section_by_name (map->abfd, ".text"); /* Read the symbol table. */ storage_needed = bfd_get_symtab_upper_bound (map->abfd); if (storage_needed == 0) { fprintf (stderr, "no symbols\n"); bfd_close (map->abfd); return NULL; } map->symbol_table = (asymbol **)g_malloc (storage_needed); if (map->symbol_table == NULL) { fprintf (stderr, "no memory allocating symbol table\n"); bfd_close (map->abfd); return NULL; } number_of_symbols = bfd_canonicalize_symtab (map->abfd, map->symbol_table); map->path = g_strdup (binary_path); if (!executable) { nautilus_leak_symbol_map_get_offsets (map); } symbol_table_list = g_list_append (symbol_table_list, map); return map; } static NautilusLeakSymbolLookupMap * nautilus_leak_symbol_map_load_if_needed (const char *binary_path, gboolean executable) { GList *p; NautilusLeakSymbolLookupMap *map; for (p = symbol_table_list; p != NULL; p = p->next) { map = (NautilusLeakSymbolLookupMap *)p->data; if (strcmp (map->path, binary_path) == 0) /* no need to load the symbols, already got the map */ return map; } return nautilus_leak_symbol_map_load (binary_path, executable); } void nautilus_leak_print_symbol_cleanup (void) { /* free the cached symbol tables */ GList *p; NautilusLeakSymbolLookupMap *map; for (p = symbol_table_list; p != NULL; p = p->next) { map = (NautilusLeakSymbolLookupMap *)p->data; bfd_close (map->abfd); g_free (map->symbol_table); g_free (map->path); g_free (map); } g_list_free (symbol_table_list); symbol_table_list = NULL; } static gboolean nautilus_leak_find_symbol_address (void *address, char **function_name, char **source_file_name, int *line) { GList *p; NautilusLeakSymbolLookupMap *map; Dl_info info; if (dladdr (address, &info) != 0) { /* We know the function name and the binary it lives in, now try to find * the function and the offset. */ map = nautilus_leak_symbol_map_load_if_needed (info.dli_fname, false); /* temporarily force a dladdr-only lookup. * should make the bfd symbol lookup work. */ if (map != NULL && nautilus_leak_find_symbol_in_map (map, (long)address, function_name, source_file_name, (unsigned int *)line)) { return TRUE; } /* just return the function name and the library binary path */ *function_name = g_strdup (info.dli_sname); *source_file_name = g_strdup (info.dli_fname); *line = -1; return TRUE; } else { /* Usually dladdr will succeed, it seems to only fail for * address lookups for functions in the main binary. */ for (p = symbol_table_list; p != NULL; p = p->next) { map = (NautilusLeakSymbolLookupMap *)p->data; if (nautilus_leak_find_symbol_in_map (map, (long)address, function_name, source_file_name, (unsigned int *)line)) return TRUE; } } return FALSE; } void get_function_at_address (const char *app_path, void *address, string &result) { char *function_name; char *source_file_name; int line; nautilus_leak_symbol_map_load_if_needed (app_path, true); if (nautilus_leak_find_symbol_address (address, &function_name, &source_file_name, &line)) { result = function_name; g_free (function_name); g_free (source_file_name); } else { result = "unknown function"; } } void nautilus_leak_print_symbol_address (const char *app_path, void *address) { char *function_name; char *source_file_name; int line; nautilus_leak_symbol_map_load_if_needed (app_path, true); if (nautilus_leak_find_symbol_address (address, &function_name, &source_file_name, &line)) { if (line >= 0) { printf("%10p %-30s %s:%d\n", address, function_name, source_file_name, line); } else { printf("%10p %-30s in library %s\n", address, function_name, source_file_name); } g_free (function_name); g_free (source_file_name); } else { printf("%p (unknown function)\n", address); } }