我知道有很多关于共享和静态库的用例的问题,这个问题与此无关。我问的是存储在磁盘上的文件格式的差异。
为什么问题是,两者之间有什么区别?或者它们是否完全相同,仅在使用方面有所不同?
我认为他们不一样,因为跑步' nm'在共享库上需要-D标志。显然,它需要做一些不同的事情。为什么呢?
它们都是ELF文件吗?
共享库可以包含某些依赖路径的唯一区别是什么?
答案 0 :(得分:7)
静态库,例如libfoo.a
不是任何形式的可执行文件。
它只是unix ar
format中的索引存档
恰好是ELF的其他文件
目标文件。
创建静态库,就像任何存档一样:
ar crs libfoo.a objfile0.o objfile1.0...objfileN.o
输出新存档(c
)libfoo.a,插入这些目标文件(r
)
和索引已添加(s
)。
您会听到在程序中链接 libfoo.a
。这并不意味着
libfoo.a
本身与程序相关联或与程序相关联。这意味着libfoo.a
作为存档传递给链接器,它可以从中提取和链接到存档
程序只是程序所需的归档中的那些目标文件。
因此静态库(ar
格式)的格式只是一个对象文件
链接器输入的捆绑格式:它同样可能是其他一些捆绑
格式对链接器的任务没有任何影响,即消化一组
目标文件和共享库,并生成程序或共享库,
从他们。 ar
格式是历史的选择。
另一方面,共享库,例如libfoo.so
, 是ELF文件
而不是任何档案。
不要试图怀疑静态库是一种ELF文件
事实上,所有着名的ELF解析器 - objdump
,readelf
,nm
-
将解析一个静态库。这些工具都知道静态库
存档的 ELF目标文件,因此它们只解析所有目标文件
在库中,就像你在命令行上列出它们一样。
-D
选项与nm
的使用只是指示工具选择
仅限动态符号表中的符号(如果有)
它解析的ELF文件的大小 - 运行时链接程序可见的符号
- 无论是否从存档中解析它们。它的
与objdump -T
和readelf --dyn-syms
相同。它不是
必须使用这些选项来解析共享库中的符号。如果
你没有这样做,那么默认情况下你只会看到完整的符号表。
如果您在静态库上运行nm -D
,系统会告诉您no symbols
归档中的每个目标文件 - 同样如果您为每个目标文件运行nm -D
那些目标文件。原因是一个目标文件
没有动态符号表:只有共享库或程序有一个。
对象文件,共享库和程序都是ELF格式的变体。 如果您对ELF变体感兴趣,那些就是感兴趣的变体。
ELF格式本身是一个冗长而棘手的技术阅读,是必需的 准确区分变体的背景。简介:一个ELF文件 包含 ELF标头结构,其中一个字段包含类型标识符 该文件作为目标文件,共享库或程序。当文件是 程序或共享库,它还包含一个可选的程序头表 结构,其字段为运行时链接程序/加载程序提供参数 它需要在进程中加载文件。在ELF结构方面, 程序和共享库之间的差异很小:它是 详细的内容,使他们的行为有所不同 从装载机中引出。
对于漫长而棘手的技术阅读,请尝试Excutable and Linkable Format (ELF)
答案 1 :(得分:1)
<强>来源
我在我的示例中使用的源代码如下:
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
答案 2 :(得分:0)
静态库只不过是可重定位对象的集合(它甚至不是ELF,而是ELF的虚拟存档)。
共享库是一个独立的功能,具有已定义的接口(即符号表)和依赖项(它是ELF文件)。