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 目录中),这应该(希望)工作.

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...