Recientemente se nos ha pedido que enviemos una versión de Linux de una de nuestras bibliotecas, anteriormente desarrollábamos bajo Linux y se enviaban para Windows, donde el despliegue de bibliotecas generalmente es mucho más fácil. El problema al que nos hemos referido es al quitar los símbolos exportados a solo aquellos en la interfaz expuesta. Hay tres buenas razones para querer hacer estoStripping linux shared libraries
- Para proteger a los aspectos de propiedad de nuestra tecnología de la exposición a través de los símbolos exportados.
- Para evitar que los usuarios tengan problemas con los nombres de símbolos conflictivos.
- Para acelerar la carga de la biblioteca (al menos eso me dicen).
Tomando un ejemplo sencillo, entonces:
test.cpp
#include <cmath>
float private_function(float f)
{
return std::abs(f);
}
extern "C" float public_function(float f)
{
return private_function(f);
}
compilado con (g ++ 4.3.2, ld 2.18.93.20081009)
g++ -shared -o libtest.so test.cpp -s
y la inspección de los símbolos con
nm -DC libtest.so
da
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
obviamente inadecuada. Así que la próxima nos redeclare la función pública como
extern "C" float __attribute__ ((visibility ("default")))
public_function(float f)
y compilar con
g++ -shared -o libtest.so test.cpp -s -fvisibility=hidden
que da
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
lo cual es bueno, excepto que std :: abs está expuesto. Más problemático es cuando comenzamos a vincular en otras bibliotecas (estáticas) fuera de nuestro control, todos los símbolos que utilizamos de esas bibliotecas se exportan. Además, al empezar a utilizar contenedores STL:
#include <vector>
struct private_struct
{
float f;
};
void other_private_function()
{
std::vector<private_struct> v;
}
nos encontramos con muchas exportaciones adicionales de la biblioteca de 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: Con optimizaciones en que necesita para asegurarse de que el vector es realmente utilizado para que el compilador no optimice los símbolos no utilizados.
Creo que mi colega ha logrado construir una solución ad-hoc que implica archivos de la versión y modificar las cabeceras de STL que parece funcionar, pero me gustaría preguntar (!):
¿Hay una manera limpia para quitar todos los símbolos innecesarios (IE que no son parte de la funcionalidad de la biblioteca expuesta) de una biblioteca compartida de Linux? He intentado bastantes opciones tanto para g ++ como para ld con poco éxito, así que preferiría respuestas que se sabe que funcionan en lugar de creer.
En particular:
- Símbolos de la (de código cerrado) bibliotecas estáticas no se exportan.
- Los símbolos de la biblioteca estándar no se exportan.
- Los símbolos no públicos de los archivos objeto no se exportan.
Nuestra interfaz es exportada C.
Soy consciente de las otras preguntas similares sobre SO:
- NOT sharing all classes with shared library
- How to REALLY strip a binary in MacOs
- GNU linker: alternative to --version-script to list exported symbols at the command line?
pero h Ave tuvo poco éxito con las respuestas.
En enlace estático de bibliotecas de sistema: es ilegal que lo haga. Es decir, dado que [(e)] (http://www.eglibc.org/) [GLIBC] (http://www.gnu.org/software/libc/) tiene licencia bajo [LGPL] (http://opensource.org/licenses/LGPL-3.0) y dado que esa licencia se aplica a todos los códigos que la usan, excepto si están vinculados dinámicamente, al vincular estáticamente usted hace que su código esté cubierto por LGPL y se les exige que proporcionen las fuentes (a cualquier persona a quien le haya proporcionado información binaria piden fuentes). Esto no se aplica a libgcc y libstdC++, que específicamente no se aplican a ningún código que use solo API pública, sin importar cuán vinculado esté. –
Soy consciente de esto, y no me refiero a los símbolos de glibc, todos los símbolos anteriores se generan mediante instanciación de plantilla de la biblioteca estándar de C++ y, por necesidad, se generan en mis archivos de objeto (ya que las instancias de plantilla pueden ' estar en la biblioteca!). –