我有一个项目运行在Python3.8x64Windows上,有一个anaconda环境。
它使用了一个内部C++ +dll x64,使用了CyType的Python包装(C++代码由于各种原因不能共享;最重要的原因是我自己不能访问99%的代码)。
该项目在Python3.8x64上运行良好。
主C++ DLL入口点为JSON文件获取一个字符串路径进行初始化。
class RedactedName:
def __init__(some_args): # redacted
# some code
self.dll = ctypes.CDLL(self.dll_path)
self.dll.load_config_file.argtypes = [ctypes.c_char_p]
self.dll.load_config_file.restype = ctypes.c_bool
# some code
def load_config_file(self, file_path: str) -> bool:
self.conf_file_path = file_path # path of a JSON file, the C++ will load it
s = ctypes.c_char_p(file_path.encode('utf-8'))
b = self.dll.load_config_file(s) # <-- SEG FAULT HERE with py3.6.6 only. ok with py3.8
return bool(b)
bool load_config_file(/* in */ const char * input_json) {
json conf;
const std::string & json_file_path_str(input_json);
REDACTED_FUNCTION_CALL_A(/* in */ json_file_path_str, /* out */ conf); // <-- seg fault in here, depending on python version, see GDB call stack
// other stuff
return true; // no joke
}
使用Python 3.6。6 x64,C++调用
self.dll.load_config_file
进入
SIGSEGV
C++ DLL调用堆栈看起来像这样(幸运地,DLL包含一些符号)。
# (with python breakpoint right before DLL load)
$ attach <ptyhon process PID>
$ catch load mylibname
$ catch throw
$ c
# now, resume python execution
# when gdb breaks on dll load, add breakpoint
$ b load_config_file
(gdb) n
Single stepping until exit from function
_ZN3REDACTED5REDACTED8REDACTED4REDACTEDKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERN8nlohmann10basic_jsonISt3mapSt6vectorS7_bxydSaNSA_14adl_serializerEEE,
which has no line number information.
Thread 1 received signal SIGSEGV, Segmentation fault.
0x000000006fcd2194 in __gnu_cxx::__exchange_and_add (__val=-1, __mem=0x0) at /it/sbx/20.1/x86_64-windows/gcc/build/x86_64-pc-mingw32/libstdc++-v3/include/ext/atomicity.h:82
82 /it/sbx/20.1/x86_64-windows/gcc/build/x86_64-pc-mingw32/libstdc++-v3/include/ext/atomicity.h: No such file or directory.
#0 0x000000006fcd2194 in __gnu_cxx::__exchange_and_add (__val=-1, __mem=0x0) at /it/sbx/20.1/x86_64-windows/gcc/build/x86_64-pc-mingw32/libstdc++-v3/include/ext/atomicity.h:82
#1 __gnu_cxx::__exchange_and_add_dispatch (__val=-1, __mem=0x0) at /it/sbx/20.1/x86_64-windows/gcc/build/x86_64-pc-mingw32/libstdc++-v3/include/ext/atomicity.h:82
#2 __gnu_cxx::__exchange_and_add_dispatch (__val=-1, __mem=0x0) at /it/sbx/20.1/x86_64-windows/gcc/build/x86_64-pc-mingw32/libstdc++-v3/include/ext/atomicity.h:78
#3 std::locale::_Impl::_M_remove_reference (this=0x0) at /it/sbx/20.1/x86_64-windows/gcc/build/x86_64-pc-mingw32/libstdc++-v3/include/bits/locale_classes.h:564
#4 std::locale::operator= (this=this@entry=0xbc8f3ecf50, __other=
@0xbc8f3ec9f8: {static none = 0, static ctype = 1, static numeric = 2, static collate = 4, static time = 8, static monetary = 16, static messages = 32, static all = 63, _M_impl = 0x6fcfc6a0 <(anonymous namespace)::c_locale_impl>, static _S_classic = 0x6fcfc6a0 <(anonymous namespace)::c_locale_impl>, static _S_global = 0x6fcfc6a0 <(anonymous namespace)::c_locale_impl>, static _S_categories = 0x6fd06aa0 <__gnu_cxx::category_names>, static _S_once = {done = 1, started = 0}, static _S_twinned_facets = 0x6fd09140 <std::locale::_S_twinned_facets>}) at ../../../../../src/libstdc++-v3/src/c++98/locale.cc:116
#5 0x000000006fce906c in std::ios_base::_M_init (this=this@entry=0xbc8f3ece80) at ../../../../../src/libstdc++-v3/src/c++98/ios_locale.cc:44
#6 0x000000006fceaf91 in std::basic_ios<char, std::char_traits<char> >::init (this=0xbc8f3ece80, __sb=0x0) at /it/sbx/20.1/x86_64-windows/gcc/build/x86_64-pc-mingw32/libstdc++-v3/include/bits/basic_ios.tcc:126
#7 0x000000006ce0b7fb in REDACTED_FUNCTION_CALL_A(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long long, unsigned long long, double, std::allocator, nlohmann::adl_serializer>&) ()
from C:\Users\<redacted>/<redacted>_lib.dll
#8 0x000000006cdc15c6 in load_config_file (input_json=0x27eee2877a0 "C:\\Users\\<a_valid_path_to_avalid_json_file>") at <redacted>
#9 0x0000000062880943 in _ctypes!DllCanUnloadNow () from C:\Program Files\Python36\DLLs\_ctypes.pyd
其他信息:
-
python调用完全是顺序的,没有线程,因此
s
变量不能在结束前进行垃圾回收
自己dll。加载配置文件
-
C++ DLL没有被重新编译。它与Py3中的完全相同。8环境。
-
C++ DLL依赖于
libwinpthread-1.dll
libgcc_s_seh-1.dll
libstdc++-6.dll
-
DLL是用GNAT PRO构建的
g++ (GCC) 8.3.1 20190923 (for GNAT Pro 20.1)
-
-
DLL链接指令包含“-static libgcc”
任何关于为什么py3的线索。6.6给出了这个奇怪的cpp调用堆栈&SIGSEGV和py3。8不是吗?
如果是py3。8版本不起作用,我想它是
undefined reference to `__gnu_cxx::__exchange_and_add(int*, int)'
.
但事实并非如此。。。
-
std::basic_ios<char, std::char_traits<char> >::init
似乎与流缓冲区初始化有关。可能是std输出流的冲突吗?不太可能
-
蟒蛇3。6搞乱环境变量?不太可能
-
蟒蛇3。6搞乱DLL,造成依赖冲突?不大可能发生的
据我所知,这些问题是无关的,因为函数返回类型是bool,并且与源代码精确一致。和py3。8个工程。
Python变更日志
3.6
3.7
3.8
更新2021-11-04 1
我曾经
catch load my_lib
和
catch load libstdc++
当使用Py3时。8 AcANDA环境,我的C++ DLL被加载(第一个catch),然后它加载LIbSTDc++ + 6。动态链接库
它就存储在它旁边
使用pip venv Py3时。6,加载我的C++ DLL(第一个catch),
然后加载libstdc++-6。dll来自我的GNAT安装(第二个捕获)
.
到目前为止,这可能是我能发现的主要区别。
我非常确定2 libstdc++有不同的版本。
python的内容
os.environ
两个环境之间的输出略有不同,主要是由于环境变量
LIBRARY_ROOTS
指向相应的python安装库文件夹(在这两种情况下
PATH
尝试不同的
libstdc++-6。动态链接库
libstdc++-6。动态链接库
版本对观察到的行为没有影响。
看起来Python版本更改了DLL的执行。