首页 > 解决方案 > 有没有办法让链接器从库中提取目标文件的一部分进行链接?

问题描述

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

我将这些翻译单元中的多个编译到不同的库中,这些库之前是通过单独编译每个 C 文件来创建的。

例如:

旧库.lib:

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

新库.lib:

translation_unit_1.o
translation_unit_2.o

翻译单元_1.c:

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

翻译单元_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

您知道的任何链接器是否有办法让链接器仅将 file2.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 此文件不用于链接,并且具有未解决的依赖关系,可能位于另一个库或对象中

主.c:

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

文件1.c:

int file1_a(void) {
    return 1;
}

文件2.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)

标签: clinkertranslation-unit

解决方案


使用 GCC/binutils ELF 工具链或适当兼容的工具,您可以通过以下方式执行此操作:

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

例如(在 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

你可以gccclang整个替换。

链接成功是因为:

  • 在编译中,指示编译器在目标文件的不同代码段中发出每个函数定义,不包含任何其他内容,而不是按照默认-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) 被扔掉了。

顺便提一句...

由于在链接中使用静态库的方式single_translation_unit.o如果 library2library2知道您的程序 引用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,它精心打造最小化每次源更改所需的重建量。如果您可以从统一构建中受益,那么您可能只能从统一构建以及高效的增量构建中受益。


推荐阅读