How to Use the _Attribute_((Visibility("Default")))

How to use the __attribute__((visibility(default)))?

Is it possible to customize which functions are exposed by modifying the compilation command above?

No. Compilation option -fvisibility=[default|internal|hidden|protected]
(and note it is not a linkage option) makes the compiler attribute the specified dynamic visibility type to all global symbols
generated in the compilation unit except those that are specifically excluded by having a countervailing __attribute__((visibility(....)))
applied in the source code. Which makes the answer to your other question:

Is it possible to make area() public and set_values(int,int) local as shown in the first link without altering the code?

also No.

How would you change the source code to make Rectangle::area() dynamically
visible while all other global symbols are hidden for dynamic linkage by -fvisibility=hidden?
Here is a walk-through:

Let's start with:

rectangle.cpp (1)

class Rectangle {
int width, height;
public:
void set_values (int,int);
int area() {return width*height;}

};

void Rectangle::set_values (int x, int y) {
width = x;
height = y;
}

and simply compile it to a PIC rectangle.o so:

$ g++ -Wall -c -fPIC rectangle.cpp

Then check the global symbol table:

$ nm -C rectangle.o
0000000000000000 T Rectangle::set_values(int, int)

Note that Rectangle::area isn't there. It's not available for
linkage at all, so the question of its dynamic visibility just does not arise.

That is because it is defined inline in the class definition and never called
in the compilation unit, so gcc need not even compile its definition. It vanishes
from the object file.

Rectangle::set_values, on the other hand, is not defined inline, so the compiler
emits a global symbol and definition.

To make Rectangle::area eligible for some visibility type, we first need to make
it a global symbol by not defining it inline:

rectangle.cpp (2)

class Rectangle {
int width, height;
public:
void set_values (int,int);
int area();

};

int Rectangle::area() {return width*height;}

void Rectangle::set_values (int x, int y) {
width = x;
height = y;
}

Recompile and again check the global symbol table:

$ g++ -Wall -c -fPIC rectangle.cpp
$ nm -C rectangle.o
000000000000001a T Rectangle::set_values(int, int)
0000000000000000 T Rectangle::area()

Good. Now a global definition of Rectangle::area appears.

Next let's make a shared library librectangle.so from rectangle.o:

$ g++ -o librectangle.so --shared rectangle.o

Here are the Rectangle::* symbols in its global symbol table:

$ nm -C librectangle.so | grep 'Rectangle::'
00000000000005d4 T Rectangle::set_values(int, int)
00000000000005ba T Rectangle::area()

And here are the Rectangle::* symbols in its dynamic symbol table:

$ nm -CD librectangle.so | grep 'Rectangle::'
00000000000005d4 T Rectangle::set_values(int, int)
00000000000005ba T Rectangle::area()

They're the same.

Now let's hide those symbols for dynamic linkage. We need to recompile rectangle.cpp
then relink the shared library:

$ g++ -Wall -c -fPIC -fvisibility=hidden rectangle.cpp
$ g++ -o librectangle.so --shared rectangle.o

Here again are the Rectangle::* symbols now in the global symbol table:

$ nm -C librectangle.so | grep 'Rectangle::'
0000000000000574 t Rectangle::set_values(int, int)
000000000000055a t Rectangle::area()

They're the same as before.

And here are the Rectangle::* symbols now in the dynamic symbol table:

$ nm -CD librectangle.so | grep 'Rectangle::'; echo Done
Done

Now there are none, thanks to -fvisibility=hidden.

Finally, let's make just Rectangle::area dynamically visible, keeping all
the other global symbols dynamically hidden. We need to change the source code
again:

rectangle.cpp (3)

class Rectangle {
int width, height;
public:
void set_values (int,int);
__attribute__((visibility("default"))) int area();

};

int Rectangle::area() {return width*height;}

void Rectangle::set_values (int x, int y) {
width = x;
height = y;
}

Then recompile and relink:

$ g++ -Wall -c -fPIC -fvisibility=hidden rectangle.cpp
$ g++ -o librectangle.so --shared rectangle.o

The global symbol table still shows:

$ nm -C librectangle.so | grep 'Rectangle::'
00000000000005a4 t Rectangle::set_values(int, int)
000000000000058a T Rectangle::area()

And the dynamic symbol table only shows:

$ nm -CD librectangle.so | grep 'Rectangle::'
000000000000058a T Rectangle::area()

Rectangle::area is now the only symbol that the shared library exposes for
dynamic linkage.

And before you go...

One thing more about:

Is it possible to make area() public and set_values(int,int) local as shown in the first link without altering the code?

Making a symbol hidden for dynamic linkage doesn't make it local. Dynamic visibility (default|internal|hidden|protected)
is an attribute of global symbols only. For linkage purposes, local symbols don't exist. The only ways to
make a symbol local that would otherwise be global are:-

  • In C or C++ source, qualify its definition with the static keyword
  • In C++ source, enclose its definition in an anonymous namespace

Then the symbol does not appear in the global, or dynamic, symbol tables.

[[deprecated]] + __attribute__ ((visibility (default))) in GCC 6.2

After a lot of time, I found that:

void [[deprecated]] [[gnu::visibility("default")]] a_function();

should have been:

 [[deprecated]] [[gnu::visibility("default")]] void a_function();

In this case, both version 0 and 4 work as expected.

GCC: In what way is visibility internal pretty useless in real world usage?

If you just want to hide your internal symbols, just use -fvisibility=hidden. It does exactly what you want.

The internal flag goes much further than the hidden flag. It tells the compiler that ABI compatibility isn't important, since nobody outside the module will ever use the function. If some outside code does manage to call the function, it will probably crash.

Unfortunately, there are plenty of ways to accidentally expose internal functions to the outside world, including function pointers and C++ virtual methods. Plenty of libraries use callbacks to signal events, for example. If your program uses one of these libraries, you must never use an internal function as the callback. If you do, the compiler and linker won't notice anything wrong, and your program will have subtle, hard-to-debug crash bugs.

Even if your program doesn't use function pointers now, it might start using them years down the road when everyone (including you) has forgotten about this restriction. Sacrificing safety for tiny performance gains is usually a bad idea, so internal visibility is not a recommended project-wide default.

The internal visibility is more useful if you have some heavily-used code that you are trying to optimize. You can mark those few specific functions with __attribute__ ((visibility ("internal"))), which tells the compiler that speed is more important than compatibility. You should also leave a comment for yourself, so you remember to never take a pointer to these functions.

How exactly does __attribute__((constructor)) work?

  1. It runs when a shared library is loaded, typically during program startup.
  2. That's how all GCC attributes are; presumably to distinguish them from function calls.
  3. GCC-specific syntax.
  4. Yes, this works in C and C++.
  5. No, the function does not need to be static.
  6. The destructor runs when the shared library is unloaded, typically at program exit.

So, the way the constructors and destructors work is that the shared object file contains special sections (.ctors and .dtors on ELF) which contain references to the functions marked with the constructor and destructor attributes, respectively. When the library is loaded/unloaded the dynamic loader program (ld.so or somesuch) checks whether such sections exist, and if so, calls the functions referenced therein.

Come to think of it, there is probably some similar magic in the normal static linker so that the same code is run on startup/shutdown regardless if the user chooses static or dynamic linking.

Understanding C++ visibility support

In the other examples I found that it is sufficient to only decorate the declarations in the header files. Why also decorate the definitions in the source files?

If global function is declared in a header and that header is included in source file where function is defined, annotation in header will suffice (compiler will indeed pick up the attribute from the header). Otherwise you'll need to annotate it in source code.

I haven't seen this extern keyword in the other examples and I have never used it for public functions. Why do I have to use it here?

The extern keyword is optional in function declarations but it's often used for clarity.

Does __attribute__((visibility(default))) apply to nested structures?

Checking the code with "nm -CD foo.so", we can see that the nested struct is in fact visible:

0000000000201048 B __bss_start
w __cxa_finalize
0000000000201048 D _edata
0000000000201050 B _end
00000000000009cc T _fini
w __gmon_start__
0000000000000780 T _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
U operator delete(void*)
000000000000099c W Foo::impl::method1()
00000000000009a8 W Foo::impl::method2()
00000000000009b4 W Foo::impl::impl()
00000000000009b4 W Foo::impl::impl()
0000000000000960 T Foo::method1()
000000000000097e T Foo::method2()
0000000000000910 T Foo::Foo()
0000000000000910 T Foo::Foo()
0000000000000942 T Foo::~Foo()
0000000000000942 T Foo::~Foo()
U operator new(unsigned long)

However, declaring Foo::impl as a non-nested struct Foo_impl, we then get the following:

0000000000201030 B __bss_start
w __cxa_finalize
0000000000201030 D _edata
0000000000201038 B _end
000000000000088c T _fini
w __gmon_start__
0000000000000668 T _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
U operator delete(void*)
0000000000000820 T Foo::method1()
000000000000083e T Foo::method2()
00000000000007d0 T Foo::Foo()
00000000000007d0 T Foo::Foo()
0000000000000802 T Foo::~Foo()
0000000000000802 T Foo::~Foo()
U operator new(unsigned long)

so it appears that yes, the visibility specifier does in fact propogate to nested structures.



Related Topics



Leave a reply



Submit