xref: /openbsd/lib/libc/dlfcn/init.c (revision fc61954a)
1 /*	$OpenBSD: init.c,v 1.5 2016/09/06 18:49:34 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/syscall.h>
23 
24 #ifndef PIC
25 #include <sys/mman.h>
26 #endif
27 
28 #include <tib.h>
29 #include <limits.h>		/* NAME_MAX */
30 #include <link.h>
31 #include <stdlib.h>		/* atexit */
32 #include <string.h>
33 #include <unistd.h>
34 
35 #include "init.h"
36 
37 /* XXX should be in an include file shared with csu */
38 char	***_csu_finish(char **_argv, char **_envp, void (*_cleanup)(void));
39 
40 /* provide definition for this */
41 int	_pagesize = 0;
42 
43 /*
44  * In dynamicly linked binaries environ and __progname are overriden by
45  * the definitions in ld.so.
46  */
47 char	**environ __attribute__((weak)) = NULL;
48 char	*__progname __attribute__((weak)) = NULL;
49 
50 
51 #ifndef PIC
52 struct dl_phdr_info	_static_phdr_info = { .dlpi_name = "a.out" };
53 
54 static inline void early_static_init(char **_argv, char **_envp);
55 static inline void setup_static_tib(Elf_Phdr *_phdr, int _phnum);
56 #endif /* PIC */
57 
58 
59 /*
60  * extract useful bits from the auxiliary vector and either
61  * a) register ld.so's cleanup in dynamic links, or
62  * b) init __progname, environ, and the TIB in static links.
63  */
64 char ***
65 _csu_finish(char **argv, char **envp, void (*cleanup)(void))
66 {
67 	AuxInfo	*aux;
68 #ifndef PIC
69 	Elf_Phdr *phdr = NULL;
70 	int phnum = 0;
71 
72 	/* static libc in a static link? */
73 	if (cleanup == NULL)
74 		early_static_init(argv, envp);
75 #endif /* !PIC */
76 
77 	/* Extract useful bits from the auxiliary vector */
78 	while (*envp++ != NULL)
79 		;
80 	for (aux = (void *)envp; aux->au_id != AUX_null; aux++) {
81 		switch (aux->au_id) {
82 		case AUX_pagesz:
83 			_pagesize = aux->au_v;
84 			break;
85 #ifndef PIC
86 		case AUX_base:
87 			_static_phdr_info.dlpi_addr = aux->au_v;
88 			break;
89 		case AUX_phdr:
90 			phdr = (void *)aux->au_v;
91 			_static_phdr_info.dlpi_phdr = phdr;
92 			break;
93 		case AUX_phnum:
94 			phnum = aux->au_v;
95 			_static_phdr_info.dlpi_phnum = phnum;
96 			break;
97 #endif /* !PIC */
98 		}
99 	}
100 
101 #ifndef PIC
102 	/* static libc in a static link? */
103 	if (cleanup == NULL)
104 		setup_static_tib(phdr, phnum);
105 #endif /* !PIC */
106 
107 	if (cleanup != NULL)
108 		atexit(cleanup);
109 
110 	return &environ;
111 }
112 
113 #ifndef PIC
114 /*
115  * static libc in a static link?  Then disable kbind and set up
116  * __progname and environ
117  */
118 static inline void
119 early_static_init(char **argv, char **envp)
120 {
121 	static char progname_storage[NAME_MAX+1];
122 
123 	/* disable kbind */
124 	syscall(SYS_kbind, (void *)NULL, (size_t)0, (long long)0);
125 
126 	environ = envp;
127 
128 	/* set up __progname */
129 	if (*argv != NULL) {		/* NULL ptr if argc = 0 */
130 		const char *p = strrchr(*argv, '/');
131 
132 		if (p == NULL)
133 			p = *argv;
134 		else
135 			p++;
136 		strlcpy(progname_storage, p, sizeof(progname_storage));
137 	}
138 	__progname = progname_storage;
139 }
140 
141 /*
142  * static TLS handling
143  */
144 #define ELF_ROUND(x,malign)	(((x) + (malign)-1) & ~((malign)-1))
145 
146 /* for static binaries, the location and size of the TLS image */
147 static void		*static_tls;
148 static size_t		static_tls_fsize;
149 
150 size_t			_static_tls_size = 0;
151 
152 static inline void
153 setup_static_tib(Elf_Phdr *phdr, int phnum)
154 {
155 	struct tib *tib;
156 	char *base;
157 	int i;
158 
159 	if (phdr != NULL) {
160 		for (i = 0; i < phnum; i++) {
161 			if (phdr[i].p_type != PT_TLS)
162 				continue;
163 			if (phdr[i].p_memsz == 0)
164 				break;
165 			if (phdr[i].p_memsz < phdr[i].p_filesz)
166 				break;		/* invalid */
167 #if TLS_VARIANT == 1
168 			_static_tls_size = phdr[i].p_memsz;
169 #elif TLS_VARIANT == 2
170 			/*
171 			 * variant 2 places the data before the TIB
172 			 * so we need to round up to the alignment
173 			 */
174 			_static_tls_size = ELF_ROUND(phdr[i].p_memsz,
175 			    phdr[i].p_align);
176 #endif
177 			if (phdr[i].p_vaddr != 0 && phdr[i].p_filesz != 0) {
178 				static_tls = (void *)phdr[i].p_vaddr;
179 				static_tls_fsize = phdr[i].p_filesz;
180 			}
181 			break;
182 		}
183 	}
184 
185 	/*
186 	 * We call getpagesize() here instead of using _pagesize because
187 	 * there's no aux-vector in non-PIE static links, so _pagesize
188 	 * might not be set yet.  If so getpagesize() will get the value.
189 	 */
190 	base = mmap(NULL, ELF_ROUND(_static_tls_size + sizeof *tib,
191 	    getpagesize()), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
192 # if TLS_VARIANT == 1
193 	tib = (struct tib *)base;
194 # elif TLS_VARIANT == 2
195 	tib = (struct tib *)(base + _static_tls_size);
196 # endif
197 
198 	_static_tls_init(base);
199 	TIB_INIT(tib, NULL, NULL);
200 	tib->tib_tid = getthrid();
201 	TCB_SET(TIB_TO_TCB(tib));
202 #if ! TCB_HAVE_MD_GET
203 	_libc_single_tcb = TIB_TO_TCB(tib);
204 #endif
205 }
206 
207 void
208 _static_tls_init(char *base)
209 {
210 	if (_static_tls_size) {
211 #if TLS_VARIANT == 1
212 		base += sizeof(struct tib);
213 #endif
214 		if (static_tls != NULL)
215 			memcpy(base, static_tls, static_tls_fsize);
216 		memset(base + static_tls_fsize, 0,
217 		    _static_tls_size - static_tls_fsize);
218 	}
219 }
220 #endif /* !PIC */
221