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

时间:2017-01-26 17:30:25

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

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

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

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

它们都是ELF文件吗?

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

3 个答案:

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

-D选项与nm的使用只是指示工具选择 仅限动态符号表中的符号(如果有) 它解析的ELF文件的大小 - 运行时链接程序可见的符号 - 无论是否从存档中解析它们。它的 与objdump -Treadelf --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文件)。