PythonAteMyHamster PythonAteMyHamster - 1 month ago 15
C Question

C symbol visibility in static archives

I have files foo.c bar.c and baz.c, plus wrapper code myfn.c defining a function myfn() that uses code and data from those other files.

I would like to create something like an object file or archive, myfn.o or libmyfn.a, so that myfn() can be made available to other projects without also exporting a load of symbols from {foo,bar,baz}.o as well.

What's the right way to do that in Linux/gcc? Thanks.




Update: I've found one way of doing it. I should've emphasised originally that this was about static archives, not DSOs. Anyway, the recipe:


  1. #define PUBLIC __attribute__ ((visibility("default")))
    then mark
    myfn()
    as
    PUBLIC
    in
    myfn.c
    . Don't mark anything else
    PUBLIC
    .

  2. Compile objects with
    gcc -c foo.c bar.c baz.c myfn.c -fvisibility=hidden
    , which marks everything as hidden except for
    myfn()
    .

  3. Create a convenience archive using
    ld
    's partial-linking switch:
    ld -r foo.o bar.o baz.o myfn.o -o libmyfn.a

  4. Localise everything that wasn't
    PUBLIC
    like so:
    objcopy --localize-hidden libmyfn.a

  5. Now
    nm
    says
    myfn
    is the only global symbol in
    libmyfn.a
    and subsequent linking into other programs works just fine:
    gcc -o main main.c -L. -lmyfn
    (here, the program calls
    myfn()
    ; if it tried to call
    foo()
    then compilation would fail).



If I use
ar
instead of
ld -r
in step 3 then compilation fails in step 5: I guess
ar
hasn't linked
foo
etc to
myfn
, and no longer can once those functions are localised, whereas
ld -r
resolves the link before it gets localised-away.

I'd welcome any response that confirms this is the "right" way, or describes a slicker way of achieving the same.

Answer

Unfortunately, C linkage for globals is all-or-nothing, in the sense that the globals of all modules would be available in libmyfn.a's final list of external symbols.

gcc tool chain offers an extension that lets you hide symbols from outside users, while making them available to other translation units in your library:

foo.h:

void foo();

foo.c:

void foo() __attribute__ ((visibility ("hidden")));

myfn.h:

void myfn();

myfn.c:

#include <stdio.h>
#include "foo.h"

void myfn() {
    printf("calling foo...\n");
    foo();
    printf("calling foo again...\n");
    foo();
}

For portability, you would probably benefit from making a macro for __attribute__ ((visibility ("hidden"))), and placing it in a conditional compilation block conditioned on gcc.

In addition, Linux offers a utility called strip, which lets you remove some of the symbols from compiled object files. Options -N and -K let you identify individual symbols that you want to keep or remove.