3

作为一项心理锻炼,我正在尝试编写一个直接链接到我的 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当显示共享库中明显存在具有此确切名称的符号时,为什么我会看到此链接器错误?

4

2 回答 2

0

If has_init is not part of a class, then you can still call the function by it's mangled name. However, this will only work if it is a free function. If it is part of a class, then you can not really call it without class definition, as you don't know what it does to the class members and you would have to pass the object as the first argument.

#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;
}
于 2021-05-02T08:43:47.093 回答
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 目录中),这应该(希望)工作。

于 2021-05-07T15:16:16.333 回答