llvm / clang是否支持“弱”'弱链接的属性?

时间:2015-10-01 05:02:47

标签: c++ linker clang llvm weak-linking

简而言之:llvm / clang支持'弱'属性?

我正在学习一些Arduino库源(HardwareSerial.cpp更详细),并且我发现了一些我以前从未使用过的有趣属性weak

#if defined(HAVE_HWSERIAL0)
  void serialEvent() __attribute__((weak));
  bool Serial0_available() __attribute__((weak));
#endif

我发现它很有趣并且我已经读过如果未定义链接器应将其设置为NULL。

然而,在clang的测试中,我无法使用它。

lib.cpp:

#include "lib.h"
#include <stdio.h>

void my_weak_func() __attribute__((weak));

void lib_func() {
    printf("lib_func()\n");

    if (my_weak_func)
        my_weak_func();
}

lib.h:

#ifndef LIB_FUNC
#define LIB_FUNC

void lib_func();

#endif

main.cpp中:

#include "lib.h"
#include <stdio.h>

#ifdef DEFINE_WEAK
void my_weak_func() {
    printf("my_weak_func()\n"); 
}
#endif

int main() {

    lib_func();

    printf("finished\n");
    return 0;
}

如果我使用g++ lib.cpp main.cpp -o main -DDEFINE_WEAK我可以使用它:

MBA-Anton:Weak_issue asmirnov$ ./main
lib_func()
my_weak_func()
finished

但如果我使用g++ lib.cpp main.cpp -o main我无法关联该应用:

Undefined symbols for architecture x86_64:
  "my_weak_func()", referenced from:
      lib_func() in lib-ceb555.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

更详细的铿锵声:

MBA-Anton:Weak_issue asmirnov$ g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include/c++/4.2.1
Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
Target: x86_64-apple-darwin14.3.0
Thread model: posix

我该怎么办? llvm / clang是否支持weak属性?

PS。我已经尝试以Apple describes的方式重写lib.cpp并且仍然存在相同的链接器错误:

#include "lib.h"
#include <stdio.h>

extern void my_weak_func() __attribute__((weak_import));

void lib_func() {
    printf("lib_func()\n");

    if (my_weak_func != NULL)
        my_weak_func();
}

2 个答案:

答案 0 :(得分:4)

似乎(我能说得最好),Apple对弱连接的描述具有误导性。如果定义在链接时实际可用,我只能成功将一个函数标记为弱/弱_import。这与通常的Linux行为相反,在链接时无需定义弱链接符号。

例如,以下内容使用gcc 4.8.2在Ubuntu 14.04上进行编译,但在带有clang的OS X 10.9.5上进行编译

/* test.c */
int weakfunc() __attribute__((weak));

int main()
{
    if (weakfunc) return weakfunc();
    else        return -1;
}

我发现最简单的解决方法是明确告诉链接器保留未定义的符号。例如,clang test.c -Wl,-U,_myfunc。请注意,符号的名称在C和C ++之间会有所不同。在C中(至少对我来说,我认为这是一致的),符号名称前面有一个下划线,如下所示。在C ++中,名称被破坏了,所以你得到类似__Z8weakfuncv的东西(不一定一致 - 我只在Ubuntu盒子上的受损名称上得到一个前导下划线)。

遵循此方法,如果函数是在运行时定义的(例如,通过设置DYLD_INSERT_LIBRARIES环境变量预加载的库,或者共享库依赖项的版本在运行时与构建时不同) ,符号将被解析,并根据需要调用函数。如果符号未在运行时定义,则检查函数失败,我们继续根据需要返回-1。

更复杂的解决方案是链接虚拟库,该虚拟库提供相关功能的实现。例如,如果在同一目录中将以下内容编译为libdummy.dylib:

int weakfunc()
{
    return 1;
}

你可以弱连接它

clang test.c -weak_library ./libdummy.dylib -flat_namespace

然后在链接时定义符号,因此链接器很满意,并且将在生成的二进制文件中标记为弱链接。通过将libdummy.dylib与-weak_library而不是标准-l/-L链接相关联,库依赖本身很弱,因此即使libdummy.dylib在运行时不可用,仍然可以运行可执行文件。

-flat_namespace参数告诉链接器使用&#34; flat&#34;命名空间而不是&#34;两级&#34;命名空间,显然是OS X上的默认命名空间。在两级命名空间中,每个链接的符号都标有它来自的库,所以如果没有这个,链接器只接受来自名为libdummy.dylib的库的weakfunc版本。请注意,在第一种将符号标记为未定义的情况下,该符号被视为来自平面命名空间,因为链接器不知道它在运行时可能存在哪个库。

答案 1 :(得分:1)

由于链接器没有足够的信息,它在设计上会失败。具体来说,由于两个默认链接器设置的组合,它不起作用:

-two_levelnamespace

-two_levelnamespace指示链接器通过名称和库安装路径绑定外部符号。使用链接器时,给定已传递的库集,链接器将根据符号在链接时找到它们的位置将它们与库关联。如果链接器找不到符号,则它将不知道它来自哪个库。

您可以使用-flat_namespace关闭两级命名空间,但总的来说,我认为将其保留为好习惯。

Linux的ld.so不支持两级命名空间,因此这不是问题。假定每个未定义的符号在某个库中都有定义,以便在运行时发现。

-undefined error

-undefined设置确定如何处理链接时没有可见定义的符号,默认设置为错误输出。另一个明智的选择是dynamic_lookup,它告诉动态链接程序自行确定符号的位置。


更改这些设置中的任何一个都可以解决您的问题,但是操作繁琐。您还可以通过将error传递给-U _my_weak_funcld传递给Clang(告诉它,将链接传递给特定的符号)来使链接器对特定符号使用动态查找,并将默认值保留为-Wl,-U,_my_weak_func。将其转发给链接器)。 _符号名称前缀是必需的。

您可以制作一个tbd文件并使用它代替动态库,以告诉链接器如果实现了该链接,则可以在哪里确切地找到一个弱符号,而不是强制弱函数使用动态查找。苹果将​​tbd文件用于其库和框架,这使得弱链接可以正常工作。但是,此过程有些繁琐,因为Apple不提供自动为库创建tbd文件的工具。您需要将以下格式的文件作为库传递给编译器:

--- !tapi-tbd-v3
archs:           [ $ARCH ]
uuids:           [ '$ARCH: $UUID' ]
platform:        $ARCH
install-name:    $INSTALL_PATH
current-version: $CURRENT_VERSION
objc-constraint: none
exports:         
  - archs:           [ $ARCH ]
    symbols:         [ _my_weak_func ]
...

位置:

  • $ARCH是要构建的事物的体系结构名称(例如“ x86_64”,不带引号)
  • 可以使用$UUID来查询
  • otool -l $path_to_your_lib | grep -A 2 LC_UUID
  • 可以使用$INSTALL_PATH来查询
  • otool -l $path_to_your_lib | grep -A 4 LC_ID_DYLIB和$ CURRENT_VERSION

这将使链接程序知道哪个库应包含您的弱符号。