热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

链接、装载和库看完这个系列就够了(一)(静态库链接顺序问题)

初衷工作中经常会碰到不同平台之间移植代码,项目中引入各种开源库,经常会碰到很多奇怪的编译、链接问题,这里做一下整理,尽量包

初衷

工作中经常会碰到不同平台之间移植代码,项目中引入各种开源库,经常会碰到很多奇怪的编译、链接问题,这里做一下整理,尽量包含常见的大部分问题。首先我们来关注一下链接顺序引发的问题。

静态库链接顺序问题

最常见的问题

正常情况下(每个静态库之间没有相互依赖关系),静态库之间链接顺序是不需要关注的,但是如果两个库之间存在相互的调用,就会出现链接问题,看一个例子(头文件只声明函数,不再贴出):

//lib_a1.c
#include "lib_a1.h"
int a1_fun()
{return 0;
}

//lib_a2.c
#include "lib_a2.h"
#include "lib_a1.h"
int a2_fun()
{a1_fun();return 0;
}

//main.c
#include "lib_a1.h"
#include "lib_a2.h"int main()
{//a1_fun();a2_fun();return 0;
}

#Makefile
all:main
lib_a1.a: lib_a1.oar rcs $&#64; $<
lib_a2.a: lib_a2.oar rcs $&#64; $

此时执行&#xff0c;会报未定义的问题&#xff08;undefined reference to &#96;a1_fun’&#xff09;&#xff1a;

#make clean;make
rm -rf *.o *.a *.so main
cc -c -o main.o main.c
cc -c -o lib_a1.o lib_a1.c
ar rcs lib_a1.a lib_a1.o
cc -c -o lib_a2.o lib_a2.c
ar rcs lib_a2.a lib_a2.o
gcc -o main main.o -l_a1 -l_a2 -L.
./lib_a2.a(lib_a2.o): In function &#96;a2_fun&#39;:
lib_a2.c:(.text&#43;0xa): undefined reference to &#96;a1_fun&#39;
collect2: error: ld returned 1 exit status
Makefile:9: recipe for target &#39;main&#39; failed
make: *** [main] Error 1

如果在Makefile中修改一下链接顺序&#xff0c;也就是先链接lib_a2.a&#xff0c;此时编译正常&#xff1a;

# make
gcc -o main main.o -l_a2 -l_a1 -L.

出现这样的问题的原因是静态链接库在链接的时候是有顺序的&#xff0c;具体来说&#xff0c;gcc在链接时是按照链接参数给定的顺序依次读入&#xff0c;如果首先读入的是lib_a2.a&#xff0c;此时发现a1_fun()函数还未定义&#xff0c;会加入未定义符号表&#xff0c;然后读入lib_a1.a时&#xff0c;发现a1_fun()在这里面定义&#xff0c;此时把a1_fun()从未定义的符号表中去除。链接完成后检查未定义表&#xff0c;此时没有未定义的符号&#xff0c;则链接正常。
反过来&#xff0c;如果一开始读取lib_a1.a&#xff0c;发现a1_fun()函数并没有在未定义符号表中&#xff0c;此时链接器不会把a1_fun()函数链接到最终的可执行文件中&#xff0c;然后后续读入lib_a2.a的时候&#xff0c;发现a1_fun()没有定义(因为上一步a1_fun()并没有链接进来)&#xff0c;链接结束后就会报a1_fun()没有定义。

一个不好的解决办法

对于这种小问题&#xff0c;一个解决办法就是上面的判断每个静态库的依赖关系&#xff0c;按照依赖关系进行决定链接顺序。同时还有另外一个办法就是多次添加链接&#xff1a;

# make
gcc -o main main.o -l_a1 -l_a2 -l_a1 -L.

链接顺序是a1,a2,然后再链接a1,此时也可以链接正常&#xff0c;原因可以参考上面的解释。

使用-Xlinker

一旦项目很大的时候&#xff0c;或者静态库依赖复杂的时候&#xff0c;找出每个依赖关系并不简单&#xff0c;此时可以使用-Xlinker参数&#xff1a;

# make
gcc -o main main.o -Xlinker "-(" -l_a1 -l_a2 -Xlinker "-)" -L.

此时链接正常&#xff0c;这是因为链接器在处理”-(”和”-)”之间的静态库时&#xff0c;是会重复查找这些静态库的&#xff0c;不过这个参数带来的一个问题就是会导致链接速度变慢。是否使用-Xlinker需要自己去做权衡。

main.c带来的问题
好了&#xff0c;我们把Makefile恢复到初始状态&#xff1a;

#Makefile
all:mainlib_a1.a: lib_a1.oar rcs $&#64; $<
lib_a2.a: lib_a2.oar rcs $&#64; $

之前把main.c的内容忽略了&#xff0c;其实main.c的内容也是对链接顺序有影响的。
如果把main.c的注释的那一行&#xff08;//a1_fun()&#xff09;恢复&#xff0c;或者把main函数的两行调用都注释掉&#xff0c;那么链接又正常了。

# make clean;make
cc -c -o main.o main.c
cc -c -o lib_a1.o lib_a1.c
ar rcs lib_a1.a lib_a1.o
cc -c -o lib_a2.o lib_a2.c
ar rcs lib_a2.a lib_a2.o
gcc -o main main.o -l_a1 -l_a2 -L.

其实原理是一样的&#xff0c;我们分析一下注释恢复这种情况&#xff0c;链接器首先读入main.o&#xff0c;发现a1_fun()和a2_fun()都没有定义&#xff0c;所以把它们加入未定义符号表&#xff0c;&#xff0c;然后链接lib_a1时&#xff0c;会把a1_fun()链接到可执行文件&#xff0c;同理链接lib_a2时&#xff0c;也类似。所以最终没有未定义的符号&#xff0c;所以就不会报错。这就引发一个问题奇怪的问题&#xff0c;如果我们把main.o写到最后&#xff1a;

# make
gcc -o main -l_a1 -l_a2 -L. main.o
main.o: In function &#96;main&#39;:
main.c:(.text&#43;0xa): undefined reference to &#96;a1_fun&#39;
main.c:(.text&#43;0x14): undefined reference to &#96;a2_fun&#39;
collect2: error: ld returned 1 exit status
Makefile:9: recipe for target &#39;main&#39; failed
make: *** [main] Error 1

我们发现链接又报错了&#xff0c;原因和上面类似&#xff0c;不在重复。所以一个看似很简单的问题&#xff0c;其实隐藏着很多问题&#xff0c;所以也就能解释为什么我们在项目的处理中会碰到各种各样的奇怪的问题。在这里推荐一本书<<程序员的自我修养—链接、装载与库>>&#xff0c;这本书对链接进行了很深入的讲解。


推荐阅读
  • 手机上编写和运行PHP代码的最佳软件推荐 ... [详细]
  • 利用PaddleSharp模块在C#中实现图像文字识别功能测试
    PaddleSharp 是 PaddleInferenceCAPI 的 C# 封装库,适用于 Windows (x64)、NVIDIA GPU 和 Linux (Ubuntu 20.04) 等平台。本文详细介绍了如何使用 PaddleSharp 在 C# 环境中实现图像文字识别功能,并进行了全面的功能测试,验证了其在多种硬件配置下的稳定性和准确性。 ... [详细]
  • 在CentOS上部署和配置FreeSWITCH
    在CentOS系统上部署和配置FreeSWITCH的过程涉及多个步骤。本文详细介绍了从源代码安装FreeSWITCH的方法,包括必要的依赖项安装、编译和配置过程。此外,还提供了常见的配置选项和故障排除技巧,帮助用户顺利完成部署并确保系统的稳定运行。 ... [详细]
  • Spring Boot 实战(一):基础的CRUD操作详解
    在《Spring Boot 实战(一)》中,详细介绍了基础的CRUD操作,涵盖创建、读取、更新和删除等核心功能,适合初学者快速掌握Spring Boot框架的应用开发技巧。 ... [详细]
  • 本文探讨了如何在C#中实现USB条形码扫描仪的数据读取,并自动过滤掉键盘输入,即使不知道设备的供应商ID(VID)和产品ID(PID)。通过详细的技术指导和代码示例,展示了如何高效地处理条形码数据,确保系统能够准确识别并忽略来自键盘的干扰信号。该方法适用于多种USB条形码扫描仪,无需额外配置设备信息。 ... [详细]
  • 结语 | 《探索二进制世界:软件安全与逆向分析》读书笔记:深入理解二进制代码的逆向工程方法
    结语 | 《探索二进制世界:软件安全与逆向分析》读书笔记:深入理解二进制代码的逆向工程方法 ... [详细]
  • 从无到有,构建个人专属的操作系统解决方案
    操作系统(OS)被誉为程序员的三大浪漫之一,常被比喻为计算机的灵魂、大脑、内核和基石,其重要性不言而喻。本文将详细介绍如何从零开始构建个人专属的操作系统解决方案,涵盖从需求分析到系统设计、开发与测试的全过程,帮助读者深入理解操作系统的本质与实现方法。 ... [详细]
  • 在Linux环境下编译安装Heartbeat时,常遇到依赖库缺失的问题。为确保顺利安装,建议预先通过yum安装必要的开发库,如glib2-devel、libtool-ltdl-devel、net-snmp-devel、bzip2-devel和ncurses-devel等。这些库是编译过程中不可或缺的组件,能够有效避免编译错误,确保Heartbeat的稳定运行。 ... [详细]
  • 本文详细探讨了C语言中`extern`关键字的简易编译方法,并深入解析了预编译、`static`和`extern`的综合应用。通过具体的代码示例,介绍了如何在不同的文件之间共享变量和函数声明,以及这些关键字在编译过程中的作用和影响。文章还讨论了预编译过程中宏定义的使用,为开发者提供了实用的编程技巧和最佳实践。 ... [详细]
  • 本文详细介绍了如何在Linux系统中搭建51单片机的开发与编程环境,重点讲解了使用Makefile进行项目管理的方法。首先,文章指导读者安装SDCC(Small Device C Compiler),这是一个专为小型设备设计的C语言编译器,适合用于51单片机的开发。随后,通过具体的实例演示了如何配置Makefile文件,以实现代码的自动化编译与链接过程,从而提高开发效率。此外,还提供了常见问题的解决方案及优化建议,帮助开发者快速上手并解决实际开发中可能遇到的技术难题。 ... [详细]
  • 深入解析Gradle中的Project核心组件
    在Gradle构建系统中,`Project` 是一个核心组件,扮演着至关重要的角色。通过使用 `./gradlew projects` 命令,可以清晰地列出当前项目结构中包含的所有子项目,这有助于开发者更好地理解和管理复杂的多模块项目。此外,`Project` 对象还提供了丰富的配置选项和生命周期管理功能,使得构建过程更加灵活高效。 ... [详细]
  • 本文介绍了C语言中指针的基础知识及其初步应用。首先,文章详细解释了如何定义变量和指针,例如通过 `int i, j, k;` 定义整型变量,以及使用 `int *pi, *pj, *pk;` 来声明指向整型数据的指针。接着,探讨了变量和指针的初始化方法,强调了正确的初始化对于避免程序错误的重要性。此外,还简要介绍了指针在数组、函数参数传递等场景中的基本应用,为初学者提供了全面的入门指导。 ... [详细]
  • 在 Linux 系统中,`/proc` 目录实现了一种特殊的文件系统,称为 proc 文件系统。与传统的文件系统不同,proc 文件系统主要用于提供内核和进程信息的动态视图,通过文件和目录的形式呈现。这些信息包括系统状态、进程细节以及各种内核参数,为系统管理员和开发者提供了强大的诊断和调试工具。此外,proc 文件系统还支持实时读取和修改某些内核参数,增强了系统的灵活性和可配置性。 ... [详细]
  • 在C#中,当一个实例方法被标记为 `virtual` 关键字时,该方法即成为虚方法。虚方法的主要特点在于其可以在派生类中被重写,从而改变其行为。这种机制允许子类根据需要提供不同的实现,增强了代码的灵活性和可扩展性。虚方法的使用不仅提高了代码的复用率,还为面向对象编程中的多态性提供了基础支持。 ... [详细]
  • 本文探讨了将PEBuilder转换为DIBooter.sh的方法,重点介绍了如何将DI工具集成到启动层,实现离线镜像引导安装。通过使用DD命令替代传统的grub-install工具,实现了GRUB的离线安装。此外,还详细解析了bootice工具的工作原理及其在该过程中的应用,确保系统在无网络环境下也能顺利引导和安装。 ... [详细]
author-avatar
weibophp
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有