【发布时间】:2020-03-12 15:16:43
【问题描述】:
我正在尝试使用“克隆”系统调用创建一个线程......我搜索了太多!
例如,
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;
}
这个strace是:
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 中使用 clone 和 wait 函数,我们将有 'wait4' 系统调用......甚至在我的 'wait' 系统调用中,子 ID 是正确的 !!!!!!!!!!!!所以应该没问题!
C 克隆示例
#define _GNU_SOURCE
#include <sched.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <wait.h>
#define MEM_SIZE 1024
#define CLONE_FLAGS (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_PARENT | CLONE_THREAD | CLONE_IO)
int
thread_func(void * data) {
static const char msg[] = "Hello from Child process\n";
write(0, msg, sizeof(msg)-1);
exit(0);
}
int
main() {
static const char msg[] = "Child process is terminated\n";
void * memory;
if((memory = mmap(NULL, MEM_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0)) == MAP_FAILED) {
printf("memory allocation failed\n");
return 1;
}
int pid = clone(thread_func, (memory + MEM_SIZE), CLONE_FLAGS, NULL);
if(pid < 0) {
munmap(memory, MEM_SIZE);
printf("clone() failed\n");
return 1;
}
waitpid(pid, NULL, 0);
write(0, msg, sizeof(msg)-1);
munmap(memory, MEM_SIZE);
exit(0);
}
有点奇怪!!!!同样的错误(段...)!!!! 即使在 C 示例中,我也会遇到相同的错误!!!!
这是 strace :
mprotect(0x7fd8b4492000, 12288, PROT_READ) = 0
mprotect(0x403000, 4096, PROT_READ) = 0
mprotect(0x7fd8b44e1000, 4096, PROT_READ) = 0
munmap(0x7fd8b449e000, 98201) = 0
mmap(NULL, 1024, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0) = 0x7fd8b44e0000
clone(child_stack=0x7fd8b44e03f0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_PARENT|CLONE_THREAD|CLONE_IOstrace: Process 19911 attached
) = 19911
[pid 19911] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_ACCERR, si_addr=0x7fd8b44df9c0} ---
[pid 19910] wait4(19911, <unfinished ...>) = ?
[pid 19911] +++ killed by SIGSEGV (core dumped) +++
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)
【问题讨论】:
-
我们上次已经在 cmets 中回答了这个问题。原始的
clone系统调用不会从内存中读取函数指针。 您必须自己使用在子线程/进程中运行的代码来执行此操作。相反,您让两个线程都运行wait4、munmap和exit。 -
你之前的问题去哪儿了?你把它和回答它的 cmets 一起删除了吗?
-
不,它没有被删除....但是如果您查看链接,他们将线程函数地址放在堆栈顶部,并且没有直接调用线程函数!!!其实它接缝clone调用函数!
-
是的,glibc 的
clone包装器确实保存了一个函数指针并在子线程中调用它。 -
哪个指令段错误?你确定你的
wait4系统调用是正确的吗?如果它在没有等待的情况下立即返回,与ret竞争的 munmap 将解释段错误并获得两个订单。使用strace调试您的代码。
标签: linux assembly x86-64 system-calls fasm