目标文件格式

news2024/11/23 7:27:19

目标文件里有什么

目标文件格式

目标文件就是源代码编译后但未进行链接的中间文件(linux下的.o)。

ELF文件:从广义上看,目标文件与可执行文件的格式其实几乎是一样的,可以将目标文件与可执行文件看成是一种类型的文件(ELF文件)。

表1 ELF文件类型

ELF文件类型

说明

实例

可重定位文件

这类文件包含了代码和数据,可以被用来链接成可执行文件或共享目标文件,静态库也可以归类为这一类

Linux的.o

可执行文件

这类文件包含了可以直接执行的程序,它的代表就是ELF可执行文件,它们一般都没有扩展名

比如/bin/bash文件

共享目标文件

这种文件包含了代码和数据,可以在以下两种情况下使用。一种是链接器可以使用这种文件跟其他的可重定位文件和共享目标文件链接,产生新的目标文件。第二种是动态连接器可以将及格这种共享目标文件与可执行文件结合,作为进程映像的一部分来运行

Linux的.so,如/lib/glibc-2.5.so

核心转储文件

当进程意外终止时,系统可以将该进程的地址空间的内容及终止时的一些其他信息转储到核心转储文件

Linux下的core dump

目标文件是什么样的

目标文件中的内容至少有编译后的机器指令代码,数据,符号表,调试信息,字符串等。一般目标文件将这些信息按不同的属性,以“节”的形式存储,在一般情况下,它们都表示一个一定长度的区域。

程序源代码编译后的机器指令经常被放在代码段,代码段常见的名字有“.code”或“.text”;全局变量和局部静态变量数据经常放在数据段,数据段的一般名字都叫“.data”。让我们来看一个简单的程序被编译成目标文件后的结构,如下图所示。

图1

从上图可以看到,ELF文件的开头是一个“文件头”,它描述了整个文件属性,包括是否可执行,是静态链接还是动态链接以及入口地址(如果是可执行文件),目标硬件,目标操作系统等信息,文件头还包括一个段表,段表其实是一个描述文件中各个段的数组。

从上图可以看到,一般C语言的编译后可执行语句都编译成机器代码,保存在.text段;已初始化的全局变量和局部静态变量都保存在.data段;未初始化的全局变量和局部静态变量一般放在一个叫做“.bss”的段里。

总体来说,程序源代码被编译以后主要分成两种段:程序指令和程序数据。代码段属于程序指令,而数据段和.bss段属于程序数据。

为什么要把程序的指令和数据的存放分开?混杂地方在一个段里面不是更加简单?其实数据和指令分段的好处有很多。主要有如下几个方面

  1. 一方面是当程序被装载后,数据和指令分别被映射到两个虚存区域。由于数据区域对于进程来说是可读写的,而指令区域对于进程来说是只读的,所以这两个虚存区域的权限可以被分别设置成可读写和只读。这样就可以防止程序的指令被有意或者无意的改写。
  2. 另外一方面是对于现代的CPU来说,它们有着极为强大的缓存体系。由于缓存在现代的计算机中地位非常重要,所以程序必须尽量提高缓存的命中率。指令区和数据区的分离有利于提高程序的局部性。现代CPU的缓存一般都被设计成数据缓存和指令缓存分离,所以程序的指令和数据被分开存放对CPU的缓存命中率提高有好处。
  3. 第三个原因,其实也是最重要的原因,就是当系统中运行着多个程序的副本时,他们的指令都是一样的,所以内存中只需要保存一份该程序的指令部分。对于指令这种只读的区域来说是这样,对于其他的只读数据也一样,比如很多程序里面带有的图标,图片等资源属于可以共享。当然每个副本进程的数据区域是不一样的,它们是进程私有的。

挖掘a.o

首先来看下a.c的代码清单:

图2

我们使用GCC来编译这个文件(参数-c表示只编译不链接):

gcc -c a.c

我们得到一个目标文件a.o,使用objdump来查看a.o内部的结构,该工具可以用来查看目标文件的结构和内容。运行以下命令:

objdump -h a.o

图3

从上面的结果来看,a.o的段包括了代码段(.text),数据段(.data),BSS段(.bss),只读数据段(.rodata),注释信息段(.comment)和堆栈提示段(.note.GNU-stack)等,再来看看段的属性,其中最容易理解的是段的长度(Size)和段所在的位置(File Offset),每个段的第2行的“CONTENTS”,“ALLOC”等表示段的各种属性,“CONTENTS”表示段在文件中存在。我们可以看到BSS段没有“CONTENTS”,表示它实际上再ELF文件中不存在内容,我们用下图来表示“.text”,“.data”,“.rodata”和“.comment”段在ELF文件的长度和文件中的偏移位置。

图4

代码段

挖掘各个段的内容,我们还是用objdump这个工具。Objdump的“-s”参数可以将所有段的内容以十六进制的方式打印出来,“-d”参数可以将所有包含指令的段反汇编,下图是使用objdump输出a.o中有关代码段内容的提取:

$ objdump -s -d a.o

a.o:     文件格式 elf64-x86-64

Contents of section .text(代码段):

 0000 554889e5 4883ec10 897dfc8b 45fc89c6  UH..H....}..E...

 0010 bf000000 00b80000 0000e800 000000c9  ................

 0020 c3554889 e54883ec 10c745f8 01000000  .UH..H....E.....

 0030 8b150000 00008b05 00000000 01c28b45  ...............E

 0040 f801c28b 45fc01d0 89c7e800 000000b8  ....E...........

 0050 00000000 c9c3                        ......          

Contents of section .data(数据段):

 0000 54000000 55000000                    T...U...        

Contents of section .rodata(只读数据段):

 0000 25640a00                             %d..            

Contents of section .comment:

 0000 00474343 3a202855 62756e74 7520342e  .GCC: (Ubuntu 4.

 0010 382e342d 32756275 6e747531 7e31342e  8.4-2ubuntu1~14.

 0020 30342e33 2920342e 382e3400           04.3) 4.8.4.    

Contents of section .eh_frame:

 0000 14000000 00000000 017a5200 01781001  .........zR..x..

 0010 1b0c0708 90010000 1c000000 1c000000  ................

 0020 00000000 21000000 00410e10 8602430d  ....!....A....C.

 0030 065c0c07 08000000 1c000000 3c000000  .\..........<...

 0040 00000000 35000000 00410e10 8602430d  ....5....A....C.

 0050 06700c07 08000000                    .p......        

Disassembly of section .text:

0000000000000000 <func1>:

   0: 55                    push   %rbp

   1: 48 89 e5              mov    %rsp,%rbp

   4: 48 83 ec 10           sub    $0x10,%rsp

   8: 89 7d fc              mov    %edi,-0x4(%rbp)

   b: 8b 45 fc              mov    -0x4(%rbp),%eax

   e: 89 c6                 mov    %eax,%esi

  10: bf 00 00 00 00        mov    $0x0,%edi

  15: b8 00 00 00 00        mov    $0x0,%eax

  1a: e8 00 00 00 00        callq  1f <func1+0x1f>

  1f: c9                    leaveq

  20: c3                    retq   

0000000000000021 <main>:

  21: 55                    push   %rbp对应Contents of section .text的第一个字节

  22: 48 89 e5              mov    %rsp,%rbp

  25: 48 83 ec 10           sub    $0x10,%rsp

  29: c7 45 f8 01 00 00 00 movl   $0x1,-0x8(%rbp)

  30: 8b 15 00 00 00 00     mov    0x0(%rip),%edx        # 36 <main+0x15>

  36: 8b 05 00 00 00 00     mov    0x0(%rip),%eax        # 3c <main+0x1b>

  3c: 01 c2                 add    %eax,%edx

  3e: 8b 45 f8              mov    -0x8(%rbp),%eax

  41: 01 c2                 add    %eax,%edx

  43: 8b 45 fc              mov    -0x4(%rbp),%eax

  46: 01 d0                 add    %edx,%eax

  48: 89 c7                 mov    %eax,%edi

  4a: e8 00 00 00 00        callq  4f <main+0x2e>

  4f: b8 00 00 00 00        mov    $0x0,%eax

  54: c9                    leaveq

  55: c3                    retq  对应Contents of section .text的最后一个字节

“Contents of section .text”就是.text段的数据以十六进制方式打印出来的内容,总共0x56字节(黄色部分),跟我们上图的“.text”段长度相符合,最左边一列是偏移量,中间4列是十六进制内容,最右面一列是.text的ASCII码形式。对照下面的反汇编结果,可以很明显的看到,.text段里所包含的正是a.c里两个函数func1()和main()的指令。.text段的第一个字节”0x55”就是函数“func1()”函数的第一条“push %rbp”指令,而最后一个字节0xc3正是main()函数的最后一条指令”ret”。

数据段和只读数据段

.data段保存的是那些已经初始化了的全局静态变量和局部静态变量。从上文源代码a.c里面一共有两个这样的变量,分别是global_int_varabal和static_var。这两个变量每个4个字节,一共刚好8个字节,所以“.data”这个段的大小为8个字节(上文紫色部分),0x54是84的十六进制,0x55是85的十六进制。

  1. c里面我们在调用“printf”的时候,用到了一个字符串常量“%d\n”,它是一种只读数据,所以它被放到了“.rodata”段,我们可以从输出结果看到“.rodata”这个段的4个字节刚好是这个字符串常量的ASCII字节序,最后以\0结尾(蓝色部分)。

BSS段

.bss段存放的是未初始化的全局变量和局部静态变量。

ELF文件结构描述

上文通过a.o的结构大致了解了ELF文件的轮廓 ,接着就来看看ELF文件的结构格式。下图描述了ELF目标文件的总体结构。

图5 ELF结构

ELF Header

.text

.data

.bss

Other sections

Section Header table

String Tables

Symbol Tables

从图5可以看出ELF目标文件格式的最前部是ELF文件头(ELF Header),它包含了描述整个文件的基本属性,比如ELF文件版本,目标机器型号,程序入口地址等,紧接着是ELF文件各个段。其中ELF文件中与段有关的重要结构就是段表(Section Header Table),该表描述了ELF文件包含的所有段的信息,比如每个段的段名,段的长度,在文件中的偏移,读写权限以及段的其他属性。

文件头

我们可以用readelf命令来详细查看ELF文件,如下图6所示

图6 ELF文件头

从上面输出的结果可以看到,ELF的文件头定义了ELF魔数,文件机器字节长度,数据存储方式,版本,运行平台,ABI版本,ELF重定位类型,硬件平台,硬件平台版本,入口地址,程序头入口和长度,段表的位置和长度以及段的数量等。

ELF文件头结构以及相关常数被定义在”/usr/include/elf.h”中,因为ELF文件在各种版本平台下都通用,ELF文件有32位版本和64位版本,分别叫做”Elf32_Endr”和”Elf64_Ehdr”,如图7所示。

图7 ELF文件头结构体成员

段表
我们知道ELF文件中有很多各种各样的段,通过段表来保存这些段的基本属性。段表是ELF文件中除了文件头以外最重要的结构,它描述了EFL的各个段的信息,比如每个段的段名,段的长度(棕色圈矩形框),在文件中的偏移(红色圈矩形框),读写权限以及段的其他属性。也就是说,ELF文件的段结构就是有段表决定的,编译器,连接器和装载器都是依靠段表来定位和访问各个段的属性的。段表在ELF文件中的位置有EFL文件头的”e_shoff”成员决定,比如在a.o中,段表位于偏移0x190。

图8 表段信息

Readelf输出的结构就是ELF文件段表的内容,那么就让我们对着这个输出来看看段表的结构。段表的结构比较简单,它是一个以”Elf64_Shdr”(在/usr/include/elf.h,占64个字节)结构为元素的数组。数组元素的个数等于段的个数,每个”Elf64_Shdr”结构体对应一个段。”Elf64_Shdr”又称为段描述符(Section Descriptor)。对于a.o来说,段表就是有13个元素的数组。ELF段表的这个数组的第一个元素是无效的段描述符,它的类型是”NULL”,除此之外每个段描述符都对应一个段。在a.o共有12个有效的段。

到了这一步,我们才把a.o的所有段的位置和长度给分析清楚了。在下图9中,SectionTable长度为0x340,也就是832个字节,它包含了13个段描述符,每个段描述符为64个字节,这个长度刚好等于sizeof(Elf64_Shdr),符合段描述符的结构体长度,整个文件最后一个段”.real_ch_frame”结束后,长度为0x58,即1880字节,刚好等于a.o的文件长度,如图10所示。

图9 a.0的section table以及所有段的位置和长度

图10 a.o的总长度

重定位表

我们注意到,a.o中有一个叫做“.rel.text”的段,也就是说它是一个重定位表。正如我们最开始所说的,连接器在处理目标文件时,需要对目标文件某些部分进行重定位,即代码段和数据段中哪些对绝对地址的引用的位置。这些重定位的信息都记录在ELF文件的重定位表里面,对于每个需要重定位的代码段和数据段,都会有一个相应的重定位表。比如在a.o中,“.rel.text”就是针对“.text”段的重定位表,因为“.text”段中至少有一个绝对地址的引用,那就是对“printf”的调用;而“.data”段则没有绝对地址的引用,它只包含了几个常量,所以a.o中没有针对“.data”段的重定位表“.rel.data”。

链接的接口---符号

链接过程的本质就是要把多个不同的目标文件之间相互“粘”到一起。为了使不同目标文件之间能够相互粘合,这些目标文件之间必须有固定的规则才行。在链接中,目标文件之间相互拼合实际上是目标文件之间对地址的引用,即对函数和变量的地址的引用。比如目标文件B要用到目标文件A的函数“foo”,那么我们就称目标文件A定义了函数“foo”,称目标文件B引用了目标文件A的函数“foo”。这两个概念同意也适用于变量。每个函数或变量都有自己独特的名字,才能避免链接过程中不同变量和函数之间混淆。在链接中,我们将函数和变量统称为符号,函数名或变量名就是符号名。

我们可以将符号看作是链接中的粘合剂,整个链接过程正是基于符号才能正确完成。链接过程中很关键的一部分就是符号的管理,每一个目标文件都会有一个相应的符号表,这个表里面记录了目标文件中所用到的所有符号。每个定义的符号有一个对应的值,叫做符号值,对于变量和函数来说,符号值就是它们的地址。除了函数和变量之外,还存在其他几种不常用的符号:

  1. 定义在目标文件的全局符号,可以被其他目标文件引用。
  2. 在本目标文件中引用的全局符号,却没有定义在本目标文件,这一般叫做外部符号。
  3. 段名,这种符号往往由编译器产生,它的值就是该段的起始地址。
  4. 局部符号,这类符号往往只在编译单元内部可见。
  5. 行号信息,即目标文件指令与源代码中代码行的对应关系。

ELF符号表结构

ELF文件中的符号表往往是文件中的一个段,段名一般叫“.symtab”。符号表的结构很简单,它是一个Elf32_Sym(32位系统)/Elf64_Sym(64位系统)的数组,每个Elf64_Sym结构对应一个符号。

图11 符号表结构信息

st_name:符号名。这个成员包含了该符号名在字符串表的下标

st_value:符号相对应的值。这个值跟符号有关,可能是一个绝对值,也可能是一个地址等,不同的符号,它所对应的值的含义不同

st_size:符号大小。对于包含数据的符号,这个值是该数据类型的大小

st_info:符号类型和绑定信息

st_other:该成员目前位0

st_shndx:符号所在的段

特殊符号

当我们使用ld作为链接器来链接生产可执行文件时,它会为我们定义很多特殊的符号,这些符号并没有在你的程序中定义,但是你可以直接声明并且引用它,我们称之为特殊符号。其实这些符号是被定义在ld链接器的链接脚本中的。目前你只需认为这些符号是特殊的,你无须定义它们,但可以声明它们并且使用。链接器会在将程序最终链接成可执行文件的时候将其解析成正确的值,注意,只有使用ld链接生产最终可执行文件的时候这些符号才会存在。几个很有代表性的特色符号如下:

  1. __executable_start:该符号为程序起始地址,注意,不是入口地址,是程序的最开始的地址。
  2. __exext或_exext或etext:该符号为代码段结束地址,即代码段最末尾的地址
  3. _edata或edata:该符号为数据段结束地址,即数据段最末尾的地址
  4. _end或end:该符号为程序结束地址
  5. 以上地址都为程序被装载时的虚拟地址

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

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

相关文章

【Vue面试题二十】、你有写过自定义指令吗?自定义指令的应用场景有哪些?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;你有写过自定义指令吗&a…

相似性搜索:第 1 部分- kNN 和倒置文件索引

图片来源&#xff1a;维亚切斯拉夫叶菲莫夫 一、说明 SImilarity 搜索是一个问题&#xff0c;给定一个查询的目标是在所有数据库文档中找到与其最相似的文档。 在数据科学中&#xff0c;相似性搜索经常出现在NLP领域&#xff0c;搜索引擎或推荐系统中&#xff0c;其中需要检索最…

课题学习(七)----粘滑运动的动态算法

一、 粘滑运动的动态算法 在实际钻井过程中&#xff0c;钻柱会出现扭振和粘滑现象&#xff08;粘滑运动–B站视频连接&#xff09;&#xff0c;但并不总是呈现均匀旋转。如下图所示&#xff0c;提取一段地下数据时&#xff0c;转盘转速保持在100 r/min&#xff0c;钻头转速在0-…

Namomo Summer Camp 23 Day 1

Namomo Summer Camp 23 Day 1 - Virtual Judge B - Brexiting and Brentering AC代码: #include<bits/stdc.h> #define endl \n //#define int long long using namespace std; string s; void solve() {cin>>s;int x-1;for(int is.size()-1;i>0;i--){if(s[i…

YOLOv5训练自己的数据集(超详细)

YOLOv5训练自己的数据集整个过程主要包括&#xff1a;环境安装----制作数据集----模型训练----模型测试----模型推理 一、准备深度学习环境 本人的笔记本电脑系统是&#xff1a;Windows10 首先进入YOLOv5开源网址 &#xff0c;手动下载zip或是git clone 远程仓库&#xff0c;本…

【C++心愿便利店】No.8---C++之重识类和对象

文章目录 前言一、再谈构造函数二、static成员三、友元四、内部类五、匿名对象六、再次理解类和对象 前言 &#x1f467;个人主页&#xff1a;小沈YO. &#x1f61a;小编介绍&#xff1a;欢迎来到我的乱七八糟小星球&#x1f31d; &#x1f4cb;专栏&#xff1a;C 心愿便利店 &…

6、docker下mysql修改配置文件

1、查看mysql镜像 如果没有mysql镜像则下载 docker images |grep mysql 2、查看mysql容器 docker ps |grep mysql 如果没有显示mysql容器信息&#xff0c;则创建 3、创建容器 docker run -it --name mysql-test -e MYSQL_ROOT_PASSWORDroot -p 3306:3306 -d f9653 4、在…

AB实验--科学增长

涉及的内容&#xff1a; AB实验的前置知识 AB实验的架构 AB实验的创建 AB实验的分析 AB实验的展示 AB实验的监控 AB扩展---指标监控 AB扩展---指标异动 AB扩展---异动分析 AB实验参考书籍 1.什么是AB AB 测试&#xff08;也称为拆分测试&#xff09;是一种统计方法&a…

SiC外延片测试方案

外延材料是实现器件制造的关键&#xff0c;主要技术指标有外延层厚度、晶格布局&#xff0c;材料结构&#xff0c;形貌以及物理性质&#xff0c;表面粗糙度和掺杂浓度等。下面阐述SiC外延表面常见的测试手段&#xff1a; 1. 外延层厚度&#xff08;傅里叶变换红外FT-IR&#xf…

xray安装与bp组合使用-被动扫描

xray安装与bp组合使用-被动扫描 文章目录 xray安装与bp组合使用-被动扫描1 工具官方文档&#xff1a;2 xray官网3 工具使用4 使用指令说明5 此为设置被动扫描6 被动扫描-启动成功7 启动bp7.1 设置bp的上层代理7.2 添加上层代理7777 --》指向的是xray7.3 上层代理设置好后&#…

实施运维03

一.制作启动盘&#xff08;老毛桃&#xff0c;大白菜&#xff0c;傲梅&#xff09; 1.网上下载启动盘工具 https://msdn.itellyou.cn/ 二.重装系统 1.1.插上启动盘 2.电脑关机 3.电脑开机&#xff0c;开机的时候按住F12键 4.选择启动盘进去&#xff0c;选择一个要重装的系统…

关于mybatis中collection出现的问题(ofType 和 javaType )

关于mybatis中collection出现的问题 我在代码中的collection标签中使用了javaType导致映射是失败的&#xff0c;我使用了ofType就可以了&#xff0c;下面介绍这两个标签之间的区别。 ofType 和 javaType 属性都用于指定集合或关联对象的类型&#xff0c;但它们的使用方式和含义…

蓝牙资讯|2024年智能家居新趋势,蓝牙助力智能家居发展

2024年将迎来变革&#xff0c;智能家居趋势不仅会影响我们的生活空间&#xff0c;还会提高我们的生活质量&#xff0c;让我们有更多时间享受属于自己的时光。 2024年智能家居新趋势 趋势一&#xff1a;多功能科技 2024年预示着多功能技术的趋势&#xff0c;创新将成为焦点。混…

主机jvisualvm连接到tomcat服务器查看jvm状态

​使用JMX方式连接到tomcat&#xff0c;连接后能够查看前边的部分内容&#xff0c;但是不能查看Visual GC&#xff0c;显示不受此JVM支持&#xff0c; 对了&#xff0c;要显示Visual GC&#xff0c;首先要安装visualvm工具&#xff0c;具体安装方式就是根据自己的jdk版本下载…

【PWN · 栈迁移】[CISCN 2019东南]PWN2

一道非常典型、适合用作学习栈迁移的题目。 前言 当存在栈溢出但是溢出字符数并不多的情况下&#xff0c;可以尝试在别处构造rop链&#xff0c;通过栈迁移到目标内存区域&#xff0c;执行rop链。这里不讲栈迁移原理&#xff0c;仅是对题目的分析&#xff0c;适合对栈迁移有初步…

Java 操作 Excel:生成数据、设置单元格样式、设置数据有效性(hutool)

必读信息 该篇文章&#xff0c;主要通过 Java 代码对 Excel 文件的常用操作&#xff0c;包括&#xff1a;生成表格、修改单元格样式、设置数据有效性。 该篇文章&#xff0c;在官网文献下增加个人的看法和理解&#xff0c;如文中有出现不符、错误或需要补充的地方&#xff0c…

CVE-2017-12615 Tomcat远程命令执行漏洞

漏洞简介 2017年9月19日&#xff0c;Apache Tomcat官方确认并修复了两个高危漏洞&#xff0c;漏洞CVE编号&#xff1a;CVE-2017-12615和CVE-2017-12616&#xff0c;其中 远程代码执行漏洞&#xff08;CVE-2017-12615&#xff09; 当 Tomcat 运行在 Windows 主机上&#xff0c;…

AutoDL平台transformers环境搭建

AutoDL平台transformers环境搭建 租借GPU可以参考 AutoDL平台租借GPU详解 一、激活base环境 1.进入终端 vim ~/.bashrc2、然后按英文模式的 i 进入编辑&#xff0c;按键盘下键到最后输入 source root/miniconda3/etc/profile.d/conda.sh3、然后先按键盘Esc键&#xff0c…

Linux系统编程01

C语言程序编译过程 多个源文件生成一个可执行文件的过程 预处理阶段主要是将带 # 号的类似于 #include #define #ifdef等进行处理替换 gcc -S 下面讲解C语言源代码编译成汇编语言之后&#xff0c;之间的对应情况 源代码 使用gcc -S test15.c -o test15.s指令让源代码进行编译…

Adaptive AUTOSAR CM模块介绍(五)

Proxy Class API’s proxy class类提供类&#xff08;静态&#xff09;方法来FindService() 实例&#xff0c;服务是动态的&#xff0c;因此&#xff0c;AUTOSAR提供了两个方法来提供发现服务。 StartFindService():是一个类方法&#xff0c;它在后台启动一个周期性的“FindS…