strange behavior when compile with -fvisibility-inlines-hidden flag

Hi all,

Consider the following code (full source code can be found in attachment)

object.h

#define _DESTRUCTOR_IN_HEADER

class Object
{
public:
Object();
#ifdef _DESTRUCTOR_IN_HEADER
~Object()
{
}
#else
~Object();
#endif

private:
class Counter
{
public:
Counter()
: _count(0)
{
printf(“Counter: %p %d\n”, this, _count);
}

~Counter()
{
printf("~Counter: %p %d\n", this, _count);
}

void operator++() { ++_count; }
void operator–() { --_count; }

private:
int _count;
};

class Foo
{
public:
Foo() { ++counter(); }
~Foo() { --counter(); }

private:
Counter& counter()
{
static Counter s_counter;
return s_counter;
}
} foo;
};

object.cpp

#include “object.h”

Object::Object()
{
}

#ifndef _DESTRUCTOR_IN_HEADER
Object::~Object()
{

}
#endif

Build the source to a shared library (compile with -fvisibility-inlines-hidden flag),
uses in main (another module)

Object *obj = new Object;
delete obj;

you may see the strange output when running

Counter: 0x7f2ded933efc 0
Counter: 0x6012d4 0
~Counter: 0x6012d4 -1
~Counter: 0x7f2ded933efc 1

The Counter construct/destruct twice, the second one (Counter: 0x6012d4 0) construct from
delete obj > Object::Foo::~Foo() > Object::Foo::counter()

when comment out the line #define _DESTRUCTOR_IN_HEADER or remove the
-fvisibility-inlines-hidden flag, it works as expected
remove the compile flag

Counter: 0x6013a4 0
~Counter: 0x6013a4 0

comment out #define _DESTRUCTOR_IN_HEADER

Counter: 0x7f1eaa16629c 0
~Counter: 0x7f1eaa16629c 0

A bit difference, as the address isn’t the same (heap and stack)

this code works with GCC (as least 5.2 as my test) with or without the -fvisibility-inlines-hidden
flag, of course it works with MSVC too.

I don’t known is this a bug with the flag or because the silly code makes it (as when constructor and
destructor defined in the same file, it works).

It seems that my last post failed, seems no body answers me, so I repost again, sorry.

If this is the wrong place, please let me know where to post, thanks.

demo.tar.xz (832 Bytes)

Hi all,

Consider the following code (full source code can be found in attachment)

object.h

#define _DESTRUCTOR_IN_HEADER

class Object
{
public:
    Object();
#ifdef _DESTRUCTOR_IN_HEADER
    ~Object()
    {
    }
#else
    ~Object();
#endif

private:
    class Counter
    {
    public:
        Counter()
            : _count(0)
        {
            printf("Counter: %p %d\n", this, _count);
        }

        ~Counter()
        {
            printf("~Counter: %p %d\n", this, _count);
        }

        void operator++() { ++_count; }
        void operator--() { --_count; }

    private:
        int _count;
    };

    class Foo
    {
    public:
        Foo() { ++counter(); }
        ~Foo() { --counter(); }

    private:
        Counter& counter()
        {
            static Counter s_counter;
            return s_counter;
        }

When you use -fvisibility-inlines-hidden, you give this function hidden
visibility, so each DSO gets its own copy of the function. We also give
each DSO its own copy of each inline function's static local variables.
Thus the Object::Object() constructor in your shared library increments one
counter, and the Object::~Object() destructor in your main binary
decrements a different counter.

GCC appears to not make the static local variable hidden when it makes the
surrounding function hidden. That makes sense, since
-fvisibility-inlines-hidden is supposed to not change the semantics of the
program unless you're comparing addresses of inline functions.

    } foo;

Hi all,

Consider the following code (full source code can be found in attachment)

object.h

#define _DESTRUCTOR_IN_HEADER

class Object
{
public:
    Object();
#ifdef _DESTRUCTOR_IN_HEADER
    ~Object()
    {
    }
#else
    ~Object();
#endif

private:
    class Counter
    {
    public:
        Counter()
            : _count(0)
        {
            printf("Counter: %p %d\n", this, _count);
        }

        ~Counter()
        {
            printf("~Counter: %p %d\n", this, _count);
        }

        void operator++() { ++_count; }
        void operator--() { --_count; }

    private:
        int _count;
    };

    class Foo
    {
    public:
        Foo() { ++counter(); }
        ~Foo() { --counter(); }

    private:
        Counter& counter()
        {
            static Counter s_counter;
            return s_counter;
        }

When you use -fvisibility-inlines-hidden, you give this function hidden
visibility, so each DSO gets its own copy of the function. We also give
each DSO its own copy of each inline function's static local variables.
Thus the Object::Object() constructor in your shared library increments one
counter, and the Object::~Object() destructor in your main binary
decrements a different counter.

GCC appears to not make the static local variable hidden when it makes the
surrounding function hidden. That makes sense, since
-fvisibility-inlines-hidden is supposed to not change the semantics of the
program unless you're comparing addresses of inline functions.

It also looks like this may have changed since the flag was originally
designed.

https://gcc.gnu.org/wiki/Visibility says "However,
-fvisibility-inlines-hidden can be used with no source alterations, unless
you need to override it for inlines where address identity is important
either for the function itself *or any function local static data.*"
(Clearly this is somewhat bogus: if the addresses of function local statics
are different, their contents may also be different.)

https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Dialect-Options.html says:
"This switch declares that the user does not attempt to compare pointers to
inline functions or methods where the addresses of the two functions are
taken in different shared objects. [...] The behavior of this switch is not
quite the same as marking the methods as hidden directly, because it *does
not affect static variables local to the function* or cause the compiler to
deduce that the function is defined in only one shared object."

In any case, this is a Clang bug.

says "However, -fvisibility-inlines-hidden can be used with no source alterations, unless you need to override it for inlines where address identity is important either for the function itself " (Clearly this is somewhat bogus: if the addresses of function local statics are different, their contents may also be different.)

The behavior of this switch is not quite the same as marking the methods as hidden directly, because it does not affect static variables local to the function or cause the compiler to deduce that the function is defined in only one shared object.

Thanks for your detailed explanation.

One more strange thing, remove the -fvisibility-inlines-hidden flag not works with some codes of our project.
I can’t reproduce for a simple demo for now. Another flag we use is -fvisibility-ms-compat, before adding it for gcc, all works fine.
Now both gcc and clang are broken. (Since the counter code enables in debug build only, it’s not so important for now)

I will remove -fvisibility-ms-compat rebuild with clang and test again.

Enable -fvisibility-ms-compat flag might have the same effect as -fvisibility-inlines-hidden, as my test.
I think our project should change the way how constructor/destructor use, as gcc/clang/msvc not always the same.