如何解决Nm 在共享库中显示符号,但链接时未定义符号
作为一种心理练习,我正在尝试编写一个直接链接到我的 Macbook Pro 的 GPU 驱动程序的程序,而不是使用 Apple 的 Metal 框架。一些探索使我找到了这个文件(大概特定于我的特定硬件):
/System/Library/Extensions/AMDRadeonX6000MTLDriver.bundle/Contents/MacOS/AMDRadeonX6000MTLDriver
在其上运行 file
确认这是一个 Mach-O 64 位动态链接共享库。
在它上面运行 nm
告诉我它是 AMD ROCr 运行时的超集。我特别感兴趣的一个符号是这个:
$ nm -gD AMDRadeonX6000MTLDriver | grep "hsa_init"
00000000001cca20 T __ZN3HSA8hsa_initEv
$ nm -gCD AMDRadeonX6000MTLDriver | grep "hsa_init"
00000000001cca20 T HSA::hsa_init()
所以我写了这个简单的程序(rocr_test.cpp
):
typedef int hsa_status_t;
namespace HSA {
hsa_status_t hsa_init();
}
int main() {
HSA::hsa_init();
return 0;
}
然后像这样编译:
$ clang++ rocr_test.cpp -c
$ clang++ rocr_test.o /System/Library/Extensions/AMDRadeonX6000MTLDriver.bundle/Contents/MacOS/AMDRadeonX6000MTLDriver
Undefined symbols for architecture x86_64:
"HSA::hsa_init()",referenced from:
_main in rocr_main-95c854.o
ld: symbol(s) not found for architecture x86_64
clang-11: error: linker command Failed with exit code 1 (use -v to see invocation)
但是,目标文件上的 nm
显示链接器应该寻找具有相同名称的符号:
$ nm rocr_test.o
U __ZN3HSA8hsa_initEv
0000000000000000 T _main
为什么当 nm
显示共享库中明确存在具有此确切名称的符号时,我会看到此链接器错误?
解决方法
如果 has_init
不是类的一部分,那么您仍然可以通过它的错误名称调用该函数。但是,这仅在它是免费功能时才有效。如果它是类的一部分,那么没有类定义就不能真正调用它,因为您不知道它对类成员做了什么,并且必须将对象作为第一个参数传递。
#include <iostream>
#include <dlfcn.h>
using namespace std;
typedef int hsa_status_t;
typedef hsa_status_t (*hsa_init_t)();
hsa_init_t hsa_init;
const char *hsa_init_name = "__ZN3HSA8hsa_initEv";
const char *libPath = "/System/Library/Extensions/AMDRadeonX6000MTLDriver.bundle/Contents/MacOS/AMDRadeonX6000MTLDriver";
int main()
{
void *libraryHandle = dlopen(libPath,RTLD_NOW);
if (!libraryHandle)
{
cout << "Error opening library: " << libPath << " Error: " << dlerror() << endl;
return 0;
}
dlerror(); // clear any existing error
hsa_init = (hsa_init_t)dlsym(libraryHandle,hsa_init_name);
if (!hsa_init)
{
cout << "Error importing symbol: " << hsa_init_name << " Error: " << dlerror() << endl;
return 0;
}
hsa_init();
return 0;
}
,
Apple 的编译器有点不同,为了与库链接,它需要使用“.tbd”文件。这是一个文本文件,包含符号列表、UUID 和它所链接的 mach-O 的基本细节。您可以在 SDK 中找到大量示例(转到 SDK 根目录并找到 .-type f -name "*.tbd")。 TBD 看起来像:
--- !tapi-tbd-v3
archs: [ x86_64 ]
uuids: ['x86_64: 8891E6F5-0B7C-3CC7-88C1-9F5303311EC7' ]
platform: ios
install-name: /System/Library/Extensions/AMDRadeonX6000MTLDriver.bundle/Contents/MacOS/AMDRadeonX6000MTLDriver
objc-constraint: none
exports:
- archs: [ x86_64 ]
symbols: [ __Z34amdMtl_GFX10_GetFallbackFamilyNameP15GFX10_HwInfoRec,__Z35amdMtl_GFX10_GetFallbackProductNameP15GFX10_HwInfoRec,__Z25amdMtl_GFX10_AllocLsHsMgrP15GFX10_MtlDeviceP14AMDPPMemMgrRec,...
您必须为 Bundle 创建一个 TBD,(上面是使用 jtool2 --tbd 创建的),并指示编译器使用它(或将它放在 SDK 目录中),这应该(希望)工作.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。