深入理解计算机系统_可重定位目标文件的格式---elf格式

news2024/9/25 15:24:59

本篇笔记记录可重定位目标文件的格式— elf格式,也是《深入理解计算机系统》第7章的内容。了解这些内容,对我们很有帮助,比如代码排错,可以深入了解C/C++ 实现原理。
分别介绍如何得到可重定位目标文件及其格式。

2.1 如何得到可重定位目标文件?

可重定位目标文件,就是一个.o文件,这个.o文件不能被执行,还需要通过静态链接之后,才能成为可执行目标文件去执行。
cpp、cc1预编译 cc1编译 as汇编
XXX.c ——————————>a.i——————————>a.s——————————> XXX.o

2.2 Linux下Elf可重定位目标文件组成结构

如下图所示:
在这里插入图片描述
Linux下,每个.o文件都是这样的结构;每个节都有一个编号,编号的作用就是这个节的索引,通过索引找到这个节。链接时要做的就是将多个elf格式的.o文件合并为一个文件。
注意:每个.o的逻辑地址(就是标识编号)都是从0开始的。
下面分别说说,ELF格式的每个节的作用。

2.2.1 ELF头

可以通过readelf -h 查看elf格式头信息。
在这里插入图片描述
Magic: 7f固定,后边的45 4C 46为ELF的ascii码
Class: ELF64 //表示该ELF格式面对的是64位系统,在32位系统里面就是ELF32;
Data: 2’s complement, little endian//数据按小端序存储
Version: 1 (current) //ELF头版本号
Type: REL (Relocatable file) //目标文件类型,这里标注的是“可重定位目标文件”
Machine: Advanced Micro Devices X86-64 //cpu类别
Version: 0x1 //目标文件版本号
Entry point address: 0x0 //起始地址(入口地址),.o逻辑地址是从0开始的
Size of this header: 64 (bytes) // 格式头大小64V=B

2.2.2 .text

2 和3 都是只读节,放所有函数的机器指令,不过某些常量也会直接和指令一起存在.text当中。比如:

int main(void)
{
	int a = 20;
	a = a + 100;   //表达式中的100会直接和指令放在一起
}

2.2.3 .rodata节

只读数据节,放只读数据(放某些常量数据),比如:

int a = 100;
printf("%d", a);
char *p = "hello world";

格式字符串"%d"和"hello world"这两个字符串常量,都放在了.rodata中。

2.2.4 .data

存放已经被初始化的非0的全局变量和初始化的静态局部变量。
初始化的全局变量:

int a = 100; 		//初始化了的全局变量,a就是在.data节中
int main(void)
{
	printf("%d\n", a);
}

int main(void)
{
	static int b = 101;//已经初始化了的静态局部变量
	printf("%d", a);
}

这个例子中,如果int b 没有加static,那么b就是自动局部变量,程序运行起来后,存放在栈中。编译时候不涉及局部变量,因为编译时候还没有栈,只有运行起来后,启动代码才会初始化栈。程序运行起来后,开辟栈帧,然后将101放入栈中。

2.2.5 .bss段

放未初始化的全局变量和未初始化的静态局部变量。

int a; //未初始化了的全局变量
int main(void)
{
	printf("%d", a);
}
int main(void)
{
	static int b;//未初始化的静态局部变量
	printf("%d", a);
}	

由于int a 和 static int b未初始化,所以其实不占空间。因此,在编译好的.o文件中,.bss只是一个占位符,只有当程序真正运行起来后,才会在内存上真正的开辟.bss的空间,并在.bss空间中开辟a和b的空间,并制自动初始化为0。所以在.o中,.bss只是一个理论上的存在。
.o为什么没有开辟.bss空间?没有实际要存放的数据,开辟空间只是浪费空间。因为.o文件存放在硬盘上,如果给.o 开辟没有必要的.bss空间,会浪费磁盘空间。

2.2.6 symtab符号表

每一个.o文件都有一个符号表symtab,用于存放.o中所定义和引用的全局符号信息。比如:有a.c b.c两个文件,代码如下:

int a = 100;
int fun(int a)
{
	return 1;
}
					
extern int b;  //定义在了b.c中
					
int main(void)
{
	b = 10000;
						
	fun2(1000); //fun2定义在了c.c中
}

编译后:
a.c -> a.o
b.c -> b.o
a.o中定义的符号信息:
a:a.o自己定义的全局变量符号
fun:a.o自己定义的函数符号
main:a.o自己定义的函数符号
a.o中引用的符号信息
b:a.o引用的在b.o中定义的全局变量符号
fun2:a.o引用的在c.o中定义的fun2函数符号

符号表的意义:

链接器在链接时候,将多个.o文件链接成一个.o文件,比如将a.o 和 b.o链接在一起时候,链接器需要查看各个.o文件的符号表,才能将各自符号的定义和引用关联起来。
下图是符号表的字段
在这里插入图片描述
上图是《深入理解计算机系统》书中对符号表的定义,下面分别解释一下符号表这几个字段。
(1) name: 记录字符串在表中的偏移
name中记录的并不是字符串的名字,而是记录了字符串在.strtab中的偏移。
假如,.strtab中的内容为main\0fun2\0a_va\0…
如果name = 5, 到strtab表中搜索,当遇到\0截止,取出来的就是fun2;

(2) value:存放符号所在空间的起始地址
存放某个符号所在的段,如果在.bss段,value就存放.bss段的起始地址;

(3) size:表示value所指向空间中,符号所占空间的大小
通过value找到该空间的起始地址,通过name 找到 偏移地址,再通过size, 就可以确定所占的字节数。

(4) type: 符号类型,有3种类型
FUNC:代表的是函数;
OBJECT:代表的是全局变量;
FILE:是源文件的名字

(5) bind
有两种情况,LOCAL 和 GLOBAL
bind = local 表示本地符号,在模块内定义,只能由本模块引用,static修饰的变量和函数就是这种情况。
bind=GLOBAL(全局符号),表示符号在本模块定义,但是可以被其它模块引用(使用),extern修饰的全局变量和函数就是这种情况。

(6) section
section的值有四种情况,节索引号、ABS、UNDEF、COM
情况1:section=节索引号, 说明符号所对应的空间在哪个节里面。比如,section == 1,表示符号所在的空间再.text节中,说明符号代表的是函数,因为只有函数指令才会保存在.text中;
section == 3,符号代表的空间在.data中,说明符号是初始化了的全局变量,因为只有初始化了的全局变量才会在.data节。
**情况2:**section=ABS,表示该符号不需要被“链接程序”处理。比如,如果符号名是.c,这个符号不是全局变量、不是函数,只是一个源文件名而已,链接器(ld/collect2)在链接“可重定位目标文件”时,这个符号不需要被处理。
情况3:section=UNDEF,表示这个符号,只是在本模块中被引用了,这个符号并不是由本模块定义的。在本某块找不到定义,所以这个符号的section就被标注为了UNDEF,表示这个符号被定义在了其它模块中,链接时要到其它模块中去找搜寻它的定义。经常在编译时候,报ld 开头的链接错误,原因就是因为找到符号的定义,有两个原因:要不然就是忘了链接所需的目标文件,要不然就是函数/变量名称写错了。
情况4 :section=COM,表示还未被分配空间,未被初始化,比如未初始化的全局变量。
通过readelf -s 查看符号表,如下:
在这里插入图片描述
在这里插入图片描述
比如,main
(a)符号名:main
(b)类型:函数
©本地符号/全局符号:全局被extern修饰的全局变量和函数,都会被标记为GLOBAL;
(d) 所在节:函数指令存在了编号为1的.text节
(e) 节中位置:偏移为0,表示main指令从.text第一个字节处开始存放
(f) 大小:从.text的第一个字节往后,占26字节

2.2.7 .rel.text

将多个.o链接到一起时,每个.o的.text会被整合为一个.text,整合.text时就必须依赖.rel.text所记录的一些有关.text中指令的位置信息.

2.2.8

将多个.o链接到一起时,每个.o的.data会被整合为一个.data,整合到一起时,就必须要依赖.rel.data所记录的一些有关.data的信息.

2.2.9 debug

符号调试表,记录调试信息,编译时必须加-g选项,编译时才会在.debug节中加入调试信息。

2.2.10 line

存放代码行号,因为调试的时候往往需要显示源码的行号。只有gcc编译时加了-g选项后,才会加入行号信息。

2.2.11 strtab

.symtab、.debug所用到符号名字、每个节的节名字(比如.text等)、源文件名字(***.c)等,都存在.strtab中.比如,.text\0.rodata\0…fun\0main\0,查找时,就通过name偏移就可以找到。

2.2.12 节头部表

描述目标文件中每个节的某些相关信息。

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

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

相关文章

操作系统~Linux~线程的互斥,mutex互斥锁的使用及其原理

1.一些基本概念 1.临界资源:凡是被线程共享访问的资源都是临界资源(多线程、多进程打印数据到显示器,显示器就是临界资源) 2.临界区:代码中访问临界资源的代码(在代码中,…

kotlin学习笔记之注解与反射

一、声明并应用注解 一个注解允许你把额外的元数据关联到一个声明上。然后元数据就可以被相关的源代码工具访问,通过编译好的类文件或是在运行时,取决于这个注解是如何配置的。 1、应用注解 在kotlin中使用注解的方法和java一样。要应用一个注解&#xf…

如何通过3个月自学成为网络安全工程师!

前言: 趁着今天下班,我花了几个小时整理了下,非常不易,希望大家可以点赞收藏支持一波,谢谢。 我的经历: 我 19 年毕业,大学专业是物联网工程,我相信很多人在象牙塔里都很迷茫&…

Pycharm配置关于pyside6的外部工具

文章目录一、前言二、Pycharm配置1、designer.exe(1)打开Pycharm的设置(2)相关参数(可复制粘贴)2、Pyside6-uic.exe(1)设置(2)相关参数(可复制粘贴…

Java--抽象类和接口的区别

今天是22年最后一天了, 写篇博客记录一下吧, 这一年发生了很多事情, 也学到了很多知识, 后面要继续加油啊, 大家也要加油啊, 米娜桑. 目录 概述 区别 1. 定义关键字不同 2. 继承或实现的关键字不同 3. 子类扩展的数量不同 4. 属性访问控制符不同 5. 方法控制符不同 6.…

python互联网程序设计GUI程序设计和网络程序设计(人机互动聊天软件)

1.项目意义 1、了解网络的结构; 2、了解网络传输协议; 3、掌握基本的网络编程方法。 2.总体设计 使用 TCP 协议实现人机聊天互动,程序具有服务端和客户端: (1)必备功能&#xff1…

Java财务在线咨询网站系统财务咨询网

简介 财务咨询网站,可以咨询公司代办,代理记账等一系列的财务问题的资讯服务网站 演示视频 https://www.bilibili.com/video/BV1T54y1H7Ar/?share_sourcecopy_web&vd_sourceed0f04fbb713154db5cc611225d92156 角色 管理员客服注册用户游客 技术…

Spring之DI入门案例

目录 一:DI入门案例实现思路分析 1.要想实现依赖注入,必须要基于 IOC 管理 Bean 2.Service 中使用 new 形式创建的 Dao 对象是否保留 ? 3.Service 中需要的 Dao 对象如何进入到 Service 中 ? 4.Service 与 Dao 间的关系如何描述 ? 二&#xff1…

(Qt) cmake编译Qt项目

文章目录前言环境cmake基础预备的项目代码文件资源路径demo.promain.cppres.qrcmywidget.cppmywidget.hmywidget.ui运行效果CMake文件资源路径CMakeLists.txt生成与构建END前言 通常我们在编写qt的时候都是在Qt creator中。而如何在VS Code中编写qt就是本文需要解决的问题 环…

顺序表 —— 初始化、销毁、打印、增加、删除、查找、修改

1.何为线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…线性表在逻辑上是线性结构,也就说是连续的一条直…

zotero导出pdf

今天老师给我改论文的时候布置了一个任务,让我把所有论文的pdf按格式打包发给她。可是之前我用zotero的时候都是在线保存的,有些是没有pdf的,怎么办?而且就算有pdf,他们的命名格式也五花八门,难道一个个手改…

kafka 消息日志原理 指定偏移量消费 指定时间戳消费

Kafka 日志详解 Apache Kafka日志存储在物理磁盘上各种数据的集合,日志按照topic分区进行文件组织,每一个分区日志由一个或者多个文件组成。生产者发送的消息被顺序追加到日志文件的末尾。 如上图所述,Kafka主题被划分为3个分区。在Kafka中&…

vscode使用跳板机(密钥)进入内网并连接内网中其它机器(密码)

经过简单测试 1、不能像xshell一样选择服务器的密钥登陆,只能通过将本机的公钥传到服务器上 2、不能使用本地socket5做代理登录 3、不能使用系统代理登录 一、使用密钥连接到跳板机 1、内网穿透 2、将本机公钥上传到服务器上 1)建立密钥对 无论是win…

redis集群 mac安装

1.安装redis mac环境用brew install安装 brew install redis 安装好后默认配置启动单点服务 redis-server 注:brew默认程序安装在/usr/local/Cellar目录下 /usr/local/Cellar/redis 默认配置文件在 /usr/local/etc/redis.conf 2.创建配置文件 准备创建6个节…

谣言检测数据集

1 PHEME-R 这是一个在PHEME FP7项目的新闻学用例中收集和注释的数据集。这些谣言与9个不同的突发新闻相关。它是为分析社交媒体谣言而创建的,包含由谣言推文发起的推特对话;对话包括对这些谣言推文的回应推文。这些推文已经被注解为支持度、确定性和证…

VS2012安装教程

我要学只有我们两个人懂得C语言。 安装包:https://pan.baidu.com/s/1YR7Xk9Zlv7zQWCsERdVgIQ [提取码]:stvi 1、右键以管理员身份运行 “vs_ultimate.exe” 2、编辑软件安装位置,然后点击同意许可,之后点下一步即可! 3…

mongoDB聚合查询

管道 管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的参数。MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。 聚合管道操作 可参考菜鸟文档:菜鸟文档 命令 功能描述 $project指定输出…

shell第四天作业——流程控制之循环

题目 一、for创建20个用户,用户前缀由用户输入,用户初始密码由用户输入。 二、for循环ping测试指定网段的主机,网段由用户输入。 三、使用for/while实现批量主机root密码的修改 一、for创建20个用户,用户前缀由用户输入&#x…

2022年已然要结束了,一起来分享下你的故事吧!2023年的接力棒已经递到手里,千言万语不如一句Fighting!

【系列专栏】:博主结合工作实践输出的,解决实际问题的专栏,朋友们看过来! 《QT开发实战》 《嵌入式通用开发实战》 《从0到1学习嵌入式Linux开发》 《Android开发实战》 《实用硬件方案设计》 长期持续带来更多案例与技术文章分享…

c++语法欠缺地方

sizeof是用来计算变量占多大内存的,单位是字节(byte);sizeof 后面跟类型时,必须加上括号,例如sizeof(double);后面跟变量可以不用加括号,例如:sizeof d%d是以十进制形式输出有符号整…