C链接器和重复的符号

时间:2016-08-09 21:09:51

标签: c gcc llvm

我做了一个实验,看看如果我尝试在那里编译两次相同的函数,将会生成什么样的汇编语言。我做了以下事情:

我创建了两个简单的测试文件及其相应的标题。我们称它们为a.c / a.h和b.c / b.h.以下是这些文件的内容:

A.H:

#ifndef __A_H__
#define __A_H__

int a( void );

#endif

b.h:

#ifndef __B_H__
#define __B_H__

int b( void );

#endif

交流转换器:

#include "a.h"

int a( void )
{
    return 1;
}

b.c:

#include "b.h"
#include "a.h"

int b( void )
{
    return 1 + a();
}

然后我为:

创建了一个静态存档
gcc -c a.c -o a.o
ar -rsc a.a a.o

和b相同,包括此时的静态存档:

gcc -c b.c -o b.o
ar -rsc b.a a.a b.o

此时,我反汇编了b的静态存档,以验证它是否包含函数a()和b()的汇编代码。确实如此。

现在,我定义了最后一个文件:

main.c中:

#include <stdio.h>

#include "a.h"
#include "b.h"

int main( void )
{
    printf( "%d %d\n", a(), b() );

    return 0;
}

我这样编译:

gcc main.c a.a b.a -o main

这很好用。当我反汇编它时,我在代码中看到了a和b的以下定义:

140 0000000000400561 <a>:
141   400561:   55                      push   %rbp
142   400562:   48 89 e5                mov    %rsp,%rbp
143   400565:   b8 01 00 00 00          mov    $0x1,%eax
144   40056a:   5d                      pop    %rbp
145   40056b:   c3                      retq
146 
147 000000000040056c <b>:
148   40056c:   55                      push   %rbp
149   40056d:   48 89 e5                mov    %rsp,%rbp
150   400570:   e8 ec ff ff ff          callq  400561 <a>
151   400575:   83 c0 01                add    $0x1,%eax
152   400578:   5d                      pop    %rbp
153   400579:   c3                      retq
154   40057a:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

正如你所看到的,代码已经明确地将b定义为调用而不是内联它,但是,代码中只有一个a的定义,没有重复。

似乎gcc有:

  1. 检测到重复的对象代码并删除了重复项 --or -
  2. 首先使用了b存档,它包含对int a()的引用,因此忽略了存档。
  3. 我的问题是:这种行为对我的测试来说是间接的还是标准的,我可以期待其他编译器的相同行为吗?显然重复的代码是一个问题,但是也可能存在重复的全局引用。构建具有到同一静态存档的多个依赖路径的大型应用程序是安全/良好的做法吗?是否有不太明显的情况,而不仅仅是重复的符号名称,这样做时会出现问题?

    问这个是因为我一直在为我正在进行的项目玩这个想法,并希望做出正确的选择。

1 个答案:

答案 0 :(得分:0)

  

我的问题是:这种行为对我的测试是间接的还是标准的,我可以期待其他编译器的相同行为吗?

就编译器本身而言,没有问题:您的源中的每个函数都有一个定义。

ar而言,您也没有任何问题:您构建的档案都不包含任何重复的符号。

然而,不同的接头可能表现出不同的行为。可以想象有些人会拒绝包含重复外部符号的链接存档。典型的UNIX链接器将处理您提供的情况,但它们可能在某些细节上有所不同,例如函数a()的副本是否包含在二进制文件中。

  

显然重复的代码是一个问题,但是也可能存在重复的全局引用。构建具有到同一静态存档的多个依赖路径的大型应用程序是安全/良好的做法吗?

“通往同一静态存档的多条路径”似乎不能很好地描述您所呈现的情况。在这两种情况下,您都不会多次提供相同的存档。相反,在b案例中,您提供了具有重复成员的不同档案。链接器通常在同一链接命令中多次指定相同的存档时没有问题。在某些情况下甚至可能需要这样做;它不应该出现问题。

提供具有重复成员可能的不同存档不会出现问题,除非可能使用重复的函数实现使代码膨胀。这有点不太确定,但我怀疑它在实践中会出现问题。

这种良好做法是否属于意见问题,但我倾向于不这么认为。我也不清楚你在这种方法中看到了什么。另一方面,如果您决定继续进行,我将不会削减任何赌注或准备任何点燃。