32

.so共享对象( )文件和对象( )文件之间的关系是什么.o

你能通过例子解释一下吗?

4

2 回答 2

83

假设你有以下 C 源文件,调用它name.c

#include <stdio.h>
#include <stdlib.h>

void print_name(const char * name)
{
    printf("My name is %s\n", name);
}

当你编译它时,cc name.c你会生成name.o. .o 包含 name.c 中定义的所有函数和变量的编译代码和数据,以及将它们的名称与实际代码相关联的索引。如果您查看该索引,例如使用该nm工具(在 Linux 和许多其他 Unix 上可用),您会注意到两个条目:

00000000 T print_name
         U printf

这意味着:.o 中存储了两个符号(函数或变量的名称,但不是类、结构或任何类型的名称)。第一个,标记为T实际上包含它的定义name.o另一个,标有U只是一个参考。的代码print_name可以在这里找到,但代码printf不能。当您的实际程序运行时,它需要找到所有作为引用的符号并在其他目标文件中查找它们的定义,以便将它们链接在一起成为一个完整的程序或完整的库。因此,目标文件是在源文件中找到的定义,转换为二进制形式,可用于放入完整的程序中。

您可以将 .o 文件一个一个链接在一起,但您不能:它们通常很多,而且它们是一个实现细节。您真的希望将它们全部收集到相关对象的捆绑中,并具有公认的名称。这些包称为,它们有两种形式:静态和动态。

静态(在 Unix 中)几乎总是后缀为.a(示例包括libc.a哪个是 C 核心库,libm.a哪个是 C 数学库)等等。继续示例,您将使用ar rc libname.a name.o. 如果你继续运行nmlibname.a你会看到:

name.o:
00000000 T print_name
         U printf

如您所见,它主要是一个包含目标文件的大表,其中有一个查找所有名称的索引。就像目标文件一样,它包含每个定义.o的符号和它们引用的符号。如果您要链接到另一个.o(例如date.oto print_date),您会看到另一个类似上面的条目。

如果您将静态库链接到可执行文件中,它会将整个库嵌入到可执行文件中。这就像链接所有单个.o文件一样。正如您可以想象的那样,这会使您的程序变得非常大,特别是如果您正在使用(就像大多数现代应用程序一样)很多库。

动态共享库以.so. 它,就像它的静态模拟一样,是一个大的目标文件表,引用了所有编译的代码。你会用cc -shared libname.so name.o. 虽然看起来与nm静态库有很大不同。在我的系统上,它包含大约两打符号,其中只有两个是print_nameprintf

00001498 a _DYNAMIC
00001574 a _GLOBAL_OFFSET_TABLE_
         w _Jv_RegisterClasses
00001488 d __CTOR_END__
00001484 d __CTOR_LIST__
00001490 d __DTOR_END__
0000148c d __DTOR_LIST__
00000480 r __FRAME_END__
00001494 d __JCR_END__
00001494 d __JCR_LIST__
00001590 A __bss_start
         w __cxa_finalize@@GLIBC_2.1.3
00000420 t __do_global_ctors_aux
00000360 t __do_global_dtors_aux
00001588 d __dso_handle
         w __gmon_start__
000003f7 t __i686.get_pc_thunk.bx
00001590 A _edata
00001594 A _end
00000454 T _fini
000002f8 T _init
00001590 b completed.5843
000003c0 t frame_dummy
0000158c d p.5841
000003fc T print_name
         U printf@@GLIBC_2.0

共享库在一个非常重要的方面不同于静态库:它不会将自身嵌入到您的最终可执行文件中。相反,可执行文件包含对该共享库的引用,该引用不是在链接时而是在运行时解析的。这有很多优点:

  • 您的可执行文件要小得多。它仅包含您通过目标文件显式链接的代码。外部库是引用,它们的代码不会进入二进制文件。
  • 您可以在多个可执行文件之间共享(因此得名)一个库的位。
  • 如果您注意二进制兼容性,您可以在程序运行之间更新库中的代码,程序将获取新库而无需您更改它。

有一些缺点:

  • 将程序链接在一起需要时间。对于共享库,其中一些时间被推迟到每次可执行文件运行时。
  • 过程比较复杂。共享库中的所有附加符号都是使库在运行时链接所需的基础结构的一部分。
  • 您冒着在不同版本的库之间存在细微不兼容的风险。在 Windows 上,这称为“DLL 地狱”。

(如果您考虑一下,其中许多是程序使用或不使用引用和指针而不是直接将类的对象嵌入其他对象的原因。类比非常直接。)

好的,这是很多细节,我已经跳过了很多,例如链接过程实际上是如何工作的。我希望你能跟随它。如果不要求澄清。

于 2009-07-31T05:55:53.370 回答
1

.so 类似于 Windows 上的 .dll。.o 与 Visual Studio 下的 .obj 完全相同。

于 2009-07-31T05:42:14.833 回答