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

news2024/11/26 22:33:21

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/577961.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

玄子Share - IDEA 2023.1 自定义 代码模板(Servlet)

玄子Share - IDEA 2023.1 自定义 代码模板&#xff08;Servlet&#xff09; 23版 IDEA 内取消了自动生成 Servlet 模板类&#xff0c;不过我们可以自己定义一个 Servlet 模板 步骤 第一步打开 IDEA 设置界面&#xff0c;编辑器 -> 文件和代码模板 -> 点击加号新建模板…

chatgpt赋能python:Python补齐0,让你在编程中更得心应手

Python 补齐0&#xff0c;让你在编程中更得心应手 在Python编程中&#xff0c;我们经常会遇到数字需要补齐0的情况。比如说我们要制作一个日期格式的字符串&#xff0c;例如“2022-02-22”&#xff0c;但是当天数只有一位数时&#xff0c;需要在前面补0&#xff0c;即“2022-0…

chatgpt赋能python:Python转换器——将数据转换为所需格式的工具

Python 转换器——将数据转换为所需格式的工具 Python 是一种功能强大的编程语言&#xff0c;因为它具有处理和分析数据的能力&#xff0c;因此被广泛使用。然而&#xff0c;在现代互联网时代&#xff0c;数据的格式和大小通常不相同&#xff0c;因此经常需要将数据从一种格式…

chatgpt赋能python:Python词形还原:一种优化搜索引擎排名的方法

Python词形还原&#xff1a;一种优化搜索引擎排名的方法 什么是词形还原&#xff1f; 词形还原是自然语言处理&#xff08;NLP&#xff09;中的一种重要技术。它是将单词转化为其最基本的形式的过程&#xff0c;例如动词的原形或名词的单数形式。这个过程旨在帮助计算机更好地…

openGauss 3.1企业版升级至5.0

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

什么是SOME/IP?

本文是SOME/IP 官方文档的翻译。原文地址&#xff1a;https://www.autosar.org/fileadmin/standards/R22-11/FO/AUTOSAR_RS_SOMEIPProtocol.pdf 1.引言和概览 2. 协议要求 3. 缩略语和术语 术语/缩略语描述Byte Order Mark字节顺序标记(byte order mark, BOM)是一个Unicode字…

Spring学习记录

目录 工厂模式的三种形态 简单工厂模式 代码&#xff1a; 运行结果&#xff1a; 总结&#xff1a; 工厂模式 代码&#xff1a; 运行结果&#xff1a; 总结&#xff1a; 抽象工厂模式 代码&#xff1a; 运行结果&#xff1a; 总结&#xff1a; bean的单例与多例 设置 …

chatgpt赋能python:Python父类调用子类:多态性的核心思想

Python父类调用子类&#xff1a;多态性的核心思想 多态性是面向对象编程(OOP)的核心思想之一&#xff0c;它允许不同的对象在调用同一个方法时产生不同的行为。Python是一门支持多态性的编程语言。在Python中&#xff0c;使用父类调用子类的方法是实现多态性的关键之一。 什么…

考研C语言第五章

5.2 数组基础知识 注意要点&#xff1a; 1.下标0开始 2.没有定义的地方是0 5.3 数组访问越界与数组传递 访问越界&#xff0c;定义超过前面定义的数组长度&#xff0c;占据了后面定义数据的地方 非常危险&#xff01; 在循环里面往往容易造成越界 数组传递 1.函数引用时…

AI一键抠图

前言 由于需要批量抠图&#xff0c;原本是想用MODNet的&#xff0c;可惜最新的模型官方不开源&#xff0c;旧模型扣的人物边缘有白边。最后发现了PP飞桨。 飞桨&#xff08;PaddlePaddle&#xff09;以百度多年的深度学习技术研究和业务应用为基础&#xff0c;集深度学习核心训…

【JDK】一、jdk17的下载与安装配置(图文说明超详细)

JDK17的下载与安装 前言一、JDK17下载1、官方下载地址 &#xff08; Oracle中国的官方网站&#xff09; 二、JDK17安装1、先看一下我现在的java版本和环境变量2、开始新的安装第一步&#xff1a;双击下载的jdk-17.0.7_windows-x64_bin.exe 进入到安装页面第二步&#xff1a;jdk…

内存管理之内存寻址(笔记)

计算机体系结构中的核心问题之一就是如何有效的进行内存寻址。因为所有运算的前提都是要先从内存中取地数据。所以内存寻址技术在一定程度上代表了计算机技术。 1. 图灵机和冯诺依曼体系 图灵机&#xff1a;图灵机是一种通用自动的机器模型&#xff0c;通过二段无线延申的纸带…

C++Primer——第一讲

重制CPrimer 目录 一、第一个程序 二、代码 二、题目 前言 我们会从一个C程序开始&#xff0c;这里默认您已经安装了Dev-C或其他的IDE软件。 一、第一个程序 下面这串代码是可以输出“Hello world”的代码。 #include<bits/stdc.h> using namespace std; int main(){…

Arduino+ESP8266 MCU开发板 ----带你开发DHT11温湿度开发项目

目录 PC调试过程如图 手机APP可在各大商场APP中下载 手机APP调试结果/效果如图 ESP8266 MUC介绍 ESP8266 MUC主要特点&#xff1a; 步1&#xff1a;下载Arduino&#xff0c;本次不多做说明&#xff0c;本次使用的arduino软件为老版本的&#xff0c;新版本有关的问题本人…

统计学的假设检验

假设检验的核心其实就是反证法。反证法是数学中的一个概念&#xff0c;就是你要证明一个结论是正确的&#xff0c;那么先假设这个结论是错误的&#xff0c;然后以这个结论是错误的为前提条件进行推理&#xff0c;推理出来的结果与假设条件矛盾&#xff0c;这个时候就说明这个假…

总结881

学习目标&#xff1a; 月目标&#xff1a;5月&#xff08;1800基础部分&#xff0c;背诵15篇短文&#xff09; 周目标&#xff1a;1800高等数学部分并完成错题记录&#xff0c;英语背3篇文章并回诵 每日必复习&#xff08;5分钟&#xff09; 前天错题纠错&#xff0c;线代部…

Solidity拓展:数据类型的转换

1.数据类型隐式转换 (自动) 同一类型之间的转换:由低长度转换为高长度int8-int16-int32int256,但int不能自动转换成uint&#xff0c;因为放不下负数所以直接不让转换,且 int8 不能转换成 uint256 &#xff08;因为 uint256 不能涵盖某些值&#xff0c;例如&#xff0c; -1&…

Android解决xutils数据库kotlin添加List数组问题

Android解决xutils数据库kotlin添加List数组问题 前言&#xff1a; 上一篇我们讲解了xutils中数据库版本升级的使用和问题&#xff0c;这篇博客讲解xutils中数据库添加list数据的问题&#xff0c;这个库真的是很强大&#xff0c;但是数据库的使用真不友好&#xff0c;添加一个…

从零开始手搓一个STM32与机智云的小项目——硬件介绍

文章目录 前言硬件简介选型1.主控2.电源3.电机驱动4.舵机驱动5.USB转TTL6.其他模块 原理图绘制1.STM32最小系统1.电源输入2.晶振选择3.复位电路4.BOOT选择电路5.下载电路 2.电源部分及与PC通信部分3.功能模块的实现1.串口2.定时器输入捕获与输出比较3.硬件SPI4.ADC5.温湿度传感…

学校食堂明厨亮灶 yolov8

学校食堂明厨亮灶可以yolov8网络模型技术&#xff0c;学校食堂明厨亮灶通过对厨师的穿戴情况行为举止等进行监测。YOLOv8 算法的核心特性和改动可以归结为如下&#xff1a;提供了一个全新的 SOTA 模型&#xff0c;包括 P5 640 和 P6 1280 分辨率的目标检测网络和基于 YOLACT 的…