xref: /illumos-gate/usr/src/cmd/sgs/rtld/common/config_elf.c (revision 0dee7919e2f2a6479d16b370af93747b9416b242)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include	"_synonyms.h"
29 
30 #include	<sys/mman.h>
31 #include	<sys/types.h>
32 #include	<sys/stat.h>
33 #include	<fcntl.h>
34 #include	<limits.h>
35 #include	<stdio.h>
36 #include	<string.h>
37 #include	"rtc.h"
38 #include	"debug.h"
39 #include	"conv.h"
40 #include	"_rtld.h"
41 #include	"msg.h"
42 
43 static Config	_config = { 0 };
44 Config *	config = &_config;
45 
46 
47 /*
48  * Validate a configuration file.
49  */
50 static void
51 elf_config_validate(Addr addr, Rtc_head * head, Rt_map * lmp)
52 {
53 	const char	*str, *strtbl = config->c_strtbl;
54 	Rtc_obj		*obj;
55 	Rtc_dir		*dirtbl;
56 	Rtc_file	*filetbl;
57 	struct stat	status;
58 	int		err;
59 
60 	/*
61 	 * If this configuration file is for a specific application make sure
62 	 * we've been invoked by the application.  Note that we only check the
63 	 * basename component of the application as the original application
64 	 * and its cached equivalent are never going to have the same pathnames.
65 	 * Also, we use PATHNAME() and not NAME() - this catches things like vi
66 	 * that exec shells using execv(/usr/bin/ksh, sh ...).
67 	 */
68 	if (head->ch_app) {
69 		char	*_str, *_cname, *cname, *aname = PATHNAME(lmp);
70 
71 		obj = (Rtc_obj *)(head->ch_app + addr);
72 		cname = _cname = (char *)(strtbl + obj->co_name);
73 
74 		if ((_str = strrchr(aname, '/')) != NULL)
75 			aname = ++_str;
76 		if ((_str = strrchr(cname, '/')) != NULL)
77 			cname = ++_str;
78 
79 		if (strcmp(aname, cname)) {
80 			/*
81 			 * It's possible a user is trying to ldd(1) an alternate
82 			 * shared object and point to a configuration file that
83 			 * the shared object is part of.  In this case ignore
84 			 * any mismatch name warnings.
85 			 */
86 			if ((LIST(lmp)->lm_flags & LML_FLG_TRC_ENABLE) &&
87 			    ((FLAGS1(lmp) & FL1_RT_LDDSTUB) == 0)) {
88 				eprintf(ERR_WARNING, MSG_INTL(MSG_CONF_APP),
89 				    config->c_name, _cname);
90 				return;
91 			}
92 		}
93 
94 		/*
95 		 * If we have a valid alternative application reset its original
96 		 * name for possible $ORIGIN processing.
97 		 */
98 		if ((FLAGS1(lmp) & FL1_RT_LDDSTUB) == 0) {
99 			ORIGNAME(lmp) = _cname;
100 			DIRSZ(lmp) = cname - _cname - 1;
101 		}
102 	}
103 
104 	/*
105 	 * If alternative objects are specified traverse the directories
106 	 * specified in the configuration file, if any directory is newer than
107 	 * the time it was recorded in the cache then continue to inspect its
108 	 * files.  Any file determined newer than its configuration recording
109 	 * questions the the use of any alternative objects.  The intent here
110 	 * is to make sure no-one abuses a configuration as a means of static
111 	 * linking.
112 	 */
113 	for (dirtbl = (Rtc_dir *)(head->ch_dir + addr);
114 	    dirtbl->cd_obj; dirtbl++) {
115 		/*
116 		 * Skip directories that provide no files - this also catches
117 		 * RTC_OBJ_NOEXIST directories.
118 		 */
119 		filetbl = (Rtc_file *)(dirtbl->cd_file + addr);
120 		if (filetbl->cf_obj == 0)
121 			continue;
122 
123 		/*
124 		 * Skip directories that haven't provided real, dumped files.
125 		 */
126 		obj = (Rtc_obj *)(dirtbl->cd_obj + addr);
127 		if ((obj->co_flags & (RTC_OBJ_DUMP | RTC_OBJ_REALPTH)) !=
128 		    (RTC_OBJ_DUMP | RTC_OBJ_REALPTH))
129 			continue;
130 
131 		str = strtbl + obj->co_name;
132 
133 		if (stat(str, &status) != 0) {
134 			err = errno;
135 			eprintf(ERR_WARNING, MSG_INTL(MSG_CONF_DSTAT),
136 			    config->c_name, str, strerror(err));
137 			continue;
138 		}
139 
140 		if (status.st_mtime == obj->co_info)
141 			continue;
142 
143 		/*
144 		 * The system directory is newer than the configuration files
145 		 * entry, start checking any dumped files.
146 		 */
147 		for (; filetbl->cf_obj; filetbl++) {
148 			obj = (Rtc_obj *)(filetbl->cf_obj + addr);
149 			str = strtbl + obj->co_name;
150 
151 			/*
152 			 * Skip any files that aren't real, dumped files.
153 			 */
154 			if ((obj->co_flags &
155 			    (RTC_OBJ_DUMP | RTC_OBJ_REALPTH)) !=
156 			    (RTC_OBJ_DUMP | RTC_OBJ_REALPTH))
157 				continue;
158 
159 			if (stat(str, &status) != 0) {
160 				err = errno;
161 				eprintf(ERR_WARNING, MSG_INTL(MSG_CONF_FSTAT),
162 				    config->c_name, str, strerror(err));
163 				continue;
164 			}
165 
166 			/*
167 			 * If the files size is different somethings been
168 			 * changed.
169 			 */
170 			if (status.st_size != obj->co_info) {
171 				eprintf(ERR_WARNING, MSG_INTL(MSG_CONF_FCMP),
172 				    config->c_name, str);
173 			}
174 		}
175 	}
176 }
177 
178 int
179 elf_config(Rt_map * lmp, int aout)
180 {
181 	Rtc_head	*head;
182 	int		fd, features = 0;
183 	struct stat	status;
184 	Addr		addr;
185 	Pnode		*pnp;
186 	const char	*str = config->c_name;
187 
188 	/*
189 	 * If an alternative configuration file has been specified use it
190 	 * (expanding any tokens), otherwise try opening up the default.
191 	 */
192 	if ((str == 0) && ((rtld_flags & RT_FL_CONFAPP) == 0))
193 #if	defined(_ELF64)
194 		str = MSG_ORIG(MSG_PTH_CONFIG_64);
195 #else
196 		str = MSG_ORIG(MSG_PTH_CONFIG);
197 #endif
198 	else if (rtld_flags & RT_FL_SECURE)
199 		return (0);
200 	else {
201 		size_t	size;
202 		char	*name;
203 
204 		/*
205 		 * If we're dealing with an alternative application fabricate
206 		 * the need for a $ORIGIN/ld.config.app-name configuration file.
207 		 */
208 		if (rtld_flags & RT_FL_CONFAPP) {
209 			char	_name[PATH_MAX];
210 
211 			if ((str = strrchr(PATHNAME(lmp), '/')) != NULL)
212 				str++;
213 			else
214 				str = PATHNAME(lmp);
215 
216 			(void) snprintf(_name, PATH_MAX,
217 			    MSG_ORIG(MSG_ORG_CONFIG), str);
218 			str = _name;
219 		}
220 
221 		size = strlen(str);
222 		name = (char *)str;
223 
224 		if (expand(&name, &size, 0, 0,
225 		    (PN_TKN_ISALIST | PN_TKN_HWCAP), lmp) == 0)
226 			return (0);
227 		str = (const char *)name;
228 	}
229 	config->c_name = str;
230 
231 	/*
232 	 * If we can't open the configuration file return silently.
233 	 */
234 	if ((fd = open(str, O_RDONLY, 0)) == -1)
235 		return (DBG_CONF_PRCFAIL);
236 
237 	/*
238 	 * Determine the configuration file size and map the file.
239 	 */
240 	(void) fstat(fd, &status);
241 	if (status.st_size < sizeof (Rtc_head)) {
242 		(void) close(fd);
243 		return (DBG_CONF_CORRUPT);
244 	}
245 	if ((addr = (Addr)mmap(0, status.st_size, PROT_READ, MAP_SHARED,
246 	    fd, 0)) == (Addr)MAP_FAILED) {
247 		(void) close(fd);
248 		return (DBG_CONF_PRCFAIL);
249 	}
250 
251 	config->c_bgn = addr;
252 	config->c_end = addr + status.st_size;
253 	(void) close(fd);
254 
255 	head = (Rtc_head *)addr;
256 
257 	/*
258 	 * Make sure we can handle this version of the configuration file.
259 	 */
260 	if (head->ch_version > RTC_VER_CURRENT)
261 		return (DBG_CONF_VERSION);
262 
263 	/*
264 	 * When crle(1) creates a temporary configuration file the
265 	 * RTC_HDR_IGNORE flag is set.  Thus the mapping of the configuration
266 	 * file is taken into account but not its content.
267 	 */
268 	if (head->ch_cnflags & RTC_HDR_IGNORE)
269 		return (DBG_CONF_IGNORE);
270 
271 	/*
272 	 * Apply any new default library pathname.
273 	 */
274 	if (head->ch_edlibpath) {
275 		str = (const char *)(head->ch_edlibpath + addr);
276 #ifndef	SGS_PRE_UNIFIED_PROCESS
277 		if ((head->ch_cnflags & RTC_HDR_UPM) == 0) {
278 #if	defined(_ELF64)
279 			str = conv_upm_string(str, MSG_ORIG(MSG_PTH_USRLIB_64),
280 			    MSG_ORIG(MSG_PTH_LIB_64), MSG_PTH_LIB_64_SIZE);
281 #else
282 			str = conv_upm_string(str, MSG_ORIG(MSG_PTH_USRLIB),
283 			    MSG_ORIG(MSG_PTH_LIB), MSG_PTH_LIB_SIZE);
284 #endif
285 		}
286 #endif
287 		if ((pnp = expand_paths(lmp, str,
288 		    (LA_SER_DEFAULT | LA_SER_CONFIG), PN_TKN_HWCAP)) != 0)
289 			elf_fct.fct_dflt_dirs = pnp;
290 		features |= CONF_EDLIBPATH;
291 	}
292 	if (head->ch_eslibpath) {
293 		str = (const char *)(head->ch_eslibpath + addr);
294 #ifndef	SGS_PRE_UNIFIED_PROCESS
295 		if ((head->ch_cnflags & RTC_HDR_UPM) == 0) {
296 #if	defined(_ELF64)
297 			str = conv_upm_string(str,
298 			    MSG_ORIG(MSG_PTH_USRLIBSE_64),
299 			    MSG_ORIG(MSG_PTH_LIBSE_64), MSG_PTH_LIBSE_64_SIZE);
300 #else
301 			str = conv_upm_string(str, MSG_ORIG(MSG_PTH_USRLIBSE),
302 			    MSG_ORIG(MSG_PTH_LIBSE), MSG_PTH_LIBSE_SIZE);
303 #endif
304 		}
305 #endif
306 		if ((pnp = expand_paths(lmp, str,
307 		    (LA_SER_SECURE | LA_SER_CONFIG), PN_TKN_HWCAP)) != 0)
308 			elf_fct.fct_secure_dirs = pnp;
309 		features |= CONF_ESLIBPATH;
310 	}
311 #if	defined(__sparc) && !defined(_ELF64)
312 	if (head->ch_adlibpath) {
313 		str = (const char *)(head->ch_adlibpath + addr);
314 		if ((pnp = expand_paths(lmp, str,
315 		    (LA_SER_DEFAULT | LA_SER_CONFIG), PN_TKN_HWCAP)) != 0)
316 			aout_fct.fct_dflt_dirs = pnp;
317 		features |= CONF_ADLIBPATH;
318 	}
319 	if (head->ch_aslibpath) {
320 		str = (const char *)(head->ch_aslibpath + addr);
321 		if ((pnp = expand_paths(lmp, str,
322 		    (LA_SER_SECURE | LA_SER_CONFIG), PN_TKN_HWCAP)) != 0)
323 			aout_fct.fct_secure_dirs = pnp;
324 		features |= CONF_ASLIBPATH;
325 	}
326 #endif
327 	/*
328 	 * Apply any environment variables.  This attribute was added with
329 	 * RTC_VER_THREE.
330 	 */
331 	if ((head->ch_version >= RTC_VER_THREE) && head->ch_env &&
332 	    (!(rtld_flags & RT_FL_NOENVCFG))) {
333 		if (readenv_config((Rtc_env *)(head->ch_env + addr),
334 		    addr, aout) != 0)
335 			return (-1);
336 		features |= CONF_ENVS;
337 	}
338 
339 	/*
340 	 * Determine whether filter/filtee associations are available.
341 	 */
342 	if ((head->ch_version >= RTC_VER_FOUR) && head->ch_fltr &&
343 	    (!(rtld_flags2 & RT_FL2_NOFLTCFG))) {
344 		rtld_flags2 |= RT_FL2_FLTCFG;
345 		config->c_fltr = (Rtc_fltr *)(head->ch_fltr + addr);
346 		config->c_flte = (Rtc_flte *)(head->ch_flte + addr);
347 		features |= CONF_FLTR;
348 	}
349 
350 	/*
351 	 * Determine whether directory configuration is available.
352 	 */
353 	if ((!(rtld_flags & RT_FL_NODIRCFG)) && head->ch_hash) {
354 		config->c_hashtbl = (Word *)(head->ch_hash + addr);
355 		config->c_hashchain = &config->c_hashtbl[2 +
356 		    config->c_hashtbl[0]];
357 		config->c_objtbl = (Rtc_obj *)(head->ch_obj + addr);
358 		config->c_strtbl = (const char *)(head->ch_str + addr);
359 
360 		rtld_flags |= RT_FL_DIRCFG;
361 		features |= CONF_DIRCFG;
362 	}
363 
364 	/*
365 	 * Determine whether alternative objects are specified or an object
366 	 * reservation area is required.  If the reservation can't be completed
367 	 * (either because the configuration information is out-of-date, or the
368 	 * the reservation can't be allocated), then alternative objects are
369 	 * ignored.
370 	 */
371 	if ((!(rtld_flags & (RT_FL_NODIRCFG | RT_FL_NOOBJALT))) &&
372 	    (head->ch_cnflags & RTC_HDR_ALTER)) {
373 		rtld_flags |= RT_FL_OBJALT;
374 		features |= CONF_OBJALT;
375 
376 		elf_config_validate(addr, head, lmp);
377 
378 		if (head->ch_resbgn) {
379 
380 			if (((config->c_bgn <= head->ch_resbgn) &&
381 			    (config->c_bgn >= head->ch_resend)) ||
382 			    (nu_map((caddr_t)(uintptr_t)head->ch_resbgn,
383 			    (head->ch_resend - head->ch_resbgn), PROT_NONE,
384 			    MAP_FIXED | MAP_PRIVATE) == MAP_FAILED))
385 				return (-1);
386 
387 			rtld_flags |= RT_FL_MEMRESV;
388 			features |= CONF_MEMRESV;
389 		}
390 	}
391 
392 	return (features);
393 }
394 
395 /*
396  * Determine whether the given file exists in the configuration file.
397  */
398 Rtc_obj *
399 elf_config_ent(const char *name, Word hash, int id, const char **alternate)
400 {
401 	Word		bkt, ndx;
402 	const char	*str;
403 	Rtc_obj		*obj;
404 
405 	bkt = hash % config->c_hashtbl[0];
406 	ndx = config->c_hashtbl[2 + bkt];
407 
408 	while (ndx) {
409 		obj = config->c_objtbl + ndx;
410 		str = config->c_strtbl + obj->co_name;
411 
412 		if ((obj->co_hash != hash) || (strcmp(name, str) != 0) ||
413 		    (id && (id != obj->co_id))) {
414 			ndx = config->c_hashchain[ndx];
415 			continue;
416 		}
417 
418 		if ((obj->co_flags & RTC_OBJ_ALTER) && alternate)
419 			*alternate = config->c_strtbl + obj->co_alter;
420 
421 		return (obj);
422 	}
423 	return (0);
424 }
425 
426 /*
427  * Determine whether a filter and filtee string pair exists in the configuration
428  * file.  If so, return the cached filtees that are associated with this pair as
429  * a Pnode list.
430  */
431 Pnode *
432 elf_config_flt(const char *filter, const char *string)
433 {
434 	Rtc_fltr *	fltrtbl;
435 	Pnode *		pnp = 0, *npnp, *opnp = 0;
436 
437 	for (fltrtbl = (Rtc_fltr *)config->c_fltr; fltrtbl->fr_filter;
438 	    fltrtbl++) {
439 		Rtc_flte	*fltetbl;
440 		const char	*fltr, *str;
441 
442 		fltr = config->c_strtbl + fltrtbl->fr_filter;
443 		str = config->c_strtbl + fltrtbl->fr_string;
444 		if (strcmp(filter, fltr) || strcmp(string, str))
445 			continue;
446 
447 		/*
448 		 * Create a pnode list for each filtee associated with this
449 		 * filter/filtee string pair.  Note, no expansion of filtee
450 		 * entries is called for, as any original expansion would have
451 		 * been carried out before they were recorded in the
452 		 * configuration file.
453 		 */
454 		/* LINTED */
455 		for (fltetbl = (Rtc_flte *)((char *)config->c_flte +
456 		    fltrtbl->fr_filtee); fltetbl->fe_filtee; fltetbl++) {
457 			const char	*flte;
458 
459 			flte = config->c_strtbl + fltetbl->fe_filtee;
460 
461 			if (((npnp = calloc(1, sizeof (Pnode))) == 0) ||
462 			    ((npnp->p_name = strdup(flte)) == 0))
463 				return (0);
464 
465 			DBG_CALL(Dbg_file_filter(fltr, flte, 1));
466 
467 			if (opnp == 0)
468 				pnp = npnp;
469 			else
470 				opnp->p_next = npnp;
471 
472 			npnp->p_len = strlen(flte) + 1;
473 			npnp->p_orig = LA_SER_CONFIG;
474 
475 			opnp = npnp;
476 		}
477 		return (pnp);
478 	}
479 	return (0);
480 }
481