Usuwanie współdzielonych bibliotek linuxowych

Niedawno zostaliśmy poproszeni o wysłanie wersji Linuksa jednej z naszych bibliotek, wcześniej opracowywaliśmy ją pod Linuksem i wysyłaliśmy dla Windows, gdzie wdrażanie bibliotek jest na ogół dużo łatwiejsze. Problem, na który się natknęliśmy, polega na pozbyciu się eksportowanych symboli tylko do tych znajdujących się w odsłoniętym interfejsie. Istnieją trzy dobre powody, dla których chcesz to zrobić

Aby chronić zastrzeżone aspekty naszej technologii przed ekspozycją poprzez eksportowane symbole.Aby zapobiec problemom użytkowników z konfliktowymi nazwami symboli.Aby przyspieszyć ładowanie biblioteki (przynajmniej tak mi się mówi).

Weźmy prosty przykład:

test.cpp

#include <cmath>

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

extern "C" float public_function(float f)
{
    return private_function(f);
}

skompilowany z (g ++ 4.3.2, ld 2.18.93.20081009)

g++ -shared -o libtest.so test.cpp -s

i sprawdzanie symboli za pomocą

nm -DC libtest.so

daje

         w _Jv_RegisterClasses
0000047c T private_function(float)
000004ba W std::abs(float)
0000200c A __bss_start
         w __cxa_finalize
         w __gmon_start__
0000200c A _edata
00002014 A _end
00000508 T _fini
00000358 T _init
0000049b T public_function

oczywiście niewystarczające. Tak więc, ponownie określamy funkcję publiczną jako

extern "C" float __attribute__ ((visibility ("default"))) 
    public_function(float f)

i skompiluj z

g++ -shared -o libtest.so test.cpp -s -fvisibility=hidden

co daje

         w _Jv_RegisterClasses
0000047a W std::abs(float)
0000200c A __bss_start
         w __cxa_finalize
         w __gmon_start__
0000200c A _edata
00002014 A _end
000004c8 T _fini
00000320 T _init
0000045b T public_function

co jest dobre, z wyjątkiem tego, że std :: abs jest odsłonięty. Bardziej problematyczne jest, gdy zaczynamy linkowanie w innych (statycznych) bibliotekach poza naszą kontrolą,wszystkie symbole, których używamy z tych bibliotek, zostaną wyeksportowane. Ponadto, gdy zaczynamy używać kontenerów STL:

#include <vector>
struct private_struct
{
    float f;
};

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

kończymy na wielu dodatkowych eksportach z biblioteki C ++

00000b30 W __gnu_cxx::new_allocator<private_struct>::deallocate(private_struct*, unsigned int)
00000abe W __gnu_cxx::new_allocator<private_struct>::new_allocator()
00000a90 W __gnu_cxx::new_allocator<private_struct>::~new_allocator()
00000ac4 W std::allocator<private_struct>::allocator()
00000a96 W std::allocator<private_struct>::~allocator()
00000ad8 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_impl::_Vector_impl()
00000aaa W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_impl::~_Vector_impl()
00000b44 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_M_deallocate(private_struct*, unsigned int)
00000a68 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_M_get_Tp_allocator()
00000b08 W std::_Vector_base<private_struct, std::allocator<private_struct> >::_Vector_base()
00000b6e W std::_Vector_base<private_struct, std::allocator<private_struct> >::~_Vector_base()
00000b1c W std::vector<private_struct, std::allocator<private_struct> >::vector()
00000bb2 W std::vector<private_struct, std::allocator<private_struct> >::~vector()

NB: W przypadku optymalizacji należy upewnić się, że wektor jest rzeczywiście używany, aby kompilator nie optymalizował nieużywanych symboli.

Uważam, że mój kolega zdołał zbudować rozwiązanie ad hoc obejmujące pliki wersji i modyfikujące nagłówki STL (!), Które wydają się działać, ale chciałbym zapytać:

Czy istnieje czysty sposób, aby usunąć wszystkie niepotrzebne symbole (te z IE, które nie są częścią ujawnionej funkcjonalności biblioteki) z biblioteki współdzielonej linux? Próbowałem wiele opcji zarówno dla g ++, jak i ld z małym powodzeniem, więc wolę odpowiedzi, o których wiadomo, że działają, a nie wierzą.

W szczególności:

Symbole z bibliotek statycznych (z zamkniętym źródłem) nie są eksportowane.Symbole z biblioteki standardowej nie są eksportowane.Symbole niepubliczne z plików obiektowych nie są eksportowane.

Nasz eksportowany interfejs to C.

Jestem świadomy innych podobnych pytań dotyczących SO:

NIE współdzielenie wszystkich klas z biblioteką współdzielonąJak NAPRAWDĘ usunąć binarny plik w MacOGNU linker: alternatywa dla skryptu --version do wyświetlenia wyeksportowanych symboli w wierszu poleceń?

ale nie udało się uzyskać odpowiedzi.

questionAnswers(5)

yourAnswerToTheQuestion