crt0-common.c revision 1.30 1 /* $NetBSD: crt0-common.c,v 1.30 2025/05/02 23:04:06 riastradh Exp $ */
2
3 /*
4 * Copyright (c) 1998 Christos Zoulas
5 * Copyright (c) 1995 Christopher G. Demetriou
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed for the
19 * NetBSD Project. See http://www.NetBSD.org/ for
20 * information about NetBSD.
21 * 4. The name of the author may not be used to endorse or promote products
22 * derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 * <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>>
36 */
37
38 #include <sys/cdefs.h>
39 __RCSID("$NetBSD: crt0-common.c,v 1.30 2025/05/02 23:04:06 riastradh Exp $");
40
41 #include <sys/types.h>
42 #include <sys/exec.h>
43 #include <sys/exec_elf.h>
44 #include <sys/syscall.h>
45
46 #include <machine/profile.h>
47
48 #include <limits.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51
52 #include "csu-common.h"
53
54 extern int main(int, char **, char **);
55
56 typedef void (*fptr_t)(void);
57 #ifndef HAVE_INITFINI_ARRAY
58 extern void _init(void);
59 extern void _fini(void);
60 #endif
61
62 /*
63 * Arrange for _DYNAMIC to be weak and undefined (and therefore to show up
64 * as being at address zero, unless something else defines it). That way,
65 * if we happen to be compiling without -static but with without any
66 * shared libs present, things will still work.
67 */
68
69 __weakref_visible int rtld_DYNAMIC __weak_reference(_DYNAMIC);
70
71 #ifdef MCRT0
72 extern void monstartup(u_long, u_long);
73 extern void _mcleanup(void);
74 extern unsigned char __etext, __eprol;
75 #endif /* MCRT0 */
76
77 static char empty_string[] = "";
78
79 char **environ __common;
80 struct ps_strings *__ps_strings __common = 0;
81 char *__progname __common = empty_string;
82
83 __dead __dso_hidden void ___start(void (*)(void), struct ps_strings *);
84
85 #define write(fd, s, n) __syscall(SYS_write, (fd), (s), (n))
86
87 #define _FATAL(str) \
88 do { \
89 write(2, str, sizeof(str)-1); \
90 _exit(1); \
91 } while (0)
92
93 /*
94 * If we are using INIT_ARRAY/FINI_ARRAY and we are linked statically,
95 * we have to process these instead of relying on RTLD to do it for us.
96 *
97 * Since we don't need .init or .fini sections, just code them in C
98 * to make life easier.
99 */
100 extern const fptr_t __preinit_array_start[] __dso_hidden;
101 extern const fptr_t __preinit_array_end[] __dso_hidden __weak;
102 extern const fptr_t __init_array_start[] __dso_hidden;
103 extern const fptr_t __init_array_end[] __dso_hidden __weak;
104 extern const fptr_t __fini_array_start[] __dso_hidden;
105 extern const fptr_t __fini_array_end[] __dso_hidden __weak;
106
107 static inline void
108 _preinit(void)
109 {
110 for (const fptr_t *f = __preinit_array_start; f < __preinit_array_end; f++) {
111 (*f)();
112 }
113 }
114
115 static inline void
116 _initarray(void)
117 {
118 for (const fptr_t *f = __init_array_start; f < __init_array_end; f++) {
119 (*f)();
120 }
121 }
122
123 static void
124 _finiarray(void)
125 {
126 for (const fptr_t *f = __fini_array_start; f < __fini_array_end; f++) {
127 (*f)();
128 }
129 }
130
131 #if \
132 defined(__aarch64__) || \
133 defined(__powerpc__) || \
134 defined(__sparc__) || \
135 defined(__x86_64__)
136 #define HAS_IPLTA
137 static void fix_iplta(void) __noinline;
138 #elif \
139 defined(__arm__) || \
140 defined(__i386__)
141 #define HAS_IPLT
142 static void fix_iplt(void) __noinline;
143 #endif
144
145
146 #ifdef HAS_IPLTA
147 #include <stdio.h>
148 extern const Elf_Rela __rela_iplt_start[] __dso_hidden __weak;
149 extern const Elf_Rela __rela_iplt_end[] __dso_hidden __weak;
150 #ifdef __sparc__
151 #define IFUNC_RELOCATION R_TYPE(JMP_IREL)
152 #include <machine/elf_support.h>
153 #define write_plt(where, value) sparc_write_branch((void *)where, (void *)value)
154 #else
155 #define IFUNC_RELOCATION R_TYPE(IRELATIVE)
156 #define write_plt(where, value) *where = value
157 #endif
158
159 static void
160 fix_iplta(void)
161 {
162 const Elf_Rela *rela, *relalim;
163 uintptr_t relocbase = 0;
164 Elf_Addr *where, target;
165
166 rela = __rela_iplt_start;
167 relalim = __rela_iplt_end;
168 for (; rela < relalim; ++rela) {
169 if (ELF_R_TYPE(rela->r_info) != IFUNC_RELOCATION)
170 abort();
171 where = (Elf_Addr *)(relocbase + rela->r_offset);
172 target = (Elf_Addr)(relocbase + rela->r_addend);
173 target = ((Elf_Addr(*)(void))target)();
174 write_plt(where, target);
175 }
176 }
177 #endif
178 #ifdef HAS_IPLT
179 extern const Elf_Rel __rel_iplt_start[] __dso_hidden __weak;
180 extern const Elf_Rel __rel_iplt_end[] __dso_hidden __weak;
181 #define IFUNC_RELOCATION R_TYPE(IRELATIVE)
182
183 static void
184 fix_iplt(void)
185 {
186 const Elf_Rel *rel, *rellim;
187 uintptr_t relocbase = 0;
188 Elf_Addr *where, target;
189
190 rel = __rel_iplt_start;
191 rellim = __rel_iplt_end;
192 for (; rel < rellim; ++rel) {
193 if (ELF_R_TYPE(rel->r_info) != IFUNC_RELOCATION)
194 abort();
195 where = (Elf_Addr *)(relocbase + rel->r_offset);
196 target = ((Elf_Addr(*)(void))*where)();
197 *where = target;
198 }
199 }
200 #endif
201
202 #if defined(__x86_64__) || defined(__i386__)
203 # define HAS_RELOCATE_SELF
204 # if defined(__x86_64__)
205 # define RELA
206 # define REL_TAG DT_RELA
207 # define RELSZ_TAG DT_RELASZ
208 # define REL_TYPE Elf_Rela
209 # else
210 # define REL_TAG DT_REL
211 # define RELSZ_TAG DT_RELSZ
212 # define REL_TYPE Elf_Rel
213 # endif
214
215 #include <elf.h>
216
217 static void relocate_self(struct ps_strings *) __noinline;
218
219 static void
220 relocate_self(struct ps_strings *ps_strings)
221 {
222 AuxInfo *aux = (AuxInfo *)(ps_strings->ps_argvstr + ps_strings->ps_nargvstr +
223 ps_strings->ps_nenvstr + 2);
224 uintptr_t relocbase = (uintptr_t)~0U;
225 const Elf_Phdr *phdr = NULL;
226 Elf_Half phnum = (Elf_Half)~0;
227
228 for (; aux->a_type != AT_NULL; ++aux) {
229 switch (aux->a_type) {
230 case AT_BASE:
231 if (aux->a_v)
232 return;
233 break;
234 case AT_PHDR:
235 phdr = (void *)aux->a_v;
236 break;
237 case AT_PHNUM:
238 phnum = (Elf_Half)aux->a_v;
239 break;
240 }
241 }
242
243 if (phdr == NULL || phnum == (Elf_Half)~0)
244 return;
245
246 const Elf_Phdr *phlimit = phdr + phnum, *dynphdr = NULL;
247
248 for (; phdr < phlimit; ++phdr) {
249 if (phdr->p_type == PT_DYNAMIC)
250 dynphdr = phdr;
251 if (phdr->p_type == PT_PHDR)
252 relocbase = (uintptr_t)phdr - phdr->p_vaddr;
253 }
254 if (dynphdr == NULL || relocbase == (uintptr_t)~0U)
255 return;
256
257 Elf_Dyn *dynp = (Elf_Dyn *)((uint8_t *)dynphdr->p_vaddr + relocbase);
258
259 const Elf_Relr *relr = 0, *relrlim;
260 const REL_TYPE *relocs = 0, *relocslim;
261 Elf_Addr relrsz = 0, relocssz = 0;
262
263 for (; dynp->d_tag != DT_NULL; dynp++) {
264 switch (dynp->d_tag) {
265 case DT_RELR:
266 relr =
267 (const Elf_Relr *)(relocbase + dynp->d_un.d_ptr);
268 break;
269 case DT_RELRSZ:
270 relrsz = dynp->d_un.d_val;
271 break;
272 case REL_TAG:
273 relocs =
274 (const REL_TYPE *)(relocbase + dynp->d_un.d_ptr);
275 break;
276 case RELSZ_TAG:
277 relocssz = dynp->d_un.d_val;
278 break;
279 }
280 }
281 relrlim = (const Elf_Relr *)((const uint8_t *)relr + relrsz);
282 relocslim = (const REL_TYPE *)((const uint8_t *)relocs + relocssz);
283 while (relr < relrlim) {
284 Elf_Addr *where;
285
286 where = (Elf_Addr *)(relocbase + *relr);
287 *where++ += relocbase;
288 while (++relr < relrlim && *relr & 1) {
289 unsigned i;
290
291 for (i = 1; i < CHAR_BIT*sizeof(*relr); i++, where++) {
292 if (*relr & ((Elf_Relr)1 << i))
293 *where += relocbase;
294 }
295 }
296 }
297 for (; relocs < relocslim; ++relocs) {
298 Elf_Addr *where;
299
300 where = (Elf_Addr *)(relocbase + relocs->r_offset);
301
302 switch (ELF_R_TYPE(relocs->r_info)) {
303 case R_TYPE(RELATIVE): /* word64 B + A */
304 #ifdef RELA
305 *where = (Elf_Addr)(relocbase + relocs->r_addend);
306 #else
307 *where += (Elf_Addr)relocbase;
308 #endif
309 break;
310 #ifdef IFUNC_RELOCATION
311 case IFUNC_RELOCATION:
312 break;
313 #endif
314 default:
315 abort();
316 }
317 }
318 }
319 #endif
320
321 void
322 ___start(void (*cleanup)(void), /* from shared loader */
323 struct ps_strings *ps_strings)
324 {
325 #if defined(HAS_RELOCATE_SELF)
326 relocate_self(ps_strings);
327 #endif
328
329 if (ps_strings == NULL)
330 _FATAL("ps_strings missing\n");
331 __ps_strings = ps_strings;
332
333 environ = ps_strings->ps_envstr;
334
335 if (ps_strings->ps_argvstr[0] != NULL) {
336 char *c;
337 __progname = ps_strings->ps_argvstr[0];
338 for (c = ps_strings->ps_argvstr[0]; *c; ++c) {
339 if (*c == '/')
340 __progname = c + 1;
341 }
342 } else {
343 __progname = empty_string;
344 }
345
346 _libc_init();
347
348 if (&rtld_DYNAMIC == NULL) {
349 #ifdef HAS_IPLTA
350 fix_iplta();
351 #endif
352 #ifdef HAS_IPLT
353 fix_iplt();
354 #endif
355 }
356
357 _preinit();
358
359 if (cleanup != NULL)
360 atexit(cleanup);
361
362 #ifdef MCRT0
363 atexit(_mcleanup);
364 monstartup((u_long)&__eprol, (u_long)&__etext);
365 #endif
366
367 atexit(_finiarray);
368 _initarray();
369
370 #ifndef HAVE_INITFINI_ARRAY
371 atexit(_fini);
372 _init();
373 #endif
374
375 exit(main(ps_strings->ps_nargvstr, ps_strings->ps_argvstr, environ));
376 }
377