__attribute __((弱))和静态库

时间:2018-08-02 15:04:31

标签: c++ g++

我想在代码中引入一个弱符号,但是,当使用* .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.cmake -c b.c)还是* .a文件(ar cr a.oar 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进行编译是不可行的)。

2 个答案:

答案 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.cppmain.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.ob.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.ab.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.ab.a的链接顺序也没关系, 但这是因为它们都不对链接有任何贡献。

现在让我们谈谈通过更改a.ha.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.obb.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.obb.o,并且每个都包含 foo的定义,没有多定义错误,因为每个定义 是aa.obb.o之前被加载,并且foo的定义从aa.o链接。

那么foobb.o的定义发生了什么?映射文件显示给我们:

映射文件(1)

...
...
Discarded input sections
...
...
 .text.foo      0x0000000000000000       0x13 bb.o
...
...

链接器丢弃了包含定义的功能部分 在bb.o

让我们颠倒aa.obb.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.oaa.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

现在,fooaa.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.obb.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.cmv 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选择给定符号的非弱版本:
  -请勿存档此类对象
  -归档之前,请确保弱符号和非弱符号包含在同一对象中
  -使用部分链接作为替代。