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

27,971

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:-

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

Share:
27,971
Tony Tannous
Author by

Tony Tannous

Sunspots have faded and now I'm doing time.

Updated on July 09, 2022

Comments

  • Tony Tannous
    Tony Tannous almost 2 years

    Reading Visibility in the GNU wiki, it is clear.

    Taking this example from C++ Tutorials

    // classes example
    #include <iostream>
    using namespace std;
    
    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;
    }
    

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

    I wrote my makefile to get the .so

    someproj.so : someproj.cpp
        g++ --std=c++11 -O3 -fPIC -shared someproj.cpp -o someproj.so
    

    Modified to make all symbols hidden by adding -fvisibility=hidden

    someproj.so : someproj.cpp
        g++ --std=c++11 -O3 -fvisibility=hidden -fPIC -shared someproj.cpp -o someproj.so
    

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

    Currently using 4.7.2 version of gcc

  • russoue
    russoue over 5 years
    Great answer with examples! Thank you.
  • SeeTheC
    SeeTheC over 4 years
    Nicely explained.
  • jw_
    jw_ about 4 years
    @russoue Yes, best answer I've ever seen. Another example of why 1 vote=1 rep is a bad idea and why the system need a rep revamp.
  • DeltA
    DeltA over 3 years
    Great answer. I wish I could upvote more than once.
  • dragonxlwang
    dragonxlwang over 3 years
    text-book style answer that I wish there's such an answer for every SO question!