Usuwanie bibliotek współdzielonych Linuksa

Ostatnio zostaliśmy poproszeni o dostarczenie Linuksowej wersji jednej z naszych bibliotek, wcześniej rozwijanej pod Linuksem i dostarczonej dla systemu Windows, gdzie wdrażanie bibliotek jest ogólnie dużo łatwiejsze. Problem, który napotkaliśmy, polega na tym, że eksportowane symbole ograniczają się tylko do tych znajdujących się w odsłoniętym interfejsie. Istnieją trzy powody, dla których warto to zrobić

  • aby chronić zastrzeżone aspekty naszej technologii przed ekspozycją poprzez eksportowane symbole.
  • To zapobiegaj problemom użytkowników ze sprzecznymi nazwami symboli.
  • aby przyspieszyć ładowanie biblioteki (przynajmniej tak mi powiedziano).

Biorąc 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 nieodpowiednie. Następnie ponownie deklarujemy funkcję publiczną jako

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

I kompilować z

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

Które 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, poza tym, że std::abs jest odsłonięty. Bardziej problematyczne jest to, gdy zaczynamy linkować w innych (statycznych) bibliotekach poza naszą kontrolą, wszystkie symbole, których używamy z tych bibliotek, są eksportowane . Dodatkowo, kiedy zaczynamy używać kontenerów STL:

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

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

Kończymy z wieloma dodatkowymi eksportami 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: przy włączonej optymalizacji musisz upewnić się, że wektor jest rzeczywiście używany, aby kompilator nie optymalizował nieużywane symbole Na Zewnątrz.

Wydaje mi się, że mojemu koledze udało się skonstruować rozwiązanie ad-hoc obejmujące pliki wersji i modyfikację nagłówków STL (!) to chyba działa, ale chciałbym zapytać:

Czy istnieje czysty sposób na usunięcie wszystkich niepotrzebnych symboli (tj. tych, które nie są częścią funkcji exposed library) z biblioteki współdzielonej Linuksa? próbowałem dość wiele opcji zarówno do G++ i ld z małym sukcesem, więc wolałbym odpowiedzi, które są znane do pracy raczej niż sądzono.

W szczególności:

  • symbole z bibliotek statycznych (closed-source) nie są eksportowane.
  • symbole z biblioteki standardowej nie są eksportowane.
  • Niepubliczne symbole z plików obiektowych nie są eksportowane.

Nasz eksportowany interfejs To C.

Znam inne podobne pytania na SO:

Ale nie odniosły sukcesu z odpowiedziami.

Author: Community, 2010-01-18

5 answers

Więc rozwiązanie, które mamy na razie jest następujące:

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);
}

Eksport.wersja

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

Skompilowane z

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

Daje

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
Co jest zbliżone do tego, czego szukamy. Jest jednak kilka gotchas:
  • musimy się upewnić, że nie używamy prefiksu "exported" (w tym prostym przykładzie "public", ale oczywiście czegoś bardziej użytecznego w naszym przypadku) w kodzie wewnętrznym.
  • wiele nazw symboli wciąż kończy się w łańcuchu tabela, która wydaje się być do RTTI, - fno-rtti sprawia, że znikają w moich prostych testach, ale jest raczej nuklearnym rozwiązaniem.

Z radością przyjmuję wszelkie lepsze rozwiązania, które każdy wymyśli!

 6
Author: Adam Bowen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-19 09:33:13

Twoje użycie domyślnego atrybutu visibility i -fvisibility = hidden powinno zostać rozszerzone o-fvisibility-inlines-hidden.

Powinieneś także zapomnieć o próbie ukrycia eksportu stdlib, zobacz ten błąd GCC Dlaczego.

Jeśli masz wszystkie publiczne symbole w określonych nagłówkach, możesz je zawinąć w #pragma GCC visibility push(default) i #pragma GCC visibility pop zamiast używać atrybutów. Jeśli jednak tworzysz międzyplatformową bibliotekę, spójrz na kontrolowanie eksportowanych symboli współdzielonych Biblioteki dla techniki unifikacji strategii eksportu DSO systemu Windows i Linux.

 5
Author: joshperry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-18 21:05:48

Dla przypomnienia, że Ulrich Drepper napisał esej dotyczący (all?) aspekty pisania bibliotek współdzielonych dla Linuksa/Uniksa, które obejmują kontrolę eksportowanych symboli wśród wielu innych tematów.

Było to bardzo przydatne w wyjaśnieniu, jak eksportować tylko funkcje na białej liście ze współdzielonej biblioteki lib.

 5
Author: grrussel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-10 15:46:49

Jeśli zamkniesz swoją prywatną część w anonimowej przestrzeni nazw, to ani std::abs, ani private_function nie będą widoczne w tabeli symboli:

namespace{
#include<cmath>
  float private_function(float f)
  {
    return std::abs(f);
  }
}
extern "C" float public_function(float f)
{
        return private_function(f);
}

Kompilowanie (g++ 4.3.3):

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

: {]}

# 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
 4
Author: catwalk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-19 09:49:35

Ogólnie rzecz biorąc, w wielu systemach Linux i Unix, odpowiedź tutaj jest taka, że nie ma odpowiedzi tutaj w czasie połączenia. to dość fundamentalne dla tego, jak ld.so działa.

Prowadzi to do pracochłonnych alternatyw. Na przykład zmieniamy nazwę STL na live w _STL zamiast std, aby uniknąć konfliktów o STL, i używamy przestrzeni nazw high, low I in-between, aby trzymać nasze symbole z dala od możliwych konfliktów z symbolami innych ludzi.

Oto rozwiązanie, którego nie miłość:

  1. utwórz małe. więc tylko z narażonym API to.
  2. niech otworzy rzeczywistą implementację za pomocą dlopen i połączy się z dlsym.

Dopóki nie używasz RTLD_GLOBAL, masz teraz pełną izolację, jeśli nie szczególną tajemnicę .. - Bsymbolic też może być pożądane.

 2
Author: bmargulies,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-18 19:34:45