xref: /netbsd/lib/csu/common/crt0-common.c (revision 6beeb802)
1*6beeb802Sskrll /* $NetBSD: crt0-common.c,v 1.27 2022/06/21 06:52:17 skrll Exp $ */
201bc9d50Sjoerg 
301bc9d50Sjoerg /*
401bc9d50Sjoerg  * Copyright (c) 1998 Christos Zoulas
501bc9d50Sjoerg  * Copyright (c) 1995 Christopher G. Demetriou
601bc9d50Sjoerg  * All rights reserved.
701bc9d50Sjoerg  *
801bc9d50Sjoerg  * Redistribution and use in source and binary forms, with or without
901bc9d50Sjoerg  * modification, are permitted provided that the following conditions
1001bc9d50Sjoerg  * are met:
1101bc9d50Sjoerg  * 1. Redistributions of source code must retain the above copyright
1201bc9d50Sjoerg  *    notice, this list of conditions and the following disclaimer.
1301bc9d50Sjoerg  * 2. Redistributions in binary form must reproduce the above copyright
1401bc9d50Sjoerg  *    notice, this list of conditions and the following disclaimer in the
1501bc9d50Sjoerg  *    documentation and/or other materials provided with the distribution.
1601bc9d50Sjoerg  * 3. All advertising materials mentioning features or use of this software
1701bc9d50Sjoerg  *    must display the following acknowledgement:
1801bc9d50Sjoerg  *          This product includes software developed for the
1901bc9d50Sjoerg  *          NetBSD Project.  See http://www.NetBSD.org/ for
2001bc9d50Sjoerg  *          information about NetBSD.
2101bc9d50Sjoerg  * 4. The name of the author may not be used to endorse or promote products
2201bc9d50Sjoerg  *    derived from this software without specific prior written permission.
2301bc9d50Sjoerg  *
2401bc9d50Sjoerg  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2501bc9d50Sjoerg  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2601bc9d50Sjoerg  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2701bc9d50Sjoerg  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2801bc9d50Sjoerg  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2901bc9d50Sjoerg  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3001bc9d50Sjoerg  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3101bc9d50Sjoerg  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3201bc9d50Sjoerg  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3301bc9d50Sjoerg  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3401bc9d50Sjoerg  *
3501bc9d50Sjoerg  * <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>>
3601bc9d50Sjoerg  */
3701bc9d50Sjoerg 
3801bc9d50Sjoerg #include <sys/cdefs.h>
39*6beeb802Sskrll __RCSID("$NetBSD: crt0-common.c,v 1.27 2022/06/21 06:52:17 skrll Exp $");
4001bc9d50Sjoerg 
4101bc9d50Sjoerg #include <sys/types.h>
42e4a3eddeSjoerg #include <sys/exec.h>
437acd15a3Sjoerg #include <sys/exec_elf.h>
4401bc9d50Sjoerg #include <sys/syscall.h>
4501bc9d50Sjoerg #include <machine/profile.h>
4601bc9d50Sjoerg #include <stdlib.h>
4701bc9d50Sjoerg #include <unistd.h>
4801bc9d50Sjoerg 
495bd02060Schristos #include "csu-common.h"
505bd02060Schristos 
5101bc9d50Sjoerg extern int main(int, char **, char **);
5201bc9d50Sjoerg 
537acd15a3Sjoerg typedef void (*fptr_t)(void);
54d0ce0a99Schristos #ifndef HAVE_INITFINI_ARRAY
5501bc9d50Sjoerg extern void	_init(void);
5601bc9d50Sjoerg extern void	_fini(void);
5718b01b61Schristos #endif
58399a2aeaSjoerg extern void	_libc_init(void);
5901bc9d50Sjoerg 
6001bc9d50Sjoerg /*
6101bc9d50Sjoerg  * Arrange for _DYNAMIC to be weak and undefined (and therefore to show up
6201bc9d50Sjoerg  * as being at address zero, unless something else defines it).  That way,
6301bc9d50Sjoerg  * if we happen to be compiling without -static but with without any
6401bc9d50Sjoerg  * shared libs present, things will still work.
6501bc9d50Sjoerg  */
6626b475b1Sjoerg 
67ed49abf5Sjoerg __weakref_visible int rtld_DYNAMIC __weak_reference(_DYNAMIC);
6801bc9d50Sjoerg 
6901bc9d50Sjoerg #ifdef MCRT0
7001bc9d50Sjoerg extern void	monstartup(u_long, u_long);
7101bc9d50Sjoerg extern void	_mcleanup(void);
7201bc9d50Sjoerg extern unsigned char __etext, __eprol;
7301bc9d50Sjoerg #endif /* MCRT0 */
7401bc9d50Sjoerg 
7501bc9d50Sjoerg static char	 empty_string[] = "";
765bd02060Schristos 
775bd02060Schristos char		**environ __common;
785bd02060Schristos struct ps_strings *__ps_strings __common = 0;
795bd02060Schristos char		*__progname __common = empty_string;
8001bc9d50Sjoerg 
817acd15a3Sjoerg __dead __dso_hidden void ___start(void (*)(void), struct ps_strings *);
8201bc9d50Sjoerg 
8301bc9d50Sjoerg #define	write(fd, s, n)	__syscall(SYS_write, (fd), (s), (n))
8401bc9d50Sjoerg 
8501bc9d50Sjoerg #define	_FATAL(str)				\
8601bc9d50Sjoerg do {						\
8701bc9d50Sjoerg 	write(2, str, sizeof(str)-1);		\
8801bc9d50Sjoerg 	_exit(1);				\
8901bc9d50Sjoerg } while (0)
9001bc9d50Sjoerg 
91e079cdf7Smatt /*
92e079cdf7Smatt  * If we are using INIT_ARRAY/FINI_ARRAY and we are linked statically,
93e079cdf7Smatt  * we have to process these instead of relying on RTLD to do it for us.
94e079cdf7Smatt  *
95e079cdf7Smatt  * Since we don't need .init or .fini sections, just code them in C
96e079cdf7Smatt  * to make life easier.
97e079cdf7Smatt  */
986d2eb438Sjoerg extern const fptr_t __preinit_array_start[] __dso_hidden;
996d2eb438Sjoerg extern const fptr_t __preinit_array_end[] __dso_hidden __weak;
1006d2eb438Sjoerg extern const fptr_t __init_array_start[] __dso_hidden;
1016d2eb438Sjoerg extern const fptr_t __init_array_end[] __dso_hidden __weak;
1026d2eb438Sjoerg extern const fptr_t __fini_array_start[] __dso_hidden;
1036d2eb438Sjoerg extern const fptr_t __fini_array_end[] __dso_hidden __weak;
104e079cdf7Smatt 
105e079cdf7Smatt static inline void
_preinit(void)10607af1b28Smatt _preinit(void)
10707af1b28Smatt {
1086d2eb438Sjoerg 	for (const fptr_t *f = __preinit_array_start; f < __preinit_array_end; f++) {
10907af1b28Smatt 		(*f)();
11007af1b28Smatt 	}
11107af1b28Smatt }
11207af1b28Smatt 
11307af1b28Smatt static inline void
_initarray(void)114d0ce0a99Schristos _initarray(void)
115e079cdf7Smatt {
1166d2eb438Sjoerg 	for (const fptr_t *f = __init_array_start; f < __init_array_end; f++) {
117e079cdf7Smatt 		(*f)();
118e079cdf7Smatt 	}
119e079cdf7Smatt }
120e079cdf7Smatt 
121e079cdf7Smatt static void
_finiarray(void)122d0ce0a99Schristos _finiarray(void)
123e079cdf7Smatt {
1246d2eb438Sjoerg 	for (const fptr_t *f = __fini_array_start; f < __fini_array_end; f++) {
125e079cdf7Smatt 		(*f)();
126e079cdf7Smatt 	}
127e079cdf7Smatt }
128e079cdf7Smatt 
129450579d1Sskrll #if \
130*6beeb802Sskrll     defined(__aarch64__) || \
131450579d1Sskrll     defined(__powerpc__) || \
132450579d1Sskrll     defined(__sparc__) || \
133450579d1Sskrll     defined(__x86_64__)
1344ba63be7Sjoerg #define HAS_IPLTA
1354ba63be7Sjoerg static void fix_iplta(void) __noinline;
136450579d1Sskrll #elif \
137450579d1Sskrll     defined(__arm__) || \
138450579d1Sskrll     defined(__i386__)
1394ba63be7Sjoerg #define HAS_IPLT
1404ba63be7Sjoerg static void fix_iplt(void) __noinline;
1414ba63be7Sjoerg #endif
1424ba63be7Sjoerg 
1434ba63be7Sjoerg 
1444ba63be7Sjoerg #ifdef HAS_IPLTA
1454ba63be7Sjoerg #include <stdio.h>
1464ba63be7Sjoerg extern const Elf_Rela __rela_iplt_start[] __dso_hidden __weak;
1474ba63be7Sjoerg extern const Elf_Rela __rela_iplt_end[] __dso_hidden __weak;
148a0c0dc61Sjoerg #ifdef __sparc__
149a0c0dc61Sjoerg #define IFUNC_RELOCATION R_TYPE(JMP_IREL)
150a0c0dc61Sjoerg #include <machine/elf_support.h>
151a0c0dc61Sjoerg #define write_plt(where, value) sparc_write_branch((void *)where, (void *)value)
152a0c0dc61Sjoerg #else
153a0c0dc61Sjoerg #define IFUNC_RELOCATION R_TYPE(IRELATIVE)
154a0c0dc61Sjoerg #define write_plt(where, value) *where = value
155a0c0dc61Sjoerg #endif
1564ba63be7Sjoerg 
1574ba63be7Sjoerg static void
fix_iplta(void)1584ba63be7Sjoerg fix_iplta(void)
1594ba63be7Sjoerg {
1604ba63be7Sjoerg 	const Elf_Rela *rela, *relalim;
1614ba63be7Sjoerg 	uintptr_t relocbase = 0;
1624ba63be7Sjoerg 	Elf_Addr *where, target;
1634ba63be7Sjoerg 
1644ba63be7Sjoerg 	rela = __rela_iplt_start;
1654ba63be7Sjoerg 	relalim = __rela_iplt_end;
1664ba63be7Sjoerg 	for (; rela < relalim; ++rela) {
167a0c0dc61Sjoerg 		if (ELF_R_TYPE(rela->r_info) != IFUNC_RELOCATION)
1684ba63be7Sjoerg 			abort();
1694ba63be7Sjoerg 		where = (Elf_Addr *)(relocbase + rela->r_offset);
1704ba63be7Sjoerg 		target = (Elf_Addr)(relocbase + rela->r_addend);
1714ba63be7Sjoerg 		target = ((Elf_Addr(*)(void))target)();
172a0c0dc61Sjoerg 		write_plt(where, target);
1734ba63be7Sjoerg 	}
1744ba63be7Sjoerg }
1754ba63be7Sjoerg #endif
1764ba63be7Sjoerg #ifdef HAS_IPLT
1774ba63be7Sjoerg extern const Elf_Rel __rel_iplt_start[] __dso_hidden __weak;
1784ba63be7Sjoerg extern const Elf_Rel __rel_iplt_end[] __dso_hidden __weak;
179a0c0dc61Sjoerg #define IFUNC_RELOCATION R_TYPE(IRELATIVE)
1804ba63be7Sjoerg 
1814ba63be7Sjoerg static void
fix_iplt(void)1824ba63be7Sjoerg fix_iplt(void)
1834ba63be7Sjoerg {
1844ba63be7Sjoerg 	const Elf_Rel *rel, *rellim;
1854ba63be7Sjoerg 	uintptr_t relocbase = 0;
1864ba63be7Sjoerg 	Elf_Addr *where, target;
1874ba63be7Sjoerg 
1884ba63be7Sjoerg 	rel = __rel_iplt_start;
1894ba63be7Sjoerg 	rellim = __rel_iplt_end;
1904ba63be7Sjoerg 	for (; rel < rellim; ++rel) {
191a0c0dc61Sjoerg 		if (ELF_R_TYPE(rel->r_info) != IFUNC_RELOCATION)
1924ba63be7Sjoerg 			abort();
1934ba63be7Sjoerg 		where = (Elf_Addr *)(relocbase + rel->r_offset);
1944ba63be7Sjoerg 		target = ((Elf_Addr(*)(void))*where)();
1954ba63be7Sjoerg 		*where = target;
1964ba63be7Sjoerg 	}
1974ba63be7Sjoerg }
1984ba63be7Sjoerg #endif
1994ba63be7Sjoerg 
20001a77a40Sjoerg #if defined(__x86_64__) || defined(__i386__)
20101a77a40Sjoerg #  define HAS_RELOCATE_SELF
20201a77a40Sjoerg #  if defined(__x86_64__)
20301a77a40Sjoerg #  define RELA
20401a77a40Sjoerg #  define REL_TAG DT_RELA
20501a77a40Sjoerg #  define RELSZ_TAG DT_RELASZ
20601a77a40Sjoerg #  define REL_TYPE Elf_Rela
20701a77a40Sjoerg #  else
20801a77a40Sjoerg #  define REL_TAG DT_REL
20901a77a40Sjoerg #  define RELSZ_TAG DT_RELSZ
21001a77a40Sjoerg #  define REL_TYPE Elf_Rel
21101a77a40Sjoerg #  endif
21201a77a40Sjoerg 
21301a77a40Sjoerg #include <elf.h>
21401a77a40Sjoerg 
21501a77a40Sjoerg static void relocate_self(struct ps_strings *) __noinline;
21601a77a40Sjoerg 
21701a77a40Sjoerg static void
relocate_self(struct ps_strings * ps_strings)21801a77a40Sjoerg relocate_self(struct ps_strings *ps_strings)
21901a77a40Sjoerg {
22001a77a40Sjoerg 	AuxInfo *aux = (AuxInfo *)(ps_strings->ps_argvstr + ps_strings->ps_nargvstr +
22101a77a40Sjoerg 	    ps_strings->ps_nenvstr + 2);
2228d092b50Skre 	uintptr_t relocbase = (uintptr_t)~0U;
2238d092b50Skre 	const Elf_Phdr *phdr = NULL;
2248d092b50Skre 	Elf_Half phnum = (Elf_Half)~0;
22501a77a40Sjoerg 
22601a77a40Sjoerg 	for (; aux->a_type != AT_NULL; ++aux) {
22701a77a40Sjoerg 		switch (aux->a_type) {
22801a77a40Sjoerg 		case AT_BASE:
22901a77a40Sjoerg 			if (aux->a_v)
23001a77a40Sjoerg 				return;
23101a77a40Sjoerg 			break;
23201a77a40Sjoerg 		case AT_PHDR:
23301a77a40Sjoerg 			phdr = (void *)aux->a_v;
23401a77a40Sjoerg 			break;
23501a77a40Sjoerg 		case AT_PHNUM:
23601a77a40Sjoerg 			phnum = (Elf_Half)aux->a_v;
23701a77a40Sjoerg 			break;
23801a77a40Sjoerg 		}
23901a77a40Sjoerg 	}
2408d092b50Skre 
2418d092b50Skre 	if (phdr == NULL || phnum == (Elf_Half)~0)
2428d092b50Skre 		return;
2438d092b50Skre 
2448d092b50Skre 	const Elf_Phdr *phlimit = phdr + phnum, *dynphdr = NULL;
24501a77a40Sjoerg 
24601a77a40Sjoerg 	for (; phdr < phlimit; ++phdr) {
24701a77a40Sjoerg 		if (phdr->p_type == PT_DYNAMIC)
24801a77a40Sjoerg 			dynphdr = phdr;
24901a77a40Sjoerg 		if (phdr->p_type == PT_PHDR)
25001a77a40Sjoerg 			relocbase = (uintptr_t)phdr - phdr->p_vaddr;
25101a77a40Sjoerg 	}
2528d092b50Skre 	if (dynphdr == NULL || relocbase == (uintptr_t)~0U)
2538d092b50Skre 		return;
2548d092b50Skre 
25501a77a40Sjoerg 	Elf_Dyn *dynp = (Elf_Dyn *)((uint8_t *)dynphdr->p_vaddr + relocbase);
25601a77a40Sjoerg 
25701a77a40Sjoerg 	const REL_TYPE *relocs = 0, *relocslim;
25801a77a40Sjoerg 	Elf_Addr relocssz = 0;
25901a77a40Sjoerg 
26001a77a40Sjoerg 	for (; dynp->d_tag != DT_NULL; dynp++) {
26101a77a40Sjoerg 		switch (dynp->d_tag) {
26201a77a40Sjoerg 		case REL_TAG:
26301a77a40Sjoerg 			relocs =
26401a77a40Sjoerg 			    (const REL_TYPE *)(relocbase + dynp->d_un.d_ptr);
26501a77a40Sjoerg 			break;
26601a77a40Sjoerg 		case RELSZ_TAG:
26701a77a40Sjoerg 			relocssz = dynp->d_un.d_val;
26801a77a40Sjoerg 			break;
26901a77a40Sjoerg 		}
27001a77a40Sjoerg 	}
27101a77a40Sjoerg 	relocslim = (const REL_TYPE *)((const uint8_t *)relocs + relocssz);
27201a77a40Sjoerg 	for (; relocs < relocslim; ++relocs) {
27301a77a40Sjoerg 		Elf_Addr *where;
27401a77a40Sjoerg 
27501a77a40Sjoerg 		where = (Elf_Addr *)(relocbase + relocs->r_offset);
27601a77a40Sjoerg 
27701a77a40Sjoerg 		switch (ELF_R_TYPE(relocs->r_info)) {
27801a77a40Sjoerg 		case R_TYPE(RELATIVE):  /* word64 B + A */
27901a77a40Sjoerg #ifdef RELA
28001a77a40Sjoerg 			*where = (Elf_Addr)(relocbase + relocs->r_addend);
28101a77a40Sjoerg #else
28201a77a40Sjoerg 			*where += (Elf_Addr)relocbase;
28301a77a40Sjoerg #endif
28401a77a40Sjoerg 			break;
28501a77a40Sjoerg #ifdef IFUNC_RELOCATION
28601a77a40Sjoerg 		case IFUNC_RELOCATION:
28701a77a40Sjoerg 			break;
28801a77a40Sjoerg #endif
28901a77a40Sjoerg 		default:
29001a77a40Sjoerg 			abort();
29101a77a40Sjoerg 		}
29201a77a40Sjoerg 	}
29301a77a40Sjoerg }
29401a77a40Sjoerg #endif
29501a77a40Sjoerg 
29601bc9d50Sjoerg void
___start(void (* cleanup)(void),struct ps_strings * ps_strings)297e4a3eddeSjoerg ___start(void (*cleanup)(void),			/* from shared loader */
29801bc9d50Sjoerg     struct ps_strings *ps_strings)
29901bc9d50Sjoerg {
30001a77a40Sjoerg #if defined(HAS_RELOCATE_SELF)
30101a77a40Sjoerg 	relocate_self(ps_strings);
30201a77a40Sjoerg #endif
30301bc9d50Sjoerg 
304e4a3eddeSjoerg 	if (ps_strings == NULL)
305e4a3eddeSjoerg 		_FATAL("ps_strings missing\n");
306e4a3eddeSjoerg 	__ps_strings = ps_strings;
307e4a3eddeSjoerg 
308e4a3eddeSjoerg 	environ = ps_strings->ps_envstr;
309e4a3eddeSjoerg 
310e4a3eddeSjoerg 	if (ps_strings->ps_argvstr[0] != NULL) {
31101bc9d50Sjoerg 		char *c;
312e4a3eddeSjoerg 		__progname = ps_strings->ps_argvstr[0];
313e4a3eddeSjoerg 		for (c = ps_strings->ps_argvstr[0]; *c; ++c) {
31401bc9d50Sjoerg 			if (*c == '/')
31501bc9d50Sjoerg 				__progname = c + 1;
31601bc9d50Sjoerg 		}
31701bc9d50Sjoerg 	} else {
31801bc9d50Sjoerg 		__progname = empty_string;
31901bc9d50Sjoerg 	}
32001bc9d50Sjoerg 
3217acd15a3Sjoerg 	if (cleanup != NULL)
32201bc9d50Sjoerg 		atexit(cleanup);
32301bc9d50Sjoerg 
324399a2aeaSjoerg 	_libc_init();
325399a2aeaSjoerg 
3264ba63be7Sjoerg 	if (&rtld_DYNAMIC == NULL) {
3274ba63be7Sjoerg #ifdef HAS_IPLTA
3284ba63be7Sjoerg 		fix_iplta();
3294ba63be7Sjoerg #endif
3304ba63be7Sjoerg #ifdef HAS_IPLT
3314ba63be7Sjoerg 		fix_iplt();
3324ba63be7Sjoerg #endif
3334ba63be7Sjoerg 	}
3344ba63be7Sjoerg 
33507af1b28Smatt 	_preinit();
33607af1b28Smatt 
33701bc9d50Sjoerg #ifdef MCRT0
33801bc9d50Sjoerg 	atexit(_mcleanup);
33901bc9d50Sjoerg 	monstartup((u_long)&__eprol, (u_long)&__etext);
34001bc9d50Sjoerg #endif
34101bc9d50Sjoerg 
342d0ce0a99Schristos 	atexit(_finiarray);
343d0ce0a99Schristos 	_initarray();
344d0ce0a99Schristos 
345d0ce0a99Schristos #ifndef HAVE_INITFINI_ARRAY
34601bc9d50Sjoerg 	atexit(_fini);
34701bc9d50Sjoerg 	_init();
348d0ce0a99Schristos #endif
34901bc9d50Sjoerg 
350e4a3eddeSjoerg 	exit(main(ps_strings->ps_nargvstr, ps_strings->ps_argvstr, environ));
35101bc9d50Sjoerg }
352