未调用汇编克隆系统调用线程函数

问题描述 投票:0回答:1

我正在尝试使用'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也是正确的!!!!!!!!!!所以应该没问题!

linux assembly x86-64 system-calls fasm
1个回答
3
投票

我们已经在评论last time you asked中对此进行了回答。原始的clone系统调用不会为您从内存中读取函数指针。

必须自己处理在子线程/进程中运行的代码。相反,您让两个线程继续运行wait4munmapexit

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_tidgettid来使__WCLONE等待__WALL个孩子。

阅读您使用的系统调用的手册页,特别是当strace显示它们没有达到您的预期效果时。这是调试/问题解决技术中的第2步,确定系统调用返回了错误。第一名(wait4)。

© www.soinside.com 2019 - 2024. All rights reserved.