复制命令行参数和环境的 `do_execve()` 和 `_start` 之间有什么区别?

复制命令行参数和环境的 `do_execve()` 和 `_start` 之间有什么区别?

了解Linux内核说依次execve()调用do_execve( )which

将文件路径名、命令行参数和环境字符串复制到一个或多个新分配的页框中。(最终,它们被分配给用户模式地址空间。)

我是否正确,在execve()成功终止后,该进程会调用_start的例程rt0.o

根据APUE:

当内核(通过 exec 函数之一)执行 C 程序时,会在调用 main 函数之前调用一个特殊的启动例程。可执行程序文件指定该例程作为程序的起始地址;这是由 C 编译器调用链接编辑器时设置的。该启动例程从内核获取值(命令行参数和环境)并进行设置这样主函数就会被调用,如前面所示。

__start例程是否还会再次复制命令行参数和环境?

do_execve()复制_start 命令行参数和环境之间有什么区别?复制两次不是很浪费吗?

谢谢。

答案1

我是否正确,在 execve() 成功终止后,进程会调用 crt0.o 的 _start 例程?

不必要。当execve系统调用返回时,进程将从二进制文件入口点的任何文本/代码地址继续执行(在 ELF 中,这是e_entry标头中的字段)。例子:

echo 'void run(void){ printf("in run\n"); exit(0); }' |
   gcc -Wl,-e,run -nostartfiles -include stdio.h -include stdlib.h -Wall -x c - -o /tmp/run
/tmp/run
in run

_start只是许多(大多数?)Unix 系统上入口点例程的常用名称。

_start例程是否还会再次复制命令行参数和环境?

它可以做到这一点,但通常它不会做这样的事情。它唯一应该做的就是重新排列它们,以便它们可以传递给 C 函数,例如main.

问题是你不能简单地将入口点声明为 C 函数

_start(argc, ...)

并用 获取参数va_args,因为例如。 on x86_64,C 调用约定期望(前几个)参数在寄存器中传递,这就是不是它们是如何传递到_start.

_start在打电话之前通常还有其他事情要做main;非常重要的事情是运行静态构造函数,这是用 编写的程序所必需的C++,但如果它在 ELF 二进制文件中定义了正确的节属性,则任何程序都可以使用静态构造函数(使用,您可以通过定义函数gcc在程序中执行此操作)C__attribute__((constructor)))。

libc.so基于 glibc 的系统中的标准启动代码还将通过(动态链接) --中定义的函数__libc_start_main(),这非常好,因为您可以从预加载的动态库覆盖它并添加您自己的初始化内容,而无需修改二进制文件。看这里举个例子。

相关内容