【Shell 命令集合 链接器(linker)工具】Linux ld命令 将目标文件与库链接为可执行文件或库文件

简介: 【Shell 命令集合 链接器(linker)工具】Linux ld命令 将目标文件与库链接为可执行文件或库文件

Shell 命令专栏:Linux Shell 命令全解析

描述

ld是Linux环境下的链接器(linker)工具,它的主要作用是将多个目标文件(.o.obj文件)链接成一个可执行文件或库文件。以下是ld命令的主要功能和作用:

  1. 目标文件链接ld可以将多个目标文件链接成一个单一的输出文件。这些目标文件通常是由编译器生成的,并包含程序的机器代码。
  2. 解析符号:在链接过程中,ld会解析目标文件中的符号,并确保所有的函数和变量引用都被正确地解析。
  3. 生成可执行文件ld不仅可以生成可执行文件,还可以生成共享库或静态库。
  4. 处理重定位:由于目标文件中的代码可能在任何位置执行,ld需要处理这些代码的重定位,确保代码在正确的内存地址上执行。
  5. 合并段ld会合并目标文件中的相似段,例如.text(代码段)或.data(数据段)。
  6. 链接时优化:在某些情况下,ld还可以进行链接时优化,以提高生成的可执行文件的性能。
  7. 处理启动代码ld还负责链接启动代码,这是程序开始执行时首先运行的代码片段。

总的来说,ld命令是Linux编程中不可或缺的工具,它确保程序中的所有部分都被正确地组合在一起,生成一个可以运行的可执行文件。


语法格式

ld [options] object_files [--] [library_files]

参数说明

  • -b <input-format>: 指定目标代码输入文件的格式。
  • -Bstatic: 只使用静态库。
  • -Bdynamic: 只使用动态库。
  • -Bsymbolic: 把引用捆绑到共享库中的全局符号。
  • -c <MRI-commandfile>, --mri-script=<MRI-commandfile>: 为与 MRI 链接器兼容,ld 接受由 MRI 命令语言编写的脚本文件。
  • --cref: 创建跨引用表。
  • -d,-dc,-dp: 即使指定了可重定位的输出文件(使用-r),也会为公共符号分配空间。脚本命令“FORCE_COMMON_ALLOCATION”具有相同的效果。
  • -defsym: 在输出文件中创建指定的全局符号。
  • -demangle: 在错误消息中还原符号名称。
  • -e <entry>: 使用指定的符号作为程序的初始执行点。
  • -E,--export-dynamic: 对于ELF格式文件,创建动态链接的可执行文件时,把所有符号添加到动态符号表。
  • -f <name>, --auxiliary=<name>: 对于 ELF 格式共享对象,设置 DT_AUXILIARY 名称。
  • -F <name>, --filter=<name>: 对于ELF格式共享对象,设置 DT_FILTER 名称。这告诉动态链接器,正在创建的共享对象的符号表应该用作共享对象名称的符号表的筛选器。
  • -g: 被忽略。用于提供和其他工具的兼容性。
  • -h: 对于 ELF 格式共享对象,设置 DT_SONAME 名称。
  • -I<file>, -dynamic-linker <file>, --dynamic-linker=<file>: 指定动态链接器。这仅在生成依赖动态链接库的 ELF 可执行文件时才有意义。默认的动态链接器通常是正确的,除非您知道正在做什么,否则不要使用该选项。
  • -l <namespec>, --library=<namespec>: 把指定的库文件添加到要链接的文件清单。
  • -L <searchdir>, --library-path=searchdir: 把指定的路径添加添加到搜索库的目录清单。
  • -M, --print-map: 显示链接映射,用于诊断目的。
  • -Map=<mapfile>: 将链接映射输出到指定的文件。
  • -m <emulation>: 模拟指定的链接器。
  • -N,--omagic: 指定读取/写入文本和数据段。
  • -n,--nmagic: 关闭节的页面对齐,并禁用对共享库的链接。如果输出格式支持Unix样式的幻数,则将输出标记为"NMAGIC"。
  • -noinhibit-exec: 生成输出文件,即使出现非致命链接错误。通常,如果链接器在链接过程中遇到错误,它将不会生成输出文件。
  • -no-keep-memory: ld 通常在内存中缓存输入文件的符号表来优化内存使用速度。此选项告诉 ld 不要缓存符号表。当链接大型可执行文件时,如果ld耗尽内存空间,则可能需要使用该选项。
  • -O <level>: 对于非零的优化等级,ld将优化输出。此操作会比较耗时,应该在生成最终的结果时使用。
  • -o <output>, --output=<output>: 指定输出文件的名称。
  • -oformat=<output-format>: 指定输出文件的二进制格式。
  • -R <filename>,--just-symbols=<filename>: 从指定的文件读取符号名称和地址。
  • -r,--relocatable: 生成可重定位的输出(称为部分连接)。
  • -rpath=<dir>: 把指定的目录添加到运行时库搜索路径。
  • -rpath-link=<dir>: 指定搜索运行时共享库的目录。
  • -S,--strip-debug: 忽略来自输出文件的调试器符号信息。
  • -s,--strip-all: 忽略来自输出文件的所有符号信息。
  • -shared, -Bshareable: 创建共享库。
  • -split-by-file[=size]: 为每个目标文件在输出文件中创建额外的段大小达到size。size默认为1。
  • -split-by-reloc[=count]: 按照指定的长度在输出文件中创建额外的段。
  • --section-start=<sectionname>=<org>: 在输出文件中指定的地址定位指定的段。
  • -T <scriptfile>, --script=<scriptfile>: 使用 scriptfile 作为链接器脚本。此脚本将替换 ld 的默认链接器脚本(而不是添加到其中),因此脚本必须指定输出文件所需的所有内容。如果当前目录中不存在脚本文件,ld 会在 -L 选项指定的目录中查找。
  • -Ttext=<org>: 使用指定的地址作为文本段的起始点。
  • -Tdata=<org>: 使用指定的地址作为数据段的起始点。
  • -Tbss=<org>: 使用指定的地址作为bss段的起始点。
  • -t,--trace: 在处理输入文件时显示它们的名称。
  • -u <symbol>, --undefined=<symbol>: 强制指定符号在输出文件中作为未定义符号。
  • -v, -V, --version: 显示ld版本号。
  • -warn-common: 当一个通用符号和另一个通用符号结合时发出警告。
  • -warn-constructors: 如果没有使用任何全局构造器,则发出警告。
  • -warn-once: 对于每个未定义的符号只发出一次警告。
  • -warn-section-align: 如果为了对齐而改动了输出段地址,则发出警告。
  • --whole-archive: 对于指定的存档文件,在存档中包含所有文件。
  • -X, --discard-locals: 删除所有本地临时符号。
  • -x, --discard-all: 删除所有本地符号。

错误情况

  • 未解析的符号:如果ld在链接过程中找不到某个符号的定义,它会报错,提示未解析的符号。
  • 多重定义:如果同一个符号在多个目标文件中都有定义,ld会报错。
  • 文件格式不匹配:如果尝试链接不同格式的目标文件,例如ELF和a.out,ld会报错。
  • 库未找到:使用-l选项指定的库如果在库搜索路径中未找到,ld会报错。
  • 输入/输出错误:如果在读取输入文件或写入输出文件时出现错误,ld会报错。

注意事项

当使用Linux中的ld命令时,有一些注意事项需要考虑,以确保链接过程顺利进行并生成正确的输出文件:

  1. 库的顺序:在命令行中,库的顺序很重要。通常,应该先列出目标文件,然后列出库文件。这是因为ld从左到右处理参数,解析目标文件中的符号引用时,它会在后续的库文件中查找这些符号。
  2. 系统库的链接:当链接系统库时,通常使用-l选项,例如-lm用于链接数学库。但要确保库的搜索路径设置正确,可以使用-L选项添加额外的搜索路径。
  3. 静态与动态链接:默认情况下,ld会尝试动态链接库。如果需要静态链接,可以使用-static选项。但要注意,静态链接会使输出文件体积增大。
  4. 符号冲突:确保不同的目标文件中没有重复定义的符号,否则ld会报多重定义错误。
  5. 使用适当的链接脚本ld使用链接脚本来控制输出文件的布局。如果有特殊需求,可以使用-T选项指定自定义的链接脚本。
  6. 去除不必要的符号信息:为了减小输出文件的大小,可以使用-s选项去除符号信息。但这会使得后续的调试变得困难。
  7. 确保兼容性:如果链接的目标文件是在不同的系统或使用不同的编译器选项生成的,可能会出现兼容性问题。确保所有文件都是在相同的环境下编译的。
  8. 检查输出:链接完成后,使用file命令检查输出文件的类型,确保它是期望的格式(例如,ELF可执行文件)。
  9. 避免使用过时的库或选项:随着时间的推移,某些库或选项可能已被弃用。确保使用的都是当前版本的库和推荐的链接选项。
  10. 阅读文档ld的功能和选项可能会随着版本的更新而变化。定期查阅man ld或其他相关文档,以获取最新的信息和建议。

总之,使用ld命令时需要注意多个方面,确保链接过程正确无误,并生成期望的输出文件。


底层实现

ld命令,作为GNU Binutils套件的一部分,是Linux中的标准链接器。它的底层实现涉及多个复杂的步骤和算法,以下是其核心实现的概述:

  1. 输入处理
  • ld首先读取所有输入的目标文件和库文件。
  • 它解析ELF(Executable and Linkable Format)或其他格式的文件头,获取段信息、符号表等。
  1. 符号解析
  • ld构建一个全局符号表,其中包含所有输入文件中的符号。
  • 对于未解析的符号(例如,一个目标文件中引用但在其他地方定义的函数),ld会在所有输入的库文件中搜索这些符号的定义。
  • 如果找不到某个符号的定义,ld会报错。
  1. 地址分配
  • ld将所有输入文件的段(如.text.data)合并为一个大的段,并为其分配地址。
  • 它还处理重定位条目,这些条目指定了在链接时需要修改的代码或数据的位置。
  1. 重定位
  • 由于目标文件是独立编译的,它们包含的地址引用可能不是最终的正确地址。ld使用重定位条目来更新这些引用,使它们指向正确的地址。
  1. 输出生成
  • 一旦所有符号都被解析并且所有段都被分配了地址,ld就会生成输出文件。
  • 输出文件通常是ELF格式,但也可以是其他格式,取决于目标平台。
  1. 优化
  • 在某些情况下,ld还可以进行链接时优化,例如删除未使用的代码或数据。

ld的底层实现主要是用C语言编写的,并且是开源的。 interested in the detailed implementation, you can check out the source code of the GNU Binutils project.

总的来说,ld的底层实现涉及多个复杂的步骤,从输入文件的解析到地址分配、重定位和最终的输出生成。这些步骤确保了生成的可执行文件或库文件在运行时能够正确地访问其代码和数据。


示例

示例一

链接两个目标文件 file1.ofile2.o 生成可执行文件 program:

ld -o program file1.o file2.o

示例二

链接目标文件 file.o 并使用静态库 libstatic.a:

ld -o program file.o -Bstatic -lstatic

示例三

链接目标文件,同时指定动态链接器的路径:

ld -o program file.o -I/usr/local/lib/ld-linux.so.2

示例四

链接目标文件并指定输出文件的二进制格式为 elf64-x86-64:

ld -o program file.o -oformat=elf64-x86-64

示例五

链接目标文件并强制符号 my_symbol 在输出文件中作为未定义符号:

ld -o program file.o -u my_symbol

示例六

链接目标文件并使用链接器脚本 linker_script.ld:

ld -o program file.o -T linker_script.ld

示例七

链接目标文件并显示处理输入文件时的名称:

ld -o program file.o -t

用c语言 模拟ld思想

实现ld命令的功能是一个复杂的任务,因为ld是一个完整的程序链接器,它处理多种文件格式、解析符号、处理重定位等。完整地实现ld的功能需要大量的代码和深入的知识。

但我可以为您提供一个简化版的链接器的概念实现,这将帮助您理解链接器的基本工作原理。这只是一个非常基础的示例,不能用于实际的链接任务。

#include <stdio.h>
#include <stdlib.h>
// 假设我们有两个简单的目标文件格式:
// file1.o: "HELLO "
// file2.o: "WORLD"
int main(int argc, char *argv[]) {
    if (argc < 4) {
        printf("Usage: %s output input1 input2\n", argv[0]);
        return 1;
    }
    FILE *input1 = fopen(argv[2], "rb");
    FILE *input2 = fopen(argv[3], "rb");
    FILE *output = fopen(argv[1], "wb");
    if (!input1 || !input2 || !output) {
        perror("Error opening files");
        return 1;
    }
    // 简单地将两个输入文件的内容复制到输出文件
    char ch;
    while ((ch = fgetc(input1)) != EOF) {
        fputc(ch, output);
    }
    while ((ch = fgetc(input2)) != EOF) {
        fputc(ch, output);
    }
    fclose(input1);
    fclose(input2);
    fclose(output);
    printf("Linking completed.\n");
    return 0;
}

这个简化版的链接器只是将两个输入文件的内容复制到一个输出文件。在实际的ld链接器中,它会处理各种目标文件格式、解析符号、处理重定位、合并段等复杂任务。

如果您真的想深入了解链接器的工作原理,我建议您阅读关于链接器和加载器的专业书籍,或查看开源链接器如GNU ld的源代码。



结语

在我们的探索过程中,我们已经深入了解了Shell命令的强大功能和广泛应用。然而,学习这些技术只是开始。真正的力量来自于你如何将它们融入到你的日常工作中,以提高效率和生产力。

心理学告诉我们,学习是一个持续且积极参与的过程。所以,我鼓励你不仅要阅读和理解这些命令,还要动手实践它们。尝试创建自己的命令,逐步掌握Shell编程,使其成为你日常工作的一部分。

同时,请记住分享是学习过程中非常重要的一环。如果你发现本博客对你有帮助,请不吝点赞并留下评论。分享你自己在使用Shell命令时遇到的问题或者有趣的经验,可以帮助更多人从中学习。

此外,我也欢迎你收藏本博客,并随时回来查阅。因为复习和反复实践也是巩固知识、提高技能的关键。

最后,请记住:每个人都可以通过持续学习和实践成为Shell编程专家。我期待看到你在这个旅途中取得更大进步!

目录
相关文章
|
10月前
|
安全 Linux Shell
四、Linux核心工具:Vim, 文件链接与SSH
要想在Linux世界里游刃有余,光会“走路”还不够,还得配上几样“高级装备”。首先是Vim编辑器,它像一把瑞士军刀,让你能在命令行里高效地修改文件。然后要懂“软硬链接”,软链接像个快捷方式,硬链接则是给文件起了个别名。最后,SSH是你的“传送门”,不仅能让你安全地远程登录服务器,还能用scp轻松传输文件,设置好密钥更能实现免-密登录,极大提升效率。
612 5
|
10月前
|
算法 Linux Shell
Linux实用技能:打包压缩、热键、Shell与权限管理
本文详解Linux打包压缩技巧、常用命令与原理,涵盖.zip与.tgz格式操作、跨系统传文件方法、Shell运行机制及权限管理,助你高效使用Linux系统。
Linux实用技能:打包压缩、热键、Shell与权限管理
|
10月前
|
存储 安全 Unix
七、Linux Shell 与脚本基础
别再一遍遍地敲重复的命令了,把它们写进Shell脚本,就能一键搞定。脚本本质上就是个存着一堆命令的文本文件,但要让它“活”起来,有几个关键点:文件开头最好用#!/usr/bin/env bash来指定解释器,并用chmod +x给它执行权限。执行时也有讲究:./script.sh是在一个新“房间”(子Shell)里跑,不影响你;而source script.sh是在当前“房间”里跑,适合用来加载环境变量和配置文件。
871 9
|
10月前
|
Unix Linux 程序员
Linux文本搜索工具grep命令使用指南
以上就是对Linux环境下强大工具 `grep` 的基础到进阶功能介绍。它不仅能够执行简单文字查询任务还能够处理复杂文字处理任务,并且支持强大而灵活地正则表达规范来增加查询精度与效率。无论您是程序员、数据分析师还是系统管理员,在日常工作中熟练运用该命令都将极大提升您处理和分析数据效率。
805 16
|
10月前
|
存储 Shell Linux
八、Linux Shell 脚本:变量与字符串
Shell脚本里的变量就像一个个贴着标签的“箱子”。装东西(赋值)时,=两边千万不能有空格。用单引号''装进去的东西会原封不动,用双引号""则会让里面的$变量先“变身”再装箱。默认箱子只能在当前“房间”(Shell进程)用,想让隔壁房间(子进程)也能看到,就得给箱子盖个export的“出口”戳。此外,Shell还自带了$?(上条命令的成绩单)和$1(别人递进来的第一个包裹)等许多特殊箱子,非常有用。
939 2
|
10月前
|
安全 Linux iOS开发
SonarQube Server 2025 Release 5 (macOS, Linux, Windows) - 代码质量、安全与静态分析工具
SonarQube Server 2025 Release 5 (macOS, Linux, Windows) - 代码质量、安全与静态分析工具
391 0
SonarQube Server 2025 Release 5 (macOS, Linux, Windows) - 代码质量、安全与静态分析工具
|
12月前
|
缓存 监控 Linux
Linux系统性能调优技巧和相关工具
Linux 作为一种应用应展和系统服务的优选操作系统,在处理性能和端到端点评估上持有出色表现。但是,在处理进程或系统处于低效状态时,性能调优就显得十分重要。本文将探讨一些 Linux 系统性能调优的常用技巧,并介绍相关工具
322 1
Linux系统性能调优技巧和相关工具
|
Linux 数据安全/隐私保护 iOS开发
推荐Linux环境下效能优良的双向文件同步工具
综合上述条件,对于Linux环境下的双向文件同步需求,Unison 和 Syncthing 是两个非常出色的选择。它们都有良好的社区支持和文档资源,适用于不同规模的环境,从个人使用到商业部署。Unison 特别适合那些需要手动干预同步过程、需要处理文件冲突解决的场景。而 Syncthing 更加现代化,适合需要自动、实时的数据同步与备份的环境。对于选择哪一个,这将取决于个人的使用场景和具体需求。
1305 16
|
12月前
|
Web App开发 缓存 安全
Linux一键清理系统垃圾:释放30GB空间的Shell脚本实战​
这篇博客介绍了一个实用的Linux系统盘清理脚本,主要功能包括: 安全权限检查和旧内核清理,保留当前使用内核 7天以上日志文件清理和系统日志压缩 浏览器缓存(Chrome/Firefox)、APT缓存、临时文件清理 智能清理Snap旧版本和Docker无用数据 提供磁盘空间使用前后对比和大文件查找功能 脚本采用交互式设计确保安全性,适合定期维护开发环境、服务器和个人电脑。文章详细解析了脚本的关键功能代码,并给出了使用建议。完整脚本已开源,用户可根据需求自定义调整清理策略。
1268 1
|
11月前
|
数据采集 编解码 运维
一文讲完说懂 WowKey -- WowKey 是一款 Linux 类设备的命令行(CLT)运维工具
WowKey 是一款面向 Linux 类设备的命令行运维工具,支持自动登录、批量执行及标准化维护,适用于企业、团队或个人管理多台设备,显著提升运维效率与质量。