Vinculación con versiones anteriores de símbolos en un archivo.so
linux gcc (9)
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.
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 compilació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 compilació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.
Encontré la siguiente solución de trabajo. Primero crea el archivo memcpy.c:
#include <string.h>
/* 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 compilar este archivo. Luego, vincula tu programa con -Wl, - wrap = memcpy .
Esta solución parece no ser compatible con la opción de compilació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 <string.h>
#ifdef __cplusplus
extern "C" {
#endif
void *__wrap_memcpy(void *dest, const void *src, size_t n)
{
return memmove(dest, src, n);
}
#ifdef __cplusplus
}
#endif
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:
- agregado a mis banderas gcc: -include glibc_version_nightmare.h
- creado el glibc_version_nightmare.h
- 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 <features.h> /* 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 (<SYMS>) {
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;
Puede ser causado por una versión anterior de ld (gnu link). Para seguir un problema simple:
#include <string.h>
#include <stdio.h>
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 bloqueo. Después de actualizar a 2.25, es: memset @ plt y resuelto.
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 compilación.
Sugiero que o bien vincules memcpy () estáticamente; o encuentre la fuente de memcpy () y compílela como su propia biblioteca.
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 <string.h>
asm (".symver old_memcpy, memcpy@GLIBC_2.2.5"); // hook old_memcpy as [email protected]
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.
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 <stddef.h>
#include <string.h>
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 programa, agregue
-
-Wl,--version-script memcpy_wrap.map
-
memcpy_wrap.o
para que termines con algo como:
g++ <some flags> -Wl,--version-script memcpy_wrap.map <some .o files> memcpy_wrap.o <some libs>