Bug 48323 - [4.5/4.6/4.7 Regression] Lifetime of local variables: global versus member function
Summary: [4.5/4.6/4.7 Regression] Lifetime of local variables: global versus member fu...
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: middle-end (show other bugs)
Version: 4.5.1
: P3 normal
Target Milestone: 4.5.3
Assignee: Not yet assigned to anyone
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2011-03-28 20:00 UTC by Jan van Dijk
Modified: 2011-03-28 22:25 UTC (History)
2 users (show)

See Also:
Host:
Target: x86_64-linux-gnu
Build:
Known to work:
Known to fail:
Last reconfirmed: 2011-03-28 20:31:38


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jan van Dijk 2011-03-28 20:00:56 UTC
[First presented on gcc-help. Submitted as per the suggestion of Ian Lance Taylor, see http://gcc.gnu.org/ml/gcc-help/2011-03/msg00340.html ]

Consider the following shared object:

// g++ -o libtest.so -shared -fPIC dltestlib.cpp

#include <iostream>

int global_f() { static int i=0; return i; }

struct C
{
        int class_scope_f() { static int i=0; return i; }
        C()
        {
                global_f();
//              class_scope_f(); // CULPRIT
        }
        ~C() { std::cout << "Destructor called" << std::endl; }
};

static C c;

Also consider the following test program, which merely loads and unloads this 
library using dlopen/dlclose.

// g++ dltest2.cpp -ldl

#include <dlfcn.h>
#include <iostream>

int main(int argc, const char* argv[])
{
        if (argc!=2) return -1;

        const char* fname = argv[1];
        std::cout << "Opening: " << fname << std::endl;
        void* handle = dlopen( fname, RTLD_NOW | RTLD_LOCAL);
        std::cout << "Handle after dlopen: " << handle << std::endl;
        if(!handle)
        {
                std::cout << dlerror() << std::endl;
        }

        dlclose(handle);
        // do not load. Only check if the file is resident.
        handle = dlopen( fname, RTLD_NOW | RTLD_NOLOAD);
        std::cout << "Handle after dlclose: " << handle << std::endl;
        std::cout << "Exiting..." << std::endl;
}

Providing the abovementioned library as argument, I get the expected result:

./a.out /home/jan/src/gum-cvs/ideas/jan/libtest.so

Opening: /home/jan/src/gum-cvs/ideas/jan/libtest.so
Handle after dlopen: 0x602050
Destructor called
Handle after dlclose: 0
Exiting...

In particular, the library *is* unloaded by dlclose (so the global destructor 
of object c in the library is called before the program is exiting.)

And now for the problem... 
If I comment in line 11 of the library module (marked CULPRIT), thus calling 
class_scope_f(), and try again, the result is:

Opening: /home/jan/src/gum-cvs/ideas/jan/libtest.so
Handle after dlopen: 0x602050
Handle after dlclose: 0x602050
Exiting...
Destructor called

Now the library unloading fails (the handle is still !=0, and indeed the 
destructor of c is not called before the program terminates).

I assume this is because there is a difference in lifetime between local 
static variables in functions in global scope vs. those of member functions. 
Is this the intended behaviour? (I say 'intended', not 'mandated', because I 
know so's are beyond the scope of ISO-C++.) If not, should I file a report?

I am on OpenSuSE 11.4; that is: glibc 2.11.3, gcc 4.5.1, binutils 2.21.
Comment 1 Andrew Pinski 2011-03-28 20:31:38 UTC
+	.type	_ZZN1C13class_scope_fEvE1i, @gnu_unique_object
-	.type	_ZZN1C13class_scope_fEvE1i, @object

- works but + does not.

4.4 produces object while 4.5 and above produce @gnu_unique_object.
Comment 2 Andrew Pinski 2011-03-28 20:41:55 UTC
http://gcc.gnu.org/ml/gcc-patches/2009-07/msg01240.html was the patch which changed to use @gnu_unique_object .
Jason can you explain why this is causing an issue where the function passed to __cxa_atexit being called?
Comment 3 Jakub Jelinek 2011-03-28 20:55:38 UTC
It has nothing to do with __cxa_atexit.  The dynamic linker sets
                            if (map->l_type == lt_loaded)
                              /* Make sure we don't unload this object by
                                 setting the appropriate flag.  */
                              map->l_flags_1 |= DF_1_NODELETE;
whenever doing successful symbol lookup of a STB_GNU_UNIQUE symbol, so that that symbol will always be found at that point afterwards.
Comment 4 Jan van Dijk 2011-03-28 22:17:15 UTC
(In reply to comment #3)
Sorry for being pedantic, but would you care to explain how your observation renders this report invalid? I am afraid I do not understand this resolution. 

Do you assert that the two cases (static local in global vs. class scope function) are deliberately treated differently? If so, is this an implementation choice, or is it based on a document that I should have read (which one)?

If this isn't changed back, shouldn't this change at least be documented? It may break more user code, not just mine...

Thanks again for your time.
Comment 5 Andrew Pinski 2011-03-28 22:25:00 UTC
(In reply to comment #4)
> (In reply to comment #3)
> Sorry for being pedantic, but would you care to explain how your observation
> renders this report invalid? I am afraid I do not understand this resolution. 
> 
> Do you assert that the two cases (static local in global vs. class scope
> function) are deliberately treated differently? If so, is this an
> implementation choice, or is it based on a document that I should have read
> (which one)?

Yes they are different because C::class_scope_f is a vague linkage while global_f is global linkage.  So the i in each one is of different linkage.  If you want C type to be local to the shared, then you need to use either the hidden attribute on it or use -fvisibility=hidden.