/* * heap_symShow.c - convert addresses to line number and function name */ #include #include #include #include "../private.h" #include "../context.h" #undef lub_heap_init /* defined for cygwin builds */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ extern void sysheap_suppress_leak_detection(void); extern void sysheap_restore_leak_detection(void); #if defined(__CYGWIN__) /*--------------------------------------------------------- * under CYGWIN we have the nasty behaviour that * argv[0] is the program name without the .exe extension * which is needed to load the symbol table; hence this * crufty hack *8-) *--------------------------------------------------------- */ void cygwin_lub_heap_init(const char *file_name) { char buffer[80]; sprintf(buffer, "%s.exe", file_name); lub_heap_init(buffer); } /*--------------------------------------------------------- */ #endif /* __CYGWIN__ */ #ifndef HAVE_LIBBFD /*--------------------------------------------------------- */ /* * Simple vanilla dump of symbols address * This is automatically used unless GPL support has been * configured. */ void lub_heap_symShow(unsigned address) { printf(" 0x%08x", address); } /*--------------------------------------------------------- */ bool_t lub_heap_symMatch(unsigned address, const char *substring) { return BOOL_TRUE; } /*--------------------------------------------------------- */ /* * dummy initialisation call */ void lub_heap_init(const char *file_name) { file_name = file_name; lub_heap__set_framecount(MAX_BACKTRACE); } /*--------------------------------------------------------- */ #else /* HAVE_LIBBFD */ /* This file is part of the CLISH project http://clish.sourceforge.net/ The code in this file is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 This code 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Derived from addr2line.c in the GNU binutils package by Ulrich.Lauther@mchp.siemens.de */ #include "bfd.h" /*--------------------------------------------------------- */ /* These global variables are used to pass information between translate_addresses and find_address_in_section. */ /*--------------------------------------------------------- */ typedef struct { bfd_vma pc; asymbol **syms; const char *file_name; const char *fn_name; unsigned int line_num; bfd_boolean found; } info_t; /*--------------------------------------------------------- */ typedef struct { bfd *abfd; asymbol **syms; /* Symbol table. */ } lub_heap_posix_t; /*--------------------------------------------------------- */ static lub_heap_posix_t my_instance; /* instance of this class */ /*--------------------------------------------------------- */ /* Look for an address in a section. This is called via bfd_map_over_sections. */ /*--------------------------------------------------------- */ static void find_address_in_section(bfd * abfd, asection * section, void *data) { info_t *info = data; bfd_vma vma; bfd_size_type size; if (info->found) return; if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) return; vma = bfd_get_section_vma(abfd, section); if (info->pc < vma) return; size = bfd_get_section_size(section); if (info->pc >= vma + size) return; info->found = bfd_find_nearest_line(abfd, section, info->syms, info->pc - vma, &info->file_name, &info->fn_name, &info->line_num); if (!info->line_num || !info->file_name) { info->found = 0; } if (info->found) { const char *last_file_name; const char *last_fn_name; unsigned last_line_num; bfd_find_nearest_line(abfd, section, info->syms, vma + size - 1, &last_file_name, &last_fn_name, &last_line_num); if ((last_file_name == info->file_name) && (last_fn_name == info->fn_name) && (last_line_num == info->line_num)) { info->found = FALSE; } } if (info->found) { /* strip to the basename */ char *p; p = strrchr(info->file_name, '/'); if (NULL != p) { info->file_name = p + 1; } } } /*--------------------------------------------------------- */ static void lub_heap_posix_fini(void) { lub_heap_posix_t *this = &my_instance; if (this->syms != NULL) { free(this->syms); this->syms = NULL; } if (this->abfd) { bfd_close(this->abfd); } } /*--------------------------------------------------------- */ void lub_heap_init(const char *file_name) { lub_heap_posix_t *this = &my_instance; /* remember to cleanup */ atexit(lub_heap_posix_fini); /* switch on leak detection */ lub_heap__set_framecount(MAX_BACKTRACE); sysheap_suppress_leak_detection(); bfd_init(); this->syms = NULL; this->abfd = bfd_openr(file_name, "pe-i386"); if (this->abfd) { if (bfd_check_format_matches(this->abfd, bfd_object, NULL)) { unsigned int size; long symcount = 0; if (!(bfd_get_file_flags(this->abfd) & HAS_SYMS) == 0) { /* Read in the symbol table. */ symcount = bfd_read_minisymbols(this->abfd, FALSE, (void *)&this-> syms, &size); if (0 == symcount) { /* try reading dynamic symbols */ symcount = bfd_read_minisymbols(this->abfd, TRUE /* dynamic */ , (void *)&this-> syms, &size); } } if (0 == symcount) { fprintf(stderr, "*** no symbols found in '%s'\n", bfd_get_filename(this->abfd)); } } } sysheap_restore_leak_detection(); } /*--------------------------------------------------------- */ static void _lub_heap_symGetInfo(lub_heap_posix_t * this, unsigned address, info_t * info) { info->pc = address; info->syms = this->syms; info->fn_name = NULL; info->file_name = NULL; info->line_num = 0; info->found = FALSE; sysheap_suppress_leak_detection(); if (NULL != this->abfd) { bfd_map_over_sections(this->abfd, find_address_in_section, info); } sysheap_restore_leak_detection(); } /*--------------------------------------------------------- */ void lub_heap_symShow(unsigned address) { lub_heap_posix_t *this = &my_instance; info_t info; _lub_heap_symGetInfo(this, address, &info); if (!info.found) { if (this->abfd && this->syms) { /* a line with the function names */ printf("%26s", ""); } printf(" 0x%08x", address); if (!this->abfd) { /* aline with the function names */ printf(" - lub_heap_init() not called."); } } else { printf(" %20s:%-4d", info.file_name, info.line_num); printf(" %s()", info.fn_name); } } /*--------------------------------------------------------- */ bool_t lub_heap_symMatch(unsigned address, const char *substring) { lub_heap_posix_t *this = &my_instance; info_t info; bool_t result = BOOL_FALSE; _lub_heap_symGetInfo(this, address, &info); if (info.found) { if (strstr(info.fn_name, substring)) { result = TRUE; } } return result; } /*--------------------------------------------------------- */ #endif /* HAVE_LIBBFD */