![为什么 execve 和 brk(NULL) 总是前两个系统调用?](https://linux22.com/image/139094/%E4%B8%BA%E4%BB%80%E4%B9%88%20execve%20%E5%92%8C%20brk(NULL)%20%E6%80%BB%E6%98%AF%E5%89%8D%E4%B8%A4%E4%B8%AA%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%EF%BC%9F.png)
当我尝试时
strace ping google.com
或者
strace ls
或者
even strace curl <domain>
前两个系统调用始终是,
execve("/usr/bin/curl", ["curl", "google.com"], 0x7ffecf1bc378 /* 61 vars */) = 0
brk(NULL) = 0x55f553c49000
有人可以告诉我当我执行任何操作时 execve 是否总是第一个系统调用吗?
我阅读了本手册页,https://linux.die.net/man/2/execve
但不明白到底execve
是系统调用还是可执行程序?
答案1
答案2
我认为所有这些系统调用只是动态链接器在执行之前设置的东西。我做了一个小测试程序来检查这一点。
me@bar:~/foo$ cat main.c
#include <unistd.h>
void main(){
syscall(60,0);
}
现在我们先静态编译,然后动态编译。该-Wl,-emain
参数告诉 gcc 传递-emain
给链接器,这意味着设置main
为入口点。果然程序立即退出。
me@bar:~/foo$ gcc -static main.c -Wl,-emain -o main
me@bar:~/foo$ strace ./main 2>&1 | head
execve("./main", ["./main"], 0x7fff6d7c4b40 /* 50 vars */) = 0
exit(0) = ?
+++ exited with 0 +++
现在没有-static
标志,我们得到你通常看到的东西。
me@bar:~/foo$ gcc main.c -Wl,-emain -o main
me@bar:~/foo$ strace ./main 2>&1 | head
execve("./main", ["./main"], 0x7fff06b48d20 /* 50 vars */) = 0
brk(NULL) = 0x558c61a90000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffd68e8e950) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=77239, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 77239, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe55ed7e000
...
即使代码是直接跳转到main
!
现在我认为这可能是 libc 特有的,也许它需要自行设置或类似的东西。但实际上不是,我尝试制作一个小动态库来链接,然后在 strace 上显示以下内容:
$ ldd main
linux-vdso.so.1 (0x00007fff9c8cd000)
libfoo.so => /home/me/path/to/libfoo.so (0x00007f80ed20a000)
$ strace ./main
execve("./main", ["./main"], 0x7ffcdf5b5a70 /* 51 vars */) = 0
brk(NULL)
...
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/home/me/path/to/libfoo.so", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=13240, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f028541c000
mmap(NULL, 16384, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f0285418000
mmap(0x7f0285419000, 4096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1000) = 0x7f0285419000
mmap(0x7f028541a000, 4096, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f028541a000
mmap(0x7f028541b000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7f028541b000
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7f028541d040) = 0
set_tid_address(0x7f028541d310) = 6310
set_robust_list(0x7f028541d320, 24) = 0
rseq(0x7f028541d960, 0x20, 0, 0x53053053) = 0
mprotect(0x7f028541b000, 4096, PROT_READ) = 0
mprotect(0x402000, 4096, PROT_READ) = 0
exit(0) = ?
+++ exited with 0 +++
我不确定大多数调用在做什么,但很明显它们只是将我的小函数libfoo
(实际上是一个仅执行退出系统调用的函数)映射到内存中。因此,这些跟踪显然来自动态链接器,将动态库映射到内存(在文件描述符 3 上打开它之后),然后将执行移交给程序本身(在本例中只是立即退出)。
现在,我猜想brk
调用是因为动态链接器需要计算出程序在运行时将具有的内存布局,因此它需要对该值进行一些数学计算。 Linux 是自由软件,这可能在动态链接器的源代码中有所解释。希望这可以帮助。