Tuesday, March 13, 2012

How to create a dynamic library with symbols hidden. "-fvisibility"


The requirement is you need to provide a .so to the application exposing only your new symbols and not the symbols of the other .o/.so files that you might have used in creating your file.

Lets see an example of how to do it.

The ace card we use for this game is the gcc flag "-fvisibility=hidden".

Lets create a lib1.o which will serve as the .o that we want to include into our new file. After that we create a lib2.o that provides a wrapper (lib2_fun2) around the function (lib1_fun1) that we want to provide to the application. Finally we test this concept with a test application where we will call all the functions to check their visibility/ availability at this layer.


Step 1:

lib1.c : Function with Default visibility, built with hidden visibility.

void /*__attribute__ ((visibility ("default")) )*/ lib1_fun1(void);

void /*__attribute__ ((visibility ("default")) )*/ lib1_fun1(void)
{
    printf("%s\n",__FUNCTION__);
}


$ gcc -fvisibility=hidden -fPIC -Wall -c lib1.c

$ readelf -s lib1.o | grep fun
    Num:    Value  Size Type    Bind   Vis      Ndx Name
    12: 00000000    38 FUNC    GLOBAL HIDDEN     2 lib1_fun1

Notice the Vis column saying "HIDDEN". :-) wasn't that what we wanted.
---------------------------------------------------------------------------------
Step 2:

lib2.c : Function _fun1 with hidden visibility & _fun2 with Default visibility, file built with hidden visibility.

void __attribute__ ((visibility ("hidden")) ) lib2_fun1(void);
void __attribute__ ((visibility ("default")) ) lib2_fun2(void);

void __attribute__ ((visibility ("default")) ) lib2_fun2(void)
{
    printf("%s is calling ",__FUNCTION__);
    lib1_fun1(); //This is the function we want to wrap and provide functionality to outside world
}

$ gcc -fvisibility=hidden -fPIC -Wall -c lib2.c

$ readelf -s lib2.o | grep fun
    Num:    Value  Size Type    Bind   Vis      Ndx Name
    13: 00000000    38 FUNC    GLOBAL HIDDEN     2 lib2_fun1
    17: 00000026    53 FUNC    GLOBAL DEFAULT    2 lib2_fun2
    19: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND lib1_fun1
---------------------------------------------------------------------------------

Make a .so file which includes both the object files in it.

$ gcc -fvisibility=hidden -shared -Wl,-soname,lib12.so -o lib12.so lib1.o lib2.o

$ readelf -s lib12.so | grep fun
    Num:    Value  Size Type    Bind   Vis      Ndx Name
     6: 000004ea    53 FUNC    GLOBAL DEFAULT   11 lib2_fun2
    45: 0000049c    38 FUNC    LOCAL  DEFAULT   11 lib1_fun1
    47: 000004c4    38 FUNC    LOCAL  DEFAULT   11 lib2_fun1
    51: 000004ea    53 FUNC    GLOBAL DEFAULT   11 lib2_fun2
  
Notice the lib1_fun1 is declared as 'LOCAL' even though the Visibilty is 'DEFAULT'

---------------------------------------------------------------------------------

With this we have achieved hiding the symbol from the outside world's usage. But the symbol is still exposed when we do a readelf. How do we get rid of that?

Thats were we use another tool called "strip"

$ strip --strip-all --discard-all lib12.so

$ readelf -s lib12.so | grep fun
    Num:    Value  Size Type    Bind   Vis      Ndx Name
     6: 000004ea    53 FUNC    GLOBAL DEFAULT   11 lib2_fun2

With this, the application writer can only "see" the symbols you want him to see and rest are completely removed. :-)

---------------------------------------------------------------------------------

Now make the testap to test all our theories.

int main(void)
{
    lib1_fun1();   
    lib2_fun1();   
    lib2_fun2();   
    return 0; 
}

$gcc -o testapp testlib.c -L. -l12

The result is

    /tmp/cc2DnM96.o: In function `main':
    testlib.c:(.text+0x7): undefined reference to `lib1_fun1'
    testlib.c:(.text+0xc): undefined reference to `lib2_fun1'
    collect2: ld returned 1 exit status

This shows that the only symbol available to the application out of the lib12.so is 'lib2_fun2' which we wanted to provide and hence declared as global.  The rest of the symbols are only 'Local' to the library and are NOT available outside the library to any application that might link to it.


For more information about the "-fvisibility" option, check the below links
http://blog.fesnel.com/blog/2009/08/19/hiding-whats-exposed-in-a-shared-library/
http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Function-Attributes.html  


No comments:

Post a Comment