一个简单的答案是:程序调用时堆栈的布局由二进制格式定义(例如 ELF 规范:http://refspecs.linuxbase.org/elf/abi386-4.pdf http://refspecs.linuxbase.org/elf/abi386-4.pdf,查看图 3-31 上的初始堆栈布局exec
).
由于 Linux 不限于 ELF 可执行文件,并且可以使用其支持多种二进制格式binfmt
解释器框架,实际处理argv
/envp
数据分两个阶段发生。
首先,当sys_execve
系统调用被调用时,内核将为新的进程堆栈创建虚拟内存映射。然后它将使用堆栈底部的一页或几页(最多 32 页任意限制)来存储argv
/envp
提供的参数:
bprm->exec = bprm->p;
retval = copy_strings(bprm->envc, envp, bprm);
if (retval < 0)
goto out;
retval = copy_strings(bprm->argc, argv, bprm);
if (retval < 0)
goto out;
(http://code.metager.de/source/xref/linux/stable/fs/exec.c#1502 http://code.metager.de/source/xref/linux/stable/fs/exec.c#1502)
在第二阶段期间,bprm
对象被传递给binfmt
解释器(以及相关的内存映射)。除了其他之外,解释器还将向堆栈推送附加信息,包括参数/环境条目计数和指针。对于 elf 可执行文件,它将发生在create_elf_tables
:
/* Now, let's put argc (and argv, envp if appropriate) on the stack */
if (__put_user(argc, sp++))
return -EFAULT;
argv = sp;
envp = argv + argc + 1;
/* Populate argv and envp */
p = current->mm->arg_end = current->mm->arg_start;
while (argc-- > 0) {...
(http://code.metager.de/source/xref/linux/stable/fs/binfmt_elf.c#293 http://code.metager.de/source/xref/linux/stable/fs/binfmt_elf.c#293)
这里的解释器实际上正在构建上面链接的规范中图 3-31 中概述的结构。