views:

1880

answers:

3

I'm looking for ways to restrict the number of C symbols exported to a Linux static library (archive). I'd like to limit these to only those symbols that are part of the official API for the library. I already use 'static' to declare most functions as static, but this restricts them to file scope. I'm looking for a way to restrict to scope to the library.

I can do this for shared libraries using the techniques in Ulrich Drepper's How to Write Shared Libraries, but I can't apply these techniques to static archives. In his earlier Good Practices in Library Design paper, he writes:

The only possibility is to combine all object files which need certain internal resources into one using 'ld -r' and then restrict the symbols which are exported by this combined object file. The GNU linker has options to do just this.

Could anyone help me discover what these options might be? I've had some success with 'strip -w -K prefix_*', but this feels brutish. Ideally, I'd like a solution that will work with both GCC 3 and 4.

Thanks!

A: 

My way of doing it is to mark everything that is not to be exported with INTERNAL, include guard all .h files, compile dev builds with -DINTERNAL= and compile release builds with a single .c file that includes all other library .c files with -DINTERNAL=static.

Joshua
It would work if you have everything in one file or you can compile to it. Often you do not. Sometimes you need to have small files to combine with other languages (for example Haskell - that's the reason I found this page).
Maciej Piechotka
@Maciej: Well OP asked about gcc ...
Joshua
+5  A: 

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.

tpgould
+2  A: 

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.

Employed Russian