2016-08-09 64 views
0

我做了一个实验,看看如果我试着获得相同的函数在那里编译两次,会产生什么样的汇编语言。我做了以下内容:C链接器和重复符号

我创建了两个简单的测试文件及其相应的标题。我们称它们为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中调用,而不是内联的,然而,这只是有一个在代码中定义一个,没有重复。

看来GCC有两种:

  1. 检测到的复制对象的代码和除去的B归档首次使用的重复 - 或 -
  2. ,并且它包括为int一个基准(),所以一个存档被忽略。

我的问题是:这种行为是否符合我的测试要求?还是标准的,我能期待其他编译器的行为吗?显然重复代码是一个问题,但是也可能有重复的全局引用。构建一个具有多个依赖路径到相同静态存档的大型应用程序是否安全/良好实践?是否存在不太明显的情况,而不仅仅是重复的符号名称,在执行此操作时会出现问题

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

+1

[如何处理C中重复的函数名?]可能的重复(http://stackoverflow.com/questions/3098380/how-to-deal-with-duplicated-function-name-within-c) – KevinDTimm

+0

目前你的'bc'没有看到'a()'的声明。在'b.c'中缺少'#include“a.h”'? – jweyrich

+1

建议删除C++标记 – chux

回答

0

我的问题是:这种行为是否符合我的测试要求?还是标准的,我能期待其他编译器的行为吗?

就编译器本身而言,不存在任何问题:对于源中的每个函数都有一个定义。

ar而言,您也不存在任何问题:您所创建的存档都不包含任何重复的符号。

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

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

“到相同静态存档的多个路径”似乎不能很好地表征您所呈现的情况。在任何情况下,您都不止一次提供相同的档案。相反,在b的情况下,您提供了具有重复成员的不同档案。链接器通常在同一个链接命令中多次指定相同的存档时遇到问题。在某些情况下,甚至有必要这样做;它不应该出现问题。

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

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

+0

非常感谢您的反应。我应该澄清我的'多重路径'声明,说当我将二进制与a.a和b.a,b链接时。a具有也在a.a中定义的函数,但链接器在这种情况下足够聪明以选择一个函数。我关心的情况可能不会。 –

+0

我有一个包含在多个二进制文件中的公共代码存档文件,而且我还将其中一个二进制文件的代码拉入另一个。编译这个项目有两种方法。一个是单一的,一个不是。这是基本的想法。 –