Stripping linux shared libraries
If you wrap up your private part in an anonymous namespace then neither std::abs
nor private_function
can be seen in the symbol table:
namespace{
#include<cmath>
float private_function(float f)
{
return std::abs(f);
}
}
extern "C" float public_function(float f)
{
return private_function(f);
}
compiling (g++ 4.3.3):
g++ -shared -o libtest.so test.cpp -s
inspecting:
# nm -DC libtest.so
w _Jv_RegisterClasses
0000200c A __bss_start
w __cxa_finalize
w __gmon_start__
0000200c A _edata
00002014 A _end
000004a8 T _fini
000002f4 T _init
00000445 T public_function
Just to note that Ulrich Drepper wrote an essay regarding (all?) aspects of writing shared libraries for Linux/Unix, which covers control of exported symbols amongst many other topics.
This was very handy in making it clear how to export only functions on a whitelist from a shared lib.
Your use of the default visibility attribute and -fvisibility=hidden should be augmented with -fvisibility-inlines-hidden.
You should also forget about trying to hide stdlib exports, see this GCC bug for why.
Also, if you have all of your public symbols in a specific headers you can wrap them in #pragma GCC visibility push(default)
and #pragma GCC visibility pop
instead of using attributes. Though if you are creating a cross platform library, take a look at Controlling Exported Symbols of Shared Libraries for a technique to unify your windows DLL and Linux DSO export strategy.
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!