【问题标题】:File format differences between a static library (.a) and a shared library (.so)?静态库 (.a) 和共享库 (.so) 之间的文件格式差异?
【发布时间】:2017-06-12 06:38:36
【问题描述】:

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

为什么问题是,两者之间有什么区别?还是它们完全一样,只是用法不同?

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

它们都是 ELF 文件吗?

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

【问题讨论】:

  • @yugr 不是重复的。这些答案都没有提到文件的内部格式。
  • @yugr 我不认为这是重复的,因为接受答案列出了每个库文件的用例,而不是关于序列化格式(这是这里要问的)。
  • 共享库是可以加载到内存中的链接图像 (ELF)。静态库是一个包含未链接的可重定位文件(也称为 ELF)的存档,这些文件可以链接到可以加载到内存中的内容中。

标签: linux linker shared-libraries static-libraries elf


【解决方案1】:

一个静态库,例如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 解析器 - objdumpreadelfnm - 将解析一个静态库。这些工具都知道静态库是 of ELF 目标文件的存档,因此它们只解析所有目标文件 在库中,就像您在命令行中列出它们一样。

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

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

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

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

【讨论】:

  • 太棒了!简单明了。这正是我所需要的。非常感谢!
  • 很好的答案..帮助我理解了链接器背后的层..
【解决方案2】:

来源

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

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 文件的 ar 存档,不是 ELF。
  • @yugr 是的,这就是我所说的“它不是一个 ELF 对象,而是包含一个 ELF 对象”:)
  • 演示不错。但是有一个错误。通常 .so 头文件中的动态库类型是 --- 类型:DYN,而不是 EXEC。
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-09
    • 2013-07-13
    • 2020-11-20
    • 2011-07-23
    • 1970-01-01
    相关资源
    最近更新 更多