Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Restricting symbols in a Linux static library

Tags:

c

linux

static

gcc

People also ask

Do static libraries have symbols?

At link time, a static library can have unresolved symbols in it, as long as you don't need the unresolved symbols, and you don't need any symbol that is in a .o file that contains an unresolved symbol.

What commands can be used to list the symbols stored in a static library?

If we want to see the contents of our library, we can use the ar option -t . We can also see the symbols in our library, using the command nm , which lists each symbol's symbol value, symbol type, and symbol name from object files.

What is static library in Linux?

Static libraries are simply a collection of ordinary object files; conventionally, static libraries end with the ``. a'' suffix. This collection is created using the ar (archiver) program.

What is dynamic library in Linux?

Libraries that exist as separate files outside of the executable files are known as dynamic libraries. At compile time the program makes one copy of the library's files.


I don't believe GNU ld has any such options; Ulrich must have meant objcopy, which has many such options: --localize-hidden, --localize-symbol=symbolname, --localize-symbols=filename.

The --localize-hidden in particular allows one to have a very fine control over which symbols are exposed. Consider:

int foo() { return 42; }
int __attribute__((visibility("hidden"))) bar() { return 24; }

gcc -c foo.c
nm foo.o
000000000000000b T bar
0000000000000000 T foo

objcopy --localize-hidden foo.o bar.o
nm bar.o
000000000000000b t bar
0000000000000000 T foo

So bar() is no longer exported from the object (even though it is still present and usable for debugging). You could also remove bar() all together with objcopy --strip-unneeded.


Static libraries can not do what you want for code compiled with either GCC 3.x or 4.x.

If you can use shared objects (libraries), the GNU linker does what you need with a feature called a version script. This is usually used to provide version-specific entry points, but the degenerate case just distinguishes between public and private symbols without any versioning. A version script is specified with the --version-script= command line option to ld.

The contents of a version script that makes the entry points foo and bar public and hides all other interfaces:

{ global: foo; bar; local: *; };

See the ld doc at: http://sourceware.org/binutils/docs/ld/VERSION.html#VERSION

I'm a big advocate of shared libraries, and this ability to limit the visibility of globals is one their great virtues.

A document that provides more of the advantages of shared objects, but written for Solaris (by Greg Nakhimovsky of happy memory), is at http://developers.sun.com/solaris/articles/linker_mapfiles.html

I hope this helps.


The merits of this answer will depend on why you're using static libraries. If it's to allow the linker to drop unused objects later then I have little to add. If it's for the purpose of organisation - minimising the number of objects that have to be passed around to link applications - this extension of Employed Russian's answer may be of use.

At compile time, the visibility of all symbols within a compilation unit can be set using:

-fvisibility=hidden
-fvisibility=default

This implies one can compile a single file "interface.c" with default visibility and a larger number of implementation files with hidden visibility, without annotating the source. A relocatable link will then produce a single object file where the non-api functions are "hidden":

ld -r interface.o implementation0.o implementation1.o -o relocatable.o

The combined object file can now be subjected to objcopy:

objcopy --localize-hidden relocatable.o mylibrary.o

Thus we have a single object file "library" or "module" which exposes only the intended API.


The above strategy interacts moderately well with link time optimisation. Compile with -flto and perform the relocatable link by passing -r to the linker via the compiler:

gcc -fuse-linker-plugin -flto -nostdlib -Wl,-r {objects} -o relocatable.o

Use objcopy to localise the hidden symbols as before, then call the linker a final time to strip the local symbols and whatever other dead code it can find in the post-lto object. Sadly, relocatable.o is unlikely to have retained any lto related information:

gcc -nostdlib -Wl,-r,--discard-all relocatable.o mylibrary.o

Current implementations of lto appear to be active during the relocatable link stage. With lto on, the hidden=>local symbols were stripped by the final relocatable link. Without lto, the hidden=>local symbols survived the final relocatable link.

Future implementations of lto seem likely to preserve the required metadata through the relocatable link stage, but at present the outcome of the relocatable link appears to be a plain old object file.


This is a refinement of the answers from EmployedRussian and JonChesterfield, which may be helpful if you're generating both dynamic and static libraries.

Start with the standard mechanism for hiding symbols in DSOs (the dynamic version of your lib). Compile all files with -fvisibility=hidden. In the header file which defines your API, change the declarations of the classes and functions you want to make public:

   #define DLL_PUBLIC __attribute__ ((visibility ("default")))
   extern DLL_PUBLIC int my_api_func(int);

See here for details. This works for both C and C++. This is sufficient for DSOs, but you'll need to add these build steps for static libraries:

ld -r obj1.o obj2.o ... objn.o -o static1.o
objcopy --localize-hidden static1.o static2.o
ar -rcs mylib.a static2.o

The ar step is optional - you can just link against static2.o.