-/* Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
-This file is part of the GNU C Library.
+/* Copyright (C) 1994-2012 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 Library General Public License as
-published by the Free Software Foundation; either version 2 of the
-License, or (at your option) any later version.
+ 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
-Library General Public License for more details.
+ 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 Library General Public
-License along with the GNU C Library; see the file COPYING.LIB. If
-not, write to the Free Software Foundation, Inc., 675 Mass Ave,
-Cambridge, MA 02139, USA. */
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
#include <errno.h>
#include <unistd.h>
#include <hurd.h>
#include <hurd/signal.h>
#include <setjmp.h>
-#include "thread_state.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>
+
+#undef __fork
/* Things that want to be locked while forking. */
struct hurd_sigstate *volatile ss;
ss = _hurd_self_sigstate ();
- __spin_lock (&ss->lock);
- ss->critical_section = 1;
- __spin_unlock (&ss->lock);
+ __spin_lock (&ss->critical_section_lock);
#undef LOSE
-#define LOSE assert_perror (err) /* XXX */
+#define LOSE do { assert_perror (err); goto lose; } while (0) /* XXX */
if (! setjmp (env))
{
so nothing changes. */
err = __proc_dostop (_hurd_ports[INIT_PORT_PROC].port, ss->thread);
if (!err)
- {
- stopped = 1;
+ {
+ stopped = 1;
#define XXX_KERNEL_PAGE_FAULT_BUG /* XXX work around page fault bug in mk */
__thread_abort (_hurd_msgport_thread);
#endif
/* Create the child task. It will inherit a copy of our memory. */
- err = __task_create (__mach_task_self (), 1, &newtask);
- }
+ 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. */
__mach_port_deallocate (__mach_task_self (), old);
/* The new task will receive its own exceptions
on its message port. */
- if (err = __task_set_special_port (newtask,
- TASK_EXCEPTION_PORT,
- 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,
{
/* 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, *record_refs = NULL;
+ mach_port_urefs_t refs = 0, *record_refs = NULL;
mach_port_t insert;
- if (portnames[i] == newtask)
- /* Skip the name we use for the child's task port. */
+ 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 = newtask;
else if (portnames[i] == _hurd_ports[INIT_PORT_PROC].port)
{
- /* Get the proc server port for the new task. */
- if (err = __proc_task2proc (portnames[i], newtask, &insert))
- LOSE;
+ /* Use the proc server port for the new task. */
+ insert = newproc;
+ insert_type = MACH_MSG_TYPE_COPY_SEND;
}
else if (portnames[i] == ss->thread)
{
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). */
+ 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;
if (j < nthreads)
continue;
+ /* Copy our own send right. */
insert = portnames[i];
}
/* Find out how many user references we have for
/* Insert the chosen send right into the child. */
err = __mach_port_insert_right (newtask,
portnames[i],
- insert,
- MACH_MSG_TYPE_COPY_SEND);
+ insert, insert_type);
switch (err)
{
case KERN_NAME_EXISTS:
ports_locked = 0;
/* All state has now been copied from the parent. It is safe to
- resume other parent threads. */
+ resume other parent threads. */
resume_threads ();
/* Create the child main user thread and signal thread. */
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. */
+ 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)))
(natural_t *) &state, &statecount))
LOSE;
#if STACK_GROWTH_UP
- state.SP = __hurd_sigthread_stack_base;
+#define THREADVAR_SPACE (__hurd_threadvar_max \
+ * sizeof *__hurd_sightread_variables)
+ if (__hurd_sigthread_stack_base == 0)
+ {
+ state.SP &= __hurd_threadvar_stack_mask;
+ state.SP += __hurd_threadvar_stack_offset + THREADVAR_SPACE;
+ }
+ else
+ state.SP = __hurd_sigthread_stack_base;
#else
- state.SP = __hurd_sigthread_stack_end;
+ if (__hurd_sigthread_stack_end == 0)
+ {
+ /* The signal thread has a normal stack assigned by cthreads.
+ 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);
/* 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, &state))
+ LOSE;
+
if (err = __thread_set_state (thread, MACHINE_THREAD_STATE_FLAVOR,
(natural_t *) &state, statecount))
LOSE;
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. */
+ 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);
- /* We are the only thread in this new task, so we will
- take the task-global signals. */
+ /* We are one of the (exactly) two threads in this new task, we
+ will take the task-global signals. */
_hurd_sigthread = ss->thread;
- /* Unchain the sigstate structures for threads that 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. */
+ /* 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;
&_hurd_orphaned));
/* Forking clears the trace flag. */
- _hurd_exec_flags &= ~EXEC_TRACED;
+ __sigemptyset (&_hurdsig_traced);
/* Run things that want to run in the child task to set up. */
RUN_HOOK (_hurd_fork_child_hook, ());
if (!err)
err = __thread_resume (_hurd_msgport_thread);
- /* Free the old sigstate structures. */
+ /* Reclaim the signal thread's sigstate structure and free the
+ other old sigstate structures. */
while (oldstates != NULL)
{
struct hurd_sigstate *next = oldstates->next;
- free (oldstates);
+
+ 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;
return err ? __hurd_fail (err) : pid;
}
+libc_hidden_def (__fork)
weak_alias (__fork, fork)