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