Wenn Sie Ihren privaten Teil in einem anonymen Namensraum verpacken, dann weder std::abs
noch private_function
ist in der Symboltabelle ersichtlich:
namespace{
#include<cmath>
float private_function(float f)
{
return std::abs(f);
}
}
extern "C" float public_function(float f)
{
return private_function(f);
}
Kompilieren (g++ 4.3.3):
g++ -shared -o libtest.so test.cpp -s
Inspektion:
# 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
Nur um anzumerken, dass Ulrich Drepper einen Aufsatz über (alle?) Aspekte des Schreibens gemeinsam genutzter Bibliotheken für Linux/Unix geschrieben hat, der neben vielen anderen Themen die Kontrolle exportierter Symbole behandelt.
Dies war sehr praktisch, um zu verdeutlichen, wie man nur Funktionen auf einer Whitelist aus einer gemeinsam genutzten Bibliothek exportiert.
Ihre Verwendung des Standard-Sichtbarkeitsattributs und -fvisibility=hidden sollte durch -fvisibility-inlines-hidden.
ergänzt werdenSie sollten auch den Versuch vergessen, stdlib-Exporte zu verstecken, siehe diesen GCC-Fehler für den Grund.
Wenn Sie alle Ihre öffentlichen Symbole in einem bestimmten Header haben, können Sie sie auch in #pragma GCC visibility push(default)
einschließen und #pragma GCC visibility pop
anstatt Attribute zu verwenden. Wenn Sie jedoch eine plattformübergreifende Bibliothek erstellen, werfen Sie einen Blick auf Steuern exportierter Symbole gemeinsam genutzter Bibliotheken, um eine Technik zum Vereinheitlichen Ihrer Windows-DLL- und Linux-DSO-Exportstrategie zu finden.
Die Lösung, die wir jetzt haben, lautet also wie folgt:
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:
*;
};
kompiliert mit
g++ -shared test.cpp -o libtest.so -fvisibility=hidden -fvisibility-inlines-hidden -s -Wl,--version-script=exports.version
gibt
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
Was dem, was wir suchen, ziemlich nahe kommt. Es gibt jedoch ein paar Fallstricke:
- Wir müssen sicherstellen, dass wir nicht das Präfix "exportiert" (in diesem einfachen Beispiel "öffentlich", aber in unserem Fall offensichtlich etwas nützlicheres) im internen Code verwenden.
- Viele Symbolnamen landen immer noch in der String-Tabelle, was anscheinend auf RTTI zurückzuführen ist, -fno-rtti lässt sie in meinen einfachen Tests verschwinden, ist aber eine ziemlich nukleare Lösung.
Ich nehme gerne jede bessere Lösung an, die jemand hat!