【发布时间】:2008-12-01 23:07:54
【问题描述】:
我不明白 GCC 在 Linux 下是如何工作的。在源文件中,当我执行以下操作时:
#include <math.h>
编译器是否提取适当的二进制代码并将其插入到编译后的可执行文件中,或者编译器是否插入对外部二进制文件的引用(a-la Windows DLL?)
我猜这个问题的通用版本是:*nix 下是否有与 Windows DLL 等效的概念?
【问题讨论】:
我不明白 GCC 在 Linux 下是如何工作的。在源文件中,当我执行以下操作时:
#include <math.h>
编译器是否提取适当的二进制代码并将其插入到编译后的可执行文件中,或者编译器是否插入对外部二进制文件的引用(a-la Windows DLL?)
我猜这个问题的通用版本是:*nix 下是否有与 Windows DLL 等效的概念?
【问题讨论】:
嗯。当您包含math.h 时,编译器将读取包含可以使用的函数和宏的声明的文件。如果您调用在该文件 (header) 中声明的函数,则编译器会在目标文件中的该位置插入一条调用指令,该指令将由您编译的文件生成(我们称之为 test.c和创建的目标文件test.o)。它还在该对象文件的重定位表中添加了一个条目:
Relocation section '.rel.text' at offset 0x308 contains 1 entries:
Offset Info Type Sym.Value Sym. Name
0000001c 00000902 R_386_PC32 00000000 bar
这将是功能栏的重定位条目。将在符号表中创建一个条目,说明该函数尚未定义:
9: 00000000 0 NOTYPE GLOBAL DEFAULT UND bar
当您将 test.o 目标文件链接到程序中时,您需要链接名为 libm.so 的数学库。 so 扩展类似于 Windows 的 .dll 扩展。这意味着它是一个共享对象文件。编译器在链接时将修复出现在test.o 重定位表中的所有位置,将其条目替换为 bar 函数的正确地址。根据您是使用库的共享版本还是静态库(当时称为libm.a),编译器将在编译后或稍后在您实际启动程序的运行时进行修复。完成后,它将在该程序所需的共享库表中注入一个条目。 (可以用readelf -d ./test显示):
Dynamic section at offset 0x498 contains 22 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libm.so.6]
0x00000001 (NEEDED) Shared library: [libc.so.6]
... ... ...
现在,如果您启动程序,动态链接器将查找该库,并将该库链接到您的可执行映像。在 Linux 中,执行此操作的程序称为 ld.so。静态库在动态部分中没有位置,因为它们只是链接到其他目标文件,然后就被遗忘了;从那时起,它们就是可执行文件的一部分。
实际上它实际上要复杂得多,我也没有详细了解这一点。不过,这是一个粗略的计划。
【讨论】:
这里涉及到几个方面。
首先,头文件。编译器只是在包含文件的位置包含文件的内容,仅此而已。据我所知,GCC 甚至没有区别对待标准头文件(但我可能错了)。
但是,头文件实际上可能不包含实现,仅包含其声明。如果实现位于其他地方,您必须告诉编译器/链接器。默认情况下,您只需将适当的库文件 传递给编译器,或传递库名称即可。例如,以下两个是等价的(前提是libcurl.a位于链接器可以找到的目录中):
gcc codefile.c -lcurl
gcc codefile.c /path/to/libcurl.a
这告诉link editor(“链接器”)将您的代码文件链接到静态库libcurl.a的实现(编译器gcc实际上忽略了这些参数,因为它不知道如何处理它们,并简单地将它们传递给链接器)。但是,这称为静态链接。还有dynamic linking,它发生在你的程序启动时,它发生在Windows 下的.dlls(而静态库对应于Windows 上的.lib 文件)。 Linux下的动态库文件通常文件扩展名为.so。
了解有关这些文件的更多信息的最佳方法是熟悉 GCC 链接器 ld,以及出色的工具集 binutils,您可以使用它轻松编辑/查看库文件(任何二进制代码文件,真的)。
【讨论】:
*nix 下是否有与 Windows DLL 等效的概念?
是的,它们被称为“共享对象”或 .so 文件。它们在运行时动态链接到您的二进制文件中。在 linux 中,您可以在可执行文件上使用“ldd”命令来查看二进制文件链接到哪些共享对象。您可以使用 sysinternals 中的 ListDLLs 在 Windows 中完成同样的事情。
【讨论】:
编译器可以做它想做的任何事情,只要它实际上就像你包含了文件一样。 (我所知道的所有编译器,包括 GCC,都只包含一个名为 math.h 的文件。)
不,它通常不包含函数定义本身。那就是 libm.so,一个“共享对象”,类似于 windows .DLL。它应该在每个系统上,因为它是 C 运行时 libc.so 的伴侣。
编辑:这就是为什么如果你使用数学函数你必须将 -lm 传递给链接器 - 它指示它链接到 libm.so。
【讨论】:
有。包含对头文件进行文本包含(这是标准的 C/C++ 行为)。您正在寻找的是 linker 。 gcc/g++ 的 -l 参数告诉链接器要添加哪些库。对于数学 (libm.so),您将使用 -lm。常见的模式是:
math.h 是这个主题的轻微变化。
【讨论】: