LD 指的是 Linux 下的链接器(Linker,也称 ld)。
它是将由编译器生成的目标文件链接在一起形成可执行文件或库的程序。
1.链接器用于解决目标文件和库之间的外部引用,合并多个目标文件,并生成最终的可执行文件或库文件。
2.链接器还负责将可执行文件加载到内存中,并将其与任何所需的共享库链接在一起。
它是软件开发过程的关键部分,对于在 Linux 上创建可执行程序和库来说是必不可少的。
可重定位目标文件是一种二进制文件,其中包含了程序的代码和数据。
下面是一个简单的可重定位目标文件的结构示意图:
+---------------------+
| File Header |
+---------------------+
| Section Header 1 |
+---------------------+
| Section Header 2 |
+---------------------+
| ... |
+---------------------+
| Section Header n |
+---------------------+
| Section 1 |
+---------------------+
| Section 2 |
+---------------------+
| ... |
+---------------------+
| Section n |
+---------------------+
| Symbol Table |
+---------------------+
| Relocation Table |
+---------------------+
这个文件由许多不同的节(section)组成,每一节都是一段连续的字节序列,其中存储了相应的代码和数据。这些节包含了程序中的不同部分,例如指令、数据、符号表、重定位表等等。
这些节可以在链接时被合并,生成可执行文件,或者在运行时被动态加载,成为进程的一部分。
可重定位目标文件的结构通常分为以下几个部分:
包含文件类型、目标机器类型、节头表的起始地址等信息。
描述了所有节的的属性信息,如节的名称、大小、类型、所在段等等。
包含了程序的代码和数据,每个节区都有一个节头表项与之对应。
在可重定位目标文件中,程序储存在不同的节(section)中,不同类型的全局变量可能会被分配到不同的节中。
在可重定位目标文件中,指令通常存储在一个叫做“.text”的节中,已经初始化的全局变量通常存储在“.data”或“.rodata”节中,未初始化的全局变量通常存储在“.bss”节中。
这是因为在编译时,编译器会将代码和数据分别打包成不同的节,以便于在链接时更好地进行目标文件的组合。
同时,不同的目标文件中也可能出现相同名称的函数或变量,这时在链接时需要进行符号解析和重定位,保证最终生成的可执行文件能够正确地执行。
存储了所有定义和引用过的符号、符号的类型、值、所在节等信息。
描述了需要重定位的节区和需要进行重定位的位置。
命令行参数指的是用户在命令行上输入给Linux ld程序的参数,这些参数可以控制Linux ld程序的行为,如告诉程序链接哪些目标文件、链接生成什么类型的可执行文件、使用哪种链接脚本等等。常见的ld参数包括:
-o:指定输出文件名
--entry:指定程序入口点
-L:指定搜索库文件的路径
-l:指定要链接的库文件
-Bstatic/-Bdynamic:指定静态/动态链接
-T:指定链接脚本文件
这些参数可以通过man ld查看Linux ld程序的使用手册获得更详细的信息。
可执行目标文件也称为可执行文件,是经过链接后的目标文件。
包含了程序的执行代码、数据和程序入口等信息,可以直接被计算机加载和执行。
在Linux系统中,可执行文件通常以ELF格式存储(也可以是其他格式,如COFF、Mach-O等)。
执行可执行目标文件时,操作系统会将其加载到内存中,并从程序入口开始执行。
可执行文件可以是应用程序、脚本、驱动程序等各种类型的程序。
像 Linux ld程序这样的静态链接器(static linker)以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的、可以加载和运行的可执行目标文件作为输出。
为了构造可执行文件,链接器必须完成两个主要任务:
在编译器的语境中,符号(Symbol)是指用户定义的名称、变量、函数、类、模块等实体的名称,也可以理解为程序中标识某个变量或函数的标签或名字。
在编译器将源代码转化为目标文件的过程中,编译器会将这些符号存储在目标文件的符号表中。在链接时,链接器会读取多个目标文件中的符号表,并将它们合并起来,以便能够在程序中正确地访问各个变量和函数。
符号解析(Symbol Resolution)是指在链接阶段确定每个符号最终所代表的地址或实现,并将符号引用替换成符号定义,从而使得程序能够正确地运行。它通常包括符号查找(Symbol Lookup)、符号绑定(Symbol Binding)、符号重定位(Symbol Relocation)等过程。
重定位(Relocation)是指将程序中的符号引用或地址引用映射到目标地址的过程。
在程序编译的过程中,编译器会将程序中用到的符号和地址表示为相对地址或偏移量,这些相对地址或偏移量需要在程序运行时动态地映射到实际的绝对地址上,这个过程就是重定位过程。
具体来说,当编译器生成目标文件时,它会在目标文件中为每个符号分配一个相对于目标文件起始地址的地址。当目标文件被加载到内存中时,操作系统会为目标文件的每个节(section)分配一段连续的内存空间,并将目标文件中每个符号的相对地址加上该节的起始地址,得到该符号在内存中的绝对地址,这个过程就是重定位过程。
重定位通常在链接器中完成,包括静态链接器和动态链接器。这里先不讨论动态链接器。
静态链接器在将多个目标文件链接成一个可执行文件时会进行重定位。
重定位是将程序中的相对地址映射到实际的绝对地址的过程,是程序在运行时动态映射内存地址的重要步骤之一。
到这里我们可以开始总结了,什么是静态链接?有什么用?
静态链接是将程序的所有模块(包括目标文件和库文件)在编译阶段之后的链接阶段合并为一个单独的可执行文件的过程。
在这个过程中,符号引用被解析并绑定到相应的地址,生成一个包含完整代码和数据的独立可执行文件。
主要用途:
生成独立可执行文件: 静态链接生成一个完全独立于其他文件的可执行文件,不需要依赖外部库或模块。
提高执行速度: 由于所有代码和数据都被合并到一个文件中,避免了运行时的模块加载和地址解析,因此可能提高程序的执行速度。
便于分发: 静态链接的可执行文件包含了所有必要的代码和数据,使得程序更容易分发和部署,因为用户只需要一个文件就能运行整个程序。
符号隐藏: 静态链接将所有模块合并在一起,可以隐藏模块的内部实现细节,只暴露公共接口,提高代码的安全性。
静态链接的缺点包括生成较大的可执行文件以及每次更新都需要重新链接。这导致在一些场景中,如大型应用程序或者涉及到频繁更新的情况下,动态链接可能更为常见。
《深入理解计算机系统》