/** @file $Id$ $Date$ $Author$ @copy © Marc Wäckerlin @license LGPL, see file COPYING */ #include #include #include #include #include #ifndef NO_LTDL # include #endif #include #include #include #include #include #include #include #include #include #include #if defined(__solaris__) #include #endif #if defined (__GLIBC__) #include #endif #include #include #ifdef HAVE_DEMANGLE_H /* the defines work around wrong definitions in libiberty.h */ #define HAVE_DECL_BASENAME 1 #define HAVE_DECL_FFS 1 #define HAVE_DECL_ASPRINTF 1 #define HAVE_DECL_VASPRINTF 1 #define HAVE_DECL_SNPRINTF 1 #define HAVE_DECL_VSNPRINTF 1 #define HAVE_DECL_STRVERSCMP 1 # include #else # define DMGL_PARAMS (1 << 0) /* Include function args */ # define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ extern "C" { extern char * cplus_demangle(const char *, int); } #endif #if (__GNUC__==3 && __GNUC_MINOR__<4 || __GNUC__<3) \ && defined(_REENTRANT) && !defined(_MT) #define _MT #endif namespace mrw { //---------------------------------------------------------------------------- static std::string demangle(bfd* abfd, const char* name) { if (bfd_get_symbol_leading_char(abfd) == name[0]) ++name; /* This is a hack for better error reporting on XCOFF, PowerPC64-ELF or the MS PE format. These formats have a number of leading '.'s on at least some symbols, so we remove all dots to avoid confusing the demangler. */ const char* p(name); while (p && *p == '.') ++p; # ifdef __solaris__ char res[1024]; if (cplus_demangle(p, res, 1024)) return name; # else Auto::Free res(cplus_demangle(p, DMGL_ANSI | DMGL_PARAMS)); # endif if (res) { /* Now put back any stripped dots. */ if (p==name) return static_cast(res); std::string add_dots(p-name, '.'); return add_dots+=static_cast(res); } return name; } } //---------------------------------------------------------------------------- mrw::StackTrace::StackTrace() { // maximum trace level is limited here to 50, see below why # if defined(__GLIBC__) { const int TRACE_LEVEL(50); void* ba[TRACE_LEVEL]; for (int n(backtrace(ba, TRACE_LEVEL)), i(0); i Memcheck:Leak fun:_vgrZU_libcZdsoZa_malloc fun:_dl_map_object_deps fun:dl_open_worker fun:_dl_catch_error fun:_dl_open fun:do_dlopen fun:_dl_catch_error fun:dlerror_run fun:__libc_dlopen_mode fun:init fun:backtrace fun:_ZN3mrw10StackTraceC1Ev } */ # endif } //---------------------------------------------------------------------------- mrw::StackTrace::operator std::string() const { std::stringstream s; std::string::size_type fusz(0), adsz(0); //lisz(0), fisz(0) std::list l; for (AddressTrace::const_reverse_iterator it(_trace.rbegin()); it!=_trace.rend(); ++it) { CodePos c(translate(*it)); if (((std::stringstream&)(std::stringstream()< adsz) adsz = ((std::stringstream&)(std::stringstream()< fusz) fusz = c.function.size(); l.push_back(c); } for (std::list::iterator it(l.begin()); it!=l.end(); ++it) s<<'['<address<<"] " <function<function.size()+1))<<' ' <file<<':'<line<=sizeof(void*)); bfd_vma vma_addr(reinterpret_cast(addr)); std::map::iterator it(_addrs.lower_bound(vma_addr)); if (it==_addrs.begin() || (--it)->first > vma_addr || _dic.find(it->second)==_dic.end() || _dic[it->second][it->first].first <= vma_addr) return UNKNOWN; static const char* file(0); static const char* function(0); unsigned int line; if (!bfd_find_nearest_line (const_cast(static_cast(_bfd[it->second])), _dic[it->second][it->first].second, _syms[it->second], vma_addr - it->first, &file, &function, &line)) return UNKNOWN; return CodePos(addr, mrw::demangle(_bfd[it->second], function), file?file:"????", line); } //---------------------------------------------------------------------------- bool mrw::StackTrace::createSymtable(const std::string& fname, void* offs) { #ifdef _MT boost::recursive_mutex::scoped_lock lock(_mutex); #endif if (_dic.find(fname)!=_dic.end()) return true; // already loaded try { #ifdef NO_LTDL static bfd*(*bfd_openr)(const char*, const char*) = ::bfd_openr; static bfd_boolean(*bfd_check_format)(bfd*, bfd_format) = ::bfd_check_format; static bfd_boolean(*bfd_check_format_matches)(bfd*, bfd_format, char***) = ::bfd_check_format_matches; static void(*bfd_map_over_sections) (bfd*, void(*)(bfd*, asection*, void*), void*) = ::bfd_map_over_sections; #else static DynamicLibrary lib("libbfd"); static bfd*(*bfd_openr)(const char*, const char*) = (bfd*(*)(const char*, const char*))lib.symbol("bfd_openr"); static bfd_boolean(*bfd_check_format)(bfd*, bfd_format) = (bfd_boolean(*)(bfd*, bfd_format))lib.symbol("bfd_check_format"); static bfd_boolean(*bfd_check_format_matches)(bfd*, bfd_format, char***) = (bfd_boolean(*)(bfd*, bfd_format, char***)) lib.symbol("bfd_check_format_matches"); static void(*bfd_map_over_sections) (bfd*, void(*)(bfd*, asection*, void*), void*) = (void(*)(bfd*, void(*)(bfd*, asection*, void*), void*)) lib.symbol("bfd_map_over_sections"); #endif if (fname=="") return createSymtable(filename()); AutoBfd abfd((*bfd_openr)(fname.c_str(), 0)); long memsz(-1); Auto::Free m(0); if (!abfd || (*bfd_check_format)(abfd, bfd_archive) || !(*bfd_check_format_matches)(abfd, bfd_object, &(m.getClean())) || !(bfd_get_file_flags(const_cast(static_cast(abfd))) &HAS_SYMS) || (memsz=bfd_get_symtab_upper_bound (const_cast(static_cast(abfd))))<0) { _error = "cannot map bfd symbols - 'bfd_get_file_flags' failed " "for file: \""+fname+'"'; return false; } mrw::AutoPtrAry syms(new asymbol*[memsz]); if (bfd_canonicalize_symtab(const_cast(static_cast(abfd)), syms)<0) { _error = "cannot map bfd symbols - 'bfd_canonicalize_symtab' failed" "for file: \""+fname+'"'; return false; } _bfd[fname] = abfd; _syms[fname] = syms; _dic[fname]; std::pair fileoffs = std::make_pair(fname, offs); (*bfd_map_over_sections)(_bfd[fname], buildSectionMap, &fileoffs); return true; } catch (const std::exception& x) { _error = std::string("exception received: \"")+x.what()+"\", " "while processing file: \""+fname+'"'; return false; // shared library for bfd not found or similar } } //---------------------------------------------------------------------------- bool mrw::StackTrace::createSymtable(const mrw::StackTrace::BinFiles& files) { #ifdef _MT boost::recursive_mutex::scoped_lock lock(_mutex); #endif bool success(true); for (BinFiles::const_iterator it(files.begin()); it!=files.end(); ++it) { if (!createSymtable(it->first, it->second)) success=false; } return success; } //---------------------------------------------------------------------------- int mrw::StackTrace::bfdClose(bfd* abfd) noexcept { try { #ifdef NO_LTDL static int(*bfd_close)(bfd*) = ::bfd_close; #else static DynamicLibrary lib(""); static int(*bfd_close)(bfd*) = (int(*)(bfd*))lib.symbol("bfd_close"); #endif return (*bfd_close)(abfd); } catch (...) { return -1; // dynamic library loading problems } } //---------------------------------------------------------------------------- mrw::StackTrace::BinFiles mrw::StackTrace::filename() { mrw::StackTrace::BinFiles res; # if defined(__solaris__) { std::string s; s<<"/proc/"<>range>>perm>>x1>>x2>>size; if (mrw::to(size)>0) { s>>lib; range.resize(range.find_first_not_of("0123456789abcdefABCDEF")); void* addr(0); range>>addr; // added check: no names in brackets: [lib], because there are // [heap], [stack] and [vdso] that cannot be loaded // question: should only files with ending '.so' be loaded? if (lib.size() && lib[0]!='[' && lib[lib.size()-1]!=']' && addr!=0) res< fileoffset = *(std::pair*)fileoffs; bfd_vma vma(bfd_get_section_vma(abfd, section)+ reinterpret_cast(fileoffset.second)); #ifdef bfd_get_section_size bfd_size_type sz(bfd_get_section_size(section)); #elif defined bfd_get_section_size_before_reloc bfd_size_type sz(bfd_get_section_size_before_reloc(section)); #else bfd_size_type sz(section->size); #endif _dic[fileoffset.first][vma] = Translator::mapped_type(vma+sz, section); _addrs[vma] = fileoffset.first; } //---------------------------------------------------------------------------- std::map mrw::StackTrace::_dic; std::map mrw::StackTrace::_addrs; std::map mrw::StackTrace::_bfd; std::map > mrw::StackTrace::_syms; #ifdef _MT boost::recursive_mutex mrw::StackTrace::_mutex; #endif std::string mrw::StackTrace::_error;