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