【问题标题】:Nm shows symbol in shared library, but undefined symbol when linkingNm 在共享库中显示符号,但链接时未定义符号
【发布时间】:2021-07-22 00:19:05
【问题描述】:

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

【问题讨论】:

  • 你检查过namemangling是一样的,所以它指的是同一个符号吗?或者它可能不是命名空间的一部分而是类成员?
  • 这是一个好主意——看起来符号的名称在共享库中是 __ZN3HSA8hsa_initEv,并且我在命名空间中为函数获得了与在类。
  • 请不要在包含问题信息的问题中添加 cmets。编辑问题并在此处添加此信息。
  • 您的评论讲述了您在类中定义的函数,但问题是关于在命名空间中定义的函数。当心。我建议在“问题”中添加两个损坏名称的副本。我希望在问题中明确写出名称对您有很好的帮助。
  • rocr_test.o 是 Mach-O 还是 ELF 格式?使用-v 选项运行链接器步骤是否提供任何提示?

标签: c++ macos linker linker-errors amd-gpu


【解决方案1】:

如果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;
}

【讨论】:

  • 嗯...这似乎也不起作用。奇怪的是,如果我编译成目标文件,我会在目标文件和动态库中看到 exact same 符号名称。我对该函数的签名相当有信心,因为它与AMD's open source runtime library 中定义的函数相匹配。 Apple 是否有可能做了一些事情,使链接到他们的 ROCr 运行时分支变得不可能/困难?
  • 您是否也尝试在前面添加_?也许我记错了,在这种情况下,您必须输入确切的符号?我自己也使用过这种方法,所以应该可以。
  • 删除两个前导下划线之一似乎是对的。如果我在链接之前生成了一个目标文件,则表明您的代码生成了一个与动态库导出的符号同名的“未定义”符号(即带有两个前导下划线)。因此,我怀疑这个问题与名称修改或符号名称无关。也许还有其他事情发生?
  • 也可能是他们使用了一些不同的编译器,生成的对象文件格式略有不同?由于该文件不应该由用户代码直接链接,因此在正常情况下不会导致任何问题。
  • 我用 gcc 测试了它,在这里它可以工作。只有我没有删除领先的_,所以你可以试试:#define hsa_init __ZN3HSA8hsa_initEv。我认为记错了,因为调用它时,您需要确切的符号。前导 _ 仅添加在导出的符号上,因此应包含在内,并且在调用时需要存在。
【解决方案2】:

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

【讨论】:

  • 有趣!这似乎是问题所在,尽管我很惊讶这会影响“标准”Clang(我的系统上都有)和 GCC。我尝试通过 Homebrew 安装 jtool2,但 jtool2 --tbd AMDRadeonX6000MTLDriver 似乎没有任何效果。如果我首先使用--analyze 运行,那么后续操作会打印出以Malformed line 开头的13 个错误。您能否更详细地描述一下您是如何制作 TBD 文件的?
  • 好的,我能够使用 Xcode 工具链中的 tapi 实用程序生成 TBD 文件。现在我有了 TBD 文件,我该如何“指导编译器使用它”?
猜你喜欢
  • 2012-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-16
  • 2011-01-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多