调用 DLL 函数的 SEGFAULT

SEGFAULT calling DLL functions

本文关键字:SEGFAULT 函数 DLL 调用      更新时间:2023-10-16

我目前正在尝试编辑一个已经使用 OpenCL.dll 的项目,以使其动态加载库。我希望能够在任何 OpenCL 系统中使用它,只有错误消息和禁用功能。

首先,我在函数中添加了一些包装器。(此代码位于C++类中,并且是公共的)

    typedef cl_int h_clGetPlatformIDs(cl_uint, cl_platform_id *, cl_uint *);
    typedef cl_int h_clGetDeviceIDs(cl_platform_id, cl_device_type, cl_uint, cl_device_id *, cl_uint *);
    typedef cl_context h_clCreateContext(cl_context_properties *, cl_uint, const cl_device_id *, void *(const char *, const void *, size_t, void *), void *, cl_int *);
    typedef cl_command_queue h_clCreateCommandQueue(cl_context, cl_device_id, cl_command_queue_properties, cl_int *);
    typedef cl_program h_clCreateProgramWithSource(cl_context, cl_uint, const char **, const size_t *, cl_int *);
    typedef cl_int (CALLBACK h_clBuildProgram)(cl_program, cl_uint,const cl_device_id *, const char *, void (*)(cl_program, void * ), void * ) CL_API_SUFFIX__VERSION_1_0;
    typedef cl_int h_clGetProgramBuildInfo(cl_program, cl_device_id, cl_program_build_info, size_t, void  *, size_t  *);
    typedef cl_kernel h_clCreateKernel(cl_program, const char *, cl_int *);
    typedef cl_mem h_clCreateBuffer(cl_context, cl_mem_flags, size_t, void *, cl_int *);
    typedef cl_int h_clEnqueueWriteBuffer(cl_command_queue, cl_mem, cl_bool, size_t, size_t, const void *, cl_uint, const cl_event *, cl_event *);
    typedef cl_int h_clSetKernelArg(cl_kernel, cl_uint, size_t, const void *);
    typedef cl_int h_clEnqueueNDRangeKernel(cl_command_queue, cl_kernel, cl_uint, const size_t*, const size_t*, const size_t*, cl_uint, const cl_event*, cl_event*);
    typedef cl_int h_clFlush(cl_command_queue);
    typedef cl_int h_clEnqueueReadBuffer(cl_command_queue, cl_mem, cl_bool, size_t, size_t, void *, cl_uint, const cl_event *, cl_event *);
    typedef cl_int h_clWaitForEvents(cl_uint, const cl_event *);
    typedef cl_int h_clReleaseMemObject(cl_mem );
    typedef cl_int h_clReleaseEvent(cl_event );
    typedef cl_int h_clReleaseProgram(cl_program );
    typedef cl_int h_clReleaseKernel(cl_kernel);
    typedef cl_int h_clReleaseCommandQueue(cl_command_queue );
    typedef cl_int h_clReleaseContext(cl_context );
    h_clGetPlatformIDs* clGetPlatformIDs;
    h_clGetDeviceIDs* clGetDeviceIDs;
    h_clCreateContext* clCreateContext;
    h_clCreateCommandQueue* clCreateCommandQueue;
    h_clCreateProgramWithSource* clCreateProgramWithSource;
    h_clBuildProgram* clBuildProgram;
    h_clGetProgramBuildInfo* clGetProgramBuildInfo;
    h_clCreateKernel* clCreateKernel;
    h_clCreateBuffer* clCreateBuffer;
    h_clEnqueueWriteBuffer* clEnqueueWriteBuffer;
    h_clSetKernelArg* clSetKernelArg;
    h_clEnqueueNDRangeKernel* clEnqueueNDRangeKernel;
    h_clFlush* clFlush;
    h_clEnqueueReadBuffer* clEnqueueReadBuffer;
    h_clWaitForEvents* clWaitForEvents;
    h_clReleaseMemObject* clReleaseMemObject;
    h_clReleaseEvent* clReleaseEvent;
    h_clReleaseProgram* clReleaseProgram;
    h_clReleaseKernel* clReleaseKernel;
    h_clReleaseCommandQueue* clReleaseCommandQueue;
    h_clReleaseContext* clReleaseContext;

有了这个,我可以直接为处理程序分配 GetProcAdress 返回的内容,然后只需调用函数(当然,我首先加载 DLL)。

    clReleaseContext = (h_clReleaseContext*) GetProcAddress(ocl_lib_handle, "clReleaseContext");

还有一个常见的调用示例:

clReleaseContext((cl_context)context);

但我总是得到SEGFAULT称这个:

clBuildProgram(program, 0, NULL, "-cl-fast-relaxed-math", NULL, NULL);

这很奇怪,因为所有其他工作正常。我在这里发布clBuildProgram Args作为参考:

    extern CL_API_ENTRY cl_int CL_API_CALL
clBuildProgram(cl_program           /* program */,
               cl_uint              /* num_devices */,
               const cl_device_id * /* device_list */,
               const char *         /* options */, 
               void (*pfn_notify)(cl_program /* program */, void * /* user_data */),
               void *               /* user_data */) CL_API_SUFFIX__VERSION_1_0;

谢谢!

您的 typedef 必须 OpenCL 标头中的声明完全匹配。 他们没有,你不使用CL_API_ENTRY,CL_API_CALL。 我在第一个参数的原始声明中没有看到回调。

这当然是编写和维护的可怕代码。 否则,clBuildProgram() 函数有很多机会在没有您帮助的情况下轰炸访问违规。 首先用一个测试程序来消除它,让你的主代码正常运行。 乞求、偷窃或借用以利用 MSVC 链接器的/DELAYLOAD 功能。

每次调用GetProcAddress时,都应检查 NULL 的返回值,以查看是否能够在 DLL 中找到该函数。

如果在尝试加载 clBuildProgram 时返回 NULL,则函数名称查找有问题。

如果它为您提供了一个有效的指针,但 typedef 与签名不完全匹配,那么您会将损坏的数据传递给函数,并可能崩溃。

如果你有一个有效的指针和一个正确的 typedef,那么也许你只是在传递糟糕的参数? 我在您对clBuildProgram的调用中看到了很多0NULL - 也许错误在 CL 代码中? 当您直接调用它时(即没有动态加载),您可以使用相同的参数调用clBuildProgram吗?

一个可能的 int :您正在合并 C 和 C++ 中没有相同 ABI 的 API,从二进制传递参数是不同的(这就是为什么在 C++ 中使用 C 时,您需要使用

extern "C" {
  #include "c_api.h"
}

你应该谷歌一下这个主题,看看如何使用 C 调用约定/重整强制调用你的处理程序。不确定这是解决方案,但绝对值得调查,因为它会导致这个确切的结果。

我不知道究竟

是什么解决了这个问题。我用 OpenCL 文件的 typedefs 副本重写了一个 .h 文件,并添加了 __stdcall。然后使用外部"C"。

并且正在工作!!

谢谢大家!