Se avvolgi la tua parte privata in uno spazio dei nomi anonimo, allora nemmeno std::abs
né private_function
può essere visto nella tabella dei simboli:
namespace{
#include<cmath>
float private_function(float f)
{
return std::abs(f);
}
}
extern "C" float public_function(float f)
{
return private_function(f);
}
compilazione (g++ 4.3.3):
g++ -shared -o libtest.so test.cpp -s
ispezionando:
# 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
Solo per notare che Ulrich Drepper ha scritto un saggio riguardante (tutti?) gli aspetti della scrittura di librerie condivise per Linux/Unix, che copre il controllo dei simboli esportati tra molti altri argomenti.
Questo è stato molto utile per chiarire come esportare solo le funzioni su una whitelist da una libreria condivisa.
Il tuo utilizzo dell'attributo di visibilità predefinito e -fvisibility=hidden dovrebbe essere potenziato con -fvisibility-inlines-hidden.
Dovresti anche dimenticare di provare a nascondere le esportazioni stdlib, vedi questo bug GCC per il motivo.
Inoltre, se hai tutti i tuoi simboli pubblici in intestazioni specifiche, puoi racchiuderli in #pragma GCC visibility push(default)
e #pragma GCC visibility pop
invece di utilizzare gli attributi. Tuttavia, se stai creando una libreria multipiattaforma, dai un'occhiata a Controllo dei simboli esportati delle librerie condivise per una tecnica per unificare la tua DLL di Windows e la strategia di esportazione DSO di Linux.
Quindi la soluzione che abbiamo per ora è la seguente:
prova.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:
*;
};
compilato con
g++ -shared test.cpp -o libtest.so -fvisibility=hidden -fvisibility-inlines-hidden -s -Wl,--version-script=exports.version
dà
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
Che è abbastanza vicino a quello che stiamo cercando. Ci sono alcuni trucchi però:
- Dobbiamo assicurarci di non utilizzare il prefisso "esportato" (in questo semplice esempio "pubblico", ma ovviamente qualcosa di più utile nel nostro caso) nel codice interno.
- Molti nomi di simboli finiscono ancora nella tabella delle stringhe, che sembra dipendere da RTTI, -fno-rtti li fa sparire nei miei semplici test, ma è una soluzione piuttosto nucleare.
Sono felice di accettare qualsiasi soluzione migliore che qualcuno propone!