Paradoxon:导入时 Python 的 ctypes.CDLL 会无声崩溃,但直接运行时不会崩溃 - 这怎么可能?

2024-02-02

所以,作为一个 Linux 爱好者,我在 Windows 上偶然发现了一些我无法解释的非常令人费解的事情。

我有一个类似于此示例的项目结构:

D:\PROJECT
|
|   tolkien.py
|   __init__.py
|   
\---MiddleEarth
    |   gondor.py
    |   isengrad.c
    |   __init__.py
    |   
    \---lib
            isengrad.so

Problem:我编译isengrad.c进入共享库isengrad.so,然后将其加载到gondor.py。我的目标是导入gondor.py into tolkien.py.
While gondor.py直接运行时运行完美,当我导入它时,代码在我通过加载共享库时退出ctypes.CDLL,没有任何错误消息。

再生产:文件的内容(添加了一些“状态消息”以跟踪问题发生的位置):

伊森格拉德.c:

int isengrad(int hobbit){
    return hobbit/2;
}

然后将其编译为伊森格拉德.so with

D:\project>chdir MiddleEarth
D:\project\MiddleEarth>gcc -fPIC -shared -o lib/isengrad.so isengrad.c

然后访问共享库刚铎.py:

print("started gondor")

import os, ctypes
path_to_isengrad = "D:/project/MiddleEarth/lib/isengrad.so"  

print("gondor loads isengrad")
gondor = ctypes.CDLL(path_to_isengrad)     # <--- crashes here when imported, not when ran directly
print("gondor loaded isengrad")


gondor.isengrad.argtypes = (ctypes.c_int,)

def faramir(hobbit):
    catched_hobbits = gondor.isengrad(hobbit)
    return catched_hobbits

if __name__ == '__main__':
    print(faramir(5))
    print("gondor ran")

print("gondor finished")

然后导入托尔金.py:

print("started tolkien")
from MiddleEarth import gondor
print("tolkien imported gondor")

got = gondor.faramir(4)
print(got)

print("tolkien worked")

现在检查我使用时会发生什么gondor.py当我导入它时直接VStolkien.py:

D:\project>python MiddleEarth/gondor.py
started gondor
gondor loads isengrad
gondor loaded isengrad
2
gondor ran
gondor finished

D:\project>python tolkien.py
started tolkien
started gondor
gondor loads isengrad

D:\project>

直接运行完全没有问题。但是导入它会导致整个事情在加载共享库时崩溃,没有任何文字和回溯。这是怎么发生的?我什至硬编码了共享库的路径,所以不同的工作目录应该不是问题...我在 Kubuntu 上的同一个项目没有任何问题,所以这可能是一些与 Windows 相关的东西。

环境:

  • Python: Python 3.7.3 (default, Mar 27 2019, 17:13:21) [MSC v.1915 64 bit (AMD64)] :: Anaconda, Inc. on win32
  • OS: Windows 10 10.0.17134 Build 17134(安装在C:)
  • GCC:通过 Cygwin 安装,版本 7.4.0
  • 请询问是否需要任何其他详细信息。

从看到这个问题的那一刻起我就想说未定义的行为 (UB). Python带有它的C运行 (UCRTLib),同时Cygwin .dll有它自己的。混合编译器和C进程中的运行时间通常会导致灾难。
我找到了官方的说法[赛格温]:6.15.我可以同时链接 MSVCRT*.DLL 和 cygwin1.dll 吗? https://cygwin.com/faq/faq.html#faq.programming.msvcrt-and-cygwin (emphasis是我的):

不,您必须使用其中之一,它们是互斥的.

Check [SO]:如何规避 Windows 通用 CRT 标头对 vcruntime.h 的依赖(@CristiFati 的回答) https://stackoverflow.com/questions/45340527/how-to-circumvent-windows-universal-crt-headers-dependency-on-vcruntime-h/50838055#50838055欲了解更多详细信息MSVCRT*.DLL

现在,美丽UB是它描述了一种看似随机的行为。

我准备了一个全面的示例(稍微修改您的代码)。

isengrad.c:

#if defined(_WIN32)
#  define ISENGRAD_EXPORT_API __declspec(dllexport)
#else
#  define ISENGRAD_EXPORT_API
#endif


ISENGRAD_EXPORT_API int isengrad(int hobbit) {
    return hobbit / 2;
}

脚本0.py:

#!/usr/bin/env python3

import sys
import ctypes


dll_name = "./lib/isengrad_{0:s}_{1:03d}.dll".format(sys.argv[1][:3] if sys.argv else sys.platform[:3].lower(), ctypes.sizeof(ctypes.c_void_p) * 8)
print("Attempting to load: {0:s}".format(dll_name))
isengrad_dll = ctypes.CDLL(dll_name)
print("DLL Loaded")


def main():
    isengrad_func = isengrad_dll.isengrad
    isengrad_func.argtypes = [ctypes.c_int]
    isengrad_func.restype = ctypes.c_int

    res = isengrad_func(46)
    print("{0:s} returned {1:}".format(isengrad_func.__name__, res))


if __name__ == "__main__":
    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    main()
    print("\nDone.")

脚本1.py:

#!/usr/bin/env python3

import sys
import script0


def main():
    pass


if __name__ == "__main__":
    print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    main()
    print("\nDone.")

Outputs:

  • I'll be using 3 windows:
    • cmd - Win (32bit and 64bit)
    • Cygwin's Mintty:
      • 64bit
      • 32bit
  • 请注意,即使我将每个内容粘贴到一个块中(以避免分散它们),我也会在运行命令时在它们之间切换
  • Cygwin 32bit:

    [cfati@cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q056855348]> ~/sopr.sh
    *** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
    
    [032bit prompt]> gcc -shared -fPIC -o lib/isengrad_cyg_032.dll isengrad.c
    [032bit prompt]>  ls lib/*.dll
    lib/isengrad_cyg_032.dll  lib/isengrad_cyg_064.dll  lib/isengrad_win_032.dll  lib/isengrad_win_064.dll
    [032bit prompt]>
    [032bit prompt]> python3 script0.py cyg
    Attempting to load: ./lib/isengrad_cyg_032.dll
    DLL Loaded
    Python 3.6.4 (default, Jan  7 2018, 17:45:56) [GCC 6.4.0] 32bit on cygwin
    
    isengrad returned 23
    
    Done.
    [032bit prompt]>
    [032bit prompt]> python3 script1.py cyg
    Attempting to load: ./lib/isengrad_cyg_032.dll
    DLL Loaded
    Python 3.6.4 (default, Jan  7 2018, 17:45:56) [GCC 6.4.0] 32bit on cygwin
    
    
    Done.
    [032bit prompt]>
    [032bit prompt]> python3 script0.py win
    Attempting to load: ./lib/isengrad_win_032.dll
    DLL Loaded
    Python 3.6.4 (default, Jan  7 2018, 17:45:56) [GCC 6.4.0] 32bit on cygwin
    
    isengrad returned 23
    
    Done.
    [032bit prompt]>
    [032bit prompt]> python3 script1.py win
    Attempting to load: ./lib/isengrad_win_032.dll
    DLL Loaded
    Python 3.6.4 (default, Jan  7 2018, 17:45:56) [GCC 6.4.0] 32bit on cygwin
    
    
    Done.
    
  • Cygwin 64bit:

    [cfati@cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q056855348]> ~/sopr.sh
    *** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
    
    [064bit prompt]>  gcc -shared -fPIC -o lib/isengrad_cyg_064.dll isengrad.c
    [064bit prompt]> ls lib/*.dll
    lib/isengrad_cyg_032.dll  lib/isengrad_cyg_064.dll  lib/isengrad_win_032.dll  lib/isengrad_win_064.dll
    [064bit prompt]>
    [064bit prompt]> python3 script0.py cyg
    Attempting to load: ./lib/isengrad_cyg_064.dll
    DLL Loaded
    Python 3.6.8 (default, Feb 14 2019, 22:09:48) [GCC 7.4.0] 64bit on cygwin
    
    isengrad returned 23
    
    Done.
    [064bit prompt]>
    [064bit prompt]> python3 script1.py cyg
    Attempting to load: ./lib/isengrad_cyg_064.dll
    DLL Loaded
    Python 3.6.8 (default, Feb 14 2019, 22:09:48) [GCC 7.4.0] 64bit on cygwin
    
    
    Done.
    [064bit prompt]>
    [064bit prompt]> python3 script0.py win
    Attempting to load: ./lib/isengrad_win_064.dll
    DLL Loaded
    Python 3.6.8 (default, Feb 14 2019, 22:09:48) [GCC 7.4.0] 64bit on cygwin
    
    isengrad returned 23
    
    Done.
    [064bit prompt]>
    [064bit prompt]> python3 script1.py win
    Attempting to load: ./lib/isengrad_win_064.dll
    DLL Loaded
    Python 3.6.8 (default, Feb 14 2019, 22:09:48) [GCC 7.4.0] 64bit on cygwin
    
    
    Done.
    
  • cmd:

    [cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q056855348]> sopr.bat
    *** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
    
    [prompt]> dir /b lib
    
    [prompt]> "c:\Install\x86\Microsoft\Visual Studio Community\2017\VC\Auxiliary\Build\vcvarsall.bat" x64
    **********************************************************************
    ** Visual Studio 2017 Developer Command Prompt v15.9.14
    ** Copyright (c) 2017 Microsoft Corporation
    **********************************************************************
    [vcvarsall.bat] Environment initialized for: 'x64'
    
    [prompt]> cl /nologo /DDLL isengrad.c  /link /NOLOGO /DLL /OUT:lib\isengrad_win_064.dll
    isengrad.c
       Creating library lib\isengrad_win_064.lib and object lib\isengrad_win_064.exp
    
    [prompt]>
    [prompt]> "c:\Install\x86\Microsoft\Visual Studio Community\2017\VC\Auxiliary\Build\vcvarsall.bat" x86
    **********************************************************************
    ** Visual Studio 2017 Developer Command Prompt v15.9.14
    ** Copyright (c) 2017 Microsoft Corporation
    **********************************************************************
    [vcvarsall.bat] Environment initialized for: 'x86'
    
    [prompt]> cl /nologo /DDLL isengrad.c  /link /NOLOGO /DLL /OUT:lib\isengrad_win_032.dll
    isengrad.c
       Creating library lib\isengrad_win_032.lib and object lib\isengrad_win_032.exp
    
    [prompt]> dir /b lib\*.dll
    isengrad_cyg_032.dll
    isengrad_cyg_064.dll
    isengrad_win_032.dll
    isengrad_win_064.dll
    
    [prompt]> set _PATH=%PATH%
    
    [prompt]> :: Python 32bit
    [prompt]> set PATH=%_PATH%;e:\Install\x86\Cygwin\Cygwin\Version\bin
    
    [prompt]> "e:\Work\Dev\VEnvs\py_032_03.07.03_test0\Scripts\python.exe" script0.py win
    Attempting to load: ./lib/isengrad_win_032.dll
    DLL Loaded
    Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 21:26:53) [MSC v.1916 32 bit (Intel)] 32bit on win32
    
    isengrad returned 23
    
    Done.
    
    [prompt]> "e:\Work\Dev\VEnvs\py_032_03.07.03_test0\Scripts\python.exe" script1.py win
    Attempting to load: ./lib/isengrad_win_032.dll
    DLL Loaded
    Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 21:26:53) [MSC v.1916 32 bit (Intel)] 32bit on win32
    
    
    Done.
    
    [prompt]> "e:\Work\Dev\VEnvs\py_032_03.07.03_test0\Scripts\python.exe" script0.py cyg
    Attempting to load: ./lib/isengrad_cyg_032.dll
    DLL Loaded
    Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 21:26:53) [MSC v.1916 32 bit (Intel)] 32bit on win32
    
    isengrad returned 23
    
    Done.
    
    [prompt]> "e:\Work\Dev\VEnvs\py_032_03.07.03_test0\Scripts\python.exe" script1.py cyg
    Attempting to load: ./lib/isengrad_cyg_032.dll
    DLL Loaded
    Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 21:26:53) [MSC v.1916 32 bit (Intel)] 32bit on win32
    
    
    Done.
    
    [prompt]> :: Python 64bit
    [prompt]> set PATH=%_PATH%;c:\Install\x64\Cygwin\Cygwin\AllVers\bin
    
    [prompt]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" script0.py win
    Attempting to load: ./lib/isengrad_win_064.dll
    DLL Loaded
    Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32
    
    isengrad returned 23
    
    Done.
    
    [prompt]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" script1.py win
    Attempting to load: ./lib/isengrad_win_064.dll
    DLL Loaded
    Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32
    
    
    Done.
    
    [prompt]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" script0.py cyg
    Attempting to load: ./lib/isengrad_cyg_064.dll
    DLL Loaded
    Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] 64bit on win32
    
    isengrad returned 23
    
    Done.
    
    [prompt]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" script1.py cyg
    Attempting to load: ./lib/isengrad_cyg_064.dll
    
    [prompt]>
    [prompt]> echo %errorlevel%
    -1073741819
    

可以看出,交叉编译器.exe - .dll在 7 个(共 8 个)案例中工作(崩溃于64bit Win Python with 脚本1.py),而同一个编译器在所有 8 个中都工作。

因此,我建议在使用此类环境时,尝试保留用于构建各个部分的编译器持续的 (or 兼容的至少)。


Update #0

我只是想到了事情可能出错的一个原因64bit: sizeof(long)通常有所不同(以下大小以字节为单位):

  • 4 on Win
  • 8 on Cygwin (on Nix, 一般来说)

同样的事情sizeof(long double)(这是2 * sizeof(long)).

所以,如果Cygwin .dll暴露一些long值大于2 ** 64 (1 ),它将被截断Win进程,在这种情况下可能会发生崩溃。理论上,这种情况应该也会影响相反的情况,但事实并非如此。

还有其他因素可能导致此行为,例如默认内存对齐等等。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Paradoxon:导入时 Python 的 ctypes.CDLL 会无声崩溃,但直接运行时不会崩溃 - 这怎么可能? 的相关文章

随机推荐

  • 使用水豚运行并行硒测试

    背景 我有一套Capybara针对我的 Rails 3 应用程序运行的集成测试 对于我正在使用的测试套件的其他部分Rspec 我的 Mac OSX 开发机器上有一个 selenium 2 6 0 独立服务器集线器 java jar sele
  • 使用 RoutedEventArgs 传递变量?

    我有以下类 并且想要将文本变量作为 RoutedEventArgs 传递 public class CloseableTabItem TabItem String text public CloseableTabItem This styl
  • ssh 卡在 SSH2_MSG_KEX_DH_GEX_REQUEST(1024<7680<8192) 发送上

    我可以通过网络管理器和 openconnect 插件连接到 VPN 但是当我连接到公司的服务器时 我得到以下日志 root XSign ssh ssh username xxx v OpenSSH 7 1p1 OpenSSL 1 0 2d
  • 选择另一个表中没有外键的主键

    为了简化起见 我有两个使用外键一对多关联的表 例如 Users table id name Actions table id user id 一个用户可能有很多操作 也可能没有 我需要一个 sql select 返回在 actions 表中
  • SpEL @ConditionalOnProperty 字符串属性为空或 null

    目前 我在 applications yaml 文件中的 String 属性条件下创建 dataSource bean 时遇到问题 理想情况下 我只想在 application yaml 文件中设置了 url 时才创建 dataSource
  • 类型错误:match.loader.options.plugins 不是一个函数

    我正在尝试在 ReactJS 应用程序中使用 tailwindCSS 这些是脚本命令package json file scripts start craco start build craco build test craco test
  • 如何检查字符是否正确

    我有一堆字符 想要删除所有不是 的内容 E 和 G 我尝试使用这个 if buffer get buffertest G E 但遇到了类型不兼容的问题 这个根本问题是按位 OR 运算符和 Java 运算符优先级层次结构的错误使用 这种类型的
  • 制作向后兼容的 WCF 服务

    TLDR 如何创建向后兼容的 WCF 服务 也就是说 当我在服务器端部署新版本的服务时 旧版本上的所有客户端都可以仍然使用该服务 我正在创建一个 Web 服务 允许客户端应用程序获取插件列表 我至少会进行一次手术 例如FindPlugins
  • 序列化 System.Globalization.CultureInfo 类型的对象时检测到循环引用

    我正在使用 jquery 调用一个 Web 服务 该服务返回一个包含几个表的数据集 这工作正常 直到我需要设置我的网络方法来接受参数 我在客户端反映了这一点 data paramname paramval 现在 当 webmethod 返回
  • 在一个 SQL 文件中创建多个过程?

    我想在一个 SQL 文件中创建多个过程 即 create or replace procedure pro1 as begin null end pro1 create or replace procedure pro2 as begin
  • 如何在 JavaScript 中禁用 ondblclick?

    是否可以禁用ondblclick event document ondblclick function return false 或者如果您不想在整个站点范围内这样做 document getElementById something on
  • NSUndoManager、核心数据和选择性撤消/重做

    我正在开发一个核心数据应用程序 该应用程序具有相当大的托管对象层次结构 类似于树 创建基础对象时 它会创建一些子对象 这些子对象又创建自己的子对象 依此类推 这些子对象中的每一个都可以使用 NSURLConnections 收集信息 现在
  • 当应用程序 jar 位于 hdfs 中时 Spark-submit 不起作用

    我正在尝试使用 bin spark submit 运行 Spark 应用程序 当我在本地文件系统中引用我的应用程序 jar 时 它可以工作 但是 当我将应用程序 jar 复制到 hdfs 中的目录时 出现以下异常 警告 跳过远程 jar h
  • 如何在select2下拉框中添加HTML内容

    我用过选择2插件 http ivaynberg github io select2 用于标签输入 这是我的基本工作的小提琴 http jsfiddle net learner73 LfhL33kc 1 我需要在下拉框中显示每个选项 标签的
  • 无法在 electro-forge 中使用 electro-packager 进行构建

    我已经使用 electro forge 创建了一个默认项目 当我尝试使用命令打包我的项目时electron forge 进程退出并出现以下错误 我究竟做错了什么 我按照说明来到了开球台电子锻造 https github com electr
  • 参数化类的原始使用

    我编写了一个辅助方法 用于通过反射获取指定类型的静态字段的值 代码工作正常 但我收到 参数化类的原始使用 在线警告 final List
  • 如何删除菜单栏中的表情符号、符号和听写?

    如何删除菜单栏中的表情符号 符号和听写 我在 Xcode 中找不到它 也没有在那里看到它 但在应用程序中却看到了 这是一个更好的解决方案 不需要 破解 编辑菜单文本 只需将以下方法添加到您的 AppDelegate func applica
  • 如何使用 Android Handler 更新 UI 线程中的 TextView?

    我想更新一个TextView来自 Android 应用程序中的异步任务 执行此操作的最简单方法是什么Handler 还有一些类似的问题 比如 Android 使用 Handler 更新 TextView https stackoverflo
  • 如何跟踪 SQL 更新的进度?

    假设我有一个更新 例如 UPDATE db1 sc1 tb1 SET c1 LEFT c1 LEN c1 1 WHERE c1 like 此更新基本上将遍历数百万行并修剪冒号 如果 c1 列中有冒号 我如何跟踪表中的进展情况 Thanks
  • Paradoxon:导入时 Python 的 ctypes.CDLL 会无声崩溃,但直接运行时不会崩溃 - 这怎么可能?

    所以 作为一个 Linux 爱好者 我在 Windows 上偶然发现了一些我无法解释的非常令人费解的事情 我有一个类似于此示例的项目结构 D PROJECT tolkien py init py MiddleEarth gondor py