xref: /openbsd-src/lib/libc/dlfcn/init.c (revision fb8aa7497fded39583f40e800732f9c046411717)
1 /*	$OpenBSD: init.c,v 1.4 2016/05/07 19:05:22 guenther Exp $ */
2 /*
3  * Copyright (c) 2014,2015 Philip Guenther <guenther@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 
19 #define _DYN_LOADER
20 
21 #include <sys/types.h>
22 #include <sys/exec_elf.h>
23 #include <sys/syscall.h>
24 
25 #ifndef PIC
26 #include <sys/mman.h>
27 #endif
28 
29 #include <tib.h>
30 #include <limits.h>		/* NAME_MAX */
31 #include <stdlib.h>		/* atexit */
32 #include <string.h>
33 #include <unistd.h>
34 
35 /* XXX should be in an include file shared with csu */
36 char	***_csu_finish(char **_argv, char **_envp, void (*_cleanup)(void));
37 
38 /* provide definition for this */
39 int	_pagesize = 0;
40 
41 /*
42  * In dynamicly linked binaries environ and __progname are overriden by
43  * the definitions in ld.so.
44  */
45 char	**environ __attribute__((weak)) = NULL;
46 char	*__progname __attribute__((weak)) = NULL;
47 
48 
49 #ifndef PIC
50 static inline void early_static_init(char **_argv, char **_envp);
51 static inline void setup_static_tib(Elf_Phdr *_phdr, int _phnum);
52 #endif /* PIC */
53 
54 
55 /*
56  * extract useful bits from the auxiliary vector and either
57  * a) register ld.so's cleanup in dynamic links, or
58  * b) init __progname, environ, and the TIB in static links.
59  */
60 char ***
61 _csu_finish(char **argv, char **envp, void (*cleanup)(void))
62 {
63 	AuxInfo	*aux;
64 #ifndef PIC
65 	Elf_Phdr *phdr = NULL;
66 	int phnum = 0;
67 
68 	/* static libc in a static link? */
69 	if (cleanup == NULL)
70 		early_static_init(argv, envp);
71 #endif /* !PIC */
72 
73 	/* Extract useful bits from the auxiliary vector */
74 	while (*envp++ != NULL)
75 		;
76 	for (aux = (void *)envp; aux->au_id != AUX_null; aux++) {
77 		switch (aux->au_id) {
78 		case AUX_pagesz:
79 			_pagesize = aux->au_v;
80 			break;
81 #ifndef PIC
82 		case AUX_phdr:
83 			phdr = (void *)aux->au_v;
84 			break;
85 		case AUX_phnum:
86 			phnum = aux->au_v;
87 			break;
88 #endif /* !PIC */
89 		}
90 	}
91 
92 #ifndef PIC
93 	/* static libc in a static link? */
94 	if (cleanup == NULL)
95 		setup_static_tib(phdr, phnum);
96 #endif /* !PIC */
97 
98 	if (cleanup != NULL)
99 		atexit(cleanup);
100 
101 	return &environ;
102 }
103 
104 #ifndef PIC
105 /*
106  * static libc in a static link?  Then disable kbind and set up
107  * __progname and environ
108  */
109 static inline void
110 early_static_init(char **argv, char **envp)
111 {
112 	static char progname_storage[NAME_MAX+1];
113 
114 	/* disable kbind */
115 	syscall(SYS_kbind, (void *)NULL, (size_t)0, (long long)0);
116 
117 	environ = envp;
118 
119 	/* set up __progname */
120 	if (*argv != NULL) {		/* NULL ptr if argc = 0 */
121 		const char *p = strrchr(*argv, '/');
122 
123 		if (p == NULL)
124 			p = *argv;
125 		else
126 			p++;
127 		strlcpy(progname_storage, p, sizeof(progname_storage));
128 	}
129 	__progname = progname_storage;
130 }
131 
132 /*
133  * static TLS handling
134  */
135 #define ELF_ROUND(x,malign)	(((x) + (malign)-1) & ~((malign)-1))
136 
137 /* for static binaries, the location and size of the TLS image */
138 static void		*static_tls;
139 static size_t		static_tls_fsize;
140 
141 size_t			_static_tls_size = 0;
142 
143 static inline void
144 setup_static_tib(Elf_Phdr *phdr, int phnum)
145 {
146 	struct tib *tib;
147 	char *base;
148 	int i;
149 
150 	if (phdr != NULL) {
151 		for (i = 0; i < phnum; i++) {
152 			if (phdr[i].p_type != PT_TLS)
153 				continue;
154 			if (phdr[i].p_memsz == 0)
155 				break;
156 			if (phdr[i].p_memsz < phdr[i].p_filesz)
157 				break;		/* invalid */
158 #if TLS_VARIANT == 1
159 			_static_tls_size = phdr[i].p_memsz;
160 #elif TLS_VARIANT == 2
161 			/*
162 			 * variant 2 places the data before the TIB
163 			 * so we need to round up to the alignment
164 			 */
165 			_static_tls_size = ELF_ROUND(phdr[i].p_memsz,
166 			    phdr[i].p_align);
167 #endif
168 			if (phdr[i].p_vaddr != 0 && phdr[i].p_filesz != 0) {
169 				static_tls = (void *)phdr[i].p_vaddr;
170 				static_tls_fsize = phdr[i].p_filesz;
171 			}
172 			break;
173 		}
174 	}
175 
176 	/*
177 	 * We call getpagesize() here instead of using _pagesize because
178 	 * there's no aux-vector in non-PIE static links, so _pagesize
179 	 * might not be set yet.  If so getpagesize() will get the value.
180 	 */
181 	base = mmap(NULL, ELF_ROUND(_static_tls_size + sizeof *tib,
182 	    getpagesize()), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
183 # if TLS_VARIANT == 1
184 	tib = (struct tib *)base;
185 # elif TLS_VARIANT == 2
186 	tib = (struct tib *)(base + _static_tls_size);
187 # endif
188 
189 	_static_tls_init(base);
190 	TIB_INIT(tib, NULL, NULL);
191 	tib->tib_tid = getthrid();
192 	TCB_SET(TIB_TO_TCB(tib));
193 #if ! TCB_HAVE_MD_GET
194 	_libc_single_tcb = TIB_TO_TCB(tib);
195 #endif
196 }
197 
198 void
199 _static_tls_init(char *base)
200 {
201 	if (_static_tls_size) {
202 #if TLS_VARIANT == 1
203 		base += sizeof(struct tib);
204 #endif
205 		if (static_tls != NULL)
206 			memcpy(base, static_tls, static_tls_fsize);
207 		memset(base + static_tls_fsize, 0,
208 		    _static_tls_size - static_tls_fsize);
209 	}
210 }
211 #endif /* !PIC */
212