This is the mail archive of the
gcc-help@gcc.gnu.org
mailing list for the GCC project.
Re: Global variable in static library - double free or corruption error
- From: Jeffrey Walton <noloader at gmail dot com>
- To: Alexey Skidanov <Alexey dot Skidanov at orbotech dot com>
- Cc: gcc-help at gcc dot gnu dot org, skidanovalexey at yahoo dot com
- Date: Fri, 22 Oct 2010 00:15:13 -0400
- Subject: Re: Global variable in static library - double free or corruption error
- References: <B82EE7AE3CBADB41BB289C2270B2B51102613B60@excg-isl01>
- Reply-to: noloader at gmail dot com
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@orbotech.com> 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@gmail.com)
// 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;
}