有没有一种方法可以使链接程序从库中提取目标文件的一部分进行链接?

时间:2019-02-09 14:36:41

标签: c linker translation-unit

我有一个包含数千个C文件,许多库和数十个程序的项目,要链接起来,为了加快编译速度,我将C文件组合为包含多个C文件的翻译单元。有时称为单一编译单元,单一翻译单元或统一构建。

我将这些翻译单元中的多个翻译成不同的库,并且这些库以前是通过分别编译每个C文件来创建的。

例如:

旧的library.lib:

file1.o
file2.o
file3.o
file4.o
file5.o
file6.o

新library.lib:

translation_unit_1.o
translation_unit_2.o

translation_unit_1.c:

#include "file1.c"
#include "file2.c"
#include "file3.c"

translation_unit_2.c:

#include "file4.c"
#include "file5.c"
#include "file6.c"

因此它们被编译为:translation_unit_1.o和translation_unit_2.o。该库是上面显示的新library.lib。

现在说我有一个要链接到library.lib的程序,该程序引用file2.c中的函数。但是它具有file1.c的不同版本,可以编译该版本以复制库中file1.c中的符号,因此只需要来自library.lib的file2.c进行链接。或者也许我需要从file1.c链接代码,但无法链接file2.c,因为它具有我不想依赖的依赖关系(下面的示例)。

程序:

main.o
file1.o
library.lib

您知道的任何链接器是否有办法使链接器仅将文件2.c中的代码从translation_unit_1.o对象代码中拉出,并使用该链接来链接main.o以创建程序?

另一种选择是,如果可能的话,将translation_unit_1.o拆分为file1.o,file2.o和file3.o,然后将其提供给链接器。

感谢您的帮助。

修改1

这是针对使用基于ARM ADS 1.2工具链编译的ELF的裸机ARM平台和使用Visual Studio工具链的Windows平台编译的单个代码库。但是,欢迎提出有关如何在其他平台和工具链上解决该问题的想法。

这是在MacOS上使用clang的具体示例。

下面的示例代码在这里:https://github.com/awmorgan/single_translation_unit_lib_link

图书馆:

file1.c需要链接此文件

file2.c,此文件不用于链接,并且具有未解决的依赖关系,该依赖关系可能在另一个库或对象中

main.c:

int main( void ) {
    extern int file1_a( void );
    int x = file1_a();
}

file1.c:

int file1_a(void) {
    return 1;
}

file2.c:

int file2_a( void ) {
    extern int file3_a( void );
    return file3_a(); // file3_a() is located somewhere else
}

single_translation_unit.c:

#include "file1.c"
#include "file2.c"

这可以产生program1.out:

++ clang -c file1.c -o file1.o
++ clang -c file2.c -o file2.o
++ libtool -static file1.o file2.o -o library1.lib
++ clang -c main.c -o main1.o
++ clang main1.o library1.lib -o program1.out

这无法产生program2.out:

++ clang -c single_translation_unit.c -o single_translation_unit.o
++ libtool -static single_translation_unit.o -o library2.lib
++ clang -c main.c -o main2.o
++ clang main2.o library2.lib -o program2.out
Undefined symbols for architecture x86_64:
  "_file3_a", referenced from:
      _file2_a in library2.lib(single_translation_unit.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

更改链接顺序也不起作用:

++ clang library2.lib main2.o -o program2.out
Undefined symbols for architecture x86_64:
  "_file3_a", referenced from:
      _file2_a in library2.lib(single_translation_unit.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

2 个答案:

答案 0 :(得分:0)

  

是否可以使用clang,gcc,microsoft或任何链接器

clanggccmicrosoft中没有一个是链接程序(前两个是编译器,第三个是公司)。

答案还取决于平台(您未指定)。

如果您是在Linux或其他ELF平台上构建的,则可以使用-ffunction-sections -fdata-sections编译代码,链接器将自动执行您想要的操作。

  

有没有一种方法可以使链接程序从库中提取目标文件的一部分进行链接?

通常,链接器对节进行操作,并且不能将节分开(全部或全部不得到)。

在没有-ffunction-sections的情况下,单个翻译单元中的所有函数最终都在单个.text节中(这是一个近似值-{{1}的模板实例化和离线函数定义}函数通常以自己的一部分结尾)。因此,链接器无法选择inline的部分,但不是全部。

答案 1 :(得分:0)

使用GCC / binutils ELF工具链或适当兼容的工具,您可以通过以下方式实现:

  1. 使用选项-ffunction-sections-fdata-sections
  2. 编译single_translation_unit.c
  3. 使用链接器选项选项-gc-sections链接program2.out

例如(在Linux上):

$ gcc -ffunction-sections -fdata-sections -c single_translation_unit.c -o single_translation_unit.o
$ ar rcs library2.a single_translation_unit.o # On Mac OS, use libtool to make the static library if you prefer.
$ gcc -c main.c -o main2.o
$ gcc main2.o library2.a -Wl,-gc-sections -o program2.out

您可以将gcc替换为clang

链接成功的原因是:

  • 在编译中,-ffunction-sections指示编译器发出每个函数定义 在目标文件的不同代码段中,不包含任何其他内容,而不是将它们全部合并为 默认情况下,一个.text部分。
  • 在链接中,-Wl,-gc-sections指示链接器丢弃未使用的节, 即程序未引用任何符号的部分。
  • 未引用函数file2_a的定义获得了不同的代码段, 不包含任何其他内容,因此未使用。链接器能够丢弃此未使用的节,以及它 file3_a的定义中未解析的对file2_a的引用。

因此,最终没有链接到file2_afile3_a的引用,如我们所见:

$ nm program2.out | egrep '(file2_a|file3_a)'; echo Done
Done

如果我们重新进行链接以请求映射文件:

$ gcc main2.o library2.a -Wl,-gc-sections,-Map=mapfile -o program2.out

然后地图文件将向我们显示:

...
...
Discarded input sections

 ...
 ...
 .text.file2_a  0x0000000000000000        0xb library2.a(single_translation_unit.o)
 ...
 ...

功能部分text.file2.a起源于library2.a(single_translation_unit.o) 确实被扔掉了。

顺便说一句...

由于the way a static library is used in linkage, 将单个目标文件single_translation_unit.o单独归档到静态库中没有任何意义 library2,然后将程序链接到library2(如果您知道程序引用了 any single_translation_unit.o中定义的符号。您最好跳过创建library2的操作,而只需链接single_translation_unit.o。 鉴于需要使用single_translation_unit.o中定义的符号,因此链接:

$ gcc main2.o library2.a [-Wl,-gc-sections] -o program2.out

与以下链接完全相同:

$ gcc main2.o single_translation_unit.o [-Wl,-gc-sections] -o program2.out

有或没有-Wl,-gc-sections

然后...

我相信您知道,虽然统一的构建对于从干净的构建来说是最快的, 与大多数自动构建系统(通常为Make based)相比,对于大多数增量构建而言,速度可能同样缓慢 经过精心设计,可以最大程度地减少每次源更改所需的重建量。如果可能的话, 得益于统一构建,它仅来自统一构建以及有效的增量构建。