系统调用execve的入口sys_execve()

本文详细介绍了Linux内核中的sys_execve系统调用过程,包括从用户空间拷贝文件名、参数及环境到内核空间,使用do_execve()函数进行二进制格式处理,加载可执行文件,并释放旧进程资源,映射虚拟地址空间等关键步骤。通过search_binary_handler函数找到正确的加载方法,确保执行文件正确运行。
摘要由CSDN通过智能技术生成
/*
 * sys_execve() executes a new program.
 */
long sys_execve(const char __user *name,  //需要执行的文件的绝对路径(存于用户空间)
		const char __user *const __user *argv, //传入系统调用的参数(存于用户空间)		 
                const char __user *const __user *envp, struct pt_regs *regs) //regs是系统调用时系统堆栈的情况(详细解释请参看情景分析之系统调用)
{
	long error;
	char *filename;

	filename = getname(name); //copy *filename frome user space to system space.
	error = PTR_ERR(filename); 
	if (IS_ERR(filename))
		return error;
	error = do_execve(filename, argv, envp, regs);

#ifdef CONFIG_X86_32
	if (error == 0) {
		/* Make sure we don't return using sysenter.. */
                set_thread_flag(TIF_IRET);
        }
#endif

	putname(filename);
	return error;
}

我们首先关注标签__user,这个标签表示其后边的变量是指向用户空间的地址的(详细的解释,请参看深入Linux内核框架P27)。

关于sys_execve参数的说明:Not only the register set with the arguments and the name of the executable file (filename) but also pointers to the arguments and the environment of the program are passed as in system programming. The notation is slightly clumsy because argv and envp are arrays of pointers, and both the pointer tothe array itself as well as all pointers in the array are located in the userspace portion of the virtual address space. Recall from the Introduction that some precautions are required when userspace memoryis accessed from the kernel, and that the __user annotations allow automated tools to check if everything is handled properly.

接下来的getname将要执行的文件名从用户空间拷贝到系统空间会调用如下函数:

static char *getname_flags(const char __user * filename, int flags) 
{
	char *tmp, *result;

	result = ERR_PTR(-ENOMEM);
	tmp = __getname();  //allocate a physical page in system space as cache. Because the file's name could be very long. (hu xi ming, Page 306)
	if (tmp)  {
		int retval = do_getname(filename, tmp);

		result = tmp;
		if (retval < 0) {
			if (retval != -ENOENT || !(flags & LOOKUP_EMPTY)) {
				__putname(tmp);
				result = ERR_PTR(retval);
			}
		}
	}
	audit_getname(result);
	return result;
}
注意函数中的__getname();为文件名分配一个物理页面作为缓冲区,因为一个绝对路径可能很长,因此如果用临时变量的话,这个路径就被存储在系统堆栈段中,这显然是不合适的,因为系统堆栈段只有约7KB的空间。

之后调用do_getname()将filename从用户空间拷贝到分配到的系统物理页面上:

static int do_getname(const char __user *filename, char *page)
{
	int retval;
	unsigned long len = PATH_MAX;

	if (!segment_eq(get_fs(), KERNEL_DS)) {  //如果进程地址限制和KERNEL_DS不和相等,即当前进程没有运行在内核态
		if ((unsigned long) filename >= TASK_SIZE) //如果filname>=TASK_SIZE,则非法访问了
			return -EFAULT;
		if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
			len = TASK_SIZE - (unsigned long) filename;    //这个是为什么????
	}

	retval = strncpy_from_user(page, filename, len);  //将filename从用户空间中拷贝到内核页面中。
	if (retval > 0) {
		if (retval < len)
			return 0;
		return -ENAMETOOLONG; 
	} else if (!retval)
		retval = -ENOENT;
	return retval;
}
对划红线部分代码的理解:在创建新进程的时候,有个copy_mm操作,将
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值