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