about summary refs log tree commit diff
path: root/sysdeps/unix/sysv/linux/aarch64/vfork.S
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/unix/sysv/linux/aarch64/vfork.S')
-rw-r--r--sysdeps/unix/sysv/linux/aarch64/vfork.S29
1 files changed, 20 insertions, 9 deletions
diff --git a/sysdeps/unix/sysv/linux/aarch64/vfork.S b/sysdeps/unix/sysv/linux/aarch64/vfork.S
index d9f2c70748..316cb65e9b 100644
--- a/sysdeps/unix/sysv/linux/aarch64/vfork.S
+++ b/sysdeps/unix/sysv/linux/aarch64/vfork.S
@@ -28,22 +28,33 @@
 
 ENTRY (__vfork)
 
-#ifdef SAVE_PID
-	SAVE_PID
-#endif
+	/* Save the TCB-cached PID away in w3, and then negate the TCB
+           field.  But if it's zero, set it to 0x80000000 instead.  See
+           raise.c for the logic that relies on this value.  */
+	mrs	x2, tpidr_el0
+	sub	x2, x2, #PTHREAD_SIZEOF
+	ldr	w3, [x2, #PTHREAD_PID_OFFSET]
+	mov	w1, #0x80000000
+	negs	w0, w3
+	csel	w0, w1, w0, eq
+	str	w0, [x2, #PTHREAD_PID_OFFSET]
+
 	mov	x0, #0x4111	/* CLONE_VM | CLONE_VFORK | SIGCHLD */
 	mov	x1, sp
 	DO_CALL (clone, 2)
-#ifdef RESTORE_PID
-	RESTORE_PID
-#endif
+
+	/* Restore the original value of the TCB cache of the PID, if we're
+	   the parent.  But in the child (syscall return value equals zero),
+	   leave things as they are.  */
+	cbz	x0, 1f
+	str	w3, [x2, #PTHREAD_PID_OFFSET]
+1:
 	cmn	x0, #4095
-	b.cs    1f
+	b.cs    .Lsyscall_error
 	RET
-1:
-	b	SYSCALL_ERROR
 
 PSEUDO_END (__vfork)
 libc_hidden_def (__vfork)
 
 weak_alias (__vfork, vfork)
+strong_alias (__vfork, __libc_vfork)