深入理解计算机系统_程序的链接过程

news2024/9/20 22:36:41

编辑好的程序,依次经过预处理(注释,宏替换,头文件包含,生成.s文件)、编译(生成汇编文件.s )、汇编(生成静态可重定位目标文件,文件名是.o)、链接后最终得到可执行目标文件,这个笔记记录一下,链接多个.o文件时候,编译器做的事情。

1 程序的链接—链接器做的两件事

链接程序collect2/ld,将所有的可重定位目标文件静态链接在一起,就得到了可执行目标文件。如果链接动态库时候,只是在函数位置记录函数的相对地址,当程序运行起来后再动态加载动态库,然后重新计算该地址。链接程序时候,编译器做了两件事事情,分别是符号解析地址重定位

1.1 第一:符号解析

符号解析的目的就是将每个符号的引用和定义联系起来,从而确定模块中引用的每个符号都有明确的定义。

1.1.1 如何解析符号?

链接器检查可重定位目标文件的(.o)的symtab表,看符号的定义情况。
(1)情况1:符号在本模块定义的(本地符号)
如果符号在本模块定义,每个符号对应的空间就被定义在了本模块的某个节中(比如.text、.data等节) ,如果正确找到就编译通过。
(2)情况2:符号由本模块引用,但是在其它模块定义的(全局符号)
被标记为UND的符号就是引用的外部符号,链接器进行符号解析时,重点解析的是标记为UND的符号,解析时链接器会检查UND符号是否在其它模块中有定义。链接器会查看其它模块的.symtab符号表,看该符号是否在其它模块中有定义。如果找到定义,就将符号的引用和定义关联起来;如果找不到,链接器就会报undefined reference to ‘符号名’的错误。

1.1.2 符号解析相关的链接错误

(1)找不到外部符号
预编译,编译,这两个阶段都会报相应的错误,汇编阶段通过不会报错,说一下链接阶段的报错。
在这里插入图片描述
(2)多个文件重复定义的问题
在这里插入图片描述
这种情况的解决方法是:加变量或函数前加static 或者将定义变为声明。
(3) 函数重名问题
C语言的函数就是通过函数名生成符号的,所以函数名称不能相同。
C++是通过函数名+参数生成函数符号的,所以C++允许函数重载。
(4) 关于强弱符号的报错问题
链接器根据符号的强弱共存规则来解析符号,链接时,对于多个模块之间的全局符号来说,是区分强弱符号的,下面是全局变量和函数强弱符号的区分。
全局变量
强符号:初始化了的全局变量;
弱符号:未初始化的全局变量;
函数
强符号:函数定义
弱符号:函数声明
每个全局符号是强符号还是弱符号,在.symtab表中会有相应的记录。分别记录一下多个模块以及相同模块之间的强弱符号共存问题。
不同模块之间的重名符号共存原则:
1 不允许多个同名的强符号同时存在,存在的话就报错;
2 只能有一个强符号,其他都是弱符号,取强舍弱。
3 同名符号如果全都是弱符号的话,链接器留其中某个,其它舍弃。
上面的例子就是情况1.
相同模块之间的重名符号共存原则:
1 本模块内只能有一个强符号,如果有多个强符号就会报错,这种错误在编译阶段就会检查出来。
2 相同作用域内定义相同的全局符号和本地符号,编译时会报错。

static int a = 20;
int a = 10;
int main(void)
{

}

注: 编译器只认强弱符号,不会认声明和定义
编译器/链接器只知道强符号和弱符号,解析时是按照强弱符号的规则来处理的。所以编译器并不知道什么是声明什么是定义。

1.2 第二:地址重定位

链接器完成符号解析后,就开始重定位了。分为静态重定位和动态重定位。
动态重定位:在程序运行的过程中完成重定位的;静态重定位:就再链接阶段完成的,下面说说静态重定位做的事情。
首先,将同名节整合为新的同名聚合节;
然后,将可执行目标文件中各聚合节的地址,重定位为实际运行的地址。
首先,将同名节整合为新的同名聚合节。链接器重点是将.text和.data解聚合为同名聚合节,通过.rel.text 和 .rel.data
在这里插入图片描述
.rel.text中的信息:实现.text的聚合
.rel.data中的信息:实现.data的聚合
然后 ,如何制定重定位后的运行地址(虚存地址)?
将可执行目标文件中各聚合节的地址,重定位为实际运行的地址其实就是将程序在内存中实际运行时的内存地址赋给聚合节。程序在内存中运行时,就是按照重定位的地址来运行的。重定位之后的地址是虚拟内存地址,所以CPU取址时候,也是取的虚存地址,经过转换后的物理地址就是要取的指令地址。
地址的指定就是通过“链接脚本文件”来指定的,“链接脚本文件”里面会说明实际的运行地址是多少,重定位时会把实际的运行地址赋值给新的聚合节,如此一来,函数和全局变量就有了真正可以运行的运行地址。实际运行时,将程序拷贝到运行地址所指定的内存位置,cpu的pc存放第一条指令地址(指向第一条指令_start),然后整个程序就运行起来了。重定位时,给聚合节具体指定的运行地址应该是多少,要看系统。对于32位Linux,程序的虚存地址是从0x08048000 开始的。
每个进程的虚拟内存地址都是0x08048000,进程被调用运行时都是从该地址开始的,并且不同的进程不会冲突,因为不同进程的虚存对应的物理内存不同。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/135050.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

信号处理系列之死区滤波器(DeadZone)

信号处理专栏相关限幅滤波的文章,可以参看下面的链接,这里不再赘述: 博途PLC信号处理系列之限幅消抖滤波_RXXW_Dor的博客-CSDN博客关于限幅滤波可以参看下面这篇文章PLC信号处理之限幅滤波(西门子三菱FB)_RXXW_Dor的博客-CSDN博客限幅滤波是一种消除随机干扰的有效方法,比…

WordPress元宇宙和VR(虚拟现实)最佳插件汇总

近年来,虚拟现实(VR )和元宇宙(Metaverse )变得越来越流行。它使用户能够在舒适的家中享受身临其境的体验。此外,将此功能添加到您的网站可以帮助您的内容更具交互性,这可能会带来更多转化。幸运…

RHCE——ansible安装配置(2)

安装并且配置ansible: 1)安装和配置ansible以及ansible控制节点server.example.com如下: 2)创建一个名为/home/student/ansible/inventory的静态库存文件如下所示: 2.1)node1 是dev主机组的成员 2.2)node2是test主机组的成员 2.3)node1和node…

c++动态内存管理

1.回顾c语言中的动态内存管理 在c语言中,我们想要动态开辟一段空间,需要使用malloc,calloc,realloc几个函数 void* malloc (size_t size); //在堆上申请size个字节的空间void* calloc (size_t num, size_t size); //第一个参数是…

压缩空气储能研究(Matlab代码)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

CMMI之技术预研

技术预研(Technical Pre-Research, TPR)是指在立项之后到开发工作完成之前的时间内,对项目将采用的关键技术提前学习和研究,以便尽可能早地发现并解决开发过程中将会遇到的技术障碍。 技术预研过程域是SPP模型的重要组成部分。本…

Android实现轮播控件Banner

背景 最近做需求要实现一个轮播图,最后通过HandlerViewPager实现了需求,所以把实现的过程总结一下,方便以后学习参考,以下是轮播图的效果: 实现思路 定时轮播 利用HandlerViewPager,Handler发送定时消息切…

初探Scala

目录 Scala介绍 Scala 环境搭建 IDEA新建Maven工程 创建执行输出Hello Scala Scala中main方法语法的详细解读 class 和 object 说明 Scala介绍 一般来说,学 Scala 的人,都会 Java,而 Scala 是基于 Java 的,因此我们需要将 S…

CSS基础总结(五)定位

文章目录 1.为什么需要定位 2.定位的组成 2.1公式 2.2定位模式 2.2.1静态定位static 2.2.2相对定位relative 2.2.3绝对定位absolute 2.2.4固定定位fixed 2.2.5粘性定位sticky 总结 2.3边偏移 3.定位叠放次序 4.定位拓展 4.1子绝父相布局法 4.2固定于版心右侧位置方…

read and write

read and write The read and write methods both perform a similar task, that is, copying data from and to application code. Therefore, their prototypes are pretty similar, and its worth introducing them at the same time: read 和 write 方法都执行类似的任务&…

Java开发 - 常用算法深度讲解,让你再也不会忘记

目录 前言 冒泡排序 原理 选择排序 原理 插入排序 原理 二分查找排序 原理 结语 前言 经常会有一些算法,我们说常用不常用,说不用也偶尔会用,当时看记住了,过几天提起来又忘记了,这是为什么呢?以…

Spring Security:PasswordEncoder密码加密匹配操作

目录 PasswordEncoder SpringBoot:注入BSryptPasswordEncoder实例 BSryptPasswordEncoder详解 父接口PasswordEncoder BSryptPasswordEncoder及其使用 成员方法 SecurityUtils安全服务工具类 测试代码 PasswordEncoder PasswordEncoder是Spring Security框架…

51单片机学习笔记_4 IO扩展:LED 点阵

IO 扩展(串转并)-74HC595 前面接的一些输入输出设备都是直接连接的单片机 IO 口,单片机仅有的 IO 口非常有限。而使用 IO 扩展可以大量增加可使用的端口。比如后面要使用的 LED 点阵,8*8个格子,使用扩展 IO 输入就更为合适。如果多级联一个&…

20230102单独编译原厂RK3588开发板的开发板rk3588-evb1-lp4-v10的Android12的内核2

20230102单独编译原厂RK3588开发板的开发板rk3588-evb1-lp4-v10的Android12的内核2 2023/1/2 21:01 《RK3588_Android12_SDK_Developer_Guide_CN.pdf》 原厂的开发板rk3588-evb1-lp4-v10单独编译内核的方式: cd kernel-5.10 export PATH../prebuilts/clang/host/lin…

educoder数据结构与算法 队列 第1关:实现一个顺序存储的队列

本文已收录于专栏 🌲《educoder数据结构与算法_大耳朵宋宋的博客-CSDN博客》🌲 目录 任务描述 相关知识 编程要求 测试说明 AC_Code 任务描述 本关任务:实现 step1/SeqQueue.cpp 中的SQ_IsEmpty、SQ_IsFull、SQ_Length、SQ_In和SQ_Out…

ceph集群搭建

一、环境准备 1.1、服务器准备 操作系统服务器IP服务器规格centos7.6192.168.161.114C/8Gcentos7.6192.168.161.124C/8Gcentos7.6192.168.161.134C/8G 1.2、服务器环境准备 1.2.1 更改主机名并添加映射 更改主机名 [rootlocalhost ~]# hostnamectl set-hostname ceph01 ##或…

内存池算法简单剖析

为什么要引入内存池算法? 我们知道C/C 语言中通过 malloc 调用 sbrk 和 mmap 这两个系统调用,向操作系统申请堆内存。但是,sbrk 和 mmap 这两个系统调用分配内存效率比较低,因为,执行系统调用是要进入内核态的,这样内…

区块链知识系列 - Oracle预言机

Oracle 预言机 区块链外信息写入区块链内的机制,一般被称为预言机(oracle mechanism)。 借助预言机外界的数据得以灌入链内, 使得DApp的玩法更多样. 比如DApp倚重的随机数, 可以考虑让一个硬件产生真随机数, 通过Oracle,定时灌入, 这将更公…

单机Docker部署应用Kraft模式的Kafka集群

单机Docker部署应用Kraft模式的Kafka集群1 Docker镜像准备1.1 下载Kafka1.2 配置容器1.3 修改kafka配置2 部署Kafka集群2.1 启动节点容器2.2 生成一个 Cluster ID2.3 格式化存储目录2.4 启动kafka服务3 知识3.1 控制器服务器3.2 进程角色3.3 仲裁投票者3.4 Kafka存储工具3.5 缺…

久泰新材料在港上市申请失效:年亏损超2亿元,崔轶钧为董事长

近日,贝多财经从港交所了解到,内蒙古久泰新材料科技股份有限公司(下称“久泰集团”或“久泰新材料”)在港交所的上市申请材料(招股书)已“失效”,目前已经无法正常查看或下载。 其中&#xff0c…