Gnu Linker: Alternative to -Version-Script to List Exported Symbols at The Command Line

GNU linker: alternative to --version-script to list exported symbols at the command line?

I'm not sure that you can do this like you want.
One way is with the linker version script like you mentioned. Another way is to add in your source code __attribute__ ((visibility("default"))) for whatever you want exported and compile everything with -fvisibility=hidden

Avoiding exporting symbols from executables on Linux

Executables don't export symbols by default, and will not do so unless you use -Wl,--export-dynamic. This is necessary only if you're dynamically loading libraries that themselves need to link into symbols in the main executable (this is a common case in C++ if your libraries contain classes which override virtual methods in the exe)

Perhaps you're confusing exporting symbols with having debug symbols. Debug symbols will be produced for the benefit of the debugger (if you don't strip the exe), but are not required to run.

Symbols in C++ – are they exported in non-debug build?

a. stripped build

b. non-shared library build

It's not clear whether you are asking about a build that satisfies both A) and B), or the A) and B) scenarios separately.

For both -- non-shared, stripped build -- yes, the symbols should be all gone.

For just A), no: if you are using shared library, the symbols will be (by default) exported from it, and strip will not remove them. If you are using ELF, you could limit symbol visibility with e.g. __attribute__((visibility("hidden"))) or an equivalent mechanism.

For just B) -- non-stripped, non-shared build, the symbols will generally be present in the symbol table, and so strings will show them. To get rid of them, use strip (which turns this into A) and B) combined).

Linking against older symbol version in a .so file

Just link memcpy statically - pull memcpy.o out of libc.a ar x /path/to/libc.a memcpy.o (whatever version - memcpy is pretty much a standalone function) and include it in your final link. Note that static linking may complicate licensing issues if your project is distributed to the public and not open-source.

Alternatively, you could simply implement memcpy yourself, though the hand-tuned assembly version in glibc is likely to be more efficient

Note that memcpy@GLIBC_2.2.5 is mapped to memmove (old versions of memcpy consistently copied in a predictable direction, which led to it sometimes being misused when memmove should have been used), and this is the only reason for the version bump - you could simply replace memcpy with memmove in your code for this specific case.

Or you could go to static linking, or you could ensure that all systems on your network have the same or better version than your build machine.

Stripping linux shared libraries

So the solution we have for now is as follows:

test.cpp

#include <cmath>
#include <vector>
#include <typeinfo>

struct private_struct
{
float f;
};

float private_function(float f)
{
return std::abs(f);
}

void other_private_function()
{
std::vector<private_struct> f(1);
}

extern "C" void __attribute__ ((visibility ("default"))) public_function2()
{
other_private_function();
}

extern "C" float __attribute__ ((visibility ("default"))) public_function1(float f)
{
return private_function(f);
}

exports.version

LIBTEST 
{
global:
public*;
local:
*;
};

compiled with

g++ -shared test.cpp -o libtest.so -fvisibility=hidden -fvisibility-inlines-hidden -s -Wl,--version-script=exports.version

gives

00000000 A LIBTEST
w _Jv_RegisterClasses
U _Unwind_Resume
U std::__throw_bad_alloc()
U operator delete(void*)
U operator new(unsigned int)
w __cxa_finalize
w __gmon_start__
U __gxx_personality_v0
000005db T public_function1
00000676 T public_function2

Which is fairly close to what we're looking for. There are a few gotchas though:

  • We have to ensure we don't use the "exported" prefix (in this simple example "public", but obviously something more useful in our case) in the internal code.
  • Many symbol names still end up in the string table, which appears to be down to RTTI, -fno-rtti makes them go away in my simple tests, but is a rather nuclear solution.

I'm happy to accept any better solutions anyone comes up with!

Limiting visibility of symbols when linking shared libraries

GNU ld can do that on ELF platforms.

Here is how to do it with a linker version script:

/* foo.c */
int foo() { return 42; }
int bar() { return foo() + 1; }
int baz() { return bar() - 1; }

gcc -fPIC -shared -o libfoo.so foo.c && nm -D libfoo.so | grep ' T '

By default, all symbols are exported:

0000000000000718 T _fini
00000000000005b8 T _init
00000000000006b7 T bar
00000000000006c9 T baz
00000000000006ac T foo

Let's say you want to export only bar() and baz(). Create a "version script" libfoo.version:

FOO {
global: bar; baz; # explicitly list symbols to be exported
local: *; # hide everything else
};

Pass it to the linker:

gcc -fPIC -shared -o libfoo.so foo.c -Wl,--version-script=libfoo.version

Observe exported symbols:

nm -D libfoo.so | grep ' T '
00000000000005f7 T bar
0000000000000609 T baz

How to apply -fvisibility option to symbols in static libraries?

Basically, visibility is handled during linking, and the linker doesn't seem impose it on static archives. A related question (though not a duplicate) was asked on SO here.

What I would advise you to do is to replace your linking stage: gcc -shared -o mylib.so foo.o libbar.a into a two stages process where you get back the object files:

  • ar x libbar.a (possibly into a suitable, empty directory)
  • gcc -fvisibility=hidden -shared -o mylib.so foo.o tempdir/*.o


Related Topics



Leave a reply



Submit