Не вызывается функция потока syscall сборки клона - PullRequest
1 голос
/ 12 марта 2020

Я пытаюсь создать тему, используя системный вызов 'clone' ... Я много раз искал toooooooo! например,
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_fun c' теперь вызывается, но у меня есть новая проблема! я получаю отказ сегмента !!!! я думаю, что это о моих 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

this это C исходный код с pthread:

#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' идентификатор ребенка указан правильно !!!!!!!!!! так что проблем не должно быть!

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)

1 Ответ

3 голосов
/ 12 марта 2020

Мы уже ответили на это в комментариях последний раз, когда вы спрашивали . Необработанный системный вызов clone не считывает указатель функции из памяти.

Вы должны сделать это самостоятельно с помощью кода, который выполняется в дочернем потоке / процессе. Вместо этого оба потока продолжают работать wait4, munmap и exit.

Справочная страница clone(2) объясняет это. Основная часть страницы описывает оболочку glib c, которая принимает указатель на функцию для вызова в дочернем потоке. Но в нем четко сказано, что это , а не необработанный системный вызов, и, чтобы увидеть раздел NOTES. Там вы найдете прототип и документацию необработанного системного вызова asm:

long raw_clone(unsigned long flags, void *stack,
                        int *parent_tid, int *child_tid,
                        unsigned long tls);

Системный вызов raw clone() более точно соответствует fork(2) в этом выполнении в дочернем процессе. от точки вызова. Таким образом, аргументы fn и arg функции-оболочки clone() опущены.

Вы можете использовать новый стек как удобное место для хранения sh указатель на функцию, где ваш код пользовательского пространства для нового потока может найти его. (Новый поток не будет иметь легкого доступа к стеку основного потока, потому что RSP будет указывать на его новый стек; я не уверен, что регистры, отличные от RAX, обнуляются перед входом в новый поток или нет. Если нет, вы можете легко просто держите указатель в регистре, отличном от RAX, RCX или R11. И, конечно, доступно хранилище stati c, но вам не нужно его использовать.)

Вы будете хотите перейти на возвращаемое значение 0, которое говорит вам, что вы находитесь в дочернем процессе . (Как и в случае с fork, клон возвращается дважды при успешном выполнении: один раз в родительском с TID, один раз в дочернем с 0. Я думаю, что это правда; справочная страница явно не документирует эту часть, но так работает вилка)


Как обсуждалось в комментариях, link2 хранит адрес функции в стеке дочерних потоков. Когда родитель возвращается из функции-оболочки, он возвращается нормально. Когда дочерний элемент возвращается, он извлекает этот адрес из того, что сейчас является его стеком.

Вы решили реализовать это с ret, который работает только в дочернем элементе; хорошо. Вы могли просто использовать jmp с указателем в регистре или памяти.


re: обновленный вопрос:

Ваш wait4 системный вызов возвращает -1 ECHILD без фактического ожидания.

Поэтому ваши ret участвуют в гонке с munmap, что приведет к отмене отображения стека потоков, что приведет к ошибке по умолчанию, если сначала произойдет munmap. Это также объясняет, что ваш вывод происходит в разных порядках, когда он не равен sh.

Я точно не знаю, каково правильное решение, но это, очевидно, не так. Посмотрите, что pthread_join использует для ожидания выхода дочернего потока. Возможно, возвращаемое значение clone не совсем подходит для использования с wait4, или wait4 не является правильным системным вызовом.

(Выходной указатель int *child_tid предположительно существует для причина, хотя, возможно, именно так и родитель, и ребенок могут получить его без системного вызова gettid или вызова VDSO.)

Или, может быть, это потому, что вы не передали __WCLONE или __WALL для получения wait4 для ожидания clone детей.

Прочитайте справочные страницы для системных вызовов, которые вы используете, особенно когда strace показывает, что они не сделали того, что вы ожидали. Это шаг 2 в методика отладки / решения проблем, после определения того, что системный вызов возвратил ошибку в первую очередь (с strace).

...