sys_clone调用后的汇编代码为何影响克隆进程运行?
x86 Intel无前缀汇编线程实现故障排查与修复
问题描述
我正在用x86 Intel无前缀汇编编写编译器,实现线程时遇到两个问题:
- 执行第一段代码时,新线程能正常运行但会触发段错误;
- 如果在这段代码后添加第二段保存返回值的代码,新线程完全无法运行。
第一段触发段错误的代码
call create_thread_stack push rax # Push f onto new stack, followed by thread_wrapper pop rsi # New thread should start in thread_wrapper when cloned sub rsi, 8 lea rdi, [rip + f] push rdi pop [rsi] sub rsi, 8 lea rdi, [rip + thread_wrapper] push rdi pop [rsi] mov rdi, -2147479808 # CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_IO mov rax, 56 # Sys_clone syscall
导致新线程无法运行的追加代码
sub rsp, 4 mov dword ptr [rsp], eax mov eax, dword ptr [rsp] add rsp, 4 mov dword ptr [rbp + -4], eax mov rax, 0 mov rsp, rbp pop rbp ret
完整原始代码
.intel_syntax noprefix .globl main .section .rodata .text f: # prints 1 and returns 0 push rbp mov rbp, rsp mov eax, 1 sub rsp, 4 mov dword ptr [rsp], eax mov edi, dword ptr [rsp] add rsp, 4 movsx rdi, edi call _printi mov eax, 0 sub rsp, 4 mov dword ptr [rsp], eax mov eax, dword ptr [rsp] add rsp, 4 movsx rax, eax mov rsp, rbp pop rbp ret main: push rbp mov rbp, rsp sub rsp, 4 mov dword ptr [rsp], eax call create_thread_stack push rax # Push f onto new stack, followed by thread_wrapper pop rsi # New thread should start in thread_wrapper when cloned sub rsi, 8 lea rdi, [rip + f] push rdi pop [rsi] sub rsi, 8 lea rdi, [rip + thread_wrapper] push rdi pop [rsi] mov rdi, -2147479808 # CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_IO mov rax, 56 # Sys_clone syscall sub rsp, 4 mov dword ptr [rsp], eax mov eax, dword ptr [rsp] add rsp, 4 mov dword ptr [rbp + -4], eax mov rax, 0 mov rsp, rbp pop rbp ret .section .rodata .int 2 .L._printi_str0: .asciz "%d" .text _printi: push rbp mov rbp, rsp and rsp, -16 mov esi, edi lea rdi, [rip + .L._printi_str0] mov al, 0 call printf@plt mov rdi, 0 call fflush@plt mov rsp, rbp pop rbp ret create_thread_stack: # Creates stack for new thread mov rdi, 0 mov rsi, 4194304 mov rdx, 3 mov r10, 290 mov r9, 0 mov r8, -1 mov rax, 9 syscall lea rax, [rax + 4194304] ret thread_exit: mov rdi, 0 mov rax, 60 syscall thread_wrapper: # Runs label below thread_wrapper on the new stack pop rax call rax call thread_exit
核心错误分析
- 对clone系统调用逻辑的误解:子线程不会自动从构造的栈地址启动,而是和父进程一样从
syscall的下一条指令继续执行,需手动判断返回值引导子线程进入目标入口。 - clone参数传递不完整:未设置
parent_tidptr、child_tidptr等参数,垃圾值可能导致异常;同时缺少CLONE_THREAD标记,子线程会被当作独立进程而非线程。 - 父进程提前退出:追加代码让main函数直接返回,导致整个进程终止,子线程还未执行就被杀死。
- 栈构造与使用不匹配:即使子线程跳转到
thread_wrapper,也未切换到构造的新栈,仍使用父进程栈执行。
修复步骤
1. 正确传递clone参数并区分父子线程
clone系统调用的x86_64参数顺序为:flags(rdi)、child_stack(rsi)、parent_tidptr(rdx)、child_tidptr(r10)、tls(r8)。需补充参数并添加CLONE_THREAD标记,同时在syscall后判断返回值,引导子线程进入目标入口。
2. 让父进程等待子线程完成
使用waitpid系统调用(rax=61),避免父进程提前退出导致子线程被终止。
3. 修正栈构造逻辑
确保子线程使用新栈时,栈顶内容符合thread_wrapper的执行预期:栈顶放置f的地址,让thread_wrapper弹出后调用。
修复后的完整代码
.intel_syntax noprefix .globl main .section .rodata .text f: # prints 1 and returns 0 push rbp mov rbp, rsp mov eax, 1 sub rsp, 4 mov dword ptr [rsp], eax mov edi, dword ptr [rsp] add rsp, 4 movsx rdi, edi call _printi mov eax, 0 sub rsp, 4 mov dword ptr [rsp], eax mov eax, dword ptr [rsp] add rsp, 4 movsx rax, eax mov rsp, rbp pop rbp ret main: push rbp mov rbp, rsp sub rsp, 4 mov dword ptr [rsp], eax call create_thread_stack # 构造新栈:栈顶放置f的地址,供thread_wrapper弹出调用 mov rsi, rax sub rsi, 8 lea rdi, [rip + f] mov [rsi], rdi # 设置clone参数 mov rdi, -2147479808 | 0x10000 # 加上CLONE_THREAD标记 mov rsi, rsi # 子线程栈指针 mov rdx, 0 # parent_tidptr设为0 mov r10, 0 # child_tidptr设为0 mov r8, 0 # tls设为0 mov rax, 56 # Sys_clone syscall # 区分父子线程 cmp eax, 0 je child_thread_entry # 父进程逻辑:保存子线程PID并等待其结束 mov dword ptr [rbp-4], eax mov rdi, eax # 子线程PID mov rsi, 0 # status指针设为0 mov rdx, 0 # options设为0 mov rax, 61 # waitpid syscall syscall # 父进程退出 mov rax, 0 mov rsp, rbp pop rbp ret child_thread_entry: # 子线程跳转到thread_wrapper执行 jmp thread_wrapper .section .rodata .int 2 .L._printi_str0: .asciz "%d" .text _printi: push rbp mov rbp, rsp and rsp, -16 mov esi, edi lea rdi, [rip + .L._printi_str0] mov al, 0 call printf@plt mov rdi, 0 call fflush@plt mov rsp, rbp pop rbp ret create_thread_stack: # Creates stack for new thread mov rdi, 0 mov rsi, 4194304 mov rdx, 3 # PROT_READ | PROT_WRITE mov r10, 290 # MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN mov r9, 0 mov r8, -1 mov rax, 9 # mmap syscall syscall lea rax, [rax + 4194304] # 返回栈顶地址(栈向下增长) ret thread_exit: mov rdi, 0 mov rax, 231 # 使用exit_group系统调用,更适合线程退出 syscall thread_wrapper: # Runs label below thread_wrapper on the new stack pop rax call rax call thread_exit
额外优化点
线程退出建议使用exit_group系统调用(rax=231),而非exit,能确保整个线程组的资源被正确清理。
内容的提问来源于stack exchange,提问作者FlappyToucan
相关产品推荐
相关产品推荐

