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

【Linux】Linux下的静态链接的底层逻辑
前言:欢迎各位光临本博客,这里小编带你直接手撕**,文章并不复杂,愿诸君**耐其心性,忘却杂尘,道有所长!!!!
在这里插入图片描述

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

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

文章目录

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

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

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


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

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

再看code.o的数据节

code.o的数据节


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

看看hello.o的数据节

hello.o的数据节


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

关键点

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

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

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

工具objdump:看内部机器码
objdump -d反汇编目标文件,能看机器指令:

objdump输出


这里,call指令的地址是00 00 00 00(全0),因为函数地址还没确定。链接后,这些空白被真实地址填充。

合并目标文件

链接过程


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

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

// hello.c(简化版)externint global_var;voidhello(){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. 地址重定位:磁盘上的“虚拟地图”

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

虚拟地址空间:平坦模式

虚拟地址空间


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

填充地址空白

地址重定位


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

关键点

  • 磁盘上的地址叫逻辑地址,内存中叫虚拟地址,物理地址是RAM真实位置。

入口地址(如_start)是程序起点,加载时CPU的EIP寄存器指向它。

入口函数_start

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

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

优势:代码不重复

动态库代码


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

进程如何看待动态库

动态库关联


进程虚拟地址空间包含共享区(如libc.so)。调用库函数时,CPU跳转到共享区执行,完成后返回。

mm_struct


操作系统用mm_struct管理虚拟内存,库代码通过页表映射到共享区。

对比静态链接

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

总结

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

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

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

Read more

远程桌面:Ubuntu Desktop操作系统上如何安装Rustdesk远程桌面客户端(离线安装包)

本文将为您提供在 Ubuntu Desktop 操作系统上通过离线安装包安装 RustDesk 远程桌面客户端的详细步骤。以下是完整的安装指南,确保清晰且易于操作。 前提条件 1. 操作系统:Ubuntu Desktop(支持的版本通常包括 20.04、22.04、24.04 等 LTS 或非 LTS 版本)。 2. 离线安装包:确保您已下载 RustDesk 的 .deb 安装包(适用于 Ubuntu/Debian 系统)。您可以从 RustDesk 官方网站(https://rustdesk.com/)或 GitHub 页面(https://github.com/rustdesk/rustdesk/

By Ne0inhk
IoTDB 性能优化双杀:查询分析与负载均衡实战指南

IoTDB 性能优化双杀:查询分析与负载均衡实战指南

IoTDB 性能优化双杀:查询分析与负载均衡实战指南 在分布式时序数据库的日常运维中,查询慢、集群负载不均是最让人头疼的两大问题。前者直接影响业务响应速度,后者则可能导致节点资源浪费或宕机风险。今天就来分享IoTDB的两大核心优化手段——查询性能分析(Explain/Explain Analyze)和Region迁移负载均衡,用实操技巧帮你搞定这两个痛点。 一、查询性能分析:精准定位慢查询瓶颈 不管是日常运维还是开发调试,搞懂查询的执行逻辑和资源消耗,才能针对性优化。IoTDB从V1.3.2版本开始,内置了两套实用的查询分析工具,不用额外部署,上手就能用。 1. 两款核心分析工具:Explain vs Explain Analyze 这两个工具各有侧重,咱们可以根据需求灵活选择: * Explain语句:相当于查询的"执行蓝图",不用实际执行SQL,就能预览执行计划。比如数据怎么检索、过滤条件是否生效、查询计划在哪些节点分配,都能一目了然。 语法特别简单:EXPLAIN <SELECT_

By Ne0inhk
告别繁琐配置!用 Trae Remote-SSH 轻松远程开发和部署

告别繁琐配置!用 Trae Remote-SSH 轻松远程开发和部署

目录 * 开发者的痛点:本地环境 VS 远程环境的开发难题 * Trae 国内版上新功能了 * Trae Remote-SSH 解决方案 * Trae Remote-SSH 运行原理 * 设备要求 * 步骤 1:打开远程资源管理器 * 步骤 2:连接远程主机 * 步骤 3:打开远程文件夹,开始开发 * 实践 通过trae远程开发 贪吃蛇 项目 * 前置条件 * 连接远程主机 * 通过本地trae远程编码 * 远程调试 * 自定义模型接入能力 * 1.点击添加模型选项 * 2.模型配置 * 3.丝滑使用自定义模型 * AI 助力远程开发 * 1.全自然语言项目初始化 * 2.智能编码全流程演示 * 3.混合编程模式进阶 * 4.远程部署一体化 * 5.

By Ne0inhk