cap.c revision 5088:26c540f30cd3
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 2007 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/types.h>
31#include	<sys/mman.h>
32#include	<dirent.h>
33#include	<stdio.h>
34#include	<stdlib.h>
35#include	<string.h>
36#include	<limits.h>
37#include	<debug.h>
38#include	<conv.h>
39#include	"_rtld.h"
40#include	"_audit.h"
41#include	"msg.h"
42
43/*
44 * qsort(3c) comparison function.
45 */
46static int
47compare(const void *fdesc1, const void *fdesc2)
48{
49	ulong_t	hwcap1 = ((Fdesc *)fdesc1)->fd_fmap.fm_hwptr;
50	ulong_t	hwcap2 = ((Fdesc *)fdesc2)->fd_fmap.fm_hwptr;
51
52	if (hwcap1 && (hwcap2 == 0))
53		return (-1);
54	if ((hwcap1 == 0) && hwcap2)
55		return (1);
56	if ((hwcap1 == 0) && (hwcap2 == 0))
57		return (0);
58
59	if (hwcap1 > hwcap2)
60		return (-1);
61	if (hwcap1 < hwcap2)
62		return (1);
63	return (0);
64}
65
66/*
67 * If this object defines a set of hardware capability requirements, insure the
68 * kernal can cope with them.
69 */
70int
71hwcap_check(Rej_desc *rej, Ehdr *ehdr)
72{
73	Cap	*cptr;
74	Phdr	*phdr;
75	int	cnt;
76
77	/* LINTED */
78	phdr = (Phdr *)((char *)ehdr + ehdr->e_phoff);
79	for (cnt = 0; cnt < ehdr->e_phnum; cnt++, phdr++) {
80		Lword	val;
81
82		if (phdr->p_type != PT_SUNWCAP)
83			continue;
84
85		/* LINTED */
86		cptr = (Cap *)((char *)ehdr + phdr->p_offset);
87		while (cptr->c_tag != CA_SUNW_NULL) {
88			if (cptr->c_tag == CA_SUNW_HW_1)
89				break;
90			cptr++;
91		}
92		if (cptr->c_tag == CA_SUNW_NULL)
93			break;
94
95		if ((val = (cptr->c_un.c_val & ~hwcap)) != 0) {
96			static Conv_cap_val_hw1_buf_t cap_buf;
97
98			rej->rej_type = SGS_REJ_HWCAP_1;
99			rej->rej_str =
100			    conv_cap_val_hw1(val, M_MACH, 0, &cap_buf);
101			return (0);
102		}
103
104		/*
105		 * Retain this hardware capabilities pointer for possible later
106		 * inspection should this object be processed as a filtee.
107		 */
108		fmap->fm_hwptr = cptr->c_un.c_val;
109	}
110	return (1);
111}
112
113static void
114remove_fdesc(Fdesc *fdp)
115{
116#if	defined(MAP_ALIGN)
117	if (fdp->fd_fmap.fm_maddr &&
118	    ((fdp->fd_fmap.fm_mflags & MAP_ALIGN) == 0)) {
119#else
120	if (fdp->fd_fmap.fm_maddr) {
121#endif
122		(void) munmap(fdp->fd_fmap.fm_maddr, fdp->fd_fmap.fm_msize);
123
124		/*
125		 * Note, this file descriptor might be duplicating information
126		 * from the global fmap descriptor.  If so, clean up the global
127		 * descriptor to prevent a duplicate (unnecessary) unmap.
128		 */
129		if (fmap->fm_maddr == fdp->fd_fmap.fm_maddr) {
130			fmap->fm_maddr = 0;
131			fmap_setup();
132		}
133	}
134	if (fdp->fd_fd)
135		(void) close(fdp->fd_fd);
136	if (fdp->fd_pname && (fdp->fd_pname != fdp->fd_nname))
137		free((void *)fdp->fd_pname);
138	if (fdp->fd_nname)
139		free((void *)fdp->fd_nname);
140}
141
142/*
143 * When $HWCAP is used to represent dependencies, take the associated directory
144 * and analyze all the files it contains.
145 */
146int
147hwcap_dir(Alist **fdalpp, Lm_list *lml, const char *name, Rt_map *clmp,
148    uint_t flags, Rej_desc *rej)
149{
150	char		path[PATH_MAX], *dst;
151	const char	*src;
152	DIR		*dir;
153	struct dirent	*dirent;
154	Aliste		off;
155	Alist		*fdalp = 0;
156	Fdesc		*fdp;
157	int		error = 0;
158
159	/*
160	 * Access the directory in preparation for reading its entries.  If
161	 * successful, establish the initial pathname.
162	 */
163	if ((dir = opendir(name)) == 0) {
164		Rej_desc	_rej = { 0 };
165
166		_rej.rej_type = SGS_REJ_STR;
167		_rej.rej_name = name;
168		_rej.rej_str = strerror(errno);
169		DBG_CALL(Dbg_file_rejected(lml, &_rej));
170		rejection_inherit(rej, &_rej);
171		return (0);
172	}
173
174	for (dst = path, src = name; *src; dst++, src++)
175		*dst = *src;
176	*dst++ = '/';
177
178	/*
179	 * Read each entry from the directory and determine whether it is a
180	 * valid ELF file.
181	 */
182	while ((dirent = readdir(dir)) != NULL) {
183		const char	*file = dirent->d_name, *oname;
184		char		*_dst;
185		Fdesc		fdesc = { 0 };
186		Rej_desc	_rej = { 0 };
187
188		/*
189		 * Ignore "." and ".." entries.
190		 */
191		if ((file[0] == '.') && ((file[1] == '\0') ||
192		    ((file[1] == '.') && (file[2] == '\0'))))
193			continue;
194
195		/*
196		 * Complete the full pathname, and verify its usability.  Note,
197		 * an auditor can supply an alternative name.
198		 */
199		for (_dst = dst, src = file, file = dst; *src; _dst++, src++)
200			*_dst = *src;
201		*_dst = '\0';
202
203		if ((oname = strdup(path)) == NULL) {
204			error = 1;
205			break;
206		}
207
208		if (load_trace(lml, &oname, clmp) == 0) {
209			free((void *)oname);
210			continue;
211		}
212		name = oname;
213
214		/*
215		 * Note, all directory entries are processed by find_path(),
216		 * even entries that are directories themselves.  This single
217		 * point for control keeps the number of stat()'s down, and
218		 * provides a single point for error diagnostics.
219		 */
220		if (find_path(lml, name, clmp, flags, &fdesc, &_rej) == 0) {
221			rejection_inherit(rej, &_rej);
222			if ((rej->rej_name != _rej.rej_name) &&
223			    (_rej.rej_name == name))
224				free((void *)name);
225			continue;
226		}
227
228		DBG_CALL(Dbg_cap_hw_candidate(lml, name));
229
230		/*
231		 * If this object has already been loaded, obtain the hardware
232		 * capabilities for later sorting.  Otherwise we have a new
233		 * candidate.
234		 */
235		if (fdesc.fd_lmp)
236			fdesc.fd_fmap.fm_hwptr = HWCAP(fdesc.fd_lmp);
237		else
238			fdesc.fd_fmap = *fmap;
239
240		if (alist_append(&fdalp, &fdesc, sizeof (Fdesc), 10) == 0) {
241			remove_fdesc(&fdesc);
242			error = 1;
243			break;
244		}
245
246		/*
247		 * Clear the global file mapping structure so that the mapping
248		 * for this file won't be overriden.
249		 */
250		fmap->fm_mflags = MAP_PRIVATE;
251		fmap->fm_maddr = 0;
252		fmap->fm_msize = FMAP_SIZE;
253		fmap->fm_hwptr = 0;
254	}
255	(void) closedir(dir);
256
257	/*
258	 * If no objects have been found, we're done.  Also, if an allocation
259	 * error occurred while processing any object, remove any objects that
260	 * had already been added to the list and return.
261	 */
262	if ((fdalp == 0) || error) {
263		if (fdalp) {
264			for (ALIST_TRAVERSE(fdalp, off, fdp))
265				remove_fdesc(fdp);
266			free(fdalp);
267		}
268		return (0);
269	}
270
271	/*
272	 * Having processed and retained all candidates from this directory,
273	 * sort them, based on the precedence of their hardware capabilities.
274	 */
275	qsort(&(fdalp->al_data[0]), ((fdalp->al_next - (sizeof (Alist) -
276	    sizeof (void *))) / fdalp->al_size), fdalp->al_size, compare);
277
278	*fdalpp = fdalp;
279	return (1);
280}
281
282static Pnode *
283_hwcap_filtees(Pnode **pnpp, Aliste nlmco, Lm_cntl *nlmc, Rt_map *flmp,
284    const char *ref, const char *dir, int mode, uint_t flags)
285{
286	Alist		*fdalp = 0;
287	Aliste		off;
288	Pnode		*fpnp = 0, *lpnp, *npnp = (*pnpp)->p_next;
289	Fdesc		*fdp;
290	Lm_list		*lml = LIST(flmp);
291	int		unused = 0;
292	Rej_desc	rej = { 0 };
293
294	if (hwcap_dir(&fdalp, lml, dir, flmp, flags, &rej) == 0) {
295		remove_rej(&rej);
296		return (0);
297	}
298
299	/*
300	 * Now complete the mapping of each of the ordered objects, adding
301	 * each object to a new Pnode.
302	 */
303	for (ALIST_TRAVERSE(fdalp, off, fdp)) {
304		Rt_map	*nlmp;
305		Grp_hdl	*ghp = 0;
306		Pnode	*pnp;
307		int	audit = 0;
308
309		if (unused) {
310			/*
311			 * Flush out objects remaining.
312			 */
313			remove_fdesc(fdp);
314			continue;
315		}
316
317		/*
318		 * Complete mapping the file, obtaining a handle, and continue
319		 * to analyze the object, establishing dependencies and
320		 * relocating.  Remove the file descriptor at this point, as it
321		 * is no longer required.
322		 */
323		DBG_CALL(Dbg_file_filtee(lml, NAME(flmp), fdp->fd_nname, 0));
324
325		nlmp = load_path(lml, nlmco, &fdp->fd_nname, flmp, mode,
326		    (flags | FLG_RT_HANDLE), &ghp, fdp, &rej);
327		remove_fdesc(fdp);
328		if (nlmp == 0)
329			continue;
330
331		/*
332		 * Create a new Pnode to represent this filtee, and substitute
333		 * the calling Pnode (which was used to represent the hardware
334		 * capability directory).
335		 */
336		if ((pnp = calloc(1, sizeof (Pnode))) == 0) {
337			if (ghp) {
338				remove_lmc(lml, flmp, nlmc, nlmco,
339				    fdp->fd_nname);
340			}
341			return (0);
342		}
343		if ((pnp->p_name = strdup(NAME(nlmp))) == NULL) {
344			if (ghp) {
345				remove_lmc(lml, flmp, nlmc, nlmco,
346				    fdp->fd_nname);
347			}
348			free(pnp);
349			return (0);
350		}
351		pnp->p_len = strlen(NAME(nlmp));
352		pnp->p_info = (void *)ghp;
353		pnp->p_next = npnp;
354
355		if (fpnp == 0) {
356			Pnode	*opnp = (*pnpp);
357
358			/*
359			 * If this is the first pnode, reuse the original after
360			 * freeing any of its pathnames.
361			 */
362			if (opnp->p_name)
363				free((void *)opnp->p_name);
364			if (opnp->p_oname)
365				free((void *)opnp->p_oname);
366			*opnp = *pnp;
367			free((void *)pnp);
368			fpnp = lpnp = pnp = opnp;
369		} else {
370			lpnp->p_next = pnp;
371			lpnp = pnp;
372		}
373
374		/*
375		 * Establish the filter handle to prevent any recursion.
376		 */
377		if (nlmp && ghp) {
378			ghp->gh_flags |= GPH_FILTEE;
379			pnp->p_info = (void *)ghp;
380		}
381
382		/*
383		 * Audit the filter/filtee established.  A return of 0
384		 * indicates the auditor wishes to ignore this filtee.
385		 */
386		if (nlmp && (lml->lm_tflags | FLAGS1(flmp)) &
387		    LML_TFLG_AUD_OBJFILTER) {
388			if (audit_objfilter(flmp, ref, nlmp, 0) == 0) {
389				audit = 1;
390				nlmp = 0;
391			}
392		}
393
394		/*
395		 * Finish processing the objects associated with this request.
396		 */
397		if (nlmp && ghp && ((analyze_lmc(lml, nlmco, nlmp) == 0) ||
398		    (relocate_lmc(lml, nlmco, flmp, nlmp) == 0)))
399			nlmp = 0;
400
401		/*
402		 * If the filtee has been successfully processed, then create
403		 * an association between the filter and the filtee.  This
404		 * association provides sufficient information to tear down the
405		 * filter and filtee if necessary.
406		 */
407		DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD));
408		if (nlmp && ghp && (hdl_add(ghp, flmp, GPD_FILTER) == 0))
409			nlmp = 0;
410
411		/*
412		 * If this object is marked an end-filtee, we're done.
413		 */
414		if (nlmp && ghp && (FLAGS1(nlmp) & FL1_RT_ENDFILTE))
415			unused = 1;
416
417		/*
418		 * If this filtee loading has failed, generate a diagnostic.
419		 * Null out the pnode entry, and continue the search.
420		 */
421		if (nlmp == 0) {
422			/*
423			 * If attempting to load this filtee required a new
424			 * link-map control list to which this request has
425			 * added objects, then remove all the objects that
426			 * have been associated to this request.
427			 */
428			if (nlmc && nlmc->lc_head)
429				remove_lmc(lml, flmp, nlmc, nlmco, pnp->p_name);
430
431			DBG_CALL(Dbg_file_filtee(lml, 0, pnp->p_name, audit));
432
433			pnp->p_len = 0;
434			pnp->p_info = 0;
435		}
436	}
437
438	free(fdalp);
439	return (fpnp);
440}
441
442Pnode *
443hwcap_filtees(Pnode **pnpp, Aliste nlmco, Lm_cntl *nlmc, Dyninfo *dip,
444    Rt_map *flmp, const char *ref, int mode, uint_t flags)
445{
446	Pnode		*pnp = *pnpp;
447	const char	*dir = pnp->p_name;
448	Lm_list		*flml = LIST(flmp);
449
450	DBG_CALL(Dbg_cap_hw_filter(flml, dir, flmp));
451
452	if ((pnp = _hwcap_filtees(pnpp, nlmco, nlmc, flmp, ref, dir, mode,
453	    flags)) != 0)
454		return (pnp);
455
456	/*
457	 * If no hardware capability filtees have been found, provide suitable
458	 * diagnostics and mark the incoming Pnode as unused.
459	 */
460	if ((flml->lm_flags & LML_FLG_TRC_ENABLE) &&
461	    (dip->di_flags & FLG_DI_AUXFLTR) && (rtld_flags & RT_FL_WARNFLTR))
462		(void) printf(MSG_INTL(MSG_LDD_HWCAP_NFOUND), dir);
463
464	DBG_CALL(Dbg_cap_hw_filter(flml, dir, 0));
465
466	pnp = *pnpp;
467	pnp->p_len = 0;
468	return (pnp);
469}
470
471/*
472 * Load an individual hardware capabilities object.
473 */
474Rt_map *
475load_hwcap(Lm_list *lml, Aliste lmco, const char *dir, Rt_map *clmp,
476    uint_t mode, uint_t flags, Grp_hdl **hdl, Rej_desc *rej)
477{
478	Alist		*fdalp = 0;
479	Aliste		off;
480	Fdesc		*fdp;
481	int		found = 0;
482	Rt_map		*lmp = 0;
483
484	/*
485	 * Obtain the sorted list of hardware capabilites objects available.
486	 */
487	if (hwcap_dir(&fdalp, lml, dir, clmp, flags, rej) == 0)
488		return (0);
489
490	/*
491	 * From the list of hardware capability objects, use the first and
492	 * discard the rest.
493	 */
494	for (ALIST_TRAVERSE(fdalp, off, fdp)) {
495		if ((found == 0) && ((lmp = load_path(lml, lmco, &fdp->fd_nname,
496		    clmp, mode, flags, hdl, fdp, rej)) != 0))
497			found++;
498
499		/*
500		 * Remove the used file descriptor and any objects remaining.
501		 */
502		remove_fdesc(fdp);
503	}
504
505	free(fdalp);
506	return (lmp);
507}
508