linux中ELF加载过程分析

【讯飞星火大模型AI】SpringBoot整合星火API:从零构建智能数据分析助手 本文详细介绍了如何使用SpringBoot框架整合讯飞星火大模型API,从零构建一个智能数据分析助手。通过清晰的步骤,指导开发者完成API密钥申请、项目搭建、SDK集成与核心代码编写,实现用户通过自然语言指令和CSV数据,即可自动获得ECharts图表代码与专业数据分析结论,极大降低数据分析与可视化的门槛。 阅读详情

 

sys_execve
   | - do_execve
|
| - search_binary_handler          
           |- linux_binfmt= elf_format
       |- elf_format-> load_elf_binary
| - elf_entry = load_elf_interp()
          |-   
| if  (BAD_ADDR(elf_entry))
                |     force_sig(SIGSEGV, current);
|      retval =-EINVAL;
 
  
binfmt_elf.c: line 1024
              elf_entry = loc->elf_ex.e_entry;
              if (BAD_ADDR(elf_entry)) {
                     force_sig(SIGSEGV, current);
                     retval = -EINVAL;
                     goto out_free_dentry;
              }
 
ELF可行档的载入:
 
内核中实际执行 execv() 或 execve() 系统调用的程序是 do_execve() ,这个函数先打开目标映像文件,并从目标文件的头部 ( 从第一个字节开始 ) 读入若干 (128) 字节,然后调用另一个函数 search_binary_handler() ,在那里面让各种可执行程序的处理程序前来认领和处理。内核所支持的每种可执行程序都有个 struct linux_binfmt 数据结构,通过向内核登记挂入一个队列。而 search_binary_handler() ,则扫描这个队列,让各个数据结构所提供的处理程序、即各种映像格式、逐一前来认领。如果某个格式的处理程序发现特征相符而,便执行该格式映像的装入和启动。
我们从 ELF 格式映像的 linux_binfmt 数据结构开始:
CODE:
#define load_elf_binary load_elf32_binary

static struct linux_binfmt elf_format = {
.module = THIS_MODULE,
.load_binary = load_elf_binary,
.load_shlib = load_elf_library,
.core_dump = elf_core_dump,
.min_coredump = ELF_EXEC_PAGESIZE
};
 
ELF 格式的二进制映像的认领、装入和启动是由 load_elf_binary() 完成的。而 “ 共享库 ” 、即动态连接库映像的装入则由 load_elf_library() 完成。实际上共享库的映像也是二进制的,但是一般说 “ 二进制 ” 映像是指带有 main() 函数的、可以独立运行并构成一个进程主体的可执行程序的二进制映像。
 
CODE:
[sys_execve() > do_execve() > search_binary_handler() > load_elf_binary()]

整个 ELF 映像就是由文件头、区段头表、程序头表、一定数量的区段、以及一定数量的部构成
而 ELF 映像的装入 / 启动过程,则就是在各种头部信息的指引下将某些部或区段装入一个进程的用户空间,并为其运行做好准备 ( 例如装入所需的共享库 ) ,最后 ( 在目标进程首次受调度运行时 ) 让 CPU 进入其程序入口的过程。
 
接着是对 elf_bss 、 elf_brk 、 start_code 、 end_code 等等变量的初始化。这些变量分别纪录着当前 ( 到此刻为止 ) 目标映像的 bss 段、代码段、数据段、以及动态分配 “ 堆 ” 在用户空间的位置。除 start_code 的初始值为 0xffffffff 外,其余均为 0 。随着映像内容的装入,这些变量也会逐步得到调整,读者不妨自己留意这些变量在整个过程中的变化。
读入了程序头表,并对 start_code 等变量进行初始化以后,下面的第一步就是在程序头表中寻找 “ 解释器 ” 部、并加以处理的过程。
 
ELF 格式的二进制映像在装入和启动的过程中需要得到一个工具软件的协助,其主要的目的在于为目标映像建立起跟共享库的动态连接。这个工具称为 “ 解释器 ” 。一个 ELF 映像在装入时需要用什么解释器是在编译 / 连接是就决定好了的,这信息就保存在映像的 “ 解释器 ” 部中。 “ 解释器 ” 部的类型为 PT_INTERP ,找到后就根据其位置 p_offset 和大小 p_filesz 把整个 “ 解释器 ” 部读入缓冲区。整个 “ 解释器 ” 部实际上只是一个字符串,即解释器的文件名,例如 “/lib/ld-linux.so.2” 。有了解释器的文件名以后,就通过 open_exec() 打开这个文件,再通过 kernel_read() 读入其开头 128 个字节,这就是映像的头部。早期的解释器映像是 a.out 格式的,现在已经都是 ELF 格式的了, /lib/ld-linux.so.2 就是个 ELF 映像。
 
 
程序段的载入:
还是从目标映像的程序头表中搜索,这一次是寻找类型为 PT_LOAD 的部 (Segment) 。在二进制映像中,只有类型为 PT_LOAD 的部才是需要装入的。
找到一个 PT_LOAD 片以后,先要确定其装入地址。正如代码前面的注释所述,这里先假定装入地址是固定的,然后再根据映像是否允许浮动而作出调整。具体片头数据结构中的 p_vaddr 提供了映像在连接时确定的装入地址 vaddr 。如果映像的类型为 ET_EXEC , ( 或者 load_addr_set 已经被设置成 1 ,见下 ) 那么装入地址就是固定的。而若类型为 ET_DYN 、即共享库,那么即使装入地址固定也要加上一个偏移量,代码中给出了计算方法,其中 ELF_ET_DYN_BASE 对于 x86 定义为 (TASK_SIZE / 3 * 2) ,所以这是 2GB 边界,而 ELF_PAGESTART 表示按页面边界对齐。
确定了装入地址以后,就通过 elf_map() 、实际上是 elf32_map() 、建立用户空间虚存区间与目标映像文件中某个连续区间之间的映射。这个函数基本上就是 do_mmap() ,其返回值就是实际映射的 ( 起始 ) 地址。对于类型为 ET_EXEC 的可执行程序映像而言,代码中的 load_bias 是 0 ,所以装入的起点就是映像自己提供的地址 vaddr 。另一方面,对于 ET_EXEC ,由于参数中的 elf_flags 中的 MAP_FIXED 标志位为 1 ,所以给定的映射地址是刚性的而不容许变通,如果与已经映射的区间有冲突就以失败告终。不过,目标映像的映射是从一片空白开始的,所以实际上不可能失败。顺便提一下,现在又多了一种 ELF 格式的目标映像,称为 FDPIC ,其装入地址就是可浮动的。
即使总的装入地址是浮动的,一旦装入了第一个 Segment 以后,下一个 Segment 的装入地址就应该是固定的了,所以这里一方面把 load_addr_set 设置成 1 ,
 
 
 
[sys_execve() > do_execve() > search_binary_handler() > load_elf_binary()]

if (elf_interpreter) {
if (interpreter_type == INTERPRETER_AOUT)
elf_entry = load_aout_interp(&loc->interp_ex, interpreter);
else
elf_entry = load_elf_interp(&loc->interp_elf_ex, interpreter, &interp_load_addr);
. . . . . .
reloc_func_desc = interp_load_addr;

allow_write_access(interpreter);
fput(interpreter);
kfree(elf_interpreter);
} else {
elf_entry = loc->elf_ex.e_entry;
}

这段程序的逻辑很简单:如果需要装入解释器,并且解释器的映像是 ELF 格式的,就通过 load_elf_interp() 装入其映像,并把将来进入用户空间时的入口地址设置成 load_elf_interp() 的返回值,那显然是解释器的程序入口。 而若不装入解释器,那么这个地址就是目标映像本身的程序入口。
显然,关键的操作是由 load_elf_interp() 完成的,所以我们追下去看 load_elf_interp() 的代码。
 
do_brk() 从用户空间分配一段空间。这段代码总体上与前面映射目标映像的那一段相似。注意解释器映像的类型一般都是 ET_DYN ,所以 load_addr 可能不等于 0 。
 
 
进程可使用 exec 系统调用执行新的命令。 exec 系统调用将一次性释放全部虚拟内存空间,之后生成新的空间并将新的命令影射入内。

   
do_execve (文件路径,参数。环境)
       
打开文件( open_namei 函数)
       
计算 exec 后的 UID/GID ,读入文件头( prepare_binprm 函数)
       
读入命令名,环境变量,起动参数( copy_strings 函数)
       
呼叫各种不同二进制文件的操作函数( search_binary_handler 函数)

    ELF
格式的话,经由 search_binary_handler 函数呼叫 load_elf_binary 函数。如果是动态联结,同时影射动态联结器( ld*.so )

    load_elf_binary(linux_binprm* bprm,pt_regs* regs)
       
分析 ELF 文件头
       
读入程序的头部分( kernel_read 函数)
        if
(存在解释器头部) {
               
读入解释器名( ld*.so ) (kernel_read 函数 )  | ( zalem note :可用
               
打开解释器文件( open_exec 函数)                 | objdump -s -j .interp xxx
            
读入解释器文件的头部( kernel_read 函数)    | 命令查看,
       
)                                                           |linux 下是 /lib/ld-linux.so.x )
       
释放空间,清楚信号,关闭指定了 close-on-exec 标识的文件( flush_old_exec 函数)
       
生成堆栈空间,塞入环境变量 / 参数部分( setup_arg_pages 函数)
        for
(可引导的所有的程序头) {
               
将文件影射入内存空间( elf_map,do_mmap 函数)
        }
        if
(为动态联结) {
               
影射动态联结器( load_elf_interp 函数)
        }
       
释放文件( sys_close 函数)
       
确定执行中的 UID , GID ( compute_creds 函数)
       
生成 bss 领域( set_brk 函数)
        bss
领域清零( padzero 函数)
       
设定从 exec 返回时的 IP , SP ( start_thread 函数)(动态联结时的 IP 指向解释器的入口)
LPDDR6与LPDDR5 State Diagram技术探讨 阅读详情

相关推荐

GPOPSII 对控制量变化率进行约束,避免控制量振荡

在这篇文章中,在GPOPSII中通过对控制量变化的约束,优化求解得到更平滑的控制量曲线,更符合真实控制约束。

Dannyfomer的博客 1870

ELF文件加载与动态链接(一)

关于ELF文件的详细介绍,推荐阅读:ELF文件格式分析 —— 滕启明。ELF文件由ELF头部、程序头部表、节区头部表以及节区4部分组成。 通过objdump工具和readelf工具,可以观察ELF文件详细信息。 ELF文件加载过程分析 从编译、链接和运行的角度,应用程序和库程序的链接有两种方式。一种是静态链接,库程序的二进制代码链接进应用程序的映像中;一种是动态链接,库函数的...

weixin_30239339的博客 419

Label Studio + SAM2分割半自动标注与yolo11训练

Label Studio + SAM2分割半自动标注与yolo11训练

schFallingCloud 1408

ELF在Linux下的加载过程

主要函数调用: sys_execve    | - do_execve | | - search_binary_handler                    |- linux_binfmt= elf_format        |- elf_format-> load_elf_binary | - elf_entry = load_elf_interp()

joker0910的专栏 7849

紫光同创国产FPGA学习之Fabric Debugger

文章从紫光同创参考书拷贝过来的。因为我用的时候,看了一下就会用了(纯粹只是会用了,默认和xilinx的一样),没有看教程。先更新点文本,后面在写程序。 一、      软件简介 Fabric Debugger是一款界面化的FPGA芯片调试工具,能够针对公司目前以及未来的FPGA芯片进行在线调试。本软件直接与JtagHub、DebugCore交互,能够实时的配置目标FPGA、设置触发条件并且观测结...

my_share的专栏 6391

SoC验证新思路:除了UVM仿真,你还可以试试Cadence JasperGold的形式化验证

本文深入探讨了Cadence JasperGold形式化验证在SoC设计中的创新应用,对比传统UVM仿真验证,详细解析了其数学穷尽性验证优势。通过实战案例展示了JasperGold在总线协议、时钟域交叉等关键场景的验证方法,并提供了混合验证流程设计与效能优化方案,帮助工程师提升验证效率30%以上。

weixin_30566063的博客 120

Linux加载启动可执行程序的过程(一)内核空间加载ELF的过程

linux内核启动时将ELF格式注册到内核可支持的文件格式链表中,也就是通过register_binfmt 函数将定义的elf_format结构体添加到链表中。该结构体如下: 当我们执行一个可执行程序的时候, 内核会list_for_each_entry遍历所有注册的linux_binfmt对象, 对其调用load_binrary方法来尝试加载, 直到加载成功为止。上面代码可以看倒,E...

lionzl的专栏 1442

Axial Attention 和 Criss-Cross Attention及其代码实现

Axial Attention 和 Criss-Cross Attention及其代码实现 ​ 文章目录Axial Attention 和 Criss-Cross Attention及其代码实现1 Criss - Cross Attention介绍1.1 引言1.2 理论实现1.2.1 获取权重A1.2.2 Affinity操作1.3.3 全部信息获取1.3 代码实现1.3.1 官方实现1.3.2 纯pytorch实现2 Axial Attention 介绍2.1 引言2.2 理论实现2.3 代码实现2.3

fzl的博客 2万+

ELF文件的加载过程(load_elf_binary函数详解)--Linux进程的管理与调度(十三)

日期 内核版本 架构 作者 GitHub 优快云 2016-06-04 Linux-4.6 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度-之-进程的描述 加载和动态链接从编译/链接和运行的角度看,应用程序和库程序的连接有两种方式。 一种是固定的、静态的连接,就是把需要用到的库函数的目标代码(二进制)代

OSKernelLAB(gatieme) 3万+

AI辅助需求分析与提示词生成

文章标题 《AI辅助需求分析与提示词生成》 关键词 人工智能 需求分析 提示词生成 深度学习 机器学习 强化学习 数学模型 系统架构

AI天才研究院 1214

PyTorch深度学习实战(36)——Pix2Pix详解与实现

Pix2Pix 是基于生成对抗网络的图像转换框架,能够将输入图像转换为与之对应的输出图像,能够广泛用于图像到图像转换的任务,如风格转换、图像修复、语义标签到图像的转换等。Pix2Pix 的核心思想是通过对抗训练将输入图像和目标输出图像进行配对,使生成网络可以学习到输入图像到输出图像的映射关系。在本节中,将学习使用 Pix2Pix 根据给定轮廓生成图像。

盼小辉丶的博客 1万+

linux内存布局和地址空间布局随机化(ASLR)下的可分配地址空间

https://zsummer.github.io/2019/11/04/2019-11-04-aslr/ 地址空间布局随机化(ASLR) 《mmap的随机化》 《Meltdown(熔断漏洞)- Reading Kernel Memory from User Space/KASLR | 原文+中文翻译》 导语 64位下的linux地址空间虽然看起来虽然庞大2^64 但是实际上进行内核与用户空间的划分后, 包括ASLR以及PIE等机制的启用, 实际留给mmap和brk的可分配区域远远小于这个值, 大

RToax 3073

【GameFramework框架内置模块】8、文件系统(File System)

最近好忙,鸽了好久。不能颓废了,继续发力。今天分享的是GF框架的文件系统。首先,引用比较官方的说法:GF的FileSystem虚拟文件系统,使用类似磁盘存储的感念对零散的文件进行集中管理,优化资源加载时产生的内存分配,也可以对资源进行局部片段加载,极大的提升了资源加载的性能。通俗点讲就是,我这个模块就是加载磁盘文件的,优化加载时产生的内存分配。那么,它是怎么优化的呢?

优快云博客专家,Unity3D高级软件工程师,《Unity 3D从入门到实战》作者。 2452

Linux elf文件的加载过程分析

前言 在linux shell命令行启动一个程序时,实际上新启动的进程是作为shell进程的子进程存在的。shell进程会先fork出一个子进程,此时子进程是父进程的一个拷贝,所以其代码段和数据段都来自于父进程,但是子进程是要执行新的程序的,所以会调用execv函数,载入新的程序,此过程中,子进程的代码段,数据段等都会替换成新的进程的。 本文主要是对elf文件的加载过程做一个简要的分析。 lin...

liuhangtiant的博客 2053

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

linux 解析elf文件格式,Linux elf文件的加载过程分析-Go语言中文社区

前言在linux shell命令行启动一个程序时,实际上新启动的进程是作为shell进程的子进程存在的。shell进程会先fork出一个子进程,此时子进程是父进程的一个拷贝,所以其代码段和数据段都来自于父进程,但是子进程是要执行新的程序的,所以会调用execv函数,载入新的程序,此过程中,子进程的代码段,数据段等都会替换成新的进程的。本文主要是对elf文件的加载过程做一个简要的分析。linux只支...

weixin_31574891的博客 526

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

linux中ELF加载过程分析 - 博青港湾-技术空间 - 优快云Blog

导读: sys_execve   | - do_execve|| - search_binary_handler                     |- linux_binfmt= elf_format       |- elf_format-> load_elf_binary| - elf_entry = load_elf_interp()          |- 

xu的blog 2835

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

ELF文件的加载和动态链接过程

近段时间在研究Erlang核心特性的实现,也许过段时间会有个系列的总结,期待... 今天看到有人写一个深入Hello World的文章,想起来读研的时候做的一个关于程序加载和链接的课程设计,也是以Hello World为例说明的,随发出来共享。文后有下载链接。 ====================================================== 本文...

一墨的博客 1357

Linux下ELF文件格式

(in English for practice^_^)In this article,I will talk about the ELF files for Linux,and their diffs as to comprehend the linking process in a diff view angle.There are many articles talking about tha

feilengcui008的专栏 928
上一篇: RISC/DSP融合可满足VoIP产品的功耗、成本和性能目标
下一篇: 软交换与NGN
suacker
博客等级 码龄24年 46粉丝 19原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值