Utilisation de gcc et ld sur x86_64 linux Je dois créer un lien avec une version plus récente d’une bibliothèque (glibc 2.14), mais l’exécutable doit s’exécuter sur un système doté d’une version plus ancienne (2.5). Puisque le seul symbole incompatible est memcpy (nécessitant memcpy@GLIBC_2.2.5 mais la bibliothèque fournissant memcpy@GLIBC_2.14), je voudrais dire à l'éditeur de liens qu'au lieu de prendre la version par défaut pour memcpy, elle devrait prendre une ancienne version que je spécifie. .
J'ai trouvé un moyen assez arkward de le faire: spécifiez simplement une copie de l'ancien fichier .so sur la ligne de commande de l'éditeur de liens. Cela fonctionne bien, mais je n'aime pas l'idée de disposer de plusieurs fichiers .so (je ne pouvais que le faire fonctionner en spécifiant toutes les anciennes bibliothèques vers lesquelles j'ai établi un lien qui ont également des références à memcpy) vérifiées dans le svn et nécessaires à mon système de construction. .
Je cherche donc un moyen de dire à l'éditeur de liens de prendre l'ancien symbole versionné.
Les alternatives qui ne fonctionnent pas (bien) pour moi sont:
Quand on pense à tous les emplois d'un éditeur de liens, cela ne semble pas être une tâche difficile à améliorer, après tout, il dispose d'un code pour déterminer la version par défaut d'un symbole.
Toutes les autres idées qui ont le même niveau de complexité qu'une simple ligne de commande de l'éditeur de liens (comme créer un simple script de l'éditeur de liens, etc.) sont également les bienvenues, tant qu'elles ne sont pas des hacks bizarres telles que l'édition du fichier binaire résultant ...
edit: Pour conserver cela pour les futurs lecteurs, en plus des idées ci-dessous, j’ai trouvé l’option --wrap
dans l’éditeur de liens, ce qui peut parfois être utile.
Liez simplement memcpy statiquement - extrayez memcpy.o de libc.a ar x /path/to/libc.a memcpy.o
(quelle que soit la version - memcpy est quasiment une fonction autonome) et incluez-la dans votre dernier lien. Notez que les liens statiques peuvent compliquer les problèmes de licence si votre projet est distribué au public et non open-source.
Alternativement, vous pouvez simplement implémenter vous-même memcpy, bien que la version Assembly ajustée à la main dans glibc soit probablement plus efficace.
Notez que memcpy@GLIBC_2.2.5 est associé à memmove (les anciennes versions de memcpy étaient systématiquement copiées dans un sens prévisible, ce qui entraînait parfois son utilisation abusive alors que memmove aurait dû être utilisé), ce qui est la seule raison de la version bump - vous pouvez simplement remplacer memcpy par memmove dans votre code pour ce cas spécifique.
Vous pouvez également passer à la liaison statique ou vous assurer que tous les systèmes de votre réseau ont la même version ou une version supérieure à celle de votre machine de génération.
J'ai trouvé la solution de travail suivante. Commencez par créer le fichier 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);
}
Aucun CFLAGS supplémentaire nécessaire pour compiler ce fichier. Ensuite, liez votre programme avec -Wl, - wrap = memcpy .
J'ai eu un problème similaire. Une bibliothèque tierce que nous utilisons a besoin de l’ancien memcpy@GLIBC_2.2.5
. Ma solution est une approche étendue @anight publiée.
Je modifie également la commande memcpy
, mais je devais utiliser une approche légèrement différente, car la solution publiée par @anight ne fonctionnait pas.
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;
};
Construisez le wrapper:
gcc -c memcpy_wrap.c -o memcpy_wrap.o
Maintenant, enfin, lorsque vous liez le programme, ajoutez
-Wl,--version-script memcpy_wrap.map
memcpy_wrap.o
de sorte que vous allez vous retrouver avec quelque chose comme:
g++ <some flags> -Wl,--version-script memcpy_wrap.map <some .o files> memcpy_wrap.o <some libs>
J'avais un problème similaire. En essayant d'installer des composants Oracle sur RHEL 7.1, j'ai eu ceci:
$ gcc -o /some/Oracle/bin/foo .... -L/some/Oracle/lib ...
/some/Oracle/lib/libfoo.so: undefined reference to `memcpy@GLIBC_2.14'
Il semble que la glibc de (ma) RHEL ne définisse que 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
J'ai donc réussi à contourner ce problème en créant d'abord un fichier memcpy.c sans le wrapper, comme suit:
#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);
}
et un fichier memcpy.map qui exporte notre memcpy en tant que memcpy@GLIBC_2.14:
GLIBC_2.14 {
memcpy;
};
J'ai ensuite compilé mon propre memcpy.c dans une bibliothèque partagée comme celle-ci:
$ gcc -shared -fPIC -c memcpy.c
$ gcc -shared -fPIC -Wl,--version-script memcpy.map -o libmemcpy-2.14.so memcpy.o -lc
, déplacé libmemcpy-2.14.so dans/some/Oracle/lib (pointé par -L arguments dans ma liaison), et lié à nouveau par
$ gcc -o /some/Oracle/bin/foo .... -L/some/Oracle/lib ... /some/Oracle/lib/libmemcpy-2.14.so -lfoo ...
(compilé sans erreur) et vérifié par:
$ 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)
Cela a fonctionné pour moi. J'espère que ça le fait pour vous aussi.
Je suis clairement en train de réagir tard mais j’ai récemment mis à niveau (plus de raisons de ne jamais effectuer de mise à niveau) mon système d’exploitation Linux vers XUbuntu 14.04, fourni avec la nouvelle libc. Je compile sur ma machine une bibliothèque partagée utilisée par les clients qui, pour des raisons légitimes, n'ont pas mis à niveau leur environnement depuis la version 10.04. La bibliothèque partagée que j'ai compilée n'est plus chargée dans leur environnement car gcc met une dépendance sur memcpy glibc v. 2.14 (ou supérieure). Laissons de côté la folie de cela. La solution de contournement dans tout mon projet était triple:
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
Fragment 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;
Cette solution de contournement ne semble pas compatible avec l'option de compilation -flto.
Ma solution appelle memmove. memove effectue exactement les mêmes tâches que memcpy . La seule différence réside dans le fait que les zones src et dest se chevauchent, que memmove est sécurisé et que memcpy est imprévisible. Nous pouvons donc toujours appeler memmove en toute sécurité à la place de 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
Je pense que vous pouvez vous en sortir en créant un simple fichier C contenant l’instruction symver et peut-être une fonction factice appelant memcpy. Ensuite, vous devez simplement vous assurer que le fichier objet résultant est le premier fichier donné à l'éditeur de liens.
Je vous suggère de lier statcpy () statiquement; ou trouvez la source de memcpy () et compilez-la en tant que votre propre bibliothèque.
Cela peut être causé par l'ancienne version de ld (gnu link) . Pour résoudre le problème simple suivant:
#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;
}
Lorsque j'utilise ld 2.19.1, memset est déplacé vers: memset @@ GLIBC_2.0 et provoque un crash . Après la mise à niveau vers la version 2.25, il s'agit de: memset @ plt, et le problème est résolu.
Exemple minimal exécutable autonome
principal c
#include <assert.h>
#include <stdlib.h>
#include "a.h"
#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;
}
a.c
#include "a.h"
__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;
}
a.h
#ifndef A_H
#define A_H
int a(void);
#endif
une carte
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
a.o: a.c
$(CC) -fPIC -c '$<' -o '$@'
libcirosantilli_a.so: a.o
$(CC) -Wl,--version-script,a.map -L'.' -shared a.o -o '$@'
libcirosantilli_a.o: a.c
$(CC) -fPIC -c '$<' -o '$@'
clean:
rm -rf *.o *.a *.so *.out
Testé sur Ubuntu 16.04.