GNU ld(链接器)的主要功能

作用: 链接器linker是Bintutils的一种重要工具,负责将编译后的目标文件(.o)合并成一个可执行文件或者共享库。

一、链接器的文件结构可以概括为以下几个关键部分:

  1. 输入文件 (Input Files): 输入文件通常是目标文件(.o 文件)或共享库(.so 文件),它们包含了已编译的程序代码、数据和其他相关信息。链接器将这些文件合并在一起以创建最终的可执行文件或共享库。

  2. 符号表 (Symbol Table): 符号表是一个数据结构,包含了输入文件中定义的符号(如函数、变量等)以及它们的地址和属性。链接器使用符号表来解析符号之间的关系,以便正确地解决符号引用。

  3. 重定位表 (Relocation Table): 重定位表包含了目标文件中的重定位信息,它告诉链接器在合并文件时如何调整符号的地址。这些信息是在编译时由编译器生成的,以确保程序正确地链接并运行。

  4. 输出文件 (Output File): 输出文件是链接器的结果,它可以是可执行文件、共享库或者其他类型的二进制文件。输出文件包含了输入文件中的代码和数据,以及链接器解析后的符号和重定位信息。

二、链接器主要任务:

GNU ld(链接器)是用于将多个目标文件(包括目标文件、共享库、目标文件的归档文件等)合并成一个可执行文件或共享库的重要工具。它的主要功能包括:

  1. 符号解析和重定位:链接器识别并解析输入文件中的符号引用,然后执行重定位操作以确保这些引用指向正确的地址。这包括将模块中的符号引用与其定义进行匹配,以便在合并时连接它们。

  2. 合并输入文件:链接器将多个输入文件中的代码段、数据段等模块合并成一个单一的地址空间。这包括将不同模块中的代码和数据安排到正确的内存地址中。

  3. 生成输出文件:链接器将合并的模块和符号表等信息写入输出文件中,该输出文件可以是可执行文件、共享库、目标文件等,具体类型取决于链接器的参数和配置。

  4. 符号表处理:链接器生成输出文件的符号表,其中包含了可供调试和动态链接器使用的符号信息。

  5. 处理重定位信息:如果存在重定位信息,链接器将生成重定位表,用于在加载时修正代码和数据的地址。这使得程序可以在不同的内存地址上运行。

  6. 处理链接器脚本:链接器可以根据链接器脚本(linker script)中的规则和指令来组织和排列模块,以满足特定需求。链接器脚本可以自定义连接操作的方式。

  7. 共享库支持:链接器可以将多个目标文件合并成共享库(动态链接库),以供其他程序动态链接使用。

  8. 生成调试信息:链接器可以生成调试信息,以支持调试器在程序运行时进行源代码级别的调试。

  9. 解决符号冲突:链接器负责处理不同输入文件之间的符号冲突,以确保所有符号都能正确解析和定位。

  10. 目标文件格式转换:链接器可以将输入文件从一种目标文件格式转换为另一种格式,以支持不同的目标平台。

GNU ld是编译工具链的一部分,它的主要任务是将源代码和目标文件合并为可执行的、可部署的程序或库。它是将软件编译成可执行程序的关键组件之一,同时也支持许多高级功能,如动态链接、共享库创建等。

三、简单介绍ldmain.c的一些函数

1. 整个链接器的入口main函数

cpp 复制代码
int
main (int argc, char **argv)  // 主程序入口
{
  char *emulation;
  long start_time = get_run_time ();
#ifdef HAVE_SBRK
  char *start_sbrk = (char *) sbrk (0);
#endif

2. 处理命令行参数

cpp 复制代码
expandargv (&argc, &argv); 

expandargv(&argc, &argv) 主要用于处理命令行参数中的通配符,将通配符展开为实际文件名,并将它们添加到 argv 数组中。这有助于链接器识别实际要处理的文件。通常在链接器的早期阶段执行,以确保命令行参数的正确处理。

3. 初始化BFD库,BFD库用于处理二进制文件格式。

cpp 复制代码
bfd_init ();  // bfd_init(): 

bfd_init() 的主要目的是为 Binutils 链接器(ld)的运行做准备,确保 BFD库正确配置,并能够处理各种不同的二进制文件格式,以进行链接和生成目标文件。它是 Binutils 中的一个重要初始化步骤,使链接器能够有效地操作不同类型的二进制文件。

4. bfd_set_default_target 是用于设置链接器的默认 BFD 目标

cpp 复制代码
bfd_set_default_target (TARGET)

具体来说,bfd_set_default_target 的作用包括:

  1. 设置默认 BFD 目标 :链接器需要了解应该如何处理不同类型和体系结构的二进制文件。通过调用 bfd_set_default_target,可以设置默认的 BFD 目标,该目标通常基于链接器的配置或用户指定的目标。

  2. 确保正确的配置:设置正确的默认目标可确保链接器能够正确地解释和处理不同类型的目标文件。这包括目标文件的格式、字节顺序、体系结构等方面的信息。

  3. 适应多样性:链接器需要适应各种不同的目标文件类型,包括 ELF、COFF、a.out 等,以及不同的体系结构,如 x86、ARM、MIPS 等。通过设置默认 BFD 目标,链接器可以根据需要进行适应,而不需要每次都手动指定目标。

5. ld的初始化

cpp 复制代码
ldemul_before_parse ();  

ldemul_before_parse 函数是GNU Binutils链接器中的一个函数,用于在解析链接器脚本之前执行特定于仿真模式的准备工作。仿真模式是链接器根据目标文件的类型和体系结构来选择的一种配置模式。这个函数通常用于设置特定仿真模式下的链接器行为。

具体来说,ldemul_before_parse 函数的作用包括但不限于:

  1. 针对特定仿真模式的初始化:在某些仿真模式下,可能需要执行特定的初始化步骤,以确保链接器能够正确处理这种模式下的输入文件。

  2. 设置特定模式下的默认行为:一些仿真模式可能需要不同于默认配置的链接器行为。ldemul_before_parse 可能会设置这些特定模式下的默认选项或处理。

  3. 处理模拟模式下的特定逻辑:一些仿真模式可能需要额外的逻辑来处理链接器脚本或其他配置。

总之,ldemul_before_parse 函数用于在链接器开始解析链接器脚本之前,根据所选的仿真模式执行与特定模式相关的准备工作。这有助于确保链接器能够正确地处理输入文件和执行链接操作。这个函数在链接器的初始化过程中非常重要,以确保链接器适应不同的仿真模式和配置。

下是一些常见的仿真模式示例:

|----------------|----------------------------------------------------------------------------------------------------|
| ELF仿真模式 | ELF(Executable and Linkable Format)是一种通用的可执行文件格式,通常用于Linux系统。ELF仿真模式用于处理与ELF格式相关的目标文件,例如可执行文件和共享库。 |
| a.out仿真模式 | a.out是早期UNIX系统中使用的一种可执行文件格式。a.out仿真模式用于处理a.out格式的目标文件。 |
| COFF仿真模式 | COFF(Common Object File Format)是一种用于Windows和其他系统的可执行文件格式。COFF仿真模式用于处理COFF格式的目标文件。 |
| Mach-O仿真模式 | Mach-O是用于macOS系统的可执行文件格式。Mach-O仿真模式用于处理Mach-O格式的目标文件。 |
| PE仿真模式 | PE(Portable Executable)是一种用于Windows系统的可执行文件格式。PE仿真模式用于处理PE格式的目标文件。 |
| Hex仿真模式 | Hex仿真模式用于处理十六进制文件,这种文件格式包含以十六进制编码的二进制数据。 |
| SREC仿真模式 | SREC仿真模式用于处理S-Record文件格式,这是一种通用的二进制文件格式,通常用于嵌入式系统。 |
| Tekhex仿真模式 | Tekhex仿真模式用于处理Tektronix HEX文件格式,这是一种包含以十进制编码的二进制数据的文件格式。 |

每个仿真模式都定义了一组规则和配置,以确保链接器能够正确地处理特定类型的目标文件。通过选择适当的仿真模式,链接器可以了解目标文件的格式和要求,并正确执行链接操作,包括符号解析、重定位和生成可执行文件或共享库。这有助于确保不同类型的目标文件可以在同一个链接过程中正确地结合在一起。

6.命令行参数解析

cpp 复制代码
parse_args()

parse_args(argc, argv) 主要用于解析已展开的命令行参数。它负责解释用户提供的各种选项和参数,以确定链接器的配置和操作方式。这个函数在链接器初始化的稍后阶段执行,它依赖于 expandargv 的输出,以正确解释和处理用户提供的信息

7. 链接脚本词法语法分析

cpp 复制代码
yyparser()

yyparse() 是与语法分析器生成器(如Yacc/Bison)生成的语法分析器相关的函数。它通常在编译器、解释器、链接器等工具中的语法分析阶段使用。

具体来说,yyparse() 用于执行以下操作:

  1. 语法分析:它调用语法分析器,开始解析输入的源代码或源文件。语法分析是编译器前端的一个关键步骤,用于将源代码转换为抽象语法树(AST)或中间表示(IR)等数据结构。

  2. 语法规则匹配yyparse() 通过语法规则来匹配输入的代码,以确定代码的结构和语法是否符合规范。如果发现语法错误,它可能会报告错误或抛出异常。

  3. AST构建 :在分析过程中,yyparse() 会构建抽象语法树(AST),这是一个表示源代码结构的树状数据结构。AST对后续的语义分析和代码生成非常重要。

  4. 解释或生成中间代码 :一旦语法分析完成,yyparse() 可能会将源代码翻译成中间表示或执行代码生成。这取决于编译器或工具的具体需求。

yyparse() 通常与词法分析器(如Lex/Flex)一起使用,词法分析器负责将源代码分割成标记(tokens),然后 yyparse() 利用这些标记来构建语法树。

需要注意的是,yyparse() 是由语法分析器生成器生成的,通常与特定的语法规则文件一起使用。它实际上是一个无限循环,不断读取输入,分析语法,生成语法树,并执行相关操作,直到达到输入的结尾或发现语法错误。在编译器和解释器的设计中,yyparse() 扮演了一个非常重要的角色,确保源代码被正确分析和处理。

8. 获取链接脚本

cpp 复制代码
ldemul_get_script (&isfile)

链接器脚本是一种特殊的配置文件,它定义了链接器在连接目标文件时的行为。这些脚本可以包含指令和规则,用于定义符号的位置、如何分配内存、如何将节(sections)映射到输出文件等。不同的目标文件类型和体系结构可能需要不同的链接器脚本,因此链接器需要了解所处的仿真模式以获取适当的链接器脚本。

ldemul_get_script 函数的主要目的是返回当前仿真模式下应使用的链接器脚本的内容或文件路径。它可能会返回链接器脚本的文本内容,也可能返回脚本文件的路径,取决于具体的实现。链接器在初始化过程中需要加载和解释链接器脚本,以根据目标文件的类型和体系结构进行正确的连接。

这个函数通常在链接器初始化的早期阶段被调用,以获取默认的链接器脚本。链接器可以根据仿真模式来选择不同的链接器脚本,确保在不同情况下使用正确的配置。这有助于链接器正确地执行连接操作,以生成可执行文件或共享库。

9.设置特定模式下的链接器选项、规则和行为,以确保连接操作能够正确执行。

cpp 复制代码
ldemul_after_parse ();

ldemul_after_parse 函数是 GNU Binutils 链接器(ld)中的一个函数,它通常在链接器解析链接器脚本(linker script)之后执行。该函数用于执行仿真模式(emulation mode)特定的操作,以确保链接器根据所选的仿真模式正确配置和执行连接操作。

以下是 ldemul_after_parse 函数的主要作用:

  1. 特定仿真模式的配置 :不同的仿真模式可能需要不同的配置和行为。ldemul_after_parse 用于设置特定模式下的链接器选项、规则和行为,以确保连接操作能够正确执行。

  2. 链接器脚本的进一步处理:在链接器解析链接器脚本后,某些特定模式可能需要进一步处理脚本内容,以执行特殊操作。例如,某些模式可能需要处理特定的段(sections)分配规则或符号位置规则。

  3. 模拟模式下的特殊逻辑 :链接器在不同的仿真模式下可能需要执行不同的逻辑,以确保连接操作适应目标文件类型和体系结构。ldemul_after_parse 允许在特定模式下执行这种特殊逻辑。

总之,ldemul_after_parse 函数用于在链接器解析链接器脚本之后,根据所选的仿真模式执行特定于模式的配置和操作。这有助于确保链接器能够正确地执行连接操作,以生成可执行文件或共享库,同时适应不同的目标文件类型和体系结构。这是链接器初始化过程中的重要步骤,用于确保正确的链接器行为。

区别和联系

ldemul_before_parseldemul_after_parse 都是GNU Binutils链接器(ld)中的函数,用于在特定仿真模式(emulation mode)下执行特定操作。它们之间的主要区别在于执行的时间点和任务。

  1. ldemul_before_parse

    • 执行时间:ldemul_before_parse 在解析链接器脚本之前执行。
    • 主要任务:它用于在解析链接器脚本之前执行特定于仿真模式的准备工作,通常用于设置模式特定的配置选项,初始化特定于模式的数据结构,以确保链接器能够正确处理该模式下的输入文件。
  2. ldemul_after_parse

    • 执行时间:ldemul_after_parse 在解析链接器脚本之后执行
    • 主要任务:它用于在解析链接器脚本之后执行特定于仿真模式的配置和操作。通常包括配置特定模式下的链接选项、处理特定脚本规则、执行特殊逻辑等,以确保连接操作适应不同的目标文件类型和体系结构。

联系和区别:

  • 联系:两者都用于配置和准备链接器以适应不同的仿真模式。它们都执行特定于模式的操作,以确保链接器正确处理输入文件和链接操作。
  • 区别:主要区别在于执行时间点。ldemul_before_parse 在解析链接器脚本之前执行,而 ldemul_after_parse 在解析链接器脚本之后执行。ldemul_before_parse 主要用于初始化和配置,以便正确解释链接器脚本,而 ldemul_after_parse 主要用于在脚本解析后执行与模式相关的操作。

这两个函数的组合确保了链接器在不同仿真模式下能够正确配置和执行连接操作,以生成适用于特定目标文件类型和体系结构的可执行文件或共享库

相关推荐
ragnwang几秒前
C++ Eigen常见的高级用法 [学习笔记]
c++·笔记·学习
罗伯特祥25 分钟前
C调用gnuplot绘图的方法
c语言·plot
Jackey_Song_Odd40 分钟前
解决Ubuntu下无法装载 Windows D盘的问题
linux·ubuntu
Linux运维技术栈1 小时前
Ansible(自动化运维)环境搭建及ansible-vault加密配置
运维·自动化·ansible
乔巴不是狸猫1 小时前
第11周作业
linux
嵌入式科普1 小时前
嵌入式科普(24)从SPI和CAN通信重新理解“全双工”
c语言·stm32·can·spi·全双工·ra6m5
火星机器人life1 小时前
基于ceres优化的3d激光雷达开源算法
算法·3d
虽千万人 吾往矣2 小时前
golang LeetCode 热题 100(动态规划)-更新中
算法·leetcode·动态规划
Bessssss2 小时前
centos权限大集合,覆盖多种权限类型,解惑权限后有“. + t s”问题!
linux·运维·centos
苹果醋32 小时前
Golang的文件加密工具
运维·vue.js·spring boot·nginx·课程设计