aboutsummaryrefslogtreecommitdiff
path: root/sysdeps/mach/hurd/x86/init-first.c
blob: 48c330ec0674c356d84dcf3df91d798b85c83588 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/* Initialization code run first thing by the ELF startup code.  For x86/Hurd.
   Copyright (C) 1995-2023 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 <assert.h>
#include <hurd.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sysdep.h>
#include <dl-sysdep.h>
#include <set-hooks.h>
#include "hurdstartup.h"

#include <ldsodefs.h>
#include <fpu_control.h>
#include <libc-diag.h>
#include <libc-internal.h>

extern void __mach_init (void);
extern void __init_misc (int, char **, char **);

extern int __libc_argc attribute_hidden;
extern char **__libc_argv attribute_hidden;
extern char **_dl_argv;

#ifndef SHARED
static tcbhead_t __init1_tcbhead;
# ifndef __x86_64__
unsigned short __init1_desc;
# endif
#endif

#ifdef __x86_64__
unsigned char __libc_tls_initialized;
#endif

/* Things that want to be run before _hurd_init or much anything else.
   Importantly, these are called before anything tries to use malloc.  */
DEFINE_HOOK (_hurd_preinit_hook, (void));


/* We call this once the Hurd magic is all set up and we are ready to be a
   Posixoid program.  This does the same things the generic version does.  */
static void
posixland_init (int argc, char **argv, char **envp)
{
  /* Now we have relocations etc. we can start signals etc.  */
  _hurd_libc_proc_init (argv);

#ifdef SHARED
  /* Make sure we don't initialize twice.  */
  if (__libc_initial)
    {
      /* Set the FPU control word to the proper default value.  */
      __setfpucw (__fpu_control);
    }
  else
    {
      /* Initialize data structures so the additional libc can do RPCs.  */
      __mach_init ();
    }
#else /* !SHARED */
  __setfpucw (__fpu_control);
#endif

  /* Save the command-line arguments.  */
  __libc_argc = argc;
  __libc_argv = argv;
  __environ = envp;

#ifndef SHARED
  _dl_non_dynamic_init ();
#endif
  __init_misc (argc, argv, envp);
}

static void
init (void **data)
{
  int argc = (int) (uintptr_t) *data;
  char **argv = (void *) (data + 1);
  char **envp = &argv[argc + 1];
  struct hurd_startup_data *d;

  /* Since the cthreads initialization code uses malloc, and the
     malloc initialization code needs to get at the environment, make
     sure we can find it.  We'll need to do this again later on since
     switching stacks changes the location where the environment is
     stored.  */
  __environ = envp;

  while (*envp)
    ++envp;
  d = (void *) ++envp;

#ifndef SHARED

  /* If we are the bootstrap task started by the kernel,
     then after the environment pointers there is no Hurd
     data block; the argument strings start there.  */
  if ((void *) d == argv[0] || d->phdr == 0)
    {
      __libc_enable_secure = 0;
      /* With a new enough linker (binutils-2.23 or better),
         the magic __ehdr_start symbol will be available and
         __libc_start_main will have done this that way already.  */
      if (_dl_phdr == NULL)
        {
          /* We may need to see our own phdrs, e.g. for TLS setup.
             Try the usual kludge to find the headers without help from
             the exec server.  */
          extern const void __executable_start;
          const ElfW(Ehdr) *const ehdr = &__executable_start;
          _dl_phdr = (const void *) ehdr + ehdr->e_phoff;
          _dl_phnum = ehdr->e_phnum;
          assert (ehdr->e_phentsize == sizeof (ElfW(Phdr)));
        }
    }
  else
    {
      __libc_enable_secure = d->flags & EXEC_SECURE;
      _dl_phdr = (ElfW(Phdr) *) d->phdr;
      _dl_phnum = d->phdrsz / sizeof (ElfW(Phdr));
      assert (d->phdrsz % sizeof (ElfW(Phdr)) == 0);
    }
#endif

  if ((void *) d == argv[0])
    return;

  _hurd_init_dtable = d->dtable;
  _hurd_init_dtablesize = d->dtablesize;

  if (d->portarray || d->intarray)
    /* Initialize library data structures, start signal processing, etc.  */
    _hurd_init (d->flags, argv,
		d->portarray, d->portarraysize,
		d->intarray, d->intarraysize);
}

/* Do the first essential initializations that must precede all else.  */
static inline void
first_init (void)
{
  /* Initialize data structures so we can do RPCs.  */
  __mach_init ();

#ifndef SHARED
  /* In the static case, we need to set up TLS early so that the stack
     protection guard can be read at gs:0x14 by the gcc-generated snippets.  */
  _hurd_tls_init (&__init1_tcbhead);

  /* Make sure __LIBC_NO_TLS () keeps evaluating to 1.  */
# ifdef __x86_64__
  __libc_tls_initialized = 0;
# else
  asm ("movw %%gs,%w0" : "=m" (__init1_desc));
# endif
#endif

  RUN_RELHOOK (_hurd_preinit_hook, ());
}

#ifdef SHARED
/* This function is called specially by the dynamic linker to do early
   initialization of the shared C library before normal initializers
   expecting a Posixoid environment can run.  */

void
_dl_init_first (void *data)
{
  first_init ();
  init (data);
}

/* The regular posixland initialization is what goes into libc's
   normal initializer.  */
/* NOTE!  The linker notices the magical name `_init' and sets the DT_INIT
   pointer in the dynamic section based solely on that.  It is convention
   for this function to be in the `.init' section, but the symbol name is
   the only thing that really matters!!  */
strong_alias (posixland_init, _init);

void
__libc_init_first (int argc, char **argv, char **envp)
{
  /* Everything was done in the shared library initializer, _init.  */
}

#else /* SHARED */

strong_alias (posixland_init, __libc_init_first);

/* XXX This is all a crock and I am not happy with it.
   This poorly-named function is called by static-start.S,
   which should not exist at all.  */
void
inhibit_stack_protector
_hurd_stack_setup (void)
{
  /* This is the very first C code that runs in a statically linked
     executable -- calling this function is the first thing that _start in
     static-start.S does.  Once this function returns, the unusual way that it
     does (see below), _start jumps to _start1, the regular start-up code.

     _start1 expects the arguments, environment, and a Hurd data block to be
     located at the top of the stack.  The data may already be located there,
     or we may need to receive it from the exec server.  */
  void *caller = __builtin_extract_return_addr (__builtin_return_address (0));
  /* If the arguments and environment are already located on the stack, this is
     where they are, just above our call frame.  Note that this may not be a
     valid pointer in case we're supposed to receive the arguments from the exec
     server, so we can not dereference it yet.  */
  void **p = (void **) __builtin_frame_address (0) + 2;

  /* Init the essential things.  */
  first_init ();

  void doinit (intptr_t *data)
    {
      init ((void **) data);
# ifdef __x86_64__
      asm volatile ("movq %0, %%rsp\n" /* Switch to new outermost stack.  */
                    "xorq %%rbp, %%rbp\n" /* Clear outermost frame pointer.  */
                    "jmp *%1" : : "r" (data), "r" (caller));
# else
      asm volatile ("movl %0, %%esp\n" /* Switch to new outermost stack.  */
		    "xorl %%ebp, %%ebp\n" /* Clear outermost frame pointer.  */
		    "jmp *%1" : : "r" (data), "r" (caller));
# endif
      __builtin_unreachable ();
    }

  /* _hurd_startup () will attempt to receive the data block from the exec
     server; or if that is not possible, will take the data from the pointer
     we pass it here.  The important point here is that the data
     _hurd_startup () collects may be allocated in its stack frame (with
     alloca), which is why _hurd_startup () does not return the normal way.
     Instead, it invokes a callback (which is not expected to return normally
     either).

     Our callback not only passes the data pointer to init (), but also jumps
     out of the call stack back to our caller (i.e. to _start1), while setting
     the stack pointer to the data (which is somewhere on the current stack
     anyway).  This way, _start1 find the data on the top of the stack, just as
     it expects to.  */
  _hurd_startup (p, &doinit);
  __builtin_unreachable ();
}
#endif


/* This function is defined here so that if this file ever gets into
   ld.so we will get a link error.  Having this file silently included
   in ld.so causes disaster, because the _init definition above will
   cause ld.so to gain an init function, which is not a cool thing. */

void
_dl_start (void)
{
  abort ();
}