From: Sergiu Moga Date: Mon, 24 Mar 2025 16:52:48 +0000 (+0200) Subject: lib/posix-process: Add `libc` system call wrapper for `clone` X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=50f0e935487cc83fa509a4692869958dfc1f8fca;p=unikraft%2Funikraft.git lib/posix-process: Add `libc` system call wrapper for `clone` In order to successfully call the clone system call through a libc wrapper we need to be able to have an assembly sequence that both translates the arguments passed by the libc wrapper to those of the underlying syscall, since their signatures differ, as well as redirect the parent and child accordingly upon exit from said syscall: child must run its requested function and argument and parent must return to the clone caller with an unscathed register context. Said assembly sequence must be aware of the signature differences between architectures, e.g. ARM64 vs x86_64 signatures of the clone system call. Signed-off-by: Sergiu Moga Approved-by: Michalis Pappas Reviewed-by: Michalis Pappas Reviewed-by: Andrei Tatar GitHub-Closes: #1618 --- diff --git a/lib/posix-process/Makefile.uk b/lib/posix-process/Makefile.uk index 1d5b7e00d..440f0824d 100644 --- a/lib/posix-process/Makefile.uk +++ b/lib/posix-process/Makefile.uk @@ -42,6 +42,7 @@ LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_SIGNAL) += $(LIBPOSIX_PROCESS_BA endif LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_CLONE) += $(LIBPOSIX_PROCESS_BASE)/clone.c +LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_CLONE) += $(LIBPOSIX_PROCESS_BASE)/arch/$(CONFIG_UK_ARCH)/clone.S|arch LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_CLONE) += $(LIBPOSIX_PROCESS_BASE)/clonetab.ld LIBPOSIX_PROCESS_SRCS-$(CONFIG_LIBPOSIX_PROCESS_CLONE) += $(LIBPOSIX_PROCESS_BASE)/arch/$(CONFIG_UK_ARCH)/clone.c|$(CONFIG_UK_ARCH) CFLAGS-$(CONFIG_LIBPOSIX_PROCESS_PIDS) += -fno-builtin-exit -fno-builtin-exit-group diff --git a/lib/posix-process/arch/arm64/clone.S b/lib/posix-process/arch/arm64/clone.S new file mode 100644 index 000000000..55c783ae1 --- /dev/null +++ b/lib/posix-process/arch/arm64/clone.S @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (c) 2025, Unikraft GmbH and The Unikraft Authors. + * Licensed under the BSD-3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + */ + +#include + +/* + * NOTE: We need to replicate this assembly libc wrapper in our + * supported/ported external libc libraries (e.g. musl) because their + * wrappers do not typically tailor to our use case of having system calls + * regular function calls. + * + * Whenever porting a new libc library, make sure to replicate this code there + * as well, unless the library in question does somehow indeed have a more + * special wrapper that still allows our clone functionality to still work + * correctly. + */ +#if UK_LIBC_SYSCALLS +.global clone +clone: + /* Save fn and arg in callee-saved registers, but first make sure + * we can also restore them before returning in the parent. + */ + stp x20, x21, [sp, #-16] + mov x20, x0 + mov x21, x3 + + /* Now reorder arguments */ + + /* flags */ + mov x0, x2 + + /* stack is already in x1 */ + + /* parent_tid */ + mov x2, x4 + + /* tls */ + mov x3, x5 + + /* child_tid */ + mov x4, x6 + + bl uk_syscall_e_clone + + /* Let the parent just return */ + cbnz x0, 1f + + /* Mark the outermost frame (avoid unwinding beyond this) */ + mov x29, xzr + + /* The child should now just call the saved fn(arg) */ + mov x0, x21 + blr x20 + + /* If managed to return from fn(arg) we must call exit(x0) */ + bl uk_syscall_e_exit + +0: + /* Put a wfi safeguard here, just in care, cus exit is noreturn */ + wfi + b 0b + +1: + /* Restore the registers saved in the beginning */ + ldp x20, x21, [sp, #-16] + ret +#endif /* UK_LIBC_SYSCALLS */ diff --git a/lib/posix-process/arch/x86_64/clone.S b/lib/posix-process/arch/x86_64/clone.S new file mode 100644 index 000000000..b57003a3a --- /dev/null +++ b/lib/posix-process/arch/x86_64/clone.S @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (c) 2025, Unikraft GmbH and The Unikraft Authors. + * Licensed under the BSD-3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + */ + +#include + +/* + * NOTE: We need to replicate this assembly libc wrapper in our + * supported/ported external libc libraries (e.g. musl) because their + * wrappers do not typically tailor to our use case of having system calls + * regular function calls. + * + * Whenever porting a new libc library, make sure to replicate this code there + * as well, unless the library in question does somehow indeed have a more + * special wrapper that still allows our clone functionality to still work + * correctly. + */ +#if UK_LIBC_SYSCALLS +.global clone +clone: + /* Save fn and arg in callee-saved registers, but first make sure + * we can also restore them before returning in the parent. + */ + pushq %rbx + pushq %rbp + movq %rdi, %rbx + movq %rcx, %rbp + + /* Now reorder arguments */ + /* flags */ + movq %rdx, %rdi + + /* stack is already in rsi */ + + /* parent_tid */ + movq %r8, %rdx + + /* child_tid: this is passed on the stack as it does not fit + * in the registers used for the arguments + * + * NOTE: Although this runs on native builds (syscall == function call), + * we mustn't use rcx for the 4th argument like for a regular function + * call but instead we will have to use r10 as is supposed to happen + * for system calls. This is because `clone` is registered as an + * execenv system call and execenv registrations assume that the + * 4th argument is that according to the syscall calling convention. + */ + movq 24(%rsp), %r10 + + /* tls */ + movq %r9, %r8 + + call uk_syscall_e_clone + + /* Let the parent just return */ + test %eax, %eax + jnz 1f + + /* The child should now just call the saved fn(arg) */ + movq %rbp, %rdi + + /* Mark the outermost frame (avoid unwinding beyond this) */ + xorl %ebp, %ebp + call *%rbx + + /* If managed to return from fn(arg) we must call exit(rax) */ + movq %rax, %rdi + call uk_syscall_e_exit + +0: + /* Put a hlt safeguard here, just in care, cus exit is noreturn */ + hlt + jmp 0b +1: + /* Restore the registers saved in the beginning */ + popq %rbp + popq %rbx + ret +#endif /* UK_LIBC_SYSCALLS */ diff --git a/lib/posix-process/clone.c b/lib/posix-process/clone.c index 55314ff96..01f7e490f 100644 --- a/lib/posix-process/clone.c +++ b/lib/posix-process/clone.c @@ -590,17 +590,6 @@ UK_LLSYSCALL_R_E_DEFINE(int, clone, return uk_clone(&cl_args, sizeof(cl_args), execenv); } -#if UK_LIBC_SYSCALLS -int clone(int (*fn)(void *) __unused, void *sp __unused, - int flags __unused, void *arg __unused, - ... /* pid_t *parent_tid, void *tls, pid_t *child_tid */) -{ - /* TODO */ - errno = EINVAL; - return -1; -} -#endif /* UK_LIBC_SYSCALLS */ - /* * Checks that the CLONE_VM is set so that we make sure that * the address space is shared. Unikraft does currently not support