简体   繁体   English

如何防止静态库中的所有符号加载以及链接静态库时为什么导出来自同一.o文件的其他符号以进行测试

[英]How to prevent all symbols from static library to load and why other symbols from same .o file get exported to test while linking static library

Suppose there are three c files, say ac contains functions xx() , yy() and bc contains nn() , mm() and cc contains qq() , rr() . 假设有三个c文件,例如ac包含函数xx()yy()bc包含nn()mm()cc包含qq()rr()

I made a static library stat.a out of ao , bo and co . 我用aoboco制作了一个静态库stat.a If I link stat.a into a test which calls xx() , then symbol yy() also gets exported: nm test has both symbols xx and yy . 如果我将stat.a链接到调用xx()的测试中,则符号yy()也将导出: nm test具有符号xxyy

  1. I would like to know why the symbols qq and rr do not get exported ? 我想知道为什么qqrr符号没有导出?
  2. Is there any method to prevent any other symbols than xx being loaded? 有什么方法可以防止加载除xx以外的其他符号?
  1. I would like to know why the symbols qq and rr do not get exported ? 我想知道为什么qq和rr符号没有导出?

You have to inform the linker of your intention How to force gcc to link an unused static library 您必须将意图告知链接器。 如何强制gcc链接未使用的静态库

gcc -L./ -o test test.c -Wl,--whole-archive stat.a -Wl,--no-whole-archive gcc -L./ -o test test.c -Wl,-整体存档状态a -Wl,-无整体存档

  1. Is there any method to prevent any other symbols than xx being loaded? 有什么方法可以防止加载除xx以外的其他符号?

From How do I include only used symbols when statically linking with gcc? 与gcc静态链接时,我该如何仅包括使用的符号?

gcc -ffunction-sections -c ac gcc -fact-sections -c ac

gcc -L./ -o test test.c -Wl,--gc-sections stat.a gcc -L./ -o test test.c -Wl,-gc-sections stat.a

Here is an implementation of your scenario: 这是您的方案的实现:

ac 交流电

#include <stdio.h>

void xx(void)
{
    puts(__func__);
}

void yy(void)
{
    puts(__func__);
}

bc 公元前

#include <stdio.h>

void nn(void)
{
    puts(__func__);
}

void mm(void)
{
    puts(__func__);
}

cc 抄送

#include <stdio.h>

void qq(void)
{
    puts(__func__);
}

void rr(void)
{
    puts(__func__);
}

test.c 测试

extern void xx(void);

int main(void)
{
    xx();
    return 0;
}

Compile all the *.c files to *.o files: 将所有*.c文件编译为*.o文件:

$ gcc -Wall -c a.c b.c c.c test.c

Make a static library stat.a , containing ao , bo , co : 创建一个静态库stat.a ,其中包含aoboco

$ ar rcs stat.a a.o b.o c.o

Link program test , inputting test.o and stat.a : 链接程序test ,输入test.ostat.a

$ gcc -o test test.o stat.a

Run: 跑:

$ ./test
xx

Let's see the symbol tables of the object files in stat.a : 让我们看看stat.a目标文件的符号表:

$ nm stat.a

a.o:
0000000000000000 r __func__.2250
0000000000000003 r __func__.2254
                 U _GLOBAL_OFFSET_TABLE_
                 U puts
0000000000000000 T xx
0000000000000013 T yy

b.o:
0000000000000000 r __func__.2250
0000000000000003 r __func__.2254
                 U _GLOBAL_OFFSET_TABLE_
0000000000000013 T mm
0000000000000000 T nn
                 U puts

c.o:
0000000000000000 r __func__.2250
0000000000000003 r __func__.2254
                 U _GLOBAL_OFFSET_TABLE_
                 U puts
0000000000000000 T qq
0000000000000013 T rr

The definitions ( T ) of xx , yy are in member stat.a(ao) . xxyy的定义( T )在成员stat.a(ao) Definitions of nn , mm are in stat.a(bo) . nnmm定义在stat.a(bo) Definitions of qq , rr are in stat.a(co) . qqrr定义在stat.a(co)

Let's see which of those symbols are also defined in the symbol table of the program test : 让我们看看在程序test的符号表中也定义了哪些符号:

$ nm test | egrep 'T (xx|yy|qq|rr|nn|mm)'
000000000000064a T xx
000000000000065d T yy

xx , which is called in the program, is defined. 定义了在程序中调用的 xx yy , which is not called, is also defined. 还定义了yy调用)。 nn , mm , qq and rr , none of which are called, are all absent. 都不存在nnmmqqrr ,都没有被调用。

That's what you've observed. 那就是你所观察到的。

I would like to know why the symbols qq and rr do not get exported? 我想知道为什么符号qqrr无法导出?

What is a static library , such as stat.a , and what is its special role in a linkage? 什么是静态库 ,例如stat.a ,它在链接中的特殊作用是什么?

It is an ar archive that conventionally - but not necessarily - contains nothing but object files. 它是一个ar档案 ,按惯例-但不一定-除目标文件外不包含任何内容。 You can offer such an archive to the linker from which to select the object files it needs , if any, to carry on the linkage. 你可以提供这样的存档,以从中选择需要的目标文件,如果有的话,进行联动的连接。 The linker needs those object files in the archive that provide definitions for symbols that have been referenced, but not yet defined, in input files it has already linked. 链接器需要档案库中的那些目标文件,这些文件提供了已链接的输入文件中已引用但尚未定义的符号的定义。 The linker extracts the needed object files from the archive and inputs them to the linkage, exactly as if they were individually named input files and the static library was not mentioned at all. 链接器从存档中提取所需的目标文件,并将它们输入到链接中,就像它们分别被命名为输入文件一样,并且根本没有提及静态库。

So what the linker does with an input static library is different from what it does with an input object file . 因此,链接器对输入静态库所做的操作与对输入目标文件所做的操作不同。 Any input object file is linked into the output file unconditionally (whether it is needed or not). 任何输入目标文件都无条件链接到输出文件(无论是否需要)。

In this light, let's redo the linkage of test with some diagnostics ( -trace) to show what files are actually linked: 鉴于此,让我们重做test与某些诊断程序( -trace)的链接,以显示实际链接的文件:

$ gcc -o test test.o stat.a -Wl,--trace
/usr/bin/x86_64-linux-gnu-ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o
test.o
(stat.a)a.o
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o

Apart from all the boiler-plate files for a C program linkage that gcc adds by default, the only files of ours in the linkage are the two object files: 除了gcc默认添加的C程序链接的所有样板文件之外,链接中我们唯一的文件是两个目标文件:

test.o
(stat.a)a.o

The linkage: 链接:

$ gcc -o test test.o stat.a

is exactly the same as the linkage: 与链接完全相同

$ gcc -o test test.o a.o

Let's think that through. 让我们仔细考虑一下。

  • test.o was the first linker input. test.o是第一个链接器输入。 This object file was linked unconditionally into the program. 该目标文件无条件地链接到程序中。
  • test.o contains a reference (specifically, a function call) to xx but no definition of the function xx . test.o包含对xx的引用(特别是函数调用),但没有函数xx定义。
  • So the linker now needs to find a definition of xx to complete the linkage. 因此,链接器现在需要找到xx的定义才能完成链接。
  • The next linker input is the static library stat.a . 下一个链接器输入是静态库stat.a
  • The linker searches stat.a for an object file that contains a defintion of xx . 链接器在stat.a中搜索包含xx定义的目标文件。
  • It finds ao . 找到ao It extracts ao from the archive and links it into the program. 它从存档中提取ao并将其链接到程序中。
  • There are no other unresolved symbol references in the linkage for which the linker can find definitions in stat.a(bo) or stat(co) . 链接器中没有其他未解析的符号引用,链接器可以在stat.a(bo)stat(co)找到其定义。 So neither of those object files is extracted and linked. 因此,这些目标文件都没有被提取和链接。

By extracting an linking (just) stat.a(ao) the linker has got a definition of xx that it needed to resolved the function call in test.o . 通过提取一个链接(仅) stat.a(ao) ,链接器获得了xx的定义,该定义需要解析test.o的函数调用。 But ao also contains the definition of yy . 但是ao 包含yy的定义。 So that definition is also linked into the program. 因此,该定义也链接到程序中。 nn , mm , qq and rr are not defined in the program because none of them are defined in the object files that were linked into the program. 在程序中未定义nnmmqqrr ,因为在链接到程序中的目标文件中均未定义它们。

That's the answer to your first question. 那就是你第一个问题的答案。 Your second is: 您的第二个是:

Is there any method to prevent any other symbols than xx being loaded? 有什么方法可以防止加载除xx以外的其他符号?

There are at least two ways. 至少有两种方法。

One is simply to define each of xx , yy , nn , mm , qq , rr in a source file by itself . 一个简单的方法是自己定义源文件中xxyynnmmqqrr Then compile object files xx.o , yy.o , nn.o , mm.o , qq.o , rr.o and archive all of them in stat.a . 然后编译目标文件xx.oyy.onn.omm.oqq.orr.o并将它们全部归档在stat.a Then, if the linker ever needs to find an object file in stat.a that defines xx , it will find xx.o , extract and link it, and the definition of xx alone will be added to linkage. 然后,如果链接器需要在stat.a中找到定义xx的目标文件,它将找到xx.o ,提取并链接它,并且xx的定义添加到链接中。

There's another way that does not require you code just one function in each source file. 还有另一种方法,不需要您在每个源文件中仅编写一个函数。 This way depends on the fact that an ELF object file, as produced by the compiler, is composed of various sections and these sections are in fact the units that the linker distinguishes and merges together into the output file. 这种方式取决于以下事实:由编译器生成的ELF目标文件由各个部分组成,而这些部分实际上是链接程序区分并合并到输出文件中的单元。 By default, there is a standard ELF section for each kind of symbol. 默认情况下,每种符号都有一个标准的ELF部分。 The compiler places all of the function definitions in one code section and all data definitions in an appropriate data section. 编译器将所有函数定义放在一个代码段中,并将所有数据定义放在一个适当的数据段中。 The reason that your linkage of program test contains the definitions of both xx and yy is that the compiler has placed both of these definitions in the single code section of ao , so the linker can either merge that code section into the program, or not: it can only link the definitions of xx and yy , or neither of them, so it is obliged to link both, even though only xx is needed. 您的程序test链接同时包含xxyy的定义的原因是,编译器已将这两个定义都放在ao的单个代码段中,因此链接程序可以将该代码段合并到程序中,也可以不合并:它只能链接xx yy的定义,也不能两者都链接,因此即使只需要xx ,也必须链接两者。 Let's see the disassembly of the code section of ao . 让我们看看ao的代码部分的反汇编。 By default the code section is is called .text : 默认情况下,代码段称为.text

$ objdump -d a.o

a.o:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <xx>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 8d 3d 00 00 00 00    lea    0x0(%rip),%rdi        # b <xx+0xb>
   b:   e8 00 00 00 00          callq  10 <xx+0x10>
  10:   90                      nop
  11:   5d                      pop    %rbp
  12:   c3                      retq

0000000000000013 <yy>:
  13:   55                      push   %rbp
  14:   48 89 e5                mov    %rsp,%rbp
  17:   48 8d 3d 00 00 00 00    lea    0x0(%rip),%rdi        # 1e <yy+0xb>
  1e:   e8 00 00 00 00          callq  23 <yy+0x10>
  23:   90                      nop
  24:   5d                      pop    %rbp
  25:   c3                      retq

There you see the definitions of xx and yy , both in the .text section. 在那里,您可以在.text部分看到xxyy的定义。

But you can ask the compiler to place the definition of each global symbol in its own section in the object file. 但是您可以要求编译器将每个全局符号的定义放在目标文件的其自己的部分中。 Then the linker can seperate the code section for any function definition from any other, and you can ask the linker to throw away any sections that aren't used in the output file. 然后,链接器可以将任何函数定义的代码部分与其他任何部分分开,并且您可以要求链接器丢弃输出文件中未使用的任何部分。 Let's try that. 让我们尝试一下。

Compile all the source files again, this time asking for a separate section per symbol: 再次编译所有源文件,这一次每个符号要求一个单独的部分:

$ gcc -Wall -ffunction-sections -fdata-sections -c a.c b.c c.c test.c

Now look again at the disassembly of ao : 现在再来看一下ao的反汇编:

$ objdump -d a.o

a.o:     file format elf64-x86-64


Disassembly of section .text.xx:

0000000000000000 <xx>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 8d 3d 00 00 00 00    lea    0x0(%rip),%rdi        # b <xx+0xb>
   b:   e8 00 00 00 00          callq  10 <xx+0x10>
  10:   90                      nop
  11:   5d                      pop    %rbp
  12:   c3                      retq

Disassembly of section .text.yy:

0000000000000000 <yy>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 8d 3d 00 00 00 00    lea    0x0(%rip),%rdi        # b <yy+0xb>
   b:   e8 00 00 00 00          callq  10 <yy+0x10>
  10:   90                      nop
  11:   5d                      pop    %rbp
  12:   c3                      retq

Now we've got two code sections in ao : .text.xx , containing only the definition of xx , and .text.yy , containing only the definition of yy . 现在我们在ao两个代码段: .text.xx ,仅包含xx的定义; .text.yy ,仅包含yy的定义。 The linker can merge either of these sections into a program and not merge the other. 链接器可以将这些部分中的任何一个合并到一个程序中,而不能合并另一个。

Rebuild stat.a 重建stat.a

$ rm stat.a
$ ar rcs stat.a a.o b.o c.o

Relink the program, this time asking the linker to discard unused input sections ( -gc-sections ). 重新链接程序,这次要求链接器丢弃未使用的输入节( -gc-sections )。 We'll also ask it to trace the files it loads ( -trace ) and to print a mapfile for us ( -Map=mapfile ): 我们还将要求它跟踪加载的文件( -trace )并为我们打印一个-Map=mapfile文件( -Map=mapfile ):

$ gcc -o test test.o stat.a -Wl,-gc-sections,-trace,-Map=mapfile
/usr/bin/x86_64-linux-gnu-ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o
test.o
(stat.a)a.o
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o

The -trace output is exactly the same as before. -trace输出与以前完全相同。 But check again which of our symbols are defined in the program: 但是,请再次检查程序中定义了哪些符号:

$ nm test | egrep 'T (xx|yy|qq|rr|nn|mm)'
000000000000064a T xx

Only xx , which is what you want. 只有xx ,这是您想要的。

The output of the program is the same as before: 该程序的输出与以前相同:

$ ./test
xx

Finally look at the mapfile. 最后看一下地图文件。 Near the top you see: 在顶部附近,您会看到:

mapfile 映射文件

...
Discarded input sections
...
...
 .text.yy       0x0000000000000000       0x13 stat.a(a.o)
...
...

The linker was able to throw away the redundant code section .text.yy from the input file stat.a(ao) . 链接器能够从输入文件stat.a(ao)丢弃冗余代码段.text.yy That's why the redundant definition of yy is no longer in the program. 这就是程序中不再存在yy的冗余定义的原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 链接到共享库时,不会导出 static 库中的符号 - Symbols from a static library are not exported while linking to a shared library 从静态库中剥离所有符号是否会阻止链接? - Does stripping all symbols from a static library prevent linking? dll链接静态库-未使用函数中未解析的链接器符号 - dll linking static library - unresolved linker symbols from unused functions 从C静态库中删除内部符号 - Removing internal symbols from C static library 在 Visual Studio 中编写静态库时如何控制导出的符号? - How to control exported symbols when writing a static library in Visual Studio? 强制使用GCC从链接的静态库导出特定的可见符号(同时排除其他符号) - Forcing specific visible symbol export from linked static library with GCC (while excluding other symbols) 链接从静态库中链接符号的动态库:macOS 与 Linux - Linking a dynamic library that links in symbols from a static library: macOS vs Linux 如何始终包含静态库中的符号? - how do I always include symbols from a static library? 已定义符号的静态库链接问题“未定义符号” - Static Library Linking Issue "Undefined symbols" for symbols that are defined 如何从 C static 库中分离调试符号,然后将其加载到 GDB - How to separate debug symbols from a C static library and later load it in GDB
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM