Linux下可执行文件格式详解

Linux下面,目标文件、共享对象文件、可执行文件都是使用ELF文件格式来存储的。程序经过编译之后会输出目标文件,然后经过链接可以产生可执行文件或者共享对象文件。Linux下面使用的ELF文件和Windows操作系统使用的PE文件都是从Unix系统的COFF文件格式演化来的。

我们先来了解一些基本的想法。

首先,最重要的思路是一个程序从人能读懂的格式转换为供操作系统执行的二进制格式之后,代码和数据是分开存放的,之所以这样设计有这么几个原因:

1、程序执行之后,代码和数据可以被映射到不同属性的虚拟内存中。因为代码一般是只读的,而数据是可读可写的;

2、现代CPU有强大的缓存体系。程序和代码分离可以提高程序的局部性,增加缓存命中的概率;

3、还有最重要的一个原因是当有多个程序副本在运行的时候,只读部分可以只在内存中保留一份,这样大大节省了内存。

在ELF的定义中,把他们分开存放的地方称为一个 Section ,就是一个段。

一个ELF文件中重要的段包括:

.text 段:存储 只读程序

.data 段:存储 已经初始化的全局变量和静态变量

.bss 段:存储 未初始化的全局变量和静态变量,因为这些变量的值为0,所以这个段在文件当中不占据空间

.rodata 段:存储 只读数据,比如字符串常量

我们用一个例子来看一下ELF文件的格式到底是什么。首先,在Linux下编写一个C程序:SimpleSection.c

int printf(const char *format, ... );

int global_init_var = 16;
int global_unint_var;

void func1 (int );

int main()
{
    static int static_var = -32;
    static int static_var_uninit;

    int a = 1;
    int b;

    func1(static_var + global_init_var + a + b);

    return a;
}

void func1 (int i)
{
    printf("%d\n", i);
}

然后,产生目标文件:

[[email protected] Program]# gcc -c SimpleSection.c
[[email protected] Program]# file SimpleSection.o
SimpleSection.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped

file命令的结果也告诉我们,这是一个32位ELF的文件,类型是 relocatable ,就是可重定位。所以目标文件又叫做可重定位文件。

elf文件的最开始是elf文件头信息,32位有52个字节组成。我们可以使用 readelf 工具来查看一下:

[[email protected] Program]# readelf -h SimpleSection.o
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          224 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           40 (bytes)
  Number of section headers:         11
  Section header string table index: 8

Entry point address 指的是程序入口地址,如果是可执行文件,这个字段会有值;

他之前的字段是一些说明字段;

Start of program headers 指的是 程序头表 的起始位置。程序头表 是从装载视图的角度对elf的各个段进行的分类信息;结构和段表相似;

Start of section headers 指出了elf除文件头以外的最重要的信息:段表 的起始位置。段表包含了各个段的名称、属性、大小、位置等重要信息。操作系统首先找到段表,然后根据段表的信息去找到各个段。段表是一个类似数组的结构,一个段的信息是这个数组的一个元素。

Size of this header 指的是头文件大小,32位都是 52 个字节,0x34个字节。

Size of program headers 指的是每个 程序头表 的大小。

Number of program headers 指的是 程序头表 的数目。

Size of sections headers 指的是每个 段表 的大小;

Number of section headers 指的是 段表的数量;

Section header string table index 指出了段表当中用到的字符串表在段表中的下标。

文件头之后,紧跟着的是 程序头,因为目标文件没有链接,所以没有装载信息。我们这里可以先不理会这个东西,以后专门再说他。

程序头之后就是各个段的数据,我们用工具查看一下:

[[email protected] Program]# readelf -S SimpleSection.o
There are 11 section headers, starting at offset 0xe0:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00000000 000034 000020 00  AX  0   0  4
  [ 2] .rel.text         REL             00000000 0003f4 000010 08      9   1  4
  [ 3] .data             PROGBITS        00000000 000054 000008 00  WA  0   0  4
  [ 4] .bss              NOBITS          00000000 00005c 000004 00  WA  0   0  4
  [ 5] .rodata           PROGBITS        00000000 00005c 000004 00   A  0   0  1
  [ 6] .comment          PROGBITS        00000000 000060 00002d 01  MS  0   0  1
  [ 7] .note.GNU-stack   PROGBITS        00000000 00008d 000000 00      0   0  1
  [ 8] .shstrtab         STRTAB          00000000 00008d 000051 00      0   0  1
  [ 9] .symtab           SYMTAB          00000000 000298 0000f0 10     10  10  4
  [10] .strtab           STRTAB          00000000 000388 00006b 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

各个字段意思依次是:段序号、段名称、段类型、段虚拟地址、偏移量、大小、ES、标志、Lk、Inf、对齐。

没有解释的列可以先不考虑,我们先关注其他几个列。

第0个段是为了读取的时候下标不用减1。

紧跟着的就是代码段,偏移量为0x34,就是说在文件头结尾之后马上就是代码段;

代码段之后,偏移量 0x54 的地方就是 数据段,占8个字节,就是程序中已经被赋值的一个全局变量和一个静态变量;

紧接着是.bss段,这里只存储了一个static变量,因为 未初始化的那个全局变量被一种优化机制存储到了 .common 段,这里可以不做理会;

然后是只读数据段.rodata,这里存储的是 printf 里面的 %d\n 这三个字符,外加结束符\0,总共4个字节的空间

我们根据Size这一列来算一下这些段总共占据的空间,(.bss由于不占空间,不用算进来):

.text 0x20

.data 0x8

.rodata 0x4

.comment 0x2d

.shstrtab 0x51

.rel.text 0x10

.symtab 0xf0

.strtab 0x6b

这里的每一个段都有一个段表元素来描述,总共11个。从头文件得知,每个元素的大小为40字节。也就是说段表总共占了 0x1b8 个字节的空间。而且段表的开始地址由于内存对齐需要,中间空了2个字节。因为段表的开始地址是第224个字节;

.rel.text 的开始抵制也由于内存对齐的要求,补了一个空字节。

在加上头文件的 0x34 个字节,总共加起来是   1028 字节。

[[email protected] Program]# ls -al SimpleSection.o
-rw-r--r-- 1 root root 1028 Aug 21 16:09 SimpleSection.o

这个目标文件的大小恰好是1028个字节。

Linux下可执行文件格式详解

时间: 2024-10-17 22:55:07

Linux下可执行文件格式详解的相关文章

(转)Linux下PS命令详解

(转)Linux下PS命令详解 整理自:http://blog.chinaunix.net/space.php?uid=20564848&do=blog&id=74654 要对系统中进程进行监测控制,查看状态,内存,CPU的使用情况,使用命令:/bin/ps (1) ps :是显示瞬间进程的状态,并不动态连续: (2) top:如果想对进程运行时间监控,应该用 top 命令: (3) kill 用于杀死进程或者给进程发送信号: (4) 查看文章最后的man手册,可以查看ps的每项输出的含义

linux下scp命令详解

scp是 secure copy的缩写, scp是linux系统下基于ssh登陆进行安全的远程文件拷贝命令.linux的scp命令可以在linux服务器之间复制文件和目录. scp命令的用处: scp在网络上不同的主机之间复制文件,它使用ssh安全协议传输数据,具有和ssh一样的验证机制,从而安全的远程拷贝文件. scp命令基本格式: scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file] [-l limit] [-o s

Linux下ps命令详解 Linux下ps命令的详细使用方法

Linux下ps命令详解 1. 运行(正在运行或在运行队列中等待) 2. 中断(休眠中, 受阻, 在等待某个条件的形成或接受到信号) 3. 不可中断(收到信号不唤醒和不可运行, 进程必须等待直到有中断发生)4. 僵死(进程已终止, 但进程描述符存在, 直到父进程调用wait4()系统调用后释放)5. 停止(进程收到SIGSTOP, SIGSTP, SIGTIN, SIGTOU信号后停止运行运行)ps工具标识进程的5种状态码:D 不可中断 uninterruptible sleep (usuall

linux下IPTABLES配置详解 (防火墙命令)

linux下IPTABLES配置详解 -A RH-Firewall-1-INPUT -p tcp -m state --state NEW -m tcp --dport 24000 -j ACCEPT-A RH-Firewall-1-INPUT -s 121.10.120.24 -p tcp -m tcp --dport 18612 -j ACCEPT 如果你的IPTABLES基础知识还不了解,建议先去看看. 开始配置 我们来配置一个filter表的防火墙. (1)查看本机关于IPTABLES的

linux下tar命令详解

 linux下tar命令详解  tar是Linux环境下最常用的备份工具之一.tar(tap archive)原意为操作磁带文件,但基于Linux的文件操作机制,同样也可适用于普通的磁盘文件.tar可用于建立.还原.查看.管理文件,也可方 便的追加新文件到备份文件中,或仅更新部分的备份文件,以及解压.删除指定的文件.熟悉其常用参数,能方便日常的系统管理工作. 一.版本 还是那句,不同的操作系统,tar的命令参数是有点区别的: 引用 # tar --version tar (GNU tar) 1.

linux下任务计划详解

一次性任务计划 用at(指定时间执行任务,需开启atd服务)   batch(不指定执行时间,在系统空闲时/系统负载较低时自动执行任务)等命令来实现 周期性任务计划 cron(需启动crond服务) at介绍 at now+3min|11:30|noon,midnight,teatime(更多时间指定格式见man at页)  回车  在>提示符下可输入多行命令,用ctrl+d提交任务 atq 查看当前等待执行的atd的命令队列,或  at  -l at会将执行结果(即执行输出)以邮件方式发给任务

Linux下chkconfig命令详解

Linux下chkconfig命令详解 chkconfig命令主要用来更新(启动或停止)和查询系统服务的运行级信息.谨记chkconfig不是立即自动禁止或激活一个服务,它只是简单的改变了符号连接. 使用语法:chkconfig [--add][--del][--list][系统服务] 或 chkconfig [--level <等级代号>][系统服务][on/off/reset] chkconfig在没有参数运行时,显示用法.如果加上服务名,那么就检查这个服务是否在当前运行级启动.如果是,返

转载的 Linux下chkconfig命令详解

Linux下chkconfig命令详解 chkconfig命令主要用来更新(启动或停止)和查询系统服务的运行级信息.谨记chkconfig不是立即自动禁止或激活一个服务,它只是简单的改变了符号连接. 使用语法: chkconfig [--add][--del][--list][系统服务] 或 chkconfig [--level <等级代号>][系统服务][on/off/reset] chkconfig 在没有参数运行时,显示用法.如果加上服务名,那么就检查这个服务是否在当前运行级启动.如果是

&lt;linux下sysctl指令详解&gt;

Sysctl指令是对系统核心参数的设置: 用法: -a 参数列出系统中所有核心设置 当然了这些核心的设置都是文件,存放于/proc/sys/net目录下. 举个有代表性的例子: net.ipv4.icmp_echo_ignore_all = 0      把所有的点改为 / 就可以了.   [[email protected] net]# net.ipv4.icmp_echo_ignore_all = 0 [[email protected] net]# cd ipv4/ [[email pro