Bei Verwendung von gcc und ld unter x86_64 linux muss eine Verknüpfung mit einer neueren Version einer Bibliothek (glibc 2.14) hergestellt werden, die ausführbare Datei muss jedoch auf einem System mit einer älteren Version (2.5) ausgeführt werden. Da das einzige inkompatible Symbol memcpy ist (es ist [email protected]_2.2.5 erforderlich, aber die Bibliothek [email protected]_2.14), möchte ich dem Linker mitteilen, dass anstelle der Standardversion für memcpy eine alte Version verwendet werden sollte, die ich angegeben habe .
Ich habe einen ziemlich schwierigen Weg gefunden, es zu tun: Geben Sie einfach eine Kopie der alten .so-Datei in der Linker-Befehlszeile an. Das funktioniert gut, aber ich mag nicht die Idee, mehrere .so-Dateien haben zu lassen (ich könnte es nur schaffen, indem ich alle alten Bibliotheken angibt, auf die ich verweise, die auch Verweise auf Memcpy enthalten), die in das SVN eingecheckt werden und von meinem Build-System benötigt werden .
Ich suche also nach einer Möglichkeit, dem Linker das alte versionierte Symbol zu geben.
Alternativen, die (gut) für mich nicht funktionieren, sind:
Wenn Sie über alle Jobs nachdenken, die ein Linker ausführt, scheint es nicht schwer zu implementieren, schließlich hat er etwas Code, um auch die Standardversion eines Symbols herauszufinden.
Alle anderen Ideen, die sich auf derselben Komplexität wie eine einfache Linker-Befehlszeile befinden (z. B. das Erstellen eines einfachen Linker-Skripts usw.), sind ebenfalls willkommen, sofern es sich nicht um seltsame Hacks wie das Bearbeiten der resultierenden Binärdatei handelt.
edit: Um dies für die zukünftigen Leser zu erhalten, habe ich zusätzlich zu den folgenden Ideen die Option --wrap
für den Linker gefunden, die manchmal auch nützlich sein kann.
Verknüpfen Sie einfach memcpy statisch - ziehen Sie memcpy.o aus libc.a ar x /path/to/libc.a memcpy.o
(egal welche Version - memcpy ist eigentlich eine eigenständige Funktion) und fügen Sie es in Ihren endgültigen Link ein. Beachten Sie, dass die statische Verknüpfung Lizenzprobleme verursachen kann, wenn Ihr Projekt an die Öffentlichkeit und nicht an Open Source verteilt wird.
Alternativ können Sie memcpy einfach selbst implementieren, obwohl die von Hand abgestimmte Assembly-Version in glibc wahrscheinlich effizienter ist
Beachten Sie, dass [email protected]_2.2.5 auf memmove abgebildet ist (alte Versionen von memcpy wurden regelmäßig in eine vorhersagbare Richtung kopiert, was dazu führte, dass sie manchmal missbraucht wurde, wenn memmove verwendet werden sollte) version bump - Sie können memcpy für diesen speziellen Fall einfach durch memmove im Code ersetzen.
Oder Sie gehen zu einer statischen Verknüpfung, oder Sie können sicherstellen, dass alle Systeme in Ihrem Netzwerk dieselbe oder eine bessere Version haben als Ihr Build-Computer.
Ich habe die folgende funktionierende Lösung gefunden. Erstellen Sie zuerst die Datei memcpy.c:
#include <string.h>
/* some systems do not have newest [email protected]@GLIBC_2.14 - stay with old good one */
asm (".symver memcpy, [email protected]_2.2.5");
void *__wrap_memcpy(void *dest, const void *src, size_t n)
{
return memcpy(dest, src, n);
}
Zum Kompilieren dieser Datei waren keine zusätzlichen CFLAGS erforderlich. Dann verknüpfen Sie Ihr Programm mit -Wl, - wrap = memcpy .
Ich hatte ein ähnliches Problem. Eine von uns verwendete Drittanbieter-Bibliothek benötigt den alten [email protected]_2.2.5
. Meine Lösung ist ein erweiterter Ansatz von @anight posted.
Ich warp auch den memcpy
Befehl, aber ich musste einen etwas anderen Ansatz verwenden, da die @anight-Lösung für mich nicht funktionierte.
memcpy_wrap.c:
#include <stddef.h>
#include <string.h>
asm (".symver wrap_memcpy, [email protected]_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;
};
Bauen Sie den Wrapper auf:
gcc -c memcpy_wrap.c -o memcpy_wrap.o
Nun endlich beim Verlinken das Programm hinzufügen
-Wl,--version-script memcpy_wrap.map
memcpy_wrap.o
so dass Sie mit etwas wie enden:
g++ <some flags> -Wl,--version-script memcpy_wrap.map <some .o files> memcpy_wrap.o <some libs>
Ich hatte ein ähnliches Problem. Beim Versuch, einige Oracle-Komponenten auf RHEL 7.1 zu installieren, habe ich Folgendes erhalten:
$ gcc -o /some/Oracle/bin/foo .... -L/some/Oracle/lib ...
/some/Oracle/lib/libfoo.so: undefined reference to `[email protected]_2.14'
Es scheint, dass (mein) RHELs glibc nur [email protected]_2.2.5 definiert:
$ readelf -Ws /usr/lib/x86_64-redhat-linux6E/lib64/libc_real.so | fgrep [email protected]
367: 000000000001bfe0 16 FUNC GLOBAL DEFAULT 8 [email protected]@GLIBC_2.2.5
1166: 0000000000019250 16 FUNC WEAK DEFAULT 8 [email protected]@GLIBC_2.2.5
So gelang es mir, dies zu umgehen, indem ich zunächst eine memcpy.c-Datei ohne Umbruch wie folgt erstellte:
#include <string.h>
asm (".symver old_memcpy, [email protected]_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);
}
und eine memcpy.map-Datei, die unser memcpy als [email protected]_2.14 exportiert:
GLIBC_2.14 {
memcpy;
};
Ich kompilierte dann mein eigenes memcpy.c in eine freigegebene Bibliothek wie folgt:
$ gcc -shared -fPIC -c memcpy.c
$ gcc -shared -fPIC -Wl,--version-script memcpy.map -o libmemcpy-2.14.so memcpy.o -lc
, libmemcpy-2.14.so in/some/Oracle/lib verschoben (von -L-Argumenten in meiner Verknüpfung gezeigt) und erneut durch verknüpft
$ gcc -o /some/Oracle/bin/foo .... -L/some/Oracle/lib ... /some/Oracle/lib/libmemcpy-2.14.so -lfoo ...
(welche ohne Fehler kompiliert wurde) und verifiziert durch:
$ 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)
Das hat bei mir funktioniert. Ich hoffe es tut es auch für dich.
Ich bin offensichtlich etwas spät dran, aber ich habe kürzlich mein Linux-Betriebssystem auf XUbuntu 14.04 aufgerüstet (mehr Gründe, um nie ein Upgrade durchzuführen), das mit der neuen libc geliefert wurde. Ich kompiliere auf meinem Computer eine gemeinsam genutzte Bibliothek, die von Clients verwendet wird, die aus irgendeinem legitimen Grund keine Aktualisierung ihrer Umgebung von 10.04 vorgenommen haben. Die gemeinsam genutzte Bibliothek, die ich kompiliert habe, wurde nicht mehr in ihre Umgebung geladen, da gcc eine Abhängigkeit von memcpy glibc v. 2.14 (oder höher) eingab. Lassen wir den Wahnsinn davon beiseite. Der Workaround für mein gesamtes Projekt war dreifach:
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,[email protected]_2.2.5");
#endif
#undef _FEATURES_H /* so gets reloaded if necessary */
#endif
Perl-Skriptfragment:
...
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;
Diese Problemumgehung scheint mit der Kompilierungsoption -flto nicht kompatibel zu sein.
Meine Lösung ruft memmove auf. memove führt genau die gleichen Jobs aus wie memcpy ..__ Der einzige Unterschied besteht darin, dass sich src und dest-Zone überschneiden, memmove sicher ist und memcpy unvorhersehbar ist. So können wir memmove sicher immer statt memcpy aufrufen
#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
Ich denke, Sie können eine einfache C-Datei erstellen, die die symver-Anweisung und möglicherweise eine Dummy-Funktion enthält, die memcpy aufruft. Dann müssen Sie nur sicherstellen, dass die resultierende Objektdatei die erste Datei ist, die der Linker erhält.
Ich schlage vor, dass Sie memcpy () statisch verlinken. oder finden Sie die Quelle von memcpy () und kompilieren Sie es als Ihre eigene Bibliothek.
Es kann durch die alte ld (gnu link) Version ..__ verursacht werden. Für folgendes einfaches Problem:
#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;
}
Wenn ich ld 2.19.1 verwende, wird memset nach: memset @@ GLIBC_2.0 verschoben und zum Absturz führen ..__ Nach dem Upgrade auf 2.25 ist es: memset @ plt und der Absturz wurde behoben.
Minimales lauffähiges eigenständiges Beispiel
haupt c
#include <assert.h>
#include <stdlib.h>
#include "a.h"
#if defined(V1)
__asm__(".symver a,[email protected]_1");
#Elif defined(V2)
__asm__(".symver a,[email protected]_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,[email protected]_1");
int a1(void) {
return 1;
}
/* @@ means "default version". */
__asm__(".symver a2,[email protected]@LIBA_2");
int a2(void) {
return 2;
}
ah
#ifndef A_H
#define A_H
int a(void);
#endif
eine Landkarte
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 '[email protected]' -lcirosantilli_a
main1.out: main.c libcirosantilli_a.so
$(CC) -DV1 -L'.' main.c -o '[email protected]' -lcirosantilli_a
main2.out: main.c libcirosantilli_a.so
$(CC) -DV2 -L'.' main.c -o '[email protected]' -lcirosantilli_a
a.o: a.c
$(CC) -fPIC -c '$<' -o '[email protected]'
libcirosantilli_a.so: a.o
$(CC) -Wl,--version-script,a.map -L'.' -shared a.o -o '[email protected]'
libcirosantilli_a.o: a.c
$(CC) -fPIC -c '$<' -o '[email protected]'
clean:
rm -rf *.o *.a *.so *.out
Getestet auf Ubuntu 16.04.