未调用程序集克隆系统调用线程函数 [英] Assembly clone syscall thread function not called

查看:41
本文介绍了未调用程序集克隆系统调用线程函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用克隆"系统调用创建一个线程……我搜索得太多了!例如,
link1
link2

现在这是我的 linux x64 汇编源代码:

格式化 ELF64 可执行文件进入 thread_linux_x64THREAD_MEM_SIZE = 1024定义 PROT_READ 0x1定义 PROT_WRITE 0x2定义 PROT_EXEC 0x4定义 MAP_PRIVATE 0x02定义 MAP_ANONYMOUS 0x20定义 CLONE_VM 0x00000100定义 CLONE_FS 0x00000200定义 CLONE_FILES 0x00000400定义 CLONE_SIGHAND 0x00000800定义 CLONE_PARENT 0x00008000定义 CLONE_THREAD 0x00010000定义 CLONE_IO 0x80000000定义 SIGCHLD 20CLONE_FLAGS = CLONE_VM OR CLONE_FS OR CLONE_FILES OR CLONE_SIGHAND OR CLONE_PARENT OR CLONE_THREAD OR CLONE_IOMMAP_FLAG = MAP_PRIVATE 或 MAP_ANONYMOUSMMAP_PERMISSION = PROT_READ OR PROT_WRITE OR PROT_EXEC段可读可执行thread_linux_x64:;使用mmap"系统调用进行内存分配移动 eax, 9 ;sys_mmap异或edi,edi;地址 = 空 (0)mov esi, THREAD_MEM_SIZE ;内存大小mov edx, MMAP_PERMISSION ;允许mov r10d, MMAP_FLAG ;旗帜mov r8d, -1 ;Fd = -1(无效的 fd)异或 r9d, r9d ;偏移 = 0系统调用cmp rax, 0 ;错误 ?jl .error_mmapmov r13, rax ;r13 = 内存地址;使用克隆"系统调用创建一个新的子进程(线程)移动 eax, 56 ;系统克隆mov edi, CLONE_FLAGS ;旗帜lea rsi, [r13 + THREAD_MEM_SIZE - 8] ;堆栈地址 - 8(8-BYTE 存储函数地址)mov QWORD [rsi], thread_func ;设置函数地址异或 edx, edx ;parent_tid = NULL (0)异或 r10d, r10d ;child_tid = NULL (0)异或 r8d, r8d ;时间 = 0系统调用cmp rax, 0 ;错误 ?jle .error_clone;使用wait4"系统调用等待创建的线程退出mov rdi, rax ;创建线程pid移动轴,61;sys_wait4异或 esi, esi ;stat_addr = null (0)异或 edx, edx ;选项 = 0异或 r10d, r10d ;使用率 = 0系统调用;使用munmap"系统调用释放分配的内存(r13)移动 eax, 11 ;sys_munmap移动 rdi, r13 ;内存地址mov esi, THREAD_MEM_SIZE ;内存大小系统调用;退出(返回 0(成功))mov eax, 60 ;系统退出异或edi,edi;返回 0系统调用.error_mmap:;设置要打印的错误消息mov rsi, .mmap_failed_msg ;错误信息mov edx, .mmap_failed_msg_len ;错误信息长度jmp 短.error.error_clone:;使用munmap"系统调用释放分配的内存(r13)移动 eax, 11 ;sys_munmap移动 rdi, r13 ;内存地址mov esi, THREAD_MEM_SIZE ;内存大小系统调用.错误:;打印错误信息移动 eax, 1 ;系统写异或edi,edi;标准输出 (0)系统调用;退出(返回 1(错误))移动 eax, 60 ;系统退出mov edi, 1 ;返回 1系统调用.mmap_failed_msg db '内存分配失败', 0x0a, 0x00.mmap_failed_msg_len = $ - .mmap_failed_msg.clone_failed_msg db '无法创建新的子进程',0x0a,0x00.clone_failed_msg_len = $ - .clone_failed_msg线程函数:;打印信息移动 eax, 1 ;系统写异或edi,edi;标准输出 (0)mov rsi, .message ;留言地址mov edx, .message_len ;消息长度系统调用;退出(返回 0(成功))mov eax, 60 ;系统退出异或edi,edi;返回 0系统调用.message db '子进程被调用', 0x0a, 0x00.message_len = $ - .message

一切看起来都很正常!!!!但是当我运行这个程序时,我什么也没得到!!!!没有调用子进程"消息打印!事实上,我认为我的线程函数没有运行......我也进行了 strace 测试,这就是结果!!!

trace -f ./thread_linux_x64

execve("./thread_linux_x64", ["./thread_linux_x64"], 0x7fffd4db1b58/* 53 vars */) = 0mmap(NULL, 1024, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f32ba3e4000克隆(child_stack=0x7f32ba3e43f8,flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_PARENT|CLONE_THREAD|CLONE_IOstrace:附加进程32064) = 32064[pid 32064] munmap(0x7f32ba3e4000, 1024 <unfinished ...>[pid 32063] wait4(32064, NULL, 0, NULL) = -1 ECHILD(无子进程)[pid 32064] <... munmap 恢复 >) = 0[pid 32063] munmap(0x7f32ba3e4000, 1024 <unfinished ...>[pid 32064] write(0, "", 0 <unfinished ...>[pid 32063] <... munmap 恢复 >) = 0[pid 32063] exit(0 <unfinished ...>[pid 32064] <...写恢复>) = 0[pid 32063] <...退出恢复>) = ?[pid 32064] exit(1) = ?[pid 32064] +++ 以 1 +++ 退出+++ 以 0 +++ 退出

这个问题快把我逼疯了!因为没有错误......一切看起来都很好!!!!

更新:

在这里我更改了我的源代码来创建线程而不调用 thread_create 或 ... 函数(在主函数中)现在我的问题解决了......事实上,'thread_func'现在被调用了,但我有一个新问题!我得到段失败!!!!我想这是关于我的 CLONE_FLAGS !!!!

格式化 ELF64 可执行文件进入 thread_linux_x64THREAD_MEM_SIZE = 1024定义 PROT_READ 0x1定义 PROT_WRITE 0x2定义 PROT_EXEC 0x4定义 MAP_PRIVATE 0x02定义 MAP_ANONYMOUS 0x20定义 CLONE_VM 0x00000100定义 CLONE_FS 0x00000200定义 CLONE_FILES 0x00000400定义 CLONE_SIGHAND 0x00000800定义 CLONE_PARENT 0x00008000定义 CLONE_THREAD 0x00010000定义 CLONE_IO 0x80000000CLONE_FLAGS = CLONE_VM OR CLONE_FS OR CLONE_FILES OR CLONE_SIGHAND OR CLONE_PARENT OR CLONE_THREAD OR CLONE_IOMMAP_FLAG = MAP_PRIVATE 或 MAP_ANONYMOUSMMAP_PERMISSION = PROT_READ OR PROT_WRITE OR PROT_EXEC段可读可执行thread_linux_x64:;使用 'mmap' 系统调用的内存分配 (sys_mmap (9))移动 eax, 9 ;sys_mmap异或edi,edi;地址 = 0 (NULL)mov esi, THREAD_MEM_SIZE ;内存分配大小mov edx, MMAP_PERMISSION ;权限 (PROT_READ, ...)mov r10d, MMAP_FLAG ;标志(MAP_PRIVATE,...)mov r8d, -1 ;文件描述符 (Fd) = -1(无效文件描述符)异或 r9d, r9d ;偏移 = 0系统调用测试 rax, rax ;错误 ?jl .error_mmapmov r13, rax ;R13 = 内存地址 (RAX);使用clone"系统调用(sys_clone (56))创建一个新的子进程(线程)移动 eax, 56 ;系统克隆mov edi, CLONE_FLAGS ;标志(CLONE_VM,...)lea rsi, [r13 + THREAD_MEM_SIZE - 16] ;栈尾 - 16(8-BYTE 存放函数地址,8-BYTE 存放数据(参数)地址)mov qword [rsi], thread_func ;设置线程函数mov qword [rsi+8], 0 ;无数据(参数 = NULL)异或 edx, edx ;* parent_tid = NULL (0)异或 r10d, r10d ;* child_tid = NULL (0)异或 r8d, r8d ;时间 = 0系统调用测试 rax, rax ;pid == 0 ?|pid<0 ?jg 短 .parent_continue ;家长!jl .error_clone ;错误 !;*** 子进程 ***退;通过使用ret"指令,我们调用了请求的函数(线程);因为我们把函数地址移到了子进程的栈中;通过使用ret"指令,我们跳转到线程函数(thread_func).parent_continue:;使用 'wait4' 系统调用等待创建的线程退出 (sys_wait4 (61))mov rdi, rax ;TID(线程 ID)移动轴,61;sys_wait4xor esi, esi异或edx, edx异或 r10d, r10d系统调用;使用 'munmap' 系统调用 (sys_munmap (11)) 释放内存 (R13)移动 eax, 11 ;sys_munmap移动 rdi, r13 ;内存地址 (R13)mov esi, THREAD_MEM_SIZE ;内存大小系统调用;写完成"消息移动 eax, 1 ;系统写异或edi,edi;标准输出 (0)mov rsi, .message ;留言地址mov edx, .message_len ;消息长度系统调用;退出(返回0)移动 eax, 60 ;系统退出异或edi,edi;返回 0系统调用.error_mmap:;设置错误消息以将其写入 STDOUTmov rsi, .mmap_failed_msg ;错误信息mov edx, .mmap_failed_msg_len ;错误信息长度jmp 短.error.error_clone:;使用 'munmap' 系统调用 (sys_munmap (11)) 释放内存 (R13)移动 eax, 11 ;sys_munmap移动 rdi, r13 ;内存地址 (R13)mov esi, THREAD_MEM_SIZE ;内存大小系统调用;设置错误消息以将其写入 STDOUTmov rsi, .clone_failed_msg ;错误信息mov edx, .clone_failed_msg_len ;错误信息长度.错误:;将错误消息写入 STDOUT移动 eax, 1 ;系统写异或edi,edi;标准输出 (0)系统调用;退出(返回 1(错误))mov eax, 60 ;系统退出mov edi, 1 ;返回 1系统调用.message db '子进程终止', 0x0a, 0x00.message_len = $ - .message.mmap_failed_msg db '内存分配失败', 0x0a, 0x00.mmap_failed_msg_len = $ - .mmap_failed_msg.clone_failed_msg db '无法创建新的子进程',0x0a,0x00.clone_failed_msg_len = $ - .clone_failed_msg线程函数:;从子进程写入消息移动 eax, 1 ;系统写异或edi,edi;标准输出 (0)mov rsi, .message ;留言地址mov edx, .message_len ;消息长度系统调用;退出(返回0)移动 eax, 60 ;系统退出异或edi,edi;返回 0系统调用.message db '子进程被调用', 0x0a, 0x00.message_len = $ - .message

在这里,一切看起来都不错!但这是函数结果 ->
子进程终止
分段错误(核心转储)

但有时我也明白这个!!!!!!!!!
子进程被调用
子进程终止

有时我也得到这个!!!!!!!!!!!!!!!!!!
子进程终止
子进程被调用

但 100% 有问题,因为分段错误"!!!!有什么问题?

strace

execve("./thread_linux_x64", ["./thread_linux_x64"], 0x7fff7cc37508/* 53 个变量 */) = 0mmap(NULL, 1024, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1f8b97b000克隆(child_stack=0x7f1f8b97b3f0,flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_PARENT|CLONE_THREAD|CLONE_IOstrace:附加进程3131) = 3131[pid 3131] write(0, "Child process is called\n\0", 25 <unfinished ...>子进程被调用[pid 3130] wait4(3131, <unfinished ...>[pid 3131] <...写恢复>) = 25[pid 3130] <... wait4 resumed>NULL, 0, NULL) = -1 ECHILD(无子进程)[pid 3131] exit(0 <unfinished ...>[pid 3130] munmap(0x7f1f8b97b000, 1024 <unfinished ...>[pid 3131] <...退出恢复>) = ?[pid 3130] <... munmap 恢复 >) = 0[pid 3131] +++ 以 0 +++ 退出write(0, "子进程终止\n\0", 29子进程终止) = 29退出(0)=?+++ 以 0 +++ 退出

C-PTHREAD 示例

这是带有 pthread 的 C 源代码:

#include #include #include void * thread_func(void * arg) {const char msg[] = "Child-> HELLO\n";asm volatile ("系统调用":: "a" (1), "D" (0), "S" (msg), "d" (sizeof(msg) - 1): "rcx", "r11", "内存");返回0;}整数主要的() {pthread_t pthread;const char msg1[] = "Parent-> HELLO\n";const char msg2[] = "Parent-> BYE\n";asm volatile ("系统调用":: "a" (1), "D" (0), "S" (msg1), "d" (sizeof(msg1) - 1): "rcx", "r11", "内存");pthread_create(& pthread, NULL, thread_func, NULL);pthread_join(pthread, NULL);asm volatile ("系统调用":: "a" (1), "D" (0), "S" (msg2), "d" (sizeof(msg2) - 1): "rcx", "r11", "内存");返回0;}

这个的目的是:

mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8c296d3000arch_prctl(ARCH_SET_FS,0x7f8c296d3740)= 0mprotect(0x7f8c29895000, 12288, PROT_READ) = 0mprotect(0x7f8c298bb000, 4096, PROT_READ) = 0mprotect(0x403000, 4096, PROT_READ) = 0mprotect(0x7f8c29906000, 4096, PROT_READ) = 0munmap(0x7f8c298c3000, 98201) = 0set_tid_address(0x7f8c296d3a10) = 10122set_robust_list(0x7f8c296d3a20, 24) = 0rt_sigaction(SIGRTMIN,{sa_handler=0x7f8c298a6c50,sa_mask=[],sa_flags=SA_RESTORER|SA_SIGINFO,sa_restorer=0x7f8c298b3b20},NULL,8)= 0rt_sigaction(SIGRT_1,{sa_handler=0x7f8c298a6cf0,sa_mask=[],sa_flags=SA_RESTORER|SA_RESTART|SA_SIGINFO,sa_restorer=0x7f8c298b3b20},NULL,8)= 0rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0write(0, "Parent-> HELLO\n", 15Parent-> HELLO) = 15mmap(NULL, 8392704, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f8c28ed2000mprotect(0x7f8c28ed3000, 8388608, PROT_READ|PROT_WRITE) = 0brk(NULL) = 0x13d2000brk(0x13f3000) = 0x13f3000brk(NULL) = 0x13f3000克隆(child_stack = 0x7f8c296d1fb0,旗帜= 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)= 10123futex(0x7f8c296d29d0,FUTEX_WAIT,10123,NULLstrace:附加进程10123<未完成...>[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 恢复 >) = 0[pid 10123] +++ 以 0 +++ 退出write(0, "Parent-> BYE\n", 13Parent-> BYE) = 13exit_group(0) = ?+++ 以 0 +++ 退出

如果我们在 C 中使用 clone 和 wait 函数,我们将有 'wait4' 系统调用......即使在我的 'wait' 系统调用中,子 ID 也是正确的!!!!!!!!!!所以应该没有问题!

C 克隆示例

#define _GNU_SOURCE#include #include #include #include #include #include #define MEM_SIZE 1024#define CLONE_FLAGS (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_PARENT | CLONE_THREAD | CLONE_IO)整数线程函数(无效 * 数据){static const char msg[] = "来自子进程的你好\n";写(0,味精,大小(味精)-1);退出(0);}整数主要的() {static const char msg[] = "子进程终止\n";void * 内存;if((memory = mmap(NULL, MEM_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0)) == MAP_FAILED) {printf("内存分配失败\n");返回 1;}int pid = clone(thread_func, (memory + MEM_SIZE), CLONE_FLAGS, NULL);如果(pid <0){munmap(内存,MEM_SIZE);printf("clone() 失败\n");返回 1;}waitpid(pid, NULL, 0);写(0,味精,大小(味精)-1);munmap(内存,MEM_SIZE);退出(0);}

奇怪的东西!!!!同样的错误(段...)!!!!即使在 C 示例中,我也收到相同的错误!!!!

这是strace:

mprotect(0x7fd8b4492000, 12288, PROT_READ) = 0mprotect(0x403000, 4096, PROT_READ) = 0mprotect(0x7fd8b44e1000, 4096, PROT_READ) = 0munmap(0x7fd8b449e000, 98201) = 0mmap(NULL, 1024, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0) = 0x7fd8b44e0000克隆(child_stack=0x7fd8b44e03f0,flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_PARENT|CLONE_THREAD|CLONE_IOstrace:附加进程19911) = 19911[pid 19911] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_ACCERR, si_addr=0x7fd8b44df9c0} ---[pid 19910] wait4(19911, <unfinished ...>) = ?[pid 19911] +++ 被 SIGSEGV 杀死(核心转储)++++++ 被 SIGSEGV 杀死(核心转储)+++分段错误(核心转储)

解决方案

我们已经在评论中回答了这个问题 你上次问.原始的 clone 系统调用不会为您从内存中读取函数指针.

必须自己使用在子线程/进程中运行的代码来完成.相反,您让两个线程继续运行 wait4munmapexit.

clone(2) 手册页解释了这一点.该页面的主要部分记录了 glibc 包装器,该包装器采用函数指针在子线程中调用.但它清楚地表明这不是 原始系统调用,并查看NOTES 部分.您会在那里找到原始 asm 系统调用的原型和文档:

<块引用>

long raw_clone(unsigned long flags, void *stack,int *parent_tid, int *child_tid,无符号长tls);

原始的 clone() 系统调用更接近于 fork(2)子进程中的执行从调用点开始继续. As这样,clone() 包装函数的 fn 和 arg 参数是省略.

您可以将新堆栈用作存储函数指针的方便位置,新线程的用户空间代码可以在该位置找到它.(新线程将无法轻松访问主线程的堆栈,因为 RSP 将指向其新堆栈;我不确定 RAX 以外的寄存器在进入新线程之前是否已清零.如果不是,您可以轻松地只需将指针保存在 RAX、RCX 或 R11 以外的寄存器中即可.当然静态存储是可用的,但您不需要使用它.)

您需要在返回值 0 上进行分支,它告诉您您在子进程中.(像 fork 一样,clone 成功时返回两次:一次在父对象中使用 TID,一次在子对象中使用 0.我认为这是真的;手册页没有清楚地记录这部分,但这就是 fork 的工作原理)<小时>

正如评论中所讨论的,link2 将函数地址存储在子线程堆栈上.当父级从包装函数返回时,它会正常返回.当子进程返回时,它会从现在的堆栈中弹出那个地址.

您选择使用仅在子进程中运行的 ret 来实现这一点;没关系.您可以将 jmp 与寄存器或内存中的指针一起使用.

<小时>

re:更新的问题:

您的 wait4 系统调用正在返回 -1 ECHILD,而没有实际等待.

因此,您的 ret 与会取消映射线程堆栈的 munmap 竞争,如果 munmap 首先发生,则会导致段错误.这也解释了当它没有崩溃时您的输出以不同的顺序发生.

我不知道正确的解决方案是什么,但显然不是这个.看看 pthread_join 用什么来等待子线程退出.也许 clone 返回值实际上并不适合用于 wait4,或者 wait4 不是正确的系统调用.>

(int *child_tid 输出指针的存在可能是有原因的,尽管可能只是为了让父子俩都可以在没有 gettid 系统调用或 VDSO 调用的情况下获得它.)

或者可能是因为你没有通过 __WCLONE__WALL 来让 wait4 等待 clone孩子们.

阅读您使用的系统调用的手册页,尤其是当 strace 显示它们没有按照您的预期执行时. 这是在确定系统调用之后调试/问题解决技术的第 2 步首先返回一个错误(使用 strace).

Im trying to create a thread using 'clone' syscall ... i searched toooooooo much ! for example,
link1
link2

and now this is my source code in assembly for 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   

everything is looks normal !!!! but when i run this program, i get nothing !!!! NO 'Child process is called' message print ! in fact, i think my thread function is not running ... i also got strace test and this is the result !!!

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 +++

This problem is driving me crazy! because there is no error ... and everything looks just fine !!!!

Update:

here i change my source code to create the thread without calling thread_create or ... function (in the main function) and now my problem fixed ... in fact, 'thread_func' now called but i have a new problem ! i get Segment failure !!!! i think it's about my 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

here, everything looks good ! but this is the function result ->
Child process is terminated
Segmentation fault (core dumped)

but sometimes i get this too !!!!!!!!
Child process is called
Child process is terminated

also sometimes i get this tooo !!!!!!!!!!!!!!!!!
Child process is terminated
Child process is called

but 100% there is a problem because "Segmentation fault" !!!! what is the problem?

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 +++

EXAMPLE WITH C-PTHREAD

this is C source code with 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;
}

and the strace for this is:

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 +++

if we use clone and wait functions in C, we going to have 'wait4' syscall ... and even in my 'wait' syscall, the child id is correct !!!!!!!!!! so it shouldn't be any problem !

C Clone EXAMPLE

#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);
}

Something wierd !!!! same error (segment ...) !!!! even in C example, i get Same error !!!!

this is 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)

解决方案

We already answered this in comments last time you asked. The raw clone system call doesn't read a function pointer from memory for you.

You have to do that yourself with code that runs in the child thread / process. Instead, you're having both threads continue on to run wait4, munmap, and exit.

The clone(2) man page explains this. The main part of the page documents the glibc wrapper that takes a function-pointer to call in the child thread. But it clearly says that's not the raw system call, and to see the NOTES section. There you'll find the raw asm system call's prototype and documentation:

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

The raw clone() system call corresponds more closely to fork(2) in that execution in the child continues from the point of the call. As such, the fn and arg arguments of the clone() wrapper function are omitted.

You can use the new stack as a convenient place to stash a function pointer where your user-space code for the new thread can find it. (The new thread won't have easy access to the main thread's stack because RSP will be pointing at its new stack; I'm not sure if registers other than RAX are zeroed before entering the new thread or not. If not you can easily just keep the pointer in a register other than RAX, RCX, or R11. And of course static storage is available, but you shouldn't need to use that.)

You'll want to branch on the return value being 0 which tells you you're in the child process. (Like fork, clone returns twice when it succeeds: once in the parent with the TID, once in the child with 0. I think that's true; the man page doesn't clearly document this part, but that's how fork works)


As discussed in comments, link2 is storing the function address on the child thread stack. When the parent returns from the wrapper function it returns normally. When the child returns, it will pop that address from what is now its stack.

You chose to implement this with a ret that only runs in the child; that's fine. You could have just used jmp with the pointer in a register or memory.


re: updated question:

Your wait4 system call is returning -1 ECHILD without actually waiting.

Therefore your ret races with the munmap that would unmap the thread stack, leading to a segfault if munmap happens first. This also explains your output happening in different orders when it doesn't crash.

I don't know exactly what the right solution is, but it's obviously not this. Have a look at what pthread_join uses to wait for a child thread to exit. Perhaps the clone return value isn't actually the right thing to use with wait4, or wait4 isn't the right system call.

(The int *child_tid output pointer presumably exists for a reason, although maybe just so both parent and child can get it without a gettid system call or VDSO call.)

Or maybe it's because you didn't pass __WCLONE or __WALL to get wait4 to wait for clone children.

Read the man pages for system calls you use, especially when strace shows they didn't do what you expected. This is step 2 in debugging / problem solving technique, after identifying that a system call returned an error in the first place (with strace).

这篇关于未调用程序集克隆系统调用线程函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆