简而言之: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();
}
答案 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_func
或ld
传递给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 这将使链接程序知道哪个库应包含您的弱符号。