Alexey - Sorry to go to the top of the thread but this does not apply to any one comment. Try running the attached program on the SOs in /usr/lib (or any directory with a shared object). All it does is a load and unload. It appears there are many victims of C++ shared objects and not knowing all the rules of RTLD_GLOBAL, ABI and ODR. I have not been able to make it through the list of libraries yet due to a crash from one shared object or another. Some of the libraries cannot even get through their constructors - /usr/lib/codeblocks/plugins/libAutoVersioning.so comes to mid. I'm actually quite surprised that a GTK module - libwx_gtk2u_core-2.8.so.0 - made an appearance. It seems the module crashed in its destructor. Need I say more? And I have not even begun to play dirty: if I fork/exec so that two processes have a shared object open, I can guarantee a crash of the second processunder the right circumstances (RTLD_GLOBAL and global C++ objects) when the first process exits. What if the 'second process' were a SELinux binary? And one final note (keep in mind we *MUST* RTLD_GLOBAL to catch C++ exceptions across module boundaries (see the GCC FAQ)): ... usually there is no reason to use RTLD GLOBAL. For reasons explained later it is always highly advised ..." [1] Jeff [1] U. Dreeper, How To Write Shared Libraries, http://people.redhat.com/drepper/dsohowto.pdf, p.10. ============================ jeffrey@bruno:~/dlopen-dlclose$ uname -a Linux bruno 2.6.32-25-generic #45-Ubuntu SMP Sat Oct 16 19:52:42 UTC 2010 x86_64 GNU/Linux ============================ Program received signal SIGSEGV, Segmentation fault. 0x00007ffff1f3ee80 in wxWindowBase::PushEventHandler(wxEvtHandler*) () from /usr/lib/libwx_gtk2u_core-2.8.so.0 (gdb) where #0 0x00007ffff1f3ee80 in wxWindowBase::PushEventHandler(wxEvtHandler*) () from /usr/lib/libwx_gtk2u_core-2.8.so.0 #1 0x00007ffff33eecc7 in PluginManager (this=0x6c1a30) at pluginmanager.cpp:170 #2 0x00007ffff33e1e34 in Mgr<PluginManager>::Get (this=<value optimized out>) at ./manager.h:183 #3 Manager::GetPluginManager (this=<value optimized out>) at manager.cpp:326 #4 0x00007ffff68190dd in PluginRegistrant (__priority=<value optimized out>, __initialize_p=<value optimized out>) at ../../../../src/include/cbplugin.h:593 #5 __static_initialization_and_destruction_0 (__priority=<value optimized out>, __initialize_p=<value optimized out>) at AutoVersioning.cpp:58 #6 0x00007ffff6836c16 in __do_global_ctors_aux () from /usr/lib/codeblocks/plugins/libAutoVersioning.so #7 0x00007ffff6817f53 in _init () from /usr/lib/codeblocks/plugins/libAutoVersioning.so #8 0x00007fff00000000 in ?? () #9 0x00007ffff7dead65 in ?? () from /lib64/ld-linux-x86-64.so.2 #10 0x00007ffff7def841 in ?? () from /lib64/ld-linux-x86-64.so.2 #11 0x00007ffff7dea9c6 in ?? () from /lib64/ld-linux-x86-64.so.2 #12 0x00007ffff7deeffa in ?? () from /lib64/ld-linux-x86-64.so.2 #13 0x00007ffff7bd8f66 in ?? () from /lib/libdl.so.2 #14 0x00007ffff7dea9c6 in ?? () from /lib64/ld-linux-x86-64.so.2 #15 0x00007ffff7bd92ac in ?? () from /lib/libdl.so.2 #16 0x00007ffff7bd8ee1 in dlopen () from /lib/libdl.so.2 #17 0x000000000040229f in DoLoadUnload (files=..., errata=...) at load-unload.cpp:204 #18 0x0000000000401fba in main (argc=1, argv=0x7fffffffe368) at load-unload.cpp:163 (gdb) ============================ Program received signal SIGSEGV, Segmentation fault. 0x00007ffff0311e8c in wxBaseArrayInt::Add(int, unsigned long) () from /usr/lib/libwx_baseu-2.8.so.0 (gdb) where #0 0x00007ffff0311e8c in wxBaseArrayInt::Add(int, unsigned long) () from /usr/lib/libwx_baseu-2.8.so.0 #1 0x00007ffff282f593 in ~wxsRegisterItem (this=0x7ffff2b5a160, __in_chrg=<value optimized out>) at ./../wxsitemfactory.h:108 #2 0x00007ffff6ec3630 in __cxa_finalize () from /lib/libc.so.6 #3 0x00007ffff2737c06 in __do_global_dtors_aux () from /usr/lib/libwxsmithlib.so.0.0.1 #4 0x0000000000000009 in ?? () #5 0x00007fffffffdfb0 in ?? () #6 0x00007ffff28ae121 in _fini () from /usr/lib/libwxsmithlib.so.0.0.1 #7 0x00000000006898b0 in ?? () #8 0x00007ffff7df0972 in ?? () from /lib64/ld-linux-x86-64.so.2 #9 0x000000000064b7f0 in ?? () #10 0x00000000006720b0 in ?? () #11 0x00000000006648a0 in ?? () #12 0x00000000006787d0 in ?? () #13 0x0000000000658a80 in ?? () #14 0x00000000006643a0 in ?? () #15 0x0000000000641910 in ?? () #16 0x000000000066bb90 in ?? () #17 0x000000000066c3a0 in ?? () #18 0x0000000000663ef0 in ?? () #19 0x0000000000672560 in ?? () #20 0x000000000066dbf0 in ?? () #21 0x000000000066e0a0 in ?? () #22 0x000000000066b590 in ?? () #23 0x00000000006549e0 in ?? () #24 0x0000000000645c70 in ?? () #25 0x0000000000679140 in ?? () #26 0x0000000000646110 in ?? () #27 0x0000000000654e90 in ?? () #28 0x0000000000683f60 in ?? () #29 0x00000000006500b0 in ?? () #30 0x000000000064fc00 in ?? () #31 0x0000000000681a40 in ?? () #32 0x0000000000681ef0 in ?? () #33 0x000000000066cc90 in ?? () #34 0x000000000066d140 in ?? () #35 0x000000000065a820 in ?? () #36 0x0000000000678080 in ?? () #37 0x000000000065acc0 in ?? () #38 0x0000000000677be0 in ?? () #39 0x0000000000647930 in ?? () #40 0x000000000065cf40 in ?? () #41 0x000000000065d3e0 in ?? () #42 0x0000000000647dd0 in ?? () #43 0x00000000006795f0 in ?? () #44 0x0000000000650ae0 in ?? () #45 0x0000000000650f80 in ?? () #46 0x0000000000684400 in ?? () #47 0x0000000000683280 in ?? () #48 0x0000000000683720 in ?? () #49 0x0000000000644b90 in ?? () #50 0x0000000000645040 in ?? () #51 0x000000000067e3a0 in ?? () #52 0x000000000067e850 in ?? () #53 0x000000000065db90 in ?? () #54 0xe0eda5b1f212b194 in ?? () #55 0xfffffffffffffffe in ?? () #56 0x0101010101010101 in ?? () #57 0x0101010101010101 in ?? () #58 0x0101010101010101 in ?? () #59 0x0101010101010101 in ?? () #60 0x0000010101010101 in ?? () #61 0x0000000000000000 in ?? () (gdb) On Thu, Oct 21, 2010 at 7:53 AM, Alexey Skidanov <Alexey.Skidanov@xxxxxxxxxxxx> wrote: > Hello, > We use the gcc 4.1.1 in our project. After some code refactoring > (creating new shared library) the application crashes at exit with > following error: > > *** glibc detected *** : double free or corruption (fasttop) *** > > In order to simulate our application, I created the test one, with two > shared libraries and one static library. The code is below: > > [SNIP]
// load-unload.exe - load and unload shared objects to see which developers // and packagers did not pay attention to *ALL* the details of a C++ shared // object, RTLD_GLOBAL (so exceptions can be caught), the ABI ,and the ODR. // // This program 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, either version 3 of the License, or // (at your option) any later version. // // This program 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, see <http://www.gnu.org/licenses/>. // // This notice must not be removed from any files, and original // attribution must remain with the files. // // Copyright (C) 2010, Jeffrey Walton (noloader@xxxxxxxxx) // Need pthreads so gdb can follow the call to dlopen(2) // g++ -g -ggdb -O0 -Wall -Wextra load-unload.cpp -o load-unload.exe -ldl -lpthread #include <iostream> using std::cout; using std::cerr; using std::endl; #include <fstream> using std::ifstream; #include <string> using std::string; #include <vector> using std::vector; #include <stdexcept> using std::runtime_error; #include <cstdlib> using std::exit; #include <cctype> using std::isdigit; #include <cstring> using std::strerror; #include <algorithm> using std::sort; using std::unique; // Include after the standard c/c++ stuff #include <errno.h> #include <dirent.h> #include <dlfcn.h> #include <link.h> #include <elf.h> #include <sys/types.h> #include <sys/stat.h> typedef vector<string> FileList; typedef vector<string> DirList; typedef vector<string> ErrList; typedef size_t MaxDepth; MaxDepth Unlimited = static_cast<MaxDepth>(-1); MaxDepth RootOnly = static_cast<MaxDepth>(1); // See APPLICATION BINARY INTERFACE, Chapter 4. #if !defined(ELFMAG0) # define ELFMAG0 0x7f #endif #if !defined(ELFMAG1) # define ELFMAG1 'E' #endif #if !defined(ELFMAG2) # define ELFMAG2 'L' #endif #if !defined(ELFMAG3) # define ELFMAG3 'F' #endif class ProgramError : public runtime_error { public: ProgramError(const char* msg, int err = -1) : runtime_error(msg), m_err(err) { } ProgramError(const string& msg, int err = -1) : runtime_error(msg.c_str()), m_err(err) { } int error() const { return m_err; } private: int m_err; }; class AutoDir { public: AutoDir(DIR*& dir) : m_dir(dir) { } virtual ~AutoDir() { if(m_dir) { closedir(m_dir); m_dir = NULL; } } private: DIR*& m_dir; }; class AutoLib { public: AutoLib(void*& lib) : m_lib(lib) { } virtual ~AutoLib() { if(m_lib) { dlclose(m_lib); m_lib = NULL; } } private: void*& m_lib; }; void DoDirectoryWalk(const string& entry, FileList& files, ErrList errata, MaxDepth = Unlimited); void DoLoadUnload(const FileList& files, ErrList errata); bool IsSharedObject(const string& file); bool IsElfFormat(const string& file); int main(int argc, char* argv[]) { ErrList errata; // non fatal, such as access denied string libdir = "/usr/lib"; // assume /usr/lib if(argc >= 2) libdir = argv[1]; cout << "Testing shared objects in " << libdir << endl; cout << "Press CTLT-C to early exit" << endl; cout << endl; size_t processed = 0; try { FileList files; MaxDepth depth = RootOnly; if(depth == Unlimited) { cout << "Recursion depth = unlimited" << endl; } else if(depth == RootOnly) { cout << "Recursion depth = root directory only" << endl; } else { cout << "Recursion depth = " << depth << endl; } // Errata is non-fatal errors, such as EACCES. // Recursion depth is RootOnly, Unlimited, or any number in between. DoDirectoryWalk(libdir, files, errata, depth /*recursion*/); char* ldpath = getenv("LD_PATH"); if(ldpath) { // Since LD_PATH was set, recurse all directories // to test all SOs on the alternate path DoDirectoryWalk(ldpath, files, errata, Unlimited /*recursion depth*/); } // Sort for sane viewing processed = files.size(); sort(files.begin(), files.end()); // Remove any duplicates (should there really be any?) FileList::iterator last = unique(files.begin(), files.end()); files.erase(last, files.end()); if(processed) { cout << "Attempting to process " << processed << " files" << endl; DoLoadUnload(files, errata); } } catch(const ProgramError& err) { cerr << err.what() << endl; exit(err.error()); } if(errata.size()) { cout << endl; cout << "Errata:" << endl; for(size_t i = 0; i < errata.size(); i++) { cout << errata[i] << endl; } } if(processed) { cout << endl; cout << "Processed " << processed << " files" << endl; } return 0; } void DoLoadUnload(const FileList& files, ErrList errata) { for(size_t i = 0; i < files.size(); i++) { cout << "Loading/unloading " << files[i] << endl; void* lib = NULL; AutoLib cleanup(lib); lib = dlopen(files[i].c_str(), RTLD_GLOBAL); if(!lib) lib = dlopen(files[i].c_str(), RTLD_GLOBAL | RTLD_LAZY); if(!lib) { errata.push_back(dlerror()); } } } // Adapted from W. Richard Stevens, Advanced Programming in the Unix Environment void DoDirectoryWalk(const string& entry, FileList& files, ErrList errata, MaxDepth maxDepth) { if(!entry.length()) { throw ProgramError("Entry is not valid", EINVAL); } DirList dirs; struct stat sbuf; struct dirent* dbuf; int ret = 0; // http://linux.die.net/man/2/lstat ret = lstat(entry.c_str(), &sbuf); if(ret < 0) { string err; if(EACCES == errno) { err = string("lstat failed for ") + entry + " (EACCES)"; errata.push_back(err); return; } err = string("lstat failed for ") + entry + ": "; throw ProgramError(err + strerror(ret), errno); } if(S_ISREG(sbuf.st_mode)) { // if(IsSharedObject(entry)) // files.push_back(entry); if(IsElfFormat(entry)) files.push_back(entry); return; } if(!S_ISDIR(sbuf.st_mode)) return; string path = entry; if(path[path.size()-1] != '/') path += '/'; DIR* dp = NULL; AutoDir cleanup(dp); dp = opendir(path.c_str()); if(!dp) throw ProgramError(strerror(ret), errno); if(maxDepth--) { // Only add directories if we are still recursing while(NULL != (dbuf = readdir(dp))) { if(0 == strcmp(".", dbuf->d_name)) continue; if(0 == strcmp("..", dbuf->d_name)) continue; dirs.push_back(path + dbuf->d_name); } for(size_t i = 0; i < dirs.size(); i++) { DoDirectoryWalk(dirs[i], files, errata, maxDepth); } } } // Cheap, but keeps us from opening each file looking for an elf signature and other attributes. bool IsSharedObject(const string& file) { static const string ext = "so"; string::size_type pos = file.rfind(ext); if(string::npos == pos) return false; // This catches all files that end with '.so' if(file.substr(pos) == ext) return true; // This catches all files that end with '.so.W',, '.so.W.X', ..., '.so.W.X.Y.Z'. It will // probably miss some interesting SOs like OpenSSL since a letter is used in versioning. pos += ext.length(); while(pos < file.length()) { const char c = file[pos]; if(c != '.' && !isdigit(c)) return false; pos++; } return true; } bool IsElfFormat(const string& file) { try { // We are only interested in the first four bytes of Elf32_Ehdr. // ELFMAG0 can take on a few values. See APPLICATION BINARY INTERFACE, Chapter 4. static const char elf1[] = { 0x7F, ELFMAG1, ELFMAG2, ELFMAG3 }; static const char elf2[] = { 0xB1, ELFMAG1, ELFMAG2, ELFMAG3 }; std::ifstream stream; stream.open(file.c_str()); char ident[4] = {0,0,0,0}; stream.read(ident, sizeof(ident)); if(0 == memcmp(elf1, ident, 4) || 0 == memcmp(elf2, ident, 4)) return true; } catch(std::exception& e) { } return false; }