by function return.  */ asm volatile ("# a %0 c %1" : : "a" (&init1), "c" (newsp)); return; switch_stacks:  asm volatile ("movl %ecx, %esp; jmp *%eax");  }
by function return.  */ asm volatile ("# a %0 c %1" : : "a" (newsp), "c" (&init1)); }
init (int *data, void *usercode, void **retaddrloc){  int argc = *data;  char **argv = (void *) (data + 1);  char **envp = &argv[argc + 1];  struct hurd_startup_data *d;  __environ = envp;  while (*envp)    ++envp;  d = (void *) ++envp;  /* The user might have defined a value for this, to get more variables.     Otherwise it will be zero on startup.  We must make sure it is set     properly before before cthreads initialization, so cthreads can know     how much space to leave for thread variables.  */  if (__hurd_threadvar_max < _HURD_THREADVAR_MAX)    __hurd_threadvar_max = _HURD_THREADVAR_MAX;  /* After possibly switching stacks, call `init1' (above) with the user     code as the return address, and the argument data immediately above     that on the stack.  */  if (_cthread_init_routine)    {      /* Initialize cthreads, which will allocate us a new stack to run on.  */      void *newsp = (*_cthread_init_routine) ();      struct hurd_startup_data *od;      /* Copy the argdata from the old stack to the new one.  */      newsp = memcpy (newsp - ((char *) &d[1] - (char *) data), data,		      (char *) d - (char *) data);      /* Set up the Hurd startup data block immediately following	 the argument and environment pointers on the new stack.  */      od = (newsp + ((char *) d - (char *) data));      if ((void *) argv[0] == d)	/* We were started up by the kernel with arguments on the stack.	   There is no Hurd startup data, so zero the block.  */	memset (od, 0, sizeof *od);      else	/* Copy the Hurd startup data block to the new stack.  */	*od = *d;      /* Push the user code address on the top of the new stack.  It will	 be the return address for `init1'; we will jump there with NEWSP	 as the stack pointer.  */      *--(void **) newsp = usercode;      /* Mutate our own return address to run the code below.  */      *retaddrloc = &&switch_stacks;      /* Force NEWSP into %ecx and &init1 into %eax, which are not restored         by function return.  */      asm volatile ("# a %0 c %1" : : "a" (&init1), "c" (newsp));      return;    switch_stacks:      /* Our return address was redirected to here, so at this point our	 stack is unwound and callers' registers restored.  Only %ecx and	 %eax are call-clobbered and thus still have the values we set just	 above.  Fetch from there the new stack pointer we will run on, and	 jmp to the run-time address of `init1'; when it returns, it will	 run the user code with the argument data at the top of the stack.  */      asm volatile ("movl %ecx, %esp; jmp *%eax");      /* NOTREACHED */    }  else    {      /* We are not switching stacks, but we must play some games with	 the one we've got, similar to the stack-switching code above.  */      *retaddrloc = &&call_init1;      /* Force the user code address into %ecx and the run-time address of	 `init1' into %eax, for use below.  */      asm volatile ("# a %0 c %1" : : "a" (&init1), "c" (usercode));      return;    call_init1:      /* As in the stack-switching case, at this point our stack is unwound	 and callers' registers restored, and only %ecx and %eax	 communicate values from the lines above.  In this case we have	 stashed in %ecx the user code return address.  Push it on the top	 of the stack so it acts as init1's return address, and then jump	 there.  */      asm volatile ("pushl %ecx; jmp *%eax");      /* NOTREACHED */    }}  