当前位置: 首页 > news >正文

Linux-0.11 文件系统exec.c详解

Linux-0.11 文件系统exec.c详解

模块简介

该模块实现了二进制可执行文件和shell脚本文件的加载和执行。

函数详解

create_tables

static unsigned long * create_tables(char * p,int argc,int envc)

该函数的作用是建立参数和环境变量指针表。

create_table的作用就是建立指针表去指向copy_string拷贝的字符串。

create_tables

	unsigned long *argv,*envp;unsigned long * sp;sp = (unsigned long *) (0xfffffffc & (unsigned long) p);sp -= envc+1;envp = sp;sp -= argc+1;argv = sp;put_fs_long((unsigned long)envp,--sp);put_fs_long((unsigned long)argv,--sp);put_fs_long((unsigned long)argc,--sp);while (argc-->0) {put_fs_long((unsigned long) p,argv++);while (get_fs_byte(p++)) /* nothing */ ;}put_fs_long(0,argv);while (envc-->0) {put_fs_long((unsigned long) p,envp++);while (get_fs_byte(p++)) /* nothing */ ;}put_fs_long(0,envp);return sp;

count

static int count(char ** argv)

该函数用于计算参数的个数。

argv数组的最后一项是NULL,以此作为循环终止的条件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aRmQGiRv-1685200650093)(null)]

	int i=0;char ** tmp;if ((tmp = argv))while (get_fs_long((unsigned long *) (tmp++)))i++;return i;

copy_strings

static unsigned long copy_strings(int argc,char ** argv,unsigned long *page,unsigned long p, int from_kmem)

该函数的作用是从用户拷贝命令行参数和环境字符串拷贝到内核空间。

在do_execve函数中定义了一个page数组unsigned long page[MAX_ARG_PAGES],该数组定义在内核空间中,用于存储用户态传递过来的环境参数和命令行参数。初始时,程序定义了一个指向该空间末端(128k-4)处的空间偏移量p, 当随着环境参数和命令行参数的拷贝,p指针将向前进行移动, 如下图所示:

copy_strings

在函数的内部,考虑了参数不处于用户空间的场景,使用from_kmen,为了便于理解,可以默认from_kmen=0。

 * from_kmem     argv *        argv ***    0          user space    user space*    1          kernel space  user space*    2          kernel space  kernel space

该函数在开始处,定义了一些参数,并获取处ds寄存器和fs寄存器的值。

	char *tmp, *pag=NULL;int len, offset = 0;unsigned long old_fs, new_fs;if (!p)return 0;	/* bullet-proofing */new_fs = get_ds();old_fs = get_fs();if (from_kmem==2)set_fs(new_fs);

接下来,因此传入的参数的数量argc个,因此对其进行遍历,依次将其拷贝到page数组中。

	while (argc-- > 0) {if (from_kmem == 1)set_fs(new_fs);if (!(tmp = (char *)get_fs_long(((unsigned long *)argv)+argc)))panic("argc is wrong");if (from_kmem == 1)set_fs(old_fs);len=0;		/* remember zero-padding */do {len++;} while (get_fs_byte(tmp++));//将tmp指向字符串的末端if (p-len < 0) {	/* this shouldn't happen - 128kB */set_fs(old_fs);return 0;}while (len) {//逐字节拷贝--p; --tmp; --len;if (--offset < 0) {offset = p % PAGE_SIZE;if (from_kmem==2)set_fs(old_fs);if (!(pag = (char *) page[p/PAGE_SIZE]) &&!(pag = (char *) (page[p/PAGE_SIZE] =get_free_page()))) return 0;if (from_kmem==2)set_fs(new_fs);}*(pag + offset) = get_fs_byte(tmp);}}if (from_kmem==2)set_fs(old_fs);return p;

change_ldt

static unsigned long change_ldt(unsigned long text_size,unsigned long * page)

该函数的作用是修改LDT中段基址和段限长度, 同时将参数和环境空间放置在数据段的末端。

	unsigned long code_limit,data_limit,code_base,data_base;int i;code_limit = text_size+PAGE_SIZE -1;code_limit &= 0xFFFFF000;data_limit = 0x4000000;code_base = get_base(current->ldt[1]);data_base = code_base;set_base(current->ldt[1],code_base);set_limit(current->ldt[1],code_limit);set_base(current->ldt[2],data_base);set_limit(current->ldt[2],data_limit);
/* make sure fs points to the NEW data segment */__asm__("pushl $0x17\n\tpop %%fs"::);data_base += data_limit;for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) {data_base -= PAGE_SIZE;if (page[i])put_page(page[i],data_base);}return data_limit;

do_execve

int do_execve(unsigned long * eip,long tmp,char * filename,char ** argv, char ** envp)

该函数的作用是用于加载并执行其他的程序。通常会跟着fork之后调用。

第一部分是变量的定义,没啥好说的。

	struct m_inode * inode;struct buffer_head * bh;struct exec ex;unsigned long page[MAX_ARG_PAGES];int i,argc,envc;int e_uid, e_gid;int retval;int sh_bang = 0;unsigned long p=PAGE_SIZE*MAX_ARG_PAGES-4;

下面这里是对变量的check。eip[1]实际上就是源程序的cs寄存器值。对于用户态的程序, TI字段为1,RPL=3,代码段的序号为1,因此cs寄存器的值为0x000f,因此如果eip[1] & 0xffff的值不是0x000f,也就意味着execve是从内核进程调用的,但是内核进程是不能被替换掉的,因此这里进行检查。

         |   段描述符索引           |TI | RPL |
0x0017 = |0 0 0 0 0 0 0 0 0 0 0 1 0| 1 | 1 1 |
0x000f = |0 0 0 0 0 0 0 0 0 0 0 0 1| 1 | 1 1 |
	if ((0xffff & eip[1]) != 0x000f)panic("execve called from supervisor mode");

接下来根据文件的路径获取i节点。

if (!(inode=namei(filename)))		/* get executables inode */return -ENOENT;

然后统计参数和环境变量的个数。

argc = count(argv);
envc = count(envp);

如果inode不是常规文件,则返回错误。

restart_interp:if (!S_ISREG(inode->i_mode)) {	/* must be regular file */retval = -EACCES;goto exec_error2;}

下面的代码用于判断进程是否有权限执行。

	i = inode->i_mode;e_uid = (i & S_ISUID) ? inode->i_uid : current->euid;e_gid = (i & S_ISGID) ? inode->i_gid : current->egid;if (current->euid == inode->i_uid)i >>= 6;else if (current->egid == inode->i_gid)i >>= 3;if (!(i & 1) &&!((inode->i_mode & 0111) && suser())) {retval = -ENOEXEC;goto exec_error2;}

执行到这里说明进程有权限运行程序,接着便是去磁盘中读取程序的第一个数据块。

if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) {retval = -EACCES;goto exec_error2;
}
ex = *((struct exec *) bh->b_data);	/* read exec-header */

下面这里便是判断文件是否是一个脚本文件。

if ((bh->b_data[0] == '#') && (bh->b_data[1] == '!') && (!sh_bang)) {

此时bh块中的数据内容已经被拷贝到了ex结构体中,因此可以释放bh块。

接下来因为Linux-0.11只支持ZMAGIC格式可执行文件,因此如果格式不等于ZMAGIC将直接返回错误。

此外,如果可执行文件太大或者文件缺失不全,那么也不能运行。首先如果程序的代码段+数据段+bss段的长度超过了50M,则返回错误。 其次如果执行文件的长度小于(代码段+数据段+符号表长度+执行头)的长度,也会返回错误。

brelse(bh);
if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize ||ex.a_text+ex.a_data+ex.a_bss>0x3000000 ||inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {retval = -ENOEXEC;goto exec_error2;
}

如果执行文件代码开始处没有位于1024字节边界处,则也不能执行。

if (N_TXTOFF(ex) != BLOCK_SIZE) {printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename);retval = -ENOEXEC;goto exec_error2;
}

如果sh_bang标志没有设置,则复制指定个数的命令行参数和环境字符串到参数和环境空间。如果sh_bang为true,代表是运行的脚本程序,此时命令行参数和环境字符串已经进行了复制。

	if (!sh_bang) {p = copy_strings(envc,envp,page,p,0);//拷贝环境字符串p = copy_strings(argc,argv,page,p,0);//拷贝命令行参数if (!p) {retval = -ENOMEM;goto exec_error2;}}

这里放回进程原来所需要执行的程序的i节点,并让进程executable指向新执行文件的i节点。然后复位原进程的所有信号处理句柄。再根据执行时关闭文件句柄close_on_exec位图标志,关闭指定的打开文件,并复位该标志。

if (current->executable)iput(current->executable);
current->executable = inode;
for (i=0 ; i<32 ; i++)current->sigaction[i].sa_handler = NULL;
for (i=0 ; i<NR_OPEN ; i++)if ((current->close_on_exec>>i)&1)sys_close(i);
current->close_on_exec = 0;
free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
if (last_task_used_math == current)last_task_used_math = NULL;
current->used_math = 0;
p += change_ldt(ex.a_text,page)-MAX_ARG_PAGES*PAGE_SIZE;
p = (unsigned long) create_tables((char *)p,argc,envc);

接下来重新设置进程个字段的值。例如brk=a_text+a_data+a_bss

current->brk = ex.a_bss +(current->end_data = ex.a_data +(current->end_code = ex.a_text));
current->start_stack = p & 0xfffff000;
current->euid = e_uid;
current->egid = e_gid;

如果执行文件的代码段加上数据段长度不在页面的边界上,则将剩余部分置为0。

i = ex.a_text+ex.a_data;
while (i&0xfff)put_fs_byte(0,(char *) (i++));
eip[0] = ex.a_entry;		/* eip, magic happens :-) */
eip[3] = p;			/* stack pointer */
return 0;

Q & A

相关文章:

Linux-0.11 文件系统exec.c详解

Linux-0.11 文件系统exec.c详解 模块简介 该模块实现了二进制可执行文件和shell脚本文件的加载和执行。 函数详解 create_tables static unsigned long * create_tables(char * p,int argc,int envc)该函数的作用是建立参数和环境变量指针表。 create_table的作用就是建立…...

NET框架程序设计-第1章.NET框架开发平台体系架构

1.1 .NET 框架基本组成 .NET 框架的核心便是通用语言运行时&#xff08;Commomn Language Runtime&#xff0c;简称 CLR&#xff09;&#xff0c;CLR 是一个可被各种不同的编程语言所使用的运行时。 托管模块(mangaed module)&#xff1a; 一个需要 CLR 才能执行的标准 Window…...

(哈希表 ) 349. 两个数组的交集 ——【Leetcode每日一题】

❓349. 两个数组的交集 难度&#xff1a;简单 给定两个数组 nums1 和 nums2 &#xff0c;返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,2,2,1], nums2 [2,2] 输出&#xff1a;[…...

JavaScript基本语法(二)

JavaScript基本语法 1、变量1.1、简介1.2、变量命名规则1.3、JS的关键字和保留字1.4、声明提升 2、JavaScript数据类型2.1、基本类型2.2、引用类型2.3、两种类型的区别2.4、字符串常用方法 3、数据类型转换 1、变量 1.1、简介 在 JavaScript 中声明一个新变量的方法是使用关键…...

ChatGPT3.5-4资源汇总,直连无梯子

什么是ChatGPT? ChatGPT&#xff0c;全称&#xff1a;聊天生成预训练转换器&#xff08;英语&#xff1a;Chat Generative Pre-trained Transformer&#xff09;&#xff0c;是OpenAI开发的人工智能聊天机器人程序&#xff0c;于2022年11月推出。该程序使用基于GPT-3.5、GPT-4…...

【Netty】使用 SSL/TLS 加密 Netty 程序(二十)

文章目录 前言一、SSL/TLS概述二、Sslhandler类 前言 回顾Netty系列文章&#xff1a; Netty 概述&#xff08;一&#xff09;Netty 架构设计&#xff08;二&#xff09;Netty Channel 概述&#xff08;三&#xff09;Netty ChannelHandler&#xff08;四&#xff09;ChannelP…...

runway gen2

来自Runway文生成视频ai大模型Gen-2_哔哩哔哩_bilibili来自Runway文生成视频ai大模型Gen-2&#xff0c;距离视频制作自由又近了一步。, 视频播放量 1651、弹幕量 0、点赞数 21、投硬币枚数 2、收藏人数 42、转发人数 22, 视频作者 旭升说, 作者简介 一起聊下互联网的那些事&…...

Day2:Windows网络编程-TCP

今天开始进入Windows网络编程的学习&#xff0c;在学习的时候总是陷入Windows复杂的参数&#xff0c;纠结于这些。从老师的讲解中&#xff0c;这些内容属于是定式&#xff0c;主要学习写的逻辑。给自己提个醒&#xff0c;要把精力放在正确的位置&#xff0c;不要无端耗费精力。…...

leetcode1985. 找出数组中的第 K 大整数

给你一个字符串数组 nums 和一个整数 k 。nums 中的每个字符串都表示一个不含前导零的整数。 返回 nums 中表示第 k 大整数的字符串。 注意&#xff1a;重复的数字在统计时会视为不同元素考虑。例如&#xff0c;如果 nums 是 ["1","2","2"]&am…...

基于深度学习的高精度野生动物检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度野生动物检测&#xff08;水牛、犀牛、斑马和大象&#xff09;识别系统可用于日常生活中或野外来检测与定位野生动物目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的野生动物目标检测识别&#xff0c;另外支持结果可视…...

从零开始 Spring Boot 35:Lombok

从零开始 Spring Boot 35&#xff1a;Lombok 图源&#xff1a;简书 (jianshu.com) Lombok是一个java项目&#xff0c;旨在帮助开发者减少一些“模板代码”。其具体方式是在Java代码生成字节码&#xff08;class文件&#xff09;时&#xff0c;根据你添加的相关Lombok注解或类来…...

深入解析Spring源码系列:Day 6 - Spring MVC原理

深入解析Spring源码系列&#xff1a;Day 6 - Spring MVC原理 欢迎来到本系列的第六篇博客。在前几篇博客中&#xff0c;我们探索了Spring框架的核心概念&#xff0c;包括Bean的生命周期、作用域、AOP原理和事务管理。今天&#xff0c;我们将深入研究Spring框架中的MVC&#xf…...

Cmake中message函数 如何优雅地输出

message函数说明 在CMake中&#xff0c;message()函数用于向终端输出信息。 message([<mode>] "message text" ...)函数的<mode>参数可以是以下之一&#xff1a; (none): 等同于STATUS&#xff0c;但不推荐使用。STATUS: 输出的信息会被发送到CMake的…...

人工智能基础部分20-生成对抗网络(GAN)的实现应用

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能基础部分20-生成对抗网络(GAN)的实现应用。生成对抗网络是一种由深度学习模型构成的神经网络系统&#xff0c;由一个生成器和一个判别器相互博弈来提升模型的能力。本文将从以下几个方面进行阐述&#xff1…...

JavaScript表单事件(上篇)

目录 一、input: 当表单元素的值发生改变时触发&#xff0c;适用于大多数表单元素。 二、change: 当表单元素的值发生改变且失去焦点时触发&#xff0c;适用于输入框、下拉列表等。 三、submit: 当表单提交时触发&#xff0c;适用于 form 元素。 四、reset: 当表单重置时触…...

vb6 Webview2微软Edge Chromium内核执行JS取网页数据测速

微软Edge Chromium内核执行JS获取网页数据测试 ExcuteScript eval(document.body.innerHTML) from : https://www.163.com 采集的网页HTM字符串占用字节空间1.2MB ExcuteScript回调事件中取得JS执行结果&#xff0c;用时 54 毫秒 其中JSON转字符13.5209毫秒 jSON数据长度: 增…...

编码,Part 1:ASCII、汉字及 Unicode 标准

个人博客 编码的历史由来就懒得介绍了&#xff0c;只需要知道人类处理文本信息是以字符为基本单位&#xff0c;而计算机在最底层只认识 0/1&#xff0c;所以当计算机要为人类存储/呈现字符时&#xff0c;就需要有一个规则&#xff0c;在字符和 0/1 序列之间建立映射关系&#…...

C++ Eigen库矩阵操作

C Eigen库 序号功能例子1赋值Eigen::MatrixXf mat (12,1); \\% mat << 1, 2, 3, 4,5,6,7,8,9,10,11,12;2Inplace操作 \\% resizemat.resize(4, 3); \\% 1 5 9 \\% 2 6 10 \\% 3 7 11 \\% 4 8 123转置 \\% transposeInPlacemat.transposeInPlace(); \\% 1 2 3 4 \\% 5…...

Linux-0.11 boot目录bootsect.s详解

Linux-0.11 boot目录bootsect.s详解 模块简介 bootsect.s是磁盘启动的引导程序&#xff0c;其概括起来就是代码的搬运工&#xff0c;将代码搬到合适的位置。下图是对搬运过程的概括&#xff0c;可以有个印象&#xff0c;后面将详细讲解。 bootsect.s主要做了如下的三件事: 搬…...

django组件552

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…...

ubuntu搭建nfs服务centos挂载访问

在Ubuntu上设置NFS服务器 在Ubuntu上&#xff0c;你可以使用apt包管理器来安装NFS服务器。打开终端并运行&#xff1a; sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享&#xff0c;例如/shared&#xff1a; sudo mkdir /shared sud…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

xmind转换为markdown

文章目录 解锁思维导图新姿势&#xff1a;将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件&#xff08;ZIP处理&#xff09;2.解析JSON数据结构3&#xff1a;递归转换树形结构4&#xff1a;Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...