X86_64 Linuxでgccとldを使用すると、ライブラリの新しいバージョン(glibc 2.14)に対してリンクする必要がありますが、実行可能ファイルは古いバージョン(2.5)のシステムで実行する必要があります。互換性のないシンボルはmemcpy(memcpy@GLIBC_2.2.5が必要ですが、memcpy @ GLIBC_2.14を提供するライブラリが必要)だけなので、memcpyのデフォルトバージョンを使用する代わりに、指定した古いバージョンを使用する必要があることをリンカーに伝えたい。
リンカのコマンドラインで古い.soファイルのコピーを指定するだけです。これは正常に機能しますが、複数の.soファイル(リンクするすべての古いライブラリを指定し、memcpyへの参照も指定することによってのみ機能する)がsvnにチェックインされ、ビルドシステムで必要になるという考えが好きではありません。
そこで、リンカに古いバージョンのシンボルを取得するように指示する方法を探しています。
私にとってうまくいかない代替案は次のとおりです:
リンカが行うすべてのジョブについて考えると、シンボルのデフォルトバージョンを把握するためのコードもあるため、それを実装するのは難しいことではありません。
結果のバイナリを編集するような奇妙なハックでない限り、単純なリンカーコマンドラインと同じ複雑さのレベルにある他のアイデア(単純なリンカースクリプトの作成など)も歓迎します...
edit:これを将来の読者のために保存するために、以下のアイデアに加えて、オプション--wrap
リンカへ。これも時々役立つかもしれません。
Memcpyを静的にリンクするだけで、memcpy.oをlibc.aから引き出しますar x /path/to/libc.a memcpy.o
(どのバージョンでも-memcpyはほとんどスタンドアロンの機能です)、最終リンクに含めます。プロジェクトがオープンソースではなく一般に配布されている場合、静的リンクはライセンスの問題を複雑にする可能性があることに注意してください。
または、membcyを自分で簡単に実装することもできますが、glibcの手動で調整されたアセンブリバージョンの方が効率的です
memcpy@GLIBC_2.2.5 はmemmoveにマップされることに注意してください(memcpyの古いバージョンは一貫して予測可能な方向にコピーされ、memmoveを使用する必要があるときに誤用されることがありました)。バージョンバンプの理由-この特定のケースでは、コード内のmemcpyをmemmoveに置き換えるだけです。
または、静的リンクに移動するか、ネットワーク上のすべてのシステムがビルドマシンと同じか、より良いバージョンであることを確認できます。
次の実用的なソリューションを見つけました。最初にファイル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);
}
このファイルをコンパイルするために追加のCFLAGSは必要ありません。次に、プログラムを-Wl、-wrap = memcpyにリンクします。
同様の問題がありました。使用するサードパーティライブラリには、古いmemcpy@GLIBC_2.2.5
。私の解決策は、@ anightが投稿した拡張アプローチです。
memcpy
コマンドもワープしますが、@ anightが投稿した解決策が役に立たなかったため、少し異なるアプローチを使用する必要がありました。
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;
};
ラッパーのビルド:
gcc -c memcpy_wrap.c -o memcpy_wrap.o
最後に、プログラムをリンクするときに追加します
-Wl,--version-script memcpy_wrap.map
memcpy_wrap.o
次のような結果になります。
g++ <some flags> -Wl,--version-script memcpy_wrap.map <some .o files> memcpy_wrap.o <some libs>
同様の問題がありました。 RHEL 7.1にいくつかのOracleコンポーネントをインストールしようとすると、次のようになりました。
$ gcc -o /some/Oracle/bin/foo .... -L/some/Oracle/lib ...
/some/Oracle/lib/libfoo.so: undefined reference to `memcpy@GLIBC_2.14'
(私の)RHELのglibcは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
そこで、最初に次のようにラップせずにmemcpy.cファイルを作成することで、この問題を回避できました。
#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);
}
memcpyをmemcpy@GLIBC_2.14としてエクスポートするmemcpy.mapファイル:
GLIBC_2.14 {
memcpy;
};
次に、自分のmemcpy.cを次のような共有ライブラリにコンパイルしました。
$ 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を/ some/Oracle/libに移動し(リンクの-L引数で示されます)、再度リンクされます
$ gcc -o /some/Oracle/bin/foo .... -L/some/Oracle/lib ... /some/Oracle/lib/libmemcpy-2.14.so -lfoo ...
(エラーなしでコンパイルされた)、次の方法で検証しました。
$ 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)
これは私のために働いた。私もそれがあなたのためになることを願っています。
私は明らかにこれに対応するのが少し遅れていますが、最近、Linux OSを新しいlibcに付属するXUbuntu 14.04にアップグレードしました(アップグレードしない理由は他にもあります)。正当な理由が何であれ、環境を10.04からアップグレードしていないクライアントが使用する共有ライブラリをマシン上でコンパイルします。 gccはmemcpy glibc v。2.14(以降)に依存するため、コンパイルした共有ライブラリは環境にロードされなくなりました。これの狂気を無視しましょう。プロジェクト全体の回避策は3つありました。
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
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;
この回避策は、-fltoコンパイルオプションと互換性がないようです。
私の解決策はmemmoveを呼び出すことです。 memoveはmemcpyとまったく同じ仕事をします。唯一の違いは、srcゾーンとdestゾーンが重複している場合、memmoveは安全で、memcpyは予測不能です。そのため、memcpyの代わりにいつでもmemmoveを安全に呼び出すことができます
#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
Symverステートメントと、おそらくmemcpyを呼び出すダミー関数を含む単純なCファイルを作成しても問題ないと思います。次に、結果のオブジェクトファイルがリンカーに渡される最初のファイルであることを確認する必要があります。
Memcpy()を静的にリンクすることをお勧めします。または、memcpy()のソースを見つけて、独自のライブラリとしてコンパイルします。
古いld(gnuリンク)バージョンが原因の可能性があります。次の簡単な問題の場合:
#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;
}
Ld 2.19.1を使用すると、memsetがmemset @@ GLIBC_2.0に再配置され、クラッシュが発生します。 2.25にアップグレードすると、memset @ pltになり、クラッシュが解決しました。
最小限の実行可能な自己完結型の例
main.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;
}
交流
#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;
}
ああ
#ifndef A_H
#define A_H
int a(void);
#endif
地図
LIBA_1{
global:
a;
local:
*;
};
LIBA_2{
global:
a;
local:
*;
};
メイクファイル
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
Ubuntu 16.04でテスト済み。