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

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

为什么问题是,两者有什么区别? 或者它们是完全一样的,只是在使用方面有所不同?

我相信他们不一样,因为在共享库上运行“nm”需要-D标志。 很明显,它需要做一些不同的事情。 为什么?

他们都是ELF文件吗?

唯一的区别是共享库可以包含一些依赖path吗?

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

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

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

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

你会听到在程序中链接 libfoo.a 。 这并不意味着libfoo.a 本身被链接到程序中。 这意味着libfoo.a作为一个档案从链接器传递给链接器,从中可以提取链接到程序中的程序所需的档案中的那些目标文件。 所以静态库( ar格式)的格式只是链接器输入的一种对象文件捆绑格式:它也可以是其他一些捆绑格式,而不影响链接器的任务,即消化一组目标文件和共享库,并从中生成一个程序或共享库。 ar格式是历史的选择。

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

不要因为所有着名的ELF解析器( objdumpreadelfnm )会解析一个静态库文件而怀疑静态库是一种ELF文件。 这些工具都知道静态库是ELF对象文件的存档,所以它们只是解析库中的所有对象文件,就像在命令行中列出它们一样。

nm使用-D选项只是指示工具只选择动态符号表中的符号(如果有的话),它解析的ELF文件(即运行时链接程序可见的符号) – 不管它们是否从档案中解析。 它和objdump -Treadelf --dyn-syms没有必要使用这些选项来解析共享库中的符号。 如果你不这样做,那么默认情况下,你会看到完整的符号表。 如果在静态库上运行nm -D ,那么对于归档中的每个目标文件,都会被告知no symbols – 同样,如果您为每个这些目标文件分别运行nm -D 。 原因是目标文件没有动态符号表:只有一个共享库或程序有一个。

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

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

对于长期和棘手的技术阅读,请尝试可执行和可链接格式(ELF)

资源

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

 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; }; 

创建共享库

 $ 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 

创建静态库

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

他们都是ELF文件吗?

有点…这是共享库的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 

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

 $ 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 

跳出来的第一件事是静态库中的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| 

在这里,我们可以很清楚地看到字符序列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) 

在ELF头文件开始之前,我们可以看到一些额外的东西,证实了我们的假设:静态库的存储方式与共享库不同。

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

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

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