静态库(.a)和共享库(.so)之间的文件格式差异?

时间:2021-10-26 03:28:58

I know that there are lots of questions about the use cases of shared vs static libraries, this question is not about that. I am asking about differences in file format stored on disk.

我知道有很多关于共享和静态库的用例的问题,这个问题与此无关。我问的是存储在磁盘上的文件格式的差异。

Why question is, what are the differences between the two? Or are they exactly the same, different only in terms of usage?

为什么问题是,两者之间有什么区别?或者它们是否完全相同,仅在使用方面有所不同?

I am lead to believe that they are not the same, since running 'nm' on a shared library requires the -D flag. Clearly it needs to do something differently. Why?

我认为它们不一样,因为在共享库上运行'nm'需要-D标志。显然,它需要做一些不同的事情。为什么?

Are they both ELF files?

它们都是ELF文件吗?

Is the only difference that the shared library can contain some paths of dependencies?

共享库可以包含某些依赖路径的唯一区别是什么?

3 个解决方案

#1


6  

A static library, e.g. libfoo.a is not an executable of any kind. It is simply an indexed archive in unix ar format of other files which happen to be ELF object files.

静态库,例如libfoo.a不是任何类型的可执行文件。它只是其他文件的unix ar格式的索引存档,恰好是ELF目标文件。

A static library is created like any archive:

像任何存档一样创建静态库:

ar crs libfoo.a objfile0.o objfile1.0...objfileN.o

outputs the new archive (c) libfoo.a, with those object files inserted (r) and index added (s).

输出新存档(c)libfoo.a,插入(r)和添加索引的目标文件。

You'll hear of linking libfoo.a in a program. This doesn't mean that libfoo.a itself is linked into or with the program. It means that libfoo.a is passed to the linker as an archive from which it can extract and link into the program just those object files within the archive that the program needs. So the format of a static libary (ar format) is just an object-file bundling format for linker input: it could equally well have been some other bundling format without any effect on the linker's mission, which is to digest a set of object files and shared libraries and generate a program, or shared library, from them. ar format was history's choice.

你会听到在程序中链接libfoo.a。这并不意味着libfoo.a本身与程序相关联或与程序相关联。这意味着libfoo.a作为存档传递给链接器,它可以从中提取程序所需的存档中的目标文件并将其链接到程序中。因此静态库(ar格式)的格式只是链接器输入的对象文件绑定格式:它同样可能是其他一些捆绑格式,对链接器的任务没有任何影响,即消化一组目标文件和共享库,并从中生成程序或共享库。 ar格式是历史的选择。

On the other hand a shared library, e.g. libfoo.so, is an ELF file and not any sort of archive.

另一方面,共享库,例如, libfoo.so,是一个ELF文件,而不是任何类型的存档。

Don't be tempted to suspect that a static library is a sort of ELF file by the fact that all the well-known ELF-parsers - objdump, readelf, nm - will parse a static libary. These tools all know that a static library is an archive of ELF object files, so they just parse all the object files in the library as if you had listed them on the commandline.

不要因为所有着名的ELF解析器(objdump,readelf,nm)将解析静态库这一事实而怀疑静态库是一种ELF文件。这些工具都知道静态库是ELF目标文件的存档,因此它们只是解析库中的所有目标文件,就像您在命令行上列出它们一样。

The use of the -D option with nm just instructs the tool to select only the symbols that are in the dynamic symbol table(s), if any, of the ELF file(s) that it parses - the symbols visible to the runtime linker - regardless of whether or not they are parsed from within an archive. It's the same as objdump -T and readelf --dyn-syms. It is not necessary to use these options to parse the symbols from a shared library. If you don't do so, then by default you'll just see the full symbol table. If you run nm -D on a static library you'll be told no symbols, for each object file in the archive - likewise if you ran nm -D for each of those object files individually. The reason for that is that an object file hasn't got a dynamic symbol table: only a shared library or progam has one.

将-D选项与nm一起使用只是指示工具仅选择它所分析的ELF文件的动态符号表(如果有)中的符号 - 运行时链接程序可见的符号 - 无论是否从存档中解析它们。它与objdump -T和readelf --dyn-syms相同。没有必要使用这些选项来解析共享库中的符号。如果您不这样做,那么默认情况下您只会看到完整的符号表。如果在静态库上运行nm -D,则会为档案中的每个目标文件指定无符号 - 同样如果您为每个目标文件单独运行nm -D。原因是目标文件没有动态符号表:只有共享库或程序有一个。

Object file, shared library and program are all variants of the ELF format. If you're interested in ELF variants, those are the variants of interest.

目标文件,共享库和程序都是ELF格式的变体。如果您对ELF变体感兴趣,那些是感兴趣的变体。

The ELF format itself is a long and thorny technical read and is required background for precisely distinguishing the variants. Intro: An ELF file contains a ELF header structure one of whose fields contains a type-identifier of the file as an object file, shared library, or program. When the file is a program or shared library, it also contains an optional Program header table structure whose fields provide the runtime linker/loader with the parameters it needs to load the file in a process. In terms of ELF structure, the differences between a program and a shared library are slight: it's the detailed content that makes the difference to the behaviour that they elicit from the loader.

ELF格式本身是一个冗长而棘手的技术阅读,是精确区分变体的必要背景。简介:ELF文件包含ELF头结构,其中一个字段包含文件的类型标识符作为目标文件,共享库或程序。当文件是程序或共享库时,它还包含一个可选的程序头表结构,其字段为运行时链接程序/加载程序提供在进程中加载​​文件所需的参数。就ELF结构而言,程序和共享库之间的差异很小:它是详细的内容,它们对从加载器中引出的行为产生影响。

For the long and thorny technical read, try Excutable and Linkable Format (ELF)

对于漫长而棘手的技术阅读,请尝试Excutable和Linkable Format(ELF)

#2


1  

Source

The source code I'm using in my example is as follows:

我在我的例子中使用的源代码如下:

class T {
public:
    T(int _x) : x(_x) { }
    T& operator=(const T& rhs) { x = rhs.x; return *this; }
    int getX() const { return x; }

private:
    int x = 0;
};

Creating the shared library

创建共享库

$ g++ -shared -fPIC -c test.cpp -o test.out && ld -o libtest.so test.out 
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400078

Creating the static library

创建静态库

$ g++ -fPIC -c test.cpp -o test.out && ar rcs libtest.a test.out

Are they both ELF files?

它们都是ELF文件吗?

Kind of ... here's the output of readelf -h for the shared library:

有点......这是共享库的readelf -h的输出:

$ readelf -h libtest.so 
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x400078
  Start of program headers:          64 (bytes into file)
  Start of section headers:          408 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         1
  Size of section headers:           64 (bytes)
  Number of section headers:         5
  Section header string table index: 2

The static library output is very similar, but not quite the same:

静态库输出非常相似,但不完全相同:

$ readelf -h libtest.a

File: libtest.a(test.out)
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          360 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           64 (bytes)
  Number of section headers:         9
  Section header string table index: 6

The first thing that jumps out is the File entry in the static library. Rather than being an ELF object, it contains an ELF object. Another way of confirming this is by looking at the files with hexdump -C (truncated). First, the shared library:

跳出来的第一件事是静态库中的File条目。它不是一个ELF对象,而是包含一个ELF对象。确认这一点的另一种方法是使用hexdump -C(截断)查看文件。首先,共享库:

$ hexdump -C libtest.so
00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 3e 00 01 00 00 00  78 00 40 00 00 00 00 00  |..>.....x.@.....|
00000020  40 00 00 00 00 00 00 00  98 01 00 00 00 00 00 00  |@...............|
00000030  00 00 00 00 40 00 38 00  01 00 40 00 05 00 02 00  |....@.8...@.....|
00000040  51 e5 74 64 06 00 00 00  00 00 00 00 00 00 00 00  |Q.td............|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000070  10 00 00 00 00 00 00 00  47 43 43 3a 20 28 47 4e  |........GCC: (GN|

We can see the character sequence ELF quite clearly here, right at the start of the file. Here's the static library output:

我们可以在文件的开头直接看到字符序列ELF。这是静态库输出:

$ hexdump -C libtest.a
00000000  21 3c 61 72 63 68 3e 0a  2f 20 20 20 20 20 20 20  |!<arch>./       |
00000010  20 20 20 20 20 20 20 20  31 34 38 35 34 36 31 31  |        14854611|
00000020  36 36 20 20 30 20 20 20  20 20 30 20 20 20 20 20  |66  0     0     |
00000030  30 20 20 20 20 20 20 20  34 20 20 20 20 20 20 20  |0       4       |
00000040  20 20 60 0a 00 00 00 00  74 65 73 74 2e 6f 75 74  |  `.....test.out|
00000050  2f 20 20 20 20 20 20 20  31 34 38 35 34 36 31 31  |/       14854611|
00000060  36 36 20 20 31 30 30 30  20 20 31 30 30 30 20 20  |66  1000  1000  |
00000070  31 30 30 36 36 34 20 20  39 33 36 20 20 20 20 20  |100664  936     |
00000080  20 20 60 0a 7f 45 4c 46  02 01 01 00 00 00 00 00  |  `..ELF........|
00000090  00 00 00 00 01 00 3e 00  01 00 00 00 00 00 00 00  |......>.........|
000000a0  00 00 00 00 00 00 00 00  00 00 00 00 68 01 00 00  |............h...|
000000b0  00 00 00 00 00 00 00 00  40 00 00 00 00 00 40 00  |........@.....@.|
000000c0  09 00 06 00 00 47 43 43  3a 20 28 47 4e 55 29 20  |.....GCC: (GNU) 

We can see a bunch of extra stuff before the ELF header starts here, confirming our hypothesis that a static library is stored differently from a shared library.

我们可以在ELF标题开始之前看到一堆额外的东西,这证实了我们的假设,即静态库的存储方式与共享库不同。

Another difference is the Type entry; the shared library is marked as executable whilst the static library is not. In fact, there's not much difference between a shared library and an executable at all: https://askubuntu.com/questions/690631/executables-vs-shared-objects

另一个区别是Type入口;共享库标记为可执行文件,而静态库不标记为可执行文件。实际上,共享库和可执行文件之间没有太大区别:https://askubuntu.com/questions/690631/executables-vs-shared-objects

#3


0  

A static library is little more than a collection of relocatable objects (it's not even ELF, but a dummy archive of ELFs).

静态库只不过是可重定位对象的集合(它甚至不是ELF,而是ELF的虚拟存档)。

A shared library is a standalone piece of functionality with a defined interface (i.e. symbol table) and dependencies (it is an ELF file).

共享库是一个独立的功能,具有定义的接口(即符号表)和依赖项(它是ELF文件)。

#1


6  

A static library, e.g. libfoo.a is not an executable of any kind. It is simply an indexed archive in unix ar format of other files which happen to be ELF object files.

静态库,例如libfoo.a不是任何类型的可执行文件。它只是其他文件的unix ar格式的索引存档,恰好是ELF目标文件。

A static library is created like any archive:

像任何存档一样创建静态库:

ar crs libfoo.a objfile0.o objfile1.0...objfileN.o

outputs the new archive (c) libfoo.a, with those object files inserted (r) and index added (s).

输出新存档(c)libfoo.a,插入(r)和添加索引的目标文件。

You'll hear of linking libfoo.a in a program. This doesn't mean that libfoo.a itself is linked into or with the program. It means that libfoo.a is passed to the linker as an archive from which it can extract and link into the program just those object files within the archive that the program needs. So the format of a static libary (ar format) is just an object-file bundling format for linker input: it could equally well have been some other bundling format without any effect on the linker's mission, which is to digest a set of object files and shared libraries and generate a program, or shared library, from them. ar format was history's choice.

你会听到在程序中链接libfoo.a。这并不意味着libfoo.a本身与程序相关联或与程序相关联。这意味着libfoo.a作为存档传递给链接器,它可以从中提取程序所需的存档中的目标文件并将其链接到程序中。因此静态库(ar格式)的格式只是链接器输入的对象文件绑定格式:它同样可能是其他一些捆绑格式,对链接器的任务没有任何影响,即消化一组目标文件和共享库,并从中生成程序或共享库。 ar格式是历史的选择。

On the other hand a shared library, e.g. libfoo.so, is an ELF file and not any sort of archive.

另一方面,共享库,例如, libfoo.so,是一个ELF文件,而不是任何类型的存档。

Don't be tempted to suspect that a static library is a sort of ELF file by the fact that all the well-known ELF-parsers - objdump, readelf, nm - will parse a static libary. These tools all know that a static library is an archive of ELF object files, so they just parse all the object files in the library as if you had listed them on the commandline.

不要因为所有着名的ELF解析器(objdump,readelf,nm)将解析静态库这一事实而怀疑静态库是一种ELF文件。这些工具都知道静态库是ELF目标文件的存档,因此它们只是解析库中的所有目标文件,就像您在命令行上列出它们一样。

The use of the -D option with nm just instructs the tool to select only the symbols that are in the dynamic symbol table(s), if any, of the ELF file(s) that it parses - the symbols visible to the runtime linker - regardless of whether or not they are parsed from within an archive. It's the same as objdump -T and readelf --dyn-syms. It is not necessary to use these options to parse the symbols from a shared library. If you don't do so, then by default you'll just see the full symbol table. If you run nm -D on a static library you'll be told no symbols, for each object file in the archive - likewise if you ran nm -D for each of those object files individually. The reason for that is that an object file hasn't got a dynamic symbol table: only a shared library or progam has one.

将-D选项与nm一起使用只是指示工具仅选择它所分析的ELF文件的动态符号表(如果有)中的符号 - 运行时链接程序可见的符号 - 无论是否从存档中解析它们。它与objdump -T和readelf --dyn-syms相同。没有必要使用这些选项来解析共享库中的符号。如果您不这样做,那么默认情况下您只会看到完整的符号表。如果在静态库上运行nm -D,则会为档案中的每个目标文件指定无符号 - 同样如果您为每个目标文件单独运行nm -D。原因是目标文件没有动态符号表:只有共享库或程序有一个。

Object file, shared library and program are all variants of the ELF format. If you're interested in ELF variants, those are the variants of interest.

目标文件,共享库和程序都是ELF格式的变体。如果您对ELF变体感兴趣,那些是感兴趣的变体。

The ELF format itself is a long and thorny technical read and is required background for precisely distinguishing the variants. Intro: An ELF file contains a ELF header structure one of whose fields contains a type-identifier of the file as an object file, shared library, or program. When the file is a program or shared library, it also contains an optional Program header table structure whose fields provide the runtime linker/loader with the parameters it needs to load the file in a process. In terms of ELF structure, the differences between a program and a shared library are slight: it's the detailed content that makes the difference to the behaviour that they elicit from the loader.

ELF格式本身是一个冗长而棘手的技术阅读,是精确区分变体的必要背景。简介:ELF文件包含ELF头结构,其中一个字段包含文件的类型标识符作为目标文件,共享库或程序。当文件是程序或共享库时,它还包含一个可选的程序头表结构,其字段为运行时链接程序/加载程序提供在进程中加载​​文件所需的参数。就ELF结构而言,程序和共享库之间的差异很小:它是详细的内容,它们对从加载器中引出的行为产生影响。

For the long and thorny technical read, try Excutable and Linkable Format (ELF)

对于漫长而棘手的技术阅读,请尝试Excutable和Linkable Format(ELF)

#2


1  

Source

The source code I'm using in my example is as follows:

我在我的例子中使用的源代码如下:

class T {
public:
    T(int _x) : x(_x) { }
    T& operator=(const T& rhs) { x = rhs.x; return *this; }
    int getX() const { return x; }

private:
    int x = 0;
};

Creating the shared library

创建共享库

$ g++ -shared -fPIC -c test.cpp -o test.out && ld -o libtest.so test.out 
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400078

Creating the static library

创建静态库

$ g++ -fPIC -c test.cpp -o test.out && ar rcs libtest.a test.out

Are they both ELF files?

它们都是ELF文件吗?

Kind of ... here's the output of readelf -h for the shared library:

有点......这是共享库的readelf -h的输出:

$ readelf -h libtest.so 
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x400078
  Start of program headers:          64 (bytes into file)
  Start of section headers:          408 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         1
  Size of section headers:           64 (bytes)
  Number of section headers:         5
  Section header string table index: 2

The static library output is very similar, but not quite the same:

静态库输出非常相似,但不完全相同:

$ readelf -h libtest.a

File: libtest.a(test.out)
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          360 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           64 (bytes)
  Number of section headers:         9
  Section header string table index: 6

The first thing that jumps out is the File entry in the static library. Rather than being an ELF object, it contains an ELF object. Another way of confirming this is by looking at the files with hexdump -C (truncated). First, the shared library:

跳出来的第一件事是静态库中的File条目。它不是一个ELF对象,而是包含一个ELF对象。确认这一点的另一种方法是使用hexdump -C(截断)查看文件。首先,共享库:

$ hexdump -C libtest.so
00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 3e 00 01 00 00 00  78 00 40 00 00 00 00 00  |..>.....x.@.....|
00000020  40 00 00 00 00 00 00 00  98 01 00 00 00 00 00 00  |@...............|
00000030  00 00 00 00 40 00 38 00  01 00 40 00 05 00 02 00  |....@.8...@.....|
00000040  51 e5 74 64 06 00 00 00  00 00 00 00 00 00 00 00  |Q.td............|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000070  10 00 00 00 00 00 00 00  47 43 43 3a 20 28 47 4e  |........GCC: (GN|

We can see the character sequence ELF quite clearly here, right at the start of the file. Here's the static library output:

我们可以在文件的开头直接看到字符序列ELF。这是静态库输出:

$ hexdump -C libtest.a
00000000  21 3c 61 72 63 68 3e 0a  2f 20 20 20 20 20 20 20  |!<arch>./       |
00000010  20 20 20 20 20 20 20 20  31 34 38 35 34 36 31 31  |        14854611|
00000020  36 36 20 20 30 20 20 20  20 20 30 20 20 20 20 20  |66  0     0     |
00000030  30 20 20 20 20 20 20 20  34 20 20 20 20 20 20 20  |0       4       |
00000040  20 20 60 0a 00 00 00 00  74 65 73 74 2e 6f 75 74  |  `.....test.out|
00000050  2f 20 20 20 20 20 20 20  31 34 38 35 34 36 31 31  |/       14854611|
00000060  36 36 20 20 31 30 30 30  20 20 31 30 30 30 20 20  |66  1000  1000  |
00000070  31 30 30 36 36 34 20 20  39 33 36 20 20 20 20 20  |100664  936     |
00000080  20 20 60 0a 7f 45 4c 46  02 01 01 00 00 00 00 00  |  `..ELF........|
00000090  00 00 00 00 01 00 3e 00  01 00 00 00 00 00 00 00  |......>.........|
000000a0  00 00 00 00 00 00 00 00  00 00 00 00 68 01 00 00  |............h...|
000000b0  00 00 00 00 00 00 00 00  40 00 00 00 00 00 40 00  |........@.....@.|
000000c0  09 00 06 00 00 47 43 43  3a 20 28 47 4e 55 29 20  |.....GCC: (GNU) 

We can see a bunch of extra stuff before the ELF header starts here, confirming our hypothesis that a static library is stored differently from a shared library.

我们可以在ELF标题开始之前看到一堆额外的东西,这证实了我们的假设,即静态库的存储方式与共享库不同。

Another difference is the Type entry; the shared library is marked as executable whilst the static library is not. In fact, there's not much difference between a shared library and an executable at all: https://askubuntu.com/questions/690631/executables-vs-shared-objects

另一个区别是Type入口;共享库标记为可执行文件,而静态库不标记为可执行文件。实际上,共享库和可执行文件之间没有太大区别:https://askubuntu.com/questions/690631/executables-vs-shared-objects

#3


0  

A static library is little more than a collection of relocatable objects (it's not even ELF, but a dummy archive of ELFs).

静态库只不过是可重定位对象的集合(它甚至不是ELF,而是ELF的虚拟存档)。

A shared library is a standalone piece of functionality with a defined interface (i.e. symbol table) and dependencies (it is an ELF file).

共享库是一个独立的功能,具有定义的接口(即符号表)和依赖项(它是ELF文件)。