我想知道究竟是什么存储在编译C++程序的.o或.so文件中。 This post很好地概述了编译过程以及其中的.o文件的功能,据this post的了解,.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符号在文件中的哪个地址处
  • 另一个表,用于指定在引用期间必须使用哪些占位符来引用 undefined symbol f()f2(int)

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

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

    那是正确的吗? .a和.so文件是否相同?

    谢谢你的帮助!

    最佳答案

    您对目标文件的一般想法非常正确。在“指定文件中哪个地址的表”中,我将“地址”替换为“偏移量”,但这仅是措辞。

    .a文件只是归档文件(一种旧格式,早于tar,但功能相同)。您可以使用tar文件替换.a文件,只要您教过链接程序解压缩它们并仅与其中包含的所有.o文件进行链接(或多或少,有更多的逻辑可以不与目标文件中的目标文件链接存档,但这不是一个最佳选择)。

    .so文件是不同的。它们比目标文件更接近最终二进制文件。解析了所有符号的.so文件至少可以在理论上作为程序运行。实际上,使用PIE(与位置无关的可执行文件),共享库和程序之间的区别(至少在理论上)仅是头文件中的几位。它们包含有关动态链接程序如何加载库的指令(与普通程序几乎相同的指令)和重定位表,该表包含告诉动态链接程序如何解析外部符号的指令(同样,程序中的相同)。 。动态库(和程序)中所有未解析的符号都通过在动态链接时(程序启动或dlopen)填充的间接表进行访问。

    如果我们将其简化很多,则对象和共享库之间的区别在于,在共享库中已经进行了更多工作以不进行文本重定位(这不是严格必要的,也不是强制执行的,但这是总的思路)。这意味着,在目标文件中,汇编器仅生成了链接器随后要填充的地址的占位符,对于共享库,地址中填充了用于跳转表的地址,因此不需要更改库的文本,只有一个有限的跳转表。

    顺便提一句。我在说ELF。较旧的格式在程序和库之间存在更多差异。

    10-07 19:32
    查看更多