续上一篇<<ELF:加载过程>>中分析elf解析器、解析器填充等内容后,本章分析elf可执行程序加载过程。
目录
内容
1. 源码流程
1.1 execve
execve函数用于装载一个可执行文件以进程为单位加载到内存中,execve在内核空间中的调用可以追溯到kernel_execve -> run_init_process函数,而用户空间通过SYSCALL_DEFINE3(execve…)->do_execve进入函数:
static int do_execve(struct filename *filename,
const char __user *const __user *__argv,
const char __user *const __user *__envp)
{
struct user_arg_ptr argv = { .ptr.native = __argv };
struct user_arg_ptr envp = { .ptr.native = __envp };
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
// #define AT_FDCWD -100 用于指示openat应使用当前工作目录(./)
}
||
\/
static int do_execveat_common(int fd, struct filename *filename,
struct user_arg_ptr argv,
struct user_arg_ptr envp,
int flags)
{
struct linux_binprm *bprm;
int retval;
...
if ((current->flags & PF_NPROC_EXCEEDED) &&
is_ucounts_overlimit(current_ucounts(), UCOUNT_RLIMIT_NPROC, rlimit(RLIMIT_NPROC))) {
// 如果已经超过用户最大进程数量 并且 用户的某个(计数中的)命名空间大于最大值,返回错误
// #define PF_NPROC_EXCEEDED 0x00001000 超出最大进程数
retval = -EAGAIN;
goto out_ret;
}
current->flags &= ~PF_NPROC_EXCEEDED; // 去掉用户进程数量状态标志
bprm = alloc_bprm(fd, filename); // 分配bprm
// 用于保存可执行文件相关信息
// 可执行文件路径,执行参数,环境变量等等
retval = count(argv, MAX_ARG_STRINGS);
// 检查参数是否有效,参数数量是否大于最大值,并捕获异常信号、KILL信号等等
// #define MAX_ARG_STRINGS 0x7FFFFFFF
if (retval == 0)
pr_warn_once("process '%s' launched '%s' with NULL argv: empty string added\n",
current->comm, bprm->filename);
bprm->argc = retval;
retval = count(envp, MAX_ARG_STRINGS); // 环境变量检查
retval = bprm_stack_limits(bprm); // 检查栈是否超出限制
/* 从内核复制和参数/环境字符串到进程堆栈 */
retval = copy_string_kernel(bprm->filename, bprm);
bprm->exec = bprm->