libc_hidden_proto (__vfork)
extern int __ttyname_r (int __fd, char *__buf, size_t __buflen);
libc_hidden_proto (__ttyname_r)
+extern __pid_t _Fork (void);
+libc_hidden_proto (_Fork);
extern int __isatty (int __fd) attribute_hidden;
extern int __link (const char *__from, const char *__to);
extern int __symlink (const char *__from, const char *__to);
times \
wait waitpid wait3 wait4 waitid \
alarm sleep pause nanosleep \
- fork vfork _exit register-atfork \
+ fork _Fork vfork _exit register-atfork \
execve fexecve execv execle execl execvp execlp execvpe \
getpid getppid \
getuid geteuid getgid getegid getgroups setuid setgid group_member \
CFLAGS-execvp.os = -fomit-frame-pointer
CFLAGS-execlp.os = -fomit-frame-pointer
CFLAGS-nanosleep.c += -fexceptions -fasynchronous-unwind-tables
+CFLAGS-fork.c = $(libio-mtsafe)
tstgetopt-ARGS = -a -b -cfoobar --required foobar --optional=bazbug \
--none random --col --color --colour
--- /dev/null
+/* _Fork implementation. Generic version.
+ Copyright (C) 2021 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <unistd.h>
+
+/* Clone the calling process, creating an exact copy. Return -1 for errors,
+ 0 to the new process, and the process ID of the new process to the
+ old process.
+ Different than fork, this functions is marked as async-signal-safe by
+ POSIX (by Austin Group issue 62). */
+pid_t
+_Fork (void)
+{
+ __set_errno (ENOSYS);
+ return -1;
+}
+libc_hidden_def (_Fork)
+stub_warning (_Fork)
-/* Copyright (C) 1991-2021 Free Software Foundation, Inc.
+/* fork - create a child process.
+ Copyright (C) 1991-2021 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
-#include <errno.h>
-#include <unistd.h>
+#include <fork.h>
+#include <libio/libioP.h>
+#include <ldsodefs.h>
+#include <malloc/malloc-internal.h>
+#include <nss/nss_database.h>
+#include <register-atfork.h>
+#include <stdio-lock.h>
+#include <sys/single_threaded.h>
+#include <unwind-link.h>
+static void
+fresetlockfiles (void)
+{
+ _IO_ITER i;
+
+ for (i = _IO_iter_begin(); i != _IO_iter_end(); i = _IO_iter_next(i))
+ if ((_IO_iter_file (i)->_flags & _IO_USER_LOCK) == 0)
+ _IO_lock_init (*((_IO_lock_t *) _IO_iter_file(i)->_lock));
+}
-/* Clone the calling process, creating an exact copy.
- Return -1 for errors, 0 to the new process,
- and the process ID of the new process to the old process. */
-int
-__fork (void)
+pid_t
+__libc_fork (void)
{
- __set_errno (ENOSYS);
- return -1;
+ /* Determine if we are running multiple threads. We skip some fork
+ handlers in the single-thread case, to make fork safer to use in
+ signal handlers. */
+ bool multiple_threads = __libc_single_threaded == 0;
+
+ __run_fork_handlers (atfork_run_prepare, multiple_threads);
+
+ struct nss_database_data nss_database_data;
+
+ /* If we are not running multiple threads, we do not have to
+ preserve lock state. If fork runs from a signal handler, only
+ async-signal-safe functions can be used in the child. These data
+ structures are only used by unsafe functions, so their state does
+ not matter if fork was called from a signal handler. */
+ if (multiple_threads)
+ {
+ call_function_static_weak (__nss_database_fork_prepare_parent,
+ &nss_database_data);
+
+ _IO_list_lock ();
+
+ /* Acquire malloc locks. This needs to come last because fork
+ handlers may use malloc, and the libio list lock has an
+ indirect malloc dependency as well (via the getdelim
+ function). */
+ call_function_static_weak (__malloc_fork_lock_parent);
+ }
+
+ pid_t pid = _Fork ();
+
+ if (pid == 0)
+ {
+ fork_system_setup ();
+
+ /* Reset the lock state in the multi-threaded case. */
+ if (multiple_threads)
+ {
+ __libc_unwind_link_after_fork ();
+
+ fork_system_setup_after_fork ();
+
+ /* Release malloc locks. */
+ call_function_static_weak (__malloc_fork_unlock_child);
+
+ /* Reset the file list. These are recursive mutexes. */
+ fresetlockfiles ();
+
+ /* Reset locks in the I/O code. */
+ _IO_list_resetlock ();
+
+ call_function_static_weak (__nss_database_fork_subprocess,
+ &nss_database_data);
+ }
+
+ /* Reset the lock the dynamic loader uses to protect its data. */
+ __rtld_lock_initialize (GL(dl_load_lock));
+
+ reclaim_stacks ();
+
+ /* Run the handlers registered for the child. */
+ __run_fork_handlers (atfork_run_child, multiple_threads);
+ }
+ else
+ {
+ /* Release acquired locks in the multi-threaded case. */
+ if (multiple_threads)
+ {
+ /* Release malloc locks, parent process variant. */
+ call_function_static_weak (__malloc_fork_unlock_parent);
+
+ /* We execute this even if the 'fork' call failed. */
+ _IO_list_unlock ();
+ }
+
+ /* Run the handlers registered for the parent. */
+ __run_fork_handlers (atfork_run_parent, multiple_threads);
+ }
+
+ return pid;
}
+weak_alias (__libc_fork, __fork)
libc_hidden_def (__fork)
-stub_warning (fork)
-
-weak_alias (__fork, fork)
+weak_alias (__libc_fork, fork)
--- /dev/null
+/* System specific fork hooks. Generic version.
+ Copyright (C) 2021 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+static inline void
+fork_system_setup (void)
+{
+}
+
+static inline void
+fork_system_setup_after_fork (void)
+{
+}
+
+static inline void
+reclaim_stacks (void)
+{
+}
--- /dev/null
+/* Copyright (C) 1994-2021 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <unistd.h>
+#include <hurd.h>
+#include <hurd/signal.h>
+#include <hurd/threadvar.h>
+#include <setjmp.h>
+#include <thread_state.h>
+#include <sysdep.h> /* For stack growth direction. */
+#include "set-hooks.h"
+#include <assert.h>
+#include "hurdmalloc.h" /* XXX */
+#include <tls.h>
+#include <malloc/malloc-internal.h>
+#include <nss/nss_database.h>
+#include <unwind-link.h>
+#include <register-atfork.h>
+
+#undef __fork
+
+
+/* Things that want to be locked while forking. */
+symbol_set_declare (_hurd_fork_locks)
+
+/* Things that want to be called before we fork, to prepare the parent for
+ task_create, when the new child task will inherit our address space. */
+DEFINE_HOOK (_hurd_fork_prepare_hook, (void));
+
+/* Things that want to be called when we are forking, with the above all
+ locked. They are passed the task port of the child. The child process
+ is all set up except for doing proc_child, and has no threads yet. */
+DEFINE_HOOK (_hurd_fork_setup_hook, (void));
+
+/* Things to be run in the child fork. */
+DEFINE_HOOK (_hurd_fork_child_hook, (void));
+
+/* Things to be run in the parent fork. */
+DEFINE_HOOK (_hurd_fork_parent_hook, (void));
+
+
+/* Clone the calling process, creating an exact copy.
+ Return -1 for errors, 0 to the new process,
+ and the process ID of the new process to the old process. */
+pid_t
+_Fork (void)
+{
+ jmp_buf env;
+ pid_t pid;
+ size_t i;
+ error_t err;
+ struct hurd_sigstate *volatile ss;
+
+ ss = _hurd_self_sigstate ();
+retry:
+ __spin_lock (&ss->critical_section_lock);
+
+#undef LOSE
+#define LOSE do { assert_perror (err); goto lose; } while (0) /* XXX */
+
+ if (! setjmp (env))
+ {
+ process_t newproc;
+ task_t newtask;
+ thread_t thread, sigthread;
+ mach_port_urefs_t thread_refs, sigthread_refs;
+ struct machine_thread_state state;
+ mach_msg_type_number_t statecount;
+ mach_port_t *portnames = NULL;
+ mach_msg_type_number_t nportnames = 0;
+ mach_port_type_t *porttypes = NULL;
+ mach_msg_type_number_t nporttypes = 0;
+ thread_t *threads = NULL;
+ mach_msg_type_number_t nthreads = 0;
+ int ports_locked = 0, stopped = 0;
+
+ void resume_threads (void)
+ {
+ if (! stopped)
+ return;
+
+ assert (threads);
+
+ for (i = 0; i < nthreads; ++i)
+ if (threads[i] != ss->thread)
+ __thread_resume (threads[i]);
+ stopped = 0;
+ }
+
+ /* Run things that prepare for forking before we create the task. */
+ RUN_HOOK (_hurd_fork_prepare_hook, ());
+
+ /* Lock things that want to be locked before we fork. */
+ {
+ void *const *p;
+ for (p = symbol_set_first_element (_hurd_fork_locks);
+ ! symbol_set_end_p (_hurd_fork_locks, p);
+ ++p)
+ __mutex_lock (*p);
+ }
+ __mutex_lock (&_hurd_siglock);
+
+ /* Acquire malloc locks. This needs to come last because fork
+ handlers may use malloc, and the libio list lock has an
+ indirect malloc dependency as well (via the getdelim
+ function). */
+ call_function_static_weak (__malloc_fork_lock_parent);
+ _hurd_malloc_fork_prepare ();
+
+ newtask = MACH_PORT_NULL;
+ thread = sigthread = MACH_PORT_NULL;
+ newproc = MACH_PORT_NULL;
+
+ /* Lock all the port cells for the standard ports while we copy the
+ address space. We want to insert all the send rights into the
+ child with the same names. */
+ for (i = 0; i < _hurd_nports; ++i)
+ __spin_lock (&_hurd_ports[i].lock);
+ ports_locked = 1;
+
+
+ /* Keep our SS locked while stopping other threads, so they don't get a
+ chance to have it locked in the copied space. */
+ __spin_lock (&ss->lock);
+ /* Stop all other threads while copying the address space,
+ so nothing changes. */
+ err = __proc_dostop (_hurd_ports[INIT_PORT_PROC].port, ss->thread);
+ __spin_unlock (&ss->lock);
+ if (!err)
+ {
+ stopped = 1;
+
+#define XXX_KERNEL_PAGE_FAULT_BUG /* XXX work around page fault bug in mk */
+
+#ifdef XXX_KERNEL_PAGE_FAULT_BUG
+ /* Gag me with a pitchfork.
+ The bug scenario is this:
+
+ - The page containing __mach_task_self_ is paged out.
+ - The signal thread was faulting on that page when we
+ suspended it via proc_dostop. It holds some lock, or set
+ some busy bit, or somesuch.
+ - Now this thread faults on that same page.
+ - GRATUIOUS DEADLOCK
+
+ We can break the deadlock by aborting the thread that faulted
+ first, which if the bug happened was the signal thread because
+ it is the only other thread and we just suspended it.
+ */
+ __thread_abort (_hurd_msgport_thread);
+#endif
+ /* Create the child task. It will inherit a copy of our memory. */
+ err = __task_create (__mach_task_self (),
+#ifdef KERN_INVALID_LEDGER
+ NULL, 0, /* OSF Mach */
+#endif
+ 1, &newtask);
+ }
+
+ /* Unlock the global signal state lock, so we do not
+ block the signal thread any longer than necessary. */
+ __mutex_unlock (&_hurd_siglock);
+
+ if (err)
+ LOSE;
+
+ /* Fetch the names of all ports used in this task. */
+ if (err = __mach_port_names (__mach_task_self (),
+ &portnames, &nportnames,
+ &porttypes, &nporttypes))
+ LOSE;
+ if (nportnames != nporttypes)
+ {
+ err = EGRATUITOUS;
+ LOSE;
+ }
+
+ /* Get send rights for all the threads in this task.
+ We want to avoid giving these rights to the child. */
+ if (err = __task_threads (__mach_task_self (), &threads, &nthreads))
+ LOSE;
+
+ /* Get the child process's proc server port. We will insert it into
+ the child with the same name as we use for our own proc server
+ port; and we will need it to set the child's message port. */
+ if (err = __proc_task2proc (_hurd_ports[INIT_PORT_PROC].port,
+ newtask, &newproc))
+ LOSE;
+
+ /* Insert all our port rights into the child task. */
+ thread_refs = sigthread_refs = 0;
+ for (i = 0; i < nportnames; ++i)
+ {
+ if (porttypes[i] & MACH_PORT_TYPE_RECEIVE)
+ {
+ /* This is a receive right. We want to give the child task
+ its own new receive right under the same name. */
+ if (err = __mach_port_allocate_name (newtask,
+ MACH_PORT_RIGHT_RECEIVE,
+ portnames[i]))
+ LOSE;
+ if (porttypes[i] & MACH_PORT_TYPE_SEND)
+ {
+ /* Give the child as many send rights for its receive
+ right as we have for ours. */
+ mach_port_urefs_t refs;
+ mach_port_t port;
+ mach_msg_type_name_t poly;
+ if (err = __mach_port_get_refs (__mach_task_self (),
+ portnames[i],
+ MACH_PORT_RIGHT_SEND,
+ &refs))
+ LOSE;
+ if (err = __mach_port_extract_right (newtask,
+ portnames[i],
+ MACH_MSG_TYPE_MAKE_SEND,
+ &port, &poly))
+ LOSE;
+ if (portnames[i] == _hurd_msgport)
+ {
+ /* We just created a receive right for the child's
+ message port and are about to insert send rights
+ for it. Now, while we happen to have a send right
+ for it, give it to the proc server. */
+ mach_port_t old;
+ if (err = __proc_setmsgport (newproc, port, &old))
+ LOSE;
+ if (old != MACH_PORT_NULL)
+ /* XXX what to do here? */
+ __mach_port_deallocate (__mach_task_self (), old);
+ /* The new task will receive its own exceptions
+ on its message port. */
+ if (err =
+#ifdef TASK_EXCEPTION_PORT
+ __task_set_special_port (newtask,
+ TASK_EXCEPTION_PORT,
+ port)
+#elif defined (EXC_MASK_ALL)
+ __task_set_exception_ports
+ (newtask, EXC_MASK_ALL & ~(EXC_MASK_SYSCALL
+ | EXC_MASK_MACH_SYSCALL
+ | EXC_MASK_RPC_ALERT),
+ port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)
+#else
+# error task_set_exception_port?
+#endif
+ )
+ LOSE;
+ }
+ if (err = __mach_port_insert_right (newtask,
+ portnames[i],
+ port,
+ MACH_MSG_TYPE_MOVE_SEND))
+ LOSE;
+ if (refs > 1
+ && (err = __mach_port_mod_refs (newtask,
+ portnames[i],
+ MACH_PORT_RIGHT_SEND,
+ refs - 1)))
+ LOSE;
+ }
+ if (porttypes[i] & MACH_PORT_TYPE_SEND_ONCE)
+ {
+ /* Give the child a send-once right for its receive right,
+ since we have one for ours. */
+ mach_port_t port;
+ mach_msg_type_name_t poly;
+ if (err = __mach_port_extract_right
+ (newtask,
+ portnames[i],
+ MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &port, &poly))
+ LOSE;
+ if (err = __mach_port_insert_right
+ (newtask,
+ portnames[i], port,
+ MACH_MSG_TYPE_MOVE_SEND_ONCE))
+ LOSE;
+ }
+ }
+ else if (porttypes[i]
+ & (MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_DEAD_NAME))
+ {
+ /* This is a send right or a dead name.
+ Give the child as many references for it as we have. */
+ mach_port_urefs_t refs = 0, *record_refs = NULL;
+ mach_port_t insert;
+ mach_msg_type_name_t insert_type = MACH_MSG_TYPE_COPY_SEND;
+ if (portnames[i] == newtask || portnames[i] == newproc)
+ /* Skip the name we use for the child's task or proc ports. */
+ continue;
+ if (portnames[i] == __mach_task_self ())
+ /* For the name we use for our own task port,
+ insert the child's task port instead. */
+ insert = newtask;
+ else if (portnames[i] == _hurd_ports[INIT_PORT_PROC].port)
+ {
+ /* Use the proc server port for the new task. */
+ insert = newproc;
+ insert_type = MACH_MSG_TYPE_COPY_SEND;
+ }
+ else if (portnames[i] == ss->thread)
+ {
+ /* For the name we use for our own thread port, we will
+ insert the thread port for the child main user thread
+ after we create it. */
+ insert = MACH_PORT_NULL;
+ record_refs = &thread_refs;
+ /* Allocate a dead name right for this name as a
+ placeholder, so the kernel will not chose this name
+ for any other new port (it might use it for one of the
+ rights created when a thread is created). */
+ if (err = __mach_port_allocate_name
+ (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))
+ LOSE;
+ }
+ else if (portnames[i] == _hurd_msgport_thread)
+ /* For the name we use for our signal thread's thread port,
+ we will insert the thread port for the child's signal
+ thread after we create it. */
+ {
+ insert = MACH_PORT_NULL;
+ record_refs = &sigthread_refs;
+ /* Allocate a dead name right as a placeholder. */
+ if (err = __mach_port_allocate_name
+ (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))
+ LOSE;
+ }
+ else
+ {
+ /* Skip the name we use for any of our own thread ports. */
+ mach_msg_type_number_t j;
+ for (j = 0; j < nthreads; ++j)
+ if (portnames[i] == threads[j])
+ break;
+ if (j < nthreads)
+ continue;
+
+ /* Copy our own send right. */
+ insert = portnames[i];
+ }
+ /* Find out how many user references we have for
+ the send right with this name. */
+ if (err = __mach_port_get_refs (__mach_task_self (),
+ portnames[i],
+ MACH_PORT_RIGHT_SEND,
+ record_refs ?: &refs))
+ LOSE;
+ if (insert == MACH_PORT_NULL)
+ continue;
+ if (insert == portnames[i]
+ && (porttypes[i] & MACH_PORT_TYPE_DEAD_NAME))
+ /* This is a dead name; allocate another dead name
+ with the same name in the child. */
+ allocate_dead_name:
+ err = __mach_port_allocate_name (newtask,
+ MACH_PORT_RIGHT_DEAD_NAME,
+ portnames[i]);
+ else
+ /* Insert the chosen send right into the child. */
+ err = __mach_port_insert_right (newtask,
+ portnames[i],
+ insert, insert_type);
+ switch (err)
+ {
+ case KERN_NAME_EXISTS:
+ {
+ /* It already has a send right under this name (?!).
+ Well, it starts out with a send right for its task
+ port, and inherits the bootstrap and exception ports
+ from us. */
+ mach_port_t childport;
+ mach_msg_type_name_t poly;
+ assert (__mach_port_extract_right (newtask, portnames[i],
+ MACH_MSG_TYPE_COPY_SEND,
+ &childport,
+ &poly) == 0
+ && childport == insert
+ && __mach_port_deallocate (__mach_task_self (),
+ childport) == 0);
+ break;
+ }
+
+ case KERN_INVALID_CAPABILITY:
+ /* The port just died. It was a send right,
+ and now it's a dead name. */
+ goto allocate_dead_name;
+
+ default:
+ LOSE;
+ break;
+
+ case KERN_SUCCESS:
+ /* Give the child as many user references as we have. */
+ if (refs > 1
+ && (err = __mach_port_mod_refs (newtask,
+ portnames[i],
+ MACH_PORT_RIGHT_SEND,
+ refs - 1)))
+ LOSE;
+ }
+ }
+ }
+
+ /* Unlock the standard port cells. The child must unlock its own
+ copies too. */
+ for (i = 0; i < _hurd_nports; ++i)
+ __spin_unlock (&_hurd_ports[i].lock);
+ ports_locked = 0;
+
+ /* All state has now been copied from the parent. It is safe to
+ resume other parent threads. */
+ resume_threads ();
+
+ /* Create the child main user thread and signal thread. */
+ if ((err = __thread_create (newtask, &thread))
+ || (err = __thread_create (newtask, &sigthread)))
+ LOSE;
+
+ /* Insert send rights for those threads. We previously allocated
+ dead name rights with the names we want to give the thread ports
+ in the child as placeholders. Now deallocate them so we can use
+ the names. */
+ if ((err = __mach_port_deallocate (newtask, ss->thread))
+ || (err = __mach_port_insert_right (newtask, ss->thread,
+ thread,
+ MACH_MSG_TYPE_COPY_SEND)))
+ LOSE;
+ /* XXX consumed? (_hurd_sigthread is no more) */
+ if (thread_refs > 1
+ && (err = __mach_port_mod_refs (newtask, ss->thread,
+ MACH_PORT_RIGHT_SEND,
+ thread_refs - 1)))
+ LOSE;
+ if ((_hurd_msgport_thread != MACH_PORT_NULL) /* Let user have none. */
+ && ((err = __mach_port_deallocate (newtask, _hurd_msgport_thread))
+ || (err = __mach_port_insert_right (newtask,
+ _hurd_msgport_thread,
+ sigthread,
+ MACH_MSG_TYPE_COPY_SEND))))
+ LOSE;
+ if (sigthread_refs > 1
+ && (err = __mach_port_mod_refs (newtask, _hurd_msgport_thread,
+ MACH_PORT_RIGHT_SEND,
+ sigthread_refs - 1)))
+ LOSE;
+
+ /* This seems like a convenient juncture to copy the proc server's
+ idea of what addresses our argv and envp are found at from the
+ parent into the child. Since we happen to know that the child
+ shares our memory image, it is we who should do this copying. */
+ {
+ vm_address_t argv, envp;
+ err = (__USEPORT (PROC, __proc_get_arg_locations (port, &argv, &envp))
+ ?: __proc_set_arg_locations (newproc, argv, envp));
+ if (err)
+ LOSE;
+ }
+
+ /* Set the child signal thread up to run the msgport server function
+ using the same signal thread stack copied from our address space.
+ We fetch the state before longjmp'ing it so that miscellaneous
+ registers not affected by longjmp (such as i386 segment registers)
+ are in their normal default state. */
+ statecount = MACHINE_THREAD_STATE_COUNT;
+ if (err = __thread_get_state (_hurd_msgport_thread,
+ MACHINE_THREAD_STATE_FLAVOR,
+ (natural_t *) &state, &statecount))
+ LOSE;
+#ifdef STACK_GROWTH_UP
+ if (__hurd_sigthread_stack_base == 0)
+ {
+ state.SP &= __hurd_threadvar_stack_mask;
+ state.SP += __hurd_threadvar_stack_offset;
+ }
+ else
+ state.SP = __hurd_sigthread_stack_base;
+#else
+ if (__hurd_sigthread_stack_end == 0)
+ {
+ /* The signal thread has a stack assigned by pthread.
+ The threadvar_stack variables conveniently tell us how
+ to get to the highest address in the stack, just below
+ the per-thread variables. */
+ state.SP &= __hurd_threadvar_stack_mask;
+ state.SP += __hurd_threadvar_stack_offset;
+ }
+ else
+ state.SP = __hurd_sigthread_stack_end;
+#endif
+ MACHINE_THREAD_STATE_SET_PC (&state,
+ (unsigned long int) _hurd_msgport_receive);
+
+ /* Do special signal thread setup for TLS if needed. */
+ if (err = _hurd_tls_fork (sigthread, _hurd_msgport_thread, &state))
+ LOSE;
+
+ if (err = __thread_set_state (sigthread, MACHINE_THREAD_STATE_FLAVOR,
+ (natural_t *) &state, statecount))
+ LOSE;
+ /* We do not thread_resume SIGTHREAD here because the child
+ fork needs to do more setup before it can take signals. */
+
+ /* Set the child user thread up to return 1 from the setjmp above. */
+ _hurd_longjmp_thread_state (&state, env, 1);
+
+ /* Do special thread setup for TLS if needed. */
+ if (err = _hurd_tls_fork (thread, ss->thread, &state))
+ LOSE;
+
+ if (err = __thread_set_state (thread, MACHINE_THREAD_STATE_FLAVOR,
+ (natural_t *) &state, statecount))
+ LOSE;
+
+ /* Get the PID of the child from the proc server. We must do this
+ before calling proc_child below, because at that point any
+ authorized POSIX.1 process may kill the child task with SIGKILL. */
+ if (err = __USEPORT (PROC, __proc_task2pid (port, newtask, &pid)))
+ LOSE;
+
+ /* Register the child with the proc server. It is important that
+ this be that last thing we do before starting the child thread
+ running. Once proc_child has been done for the task, it appears
+ as a POSIX.1 process. Any errors we get must be detected before
+ this point, and the child must have a message port so it responds
+ to POSIX.1 signals. */
+ if (err = __USEPORT (PROC, __proc_child (port, newtask)))
+ LOSE;
+
+ /* This must be the absolutely last thing we do; we can't assume that
+ the child will remain alive for even a moment once we do this. We
+ ignore errors because we have committed to the fork and are not
+ allowed to return them after the process becomes visible to
+ POSIX.1 (which happened right above when we called proc_child). */
+ (void) __thread_resume (thread);
+
+ lose:
+ if (ports_locked)
+ for (i = 0; i < _hurd_nports; ++i)
+ __spin_unlock (&_hurd_ports[i].lock);
+
+ resume_threads ();
+
+ if (newtask != MACH_PORT_NULL)
+ {
+ if (err)
+ __task_terminate (newtask);
+ __mach_port_deallocate (__mach_task_self (), newtask);
+ }
+ if (thread != MACH_PORT_NULL)
+ __mach_port_deallocate (__mach_task_self (), thread);
+ if (sigthread != MACH_PORT_NULL)
+ __mach_port_deallocate (__mach_task_self (), sigthread);
+ if (newproc != MACH_PORT_NULL)
+ __mach_port_deallocate (__mach_task_self (), newproc);
+
+ if (portnames)
+ __vm_deallocate (__mach_task_self (),
+ (vm_address_t) portnames,
+ nportnames * sizeof (*portnames));
+ if (porttypes)
+ __vm_deallocate (__mach_task_self (),
+ (vm_address_t) porttypes,
+ nporttypes * sizeof (*porttypes));
+ if (threads)
+ {
+ for (i = 0; i < nthreads; ++i)
+ __mach_port_deallocate (__mach_task_self (), threads[i]);
+ __vm_deallocate (__mach_task_self (),
+ (vm_address_t) threads,
+ nthreads * sizeof (*threads));
+ }
+
+ /* Release malloc locks. */
+ _hurd_malloc_fork_parent ();
+ call_function_static_weak (__malloc_fork_unlock_parent);
+
+ /* Run things that want to run in the parent to restore it to
+ normality. Usually prepare hooks and parent hooks are
+ symmetrical: the prepare hook arrests state in some way for the
+ fork, and the parent hook restores the state for the parent to
+ continue executing normally. */
+ RUN_HOOK (_hurd_fork_parent_hook, ());
+ }
+ else
+ {
+ struct hurd_sigstate *oldstates;
+
+ /* We are the child task. Unlock the standard port cells, which were
+ locked in the parent when we copied its memory. The parent has
+ inserted send rights with the names that were in the cells then. */
+ for (i = 0; i < _hurd_nports; ++i)
+ __spin_unlock (&_hurd_ports[i].lock);
+
+ /* Claim our sigstate structure and unchain the rest: the
+ threads existed in the parent task but don't exist in this
+ task (the child process). Delay freeing them until later
+ because some of the further setup and unlocking might be
+ required for free to work. Before we finish cleaning up,
+ we will reclaim the signal thread's sigstate structure (if
+ it had one). */
+ oldstates = _hurd_sigstates;
+ if (oldstates == ss)
+ oldstates = ss->next;
+ else
+ {
+ while (_hurd_sigstates->next != ss)
+ _hurd_sigstates = _hurd_sigstates->next;
+ _hurd_sigstates->next = ss->next;
+ }
+ ss->next = NULL;
+ _hurd_sigstates = ss;
+ __mutex_unlock (&_hurd_siglock);
+ /* Earlier on, the global sigstate may have been tainted and now needs to
+ be reinitialized. Nobody is interested in its present state anymore:
+ we're not, the signal thread will be restarted, and there are no other
+ threads.
+
+ We can't simply allocate a fresh global sigstate here, as
+ _hurd_thread_sigstate will call malloc and that will deadlock trying
+ to determine the current thread's sigstate. */
+#if 0
+ _hurd_thread_sigstate_init (_hurd_global_sigstate, MACH_PORT_NULL);
+#else
+ /* Only reinitialize the lock -- otherwise we might have to do additional
+ setup as done in hurdsig.c:_hurdsig_init. */
+ __spin_lock_init (&_hurd_global_sigstate->lock);
+#endif
+
+ /* We are one of the (exactly) two threads in this new task, we
+ will take the task-global signals. */
+ _hurd_sigstate_set_global_rcv (ss);
+
+ /* Fetch our new process IDs from the proc server. No need to
+ refetch our pgrp; it is always inherited from the parent (so
+ _hurd_pgrp is already correct), and the proc server will send us a
+ proc_newids notification when it changes. */
+ err = __USEPORT (PROC, __proc_getpids (port, &_hurd_pid, &_hurd_ppid,
+ &_hurd_orphaned));
+
+ /* Forking clears the trace flag and pending masks. */
+ __sigemptyset (&_hurdsig_traced);
+ __sigemptyset (&_hurd_global_sigstate->pending);
+ __sigemptyset (&ss->pending);
+
+ __libc_unwind_link_after_fork ();
+
+ /* Release malloc locks. */
+ _hurd_malloc_fork_child ();
+ call_function_static_weak (__malloc_fork_unlock_child);
+
+ /* Run things that want to run in the child task to set up. */
+ RUN_HOOK (_hurd_fork_child_hook, ());
+
+ /* Set up proc server-assisted fault recovery for the signal thread. */
+ _hurdsig_fault_init ();
+
+ /* Start the signal thread listening on the message port. */
+ if (!err)
+ err = __thread_resume (_hurd_msgport_thread);
+
+ /* Reclaim the signal thread's sigstate structure and free the
+ other old sigstate structures. */
+ while (oldstates != NULL)
+ {
+ struct hurd_sigstate *next = oldstates->next;
+
+ if (oldstates->thread == _hurd_msgport_thread)
+ {
+ /* If we have a second signal state structure then we
+ must have been through here before--not good. */
+ assert (_hurd_sigstates->next == 0);
+ _hurd_sigstates->next = oldstates;
+ oldstates->next = 0;
+ }
+ else
+ free (oldstates);
+
+ oldstates = next;
+ }
+
+ /* XXX what to do if we have any errors here? */
+
+ pid = 0;
+ }
+
+ /* Unlock things we locked before creating the child task.
+ They are locked in both the parent and child tasks. */
+ {
+ void *const *p;
+ for (p = symbol_set_first_element (_hurd_fork_locks);
+ ! symbol_set_end_p (_hurd_fork_locks, p);
+ ++p)
+ __mutex_unlock (*p);
+ }
+
+ _hurd_critical_section_unlock (ss);
+ if (err == EINTR)
+ /* Got a signal while inside an RPC of the critical section, retry again */
+ goto retry;
+
+ return err ? __hurd_fail (err) : pid;
+}
+libc_hidden_def (_Fork)
+++ /dev/null
-/* Copyright (C) 1994-2021 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
-
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with the GNU C Library; if not, see
- <https://www.gnu.org/licenses/>. */
-
-#include <errno.h>
-#include <unistd.h>
-#include <hurd.h>
-#include <hurd/signal.h>
-#include <hurd/threadvar.h>
-#include <setjmp.h>
-#include <thread_state.h>
-#include <sysdep.h> /* For stack growth direction. */
-#include "set-hooks.h"
-#include <assert.h>
-#include "hurdmalloc.h" /* XXX */
-#include <tls.h>
-#include <malloc/malloc-internal.h>
-#include <nss/nss_database.h>
-#include <unwind-link.h>
-#include <register-atfork.h>
-
-#undef __fork
-
-
-/* Things that want to be locked while forking. */
-symbol_set_declare (_hurd_fork_locks)
-
-/* Things that want to be called before we fork, to prepare the parent for
- task_create, when the new child task will inherit our address space. */
-DEFINE_HOOK (_hurd_fork_prepare_hook, (void));
-
-/* Things that want to be called when we are forking, with the above all
- locked. They are passed the task port of the child. The child process
- is all set up except for doing proc_child, and has no threads yet. */
-DEFINE_HOOK (_hurd_fork_setup_hook, (void));
-
-/* Things to be run in the child fork. */
-DEFINE_HOOK (_hurd_fork_child_hook, (void));
-
-/* Things to be run in the parent fork. */
-DEFINE_HOOK (_hurd_fork_parent_hook, (void));
-
-
-/* Clone the calling process, creating an exact copy.
- Return -1 for errors, 0 to the new process,
- and the process ID of the new process to the old process. */
-pid_t
-__fork (void)
-{
- jmp_buf env;
- pid_t pid;
- size_t i;
- error_t err;
- struct hurd_sigstate *volatile ss;
- struct nss_database_data nss_database_data;
-
- __run_fork_handlers (atfork_run_prepare, true);
-
- ss = _hurd_self_sigstate ();
-retry:
- __spin_lock (&ss->critical_section_lock);
-
-#undef LOSE
-#define LOSE do { assert_perror (err); goto lose; } while (0) /* XXX */
-
- if (! setjmp (env))
- {
- process_t newproc;
- task_t newtask;
- thread_t thread, sigthread;
- mach_port_urefs_t thread_refs, sigthread_refs;
- struct machine_thread_state state;
- mach_msg_type_number_t statecount;
- mach_port_t *portnames = NULL;
- mach_msg_type_number_t nportnames = 0;
- mach_port_type_t *porttypes = NULL;
- mach_msg_type_number_t nporttypes = 0;
- thread_t *threads = NULL;
- mach_msg_type_number_t nthreads = 0;
- int ports_locked = 0, stopped = 0;
-
- void resume_threads (void)
- {
- if (! stopped)
- return;
-
- assert (threads);
-
- for (i = 0; i < nthreads; ++i)
- if (threads[i] != ss->thread)
- __thread_resume (threads[i]);
- stopped = 0;
- }
-
- /* Run things that prepare for forking before we create the task. */
- RUN_HOOK (_hurd_fork_prepare_hook, ());
-
- call_function_static_weak (__nss_database_fork_prepare_parent,
- &nss_database_data);
-
- /* Lock things that want to be locked before we fork. */
- {
- void *const *p;
- for (p = symbol_set_first_element (_hurd_fork_locks);
- ! symbol_set_end_p (_hurd_fork_locks, p);
- ++p)
- __mutex_lock (*p);
- }
- __mutex_lock (&_hurd_siglock);
-
- /* Acquire malloc locks. This needs to come last because fork
- handlers may use malloc, and the libio list lock has an
- indirect malloc dependency as well (via the getdelim
- function). */
- call_function_static_weak (__malloc_fork_lock_parent);
- _hurd_malloc_fork_prepare ();
-
- newtask = MACH_PORT_NULL;
- thread = sigthread = MACH_PORT_NULL;
- newproc = MACH_PORT_NULL;
-
- /* Lock all the port cells for the standard ports while we copy the
- address space. We want to insert all the send rights into the
- child with the same names. */
- for (i = 0; i < _hurd_nports; ++i)
- __spin_lock (&_hurd_ports[i].lock);
- ports_locked = 1;
-
-
- /* Keep our SS locked while stopping other threads, so they don't get a
- chance to have it locked in the copied space. */
- __spin_lock (&ss->lock);
- /* Stop all other threads while copying the address space,
- so nothing changes. */
- err = __proc_dostop (_hurd_ports[INIT_PORT_PROC].port, ss->thread);
- __spin_unlock (&ss->lock);
- if (!err)
- {
- stopped = 1;
-
-#define XXX_KERNEL_PAGE_FAULT_BUG /* XXX work around page fault bug in mk */
-
-#ifdef XXX_KERNEL_PAGE_FAULT_BUG
- /* Gag me with a pitchfork.
- The bug scenario is this:
-
- - The page containing __mach_task_self_ is paged out.
- - The signal thread was faulting on that page when we
- suspended it via proc_dostop. It holds some lock, or set
- some busy bit, or somesuch.
- - Now this thread faults on that same page.
- - GRATUIOUS DEADLOCK
-
- We can break the deadlock by aborting the thread that faulted
- first, which if the bug happened was the signal thread because
- it is the only other thread and we just suspended it.
- */
- __thread_abort (_hurd_msgport_thread);
-#endif
- /* Create the child task. It will inherit a copy of our memory. */
- err = __task_create (__mach_task_self (),
-#ifdef KERN_INVALID_LEDGER
- NULL, 0, /* OSF Mach */
-#endif
- 1, &newtask);
- }
-
- /* Unlock the global signal state lock, so we do not
- block the signal thread any longer than necessary. */
- __mutex_unlock (&_hurd_siglock);
-
- if (err)
- LOSE;
-
- /* Fetch the names of all ports used in this task. */
- if (err = __mach_port_names (__mach_task_self (),
- &portnames, &nportnames,
- &porttypes, &nporttypes))
- LOSE;
- if (nportnames != nporttypes)
- {
- err = EGRATUITOUS;
- LOSE;
- }
-
- /* Get send rights for all the threads in this task.
- We want to avoid giving these rights to the child. */
- if (err = __task_threads (__mach_task_self (), &threads, &nthreads))
- LOSE;
-
- /* Get the child process's proc server port. We will insert it into
- the child with the same name as we use for our own proc server
- port; and we will need it to set the child's message port. */
- if (err = __proc_task2proc (_hurd_ports[INIT_PORT_PROC].port,
- newtask, &newproc))
- LOSE;
-
- /* Insert all our port rights into the child task. */
- thread_refs = sigthread_refs = 0;
- for (i = 0; i < nportnames; ++i)
- {
- if (porttypes[i] & MACH_PORT_TYPE_RECEIVE)
- {
- /* This is a receive right. We want to give the child task
- its own new receive right under the same name. */
- if (err = __mach_port_allocate_name (newtask,
- MACH_PORT_RIGHT_RECEIVE,
- portnames[i]))
- LOSE;
- if (porttypes[i] & MACH_PORT_TYPE_SEND)
- {
- /* Give the child as many send rights for its receive
- right as we have for ours. */
- mach_port_urefs_t refs;
- mach_port_t port;
- mach_msg_type_name_t poly;
- if (err = __mach_port_get_refs (__mach_task_self (),
- portnames[i],
- MACH_PORT_RIGHT_SEND,
- &refs))
- LOSE;
- if (err = __mach_port_extract_right (newtask,
- portnames[i],
- MACH_MSG_TYPE_MAKE_SEND,
- &port, &poly))
- LOSE;
- if (portnames[i] == _hurd_msgport)
- {
- /* We just created a receive right for the child's
- message port and are about to insert send rights
- for it. Now, while we happen to have a send right
- for it, give it to the proc server. */
- mach_port_t old;
- if (err = __proc_setmsgport (newproc, port, &old))
- LOSE;
- if (old != MACH_PORT_NULL)
- /* XXX what to do here? */
- __mach_port_deallocate (__mach_task_self (), old);
- /* The new task will receive its own exceptions
- on its message port. */
- if (err =
-#ifdef TASK_EXCEPTION_PORT
- __task_set_special_port (newtask,
- TASK_EXCEPTION_PORT,
- port)
-#elif defined (EXC_MASK_ALL)
- __task_set_exception_ports
- (newtask, EXC_MASK_ALL & ~(EXC_MASK_SYSCALL
- | EXC_MASK_MACH_SYSCALL
- | EXC_MASK_RPC_ALERT),
- port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE)
-#else
-# error task_set_exception_port?
-#endif
- )
- LOSE;
- }
- if (err = __mach_port_insert_right (newtask,
- portnames[i],
- port,
- MACH_MSG_TYPE_MOVE_SEND))
- LOSE;
- if (refs > 1
- && (err = __mach_port_mod_refs (newtask,
- portnames[i],
- MACH_PORT_RIGHT_SEND,
- refs - 1)))
- LOSE;
- }
- if (porttypes[i] & MACH_PORT_TYPE_SEND_ONCE)
- {
- /* Give the child a send-once right for its receive right,
- since we have one for ours. */
- mach_port_t port;
- mach_msg_type_name_t poly;
- if (err = __mach_port_extract_right
- (newtask,
- portnames[i],
- MACH_MSG_TYPE_MAKE_SEND_ONCE,
- &port, &poly))
- LOSE;
- if (err = __mach_port_insert_right
- (newtask,
- portnames[i], port,
- MACH_MSG_TYPE_MOVE_SEND_ONCE))
- LOSE;
- }
- }
- else if (porttypes[i]
- & (MACH_PORT_TYPE_SEND|MACH_PORT_TYPE_DEAD_NAME))
- {
- /* This is a send right or a dead name.
- Give the child as many references for it as we have. */
- mach_port_urefs_t refs = 0, *record_refs = NULL;
- mach_port_t insert;
- mach_msg_type_name_t insert_type = MACH_MSG_TYPE_COPY_SEND;
- if (portnames[i] == newtask || portnames[i] == newproc)
- /* Skip the name we use for the child's task or proc ports. */
- continue;
- if (portnames[i] == __mach_task_self ())
- /* For the name we use for our own task port,
- insert the child's task port instead. */
- insert = newtask;
- else if (portnames[i] == _hurd_ports[INIT_PORT_PROC].port)
- {
- /* Use the proc server port for the new task. */
- insert = newproc;
- insert_type = MACH_MSG_TYPE_COPY_SEND;
- }
- else if (portnames[i] == ss->thread)
- {
- /* For the name we use for our own thread port, we will
- insert the thread port for the child main user thread
- after we create it. */
- insert = MACH_PORT_NULL;
- record_refs = &thread_refs;
- /* Allocate a dead name right for this name as a
- placeholder, so the kernel will not chose this name
- for any other new port (it might use it for one of the
- rights created when a thread is created). */
- if (err = __mach_port_allocate_name
- (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))
- LOSE;
- }
- else if (portnames[i] == _hurd_msgport_thread)
- /* For the name we use for our signal thread's thread port,
- we will insert the thread port for the child's signal
- thread after we create it. */
- {
- insert = MACH_PORT_NULL;
- record_refs = &sigthread_refs;
- /* Allocate a dead name right as a placeholder. */
- if (err = __mach_port_allocate_name
- (newtask, MACH_PORT_RIGHT_DEAD_NAME, portnames[i]))
- LOSE;
- }
- else
- {
- /* Skip the name we use for any of our own thread ports. */
- mach_msg_type_number_t j;
- for (j = 0; j < nthreads; ++j)
- if (portnames[i] == threads[j])
- break;
- if (j < nthreads)
- continue;
-
- /* Copy our own send right. */
- insert = portnames[i];
- }
- /* Find out how many user references we have for
- the send right with this name. */
- if (err = __mach_port_get_refs (__mach_task_self (),
- portnames[i],
- MACH_PORT_RIGHT_SEND,
- record_refs ?: &refs))
- LOSE;
- if (insert == MACH_PORT_NULL)
- continue;
- if (insert == portnames[i]
- && (porttypes[i] & MACH_PORT_TYPE_DEAD_NAME))
- /* This is a dead name; allocate another dead name
- with the same name in the child. */
- allocate_dead_name:
- err = __mach_port_allocate_name (newtask,
- MACH_PORT_RIGHT_DEAD_NAME,
- portnames[i]);
- else
- /* Insert the chosen send right into the child. */
- err = __mach_port_insert_right (newtask,
- portnames[i],
- insert, insert_type);
- switch (err)
- {
- case KERN_NAME_EXISTS:
- {
- /* It already has a send right under this name (?!).
- Well, it starts out with a send right for its task
- port, and inherits the bootstrap and exception ports
- from us. */
- mach_port_t childport;
- mach_msg_type_name_t poly;
- assert (__mach_port_extract_right (newtask, portnames[i],
- MACH_MSG_TYPE_COPY_SEND,
- &childport,
- &poly) == 0
- && childport == insert
- && __mach_port_deallocate (__mach_task_self (),
- childport) == 0);
- break;
- }
-
- case KERN_INVALID_CAPABILITY:
- /* The port just died. It was a send right,
- and now it's a dead name. */
- goto allocate_dead_name;
-
- default:
- LOSE;
- break;
-
- case KERN_SUCCESS:
- /* Give the child as many user references as we have. */
- if (refs > 1
- && (err = __mach_port_mod_refs (newtask,
- portnames[i],
- MACH_PORT_RIGHT_SEND,
- refs - 1)))
- LOSE;
- }
- }
- }
-
- /* Unlock the standard port cells. The child must unlock its own
- copies too. */
- for (i = 0; i < _hurd_nports; ++i)
- __spin_unlock (&_hurd_ports[i].lock);
- ports_locked = 0;
-
- /* All state has now been copied from the parent. It is safe to
- resume other parent threads. */
- resume_threads ();
-
- /* Create the child main user thread and signal thread. */
- if ((err = __thread_create (newtask, &thread))
- || (err = __thread_create (newtask, &sigthread)))
- LOSE;
-
- /* Insert send rights for those threads. We previously allocated
- dead name rights with the names we want to give the thread ports
- in the child as placeholders. Now deallocate them so we can use
- the names. */
- if ((err = __mach_port_deallocate (newtask, ss->thread))
- || (err = __mach_port_insert_right (newtask, ss->thread,
- thread,
- MACH_MSG_TYPE_COPY_SEND)))
- LOSE;
- /* XXX consumed? (_hurd_sigthread is no more) */
- if (thread_refs > 1
- && (err = __mach_port_mod_refs (newtask, ss->thread,
- MACH_PORT_RIGHT_SEND,
- thread_refs - 1)))
- LOSE;
- if ((_hurd_msgport_thread != MACH_PORT_NULL) /* Let user have none. */
- && ((err = __mach_port_deallocate (newtask, _hurd_msgport_thread))
- || (err = __mach_port_insert_right (newtask,
- _hurd_msgport_thread,
- sigthread,
- MACH_MSG_TYPE_COPY_SEND))))
- LOSE;
- if (sigthread_refs > 1
- && (err = __mach_port_mod_refs (newtask, _hurd_msgport_thread,
- MACH_PORT_RIGHT_SEND,
- sigthread_refs - 1)))
- LOSE;
-
- /* This seems like a convenient juncture to copy the proc server's
- idea of what addresses our argv and envp are found at from the
- parent into the child. Since we happen to know that the child
- shares our memory image, it is we who should do this copying. */
- {
- vm_address_t argv, envp;
- err = (__USEPORT (PROC, __proc_get_arg_locations (port, &argv, &envp))
- ?: __proc_set_arg_locations (newproc, argv, envp));
- if (err)
- LOSE;
- }
-
- /* Set the child signal thread up to run the msgport server function
- using the same signal thread stack copied from our address space.
- We fetch the state before longjmp'ing it so that miscellaneous
- registers not affected by longjmp (such as i386 segment registers)
- are in their normal default state. */
- statecount = MACHINE_THREAD_STATE_COUNT;
- if (err = __thread_get_state (_hurd_msgport_thread,
- MACHINE_THREAD_STATE_FLAVOR,
- (natural_t *) &state, &statecount))
- LOSE;
-#ifdef STACK_GROWTH_UP
- if (__hurd_sigthread_stack_base == 0)
- {
- state.SP &= __hurd_threadvar_stack_mask;
- state.SP += __hurd_threadvar_stack_offset;
- }
- else
- state.SP = __hurd_sigthread_stack_base;
-#else
- if (__hurd_sigthread_stack_end == 0)
- {
- /* The signal thread has a stack assigned by pthread.
- The threadvar_stack variables conveniently tell us how
- to get to the highest address in the stack, just below
- the per-thread variables. */
- state.SP &= __hurd_threadvar_stack_mask;
- state.SP += __hurd_threadvar_stack_offset;
- }
- else
- state.SP = __hurd_sigthread_stack_end;
-#endif
- MACHINE_THREAD_STATE_SET_PC (&state,
- (unsigned long int) _hurd_msgport_receive);
-
- /* Do special signal thread setup for TLS if needed. */
- if (err = _hurd_tls_fork (sigthread, _hurd_msgport_thread, &state))
- LOSE;
-
- if (err = __thread_set_state (sigthread, MACHINE_THREAD_STATE_FLAVOR,
- (natural_t *) &state, statecount))
- LOSE;
- /* We do not thread_resume SIGTHREAD here because the child
- fork needs to do more setup before it can take signals. */
-
- /* Set the child user thread up to return 1 from the setjmp above. */
- _hurd_longjmp_thread_state (&state, env, 1);
-
- /* Do special thread setup for TLS if needed. */
- if (err = _hurd_tls_fork (thread, ss->thread, &state))
- LOSE;
-
- if (err = __thread_set_state (thread, MACHINE_THREAD_STATE_FLAVOR,
- (natural_t *) &state, statecount))
- LOSE;
-
- /* Get the PID of the child from the proc server. We must do this
- before calling proc_child below, because at that point any
- authorized POSIX.1 process may kill the child task with SIGKILL. */
- if (err = __USEPORT (PROC, __proc_task2pid (port, newtask, &pid)))
- LOSE;
-
- /* Register the child with the proc server. It is important that
- this be that last thing we do before starting the child thread
- running. Once proc_child has been done for the task, it appears
- as a POSIX.1 process. Any errors we get must be detected before
- this point, and the child must have a message port so it responds
- to POSIX.1 signals. */
- if (err = __USEPORT (PROC, __proc_child (port, newtask)))
- LOSE;
-
- /* This must be the absolutely last thing we do; we can't assume that
- the child will remain alive for even a moment once we do this. We
- ignore errors because we have committed to the fork and are not
- allowed to return them after the process becomes visible to
- POSIX.1 (which happened right above when we called proc_child). */
- (void) __thread_resume (thread);
-
- lose:
- if (ports_locked)
- for (i = 0; i < _hurd_nports; ++i)
- __spin_unlock (&_hurd_ports[i].lock);
-
- resume_threads ();
-
- if (newtask != MACH_PORT_NULL)
- {
- if (err)
- __task_terminate (newtask);
- __mach_port_deallocate (__mach_task_self (), newtask);
- }
- if (thread != MACH_PORT_NULL)
- __mach_port_deallocate (__mach_task_self (), thread);
- if (sigthread != MACH_PORT_NULL)
- __mach_port_deallocate (__mach_task_self (), sigthread);
- if (newproc != MACH_PORT_NULL)
- __mach_port_deallocate (__mach_task_self (), newproc);
-
- if (portnames)
- __vm_deallocate (__mach_task_self (),
- (vm_address_t) portnames,
- nportnames * sizeof (*portnames));
- if (porttypes)
- __vm_deallocate (__mach_task_self (),
- (vm_address_t) porttypes,
- nporttypes * sizeof (*porttypes));
- if (threads)
- {
- for (i = 0; i < nthreads; ++i)
- __mach_port_deallocate (__mach_task_self (), threads[i]);
- __vm_deallocate (__mach_task_self (),
- (vm_address_t) threads,
- nthreads * sizeof (*threads));
- }
-
- /* Release malloc locks. */
- _hurd_malloc_fork_parent ();
- call_function_static_weak (__malloc_fork_unlock_parent);
-
- /* Run things that want to run in the parent to restore it to
- normality. Usually prepare hooks and parent hooks are
- symmetrical: the prepare hook arrests state in some way for the
- fork, and the parent hook restores the state for the parent to
- continue executing normally. */
- RUN_HOOK (_hurd_fork_parent_hook, ());
- }
- else
- {
- struct hurd_sigstate *oldstates;
-
- /* We are the child task. Unlock the standard port cells, which were
- locked in the parent when we copied its memory. The parent has
- inserted send rights with the names that were in the cells then. */
- for (i = 0; i < _hurd_nports; ++i)
- __spin_unlock (&_hurd_ports[i].lock);
-
- /* Claim our sigstate structure and unchain the rest: the
- threads existed in the parent task but don't exist in this
- task (the child process). Delay freeing them until later
- because some of the further setup and unlocking might be
- required for free to work. Before we finish cleaning up,
- we will reclaim the signal thread's sigstate structure (if
- it had one). */
- oldstates = _hurd_sigstates;
- if (oldstates == ss)
- oldstates = ss->next;
- else
- {
- while (_hurd_sigstates->next != ss)
- _hurd_sigstates = _hurd_sigstates->next;
- _hurd_sigstates->next = ss->next;
- }
- ss->next = NULL;
- _hurd_sigstates = ss;
- __mutex_unlock (&_hurd_siglock);
- /* Earlier on, the global sigstate may have been tainted and now needs to
- be reinitialized. Nobody is interested in its present state anymore:
- we're not, the signal thread will be restarted, and there are no other
- threads.
-
- We can't simply allocate a fresh global sigstate here, as
- _hurd_thread_sigstate will call malloc and that will deadlock trying
- to determine the current thread's sigstate. */
-#if 0
- _hurd_thread_sigstate_init (_hurd_global_sigstate, MACH_PORT_NULL);
-#else
- /* Only reinitialize the lock -- otherwise we might have to do additional
- setup as done in hurdsig.c:_hurdsig_init. */
- __spin_lock_init (&_hurd_global_sigstate->lock);
-#endif
-
- /* We are one of the (exactly) two threads in this new task, we
- will take the task-global signals. */
- _hurd_sigstate_set_global_rcv (ss);
-
- /* Fetch our new process IDs from the proc server. No need to
- refetch our pgrp; it is always inherited from the parent (so
- _hurd_pgrp is already correct), and the proc server will send us a
- proc_newids notification when it changes. */
- err = __USEPORT (PROC, __proc_getpids (port, &_hurd_pid, &_hurd_ppid,
- &_hurd_orphaned));
-
- /* Forking clears the trace flag and pending masks. */
- __sigemptyset (&_hurdsig_traced);
- __sigemptyset (&_hurd_global_sigstate->pending);
- __sigemptyset (&ss->pending);
-
- __libc_unwind_link_after_fork ();
-
- /* Release malloc locks. */
- _hurd_malloc_fork_child ();
- call_function_static_weak (__malloc_fork_unlock_child);
-
- call_function_static_weak (__nss_database_fork_subprocess,
- &nss_database_data);
-
- /* Run things that want to run in the child task to set up. */
- RUN_HOOK (_hurd_fork_child_hook, ());
-
- /* Set up proc server-assisted fault recovery for the signal thread. */
- _hurdsig_fault_init ();
-
- /* Start the signal thread listening on the message port. */
- if (!err)
- err = __thread_resume (_hurd_msgport_thread);
-
- /* Reclaim the signal thread's sigstate structure and free the
- other old sigstate structures. */
- while (oldstates != NULL)
- {
- struct hurd_sigstate *next = oldstates->next;
-
- if (oldstates->thread == _hurd_msgport_thread)
- {
- /* If we have a second signal state structure then we
- must have been through here before--not good. */
- assert (_hurd_sigstates->next == 0);
- _hurd_sigstates->next = oldstates;
- oldstates->next = 0;
- }
- else
- free (oldstates);
-
- oldstates = next;
- }
-
- /* XXX what to do if we have any errors here? */
-
- pid = 0;
- }
-
- /* Unlock things we locked before creating the child task.
- They are locked in both the parent and child tasks. */
- {
- void *const *p;
- for (p = symbol_set_first_element (_hurd_fork_locks);
- ! symbol_set_end_p (_hurd_fork_locks, p);
- ++p)
- __mutex_unlock (*p);
- }
-
- _hurd_critical_section_unlock (ss);
- if (err == EINTR)
- /* Got a signal while inside an RPC of the critical section, retry again */
- goto retry;
-
- if (!err)
- {
- __run_fork_handlers (pid == 0 ? atfork_run_child : atfork_run_parent,
- true);
- }
-
- return err ? __hurd_fail (err) : pid;
-}
-libc_hidden_def (__fork)
-
-weak_alias (__fork, fork)
--- /dev/null
+/* _Fork implementation. Linux version.
+ Copyright (C) 2021 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <arch-fork.h>
+#include <pthreadP.h>
+
+/* Pointer to the fork generation counter in the thread library. */
+extern unsigned long int *__fork_generation_pointer attribute_hidden;
+
+pid_t
+_Fork (void)
+{
+ pid_t pid = arch_fork (&THREAD_SELF->tid);
+ if (pid == 0)
+ {
+ struct pthread *self = THREAD_SELF;
+
+ /* Initialize the robust mutex list setting in the kernel which has
+ been reset during the fork. We do not check for errors because if
+ it fails here, it must have failed at process startup as well and
+ nobody could have used robust mutexes.
+ Before we do that, we have to clear the list of robust mutexes
+ because we do not inherit ownership of mutexes from the parent.
+ We do not have to set self->robust_head.futex_offset since we do
+ inherit the correct value from the parent. We do not need to clear
+ the pending operation because it must have been zero when fork was
+ called. */
+#if __PTHREAD_MUTEX_HAVE_PREV
+ self->robust_prev = &self->robust_head;
+#endif
+ self->robust_head.list = &self->robust_head;
+ INTERNAL_SYSCALL_CALL (set_robust_list, &self->robust_head,
+ sizeof (struct robust_list_head));
+ }
+ return pid;
+}
+libc_hidden_def (_Fork)
+++ /dev/null
-/* Copyright (C) 2002-2021 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
- Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
-
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with the GNU C Library; if not, see
- <https://www.gnu.org/licenses/>. */
-
-#include <assert.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sysdep.h>
-#include <libio/libioP.h>
-#include <tls.h>
-#include <hp-timing.h>
-#include <ldsodefs.h>
-#include <stdio-lock.h>
-#include <atomic.h>
-#include <pthreadP.h>
-#include <register-atfork.h>
-#include <arch-fork.h>
-#include <futex-internal.h>
-#include <malloc/malloc-internal.h>
-#include <nss/nss_database.h>
-#include <unwind-link.h>
-#include <sys/single_threaded.h>
-#include <list.h>
-
-static void
-fresetlockfiles (void)
-{
- _IO_ITER i;
-
- for (i = _IO_iter_begin(); i != _IO_iter_end(); i = _IO_iter_next(i))
- if ((_IO_iter_file (i)->_flags & _IO_USER_LOCK) == 0)
- _IO_lock_init (*((_IO_lock_t *) _IO_iter_file(i)->_lock));
-}
-
-/* In case of a fork() call the memory allocation in the child will be
- the same but only one thread is running. All stacks except that of
- the one running thread are not used anymore. We have to recycle
- them. */
-static void
-reclaim_stacks (void)
-{
- struct pthread *self = (struct pthread *) THREAD_SELF;
-
- /* No locking necessary. The caller is the only stack in use. But
- we have to be aware that we might have interrupted a list
- operation. */
-
- if (GL (dl_in_flight_stack) != 0)
- {
- bool add_p = GL (dl_in_flight_stack) & 1;
- list_t *elem = (list_t *) (GL (dl_in_flight_stack) & ~(uintptr_t) 1);
-
- if (add_p)
- {
- /* We always add at the beginning of the list. So in this case we
- only need to check the beginning of these lists to see if the
- pointers at the head of the list are inconsistent. */
- list_t *l = NULL;
-
- if (GL (dl_stack_used).next->prev != &GL (dl_stack_used))
- l = &GL (dl_stack_used);
- else if (GL (dl_stack_cache).next->prev != &GL (dl_stack_cache))
- l = &GL (dl_stack_cache);
-
- if (l != NULL)
- {
- assert (l->next->prev == elem);
- elem->next = l->next;
- elem->prev = l;
- l->next = elem;
- }
- }
- else
- {
- /* We can simply always replay the delete operation. */
- elem->next->prev = elem->prev;
- elem->prev->next = elem->next;
- }
-
- GL (dl_in_flight_stack) = 0;
- }
-
- /* Mark all stacks except the still running one as free. */
- list_t *runp;
- list_for_each (runp, &GL (dl_stack_used))
- {
- struct pthread *curp = list_entry (runp, struct pthread, list);
- if (curp != self)
- {
- /* This marks the stack as free. */
- curp->tid = 0;
-
- /* Account for the size of the stack. */
- GL (dl_stack_cache_actsize) += curp->stackblock_size;
-
- if (curp->specific_used)
- {
- /* Clear the thread-specific data. */
- memset (curp->specific_1stblock, '\0',
- sizeof (curp->specific_1stblock));
-
- curp->specific_used = false;
-
- for (size_t cnt = 1; cnt < PTHREAD_KEY_1STLEVEL_SIZE; ++cnt)
- if (curp->specific[cnt] != NULL)
- {
- memset (curp->specific[cnt], '\0',
- sizeof (curp->specific_1stblock));
-
- /* We have allocated the block which we do not
- free here so re-set the bit. */
- curp->specific_used = true;
- }
- }
- }
- }
-
- /* Add the stack of all running threads to the cache. */
- list_splice (&GL (dl_stack_used), &GL (dl_stack_cache));
-
- /* Remove the entry for the current thread to from the cache list
- and add it to the list of running threads. Which of the two
- lists is decided by the user_stack flag. */
- list_del (&self->list);
-
- /* Re-initialize the lists for all the threads. */
- INIT_LIST_HEAD (&GL (dl_stack_used));
- INIT_LIST_HEAD (&GL (dl_stack_user));
-
- if (__glibc_unlikely (THREAD_GETMEM (self, user_stack)))
- list_add (&self->list, &GL (dl_stack_user));
- else
- list_add (&self->list, &GL (dl_stack_used));
-}
-
-pid_t
-__libc_fork (void)
-{
- pid_t pid;
-
- /* Determine if we are running multiple threads. We skip some fork
- handlers in the single-thread case, to make fork safer to use in
- signal handlers. POSIX requires that fork is async-signal-safe,
- but our current fork implementation is not. */
- bool multiple_threads = __libc_single_threaded == 0;
-
- __run_fork_handlers (atfork_run_prepare, multiple_threads);
-
- struct nss_database_data nss_database_data;
-
- /* If we are not running multiple threads, we do not have to
- preserve lock state. If fork runs from a signal handler, only
- async-signal-safe functions can be used in the child. These data
- structures are only used by unsafe functions, so their state does
- not matter if fork was called from a signal handler. */
- if (multiple_threads)
- {
- call_function_static_weak (__nss_database_fork_prepare_parent,
- &nss_database_data);
-
- _IO_list_lock ();
-
- /* Acquire malloc locks. This needs to come last because fork
- handlers may use malloc, and the libio list lock has an
- indirect malloc dependency as well (via the getdelim
- function). */
- call_function_static_weak (__malloc_fork_lock_parent);
- }
-
- pid = arch_fork (&THREAD_SELF->tid);
-
- if (pid == 0)
- {
- struct pthread *self = THREAD_SELF;
-
- /* See __pthread_once. */
- __fork_generation += __PTHREAD_ONCE_FORK_GEN_INCR;
-
- /* Initialize the robust mutex list setting in the kernel which has
- been reset during the fork. We do not check for errors because if
- it fails here, it must have failed at process startup as well and
- nobody could have used robust mutexes.
- Before we do that, we have to clear the list of robust mutexes
- because we do not inherit ownership of mutexes from the parent.
- We do not have to set self->robust_head.futex_offset since we do
- inherit the correct value from the parent. We do not need to clear
- the pending operation because it must have been zero when fork was
- called. */
-#if __PTHREAD_MUTEX_HAVE_PREV
- self->robust_prev = &self->robust_head;
-#endif
- self->robust_head.list = &self->robust_head;
- INTERNAL_SYSCALL_CALL (set_robust_list, &self->robust_head,
- sizeof (struct robust_list_head));
-
- /* Reset the lock state in the multi-threaded case. */
- if (multiple_threads)
- {
- __libc_unwind_link_after_fork ();
-
- /* There is one thread running. */
- __nptl_nthreads = 1;
-
- /* Initialize thread library locks. */
- GL (dl_stack_cache_lock) = LLL_LOCK_INITIALIZER;
- __default_pthread_attr_lock = LLL_LOCK_INITIALIZER;
-
- /* Release malloc locks. */
- call_function_static_weak (__malloc_fork_unlock_child);
-
- /* Reset the file list. These are recursive mutexes. */
- fresetlockfiles ();
-
- /* Reset locks in the I/O code. */
- _IO_list_resetlock ();
-
- call_function_static_weak (__nss_database_fork_subprocess,
- &nss_database_data);
- }
-
- /* Reset the lock the dynamic loader uses to protect its data. */
- __rtld_lock_initialize (GL(dl_load_lock));
-
- reclaim_stacks ();
-
- /* Run the handlers registered for the child. */
- __run_fork_handlers (atfork_run_child, multiple_threads);
- }
- else
- {
- /* Release acquired locks in the multi-threaded case. */
- if (multiple_threads)
- {
- /* Release malloc locks, parent process variant. */
- call_function_static_weak (__malloc_fork_unlock_parent);
-
- /* We execute this even if the 'fork' call failed. */
- _IO_list_unlock ();
- }
-
- /* Run the handlers registered for the parent. */
- __run_fork_handlers (atfork_run_parent, multiple_threads);
- }
-
- return pid;
-}
-weak_alias (__libc_fork, __fork)
-libc_hidden_def (__fork)
-weak_alias (__libc_fork, fork)
--- /dev/null
+/* System specific fork hooks. Linux version.
+ Copyright (C) 2021 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _FORK_H
+#define _FORK_H
+
+#include <assert.h>
+#include <ldsodefs.h>
+#include <list.h>
+#include <pthreadP.h>
+#include <sysdep.h>
+
+static inline void
+fork_system_setup (void)
+{
+ /* See __pthread_once. */
+ __fork_generation += __PTHREAD_ONCE_FORK_GEN_INCR;
+}
+
+static void
+fork_system_setup_after_fork (void)
+{
+ /* There is one thread running. */
+ __nptl_nthreads = 1;
+
+ /* Initialize thread library locks. */
+ GL (dl_stack_cache_lock) = LLL_LOCK_INITIALIZER;
+ __default_pthread_attr_lock = LLL_LOCK_INITIALIZER;
+}
+
+/* In case of a fork() call the memory allocation in the child will be
+ the same but only one thread is running. All stacks except that of
+ the one running thread are not used anymore. We have to recycle
+ them. */
+static void
+reclaim_stacks (void)
+{
+ struct pthread *self = (struct pthread *) THREAD_SELF;
+
+ /* No locking necessary. The caller is the only stack in use. But
+ we have to be aware that we might have interrupted a list
+ operation. */
+
+ if (GL (dl_in_flight_stack) != 0)
+ {
+ bool add_p = GL (dl_in_flight_stack) & 1;
+ list_t *elem = (list_t *) (GL (dl_in_flight_stack) & ~(uintptr_t) 1);
+
+ if (add_p)
+ {
+ /* We always add at the beginning of the list. So in this case we
+ only need to check the beginning of these lists to see if the
+ pointers at the head of the list are inconsistent. */
+ list_t *l = NULL;
+
+ if (GL (dl_stack_used).next->prev != &GL (dl_stack_used))
+ l = &GL (dl_stack_used);
+ else if (GL (dl_stack_cache).next->prev != &GL (dl_stack_cache))
+ l = &GL (dl_stack_cache);
+
+ if (l != NULL)
+ {
+ assert (l->next->prev == elem);
+ elem->next = l->next;
+ elem->prev = l;
+ l->next = elem;
+ }
+ }
+ else
+ {
+ /* We can simply always replay the delete operation. */
+ elem->next->prev = elem->prev;
+ elem->prev->next = elem->next;
+ }
+
+ GL (dl_in_flight_stack) = 0;
+ }
+
+ /* Mark all stacks except the still running one as free. */
+ list_t *runp;
+ list_for_each (runp, &GL (dl_stack_used))
+ {
+ struct pthread *curp = list_entry (runp, struct pthread, list);
+ if (curp != self)
+ {
+ /* This marks the stack as free. */
+ curp->tid = 0;
+
+ /* Account for the size of the stack. */
+ GL (dl_stack_cache_actsize) += curp->stackblock_size;
+
+ if (curp->specific_used)
+ {
+ /* Clear the thread-specific data. */
+ memset (curp->specific_1stblock, '\0',
+ sizeof (curp->specific_1stblock));
+
+ curp->specific_used = false;
+
+ for (size_t cnt = 1; cnt < PTHREAD_KEY_1STLEVEL_SIZE; ++cnt)
+ if (curp->specific[cnt] != NULL)
+ {
+ memset (curp->specific[cnt], '\0',
+ sizeof (curp->specific_1stblock));
+
+ /* We have allocated the block which we do not
+ free here so re-set the bit. */
+ curp->specific_used = true;
+ }
+ }
+ }
+ }
+
+ /* Add the stack of all running threads to the cache. */
+ list_splice (&GL (dl_stack_used), &GL (dl_stack_cache));
+
+ /* Remove the entry for the current thread to from the cache list
+ and add it to the list of running threads. Which of the two
+ lists is decided by the user_stack flag. */
+ list_del (&self->list);
+
+ /* Re-initialize the lists for all the threads. */
+ INIT_LIST_HEAD (&GL (dl_stack_used));
+ INIT_LIST_HEAD (&GL (dl_stack_user));
+
+ if (__glibc_unlikely (THREAD_GETMEM (self, user_stack)))
+ list_add (&self->list, &GL (dl_stack_user));
+ else
+ list_add (&self->list, &GL (dl_stack_used));
+}
+
+
+#endif
#ifndef __ARCH_FORK_H
#define __ARCH_FORK_H
+#include <sysdep.h>
+#include <sched.h>
+#include <signal.h>
#include <unistd.h>
/* Call the clone syscall with fork semantic. The CTID address is used