简体   繁体   English

LLVM/Clang 是否支持弱链接的“弱”属性?

[英]Does LLVM/Clang support the 'weak' attribute for weak linking?

In brief: does LLVM/Clang support the 'weak' attribute?简而言之:LLVM/Clang 是否支持“弱”属性?

I'm learning some Arduino library sources (HardwareSerial.cpp to be more detailed) and I've found some interesting attribute weak that I've never used before:我正在学习一些 Arduino 库源(HardwareSerial.cpp 更详细),我发现了一些我以前从未使用过的有趣的weak属性:

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

I've found it interesting and I've read that the linker should set it to NULL if it's not defined.我发现它很有趣,并且我读过链接器应该将它设置为 NULL 如果它没有定义。

However, in my tests with Clang I'm unable to use it.但是,在我对 Clang 的测试中,我无法使用它。

File lib.cpp :文件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();
}

File lib.h :文件lib.h

#ifndef LIB_FUNC
#define LIB_FUNC

void lib_func();

#endif

File main.cpp :文件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;
}

If I use g++ lib.cpp main.cpp -o main -DDEFINE_WEAK I'm able to use it:如果我使用g++ lib.cpp main.cpp -o main -DDEFINE_WEAK我可以使用它:

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

But if I use g++ lib.cpp main.cpp -o main I'm unable to link the application:但是如果我使用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)

To be more detailed about Clang:要更详细地了解 Clang:

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

What should I do?我该怎么办? Is the weak attribute supported by LLVM/Clang? LLVM/Clang 是否支持weak属性?

PS.附注。 I've already tried to rewrite lib.cpp in the way Apple describes and still get the same linker error:我已经尝试按照Apple 描述的方式重写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();
}

It seems that (best as I can tell), Apple's description of weak linking is misleading.似乎(据我所知),Apple 对弱链接的描述具有误导性。 I've only had success marking a function as weak/weak_import if the definition is actually available at link time.如果定义在链接时实际可用,我只能成功地将函数标记为weak/weak_import。 This is opposite the usual Linux behavior where a weakly linked symbol need not be defined at link time.这与通常的 Linux 行为相反,后者不需要在链接时定义弱链接符号。

For example, the following compiles on Ubuntu 14.04 with GCC 4.8.2, but not on Mac OS X v10.9.5 (Mavericks) with Clang:例如,以下代码在 Ubuntu 14.04 和 GCC 4.8.2 上编译,但不能在Mac OS X v10.9.5 (Mavericks) 和 Clang 上编译:

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

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

The easiest workaround I've found is to explicitly tell the linker to leave the symbol in question undefined.我发现的最简单的解决方法是明确告诉链接器不定义有问题的符号。 For example, clang test.c -Wl,-U,_myfunc .例如, clang test.c -Wl,-U,_myfunc Note the the name of the symbol will differ between C and C++.请注意,符号的名称在 C 和 C++ 之间会有所不同。 In C (at least for me, I assume this is consistent), the symbol name has an underscore prepended as shown here.在 C 中(至少对我来说,我认为这是一致的),符号名称前面有一个下划线,如下所示。 In C++ the name is mangled, so you get something like __Z8weakfuncv (not necessarily consistent - I only get one leading underscore on the mangled name on my Ubuntu box).在 C++ 中,名称被破坏了,所以你会得到类似__Z8weakfuncv东西(不一定一致 - 我在我的 Ubuntu 机器上的被破坏的名称上只有一个前导下划线)。

Following this approach, if the function is defined at runtime (eg through a library preloaded by setting the DYLD_INSERT_LIBRARIES environment variable or if the version of a shared library dependency is different at runtime than it was at build time), the symbol will be resolved and the function called as desired.按照这种方法,如果函数是在运行时定义的(例如,通过设置DYLD_INSERT_LIBRARIES环境变量预加载的库,或者共享库依赖项的版本在运行时与构建时不同),则符号将被解析并根据需要调用的函数。 If the symbol isn't defined at runtime, then the check for the function fails and we continue on to return -1 as desired.如果符号未在运行时定义,则函数检查失败,我们继续根据需要返回 -1。

A somewhat more complex solution is to link against a dummy library that provides an implementation of the function in question.一个稍微复杂一些的解决方案是链接一个提供相关函数实现的虚拟库。 For example, if you compile the following as libdummy.dylib in the same directory:例如,如果您在同一目录中将以下内容编译为 libdummy.dylib:

int weakfunc()
{
    return 1;
}

You can weakly link against it你可以弱链接它

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

The symbol is then defined at link time, so the linker is happy, and will be marked as weakly linked in the resulting binary.然后在链接时定义符号,因此链接器很高兴,并将在生成的二进制文件中标记为弱链接。 By linking libdummy.dylib with -weak_library rather than with the standard -l/-L linking, the library dependency itself is weak, so the executable will still run even if libdummy.dylib is not available at runtime.通过使用-weak_library而不是标准的-l/-L链接链接 libdummy.dylib,库依赖本身很弱,因此即使 libdummy.dylib 在运行时不可用,可执行文件仍将运行。

The -flat_namespace argument tells the linker to use a "flat" namespace rather than a "two-level" namespace, which is apparently the default on OS X. In a two-level namespace, each linked symbol is marked with the library that it came from, so without this the linker would only accept a version of weakfunc from a library called libdummy.dylib. -flat_namespace参数告诉链接器使用“平面”命名空间而不是“两级”命名空间,这显然是 OS X 上的默认命名空间。在两级命名空间中,每个链接符号都标有它的库来自,所以如果没有这个,链接器将只接受来自名为 libdummy.dylib 的库的弱函数版本。 Note that in the first case of marking the symbol as undefined, that symbol is treated as being from a flat namespace, since the linker has no idea what library it might be in at runtime.请注意,在将符号标记为未定义的第一种情况下,该符号被视为来自平面命名空间,因为链接器不知道它在运行时可能位于哪个库中。

It fails by design because the linker doesn't have enough information.它因设计而失败,因为链接器没有足够的信息。 Specifically, it doesn't work because of a combination of two default linker settings:具体来说,由于两个默认链接器设置的组合,它不起作用:

-two_levelnamespace

-two_levelnamespace instructs the linker to bind external symbols both by name and by library install path. -two_levelnamespace指示链接器按名称和库安装路径绑定外部符号。 When used, the linker associates symbols to libraries based on where it finds them at link-time, given the set of libraries that it was passed.使用时,链接器根据它在链接时找到它们的位置将符号与库相关联,给定传递的库集。 If the linker doesn't find the symbol, then it won't know which library it came from.如果链接器没有找到该符号,则它不会知道它来自哪个库。

You can turn off two-level namespacing with -flat_namespace , but in general, I think that it's a good practice to leave it on.您可以使用-flat_namespace关闭两级命名空间,但总的来说,我认为保留它是一个好习惯。

Linux's ld.so does not support two-level namespacing, so this is not a problem. Linux 的 ld.so 不支持二级命名空间,所以这不是问题。 Every undefined symbol is assumed to have a definition in some library, to be discovered at runtime.假设每个未定义的符号在某个库中都有一个定义,以便在运行时被发现。

-undefined error

The -undefined setting determines how to handle symbols that have no definition visible at link-time, and the default is to error out. -undefined设置确定如何处理在链接时没有定义可见的符号,默认值是错误输出。 The other sensible option is dynamic_lookup , which tells the dynamic linker to figure out where the symbol is on its own.另一个明智的选择是dynamic_lookup ,它告诉动态链接器自己找出符号的位置。


Changing either of these settings will solve your problem, but it is heavy-handed.更改这些设置中的任何一个都可以解决您的问题,但它是严厉的。 You can also tell the linker to use dynamic lookup for specific symbols and keep the default to error by passing -U _my_weak_func to ld , or -Wl,-U,_my_weak_func to Clang (which tells it to pass it forward to the linker).您还可以告诉链接器对特定符号使用动态查找,并通过将-U _my_weak_func传递给ld-Wl,-U,_my_weak_func给 Clang(告诉它将它传递给链接器)来保持默认error The _ symbol name prefix is necessary. _符号名称前缀是必需的。

You can craft a tbd file and use it in place of dynamic libraries to tell the linker exactly where a weak symbol would be found if it was implemented instead of forcing weak functions to use dynamic lookup.您可以制作一个 tbd 文件并使用它代替动态库来告诉链接器如果实现了弱符号,将在何处找到弱符号,而不是强制弱函数使用动态查找。 Apple uses tbd files for its libraries and frameworks, which is what allows weak linking to work. Apple 为其库和框架使用 tbd 文件,这使得弱链接起作用。 The process is somewhat tedious, though, because Apple doesn't offer tools to automatically create tbd files for libraries.不过,这个过程有点乏味,因为 Apple 不提供自动为库创建 tbd 文件的工具。 You'd need to pass a file of the following format as a library to the compiler:您需要将以下格式的文件作为库传递给编译器:

--- !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 ]
...

Where:在哪里:

  • $ARCH is the architecture name of the thing you want to build (like "x86_64", without quotes) $ARCH是您要构建的事物的架构名称(如“x86_64”,不带引号)
  • $UUID can be queried with otool -l $path_to_your_lib | grep -A 2 LC_UUID $UUID可以用otool -l $path_to_your_lib | grep -A 2 LC_UUID查询otool -l $path_to_your_lib | grep -A 2 LC_UUID
  • $INSTALL_PATH and $CURRENT_VERSION can be queried with otool -l $path_to_your_lib | grep -A 4 LC_ID_DYLIB $INSTALL_PATH和 $CURRENT_VERSION 可以用otool -l $path_to_your_lib | grep -A 4 LC_ID_DYLIB查询otool -l $path_to_your_lib | grep -A 4 LC_ID_DYLIB

This will let the linker know which library should contain your weak symbol.这将使链接器知道哪个库应该包含您的弱符号。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM