似乎有3种方法告诉GCC弱连接符号:
__attribute__((weak_import))
__attribute__((weak))
#pragma weak symbol_name
这些都不适合我:
#pragma weak asdf
extern void asdf(void) __attribute__((weak_import, weak));
...
{
if(asdf != NULL) asdf();
}
Run Code Online (Sandbox Code Playgroud)
我总是得到这样的链接错误:
Undefined symbols: "_asdf", referenced from: _asdf$non_lazy_ptr in ccFA05kN.o ld: symbol(s) not found collect2: ld returned 1 exit status
我在OS X 10.5.5上使用GCC 4.0.1.我究竟做错了什么?
Jef*_*eld 29
我只是调查了一下,并认为其他人可能会对我的发现感兴趣.
与weak_import的弱连接实际上只适用于动态库.你可以得到它(通过指定-undefined dynamic_lookup如上建议)与静态链接工作,但这不是这么热的想法.这意味着在运行时之前不会检测到任何未定义的符号.这是我个人在生产代码中要避免的.
这是一个Mac OS X终端会话,展示了如何使其工作:
这是fc
int f(int n)
{
return n * 7;
}
Run Code Online (Sandbox Code Playgroud)
这是whatnof.c
#include <stdio.h>
#include <stdlib.h>
extern int f (int) __attribute__((weak_import));
int main() {
if(f == NULL)
printf("what, no f?\n");
else
printf("f(8) is %d\n", f(8));
exit(0);
}
Run Code Online (Sandbox Code Playgroud)
从fc创建一个动态库:
$ cc -dynamiclib -o f.dylib f.c
Run Code Online (Sandbox Code Playgroud)
编译并链接动态库,列出动态库.
$ cc -o whatnof whatnof.c f.dylib
$ otool -L whatnof
whatnof:
f.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.0)
Run Code Online (Sandbox Code Playgroud)
运行whatnof看看会发生什么:
$ whatnof
f(8) is 56
Run Code Online (Sandbox Code Playgroud)
现在将f.dylib替换为空库(无符号):
$ mv f.dylib f.dylib.real
$ touch null.c
$ cc -dynamiclib -o f.dylib null.c
Run Code Online (Sandbox Code Playgroud)
运行相同的whatnof看看会发生什么:
$ whatnof
what, no f?
Run Code Online (Sandbox Code Playgroud)
weak_import的基本思想(或"用例")是它允许您链接一组动态(共享)库,但是对相同库的早期版本运行相同的代码.您可以检查函数是否为NULL,以查看它们是否在代码当前运行的特定动态库中受支持.这似乎是Xcode支持的基本开发模型的一部分.我希望这个例子很有用; 它帮助我放松了对Xcode设计的这一部分.
小智 6
添加-Wl,-flat_namespace,-undefined,dynamic_lookup
到用于执行最终链接的gcc编译器行.
最小可运行 Linux 示例
主程序
#include <stdio.h>
int my_weak_var __attribute__((weak)) = 1;
int main(void) {
printf("%d\n", my_weak_var);
}
Run Code Online (Sandbox Code Playgroud)
notmain.c
int my_weak_var = 2;
Run Code Online (Sandbox Code Playgroud)
使用两个对象编译并运行:
gcc -c -std=c99 -Wall -Wextra -pedantic -o main.o main.c
gcc -c -std=c99 -Wall -Wextra -pedantic -o notmain.o notmain.c
gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.o notmain.o
./main.out
Run Code Online (Sandbox Code Playgroud)
输出:
2
Run Code Online (Sandbox Code Playgroud)
编译并运行,无需notmain.o
:
gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.o
./main.out
Run Code Online (Sandbox Code Playgroud)
输出:
1
Run Code Online (Sandbox Code Playgroud)
因此我们看到,如果在 上给出notmain.o
,那么非弱符号将按预期优先。
我们可以通过以下方式分析ELF 目标文件符号:
nm main.o notmain.o
Run Code Online (Sandbox Code Playgroud)
这使:
main.o:
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
0000000000000000 V my_weak_var
U printf
notmain.o:
0000000000000000 D my_weak_var
Run Code Online (Sandbox Code Playgroud)
进而:
man nm
Run Code Online (Sandbox Code Playgroud)
包含:
符号类型。至少使用以下类型;其他也取决于目标文件格式。如果是小写,则该符号通常是本地的;如果大写,则该符号是全局的(外部)。然而,有一些小写符号显示为特殊的全局符号(“u”、“v”和“w”)。
“D”
“d” 该符号位于初始化数据部分。“V”
“v”符号是弱对象。当弱定义符号与普通定义符号链接时,使用普通定义符号不会出现错误。当链接未定义的弱符号且该符号未定义时,弱符号的值将变为零且不会出现错误。在某些系统上,大写字母表示已指定默认值。
但是,如果处理静态库,您可能必须按照以下说明.a
使用: How to make gcc link Strong Symbol in static Library to Overwrite Weak Symbol?-Wl,--whole-archive
弱符号也可以保持未定义,这在 Binutils 中会导致“平台特定行为”,请参阅:未解决的弱函数的 GCC 行为
在 Ubuntu 18.10、GCC 8.2.0 上测试。
来自 gcc 文档手册:
虚弱的
弱属性导致声明作为弱符号而不是全局符号发出。这主要用于定义可以在用户代码中覆盖的库函数,尽管它也可以与非函数声明一起使用。ELF 目标支持弱符号,使用 GNU 汇编器和链接器时 a.out 目标也支持弱符号。
这意味着一个对象可以合法地覆盖弱符号(在另一个对象/库中定义),而不会在链接时出现错误。不清楚的是您是否将库与弱符号链接。看来您还没有定义符号并且库没有正确链接。