一、实验题目
• Task1:分析 Object 文件
• Task2:对比 Object 文件链接后生成的 out 文件与源文件的变化
• Task3:调用不同 C 文件中的子函数
二、实验目的
|
• |
掌握编译和反汇编方法。 |
• |
学会分析 Object 文件。 |
|
|
• |
学会对比 Object 文件链接后生成的 out 文件与源文件的变化。 |
三、实验设计
3.1. 实验步骤
• 按照TA在实验指导中所给的指令在Ubuntu环境下完成3个Task;
• 分析每个Task的实验结果;
• 回答问题,完成实验报告。
3.2. 实验原理
3.2.1 程序的汇编、链接、运行过程
以“ 求一组数的最大值的汇编程序”max.o 为例讨论目标文件和可执行文件的格式。解释一下这个 程序的汇编、链接、运行过程:
1). 写一个汇编程序保存成文本文件max.s。
2). 汇编器读取这个文本文件转换成目标文件max.o,目标文件由若干个Section组成,我们在汇编 程序中声明的.section会成为目标文件中的Section,此外汇编器还会自动添加一些Section(比如 符号表)。
3). 然后链接器把目标文件中的Section合并成几个Segment,生成可执行文件max。 4). 最后加载器(Loader)根据可执行文件中的Segment信息加载运行这个程序。
3.2.2 对象文件
3.2.2.1 对象文件(Object files)有三个种类:
a) 可重定位的对象文件(Relocatable file)
这是由汇编器汇编生成的 .o 文件。后面的链接器(link editor)拿一个或一些 Relocatable object files 作为输入,经链接处理后,生成一个可执行的对象文件 (Executable file) 或者 一个可被共享的对象文件(Shared object file)。
b) 可执行的对象文件(Executable file) 文本编辑器vi、调式用的工具gdb、播放mp3歌曲的软件mplayer等等都是Executable objectfile。在我们的 Linux 系统里面,存在两种可执行的东西。除了这里说的 Executable object file,另外一种就是可执行的脚本(如shell脚本)。注意这些脚本不是 Executable object file, 它们只是文本文件,但是执行这些脚本所用的解释器就是 Executable object file,比如 bash shell 程序。
c) 可被共享的对象文件(Shared object file)
这些就是所谓的动态库文件,也即 .so 文件。如果拿前面的静态库来生成可执行程序,那 每个生成的可执行程序中都会有一份库代码的拷贝。如果在磁盘中存储这些可执行程序,那就会占用额外的磁盘空间;另外如果拿它们放到Linux系统上一起运行,也会浪费掉宝贵的物理内 存。如果将静态库换成动态库,那么这些问题都不会出现。
要看一个文件是属于这三种类型ELF对象文件中的哪一种,可以用file命令查看。
左边是从链接器的视角来看ELF文件,开头的ELF Header描述了体系结构和操作系统等基本信息,并 指出Section Header Table和Program Header Table在文件中的什么位置,Program Header Table在链接过程中用不到,所以是可有可无的,Section Header Table中保存了所有Section的描述信息, 通过Section Header Table可以找到每个Section在文件中的位置。
3.2.3 查看ELF对象文件的内容: ELF文件头被固定地放在不同类对象文件的最前面。至于它里面的内容,除了file命令所显示出来 的那些之外,更重要的是包含另外一些数据,用于描述ELF文件中ELF文件头之外的内容。我们可 以使用其中的 readelf 工具来读出整个ELF文件头的内容。
3.2.4 分析ELF对象文件
用readelf工具读出目标文件max.o的ELF Header和Section Header Table,然后我们逐段 分析。
a) ELF Header
ELF Header中描述了操作系统是UNIX,体系结构是80386。Section Header Table中有8个Section Header,从文件地址200(0xc8)开始,每个Section Header占40字节,共320字节,到文件地址 0x207结束。这个目标文件没有Program Header。文件地址是这样定义的:文件开头第一个字节的 地址是0,然后每个字节占一个地址。
b) Section Header
从Section Header中读出各Section的描述信息,其中.text和.data是我们在汇编程序中声明的 Section,而其它Section是汇编器自动添加的。Addr是这些段加载到内存中的地址(我们讲过程序 中的地址都是虚拟地址),加载地址要在链接时填写,现在空缺,所以是全0。Off和Size列指出了 各Section的起始文件地址和长度。比如.text段从文件地址0x34开始,一共98个字节,.data段从 文件地址0xcc开始,大小为0个字节,此段为空,.bss也同样为空。根据以上信息可以描绘出整个 目标文件的布局。
c) 符号表
.symtab是符号表。Ndx列是每个符号所在的Section编号,例如符号data_items在第3个Section里(也 就是.data段),各Section的编号见Section Header Table。Value列是每个符号所代表的地址,在 目标文件中,符号地址都是相对于该符号所在Section的相对地址,比如data_items位于.data段的 开头,所以地址是0,_start位于.text段的开头,所以地址也是0,但是start_loop和loop_exit相 对于.text段的地址就不是0了。从Bind这一列可以看出_start这个符号是GLOBAL的,而其它符号是 LOCAL的,GLOBAL符号是在汇编程序中用.globl指示声明过的符号。
² 各段解释总汇:
基本: 代码段, 数据段和BSS段
代码段 .text (code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。 这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读.
数据段 .data 通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态 内存分配。
BSS段 .bss 未初始化的全局变量和局部静态变量,一般在初始化时bss 段部分将会清零(bss 段属于静态内存分配,即程序一开始就将其清零了)BSS段属于静态内存分配。初 始化为0的全局变量也会被放在.bss因为被优化掉了,放在.bss可以节省磁盘空间
(.bss不占磁盘空间)。
其余段:
段的参数:段长( Size ),段所在位置 (File Offset)可以在每段的CONTENTS, ALLOC中找到各段属性, CONTENT表示该段在文件中存在,有专门的命令”size”可以用来查看ELF文件的代码段,数据段和 BSS段的长度。
3.2.5 连接前