我想在代码中引入一个弱符号,但是,当使用* .a文件时,我无法理解它的行为。
这是我的最小示例:
文件a.h:
void foo() __attribute__((weak));
文件a.c:
#include "a.h"
#include <stdio.h>
void foo() { printf("%s\n", __FILE__); }
文件b.c:
#include <stdio.h>
void foo() { printf("%s\n", __FILE__); }
文件main.cpp:
#include "a.h"
#include <stdio.h>
int main() { if (foo) foo(); else printf("no foo\n"); }
现在,根据我使用* .o文件(make -c a.c
和make -c b.c
)还是* .a文件(ar cr a.o
和ar cr b.o
),输出是不同的: / p>
1)g++ main.cpp a.o b.o
打印 b.c
2)g++ main.cpp b.o a.o
打印 b.c
3)g++ main.cpp a.a b.a
打印没有foo
4)g++ main.cpp b.a a.a
打印没有foo
1), 2)工作正常,但 3), 4)的输出似乎有点意外。
我极力尝试使此示例与档案一起使用,所以我做了一些更改:
文件a.h:
void foo();
文件a.c:
#include "a.h"
#include <stdio.h>
void __attribute__((weak)) foo() { printf("%s\n", __FILE__); }
此修改后:
1)g++ main.cpp a.a b.a
打印 a
2)g++ main.cpp b.a a.a
打印 b.c
所以效果更好。运行nm a.a
后将显示W _Z3foov
,因此不会违反ODR。但是,我不知道这是否是弱属性的正确用法。根据gcc文档:
weak属性导致 声明 作为弱符号而不是全局符号发出。尽管它也可以与非函数声明一起使用,但它在定义可以在用户代码中覆盖的库函数时非常有用。使用GNU汇编器和链接器时,ELF目标和a.out目标均支持弱符号。
但是我在函数定义而不是声明上使用了 weak 属性。
所以问题是为什么弱不适用于* .a文件?在定义而不是声明中使用 weak 属性是否正确?
更新
我突然意识到,与 foo()方法定义一起使用的弱属性对符号解析没有影响。没有属性,最终二进制文件将生成相同的结果:
1)g++ main.cpp a.a b.a
打印 a
2)g++ main.cpp b.a a.a
打印 b.c
因此,仅使用符号的第一个定义,这与默认的gcc行为一致。即使nm a.a
表明发出了一个弱符号,它似乎也不影响静态链接。
是否可以通过静态链接使用弱属性?
我要解决的问题的说明
我有一个供20多个客户端使用的库,我们称其为库A。我还提供了一个库B,其中包含针对A的测试实用程序。某种程度上,我需要知道库A用于测试模式,因此最简单的解决方案似乎是在与B链接期间替换符号(因为客户端已经与B链接)。
我知道有解决此问题的更干净的方法,但是我绝对不能影响客户的代码或他们的构建脚本(添加参数以指示对A进行测试或对DEFINE进行编译是不可行的)。
答案 0 :(得分:6)
要解释这里发生的情况,我们首先使用
来讨论原始源文件a.h(1):
void foo() __attribute__((weak));
和:
交流(1):
#include "a.h"
#include <stdio.h>
void foo() { printf("%s\n", __FILE__); }
示例代码中.c
和.cpp
文件的混合与
问题,并且所有代码都是C,因此我们可以说main.cpp
是main.c
,
用gcc
完成所有编译和链接:
$ gcc -Wall -c main.c a.c b.c
ar rcs a.a a.o
ar rcs b.a b.o
首先,让我们回顾一下弱声明符号之间的区别,例如 您的:
void foo() __attribute__((weak));
和强烈声明的符号,例如
void foo();
这是默认设置:
在程序中链接了对foo
的弱引用(即对弱声明的foo
的引用)时,
链接器无需在链接中的任何位置找到foo
的定义:它可以保留
未定义。如果在程序中链接了对foo
的强烈引用,
链接器需要找到foo
的定义。
一个链接最多可以包含foo
的一个强定义(即一个定义
foo
的声明强烈声明)。否则会导致多定义错误。
但是它可能包含foo
的多个弱定义,而没有错误。
如果链接包含一个或多个foo
的弱定义以及一个强
定义,则链接器选择强定义,而忽略弱定义
一个。
如果链接仅包含foo
的一个弱定义而没有强定义
定义,链接器不可避免地使用一个弱定义。
如果链接包含foo
的多个弱定义而没有强定义
定义,然后链接器会任意选择一个弱定义。
接下来,让我们回顾一下在链接中输入目标文件之间的区别 并输入一个静态库。
静态库只是我们可能提供给目标文件的ar
存档
从中选择需要进行链接的链接器。
将目标文件输入链接时,链接程序将无条件链接该文件 进入输出文件。
将静态库输入链接时,链接器将检查存档以 在其中找到任何未定义符号引用提供所需定义的对象文件 从已经链接的输入文件中产生的结果。如果找到任何此类目标文件 在档案中,它提取它们并将它们链接到输出文件中,就像 如果它们是分别命名的输入文件,并且根本没有提到静态库。
请牢记以下几点,请考虑“编译并链接”命令:
gcc main.c a.o b.o
gcc
幕后将其分解为一个编译步骤和链接
步骤,就像您已经跑步过一样:
gcc -c main.c # compile
gcc main.o a.o b.o # link
所有三个目标文件都无条件链接到(默认)程序./a.out
中。 a.o
包含一个
如我们所见,foo
的定义很弱:
$ nm --defined a.o
0000000000000000 W foo
b.o
包含一个强定义:
$ nm --defined b.o
0000000000000000 T foo
链接程序将找到两个定义,并从b.o
中选择一个强定义,我们可以
另请参阅:
$ gcc main.o a.o b.o -Wl,-trace-symbol=foo
main.o: reference to foo
a.o: definition of foo
b.o: definition of foo
$ ./a.out
b.c
颠倒a.o
和b.o
的链接顺序没有什么区别:
仍然是foo
的一个强定义,b.o
中的一个。
相比之下,请考虑使用compile-and-link命令:
gcc main.cpp a.a b.a
可分为:
gcc -c main.cpp # compile
gcc main.o a.a b.b # link
在这里,只有main.o
是无条件链接的。这带来了不确定的弱参考
foo
进入链接:
$ nm --undefined main.o
w foo
U _GLOBAL_OFFSET_TABLE_
U puts
对foo
的弱引用不需要定义。因此链接器将
不要尝试在a.a
或b.a
中的任何目标文件中找到解决该问题的定义,并且
如我们所见,将在程序中使其保持未定义状态
$ gcc main.o a.a b.a -Wl,-trace-symbol=foo
main.o: reference to foo
$ nm --undefined a.out
w __cxa_finalize@@GLIBC_2.2.5
w foo
w __gmon_start__
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U __libc_start_main@@GLIBC_2.2.5
U puts@@GLIBC_2.2.5
因此:
$ ./a.out
no foo
同样,如果您颠倒a.a
和b.a
的链接顺序也没关系,
但这是因为它们都不对链接有任何贡献。
现在让我们谈谈通过更改a.h
和a.c
发现的不同行为
到:
a.h(2):
void foo();
交流(2):
#include "a.h"
#include <stdio.h>
void __attribute__((weak)) foo() { printf("%s\n", __FILE__); }
再次:
$ gcc -Wall -c main.c a.c b.c
main.c: In function ‘main’:
main.c:4:18: warning: the address of ‘foo’ will always evaluate as ‘true’ [-Waddress]
int main() { if (foo) foo(); else printf("no foo\n"); }
看到该警告? main.o
现在包含强烈声明的对foo
的引用:
$ nm --undefined main.o
U foo
U _GLOBAL_OFFSET_TABLE_
因此,代码(链接时)必须具有 foo
的非空地址。继续:
$ ar rcs a.a a.o
$ ar rcs b.a b.o
然后尝试链接:
$ gcc main.o a.o b.o
$ ./a.out
b.c
并反转目标文件:
$ gcc main.o b.o a.o
$ ./a.out
b.c
和以前一样,顺序没有区别。所有目标文件都已链接。 b.o
提供
foo
的强定义,a.o
的定义很弱,因此b.o
获胜。
接下来尝试链接:
$ gcc main.o a.a b.a
$ ./a.out
a.c
与库顺序相反:
$ gcc main.o b.a a.a
$ ./a.out
b.c
确实有所作为。为什么?让我们重做与诊断的联系:
$ gcc main.o a.a b.a -Wl,-trace,-trace-symbol=foo
/usr/bin/x86_64-linux-gnu-ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o
main.o
(a.a)a.o
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o
main.o: reference to foo
a.a(a.o): definition of foo
忽略默认库,我们的的唯一目标文件 链接的是:
main.o
(a.a)a.o
foo
的定义来自a.o
的存档成员a.a
:
a.a(a.o): definition of foo
反转库顺序:
$ gcc main.o b.a a.a -Wl,-trace,-trace-symbol=foo
/usr/bin/x86_64-linux-gnu-ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o
main.o
(b.a)b.o
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o
main.o: reference to foo
b.a(b.o): definition of foo
这一次链接的目标文件是:
main.o
(b.a)b.o
foo
的定义来自b.o
中的b.a
:
b.a(b.o): definition of foo
在第一个链接中,链接器未完全引用以下内容
到达foo
时需要定义的a.a
。所以
在档案库中查找了提供定义的目标文件,
并找到a.o
。这个定义是一个很弱的定义,但是没关系。没有
看到了很强的定义。 a.o
是从a.a
中提取并链接的,
从而解决了对foo
的引用。下一个到达b.a
的地方
如果链接器仍需要一个foo
的强定义,则会在b.o
中找到
并寻找它。但是它不再需要了,也没有外观。链接:
gcc main.o a.a b.a
与完全相同:
gcc main.o a.o
同样是链接:
$ gcc main.o b.a a.a
与以下内容完全相同:
$ gcc main.o b.o
您的实际问题...
...出现在您对帖子的评论之一中:
与测试框架链接时,我想覆盖[原始]函数实现。
您要链接输入一些静态库lib1.a
的程序
其中有一些成员file1.o
定义了符号foo
,并且您想删除
foo
的定义,并链接其他对象中定义的其他定义
文件file2.o
。
__attribute__((weak))
不适用于该问题。解决方案更多
初级。 您只需确保在输入之前输入 file2.o
链接
lib1.a
(并且在提供foo
定义的任何其他输入之前)。
然后,链接器将使用foo
中提供的定义来解析对file2.o
的引用,并且不会尝试查找其他任何引用
到达lib1.a
时的定义。链接器完全不会消耗lib1.a(file1.o)
。它也可能不存在。
如果将file2.o
放在另一个静态库lib2.a
中怎么办?然后输入
lib2.a
之前的lib1.a
将完成lib2.a(file2.o)
之前的链接
已到达lib1.a
,并将foo
解析为file2.o
中的定义。
当然,lib2.a
成员提供的每个定义都将链接到
偏好lib1.a
中提供的相同符号的定义。如果不是那样的话
您想要的,然后不喜欢lib2.a
:链接file2.o
本身。
最后
是否可以将[we]属性与静态链接一起使用?
当然。这是第一个原则的用例:
foo.h(1)
#ifndef FOO_H
#define FOO_H
int __attribute__((weak)) foo(int i)
{
return i != 0;
}
#endif
aa.c
#include "foo.h"
int a(void)
{
return foo(0);
}
bb.c
#include "foo.h"
int b(void)
{
return foo(42);
}
程序
#include <stdio.h>
extern int a(void);
extern int b(void);
int main(void)
{
puts(a() ? "true" : "false");
puts(b() ? "true" : "false");
return 0;
}
编译所有源文件,为每个功能请求一个单独的ELF部分:
$ gcc -Wall -ffunction-sections -c prog.c aa.c bb.c
请注意,foo
的弱定义是通过foo.h
编译成 both
我们可以看到aa.o
和bb.o
:
$ nm --defined aa.o
0000000000000000 T a
0000000000000000 W foo
$ nm --defined bb.o
0000000000000000 T b
0000000000000000 W foo
现在从所有目标文件链接程序,请求链接器链接到 丢弃未使用的部分(并提供给我们映射文件和一些诊断信息):
$ gcc prog.o aa.o bb.o -Wl,--gc-sections,-Map=mapfile,-trace,-trace-symbol=foo
/usr/bin/x86_64-linux-gnu-ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o
prog.o
aa.o
bb.o
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o
aa.o: definition of foo
此链接与以下内容没有区别
:$ ar rcs libaabb.a aa.o bb.o
$ gcc prog.o libaabb.a
尽管同时加载了aa.o
和bb.o
,并且每个都包含
foo
的定义,没有多定义错误,因为每个定义
是弱。 aa.o
在bb.o
之前被加载,并且foo
的定义从aa.o
链接。
那么foo
中bb.o
的定义发生了什么?映射文件显示给我们:
映射文件(1)
...
...
Discarded input sections
...
...
.text.foo 0x0000000000000000 0x13 bb.o
...
...
链接器丢弃了包含定义的功能部分
在bb.o
让我们颠倒aa.o
和bb.o
的链接顺序:
$ gcc prog.o bb.o aa.o -Wl,--gc-sections,-Map=mapfile,-trace,-trace-symbol=foo
...
prog.o
bb.o
aa.o
...
bb.o: definition of foo
现在相反的事情发生了。 bb.o
在aa.o
之前被加载。的
foo
的定义从bb.o
和以下链接:
映射文件(2)
...
...
Discarded input sections
...
...
.text.foo 0x0000000000000000 0x13 aa.o
...
...
aa.o
中的定义被删除了。
您会看到链接器任意如何选择多个 在没有强定义的情况下,符号的弱定义。它只是 选择您给它的第一个,而忽略其余的。
我们在这里所做的实际上是GCC C ++编译器为我们所做的事情。 定义全局内联函数。重写:
foo.h(2)
#ifndef FOO_H
#define FOO_H
inline int foo(int i)
{
return i != 0;
}
#endif
重命名源文件*.c
-> *.cpp
;编译并链接:
$ g++ -Wall -c prog.cpp aa.cpp bb.cpp
现在,foo
和aa.o
中的每个对bb.o
(C ++拼写)的定义都很弱:
$ nm --defined aa.o bb.o
aa.o:
0000000000000000 T _Z1av
0000000000000000 W _Z3fooi
bb.o:
0000000000000000 T _Z1bv
0000000000000000 W _Z3fooi
链接使用找到的第一个定义:
$ g++ prog.o aa.o bb.o -Wl,-Map=mapfile,-trace,-trace-symbol=_Z3fooi
...
prog.o
aa.o
bb.o
...
aa.o: definition of _Z3fooi
bb.o: reference to _Z3fooi
并扔掉另一个:
映射文件(3)
...
...
Discarded input sections
...
...
.text._Z3fooi 0x0000000000000000 0x13 bb.o
...
...
您可能知道,C ++函数模板的每个实例都在 全局范围(或类模板成员函数的实例化)为 内联全局函数。再次重写:
#ifndef FOO_H
#define FOO_H
template<typename T>
T foo(T i)
{
return i != 0;
}
#endif
重新编译:
$ g++ -Wall -c prog.cpp aa.cpp bb.cpp
再次:
$ nm --defined aa.o bb.o
aa.o:
0000000000000000 T _Z1av
0000000000000000 W _Z3fooIiET_S0_
bb.o:
0000000000000000 T _Z1bv
0000000000000000 W _Z3fooIiET_S0_
aa.o
和bb.o
中的每一个都具有以下弱定义:
$ c++filt _Z3fooIiET_S0_
int foo<int>(int)
和链接行为现在很熟悉。一种方式:
$ g++ prog.o aa.o bb.o -Wl,-Map=mapfile,-trace,-trace-symbol=_Z3fooIiET_S0_
...
prog.o
aa.o
bb.o
...
aa.o: definition of _Z3fooIiET_S0_
bb.o: reference to _Z3fooIiET_S0_
和另一种方式:
$ g++ prog.o bb.o aa.o -Wl,-Map=mapfile,-trace,-trace-symbol=_Z3fooIiET_S0_
...
prog.o
bb.o
aa.o
...
bb.o: definition of _Z3fooIiET_S0_
aa.o: reference to _Z3fooIiET_S0_
我们的程序的行为不会因以下原因而改变:
$ ./a.out
false
true
因此,将 weak 属性应用于ELF对象链接中的符号- 静态还是动态-启用C ++模板的GCC实施 用于GNU链接器。您可以说它可以实现现代C ++的GCC实现。
答案 1 :(得分:1)
我发现here是最好的解释:
如果链接器在搜索所有输入对象之后无法解析该引用,则它将仅搜索库以解析引用。如果需要,将根据库在链接器命令行上的位置从左到右搜索库。库中的对象将按归档顺序搜索。一旦armlink找到与参考符号匹配的符号,即使它与弱定义匹配,搜索也将结束。
ELF ABI第4.6.1.2节说:
“弱定义不会更改从库中选择目标文件的规则。但是,如果链接集同时包含弱定义和非弱定义,则将始终使用非弱定义。”
“链接集”是链接器已加载的对象集。它不包括不需要的库中的对象。
因此,不建议将两个对象归档到一个库或单独的库中,其中一个对象包含给定符号的弱定义,另一个对象包含该符号的非弱定义。
观察以下内容。基本上重命名为mv a.c definition.c
mv b.c noweak.c
和mv second_a.c declaration.c
。
> for i in Makefile *.c; do echo "cat $i <<EOF"; cat $i; echo EOF; done
cat Makefile <<EOF
tgt=
tgt+=only_weak_1.out only_weak_2.out
tgt+=definition.out declaration.out noweak.out
tgt+=definition_static.out declaration_static.out noweak_static.out
tgt+=1.out 2.out 3.out 4.out
tgt+=5.out 6.out 7.out 8.out
tgt+=10.out 11.out 12.out
tgt+=13.out
tgt+=14.out
only_weak_1_obj= definition.o declaration.o
only_weak_2_obj= declaration.o definition.o
definition_obj= definition.o
declaration_obj= declaration.o
noweak_obj= noweak.o
definition_static_obj= definition.a
declaration_static_obj= declaration.a
noweak_static_obj= noweak.a
1_obj= declaration.o noweak.o
2_obj= noweak.o declaration.o
3_obj= declaration.a noweak.a
4_obj= noweak.a declaration.a
5_obj= definition.o noweak.o
6_obj= noweak.o definition.o
7_obj= definition.a noweak.a
8_obj= noweak.a definition.a
10_obj= noweak.a definition.a declaration.a
11_obj= definition.a declaration.a noweak.a
12_obj= declaration.a definition.a noweak.a
13_obj= all.a
14_obj= all.o
.PRECIOUS: % %.o %.c %.a
def: run
.PHONY: run
run: $(tgt)
{ $(foreach a,$^,echo "$($(a:.out=)_obj)#->#$(a)#:#$$(./$(a))";) } | { echo; column -t -s'#' -N 'objects, ,executable, ,output' -o' '; echo; }
.SECONDEXPANSION:
%.out: main.o $$(%_obj)
$(CC) -o $@ $^
%.o: %.c
$(CC) -c -o $@ $^
%.a: %.o
ar cr $@ $^
all.a: declaration.o definition.o noweak.o
ar cr $@ $^
all.o: declaration.o definition.o noweak.o
$(LD) -i -o $@ $^
clean:
rm -fv *.o *.a *.out
EOF
cat declaration.c <<EOF
#include <stdio.h>
__attribute__((__weak__)) void foo();
void foo() { printf("%s\n", __FILE__); }
EOF
cat definition.c <<EOF
#include <stdio.h>
__attribute__((__weak__)) void foo() { printf("%s\n", __FILE__); }
EOF
cat main.c <<EOF
#include <stdio.h>
void foo();
int main() {
if (foo) foo(); else printf("no foo\n");
return 0;
}
EOF
cat noweak.c <<EOF
#include <stdio.h>
void foo() { printf("%s\n", __FILE__); }
EOF
> make
cc -c -o definition.o definition.c
cc -c -o declaration.o declaration.c
cc -c -o main.o main.c
cc -o only_weak_1.out main.o definition.o declaration.o
cc -o only_weak_2.out main.o declaration.o definition.o
cc -o definition.out main.o definition.o
cc -o declaration.out main.o declaration.o
cc -c -o noweak.o noweak.c
cc -o noweak.out main.o noweak.o
ar cr definition.a definition.o
cc -o definition_static.out main.o definition.a
ar cr declaration.a declaration.o
cc -o declaration_static.out main.o declaration.a
ar cr noweak.a noweak.o
cc -o noweak_static.out main.o noweak.a
cc -o 1.out main.o declaration.o noweak.o
cc -o 2.out main.o noweak.o declaration.o
cc -o 3.out main.o declaration.a noweak.a
cc -o 4.out main.o noweak.a declaration.a
cc -o 5.out main.o definition.o noweak.o
cc -o 6.out main.o noweak.o definition.o
cc -o 7.out main.o definition.a noweak.a
cc -o 8.out main.o noweak.a definition.a
cc -o 10.out main.o noweak.a definition.a declaration.a
cc -o 11.out main.o definition.a declaration.a noweak.a
cc -o 12.out main.o declaration.a definition.a noweak.a
ar cr all.a declaration.o definition.o noweak.o
cc -o 13.out main.o all.a
ld -i -o all.o declaration.o definition.o noweak.o
cc -o 14.out main.o all.o
{ echo "definition.o declaration.o#->#only_weak_1.out#:#$(./only_weak_1.out)"; echo "declaration.o definition.o#->#only_weak_2.out#:#$(./only_weak_2.out)"; echo "definition.o#->#definition.out#:#$(./definition.out)"; echo "declaration.o#->#declaration.out#:#$(./declaration.out)"; echo "noweak.o#->#noweak.out#:#$(./noweak.out)"; echo "definition.a#->#definition_static.out#:#$(./definition_static.out)"; echo "declaration.a#->#declaration_static.out#:#$(./declaration_static.out)"; echo "noweak.a#->#noweak_static.out#:#$(./noweak_static.out)"; echo "declaration.o noweak.o#->#1.out#:#$(./1.out)"; echo "noweak.o declaration.o#->#2.out#:#$(./2.out)"; echo "declaration.a noweak.a#->#3.out#:#$(./3.out)"; echo "noweak.a declaration.a#->#4.out#:#$(./4.out)"; echo "definition.o noweak.o#->#5.out#:#$(./5.out)"; echo "noweak.o definition.o#->#6.out#:#$(./6.out)"; echo "definition.a noweak.a#->#7.out#:#$(./7.out)"; echo "noweak.a definition.a#->#8.out#:#$(./8.out)"; echo "noweak.a definition.a declaration.a#->#10.out#:#$(./10.out)"; echo "definition.a declaration.a noweak.a#->#11.out#:#$(./11.out)"; echo "declaration.a definition.a noweak.a#->#12.out#:#$(./12.out)"; echo "all.a#->#13.out#:#$(./13.out)"; echo "all.o#->#14.out#:#$(./14.out)"; } | { echo; column -t -s'#' -N 'objects, ,executable, ,output' -o' '; echo; }
objects executable output
definition.o declaration.o -> only_weak_1.out : definition.c
declaration.o definition.o -> only_weak_2.out : declaration.c
definition.o -> definition.out : definition.c
declaration.o -> declaration.out : declaration.c
noweak.o -> noweak.out : noweak.c
definition.a -> definition_static.out : definition.c
declaration.a -> declaration_static.out : declaration.c
noweak.a -> noweak_static.out : noweak.c
declaration.o noweak.o -> 1.out : noweak.c
noweak.o declaration.o -> 2.out : noweak.c
declaration.a noweak.a -> 3.out : declaration.c
noweak.a declaration.a -> 4.out : noweak.c
definition.o noweak.o -> 5.out : noweak.c
noweak.o definition.o -> 6.out : noweak.c
definition.a noweak.a -> 7.out : definition.c
noweak.a definition.a -> 8.out : noweak.c
noweak.a definition.a declaration.a -> 10.out : noweak.c
definition.a declaration.a noweak.a -> 11.out : definition.c
declaration.a definition.a noweak.a -> 12.out : declaration.c
all.a -> 13.out : declaration.c
all.o -> 14.out : noweak.c
在仅使用弱符号的情况下(仅only_weak_1和only_weak_2),将使用第一个定义。
如果只有静态库(情况3、4、7、8、10、11、12、13),则使用第一个定义。
如果仅使用目标文件(情况1、2、5、6、14),则弱符号将被省略,而仅使用noweak中的符号。
通过我提供的链接:
有多种方法可以确保armlink选择给定符号的非弱版本:
-请勿存档此类对象
-归档之前,请确保弱符号和非弱符号包含在同一对象中
-使用部分链接作为替代。