Linux0.11内核源码解析-exec.c

news2024/10/6 10:29:47

主要实现对二进制可执行文件和shell文件的加载和执行,其中主要的函数是do_execve(),它是系统中断调用int 0x80的功能号__NR_execve()调用,是exec()函数的主要实现以下几点功能:

1.执行对参数和环境参数空间页面的初始化操作,初始化空间页面指针数组,根据执行文件名取执行对象的i节点,计算参数个数和环境变量个数,检查文件类型、执行权限

2.根据执行文件开始部分的头数据结构,对其中信息进行处理,根据被执行文件i节点读取文件头部信息,若是shell以#!开始,则分析shell程序名和其参数;根据幻数和段长度等信息判断是否可执行

3.对当前调用进程进行运行新文件前初始化操作,指向新执行文件的i节点,复位信号处理句柄,根据头结构信息设置局部描述符地址和段长;设置参数和环境参数页面指针;修改进程各执行字段内容

4.替换堆栈上原调用execve()程序的返回地址为新执行程序运行地址,运行新加载的程序

 create_tables函数用于根据给定的当前堆栈指针值p以及参数个数argc和环境变量个数envc,在新的程序堆栈中创建环境和参数变量指针表,并返回此时的堆栈指针值sp

 create_tables

用戶内存中解析环境变量和参数字符串,创建指针表,并将地址放到堆栈上,返回栈指针

/*
 * MAX_ARG_PAGES defines the number of pages allocated for arguments
 * and envelope for the new program. 32 should suffice, this gives
 * a maximum env+arg of 128kB !
 */
#define MAX_ARG_PAGES 32

/*
 * create_tables() parses the env- and arg-strings in new user
 * memory and creates the pointer tables from them, and puts their
 * addresses on the "stack", returning the new stack pointer value.
 */
static unsigned long * create_tables(char * p,int argc,int envc)
{
	unsigned long *argv,*envp;
	unsigned long * sp;
    //堆栈指针是以4字节为边界寻址
	sp = (unsigned long *) (0xfffffffc & (unsigned long) p);
    //sp向下移动,空出环境参数占用空间个数,让环境参数指针envp指向该处
	sp -= envc+1;
	envp = sp;
    //sp向下移动,空出环境参数占用的空间个数,并让环境参数指针envp指向该处
	sp -= argc+1;
	argv = sp;

    //argc、argv、envp
	put_fs_long((unsigned long)envp,--sp);
	put_fs_long((unsigned long)argv,--sp);
	put_fs_long((unsigned long)argc,--sp);

    //argc
	while (argc-->0) {
		put_fs_long((unsigned long) p,argv++);
		while (get_fs_byte(p++)) /* nothing */ ;
	}
    //最后放置NULL
	put_fs_long(0,argv);

    //envc
	while (envc-->0) {
		put_fs_long((unsigned long) p,envp++);
		while (get_fs_byte(p++)) /* nothing */ ;
	}
    //最后放置NULL
	put_fs_long(0,envp);
	return sp;
}

count

计算参数个数

/*
 * count() counts the number of arguments/envelopes
 */
static int count(char ** argv)
{
	int i=0;
	char ** tmp;
    
	if ((tmp = argv))
        //判断为NULL就退出
		while (get_fs_long((unsigned long *) (tmp++)))
			i++;

	return i;
}

copy_string

函数从用户内存空间拷贝参数和环境字符串到内核空闲页面内存中,argc

/*
 * 'copy_string()' copies argument/envelope strings from user
 * memory to free pages in kernel mem. These are in a format ready
 * to be put directly into the top of new user memory.
 *
 * Modified by TYT, 11/24/91 to add the from_kmem argument, which specifies
 * whether the string and the string array are from user or kernel segments:
 * 
 * from_kmem     argv *        argv **
 *    0          user space    user space
 *    1          kernel space  user space
 *    2          kernel space  kernel space
 * 
 * We do this by playing games with the fs segment register.  Since it
 * it is expensive to load a segment register, we try to avoid calling
 * set_fs() unless we absolutely have to.
 */
static unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
		unsigned long p, int from_kmem)
{
	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);
	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++));
        //字符串长度判断
		if (p-len < 0) {	/* this shouldn't happen - 128kB */
			set_fs(old_fs);
			return 0;
		}
		while (len) {
			--p; --tmp; --len;
            //首次复制字符串,offset复制为0
			if (--offset < 0) {
                //p指针在页内的偏移
				offset = p % PAGE_SIZE;
                //恢复fs段寄存器
				if (from_kmem==2)
					set_fs(old_fs);
                //如果pag不为NULL,说明页面在之前的拷贝过程得到分配
                //pag指针为NULL,说明该页面还没有分配内存空间
				if (!(pag = (char *) page[p/PAGE_SIZE]) &&
				    !(pag = (char *) (page[p/PAGE_SIZE] =
				      (unsigned long *) get_free_page()))) 
					return 0;
				if (from_kmem==2)
					set_fs(new_fs);

			}
			*(pag + offset) = get_fs_byte(tmp);
		}
	}
    //还原fs
	if (from_kmem==2)
		set_fs(old_fs);
	return p;
}

修改局部描述符表中的描述符基址和段限长,并将参数和环境页面放置在数据段末端

参数:text_size 执行文件头部中a_text字段给出的代码段长度值,page参数和环境空间页面指针数组

static unsigned long change_ldt(unsigned long text_size,unsigned long * page)
{
	unsigned long code_limit,data_limit,code_base,data_base;
	int i;
//根据执行文件头部a_text的值,计算以页面长度为边界代码段限长,并设置数据段长度为64MB
	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;
}

execve系统中断调用函数,加载并执行子进程

函数系统中断调用int 0x80功能号__NR_execve,

参数:eip指向堆栈中调用系统中断的程序代码指针eip,

tmp:_sys_execve时返回地址

filename 被执行程序文件名

argv命令行参数指针数组

envp环境变量指针数组

/*
 * 'do_execve()' executes a new program.
 */
int do_execve(unsigned long * eip,long tmp,char * filename,
	char ** argv, char ** envp)
{
	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;

	if ((0xffff & eip[1]) != 0x000f)
		panic("execve called from supervisor mode");
	for (i=0 ; i<MAX_ARG_PAGES ; i++)	/* clear page-table */
		page[i]=0;
	if (!(inode=namei(filename)))		/* get executables inode */
		return -ENOENT;
	argc = count(argv);
	envc = count(envp);
	
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)) {
		/*
		 * This section does the #! interpretation.
		 * Sorta complicated, but hopefully it will work.  -TYT
		 */

		char buf[1023], *cp, *interp, *i_name, *i_arg;
		unsigned long old_fs;

		strncpy(buf, bh->b_data+2, 1022);
		brelse(bh);
		iput(inode);
		buf[1022] = '\0';
		if ((cp = strchr(buf, '\n'))) {
			*cp = '\0';
			for (cp = buf; (*cp == ' ') || (*cp == '\t'); cp++);
		}
		if (!cp || *cp == '\0') {
			retval = -ENOEXEC; /* No interpreter name found */
			goto exec_error1;
		}
		interp = i_name = cp;
		i_arg = 0;
		for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) {
 			if (*cp == '/')
				i_name = cp+1;
		}
		if (*cp) {
			*cp++ = '\0';
			i_arg = cp;
		}
		/*
		 * OK, we've parsed out the interpreter name and
		 * (optional) argument.
		 */
		if (sh_bang++ == 0) {
			p = copy_strings(envc, envp, page, p, 0);
			p = copy_strings(--argc, argv+1, page, p, 0);
		}
		/*
		 * Splice in (1) the interpreter's name for argv[0]
		 *           (2) (optional) argument to interpreter
		 *           (3) filename of shell script
		 *
		 * This is done in reverse order, because of how the
		 * user environment and arguments are stored.
		 */
		p = copy_strings(1, &filename, page, p, 1);
		argc++;
		if (i_arg) {
			p = copy_strings(1, &i_arg, page, p, 2);
			argc++;
		}
		p = copy_strings(1, &i_name, page, p, 2);
		argc++;
		if (!p) {
			retval = -ENOMEM;
			goto exec_error1;
		}
		/*
		 * OK, now restart the process with the interpreter's inode.
		 */
		old_fs = get_fs();
		set_fs(get_ds());
		if (!(inode=namei(interp))) { /* get executables inode */
			set_fs(old_fs);
			retval = -ENOENT;
			goto exec_error1;
		}
		set_fs(old_fs);
		goto restart_interp;
	}
	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;
	}
	if (N_TXTOFF(ex) != BLOCK_SIZE) {
		printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename);
		retval = -ENOEXEC;
		goto exec_error2;
	}
	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;
		}
	}
/* OK, This is the point of no return */
	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);
	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;
	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;
exec_error2:
	iput(inode);
exec_error1:
	for (i=0 ; i<MAX_ARG_PAGES ; i++)
		free_page(page[i]);
	return(retval);
}

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

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

相关文章

2023年09月 Python(二级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试&#xff08;1~6级&#xff09;全部真题・点这里 一、单选题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 第1题 yyh[2023,杭州亚运会,[拱宸桥,玉琮莲叶]] jxwyyh[2][0] print(jxw[1]*2)以上代码运行结果是&#xff1f;&#xff08; …

[已解决]ERROR tool.ExportTool: Error during export: Export job failed!

ERROR tool.ExportTool: Error during export: Export job failed! 问题 sqoop导出数据的时候遇到问题&#xff0c;ERROR tool.ExportTool: Error during export: Export job failed 思路 sqoop的shell命令是这样的 sqoop export –connect jdbc:mysql://cdh00:3306/airq…

JDK常用性能监控和故障处理工具

JDK8 在JDK安装目录下的bin文件夹&#xff0c;有一些辅助命令行工具&#xff0c;通常用来获取JVM的信息或者监控JVM&#xff0c;在排查性能问题方面是非常好用的工具。以Centos7.9系统下的openJDK1.8.0_222为例&#xff08;不同大版本的JDK命令的参数会有差异&#xff0c;不同操…

利用python进行数据分析 pdf

利用python进行数据分析 pdf 介绍 在现代社会中&#xff0c;随着大数据时代的到来&#xff0c;数据分析的需求越来越大。而Python作为一门简洁且易于学习的编程语言&#xff0c;具有强大的数据分析能力&#xff0c;成为了广大数据分析师的首选工具之一。本文将指导一位刚入行的…

相关性网络图 | 热图中添加显著性

一边学习&#xff0c;一边总结&#xff0c;一边分享&#xff01; 本期教程 写在前面 此图是一位同学看到后&#xff0c;想出的一期教程。 最近&#xff0c;自己的事情比较多&#xff0c;会无暇顾及社群和公众号教程。 1 安装和加载相关的R包 library(ggraph) library(tidy…

追求卓越 再添新荣誉 | Softing中国顺利通过ISO9001质量管理体系认证!

近日&#xff0c;羧福汀电子科技&#xff08;上海&#xff09;有限公司&#xff08;简称&#xff1a;Softing中国&#xff09;顺利通过ISO9001质量管理体系认证审核&#xff0c;并被授予认证证书。ISO9001认证的通过&#xff0c;代表Softing中国的软硬件技术研发水平及管理水平…

压敏涂料静态校准装置中的温度、真空压力和氧浓度精密控制解决方案

摘要&#xff1a;针对客户提出的在温度-10℃~80℃、绝对压力1Pa~600kPa、氧浓度0~80%范围内实现对压力敏感涂料静态特性校准测试腔室的精密自动控制要求&#xff0c;本文提出了相应的解决方案。解决方案的主要技术内容是采用TEC半导体制冷器进行温度控制、采用动态平衡法和电控…

C# Onnx 用于边缘检测的轻量级密集卷积神经网络LDC

效果 项目 代码 using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms;namespace Onnx…

Python二级 每周练习题24

练习一: 体重比较器 要求: 请编程实现如下功能: (1)程序开始运行时&#xff0c;提醒用户输入三个人的名字和体重 (可以分开输入&#xff0c;每次输入名字或者体重) (2) 程序自动比较&#xff0c;找出最重的一个人的名字和体重输出 的格式不限&#xff0c;但是要有最重人的姓名…

AcWing96. 奇怪的汉诺塔

题目 汉诺塔问题&#xff0c;条件如下&#xff1a; 1、这里有 A、B、C 和 D 四座塔。 2、这里有 n n n 个圆盘&#xff0c; n n n 的数量是恒定的。 3、每个圆盘的尺寸都不相同。 4、所有的圆盘在开始时都堆叠在塔 A 上&#xff0c;且圆盘尺寸从塔顶到塔底逐渐增大。 5、…

python实现MC协议(SLMP 3E帧)的TCP服务端

python实现MC协议&#xff08;SLMP 3E帧&#xff09;的TCP服务端是一件稍微麻烦点的事情。它不像modbusTCP那样&#xff0c;可以使用现成的pymodbus模块去实现。但是&#xff0c;我们可以根据协议帧进行组包&#xff0c;自己去实现帧的格式&#xff0c;而这一切可以基于socket模…

如何快速定制个性化外观验证码?

验证码作为人机交互界面经常出现的关键要素&#xff0c;是身份核验、防范风险、数据反爬的重要组成部分&#xff0c;广泛应用网站、App上&#xff0c;在注册、登录、交易、交互等各类场景中发挥着巨大作用&#xff0c;具有真人识别、身份核验的功能&#xff0c;在保障账户安全方…

【vtk学习笔记2】vtk编程中的基本对象与可视化管线

一、VTK编程中的基本对象 数据可视化一般需要一个数据源&#xff0c;可以来说具体的算法&#xff0c;如有限元、边界元等&#xff0c;也可以是测量数据&#xff0c;如医学检测数据等。这些基础数据经过变换处理&#xff0c;变成计算机图形流水线支持的数据&#xff0c;最后显示…

C语言编程之排列组合问题

1.动态取球 在生活中我们经常会遇到一个这样的问题&#xff0c;从N个球中随机取K个(N>K)&#xff0c;问有多少总取法。这是我们数学中的排列组合&#xff0c;通过排列组合可计算方法为&#xff1a;C(N,K);   例如一个袋子中有6个球为(1,2,3,4,5,6)&#xff0c;从袋子中一次…

Python基础介绍 —— 使用pytest进行测试!

Pytest 是 Python 的一种单元测试框架&#xff0c;与 Python 自带的 unittest 测试框架类似&#xff0c;但是比 unittest 框架使用起来更简洁&#xff0c;效率更高。 Pytest 是一个成熟的全功能的 Python 测试工具&#xff0c;可以帮助你写出更好的程序&#xff0c;让我们很方便…

MINIO 对象存储服务

MINIO 官网下载地址&#xff1a; 注&#xff1a;需要下载 MINIO SERVER&#xff08;服务端&#xff09; 和 MINIO CLIENT&#xff08;客户端&#xff09;两个文件 WINDOWS版本下载地址&#xff1a;https://min.io/download#/windows LINUX版本下载地址&#xff1a;https://mi…

数据库 索引

Mysql的索引 事务 存储引擎 索引 概念 什么是索引&#xff1a;索引是一个排序的列表&#xff0c;列表当中存储的是所有的值和包含这个值的数据所在行的物理地址 索引的作用 加快查询作用&#xff0c;这是他主要的一个作用 利用索引数据库可以快速定位&#xff0c;大大加快…

AERMOD模型、WRF/Chem模式、CMAQ、Smoke模式及EDGAR/MEIC清单制作

推荐一些大气环境、气象领域常用的一些模型软件免费教程&#xff0c;包含5大方向&#xff0c;不容错过&#xff01;评论区联系小编领取 >>>> WRF模式及在地学多领域应用&#xff08;必学&#xff09; 1、WRF模式基础 &#xff08;初始和边界场、气象预报流程、数…

毫米波雷达3-三个FFT(距离,速度,角度)

文章目录 快慢时间维度空间维度FFT原理useful links快慢时间维度 由于电磁波传播的速度非常之快,导致了回波间隔比发射间隔小了好几个数量级 下图中,每一行(快时间维)对应的是一个脉冲回波的连续采样,在快时间维度,即使一个移动的物体,我们也可以看作不动,那么对于快…

代码随想录day50|198.打家劫舍、213.打家劫舍II、337.打家劫舍 III

198.打家劫舍 &#xff08;中等&#xff09; leetcode题目链接&#xff1a;198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 文章讲解&#xff1a;代码随想录 (programmercarl.com) 视频讲解&#xff1a;动态规划&#xff0c;偷不偷这个房间呢&#xff1f;| LeetCode…