操作系统系列(五)——目标文件详解

往期地址:

  • 操作系统系列一 —— 操作系统概述
  • 操作系统系列二 —— 进程
  • 操作系统系列三 —— 编译与链接关系
  • 操作系统系列四 —— 栈与函数调用关系

本期主题:
目标文件详解


目标文件详解

  • 1.目标文件定义与分类
  • 2.目标文件是什么样的?
    • 1.直观认识目标文件
    • 2.objdump 分析工具使用
    • 3.各种段分析
  • 3.ELF文件结构描述
    • 1.elf文件头
    • 2.段表 section header table
    • 3.符号表


1.目标文件定义与分类

目标文件定义:

编译器编译源代码后生成的文件称为目标文件(object file)
目标文件从结构上来讲,还没有经过链接,所以可能有些符号还没有被调整

现在PC端的可执行文件格式主要包括了windows下的PE(Portable Execute)以及linux下的ELF(Execute Linkable Format),目标文件就是源码经过编译但是还没链接的那些中间文件,例如windows下的.obj文件和linux下.o文件,该类型文件内容与结构和可执行文件几乎一样,所以从广义上来说,可以将目标文件和可执行文件看成一种类型的文件,linux下统称为ELF文件格式
因此ELF文件格式又可以分为以下几类:

ELF文件类型 说明 实例
可重定位文件(relocate file) 编译生成的文件,可被链接生成可执行文件 linux下的.o以及windows下的.obj文件
可执行文件(executable file) 可以直接执行的程序,典型的ELF文件格式 linux下的/bin/bash以及windows下的exe文件
共享目标文件(shared object file) 动态链接文件,可以和其他的可重定位文件和共享目标文件一起链接,生成新的目标文件 linux下的.so和windows下的DLL文件
核心转存文件(core dump file) 进程信息存储文件,当进程意外终止时,系统将该进程的地址空间以及终止时的一些信息存到该文件 linux下的core dump文件

2.目标文件是什么样的?

1.直观认识目标文件

写一个简单的测试程序:main.c

#include 

int global_init_var = 100; //data段
int global_uninit_var; //bss段

int main(void)
{
        static int static_var = 200; //data段
        static int static_var2; //bss段

        int a = 1; //text
        int b; //text

        return 0;
}
jason@ubuntu:~/WorkSpace/3.OS_study/1.object_file$ gcc -c main.c 
jason@ubuntu:~/WorkSpace/3.OS_study/1.object_file$ file main.o 
main.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

可以看到main.o就是一个relocate file,目标文件会按照 段 section 的方式来存储信息。可以简单理解为下图这种情况
操作系统系列(五)——目标文件详解_第1张图片
其中,

ELF文件的开头是一个文件头,文件头描述了整个文件的文件属性,其中还有段表 section table,描述各个段的情况
.text段,编译后的执行语句都翻译成机器代码放在.text段
.data段,已经初始化的全局变量和局部静态变量
.bss段,未初始化的全局变量和局部静态变量

2.objdump 分析工具使用

常用的有objdump、readelf工具

objdump:
用法:objdump <选项> <文件>
显示来自目标 <文件> 的信息。
至少必须给出以下选项之一:
-a, --archive-headers Display archive header information
-f, --file-headers Display the contents of the overall file header
-p, --private-headers Display object format specific file header contents
-P, --private=OPT,OPT… Display object format specific contents
-h, --[section-]headers Display the contents of the section headers
-x, --all-headers Display the contents of all headers
-d, --disassemble Display assembler contents of executable sections,反汇编可执行段, .text段
-D, --disassemble-all Display assembler contents of all sections
–disassemble= Display assembler contents from
-S, --source Intermix source code with disassembly
–source-comment[=] Prefix lines of source code with
-s, --full-contents Display the full contents of all sections requested
-g, --debugging Display debug information in object file
-e, --debugging-tags Display debug information using ctags style
-G, --stabs Display (in raw form) any STABS info in the file
-W[lLiaprmfFsoRtUuTgAckK] or
–dwarf[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,
=frames-interp,=str,=loc,=Ranges,=pubtypes,
=gdb_index,=trace_info,=trace_abbrev,=trace_aranges,
=addr,=cu_index,=links,=follow-links]
Display DWARF info in the file
–ctf=SECTION Display CTF info from SECTION
-t, --syms Display the contents of the symbol table(s)
-T, --dynamic-syms Display the contents of the dynamic symbol table
-r, --reloc Display the relocation entries in the file
-R, --dynamic-reloc Display the dynamic relocation entries in the file
@ Read options from
-v, --version Display this program’s version number
-i, --info List object formats and architectures supported
-H, --help Display this information
-j, 可以执行反汇编的段

objdump -x 显示elf文件的所有header信息,包括elf header信息、section header信息还有符号表
objdump -h 显示elf文件的section header信息
objdump -s 将反汇编的信息用十六进制显示

操作系统系列(五)——目标文件详解_第2张图片
操作系统系列(五)——目标文件详解_第3张图片

通过上图dump出来的信息,主要看 “size” 和 "file off"段,可以大致将 main.o这个ELF文件结构理解为如下图所示:
操作系统系列(五)——目标文件详解_第4张图片

3.各种段分析

看前面的main.o 实际使用 objdump -s -d dump出来的信息
操作系统系列(五)——目标文件详解_第5张图片
1.text代码段
对代码指令的反汇编,内容里的十六进制与反汇编的指令能够对应上。
2.data数据段
.data段保存已经初始化了的全局变量和局部静态变量。
例如 global_init_var = 100对应的十六进制是0x64能够在data段的16进制内容中找到
3.bss段
.bss段保存 未初始化 的全局变量和局部静态变量
疑问:

前面dump的信息看到,.bss段只有4个byte,但实际上代码中有 global_unint_var和static_var2两个未初始化的变量才对,应该是占8个byte?

于是我们通过符号表来分析这个问题:
objdump -x得到下图结果:
操作系统系列(五)——目标文件详解_第6张图片
我们发现static_var2在.bss段,但是global_uninit_var却在COM段,这个涉及到链接的相关知识,不在这里细讲,可以初步给一个结论:

  • 编译单元内部可见的静态变量,未初始化会将其放在.bss段(如上面例子中的static_var2)
  • 未初始化的全局变量,由于涉及到链接机制,会被看成弱符号去进行处理,所以会放在COMMON类型下(这块内容在这篇文章操作系统系列六 —— 详细解释【静态链接】的COMM块部分中有讲解)

3.ELF文件结构描述

前面描述的elf文件结构比较粗,现在我们使用 readelf 工具,来详细了解elf文件结果

readelf 的用法:
用法:readelf <选项> elf-文件
显示关于 ELF 格式文件内容的信息
Options are:
-a --all Equivalent to: -h -l -S -s -r -d -V -A -I
-h --file-header Display the ELF file header
-S --section-headers Display the sections’ header
–sections An alias for --section-headers
-s --syms Display the symbol table
–symbols An alias for --syms

操作系统系列(五)——目标文件详解_第7张图片
ELF目标文件格式:

  • 最前部是 ELF文件头(elf header),描述了整个文件的基本属性,包括elf文件版本、目标机器型号、程序入口地址等;
  • 然后就是elf文件的各个段
  • 紧接着是 elf文件中与段信息相关的 段表(section header table),描述了elf文件的所有段的信息,包括段名、段长度、在文件中的偏移等
  • 然后就是符号表(symbol table),表明了elf文件的符号

1.elf文件头

使用readelf -h 来查看elf的文件头

操作系统系列(五)——目标文件详解_第8张图片
从上面的输出结果可以看出:
elf文件的大小端、版本、运行平台、以及程序入口地址,段的数量以及size

2.段表 section header table

前面我们使用了 "objudmp -h"来查看elf文件的段,但是这个不全的,objdump只是把关键的段给显示出来了,我们可使用readelf工具来查

readelf -S 查询段表信息

操作系统系列(五)——目标文件详解_第9张图片

3.符号表

讲到符号表,就需要讲到链接。
链接的本质就是把多个不同的目标文件相互粘在一起,形成一个整体。例如有这样一个场景,目标文件B需要使用到目标文件A中的某个函数,对于目标文件A而言,这是定义这个函数,而对于目标文件B而言,这是引用。在连接过程中,函数和变量称为符号(symbol)
因此,在每个目标文件中都有符号表,这是为了链接而准备的。

使用readelf -s 查看符号信息
操作系统系列(五)——目标文件详解_第10张图片

你可能感兴趣的:(计算机操作系统,linux,bash,运维)