OpenCL 在调用 clGetPlatformIDs 时崩溃

Posted

技术标签:

【中文标题】OpenCL 在调用 clGetPlatformIDs 时崩溃【英文标题】:OpenCL crashes on call to clGetPlatformIDs 【发布时间】:2014-08-29 18:48:08 【问题描述】:

我是 OpenCL 的新手。在配备 Intel(R) HD Graphics 4000、运行 Windows 7 的 Core i5 机器上工作。我安装了支持 OpenCL 的最新 Intel 驱动程序。 GpuCapsViewer 确认我有 OpenCL 支持设置。我使用英特尔 OpenCL SDK 开发了一个简单的 HelloWorld 程序。我成功编译了程序,但是在运行时,它在调用 clGetPlatformIDs() 时崩溃,并出现分段错误。这是我的代码:

#include <iostream>
#include <CL/opencl.h>

int main() 
    std::cout << "Test OCL  without driver" << std::endl;

    cl_int err;
    cl_uint num_platforms;

    err = clGetPlatformIDs(0, NULL, &num_platforms);
    if (err == CL_SUCCESS) 
        std::cout << "Success. Platforms available: " << num_platforms
                << std::endl;
     else 
        std::cout << "Error. Platforms available: " << num_platforms
                << std::endl;
    

    std::cout << "Test OCL without driver" << std::endl;
    std::cout << "Press button to exit." << std::endl;
    std::cin.get();
    return 0;

GpuCapsViewer 怎么会成功确认 OpenCL 支持并可以使用它来运行它的演示,但我无法运行我的代码?两者必须使用相同的功能,对吧?

这几天一直在努力。甚至尝试重新安装驱动程序。有什么想法吗?

GpuCapsViewer 说:

驱动程序:R295.93 (r295_00-233) / 10.18.10.3496 (3-11-2014) OpenGL:OpenGL 4.2(GeForce GT 630M/PCIe/SSE2 with 290 ext.) OPENCL:OpenCL 1.1,GeForce GT 630M 计算单元:2@950MHz CUDA:GeForce GT 630M CC:2.1,多处理器:2@950MHz PHYSX:GPU PhysX (NVIDIA GeForce GT 630M) 多 GPU:不支持多 GPU(2 个物理 GPU)


更新:

编译行:

g++ -I"C:\Program Files (x86)\Intel\OpenCL SDK\4.4\include" -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"Test3.d" -MT"Test3.d" -o "Test3.o" "../Test3.cpp"
Finished building: ../Test3.cpp

链接器行:

g++ -L"C:\Program Files (x86)\Intel\OpenCL SDK\4.4\lib\x64" -o "TestOpenCL"  ./HelloWorld.o ./HelloWorld2.o ./Test3.o   -lOpenCL
Finished building target: TestOpenCL

操作系统:Windows 7 Ultimate 版本 6.1(内部版本 7601:Service Pack 1)


更新 2,崩溃信息:

Problem Event Name: APPCRASH
Application Name:   TestOpenCL.exe
Application Version:    0.0.0.0
Application Timestamp:  53bc6ac5
Fault Module Name:  TestOpenCL.exe
Fault Module Version:   0.0.0.0
Fault Module Timestamp: 53bc6ac5
Exception Code: c0000005
Exception Offset:   0000000000002cc0
OS Version: 6.1.7601.2.1.0.256.1
Locale ID:  1033
Additional Information 1:   56e3
Additional Information 2:   56e3743a8a234df3bdeba0b507471c44
Additional Information 3:   8fe0
Additional Information 4:   8fe0ef5706153941955de850e5612393

更新 3:

使用 DependencyWalker(http://dependencywalker.com/) 作为垃圾箱的替代品。它会生成以下警告:

Warning: At least one delay-load dependency module was not found.
Warning: At least one module has an unresolved import due to a missing export function in a delay-load dependent module.

警告似乎是指以下 DLL,这些 DLL 均标有“打开文件时出错。系统找不到指定的文件 (2)”错误消息。

API-MS-WIN-CORE-COM-L1-1-0.DLL
API-MS-WIN-CORE-WINRT-ERROR-L1-1-0.DLL
API-MS-WIN-CORE-WINRT-L1-1-0.DLL
API-MS-WIN-CORE-WINRT-ROBUFFER-L1-1-0.DLL
API-MS-WIN-CORE-WINRT-STRING-L1-1-0.DLL
API-MS-WIN-SHCORE-SCALING-L1-1-0.DLL
DCOMP.DLL
IESHIMS.DLL

更新 4,GDB 回溯:

Program received signal SIGSEGV, Segmentation fault.
0x0000000000402cc0 in clGetPlatformIDs ()
(gdb) backtrace full
#0  0x0000000000402cc0 in clGetPlatformIDs ()
No symbol table info available.
#1  0x0000000000402af3 in main () at ../Test3.cpp:11
        err = 0
        num_platforms = 0
        platform = 0x0

(gdb) backtrace
#0  0x0000000000402cc0 in clGetPlatformIDs ()
#1  0x0000000000402af3 in main () at ../Test3.cpp:11

更新 5,GDB DISASS:

(gdb) disass
Dump of assembler code for function clGetPlatformIDs:
=> 0x0000000000402cc0 <+0>:     jmpq   *0x4b74e8(%rip)        # 0x8ba1ae
   0x0000000000402cc6 <+6>:     nop
   0x0000000000402cc7 <+7>:     nop
End of assembler dump.

更新 6,共享 GDB 信息:

(gdb) INFO SHARED
From                To                  Syms Read   Shared Object Library
0x0000000077191000  0x00000000773384e0  Yes (*)     C:\Windows\system32\ntdll.dll
0x0000000077071000  0x000000007718eab4  Yes (*)     C:\Windows\system32\kernel32.dll
0x000007fefc081000  0x000007fefc0eb13c  Yes (*)     C:\Windows\system32\KernelBase.dll
0x000007fedf8d1000  0x000007fedf8e96aa  Yes (*)     C:\Windows\system32\OpenCL.dll
0x000007fefe101000  0x000007fefe1da628  Yes (*)     C:\Windows\system32\advapi32.dll
0x000007fefe061000  0x000007fefe0fe4bc  Yes (*)     C:\Windows\system32\msvcrt.dll
0x000007fefdcc1000  0x000007fefdcde39a  Yes (*)     C:\Windows\SYSTEM32\sechost.dll
0x000007fefc6a1000  0x000007fefc7cc914  Yes (*)     C:\Windows\system32\rpcrt4.dll
(*): Shared library is missing debugging information.

二进制文件、x64 和包含文件夹:

https://drive.google.com/file/d/0BxKA63T2GnKMRW02QWZnam5lSGM/edit?usp=sharing

更新 7,GPUcaps 情况:

GPUcaps 检测到 2 个 GPU:

GPU 1:Intel(R) HD Graphics 4000 GPU 2:NVIDIA GeForce GT 630M

你可以在这里看到截图:

https://drive.google.com/file/d/0BxKA63T2GnKMa00tU1gydGNJeXc/edit?usp=sharing

更新 8:

根据@antiduh 的回答,我一直在尝试直接链接 Windows\System32 文件夹中的 OpenCL.dll。我正在使用mingw64。我明白了:

Invoking: Cross G++ Linker
g++ -L"C:\Windows\System32" -o "TestOpenCL"  ./HelloWorld.o ./HelloWorld2.o ./Test3.o   -lOpenCL
d:/ws/apps_inst/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/4.7.1/../../../../x86_64-w64-mingw32/bin/ld.exe: skipping incompatible C:\Windows\System32/OpenCL.dll when searching for -lOpenCL
d:/ws/apps_inst/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/4.7.1/../../../../x86_64-w64-mingw32/bin/ld.exe: skipping incompatible C:\Windows\System32/OpenCL.dll when searching for -lOpenCL
d:/ws/apps_inst/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/4.7.1/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find -lOpenCL
d:/ws/apps_inst/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/4.7.1/../../../../x86_64-w64-mingw32/bin/ld.exe: skipping incompatible C:\Windows\System32/msvcrt.dll when searching for -lmsvcrt
d:/ws/apps_inst/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/4.7.1/../../../../x86_64-w64-mingw32/bin/ld.exe: skipping incompatible C:\Windows\System32/advapi32.dll when searching for -ladvapi32
d:/ws/apps_inst/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/4.7.1/../../../../x86_64-w64-mingw32/bin/ld.exe: skipping incompatible C:\Windows\System32/shell32.dll when searching for -lshell32
d:/ws/apps_inst/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/4.7.1/../../../../x86_64-w64-mingw32/bin/ld.exe: skipping incompatible C:\Windows\System32/user32.dll when searching for -luser32
d:/ws/apps_inst/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/4.7.1/../../../../x86_64-w64-mingw32/bin/ld.exe: skipping incompatible C:\Windows\System32/kernel32.dll when searching for -lkernel32
d:/ws/apps_inst/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/4.7.1/../../../../x86_64-w64-mingw32/bin/ld.exe: skipping incompatible C:\Windows\System32/msvcrt.dll when searching for -lmsvcrt

更新 9: 我现在可以使用以下行手动编译、链接和运行示例代码。

g++ -I. s.cpp -L. -lOpenCL

我简化了所有内容,而且效果很好。这显然与 Eclipse 使用的编译和链接命令有很大不同。知道eclipse使用的哪些参数会导致问题吗?还有,为什么 eclipse 先编译成目标文件,然后再尝试链接它们,分两个独立的步骤?

【问题讨论】:

我在黑暗中刺伤,但是:在第一次调用 clGetPlatformIds 之前,num_platforms 是否需要设置为零?您是否需要在传递之前将“0”第一个参数强制转换为 cl_int?您的编译行和链接器行是什么?您是否链接到正确版本的库? 您使用的是什么平台(Windows、Linux)?如果是 linux,ldd 对你的二进制文件有什么看法?这与您希望从哪里获取库相符吗? 可能在这里找到了您的答案:***.com/a/20033042/344638 - 您的 OpenCL SDK 可能无法处理 (0, NULL, &amp;numPlatforms) 的情况。尝试使用对 cl_platform_id 变量的有效引用执行它,例如 clGetPlatformIDs(1, &amp;platform, &amp;numPlatforms) 并查看它是否正确设置了 numPlatforms。 @antiduh 使用 Windows。尝试将 num_platforms 设置为 0,并且还传递了有效的 cl_platform_id 引用。不用找了。用您要求的信息更新了问题。感谢您的宝贵时间。 您的代码在我看来是正确的(我使用 OpenCL)。 antiduh 的评论可能很好,但规范很清楚0, NULL, &amp;num_platforms 应该是函数的好参数。您能否显示实际的崩溃信息(例如,“错误”地访问了哪个地址?)[注意,我不为英特尔工作,因此对他们的产品没有特别的了解] 【参考方案1】:

程序使用外部库共有三种方式:

静态链接:直接将库插入到您的可执行文件中。以 .lib 文件形式呈现的外部库仅包含打包的 .obj 文件。您的程序照常调用库中的函数。编译器从 lib 中提取可执行代码,将其插入,并对它执行完整、完整的链接。就好像您针对导入的函数进行编译一样,就像它们来自您自己的源代码一样。 加载时动态链接,又称“隐式链接”:启动程序时加载库。外部库以包含可执行代码的 .dll 和包含 .dll 导出的 .lib 文件的形式呈现,由编译器和链接器临时链接。链接器使用 .lib 来了解如何在运行时调用 .dll,并将延迟绑定放入您的程序中。当操作系统启动您的程序时,它会执行“加载时”链接 - 它查找所有延迟绑定,尝试查找 .dll 文件,完成程序中延迟绑定的链接,并允许您运行文件。 “纯”运行时动态链接,又名“显式链接”:直接调用 LoadLibrary。您的程序没有对任何 .lib、.dll 或其他文件的特定引用。您的程序开始运行,它本身使用 .dll 的字符串路径调用 LoadLibrary。 LoadLibrary 将 .dll 合并到您的虚拟内存中,然后您的程序调用 GetProcAddress 以获取指向您要调用的函数的函数指针。然后,您可以使用该函数指针进行调用。

您通常无法链接到没有 .lib 的 dll。编译器希望将这些函数调用引用解析为真实地址,但我们不想放入真实地址,因为我们希望将 DLL 加载到任意内存地址(DLL 是“可重定位的”)。

据我了解,用作导入库的 .lib 包含主程序直接链接的存根 - 因此程序中的所有调用都通过存根。然后存根引用“导入地址表”。当操作系统将 DLL 加载到进程的内存中时,它通过填写 IAT 来完成。然后存根通过进行引用正确的间接跳转来调用 DLL在 IAT 中的插槽。

因此,如果 DLL MathLib 具有我的 exe 正在导入的导出函数 Factorial,则导入 .lib 文件具有我的 exe 静态编译的实际函数 Factorial。该 .lib 中的 Factorial 看起来像以下伪代码:

int Factorial( int value )  
   // Read MathLib's IAT which should always be at address 0x8ba100.
   // Factorial's real address gets stored in slot 2, so add 8 to the address
   // to read from.
   __asm jmp *0x8ba108; // nb this is an indirect jump.

然后我们希望当操作系统加载那个 DLL 时,那个 IAT 被正确填写,否则我们就一头雾水。

所以我认为发生的事情是您正在针对一个 .lib 进行编译,但“加载时间”链接到了错误的 opencl.dll。 IAT 从来没有被创建,或者是在错误的地方创建的,所以你跳进了虚无;这就是为什么这一行会产生段错误:

0x0000000000402cc0 &lt;+0&gt;: jmpq *0x4b74e8(%rip) # 0x8ba1ae

让我们弄清楚为什么我们链接错误。您的计算机上可能有 3 组 opencl.dll/opencl.lib 文件:

来自 Kronos 的 opencl.lib/dll,实际上只是一个 stub/loader 库,用于确定您计算机上的真实提供程序,并将函数调用调度到实际正确的库。 来自英特尔 SDK 和驱动程序的 opencl.lib/dll。 来自 Nvidia 驱动程序的 opencl.lib/dll。

您实际上拥有这些文件中的哪些?因此,我的估计是:

来自 kronos 的 opencl.dll 已安装到 c:\windows\system32。 没有来自 Kronos 的 opencl.lib 可能没有来自 nvidia 的 opencl.lib,因为您没有安装他们的 SDK。 您可能已经安装了英特尔的 opencl.lib 和 opencl.dll,因为您确实安装了他们的 SDK。

您肯定链接到 Intel opencl.lib,但似乎正在加载 c:\windows\system32 中的 Kronos opencl.dll。一种解决方案是让程序在运行程序时加载英特尔 opencl.dll,方法是将它们的 dll 放在程序的目录中。

但是,您声明您能够使用此编译行使事情正常进行:

g++ -I. s.cpp -L. -lOpenCL

Windows 上的 gcc 有一些巧妙之处 - in order to link against a library, you don't need to have the .lib。 Gcc 通过检查 dll 为您计算出来;当有人给他们一个dll但没有lib时,其他人已经想出了如何做同样的事情。在大多数其他编译器中,尤其是 Visual Studio,您需要有一个 .lib 和一个 .dll 来链接某些东西。这就是 Win SDK 安装数百个 .lib(例如,kernel32.lib)的原因。事实证明,编译器实际上可以根据需要推断它,但库作为一种古老的机制存在。

不管怎样,你在 gcc 链接行上面运行了它,它使用搜索路径找到了一个合适的 opencl.dll,为它发明了自己的 .lib,并针对它进行了编译;您启动了程序,它使用相同的搜索路径来获取 opencl.dll,它与您编译的相同,因此您的程序运行。呼。

我还有一些建议:

查找来自 Kronos 的 "Installable Client Driver" ICD Loader 的 opencl.lib 和 opencl.dll 对。然后,该加载程序将确定如何在运行时绑定到特定的提供程序(nvidia、intel 等)。 将 Kronos opencl.dll 与您的应用程序一起分发,这样您就不会意外地运行时链接到错误的文件。 卸载英特尔 SDK,假设它提供特定于英特尔的 opencl.lib/opencl.dll 文件。

关于 libs 和 dll 的一些更相关的问题:

When building a DLL file, does the generated LIB file contain the DLL name? Why are LIB files beasts of such a duplicitous nature?

【讨论】:

程序是否应该链接到 ICD 加载程序 OpenCL.dll 而不是任何供应商版本? @Spudd86 - 这样最好。 所有 OpenCL.dll 都应该真正与 ABI 兼容,否则它有点违背 OpenCL 的目的...... 啊,多个 OpenCL 提供程序 - 我在工作中通常不会遇到的问题! ;) @antiduh 我检查了,您在编辑 2 中是对的。但是,我无法链接到 System32 文件夹中存在的 ICD DLL。我已经用新信息更新了我的原始答案。

以上是关于OpenCL 在调用 clGetPlatformIDs 时崩溃的主要内容,如果未能解决你的问题,请参考以下文章

GPU 中的并行性 - CUDA / OpenCL

带有 clSetKernelArg 的 OpenCL 竞争条件

从opencl内核中调用一个具有通过值概念的函数。

OpenCL 管道

面向OPENCL的ALTERA SDK

OpenCL中不允许使用可变长度数组声明 - 为什么?