当我释放由 CFFI 生成的 DLL 分配的 char* 时,为什么我的应用程序会崩溃?
Why my app crashes when I free a char* allocated by a DLL generated with CFFI?
我正在使用 CFFI 生成 DLL:
import cffi
ffibuilder = cffi.FFI()
ffibuilder.embedding_api('''
char* get_string();
''')
ffibuilder.set_source('my_plugin', '')
ffibuilder.embedding_init_code('''
from my_plugin import ffi, lib
@ffi.def_extern()
def get_string():
val = "string"
return lib.strdup(val.encode())
''')
ffibuilder.cdef('''
char *strdup(const char *);
''')
ffibuilder.compile(target='my-plugin.*', verbose=True)
我通过 运行 之前的脚本生成 DLL。现在,我创建了这个 C++ 代码示例来使用我的 DLL:
#include <iostream>
#include <windows.h>
typedef char* (__stdcall *get_string_t)();
int main()
{
HINSTANCE hGetProcIDDLL = LoadLibrary("my-plugin.dll");
if (!hGetProcIDDLL) {
std::cout << "could not load the dynamic library" << std::endl;
return -1;
}
get_string_t get_string = (get_string_t)GetProcAddress(hGetProcIDDLL, "get_string");
if (!get_string) {
std::cout << "could not locate the function" << std::endl;
return -1;
}
char* val = get_string();
std::cout << "Value = " << val << std::endl;
free(val); // Crash !
std::cout << "End" << std::endl;
return 0;
}
我使用 Visual Studio 2010 的编译器编译,当我 运行 我的应用程序时,它在 free
指令期间崩溃:
> cl get_string.cpp
Compilateur d'optimisation Microsoft (R) 32 bits C/C++ version 16.00.40219.01 pour 80x86
Copyright (C) Microsoft Corporation. Tous droits réservés.
get_string.cpp
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\xlocale(323) : warning C4530: Gestionnaire d'exceptions C++ utilisé, mais les sémantiques de déroulement n'ont pas été activées. Spécifiez /EHsc
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
/out:get_string.exe
get_string.obj
> get_string.exe
Value = string
我遵循 中给出的指示。我应该怎么做才能释放内存并避免我的应用程序崩溃?事实上,如果我删除 free
指令,我的应用程序运行良好,但它不是一个干净的解决方案。
在一个地方分配然后跨 DLL 边界释放是一种危险的做法。除非您知道自己做对了(相同的 CRT 版本等),否则请避免使用它。 Thus spake Microsoft:
When you pass C Run-time (CRT) objects such as file handles, locales, and environment variables into or out of a DLL (function calls across the DLL boundary), unexpected behavior can occur if the DLL, as well as the files calling into the DLL, use different copies of the CRT libraries.
A related problem can occur when you allocate memory (either explicitly with new or malloc, or implicitly with strdup, strstreambuf::str, and so on) and then pass a pointer across a DLL boundary to be freed. This can cause a memory access violation or heap corruption if the DLL and its users use different copies of the CRT libraries.
一个解决方案是从你的 DLL 中公开一个 free
函数,它正在转换一个分配的对象,这样客户端就可以调用你的 free
函数,或者在 C++ 中你可以使用带有自定义删除器的智能指针可以正确执行此操作。
我正在使用 CFFI 生成 DLL:
import cffi
ffibuilder = cffi.FFI()
ffibuilder.embedding_api('''
char* get_string();
''')
ffibuilder.set_source('my_plugin', '')
ffibuilder.embedding_init_code('''
from my_plugin import ffi, lib
@ffi.def_extern()
def get_string():
val = "string"
return lib.strdup(val.encode())
''')
ffibuilder.cdef('''
char *strdup(const char *);
''')
ffibuilder.compile(target='my-plugin.*', verbose=True)
我通过 运行 之前的脚本生成 DLL。现在,我创建了这个 C++ 代码示例来使用我的 DLL:
#include <iostream>
#include <windows.h>
typedef char* (__stdcall *get_string_t)();
int main()
{
HINSTANCE hGetProcIDDLL = LoadLibrary("my-plugin.dll");
if (!hGetProcIDDLL) {
std::cout << "could not load the dynamic library" << std::endl;
return -1;
}
get_string_t get_string = (get_string_t)GetProcAddress(hGetProcIDDLL, "get_string");
if (!get_string) {
std::cout << "could not locate the function" << std::endl;
return -1;
}
char* val = get_string();
std::cout << "Value = " << val << std::endl;
free(val); // Crash !
std::cout << "End" << std::endl;
return 0;
}
我使用 Visual Studio 2010 的编译器编译,当我 运行 我的应用程序时,它在 free
指令期间崩溃:
> cl get_string.cpp
Compilateur d'optimisation Microsoft (R) 32 bits C/C++ version 16.00.40219.01 pour 80x86
Copyright (C) Microsoft Corporation. Tous droits réservés.
get_string.cpp
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\xlocale(323) : warning C4530: Gestionnaire d'exceptions C++ utilisé, mais les sémantiques de déroulement n'ont pas été activées. Spécifiez /EHsc
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
/out:get_string.exe
get_string.obj
> get_string.exe
Value = string
我遵循 free
指令,我的应用程序运行良好,但它不是一个干净的解决方案。
在一个地方分配然后跨 DLL 边界释放是一种危险的做法。除非您知道自己做对了(相同的 CRT 版本等),否则请避免使用它。 Thus spake Microsoft:
When you pass C Run-time (CRT) objects such as file handles, locales, and environment variables into or out of a DLL (function calls across the DLL boundary), unexpected behavior can occur if the DLL, as well as the files calling into the DLL, use different copies of the CRT libraries.
A related problem can occur when you allocate memory (either explicitly with new or malloc, or implicitly with strdup, strstreambuf::str, and so on) and then pass a pointer across a DLL boundary to be freed. This can cause a memory access violation or heap corruption if the DLL and its users use different copies of the CRT libraries.
一个解决方案是从你的 DLL 中公开一个 free
函数,它正在转换一个分配的对象,这样客户端就可以调用你的 free
函数,或者在 C++ 中你可以使用带有自定义删除器的智能指针可以正确执行此操作。