我正在尝试使用'clone'syscall创建线程...我搜索得太多了!例如,link1 link2
现在这是我在Linux x64汇编中的源代码:
FORMAT ELF64 EXECUTABLE
ENTRY thread_linux_x64
THREAD_MEM_SIZE = 1024
define PROT_READ 0x1
define PROT_WRITE 0x2
define PROT_EXEC 0x4
define MAP_PRIVATE 0x02
define MAP_ANONYMOUS 0x20
define CLONE_VM 0x00000100
define CLONE_FS 0x00000200
define CLONE_FILES 0x00000400
define CLONE_SIGHAND 0x00000800
define CLONE_PARENT 0x00008000
define CLONE_THREAD 0x00010000
define CLONE_IO 0x80000000
define SIGCHLD 20
CLONE_FLAGS = CLONE_VM OR CLONE_FS OR CLONE_FILES OR CLONE_SIGHAND OR CLONE_PARENT OR CLONE_THREAD OR CLONE_IO
MMAP_FLAG = MAP_PRIVATE OR MAP_ANONYMOUS
MMAP_PERMISSION = PROT_READ OR PROT_WRITE OR PROT_EXEC
SEGMENT READABLE EXECUTABLE
thread_linux_x64:
; Memory allocation using 'mmap' syscall
mov eax, 9 ; sys_mmap
xor edi, edi ; addr = null (0)
mov esi, THREAD_MEM_SIZE ; Memory size
mov edx, MMAP_PERMISSION ; Permission
mov r10d, MMAP_FLAG ; Flag
mov r8d, -1 ; Fd = -1 (invalid fd)
xor r9d, r9d ; Offset = 0
syscall
cmp rax, 0 ; error ?
jl .error_mmap
mov r13, rax ; r13 = memory address
; create a new child process (thread) using 'clone' syscall
mov eax, 56 ; sys_clone
mov edi, CLONE_FLAGS ; flags
lea rsi, [r13 + THREAD_MEM_SIZE - 8] ; stack address - 8 (8-BYTE to store the function address)
mov QWORD [rsi], thread_func ; set function address
xor edx, edx ; parent_tid = NULL (0)
xor r10d, r10d ; child_tid = NULL (0)
xor r8d, r8d ; tid = 0
syscall
cmp rax, 0 ; error ?
jle .error_clone
; wait for the created thread to exit using 'wait4' syscall
mov rdi, rax ; created-thread pid
mov eax, 61 ; sys_wait4
xor esi, esi ; stat_addr = null (0)
xor edx, edx ; options = 0
xor r10d, r10d ; rusage = 0
syscall
; free the allocated memory (r13) using 'munmap' syscall
mov eax, 11 ; sys_munmap
mov rdi, r13 ; memory address
mov esi, THREAD_MEM_SIZE ; memory size
syscall
; exit (return 0 (success))
mov eax, 60 ; sys_exit
xor edi, edi ; return 0
syscall
.error_mmap:
; set error message to print
mov rsi, .mmap_failed_msg ; error message
mov edx, .mmap_failed_msg_len ; error message length
jmp short .error
.error_clone:
; free the allocated memory (r13) using 'munmap' syscall
mov eax, 11 ; sys_munmap
mov rdi, r13 ; memory address
mov esi, THREAD_MEM_SIZE ; memory size
syscall
.error:
; print error message
mov eax, 1 ; sys_write
xor edi, edi ; stdout (0)
syscall
; exit (return 1 (error))
mov eax, 60 ; sys_exit
mov edi, 1 ; return 1
syscall
.mmap_failed_msg db 'Memory allocation failed', 0x0a, 0x00
.mmap_failed_msg_len = $ - .mmap_failed_msg
.clone_failed_msg db 'Unable to create a new child process', 0x0a, 0x00
.clone_failed_msg_len = $ - .clone_failed_msg
thread_func:
; print message
mov eax, 1 ; sys_write
xor edi, edi ; stdout (0)
mov rsi, .message ; message address
mov edx, .message_len ; message length
syscall
; exit (return 0 (success))
mov eax, 60 ; sys_exit
xor edi, edi ; return 0
syscall
.message db 'Child process is called', 0x0a, 0x00
.message_len = $ - .message
一切看起来都很正常!!!!但是当我运行该程序时,我什么也没得到!没有“称为子进程”的消息打印!实际上,我认为我的线程功能未运行...我也得到了strace测试,这就是结果!trace -f ./thread_linux_x64
execve("./thread_linux_x64", ["./thread_linux_x64"], 0x7fffd4db1b58 /* 53 vars */) = 0
mmap(NULL, 1024, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f32ba3e4000
clone(child_stack=0x7f32ba3e43f8, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_PARENT|CLONE_THREAD|CLONE_IOstrace: Process 32064 attached
) = 32064
[pid 32064] munmap(0x7f32ba3e4000, 1024 <unfinished ...>
[pid 32063] wait4(32064, NULL, 0, NULL) = -1 ECHILD (No child processes)
[pid 32064] <... munmap resumed>) = 0
[pid 32063] munmap(0x7f32ba3e4000, 1024 <unfinished ...>
[pid 32064] write(0, "", 0 <unfinished ...>
[pid 32063] <... munmap resumed>) = 0
[pid 32063] exit(0 <unfinished ...>
[pid 32064] <... write resumed>) = 0
[pid 32063] <... exit resumed>) = ?
[pid 32064] exit(1) = ?
[pid 32064] +++ exited with 1 +++
+++ exited with 0 +++
这个问题使我发疯!因为没有错误...而且一切看起来都很好!!!!
更新:
这里我更改了源代码以创建线程,而无需调用thread_create或...函数(在主函数中)现在我的问题已解决...实际上,现在调用了“ thread_func”,但是我遇到了一个新问题!我遇到了细分失败!!!我认为这与我的CLONE_FLAGS有关!!!!
FORMAT ELF64 EXECUTABLE
ENTRY thread_linux_x64
THREAD_MEM_SIZE = 1024
define PROT_READ 0x1
define PROT_WRITE 0x2
define PROT_EXEC 0x4
define MAP_PRIVATE 0x02
define MAP_ANONYMOUS 0x20
define CLONE_VM 0x00000100
define CLONE_FS 0x00000200
define CLONE_FILES 0x00000400
define CLONE_SIGHAND 0x00000800
define CLONE_PARENT 0x00008000
define CLONE_THREAD 0x00010000
define CLONE_IO 0x80000000
CLONE_FLAGS = CLONE_VM OR CLONE_FS OR CLONE_FILES OR CLONE_SIGHAND OR CLONE_PARENT OR CLONE_THREAD OR CLONE_IO
MMAP_FLAG = MAP_PRIVATE OR MAP_ANONYMOUS
MMAP_PERMISSION = PROT_READ OR PROT_WRITE OR PROT_EXEC
SEGMENT READABLE EXECUTABLE
thread_linux_x64:
; Memory allocation using 'mmap' syscall (sys_mmap (9))
mov eax, 9 ; sys_mmap
xor edi, edi ; addr = 0 (NULL)
mov esi, THREAD_MEM_SIZE ; Memory allocation size
mov edx, MMAP_PERMISSION ; Permission (PROT_READ, ...)
mov r10d, MMAP_FLAG ; Flag (MAP_PRIVATE, ...)
mov r8d, -1 ; File descriptor (Fd) = -1 (invalid File descriptor)
xor r9d, r9d ; Offset = 0
syscall
test rax, rax ; ERROR ?
jl .error_mmap
mov r13, rax ; R13 = Memory address (RAX)
; Create a new child process (thread) using 'clone' syscall (sys_clone (56))
mov eax, 56 ; sys_clone
mov edi, CLONE_FLAGS ; Flag (CLONE_VM, ...)
lea rsi, [r13 + THREAD_MEM_SIZE - 16] ; End of the stack - 16 (8-BYTE to store the function address and 8-BYTE to store the data (parameter) address)
mov qword [rsi], thread_func ; Set thread function
mov qword [rsi+8], 0 ; No data (parameter = NULL)
xor edx, edx ; * parent_tid = NULL (0)
xor r10d, r10d ; * child_tid = NULL (0)
xor r8d, r8d ; tid = 0
syscall
test rax, rax ; pid == 0 ? | pid < 0 ?
jg short .parent_continue ; parent !
jl .error_clone ; ERROR !
; *** CHILD PROCESS ***
ret ; by using the 'ret' instruction, we called the requested function (thread)
; because we moved the function address into the stack of child process and
; by using the 'ret' instruction, we jump to the thread function (thread_func)
.parent_continue:
; Wait for the created thread to exit using 'wait4' syscall (sys_wait4 (61))
mov rdi, rax ; TID (Thread id)
mov eax, 61 ; sys_wait4
xor esi, esi
xor edx, edx
xor r10d, r10d
syscall
; Free the memory (R13) using 'munmap' syscall (sys_munmap (11))
mov eax, 11 ; sys_munmap
mov rdi, r13 ; Memory address (R13)
mov esi, THREAD_MEM_SIZE ; Memory size
syscall
; Write 'done' message
mov eax, 1 ; sys_write
xor edi, edi ; STDOUT (0)
mov rsi, .message ; Message address
mov edx, .message_len ; Message length
syscall
; exit (return 0)
mov eax, 60 ; sys_exit
xor edi, edi ; return 0
syscall
.error_mmap:
; Set error message to write it to STDOUT
mov rsi, .mmap_failed_msg ; Error message
mov edx, .mmap_failed_msg_len ; Error message length
jmp short .error
.error_clone:
; Free the memory (R13) using 'munmap' syscall (sys_munmap (11))
mov eax, 11 ; sys_munmap
mov rdi, r13 ; Memory address (R13)
mov esi, THREAD_MEM_SIZE ; Memory size
syscall
; Set error message to write it to STDOUT
mov rsi, .clone_failed_msg ; Error message
mov edx, .clone_failed_msg_len ; Error message length
.error:
; Write error message to STDOUT
mov eax, 1 ; sys_write
xor edi, edi ; STDOUT (0)
syscall
; exit (return 1 (error))
mov eax, 60 ; sys_exit
mov edi, 1 ; return 1
syscall
.message db 'Child process is terminated', 0x0a, 0x00
.message_len = $ - .message
.mmap_failed_msg db 'Memory allocation failed', 0x0a, 0x00
.mmap_failed_msg_len = $ - .mmap_failed_msg
.clone_failed_msg db 'Unable to create a new child process', 0x0a, 0x00
.clone_failed_msg_len = $ - .clone_failed_msg
thread_func:
; Write message from child process
mov eax, 1 ; sys_write
xor edi, edi ; STDOUT (0)
mov rsi, .message ; Message address
mov edx, .message_len ; Message length
syscall
; exit (return 0)
mov eax, 60 ; sys_exit
xor edi, edi ; return 0
syscall
.message db 'Child process is called', 0x0a, 0x00
.message_len = $ - .message
这里,一切看起来不错!但这是函数结果->子进程终止分段错误(核心已转储)
但是有时候我也能得到这个!子进程称为子进程终止
有时我也会得到这个表情!!!!!!!子进程终止子进程称为
但是100%出现问题,因为“细分错误” !!!!有什么问题?
strace
execve("./thread_linux_x64", ["./thread_linux_x64"], 0x7fff7cc37508 /* 53 vars */) = 0
mmap(NULL, 1024, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1f8b97b000
clone(child_stack=0x7f1f8b97b3f0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_PARENT|CLONE_THREAD|CLONE_IOstrace: Process 3131 attached
) = 3131
[pid 3131] write(0, "Child process is called\n\0", 25 <unfinished ...>
Child process is called
[pid 3130] wait4(3131, <unfinished ...>
[pid 3131] <... write resumed>) = 25
[pid 3130] <... wait4 resumed>NULL, 0, NULL) = -1 ECHILD (No child processes)
[pid 3131] exit(0 <unfinished ...>
[pid 3130] munmap(0x7f1f8b97b000, 1024 <unfinished ...>
[pid 3131] <... exit resumed>) = ?
[pid 3130] <... munmap resumed>) = 0
[pid 3131] +++ exited with 0 +++
write(0, "Child process is terminated\n\0", 29Child process is terminated
) = 29
exit(0) = ?
+++ exited with 0 +++
具有C-PTHREAD的示例
这是带有pthread的C源代码:
#include <stdio.h>
#include <pthread.h>
#include <bits/signum.h>
void * thread_func(void * arg) {
const char msg[] = "Child-> HELLO\n";
asm volatile ("syscall"
:: "a" (1), "D" (0), "S" (msg), "d" (sizeof(msg) - 1)
: "rcx", "r11", "memory");
return 0;
}
int
main() {
pthread_t pthread;
const char msg1[] = "Parent-> HELLO\n";
const char msg2[] = "Parent-> BYE\n";
asm volatile ("syscall"
:: "a" (1), "D" (0), "S" (msg1), "d" (sizeof(msg1) - 1)
: "rcx", "r11", "memory");
pthread_create(& pthread, NULL, thread_func, NULL);
pthread_join(pthread, NULL);
asm volatile ("syscall"
:: "a" (1), "D" (0), "S" (msg2), "d" (sizeof(msg2) - 1)
: "rcx", "r11", "memory");
return 0;
}
并且此跟踪为:
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8c296d3000
arch_prctl(ARCH_SET_FS, 0x7f8c296d3740) = 0
mprotect(0x7f8c29895000, 12288, PROT_READ) = 0
mprotect(0x7f8c298bb000, 4096, PROT_READ) = 0
mprotect(0x403000, 4096, PROT_READ) = 0
mprotect(0x7f8c29906000, 4096, PROT_READ) = 0
munmap(0x7f8c298c3000, 98201) = 0
set_tid_address(0x7f8c296d3a10) = 10122
set_robust_list(0x7f8c296d3a20, 24) = 0
rt_sigaction(SIGRTMIN, {sa_handler=0x7f8c298a6c50, sa_mask=[], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x7f8c298b3b20}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {sa_handler=0x7f8c298a6cf0, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART|SA_SIGINFO, sa_restorer=0x7f8c298b3b20}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
write(0, "Parent-> HELLO\n", 15Parent-> HELLO
) = 15
mmap(NULL, 8392704, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f8c28ed2000
mprotect(0x7f8c28ed3000, 8388608, PROT_READ|PROT_WRITE) = 0
brk(NULL) = 0x13d2000
brk(0x13f3000) = 0x13f3000
brk(NULL) = 0x13f3000
clone(child_stack=0x7f8c296d1fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tid=[10123], tls=0x7f8c296d2700, child_tidptr=0x7f8c296d29d0) = 10123
futex(0x7f8c296d29d0, FUTEX_WAIT, 10123, NULLstrace: Process 10123 attached
<unfinished ...>
[pid 10123] set_robust_list(0x7f8c296d29e0, 24) = 0
[pid 10123] write(0, "Child-> HELLO\n", 14Child-> HELLO
) = 14
[pid 10123] madvise(0x7f8c28ed2000, 8368128, MADV_DONTNEED) = 0
[pid 10123] exit(0) = ?
[pid 10122] <... futex resumed>) = 0
[pid 10123] +++ exited with 0 +++
write(0, "Parent-> BYE\n", 13Parent-> BYE
) = 13
exit_group(0) = ?
+++ exited with 0 +++
如果我们在C语言中使用克隆和等待功能,我们将拥有'wait4'系统调用...甚至在我的'wait'系统调用中,子ID也是正确的!!!!!!!!!!所以应该没问题!
我们已经在评论last time you asked中对此进行了回答。原始的clone
系统调用不会为您从内存中读取函数指针。
您必须自己处理在子线程/进程中运行的代码。相反,您让两个线程继续运行wait4
,munmap
和exit
。
clone(2)
手册页对此进行了说明。该页面的主要部分介绍了glibc包装器,该包装器带有一个函数指针来调用子线程。但这显然是not原始系统调用,请参阅NOTES部分。在那里,您会找到原始的asm系统调用的原型和说明文件:
clone(2)
原始
long raw_clone(unsigned long flags, void *stack, int *parent_tid, int *child_tid, unsigned long tls);
系统调用与在子级中的执行将从调用点继续。 As这样,clone()
包装函数的fn和arg参数是省略。
您可以将新堆栈用作存放函数指针的方便位置,新线程的用户空间代码可以在其中找到它。 (新线程将无法轻松访问主线程的堆栈,因为RSP将指向其新堆栈;我不确定在进入新线程之前是否将RAX以外的寄存器清零。如果不是,您可以轻松地只需将指针保存在RAX,RCX或R11以外的寄存器中即可。当然可以使用静态存储,但您不必使用它。)
您将希望基于返回值fork(2)
进行分支,它表明您处于子进程中。 (就像fork一样,clone成功时会返回两次:一次在TID的父级中,一次在0的子级中。我认为是对的;手册页中并未明确记录这部分内容,但这就是fork的工作方式)
如注释中所述,link2将函数地址存储在子线程堆栈上。当父级从包装函数返回时,它将正常返回。当孩子返回时,它将从现在的堆栈中弹出that地址。
您选择使用仅在子代中运行的clone()
来实现此目标;没关系。您可能刚将0
与指针放在寄存器或存储器中。
re:更新后的问题:
您的ret
系统调用未实际等待就返回jmp
。
因此,您的wait4
与将取消映射线程堆栈的-1 ECHILD
竞争,如果首先发生ret
,则会导致段错误。这也说明了您的输出在不崩溃时会以不同的顺序发生。
我不确定确切的解决方案是什么,但是显然不是这样。看一下munmap
用于等待子线程退出的内容。也许munmap
返回值实际上不是与pthread_join
一起使用的正确选择,或者clone
不是正确的系统调用。
([wait4
输出指针可能存在是有原因的,尽管也许只有这样,父母和孩子都可以在没有wait4
系统调用或VDSO调用的情况下获取它。)
或者也许是因为您没有通过int *child_tid
或gettid
来使__WCLONE
等待__WALL
个孩子。
阅读您使用的系统调用的手册页,特别是当strace显示它们没有达到您的预期效果时。这是调试/问题解决技术中的第2步,确定系统调用返回了错误。第一名(wait4
)。