web-dev-qa-db-ja.com

コンパイルPython Cythonで静的にリンクされた実行可能ファイルへのコード

純粋なPython不明なシステムに配布したいスクリプト$ Python構成。したがって、Pythonスタンドアロン実行可能ファイルへのコード。

走る cython --embed ./foo.pyは問題なくfoo.c。次に、私は走る

gcc $(python3-config --cflags) $(python3-config --ldflags) ./foo.c

どこ python3-config --cflags与える

-I/usr/include/python3.5m -I/usr/include/python3.5m  -Wno-unused-result -Wsign-compare -g -fdebug-prefix-map=/build/python3.5-MLq5fN/python3.5-3.5.3=. -fstack-protector-strong -Wformat -Werror=format-security  -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes

およびpython3-config --ldflags与える

-L/usr/lib/python3.5/config-3.5m-x86_64-linux-gnu -L/usr/lib -lpython3.5m -lpthread -ldl  -lutil -lm  -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions

この方法で、問題なく実行される動的にリンクされた実行可能ファイルを取得します。 ldd a.out収量

 linux-vdso.so.1 (0x00007ffcd57fd000)
 libpython3.5m.so.1.0 => /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0 (0x00007fda76823000)
 libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fda76603000)
 libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fda763fb000)
 libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007fda761f3000)
 libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fda75eeb000)
 libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fda75b4b000)
 libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x00007fda7591b000)
 libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fda756fb000)
 /lib64/ld-linux-x86-64.so.2 (0x00007fda77103000)

今、私はオプションを追加しようとします-staticをgccに送信しますが、これはエラーになります。

/usr/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
collect2: error: ld returned 1 exit status

Lddが提供するすべての共有ライブラリも静的ライブラリとしてインストールされることを確認しました。

それで、これはpython3-configで指定されたオプションとの非互換性ですか?

12
phlegmax

経験した問題は、明らかにリンカに由来します(gccはそれを確認するために、内部でリンカを起動しました--vでgccを起動するだけです-冗長モードで)。それでは、リンケージプロセスがどのように機能するかを簡単に思い出してみましょう。

リンカは、解決する必要があるすべてのシンボルの名前を保持します。最初はシンボルmainのみです。リンカがライブラリを検査するとどうなりますか?

  1. 静的ライブラリの場合、リンカーはこのライブラリ内のすべてのオブジェクトファイルを参照し、このオブジェクトファイルが検索対象のシンボルを定義する場合、オブジェクトファイル全体が含まれます(つまり、一部のシンボルは解決されますが、さらに新しい未解決のシンボルは追加されます)。リンカーは、静的ライブラリを複数回渡す必要がある場合があります。

  2. 共有ライブラリの場合、リンカからは単一の巨大なオブジェクトファイルで構成されるライブラリと見なされます(結局、実行時にこのライブラリをロードする必要があり、何度も何度も渡す必要はありません)未使用のシンボルをプルーニングする):必要なシンボルが少なくとも1つある場合、ライブラリ全体が「リンク」されます(実際には実行時にリンクは行われません。これは一種のドライランです)。破棄され、二度と見られません。

たとえば、次とリンクする場合:

gcc -L/path -lpython3.x <other libs> foo.o 

python3.xが共有ライブラリであるか静的ライブラリであるかに関係なく、問題が発生します。リンカがそれを見ると、シンボルmainのみを探しますが、このシンボルはPythonで定義されていません-lib。したがって、python-libは破棄され、再度参照されることはありません。リンカがオブジェクトファイルfoo.oを検出した場合にのみ、Pythonシンボル全体が必要であることを認識しますが、今ではすでに手遅れです。

この問題を処理する簡単なルールがあります:オブジェクトファイルを最初に置く!つまり:

gcc -L/path  foo.o -lpython3.x <other libs> 

これで、リンカは、最初に表示されたときに、python-libから必要なものを認識します。

同様の結果を達成する方法は他にもあります。

A)スイープごとに少なくとも1つの新しいシンボル定義が追加されている限り、リンカーがアーカイブのグループを繰り返すようにします。

gcc -L/path --Wl,-start-group -lpython3.x <other libs> foo.o -Wl,-end-group

リンカオプション-Wl,-start-groupおよび-Wl,-end-groupは、このアーカイブのグループでリンカを複数回反復するように指示しているため、リンカはシンボルを含める2回目(またはそれ以上)のチャンスがあります。このオプションにより、リンケージ時間が長くなる可能性があります。

B)オプション--no-as-neededをオンにすると、このライブラリで定義されたシンボルが必要かどうかに関係なく、共有ライブラリ(および共有ライブラリのみ)がリンクされます。

gcc -L/path -Wl,-no-as-needed -lpython3.x -Wl,-as-needed <other libs> foo.o

実際には、デフォルトのld-behaviorは--no-as-neededですが、gcc-frontendはオプション--as-neededでldを呼び出すため、python-libraryの前に-no-as-neededを追加してから動作を復元できます。再びオフにします。


次に、静的リンクの問題について説明します。すべての標準ライブラリの静的バージョン(すべてglibcより上)を使用することはお勧めできません。おそらく、python-libraryのみを静的にリンクする必要があります。

リンケージのルールは簡単です。デフォルトでは、リンカーは静的バージョンよりも先に共有バージョンのライブラリを開こうとします。つまりライブラリlibmylibおよびパスAおよびBの場合、つまり.

 -L/A -L/B lmylib

次の順序でライブラリを開こうとします。

A/libmylib.so
A/libmylib.a
B/libmylib.so
B/libmylib.a

したがって、フォルダーAに静的バージョンのみがある場合、この静的バージョンが使用されます(フォルダーBに共有バージョンがあるかどうかは関係ありません)。

どのライブラリが実際に使用されるかは非常に不透明であるため、システムのセットアップに依存します。通常、-Wl,-verboseを介してリンカーのログをオンにしてトラブルシューティングを行います。

オプション-Bstaticを使用すると、ライブラリの静的バージョンの使用を強制できます。

gcc  foo.o -L/path -Wl,-Bstatic -lpython3.x -Wl,-Bdynamic <other libs>  -Wl,-verbose -o foo

注目すべきこと:

  1. foo.oはライブラリの前にリンクされます。
  2. python-libraryの直後に静的モードをオフにして、他のライブラリが動的にリンクされるようにします。

そしていま:

 gcc <cflags> L/paths foo.c -Wl,-Bstatic -lpython3.X -Wl,-Bdynamic <other libs> -o foo -Wl,-verbose
...
attempt to open path/libpython3.6m.a succeeded
...
ldd foo shows no dependency on python-lib
./foo
It works!

はい、静的glibcにリンクする場合(お勧めしません)、コマンドラインから-Xlinker -export-dynamicを削除する必要があります。

-Xlinker -export-dynamicなしでコンパイルされた実行可能ファイルは、ldopenでロードされる実行可能ファイルのこのプロパティに依存するc-extensionの一部をロードできません。

8
ead