/** @file $Id$ $Date$ $Author$ @copy © Marc Wäckerlin @license LGPL, see file COPYING $Log$ Revision 1.12 2005/02/28 07:14:03 marc change in getting section size for SUN Solaris (old bfd.h) Revision 1.11 2005/02/18 15:48:56 marc Dynamic loading of libbfd, no more dependency on specific libbfd version! Revision 1.10 2005/01/28 07:51:24 marc improved and corrected trace formatting Revision 1.9 2005/01/07 00:34:38 marc some changes for solaris Revision 1.8 2004/12/20 13:22:25 marc mrw string now throws exceptions, catch needed Revision 1.7 2004/10/13 10:47:15 marc no more need for ldd in StackTrace, read from /proc/self/maps Revision 1.6 2004/10/11 15:58:51 marc First version with working support for shared libraries! Revision 1.5 2004/10/07 16:59:12 marc new method createSymtable that takes a list of arguments -> untested! Revision 1.4 2004/10/07 09:32:45 marc correction in parameter (const&) Revision 1.3 2004/08/28 16:21:25 marc mrw-c++-0.92 (mrw) - new file: version.cpp - new file header for all sources - work around warning in mrw::auto - possibility to compile without log4cxx - work around bugs in demangle.h and libiberty.h - corrections in documentation - added simple tracing mechanism - more warnings - small corrections in Auto<>::Free and a new test for it - possibility to compile without stack trace */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__solaris__) #include #endif #if defined (__GLIBC__) #include #endif #include extern "C" { /// @bug redefined in libiberty.h # define HAVE_DECL_BASENAME 1 /// @bug redefined in libiberty.h # define HAVE_DECL_ASPRINTF 1 /// @bug redefined in libiberty.h # define HAVE_DECL_VASPRINTF 1 /** @bug - in file file: /usr/include/demangle.h - of package: binutils-2.15.90.0.1.1-31 An idiot unfortunately abused the C++ keyword @c typename as variable name to @c cplus_demangle_fill_builtin_type, so I have to work around it. */ # define typename anotherNameThatsNotAKeyword # include # undef typename // // copied from demangle.h because of compiler warnings in libliberty.h // // (... throws different exception) // #define DMGL_PARAMS (1 << 0) /* Include function args */ // #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ // /// @bug demangle.h includes libiberty.h which throws different // /// exceptions for the same functions than other standard libraries, // /// so I can't include demangle.h! // extern char * cplus_demangle(const char *, int); } #include 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() throw(std::bad_exception) { // 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 l; for (AddressTrace::const_reverse_iterator it(_trace.rbegin()); it!=_trace.rend(); ++it, first=false) { 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 CodePos(addr, "????", "????", 0); 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 CodePos(addr, "????", "????", 0); return CodePos(addr, mrw::demangle(_bfd[it->second], function), file?file:"????", line); } //---------------------------------------------------------------------------- bool mrw::StackTrace::createSymtable(const std::string& fname, void* offs) throw(std::bad_exception) { if (_dic.find(fname)!=_dic.end()) return true; // already loaded try { 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"); 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) return false; mrw::AutoPtr syms(new asymbol*[memsz]); if (bfd_canonicalize_symtab(const_cast(static_cast(abfd)), syms)<0) 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 (...) { return false; // shared library for bfd not found or similar } } //---------------------------------------------------------------------------- bool mrw::StackTrace::createSymtable(const mrw::StackTrace::BinFiles& files) throw(std::bad_exception) { 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) throw() { try { static DynamicLibrary lib(""); static int(*bfd_close)(bfd*) = (int(*)(bfd*))lib.symbol("bfd_close"); return (*bfd_close)(abfd); } catch (...) { return -1; // dynamic library loading problems } } //---------------------------------------------------------------------------- mrw::StackTrace::BinFiles mrw::StackTrace::filename() throw(std::bad_exception) { mrw::StackTrace::BinFiles res; # if defined(__solaris__) { std::string s; s<<"/proc/"<>range>>perm>>x1>>x2>>size>>lib; range.resize(range.find_first_not_of("0123456789abcdefABCDEF")); void* addr(0); range>>addr; if (lib.size() && 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)); #elsifdef 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;