.o/.a/.so文件中的具体内容

What exactly is in a .o / .a / .so file?

本文关键字:so 文件      更新时间:2023-10-16

我想知道编译C++程序产生的.o或.so文件中到底存储了什么。这篇文章很好地概述了编译过程和.o文件的功能,据我从这篇文章中了解,.a和.so文件只是多个.o文件合并到一个以静态(.a)或动态(.so)方式链接的文件中。

但我想检查一下我是否正确理解这样一个文件中存储的内容。编译以下代码后

void f();
void f2(int);
const int X = 25;
void g() {
  f();
  f2(X);
}
void h() {
  g();
}

我希望在.o文件中找到以下项目:

  • g()的机器代码,包含调用f()f2(int)的一些占位符地址
  • h()的机器代码,不带占位符
  • X的机器代码,它将只是数字25
  • 指定在文件中的哪些地址可以找到符号g()h()X的某种表格
  • 另一个表,指定哪些占位符用于引用未定义的符号f()f2(int),这些符号必须在链接过程中解析

然后像nm这样的程序将列出两个表中的所有符号名称。

我认为编译器可以通过调用f2(25)来优化调用f2(X),但它仍然需要将符号X保留在.o文件中,因为无法知道它是否会从其他.o文件使用。

这大致正确吗?.a和.so文件是一样的吗?

谢谢你的帮助!

您对对象文件的总体想法非常正确。在"指定文件中地址的表"中,我会用"偏移量"替换"地址",但这只是措辞。

.a文件只是归档文件(一种早于tar的旧格式,但作用相同)。你可以用tar文件替换.a文件,只要你教链接器解压缩它们,并只链接其中包含的所有.o文件(或多或少,有更多的逻辑可以不链接存档中不需要的对象文件,但这只是一种优化)。

.所以文件不同。与对象文件相比,它们更接近于最终的二进制文件。解析了所有符号的.so文件至少在理论上可以作为程序运行。事实上,对于PIE(独立于位置的可执行文件),共享库和程序之间的区别(至少在理论上)只是头中的几位。它们包含动态链接器如何加载库的指令(与正常程序大致相同的指令)和重新定位表,其中包含告诉动态链接器怎样解析外部符号的指令(同样,程序中也是如此)。动态库(和程序)中所有未解析的符号都是通过间接表访问的,间接表在动态链接时(程序启动或dlopen)填充。

如果我们对此进行了大量简化,那么对象和共享库之间的区别在于,在共享库中已经做了更多的工作来不进行文本重定位(这不是严格必要和强制的,但这是一般的想法)。这意味着,在对象文件中,汇编程序只为链接器随后填充的地址生成占位符,对于共享库,地址中填充了跳转表的地址,这样库的文本就不需要更改,只需要有限的跳转表。

Btw。我说的是ELF。较旧的格式在程序和库之间有更多的差异。

您在问题中描述的内容(函数的机器代码、初始化数据和重新定位表)几乎完全是.o(对象)和.so(共享对象)文件中的内容。

.a(档案)基本上是多个.o(对象)文件聚在一起,以便在链接过程中更容易引用。("链接库")

.so(共享对象)文件包括一些额外的元数据,类似于其他.so需要链接的元数据

Windows.dll(动态链接库)文件基本上是具有不同名称的共享对象(.so)。

免责声明:这大大简化了事情,但足以满足开发人员的日常需求。