非常に古い Red Hat システムで Python C 拡張機能を作成しています。システムには zlib 1.2.3 があり、大きなファイルを正しくサポートしていません。残念ながら、システムの zlib を新しいバージョンにアップグレードすることはできません。一部のパッケージが内部の zlib 構造に突き刺さり、新しい zlib バージョンでは機能しなくなるためです。
gzopen()
すべての zlib 呼び出し ( など) が、gzseek()
残りの Python 実行可能ファイルやその他の拡張機能に影響を与えることなく、ユーザー ディレクトリにインストールするカスタム zlib に解決されるように、拡張機能を構築したいと考えています。
libz.a
リンク中にgccコマンドラインに追加して静的にリンクしようとしましlibz.a
たが、うまくいきませんでした(gzopen()
たとえば、大きなファイルを作成することはまだできません)。私もgccに渡そうとし-z origin -Wl,-rpath=/path/to/zlib -lz
ましたが、それもうまくいきませんでした。
zlib の新しいバージョンはまだ名前が付けられzlib 1.x
てsoname
いるため、同じであるため、シンボルのバージョン管理は機能しないと思います。私がやりたいことをする方法はありますか?
私は 32 ビットの Linux システムを使用しています。Python のバージョンは 2.6 で、カスタムビルドされています。
編集:
最小限の例を作成しました。Cython (バージョン 0.19.1) を使用しています。
ファイルgztest.pyx
:
from libc.stdio cimport printf, fprintf, stderr
from libc.string cimport strerror
from libc.errno cimport errno
from libc.stdint cimport int64_t
cdef extern from "zlib.h":
ctypedef void *gzFile
ctypedef int64_t z_off_t
int gzclose(gzFile fp)
gzFile gzopen(char *path, char *mode)
int gzread(gzFile fp, void *buf, unsigned int n)
char *gzerror(gzFile fp, int *errnum)
cdef void print_error(void *gzfp):
cdef int errnum = 0
cdef const char *s = gzerror(gzfp, &errnum)
fprintf(stderr, "error (%d): %s (%d: %s)\n", errno, strerror(errno), errnum, s)
cdef class GzFile:
cdef gzFile fp
cdef char *path
def __init__(self, path, mode='rb'):
self.path = path
self.fp = gzopen(path, mode)
if self.fp == NULL:
raise IOError('%s: %s' % (path, strerror(errno)))
cdef int read(self, void *buf, unsigned int n):
cdef int r = gzread(self.fp, buf, n)
if r <= 0:
print_error(self.fp)
return r
cdef int close(self):
cdef int r = gzclose(self.fp)
return 0
def read_test():
cdef GzFile ifp = GzFile('foo.gz')
cdef char buf[8192]
cdef int i, j
cdef int n
errno = 0
for 0 <= i < 0x200:
for 0 <= j < 0x210:
n = ifp.read(buf, sizeof(buf))
if n <= 0:
break
if n <= 0:
break
printf('%lld\n', <long long>ifp.tell())
printf('%lld\n', <long long>ifp.tell())
ifp.close()
ファイルsetup.py
:
import sys
import os
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
if __name__ == '__main__':
if 'CUSTOM_GZ' in os.environ:
d = {
'include_dirs': ['/home/alok/zlib_lfs/include'],
'extra_objects': ['/home/alok/zlib_lfs/lib/libz.a'],
'extra_compile_args': ['-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -g3 -ggdb']
}
else:
d = {'libraries': ['z']}
ext = Extension('gztest', sources=['gztest.pyx'], **d)
setup(name='gztest', cmdclass={'build_ext': build_ext}, ext_modules=[ext])
私のカスタムzlib
は/home/alok/zlib_lfs
(zlibバージョン1.2.8)にあります:
$ ls ~/zlib_lfs/lib/
libz.a libz.so libz.so.1 libz.so.1.2.8 pkgconfig
これを使用してモジュールをコンパイルするにはlibz.a
:
$ CUSTOM_GZ=1 python setup.py build_ext --inplace
running build_ext
cythoning gztest.pyx to gztest.c
building 'gztest' extension
gcc -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/alok/zlib_lfs/include -I/opt/include/python2.6 -c gztest.c -o build/temp.linux-x86_64-2.6/gztest.o -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -g3 -ggdb
gcc -shared build/temp.linux-x86_64-2.6/gztest.o /home/alok/zlib_lfs/lib/libz.a -L/opt/lib -lpython2.6 -o /home/alok/gztest.so
gcc
必要なすべてのフラグが渡されています(へのフルパスの追加libz.a
、大きなファイルフラグなど)。
カスタム zlib を使用せずに拡張機能をビルドするには、CUSTOM_GZ
定義せずにコンパイルします。
$ python setup.py build_ext --inplace
running build_ext
cythoning gztest.pyx to gztest.c
building 'gztest' extension
gcc -fno-strict-aliasing -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/opt/include/python2.6 -c gztest.c -o build/temp.linux-x86_64-2.6/gztest.o
gcc -shared build/temp.linux-x86_64-2.6/gztest.o -L/opt/lib -lz -lpython2.6 -o /home/alok/gztest.so
gztest.so
ファイルのサイズを確認できます。
$ stat --format='%s %n' original/gztest.so custom/gztest.so
62398 original/gztest.so
627744 custom/gztest.so
そのため、静的にリンクされたファイルは、予想どおりはるかに大きくなります。
私は今できる:
>>> import gztest
>>> gztest.read_test()
foo.gz
現在のディレクトリを読み込もうとします。
non-staticallylinked を使用してこれを行うとgztest.so
、2 GB を超えて読み取ろうとするまで期待どおりに動作します。
staticallylinked を使用してこれを行うとgztest.so
、コアがダンプされます。
$ python -c 'import gztest; gztest.read_test()'
error (2): No such file or directory (0: )
0
Segmentation fault (core dumped)
誤解を招くエラーについてNo such file or directory
です。ファイルは存在し、gzopen()
実際には正常に返されます。 gzread()
失敗しますが。
バックトレースは次のgdb
とおりです。
(gdb) bt
#0 0xf730eae4 in free () from /lib/libc.so.6
#1 0xf70725e2 in ?? () from /lib/libz.so.1
#2 0xf6ce9c70 in __pyx_f_6gztest_6GzFile_close (__pyx_v_self=0xf6f75278) at gztest.c:1140
#3 0xf6cea289 in __pyx_pf_6gztest_2read_test (__pyx_self=<optimized out>) at gztest.c:1526
#4 __pyx_pw_6gztest_3read_test (__pyx_self=0x0, unused=0x0) at gztest.c:1379
#5 0xf769910d in call_function (oparg=<optimized out>, pp_stack=<optimized out>) at Python/ceval.c:3690
#6 PyEval_EvalFrameEx (f=0x8115c64, throwflag=0) at Python/ceval.c:2389
#7 0xf769a3b4 in PyEval_EvalCodeEx (co=0xf6faada0, globals=0xf6ff81c4, locals=0xf6ff81c4, args=0x0, argcount=0, kws=0x0, kwcount=0, defs=0x0, defcount=0, closure=0x0) at Python/ceval.c:2968
#8 0xf769a433 in PyEval_EvalCode (co=0xf6faada0, globals=0xf6ff81c4, locals=0xf6ff81c4) at Python/ceval.c:522
#9 0xf76bbe1a in run_mod (arena=<optimized out>, flags=<optimized out>, locals=<optimized out>, globals=<optimized out>, filename=<optimized out>, mod=<optimized out>) at Python/pythonrun.c:1335
#10 PyRun_StringFlags (str=0x80a24c0 "import gztest; gztest.read_test()\n", start=257, globals=0xf6ff81c4, locals=0xf6ff81c4, flags=0xffbf2888) at Python/pythonrun.c:1298
#11 0xf76bd003 in PyRun_SimpleStringFlags (command=0x80a24c0 "import gztest; gztest.read_test()\n", flags=0xffbf2888) at Python/pythonrun.c:957
#12 0xf76ca1b9 in Py_Main (argc=1, argv=0xffbf2954) at Modules/main.c:548
#13 0x080485b2 in main ()
問題の 1 つは、バックトレースの 2 行目がlibz.so.1
!を参照していることです。するとldd gztest.so
、他の行の中でも次のようになります。
libz.so.1 => /lib/libz.so.1 (0xf6f87000)
なぜそれが起こっているのかはわかりません。
編集2:
私は最終的に次のことをしました:
z_
プレフィックス 付きでエクスポートされたすべてのシンボルを使用して、カスタム zlib をコンパイルしました。zlib
のconfigure
スクリプトを使用すると、これが非常に簡単になります。実行するだけ./configure --zprefix ...
です。- 私のCythonコード
gzopen64()
の代わりに呼び出されました。gzopen()
これは、正しい「基になる」シンボルを使用していることを確認したかったためです。 z_off64_t
明示的に使用されます。zlib.a
カスタムを Cython によって生成された共有ライブラリに静的にリンクします。'-Wl,--whole-archive /home/alok/zlib_lfs_z/lib/libz.a -Wl,--no-whole-archive'
それを達成するためにgccとリンクしながら使用しました。他の方法があるかもしれませんし、これは必要ないかもしれませんが、正しいライブラリが確実に使用されるようにする最も簡単な方法のように思えました。
上記の変更により、大きなファイルが機能する一方で、残りの Python 拡張モジュール/プロセスは以前と同様に機能します。