c - 当您调用克隆系统调用时,谁设置了 RIP 寄存器?

标签 c binary operating-system system-calls glibc

我正在尝试实现最小内核,并且正在尝试实现克隆系统调用。在手册页中,您可以看到克隆系统调用定义如下:

int clone(int (*fn)(void *), void *stack, int flags, void *arg, ...
                 /* pid_t *parent_tid, void *tls, pid_t *child_tid */ );
如您所见,它接收一个函数指针。如果您更仔细地阅读手册页,您实际上可以看到内核中的实际系统调用实现没有收到函数指针:
long clone(unsigned long flags, void *stack,
                      int *parent_tid, int *child_tid,
                      unsigned long tls);
所以,我的问题是,谁在创建线程后修改 RIP 寄存器?是libc吗?
我在 glibc 中找到了这段代码:https://elixir.bootlin.com/glibc/latest/source/sysdeps/unix/sysv/linux/x86_64/clone.S但我不确定该函数在什么时候被实际调用。
额外的信息:
查看 clone.S 源代码时,您可以看到它在系统调用之后跳转到 thread_start 分支。在克隆系统调用之后的分支上(所以只有 child 这样做)它从堆栈中弹出函数地址和参数。谁真正将这些参数和函数地址压入堆栈?我想它必须发生在内核的某个地方,因为在 syscall指示他们不在那里。
这是一些 gdb 输出:
在系统调用之前:
[-------------------------------------code-------------------------------------]
   0x7ffff7d8af22 <clone+34>:   mov    r8,r9
   0x7ffff7d8af25 <clone+37>:   mov    r10,QWORD PTR [rsp+0x8]
   0x7ffff7d8af2a <clone+42>:   mov    eax,0x38
=> 0x7ffff7d8af2f <clone+47>:   syscall 
   0x7ffff7d8af31 <clone+49>:   test   rax,rax
   0x7ffff7d8af34 <clone+52>:   jl     0x7ffff7d8af49 <clone+73>
   0x7ffff7d8af36 <clone+54>:   je     0x7ffff7d8af39 <clone+57>
   0x7ffff7d8af38 <clone+56>:   ret
Guessed arguments:
arg[0]: 0x3d0f00 
arg[1]: 0x7ffff8020b60 --> 0x7ffff7d3fb30 (<do_something>:  push   rbx)
arg[2]: 0x7fffffffda90 --> 0x0 
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffda78 --> 0x7ffff7d3f52c (<main+172>:    pop    rsi)
0008| 0x7fffffffda80 --> 0x7fffffffda94 --> 0x73658b0000000000 
0016| 0x7fffffffda88 --> 0x7fffffffda94 --> 0x73658b0000000000 
0024| 0x7fffffffda90 --> 0x0 
0032| 0x7fffffffda98 --> 0x492e085573658b00 
0040| 0x7fffffffdaa0 --> 0x7ffff7d3f0d0 (<_init>:   sub    rsp,0x8)
0048| 0x7fffffffdaa8 --> 0x7ffff7d40830 (<__libc_csu_init>: push   r15)
0056| 0x7fffffffdab0 --> 0x7ffff7d408d0 (<__libc_csu_fini>: push   rbp)
[------------------------------------------------------------------------------]
在子线程上的系统调用指令之后(检查堆栈顶部 - 这不会发生在父线程上):
[-------------------------------------code-------------------------------------]
   0x7ffff7d8af25 <clone+37>:   mov    r10,QWORD PTR [rsp+0x8]
   0x7ffff7d8af2a <clone+42>:   mov    eax,0x38
   0x7ffff7d8af2f <clone+47>:   syscall 
=> 0x7ffff7d8af31 <clone+49>:   test   rax,rax
   0x7ffff7d8af34 <clone+52>:   jl     0x7ffff7d8af49 <clone+73>
   0x7ffff7d8af36 <clone+54>:   je     0x7ffff7d8af39 <clone+57>
   0x7ffff7d8af38 <clone+56>:   ret    
   0x7ffff7d8af39 <clone+57>:   xor    ebp,ebp
[------------------------------------stack-------------------------------------]
0000| 0x7ffff8020b60 --> 0x7ffff7d3fb30 (<do_something>:    push   rbx)
0008| 0x7ffff8020b68 --> 0x7ffff7dd5add --> 0x4c414d0074736574 ('test')
0016| 0x7ffff8020b70 --> 0x0 
0024| 0x7ffff8020b78 --> 0x411 
0032| 0x7ffff8020b80 ("Parameters: 0x7ffff7d3fb30 4001536 0x7ffff8020b70 0x7fffffffda90 0x7ffff8000b60 0x7fffffffda94\n")
0040| 0x7ffff8020b88 ("rs: 0x7ffff7d3fb30 4001536 0x7ffff8020b70 0x7fffffffda90 0x7ffff8000b60 0x7fffffffda94\n")
0048| 0x7ffff8020b90 ("fff7d3fb30 4001536 0x7ffff8020b70 0x7fffffffda90 0x7ffff8000b60 0x7fffffffda94\n")
0056| 0x7ffff8020b98 ("30 4001536 0x7ffff8020b70 0x7fffffffda90 0x7ffff8000b60 0x7fffffffda94\n")
[------------------------------------------------------------------------------]

最佳答案

是的,libc;内核接口(interface)类似于 fork : 它返回两次到同一个地方,但返回值不同。 (0 在子级或父级中的 PID/TID)。 The man page记录 glibc 包装器与内核的差异,就像其他有差异的系统调用一样。
libc 包装器将函数指针和参数存储在新线程的堆栈空间中,新线程可以在其中加载它。 (内核启动它时将其 RSP 设置为 void *stack arg 传递给 clone() ,因此它无法访问堆栈内存或寄存器中的旧局部变量,并且如果使用多个全局变量,则使用全局变量将不是线程安全的线程同时克隆自己。)
请注意,还有一个 clone3采用结构 arg 的系统调用,也更像 clone 的原始内核接口(interface). (或者至少没有 glibc 包装器。)

关于c - 当您调用克隆系统调用时,谁设置了 RIP 寄存器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66853510/

相关文章:

c - 获取以太网端口的 ioctl 文件描述符

c - wrt54g路由表/ip表

c - 需要关于如何实现这个的帮助..选择一个最好的数据结构

c# - 将字符串转换为二进制零和一

php - C++ 从 http 下载二进制文件

C: 声明数组时访问冲突

Python:将 numpy 符号数组转换为 int 并返回

c - 操作系统 : wait() in child process

windows - Windows、Mac OS X 和 Linux 是用什么语言编写的?

java - 为什么 java 也是系统/操作系统名称(python 中的帮助文档)?