使用自定义openssl版本编译python时出现核心转储

9 投票
7 回答
5659 浏览
提问于 2025-04-17 22:19

在用本地的openssl-1.0.1f共享安装编译python-3.4.0rc3时,make没有报错,但在执行make install或make test时,我却遇到了核心转储(core dump)的问题:

Program terminated with signal 11, Segmentation fault.
(gdb) bt
#0  0x00007f131dd10510 in EVP_PKEY_CTX_dup () from /data2/soft/openssl/lib/libcrypto.so.1.0.0
#1  0x00007f131dd0284f in EVP_MD_CTX_copy_ex () from /data2/soft/openssl/lib/libcrypto.so.1.0.0
#2  0x00007f131e256ab5 in EVPnew (name_obj=0x7f131e46a500, digest=0x0, initial_ctx=0x7f131e459a40, cp=0x0, len=0) at /data2/soft/python3/Python-3.4.0rc3/Modules/_hashopenssl.c:410
#3  0x00007f131e25726e in EVP_new_md5 (self=<value optimized out>, args=<value optimized out>) at /data2/soft/python3/Python-3.4.0rc3/Modules/_hashopenssl.c:799
#4  0x00000000004c7eef in ?? ()

以下是我使用的完整命令列表:

tar -axf Python-3.4.0rc3.tgz
cd Python-3*
# For lzma and a pip-compatible openssl
export CFLAGS='-I/data2/soft/openssl/include/openssl -I/data2/local/include/'
export LDFLAGS='-L/data2/soft/openssl/lib -L/data2/local/lib/'
export LD_LIBRARY_PATH="/data2/soft/openssl/lib:/data2/local/lib/:$LD_LIBRARY_PATH"
# Ready !
./configure --prefix=/data2/soft/python3
make
make install

注意:

  • 操作系统是SUSE Linux Enterprise Server 11 (x86_64)
  • 将python指向一个自定义位置,那里有lzma库
  • openssl是用./config shared --openssldir=/data2/soft/openssl构建的
  • openssl的make test显示所有测试成功。
  • 如果不使用自定义的openssl *FLAGS,make install会成功,而make test的结果是: 71个测试通过。 测试失败: test_cmd_line test_gdb test_smtpnet test_ssl

我该如何解决这个问题,或者至少调查一下发生了什么?

编辑 1--5:

共享库生成得很正确:

> ls /data2/soft/openssl/lib
drwxr-xr-x engines
-rw-r--r-- libcrypto.a
lrwxrwxrwx libcrypto.so -> libcrypto.so.1.0.0
-r-xr-xr-x libcrypto.so.1.0.0
-rw-r--r-- libssl.a
lrwxrwxrwx libssl.so -> libssl.so.1.0.0
-r-xr-xr-x libssl.so.1.0.0
drwxr-xr-x pkgconfig

所以我在Setup文件中做了这个更改:

SSL=/data2/soft/openssl/
_ssl _ssl.c \                               
    -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \
    $(SSL)/lib/libssl.a $(SSL)/lib/libcrypto.a -ldl

然后我相应地调整了LDFLAGS/CFLAGS。但在我运行make clean && make时,仍然有一个-lssl,因为_hashopen模块的原因:

gcc -pthread -shared -L/data2/local/lib/ -L/data2/local/lib/ -L/data2/local/lib/ -I/data2/local/include/ build/temp.linux-x86_64-3.4/data2/soft/python3/Python-3.4.0rc3/Modules/_hashopenssl.o -L/data2/local/lib/ -L/usr/local/lib -lssl -lcrypto -o build/lib.linux-x86_64-3.4/_hashlib.cpython-34m.so

我猜这就是导致核心转储的原因,因为它们依然存在……我尝试在Setup文件中添加类似的内容,但没有找到这个的注释项,创建它又导致了另一个更难理解的错误:

gcc -pthread   -Xlinker -export-dynamic -o python Modules/python.o libpython3.4m.a -lpthread -ldl  -lutil /data2/eoubrayrie/soft/openssl/lib/libssl.a /data2/eoubrayrie/soft/openssl/lib/libcrypto.a -ldl   /data2/eoubrayrie/soft/openssl/lib/libssl.a /data2/eoubrayrie/soft/openssl/lib/libcrypto.a -ldl   -lm  
libpython3.4m.a(config.o):(.data+0x158): undefined reference to `PyInit__hashopenssl'
collect2: ld returned 1 exit status

编辑 6:

  • 我找不到任何方法来修改_hashlib.so的生成方式,因为涉及到太多的Makefile魔法(它没有出现在任何地方,也没有'-lssl',但它们却神奇地出现在同一行)
  • 不过我可以通过老办法-I/-L让它动态链接到我自己的openssl:

    ldd build/lib.linux-x86_64-3.4/_hashlib.cpython-34m.so libssl.so.1.0.0 => /data2/soft/openssl/lib/libssl.so.1.0.0 (0x00007f5605799000) libcrypto.so.1.0.0 => /data2/soft/openssl/lib/libcrypto.so.1.0.0 (0x00007f56053bd000)

  • 现在唯一的问题是,gdb的info shared仍然告诉我在核心转储时使用了另一个库……但怎么会这样呢?

    从 到 读取的符号 共享对象库 0x00007ffff5465930 0x00007ffff5466e98 是 /data2/soft/python3/Python-3.4.0rc3/build/lib.linux-x86_64-3.4/_hashlib.cpython-34m.so 0x00007ffff5321220 0x00007ffff5351878 是 /opt/python-2.6-64/lib/libssl.so.1.0.0 0x00007ffff50d3100 0x00007ffff519b118 是 /opt/python-2.6-64/lib/libcrypto.so.1.0.0

    • env | grep -F 'python-2.6-64' -> 没有显示任何内容!
    • grep -RF 'python-2.6-64' /etc/ld.so.* -> 同样没有
    • gcc -print-search-dirs | sed 's/:/\n/g' | grep python -> 同样没有
    • find . -name '*.so*' | xargs ldd | grep ssl -> 只给我正确的库
    • 一级依赖项也不需要任何错误的ssl版本。这是通过以下命令检查的:
      find . -name '*.so*' | xargs ldd | awk '/\t+[[:alnum:].]+ => [[:alnum:]./]+ \(/ {print $3}' | sort | uniq | xargs ldd | grep ssl
    • strace ./python ./Tools/scripts/run_tests.py 2>&1 | grep python-2.6-64 -> 没有显示任何内容

那么,ld是如何选择这个错误的库的,如果它根本不知道呢?它不在任何标准位置(如果在/lib我可以理解……)

解决方案:

我找到了如何静态链接_hashlib的方法,感谢这个OpenOffice的bug:虽然-Wl,--exclude-libs=ALL"选项也没有用,但它让我找到了setup.py中的正确行。

总结:这是我应用的setup.py补丁

最后……它成功了!

@noloader 我接受你的最完整的回答,因为你的帮助非常宝贵,尽管对于遇到这个问题的任何人来说,“确切”的答案就是用上面的补丁进行编译。

7 个回答

0

这是另一个部分的回答...

不过我可以通过老办法 -I/-L 让它动态链接到我自己的 openssl:

...

现在唯一的问题是,gdb info shared 还是告诉我在核心时用的是另一个 ... 这是怎么回事呢?

这就是你熟悉的 -l-L不要使用它们,因为它们总是会出现这种情况(相信我,我以前也遭遇过)。相反,应该指定 完整libssllibcrypto 的路径。比如,可以使用 /data2/soft/openssl/lib/libssl.a

0

我找不到任何方法来修改 _hashlib.so 的生成方式,因为这里面涉及太多复杂的 Makefile 魔法(它并没有出现在任何地方,也没有 '-lssl',但它们却神奇地出现在同一行上)

grep 是你的好帮手 ;)

$ grep -R _hashlib * | grep ssl
Lib/hashlib.py:        f = getattr(_hashlib, 'openssl_' + name)
Lib/hashlib.py:            _hashlib.openssl_md_meth_names)
Lib/test/test_hashlib.py:            self.assertTrue(hasattr(_hashlib, 'openssl_md5'))
Lib/test/test_hashlib.py:            self.assertTrue(hasattr(_hashlib, 'openssl_sha1'))
Lib/test/test_hashlib.py:                constructor = getattr(_hashlib, 'openssl_'+algorithm, None)
Lib/test/ssltests.py:TESTS = ['test_asyncio', 'test_ftplib', 'test_hashlib', 'test_httplib',
Lib/test/time_hashlib.py:    print(" '_hashlib' 'openssl_hName' 'fast' tests the builtin _hashlib")
Modules/_hashopenssl.c:    "_hashlib.HASH",    /*tp_name*/
Modules/_hashopenssl.c:static struct PyModuleDef _hashlibmodule = {
Modules/_hashopenssl.c:    "_hashlib",
Modules/_hashopenssl.c:PyInit__hashlib(void)
Modules/_hashopenssl.c:    m = PyModule_Create(&_hashlibmodule);
PCbuild/build_ssl.py:# Script for building the _ssl and _hashlib modules for Windows.
PCbuild/build_ssl.py:# for the actual _ssl.pyd and _hashlib.pyd DLLs.
PCbuild/build_ssl.py:# it should configure and build SSL, then build the _ssl and _hashlib
setup.py:                exts.append( Extension('_hashlib', ['_hashopenssl.c'],
setup.py:                print("warning: openssl 0x%08x is too old for _hashlib" %
Tools/ssl/test_multiple_versions.py:    "test_asyncio", "test_ftplib", "test_hashlib", "test_httplib",
Tools/ssl/test_multiple_versions.py:MINIMAL_TESTS = ["test_ssl", "test_hashlib"]
1

这不是一个答案,只是我在和你一起研究这个问题时的一个观察:

$ make 
...

gcc -pthread -c -Wno-unused-result -Werror=declaration-after-statement
  -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I. -IInclude -I./Include
  -DPy_BUILD_CORE -o Objects/capsule.o Objects/capsule.c

-fwrapv 这个选项其实不好。它是用来让一些不合法的程序能够运行的。与其用这个选项,不如直接修复那些有问题的程序,然后去掉 -fwrapv。你可以看看 Ian Lance Taylor 的博客,里面有关于 有符号溢出 的内容。

3

我该怎么改呢?在任何地方加一行 _ssl.c: gcc ... 会覆盖默认行为吗?我的 Makefile 技能生疏了,这个文件有1600多行呢!!

这里是你需要查看的文件:

$ cd Python-3.4.0rc3

$ grep -R -- '-lcrypto' *
Modules/Setup.dist:#    -L$(SSL)/lib -lssl -lcrypto

$ grep -R -- '-lssl' *
Modules/Setup.dist:#    -L$(SSL)/lib -lssl -lcrypto

Setup.dist 中,有一些重要的行:

# Socket module helper for SSL support; you must comment out the other
# socket line above, and possibly edit the SSL variable:
#SSL=/usr/local/ssl
#_ssl _ssl.c \
#   -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \
#   -L$(SSL)/lib -lssl -lcrypto

把这些行前面的注释去掉,并把 Setup.dist 改成下面的样子。我把我的 OpenSSL 放在 /usr/local/ssl,所以我下面的设置是这样(你应该用 /data2/soft/openssl/lib/...):

SSL=/usr/local/ssl
_ssl _ssl.c \
    -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \
    /usr/local/ssl/lib/libssl.a /usr/local/ssl/lib/libcrypto.a -ldl

使用归档文件的完整路径,不要用-l。一定要加上-ldl,因为在这个配置下 OpenSSL 需要它。

一旦你修改了 Setup.dist,重新运行 ./configure 来应用这些更改。


在把上面的行改好后,运行 configure 后我的情况是这样的:

$ grep -R "libssl.a" *
Makefile:LOCALMODLIBS=  /usr/local/ssl/lib/libssl.a /usr/local/ssl/lib/libcrypto.a -ldl 
Makefile:Modules/_ssl$(SO):  Modules/_ssl.o; $(BLDSHARED)  Modules/_ssl.o  /usr/local/ssl/lib/libssl.a /usr/local/ssl/lib/libcrypto.a -ldl  -o Modules/_ssl$(SO)
Modules/Setup: /usr/local/ssl/lib/libssl.a /usr/local/ssl/lib/libcrypto.a -ldl 
Modules/Setup.dist: /usr/local/ssl/lib/libssl.a /usr/local/ssl/lib/libcrypto.a -ldl 

你可以用 lddotool -L 来测试静态链接。你不会看到 OpenSSL 的依赖关系。运行 make 后,我得到了这样的结果:

$ find . -iname python
./python
$ ldd ./python
    linux-vdso.so.1 =>  (0x00007fff67709000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f3aed8e1000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f3aed6dd000)
    libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007f3aed4d9000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f3aed257000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3aececc000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f3aedb14000)

没有 libssllibcrypto 的依赖关系可以出错 :) 而且 make test 运行得很好(实际上,有一个测试失败了,是因为 Python 的一个 bug:问题 20896):

======================================================================
ERROR: test_get_server_certificate (test.test_ssl.NetworkedTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/jwalton/Python-3.4.0rc3/Lib/test/test_ssl.py", line 1373, in test_get_server_certificate
    _test_get_server_certificate('svn.python.org', 443, SVN_PYTHON_ORG_ROOT_CERT)
  File "/home/jwalton/Python-3.4.0rc3/Lib/test/test_ssl.py", line 1354, in _test_get_server_certificate
    pem = ssl.get_server_certificate((host, port))
  File "/home/jwalton/Python-3.4.0rc3/Lib/ssl.py", line 902, in get_server_certificate
    with context.wrap_socket(sock) as sslsock:
  File "/home/jwalton/Python-3.4.0rc3/Lib/ssl.py", line 344, in wrap_socket
    _context=self)
  File "/home/jwalton/Python-3.4.0rc3/Lib/ssl.py", line 540, in __init__
    self.do_handshake()
  File "/home/jwalton/Python-3.4.0rc3/Lib/ssl.py", line 767, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:598)

----------------------------------------------------------------------
Ran 96 tests in 8.610s

FAILED (errors=1, skipped=3)
test test_ssl failed
make: *** [test] Error 1
4
How can I fix this, or at least investigate what is going on ?
...
export LDFLAGS='-L/data2/soft/openssl/lib -L/data2/local/lib/'
export LD_LIBRARY_PATH="/data2/soft/openssl/lib:/data2/local/lib/

我也经常遇到这些问题,因为我不想使用Debian、Ubuntu、Fedora等系统自带的功能受限的OpenSSL版本。例如,Ubuntu自带的OpenSSL禁用了TLSv1.1和TLSv1.2(参考:Ubuntu 12.04 LTS: OpenSSL版本过低,不支持TLS 1.2)。

你可能加载了错误版本的OpenSSL库。如果你能在调试器中运行出问题的程序,可以输入info shared来查看你实际加载了哪个libcryptolibssl

ldd也可能有帮助。你可以在Python可执行文件上运行它:ldd /data2/soft/python3/python。我说它“可能”有帮助是因为OpenSSL的版本是二进制兼容的,所以你可能只会看到对例如libcrypto.so.1.0.0的依赖(在Mac OS X上使用otool -L)。下面我使用了rpath来强制链接到/usr/local/ssl/lib/中的库。

$ ldd my-test.exe 
    linux-vdso.so.1 =>  (0x00007fffd61ff000)
    libssl.so.1.0.0 => /usr/local/ssl/lib/libssl.so.1.0.0 (0x00007f151528e000)
    libcrypto.so.1.0.0 => /usr/local/ssl/lib/libcrypto.so.1.0.0 (0x00007f1514e74000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f1514c42000)
    ...

作为一个解决方案,你可以尝试添加一个rpath

LDFLAGS='-L/data2/soft/openssl/lib -L/data2/local/lib/ -Wl,-rpath,/data2/soft/openssl/lib'

更好的解决办法是链接到OpenSSL库的静态版本,这样就可以完全避免这些问题。我相信你可以这样做:-Bstatic -lssl -lcrypto -Bdynamic -ldl

就我个人而言,我甚至不使用-Bstatic,因为会遇到不同的偶发问题。我会打开Makefile,删除所有-lssl-lcrypto的实例,并添加库的完整路径,以消除所有歧义。例如,/data2/soft/openssl/lib/libssl.a/data2/soft/openssl/lib/libcrypto.a

请注意:在Mac OS X上,rpath不会被认可。对于Mac OS X,需要采取更极端的措施,因为链接器也不认可-Bstatic。你必须使用完整路径的方法。


openssl was built with ./config shared --openssldir=/data2/soft/openssl

还有一件事……在Fedora上,仅仅指定shared是不够的。你还需要添加以下内容:

export CFLAGS="-fPIC"

否则,共享对象不会被构建。如果1.0.1f的共享对象没有被构建,你可能会得到发行版提供的低版本。你可以在安装之前检查构建了什么:

./config shared --openssldir=/data2/soft/openssl
make all

# Verify artifacts
find . -iname -libcrypto.*
find . -iname -libssl.*

# Proceed if OK
sudo make install

最后,确保Python的所有依赖项也在使用你的OpenSSL版本,而不是系统自带的OpenSSL版本。

我最近遇到过这个问题,我的程序使用了我的OpenSSL,但我的程序还使用了libevent,而libevent使用的是系统的OpenSSL版本。我通过重新构建libevent并强制它静态链接到我的OpenSSL版本来解决了这个问题。

撰写回答