Vinculación con versiones anteriores de símbolos en un archivo .so

Usando gcc y ld en x86_64 linux necesito vincularme con una versión más nueva de una biblioteca (glibc 2.14) pero el ejecutable necesita ejecutarse en un sistema con una versión anterior (2.5). Dado que el único símbolo incompatible es memcpy (que necesita memcpy@GLIBC_2.2.5 pero la biblioteca proporciona memcpy@GLIBC_2.14), me gustaría decirle al vinculador que, en lugar de tomar la versión predeterminada para memcpy, debería tomar una versión anterior que especifico .

Encontré una manera bastante ardua de hacerlo: simplemente especifique una copia del viejo archivo .so en la línea de comando del enlazador. Esto funciona bien, pero no me gusta la idea de tener múltiples archivos .so (solo pude hacer que funcione especificando todas las bibliotecas antiguas a las que enlazo que también tienen referencias a memcpy) registradas en el svn y necesarias para mi sistema de comstackción .

Así que estoy buscando una forma de decirle al enlazador que tome el símbolo versionado anterior.

Las alternativas que no funcionan (bien) para mí son:

  • Usar asm .symver (como se ve en el Archivo web del blog de Trevor Pounds ) ya que esto me requeriría asegurarme de que el symver está delante de todo el código que está usando memcpy, lo cual sería muy difícil (código base complejo con código de terceros)
  • Mantener un entorno de comstackción con las bibliotecas antiguas; simplemente porque quiero desarrollar en mi sistema de escritorio y sería una pita para sincronizar cosas en nuestra red.

Al pensar en todos los trabajos que hace un enlazador, no parece una tarea difícil de imponer, después de todo tiene algún código para descubrir la versión predeterminada de un símbolo también.

También son bienvenidas otras ideas que estén en el mismo nivel de complejidad que una simple línea de comando del enlazador (como crear un guión de enlazador simple, etc.), siempre que no sean extrañas, como editar el binario resultante …

editar: Para conservar esto para los futuros lectores, además de las ideas a continuación, encontré la opción: --wrap al enlazador, que a veces también puede ser útil.

Simplemente enlace memcpy estáticamente – saque memcpy.o de libc.a ar x /path/to/libc.a memcpy.o (cualquier versión – memcpy es una función bastante independiente) e inclúyala en su enlace final. Tenga en cuenta que la vinculación estática puede complicar los problemas de licencia si su proyecto se distribuye al público y no de código abierto.

Alternativamente, simplemente puede implementar memcpy usted mismo, aunque la versión de ensamblaje sintonizado a mano en glibc es probable que sea más eficiente

Tenga en cuenta que memcpy@GLIBC_2.2.5 está correlacionado con memmove (las versiones antiguas de memcpy se copiaron constantemente en una dirección predecible, lo que ocasionó que se utilizara incorrectamente cuando se debería haber utilizado memmove), y esta es la única razón para el bache de versión: simplemente podría reemplazar memcpy con memmove en su código para este caso específico.

O puede ir a enlaces estáticos, o puede asegurarse de que todos los sistemas en su red tengan la misma o mejor versión que su máquina de comstackción.

Encontré la siguiente solución de trabajo. Primero crea el archivo memcpy.c:

 #include  /* some systems do not have newest memcpy@@GLIBC_2.14 - stay with old good one */ asm (".symver memcpy, memcpy@GLIBC_2.2.5"); void *__wrap_memcpy(void *dest, const void *src, size_t n) { return memcpy(dest, src, n); } 

No se necesitan CFLAGS adicionales para comstackr este archivo. Luego, vincula tu progtwig con -Wl, – wrap = memcpy .

Tuve un problema similar. Una biblioteca de terceros que utilizamos necesita la antigua memcpy@GLIBC_2.2.5 . Mi solución es un enfoque extendido @anight publicado.

También memcpy comando memcpy , pero tuve que usar un enfoque ligeramente diferente, ya que la solución @anight publicada no funcionó para mí.

memcpy_wrap.c:

 #include  #include  asm (".symver wrap_memcpy, memcpy@GLIBC_2.2.5"); void *wrap_memcpy(void *dest, const void *src, size_t n) { return memcpy(dest, src, n); } 

memcpy_wrap.map:

 GLIBC_2.2.5 { memcpy; }; 

Construye la envoltura

 gcc -c memcpy_wrap.c -o memcpy_wrap.o 

Ahora, finalmente, al vincular el progtwig, agregue

  • -Wl,--version-script memcpy_wrap.map
  • memcpy_wrap.o

para que termines con algo como:

 g++  -Wl,--version-script memcpy_wrap.map  memcpy_wrap.o  

Tuve un problema similar. Al intentar instalar algunos componentes Oracle en RHEL 7.1, obtuve esto:

 $ gcc -o /some/oracle/bin/foo .... -L/some/oracle/lib ... /some/oracle/lib/libfoo.so: undefined reference to `memcpy@GLIBC_2.14' 

Parece que (mi) glibc de RHEL solo define memcpy@GLIBC_2.2.5:

 $ readelf -Ws /usr/lib/x86_64-redhat-linux6E/lib64/libc_real.so | fgrep memcpy@ 367: 000000000001bfe0 16 FUNC GLOBAL DEFAULT 8 memcpy@@GLIBC_2.2.5 1166: 0000000000019250 16 FUNC WEAK DEFAULT 8 wmemcpy@@GLIBC_2.2.5 

Por lo tanto, me las arreglé para evitar esto, al crear primero un archivo memcpy.c sin envoltura, de la siguiente manera:

 #include  asm (".symver old_memcpy, memcpy@GLIBC_2.2.5"); // hook old_memcpy as memcpy@2.2.5 void *old_memcpy(void *, const void *, size_t ); void *memcpy(void *dest, const void *src, size_t n) // then export memcpy { return old_memcpy(dest, src, n); } 

y un archivo memcpy.map que exporta nuestra memcpy como memcpy@GLIBC_2.14:

 GLIBC_2.14 { memcpy; }; 

Luego compilé mi propio memcpy.c en una biblioteca compartida como esta:

 $ gcc -shared -fPIC -c memcpy.c $ gcc -shared -fPIC -Wl,--version-script memcpy.map -o libmemcpy-2.14.so memcpy.o -lc 

, moví libmemcpy-2.14.so dentro de / some / oracle / lib (señalado por -L argumentos en mi enlace) y volví a vincular

 $ gcc -o /some/oracle/bin/foo .... -L/some/oracle/lib ... /some/oracle/lib/libmemcpy-2.14.so -lfoo ... 

(que compiló sin errores) y verificado por:

 $ ldd /some/oracle/bin/foo linux-vdso.so.1 => (0x00007fff9f3fe000) /some/oracle/lib/libmemcpy-2.14.so (0x00007f963a63e000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f963a428000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f963a20c000) librt.so.1 => /lib64/librt.so.1 (0x00007f963a003000) libc.so.6 => /lib64/libc.so.6 (0x00007f9639c42000) /lib64/ld-linux-x86-64.so.2 (0x00007f963aa5b000) 

Esto funcionó para mí. Espero que lo haga por ti también.

Obviamente estoy un poco tarde respondiendo a esto, pero recientemente actualicé (más razones para nunca actualizar) mi sistema operativo Linux a XUbuntu 14.04, que viene con la nueva libc. Recopilo una biblioteca compartida en mi máquina que es utilizada por clientes que, por cualquier razón legítima, no han actualizado su entorno desde 10.04. La biblioteca compartida que compilé ya no se carga en su entorno porque gcc puso una dependencia en memcpy glibc v. 2.14 (o superior). Dejemos de lado la locura de esto. La solución en todo mi proyecto fue triple:

  1. agregado a mis banderas gcc: -include glibc_version_nightmare.h
  2. creado el glibc_version_nightmare.h
  3. creó una secuencia de comandos perl para verificar los símbolos en .so

glibc_version_nightmare.h:

 #if defined(__GNUC__) && defined(__LP64__) /* only under 64 bit gcc */ #include  /* for glibc version */ #if defined(__GLIBC__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 14) /* force mempcy to be from earlier compatible system */ __asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); #endif #undef _FEATURES_H /* so gets reloaded if necessary */ #endif 

fragmento de script perl:

 ... open SYMS, "nm $flags $libname |"; my $status = 0; sub complain { my ($symbol, $verstr) = @_; print STDERR "ERROR: $libname $symbol requires $verstr\n"; $status = 1; } while () { next unless /\@\@GLIBC/; chomp; my ($symbol, $verstr) = (m/^\s+.\s(.*)\@\@GLIBC_(.*)/); die "unable to parse version from $libname in $_\n" unless $verstr; my @ver = split(/\./, $verstr); complain $symbol, $verstr if ($ver[0] > 2 || $ver[1] > 10); } close SYMS; exit $status; 

Creo que se puede salir con la tarea de crear un archivo C simple que contenga la sentencia symver y quizás una función ficticia que llame a memcpy. Luego solo tiene que asegurarse de que el archivo de objeto resultante sea el primer archivo que se le haya dado al vinculador.

Sugiero que o bien vincules memcpy () estáticamente; o encuentre la fuente de memcpy () y compílela como su propia biblioteca.

Puede ser causado por una versión anterior de ld (gnu link). Para seguir un problema simple:

 #include  #include  int main(int argc,char **argv) { char buf[5]; memset(buf,0,sizeof(buf)); printf("ok\n"); return 0; } 

Cuando uso ld 2.19.1, memset se reubica en: memset @@ GLIBC_2.0 y causa locking. Después de actualizar a 2.25, es: memset @ plt y resuelto.

Esta solución parece no ser compatible con la opción de comstackción -flto.

Mi solución es llamar a Memmove. memove hace exactamente los mismos trabajos que memcpy. La única diferencia es cuando src y dest zona se superponen, memmove es seguro y memcpy es impredecible. Por lo tanto, siempre podemos llamar memmove de forma segura memcpy

 #include  #ifdef __cplusplus extern "C" { #endif void *__wrap_memcpy(void *dest, const void *src, size_t n) { return memmove(dest, src, n); } #ifdef __cplusplus } #endif 

Ejemplo autónomo ejecutable mínimo

GitHub aguas arriba .

C Principal

 #include  #include  #include "ah" #if defined(V1) __asm__(".symver a,a@LIBA_1"); #elif defined(V2) __asm__(".symver a,a@LIBA_2"); #endif int main(void) { #if defined(V1) assert(a() == 1); #else assert(a() == 2); #endif return EXIT_SUCCESS; } 

C.A

 #include "ah" __asm__(".symver a1,a@LIBA_1"); int a1(void) { return 1; } /* @@ means "default version". */ __asm__(".symver a2,a@@LIBA_2"); int a2(void) { return 2; } 

ah

 #ifndef A_H #define A_H int a(void); #endif 

un mapa

 LIBA_1{ global: a; local: *; }; LIBA_2{ global: a; local: *; }; 

Makefile

 CC := gcc -pedantic-errors -std=c89 -Wall -Wextra .PHONY: all clean run all: main.out main1.out main2.out run: all LD_LIBRARY_PATH=. ./main.out LD_LIBRARY_PATH=. ./main1.out LD_LIBRARY_PATH=. ./main2.out main.out: main.c libcirosantilli_a.so $(CC) -L'.' main.c -o '$@' -lcirosantilli_a main1.out: main.c libcirosantilli_a.so $(CC) -DV1 -L'.' main.c -o '$@' -lcirosantilli_a main2.out: main.c libcirosantilli_a.so $(CC) -DV2 -L'.' main.c -o '$@' -lcirosantilli_a ao: ac $(CC) -fPIC -c '$<' -o '$@' libcirosantilli_a.so: ao $(CC) -Wl,--version-script,a.map -L'.' -shared ao -o '$@' libcirosantilli_a.o: ac $(CC) -fPIC -c '$<' -o '$@' clean: rm -rf *.o *.a *.so *.out 

Probado en Ubuntu 16.04.