C 有没有办法让链接器从库中提取对象文件的一部分进行链接?

C 有没有办法让链接器从库中提取对象文件的一部分进行链接?,c,linker,translation-unit,C,Linker,Translation Unit,我有一个包含数千个C文件、许多库和数十个程序的项目要链接,为了加快编译速度,我将C文件合并到包含多个C文件的翻译单元中。这有时被称为单个编译单元、单个翻译单元或统一构建 我将多个翻译单元编译到不同的库中,这些库以前是通过单独编译每个C文件创建的 例如: old library.lib: file1.o file2.o file3.o file4.o file5.o file6.o translation_unit_1.o translation_unit_2.o new library.li

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

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

例如:

old library.lib:

file1.o
file2.o
file3.o
file4.o
file5.o
file6.o
translation_unit_1.o
translation_unit_2.o
new library.lib:

file1.o
file2.o
file3.o
file4.o
file5.o
file6.o
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\u unit\u 1.o和translation\u unit\u 2.o。该库是上面显示的new 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中从翻译单元1.o对象代码中提取代码,并使用该代码链接main.o以生成程序

另一种方法是将翻译单元1.o拆分为file1.o、file2.o、file3.o(如果可能的话),然后将其提供给链接器

谢谢你的帮助

编辑1

这适用于为使用ARM ADS 1.2工具链编译的ELF的裸机ARM平台和使用Visual Studio工具链的Windows平台编译的单个代码库。然而,关于如何在其他平台和工具链上解决这个问题的想法是受欢迎的

下面是一个使用clang的MacOS的具体示例

下面是示例代码:

图书馆:

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
}
单翻译单位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)
有没有办法与clang、gcc、microsoft或任何链接器联系

clang
gcc
microsoft
都不是链接器(前两个是编译器,第三个是公司)

答案还取决于平台(您没有指定)

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

有没有办法让链接器从库中提取对象文件的一部分进行链接

一般来说,链接器在分区上运行,并且不能将分区分开(您要么得到全部,要么什么也得不到)

如果没有
-f函数节
,单个翻译单元中的所有函数都会在单个
.text
节中结束(这是一个近似值--
内联
函数的模板实例化和越位函数定义通常会在它们自己的节中结束)。因此,链接器不能选择
.text
的部分,但不能选择全部

有没有办法与clang、gcc、microsoft或任何链接器联系

clang
gcc
microsoft
都不是链接器(前两个是编译器,第三个是公司)

答案还取决于平台(您没有指定)

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

有没有办法让链接器从库中提取对象文件的一部分进行链接

一般来说,链接器在分区上运行,并且不能将分区分开(您要么得到全部,要么什么也得不到)


如果没有
-f函数节
,单个翻译单元中的所有函数都会在单个
.text
节中结束(这是一个近似值--
内联
函数的模板实例化和越位函数定义通常会在它们自己的节中结束)。因此,链接器无法使用GCC/binutils ELF工具链或适当兼容的工具选择
.text

的部分(但不是全部),您可以通过以下方式完成此操作:

  • 使用选项编译
    single\u translation\u unit.c
  • 使用链接器选项链接
    程序2.out
  • 例如(在Linux上):

    您可以在整个过程中将
    gcc
    替换为
    clang

    链接成功是因为:

    • 在编译中,
      -fffunction节
      指示编译器发出每个函数定义 在对象文件的不同代码段中,不包含任何其他内容,而是将它们全部合并到 默认情况下,一个
      .text
    • 在链接中,
      -Wl,-gc节
      指示链接器丢弃未使用的节, i、 e.程序未引用符号的部分
    • 未引用函数
      file2\u a
      的定义获得了一个不同的代码段, 不包含任何其他内容,因此未使用。链接器能够丢弃这个未使用的部分,并将其一起丢弃
      file2\u a
      定义中对
      file3\u a
      的未解析引用
    因此,最终没有链接到
    file2\u a
    file3\u 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)
     ...
     ...
    
    $ gcc main2.o single_translation_unit.o [-Wl,-gc-sections] -o program2.out