当前位置: 首页 > news >正文

【Linux】Linux下的静态链接的底层逻辑

前言:欢迎各位光临本博客,这里小编带你直接手撕**,文章并不复杂,愿诸君**耐其心性,忘却杂尘,道有所长!!!!

在这里插入图片描述


IF’Maxue:个人主页

 🔥 个人专栏:
《C语言》
《C++深度学习》
《Linux》
《数据结构》
《数学建模》

⛺️生活是默默的坚持,毅力是永久的享受。不破不立!

文章目录

      • 静态链接与动态链接原理详解
        • 1. **目标文件:代码的“零件包”**
        • 2. **链接过程:合并零件包,解决“地址谜题”**
        • 3. **地址重定位:磁盘上的“虚拟地图”**
        • 4. **动态链接:共享库的魔法**
        • 总结

这是一篇博客的雏形,请用通俗化的语言结合图片内容和上下文,不要修改减少或增添所有的图片,以图片为中心上下文内容要强关联,对其进行优化,
要求:通俗化,简洁化,分段式,详细化,结合代码

静态链接与动态链接原理详解

大家好!今天我们来聊聊程序编译和链接的那些事儿。想象一下,写代码就像拼乐高:源代码是零件,编译器是组装工,链接器就是最终拼接大师。静态链接和动态链接是两种拼接方式,我会用通俗语言、结合图片和代码,一步步带你搞懂核心原理。所有图片都来自真实案例,我会以它们为中心,详细解释上下文。


1. 目标文件:代码的“零件包”

当我们写C程序时,比如hello.ccode.c,编译器会先把它们变成目标文件(.o文件)。这些文件就像未组装的乐高零件包,里面包含代码和数据,但还没合并。

  • 看看hello.o的数据节
    hello.o的数据节
    这张图展示了hello.o的内部结构。数据节(Data Section)存储变量和常量,大小约12个字节。代码节(Text Section)放机器指令,比如printf函数的调用。注意:如果变量在其他文件定义(如extern int global_var;),这里会标记为“未定义”(UND),因为编译器还不知道它的地址。

  • 再看code.o的数据节
    code.o的数据节
    code.o的大小也约12个字节,和hello.o规模相似。但这里定义了全局变量(如int global_var = 42;),数据节是“已定义”状态。两个文件独立时,彼此不知道对方的存在——就像两个乐高包没拆封。

关键点

  • 编译器处理单个文件时,不检查外部依赖。例如,hello.c里调用haha()函数,但haha未定义,编译.o文件不会报错(链接时才暴露问题)。
  • 函数默认是“外部”的,编译器自动假设它在别处定义;变量需显式加extern,否则内存分配会冲突。

2. 链接过程:合并零件包,解决“地址谜题”

链接器的作用是把多个.o文件(如hello.ocode.o)拼成一个可执行程序。这就像把乐高零件包拆开,按图纸组装。合并后,所有代码和数据节统一编址,解决未定义符号的问题。

  • 合并目标文件
    链接过程
    链接器将多个.o文件的Text和Data节合并成一个。例如,hello.o的Text节和code.o的Data节拼接,形成新结构。原本独立的节(如.text.data)被重新编号(如图中Section 14)。

  • 工具objdump:看内部机器码
    objdump -d反汇编目标文件,能看机器指令:
    objdump输出
    这里,call指令的地址是00 00 00 00(全0),因为函数地址还没确定。链接后,这些空白被真实地址填充。

结合代码示例
假设hello.c调用printfcode.c定义全局变量。编译后汇编如下:

// hello.c(简化版)
extern int global_var;
void hello() {printf("Value: %d\n", global_var); // 调用外部函数和变量
}

编译成汇编(hello.s):

; hello.s 片段
call printf   ; 机器码 e8 00 00 00 00(地址未填充)

hello.c vs hello.s
call指令的机器码是e8,后跟4字节地址。链接前是0,因为模块未合并。


3. 地址重定位:磁盘上的“虚拟地图”

链接后,可执行程序在磁盘上就有完整地址了,这叫地址重定位。它基于虚拟地址空间——程序运行时“看到”的内存地图,不是真实物理地址。

  • 填充地址空白
    地址重定位
    链接器计算每个符号的偏移量。例如,printf函数在合并Text节中的位置是0x400500,就填充到call指令后。公式简单:地址=基址+偏移量地址 = 基址 + 偏移量地址=基址+偏移量

  • 虚拟地址空间:平坦模式
    虚拟地址空间
    可执行程序在磁盘上使用虚拟地址编址(如从0x400000开始)。Text节放代码,Data节放变量,BSS节放未初始化数据。这种“平坦模式”让程序加载到内存时,地址直接映射,无需大调整。

关键点

  • 磁盘上的地址叫逻辑地址,内存中叫虚拟地址,物理地址是RAM真实位置。
  • 入口地址(如_start)是程序起点,加载时CPU的EIP寄存器指向它。
    入口函数_start

4. 动态链接:共享库的魔法

动态链接(共享库)让多个程序共用同一份代码,节省内存。不同于静态链接(库代码复制到每个程序),动态库加载到内存共享区。

  • 进程如何看待动态库
    动态库关联
    进程虚拟地址空间包含共享区(如libc.so)。调用库函数时,CPU跳转到共享区执行,完成后返回。
    mm_struct
    操作系统用mm_struct管理虚拟内存,库代码通过页表映射到共享区。

  • 优势:代码不重复
    动态库代码
    动态库(如.so文件)在磁盘只有一份,所有进程共享内存中的副本。例如,100个程序用printf,内存中只存一份libc代码。

对比静态链接

  • 静态库:每个程序自带库副本,文件大、内存占用高。
  • 动态库:轻量灵活,但首次调用稍慢(需加载库)。

总结

静态链接像“打包行李”:所有代码合并成一个文件,独立但笨重。动态链接像“共享单车”:代码在内存中共用,高效灵活。关键步骤记三点:

  1. 编译生成.o文件(零件包)。
  2. 链接合并并解决地址(拼装地图)。
  3. 运行时加载到虚拟地址空间(执行地图)。

通过图片和代码,希望你对链接过程有了直观理解。下次遇到“undefined reference”错误,就知道是链接器在喊:“嘿,我找不到那个零件!”

http://www.dtcms.com/a/418151.html

相关文章:

  • 2、Lombok核心注解详解:@Getter、@Setter、@Data 等基础注解全面解析
  • 兴力网站建设wordpress文章类型模板
  • springboot高校教务管理系统设计与实现(代码+数据库+LW)
  • Vala 编程语言高级特性-具有语法支持的方法
  • JavaEE初阶4.0
  • 医疗编程AI技能树与培训技能树报告(国内外一流大学医疗AI相关专业分析2025版,上)
  • 【IEEE出版 | 高录用、稳定检索】第七届信息与计算机前沿技术国际学术会议(ICFTIC 2025)
  • 我爱学算法之—— 模拟(上)
  • 白云做网站网店怎么注册开网店
  • 有了域名和主机怎么做网站erp软件是什么软件
  • 大数据毕业设计选题推荐-基于大数据的青光眼数据可视化分析系统-大数据-Spark-Hadoop-Bigdata
  • 数据可视化 | 热力图Heatmap绘制Python代码 相关性矩阵学术可视化
  • C#对称加密(AES)的简单代码
  • AR眼镜在安防领域人脸识别技术方案|阿法龙XR云平台
  • 【传奇开心果系列】基于Flet实现的第三次大的升级优化版语音播报成语接龙小游戏V3.0.1特色和实现原理深度解析
  • 【Qt】输入类控件2——SpinBox,DateEdit,TimeEdit,Dial,Slider
  • activemq延迟消息变成实时收到了?
  • 重庆市住房和城乡建设部网站中山人才招聘网官网
  • 如何构建有效的需求知识库?如何让你的AI用它来评审新需求?
  • HTML 和 Streamlit ,到底哪个好
  • 数据结构 之 【图的遍历与最小生成树】(广度优先遍历算法、深度优先遍历算法、Kruskal算法、Prim算法实现)
  • 胶州做网站的做网站设计有哪些网页
  • 开源 C# 快速开发(十)通讯--http客户端
  • 如何用 ShedLock 让 Spring Boot 的定时任务在多实例环境下只执行一次
  • Mask R-CNN工业落地实战:计算机视觉物体检测开山鼻祖的产线级代码剖析
  • 沈阳网站制作全网性做橡胶的网站
  • C++压缩解压:Zstandard (Zstd)压缩库
  • 在网站建设中 为了防止工期拖延荥阳网站制作
  • Filebeat写ElasticSearch故障排查思路(下)
  • 禅道数据还原