首页 > 系统相关 >【linux内核】从ELF文件到Linux进程

【linux内核】从ELF文件到Linux进程

时间:2024-12-09 21:20:35浏览次数:7  
标签:文件 linux ELF 信息 0000000000000000 Linux 进程

今天我们来聊聊ELF文件,了解一下Linux如何创建进程以及ELF文件如何转变成Linux进程?

一、什么是ELF文件?

ELF(Executable and Linkable Format)文件是一种目标文件格式,用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件。它主要用于Linux平台,用于存储和传输可执行文件和库。

文件类型‌:

可执行文件‌:包含可执行的机器代码,可直接运行。

可重定位文件‌(.o文件):机器代码和数据地址相对,需重定位才能运行,通常用于编译过程。

共享对象文件(.so文件)‌:动态链接库,包含可共享代码和数据,可在运行时被多个进程共享。

核心转储文件‌(core文件):程序崩溃或异常终止时生成,包含内存状态和寄存器信息,用于调试。

二、ELF文件格式

如下图所示,ELF文件主要由:ELF头,程序头表,节区,节头表组成。

ELF头‌:包含文件的基本信息,如类型、架构、入口地址等。

程序头表‌:描述可执行文件中的段(Segment)信息,如类型、偏移地址、大小等。

节区:存储实际的代码、数据等信息。

节头表‌:描述目标文件中的节(Section)信息,如名字、类型、偏移地址、大小等。

注意:.o文件没有程序头表,ELF文件并不一定有程序头表。

段(Segment)和节(Section)有什么区别?

节是ELF文件的基本单位,包含了程序的代码,数据等信息。Linu系统为了高效加载ELF文件,将多个节划分为一个组(段),段是节的集合,Linux系统通过段加载代码(节)和数据(节)。

2.1 ELF头

ELF头是整个ELF文件的起始部分,位置固定,包含了识别和解释文件内容的关键信息。

查看ELF文件头信息

read -h ELF文件 
root@raspberrypi:/home/mfn# readelf -h a.out
ELF Header:
  Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class: ELF64
  Data: 2's complement, little endian
  Version: 1 (current)
  OS/ABI: UNIX - System V
  ABI Version: 0
  Type: DYN (Position-Independent Executable file)
  Machine: AArch64
  Version: 0x1
  Entry point address: 0x600
  Start of program headers: 64 (bytes into file)
  Start of section headers: 68504 (bytes into file)
  Flags: 0x0
  Size of this header: 64 (bytes)
  Size of program headers: 56 (bytes)
  Number of program headers: 9
  Size of section headers: 64 (bytes)
  Number of section headers: 29
  Section header string table index: 28

ELF文件头字段解析:

Magic:魔数,ELF文件识别信息。
Class:文件类型,32位或64位。
Data:编码方式,大端或小端。
Version:ELF版本。
OS/ABI:操作系统信息。
ABI:ABI版本。
‌Type:文件类型‌:可重定位文件(REL)、可执行文件(DYN)、共享对象文件(DYN),核心转储文件(CORE)。
‌Machine:‌机器类型‌,表示ELF文件的平台属性,如x86、AArch64等。
‌Entry point address:‌ 程序入口地址。
Start of program headers: 程序头表偏移量。
Start of section headers: 节头表偏移量。
Flags‌:标志。
Size of this header:ELF头大小。
Size of program headers:程序头表条目大小。
Number of program header:程序头表有多少条目。
Size of section headers:节头表条目大小。
Number of section headers:节头表有多少条目。

2.2 程序头表

程序头表(Program Header Table)用于描述文件中的段(Segment)信息,指导操作系统加载程序。

程序头表由多个程序头组成,每个头对应一个段,包含该段的详细信息:段的类型、在文件中的偏移地址、映射到内存的虚拟地址、大小及权限等。

查看程序头表信息:

readelf -l ELF文件 

程序头表字段解析:

TYPE:段类型,常见类型如下:

    PT_PHDR‌:程序头表本身在文件中的位置和大小。

    PT_LOAD:表示一个可加载的段,这种段包含了程序的实际代码和数据,需要被加载到内存中以便执行。

    PT_DYNAMIC‌:指向动态链接信息,包含了动态链接器所需的各种表和字符串,如动态库路径、依赖库列表、符号表等。

    PT_INTERP‌:指定了程序解释器的路径,这通常是动态链接器的路径。

    PT_NOTE‌:附加信息。

Offset:文件偏移,段在文件中的偏移量。

VirtAddr:虚拟地址,段加载至内存后的虚拟地址。

PhysAddr:物理地址:段的物理地址。

FileSiz:文件大小,段在文件中大小。

MemSiz:内存大小,段在内存中大小。

Flags:段标识,段属性:只读属性(R),只写属性(W),可执行属性(E)。

Align:对齐方式。

2.3 节区

ELF文件中包含多种节(Section),这些节在文件的编译、链接及执行过程中发挥关键作用。
以下是一些常见的ELF文件节:

‌.text‌:包含程序的代码段,是程序执行的主要部分。
‌.data‌:包含已初始化的全局和静态变量,程序运行时需要的数据。
‌.rodata‌:包含只读数据,如常量字符串、浮点数等。
‌.bss‌:包含未初始化的全局和静态变量,运行时被分配内存并初始化为零。
‌.symtab‌:符号表,包含程序中使用的符号信息,如函数名、变量名等。
‌.strtab‌:字符串表,包含符号表中使用的字符串。
‌.shstrtab‌:节名字符串表,包含所有节的名字。
‌.dynamic‌:动态链接信息,包含动态链接器所需的各种信息。

通过查看ELF文件符号表,可以协助我们排查程序bug:

readelf -s ELF文件

2.4 节头表

节头表(Section Header Table),用于描述文件中各个节(Section)的属性和信息。
每个节都有一个对应的节表头,它包含了节的名称、类型、大小、偏移量等关键数据,为链接器和加载器提供必要的信息。
查看节头表信息

readelf -s ELF文件
root@raspberrypi:/home/mfn# readelf -S a.out
There are 29 section headers, starting at offset 0x10b98:

Section Headers:
  [Nr] Name Type Address Offset
       Size EntSize Flags Link Info Align
  [ 0] NULL 0000000000000000 00000000
       0000000000000000 0000000000000000 0 0 0
  [ 1] .interp PROGBITS 0000000000000238 00000238
       000000000000001b 0000000000000000 A 0 0 1
  [ 2] .note.gnu.bu[...] NOTE 0000000000000254 00000254
       0000000000000024 0000000000000000 A 0 0 4
  [ 3] .note.ABI-tag NOTE 0000000000000278 00000278
       0000000000000020 0000000000000000 A 0 0 4
  [ 4] .gnu.hash GNU_HASH 0000000000000298 00000298
       000000000000001c 0000000000000000 A 5 0 8
  [ 5] .dynsym DYNSYM 00000000000002b8 000002b8
       00000000000000d8 0000000000000018 A 6 3 8
  [ 6] .dynstr STRTAB 0000000000000390 00000390
       000000000000008d 0000000000000000 A 0 0 1
  [ 7] .gnu.version VERSYM 000000000000041e 0000041e
       0000000000000012 0000000000000002 A 5 0 2
  [ 8] .gnu.version_r VERNEED 0000000000000430 00000430
       0000000000000030 0000000000000000 A 6 1 8
  [ 9] .rela.dyn RELA 0000000000000460 00000460
       00000000000000c0 0000000000000018 A 5 0 8
  [10] .rela.plt RELA 0000000000000520 00000520
       0000000000000060 0000000000000018 AI 5 22 8
  [11] .init PROGBITS 0000000000000580 00000580
       0000000000000018 0000000000000000 AX 0 0 4
  [12] .plt PROGBITS 00000000000005a0 000005a0
       0000000000000060 0000000000000000 AX 0 0 16
  [13] .text PROGBITS 0000000000000600 00000600
       000000000000012c 0000000000000000 AX 0 0 64
  [14] .fini PROGBITS 000000000000072c 0000072c
       0000000000000014 0000000000000000 AX 0 0 4
  [15] .rodata PROGBITS 0000000000000740 00000740
       0000000000000004 0000000000000004 AM 0 0 4
  [16] .eh_frame_hdr PROGBITS 0000000000000744 00000744
       000000000000003c 0000000000000000 A 0 0 4
  [17] .eh_frame PROGBITS 0000000000000780 00000780
       00000000000000a4 0000000000000000 A 0 0 8
  [18] .init_array INIT_ARRAY 000000000001fdc8 0000fdc8
       0000000000000008 0000000000000008 WA 0 0 8
  [19] .fini_array FINI_ARRAY 000000000001fdd0 0000fdd0
       0000000000000008 0000000000000008 WA 0 0 8
  [20] .dynamic DYNAMIC 000000000001fdd8 0000fdd8
       00000000000001e0 0000000000000010 WA 6 0 8
  [21] .got PROGBITS 000000000001ffb8 0000ffb8
       0000000000000030 0000000000000008 WA 0 0 8
  [22] .got.plt PROGBITS 000000000001ffe8 0000ffe8
       0000000000000038 0000000000000008 WA 0 0 8
  [23] .data PROGBITS 0000000000020020 00010020
       0000000000000010 0000000000000000 WA 0 0 8
  [24] .bss NOBITS 0000000000020030 00010030
       0000000000000008 0000000000000000 WA 0 0 1
  [25] .comment PROGBITS 0000000000000000 00010030
       000000000000001f 0000000000000001 MS 0 0 1
  [26] .symtab SYMTAB 0000000000000000 00010050
       0000000000000828 0000000000000018 27 65 8
  [27] .strtab STRTAB 0000000000000000 00010878
       000000000000021d 0000000000000000 0 0 1
  [28] .shstrtab STRTAB 0000000000000000 00010a95
       0000000000000103 0000000000000000 0 0 1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  D (mbind), p (processor specific)

节头表字段解析:

Name:节名。
Type:节类型
Address:虚拟地址。
Offset:文件偏移量。
Size:节大小,节在文件中的大小。
EntSize:节条目大小。
Flags:节属性:只读属性(R),只写属性(W),可执行属性(E)。
Link:链接,表示与该节相关联的符号表或者字符串表。
Info:节信息。
Align:对齐方式。

三、从ELF文件到Linux进程

前面已经介绍介绍了ELF文件,相信很多小伙伴都很好奇,ELF文件是如何转变成Linux进程的。
Linux创建一个新的进程需要经过两个步骤:

步骤1:父进程通过克隆方式创建子进程。

步骤2:子进程加载ELF文件生成新的进程地址空间。

对于用户程序来说,实现以上两个步骤需要调用fork和execve两个系统调用。
Linux子进程的创建必须由父进程完成,因为这样的一个约束,Linux进程之间形成了类似于家族关系的进程关系,而所有进程的共同祖先就是1号进程(init)。
子进程创建时需要继承父进程的关键信息才能正常工作,从父进程继承而来的信息只能保证子进程按照父进程的方式去工作。当子进程有特定的任务需要执行,此时从父进程继承的信息就没有意义了。子进程如果需要执行特定的任务,需要加载ELF文件,替换掉子进程从父进程继承的旧信息,生成新的进程信息,这样子进程就能独立工作了。

3.1 fork创建子进程

如下图所示,用户程序调用fork系统调用后,内核主要完成两部分工作:

  • 创建子进程并克隆关键信息。
  • 将子进程插入CPU就绪队列,等待CPU调度。

具体流程已在图中详细展示,这里不再赘述。

Linux内核克隆子进程是一个很复杂的过程,这里我们保留一些关键流程,我们来看一下子进程从父进程继承了哪些信息:

files:已打开文件表,子进程和父进程拥有相同的已打开文件表,父子进程可以操作相同的文件。
fs:文件系统信息。
sighand:信号处理函数表,子进程和父进程处理信号的方式相同。
signal:信号信息,同上。
mm:进程地址空间,子进程和父进程的代码,数据相同。需要注意:数据相同表示虚拟地址和内存存储内容相同,而实际的物理地址则不相同。
nxproxy:命名空间。

fork创建完子进程,如果子进程不需要执行特定的任务,此时子进程已经可以工作。如果子进程需要执行特定的任务,那么我们需要将任务编译成ELF文件,再通过ELF文件加载至子进程。

3.2 execve加载ELF文件

如下图所示,execve系统调用主要工作就是替换进程地址空间(mm),而替换进程地址空间需要用到编译好的ELF文件。由于进程地址空间替换时,原来从父进程继承的信息已经没有用,所以替换的过程需要清理旧信息。
进程地址空间替换内核流程如下图:

具体流程已经在图中详细展示,这里也不再赘述。
如下图所示,我们来看一下ELF文件转变成Linux进程的详细流程,用户程序调用execve系统调用后,首先会根据文件路径在磁盘中找到ELF文件,找到文件后打开文件(open),读取文件内容(read)。
此时ELF文件已经从磁盘读入内存, 接着execve系统调用按照ELF文件格式解析ELF文件,解析出.bss,.data,.text将三者以文件映射方式映射至进程地址空间。文件映射可以减少拷贝,提高访问效率。

总结

了解ELF文件以及ELF文件如何转换为Linux进程,可以让我们对Linux程序有更深入的理解,我们在编程和调试程序的时,思路会更加清晰。

原创 物联网心球

标签:文件,linux,ELF,信息,0000000000000000,Linux,进程
From: https://www.cnblogs.com/o-O-oO/p/18596060

相关文章

  • Kali Linux 安装谷歌浏览器及中文输入法教程
    KaliLinux安装谷歌浏览器及中文输入法教程在KaliLinux系统中,安装谷歌浏览器和中文输入法可以满足我们使用谷歌浏览器(谷歌翻译)以及中文输入的需求。以下是详细的安装步骤和注意事项,适合希望增强KaliLinux功能的用户。一、安装谷歌浏览器下载谷歌浏览器安装包在......
  • LinuxDay1
    LinuxDay1Linux学习所需组件VMStation通过该平台,创建虚拟Linux操作平台CentoS-7驱动所需的Linux操作系统Xshell直接连接Linux服务器的命令操作软件XftpWindows系统与Linux系统之间的文件传输软件XTerminal集Xshell与Xftp与一体的软件,更适用于Windows系统的......
  • [Linux网络]网络层-IP协议与数据链路层
    一、IP协议1.IP协议的简单认识    在TCP或UDP协议的传输层协议发送给对方的数据并不是直接给对方发了过去,而是需要经过网络层以及下面的数据链路层最后交到网卡才发送出去了。那么网络层协议做了什么呢?或者说IP协议做了什么呢?        TCP协议是有可......
  • linux-12 关于shell(十一)ls
       登录系统输入用户名和密码以后,会显示给我们一个命令提示符,就意味着我们在这里就可以输入命令了,给一个命令,这个命令必须要可执行,那问题是我的命令怎么去使用,命令格式有印象吗?在命令提示符下,我们首先是命令吧?command,后面可以带什么?参数对吗?options,再后面是arguments,我们......
  • Linux常用命令之top命令详解
    top命令是Linux系统中用于实时监控系统性能的一个非常强大的工具。它提供了一个动态的、实时的视图,展示了系统的整体状态,包括CPU使用情况、内存使用情况、交换空间使用情况以及正在运行的进程的详细信息。top命令的主要功能实时更新:与静态命令如ps不同,top会每隔......
  • 为什么 super().__new__(cls, name, bases, dct) 中的 cls 是显式传递的,而不是像 self
    问题来源:为什么定义元类和自定义元类时,在调用父类的__new__方法时都是需要显式传递cls的,而__init__在调用父类__init__方法时就是隐式的。#自定义元类classMyMeta(type):def__new__(cls,name,bases,dct):print(f"Creatingclass{name}usingMyMeta")......
  • 缺失nw_elf.dll文件的系统错误:如何有效解决?
    在使用Windows操作系统时,有时会遇到系统提示“缺失nw_elf.dll文件”的错误。这个错误通常发生在启动某些应用程序或游戏时,导致程序无法正常运行。nw_elf.dll文件是一个动态链接库文件,它与Node-Webkit(或NW.js)框架相关,该框架基于Chromium和Node.js,用于开发基于HTML5、JavaScript......
  • 腾讯通RTX停更后升级指南,兼容移动端及Linux系统
    一、腾讯通RTX继续使用的难题自腾讯通RTX停止更新并下架官网后,其用户面临着一系列无法克服的问题。这不仅包括失去技术支持、版本更新和资源下载的渠道,还涉及以下使用问题:●不兼容国产系统与移动端:腾讯通RTX仅适配Windows和Mac系统,无法支持统信UOS、银河麒麟等国产操作系统以及......
  • Linux中安装配置MongoDB
    最近在整理自己私人服务器上的各种阿猫阿狗,正好就顺手详细记录一下清理之后重装的步骤,今天先写点数据库的内容,关于在Linux中安装配置MongoDB说实话为什么会装MongoDB呢,因为之前因为公司需要做点Nodejs的中间件,我顺手玩了一下MongoDB的CRUD,文档型数据库还是挺有意思的安装环境Ce......
  • Linux-软件包管理器
    包管理器是方便软件安装卸载解决包依赖关系的重要工具centos redhat使用yum包管理器,安装包的格式是 rpmubuntu debian 使用apt包管理器,安装包格式是 debrpm命令常用参数-q查询软件包rpm-qa 查询安装的所有rpm软件包rpm-q软件包名称 查询已安装软件包-i安......