xref: /illumos-gate/usr/src/cmd/sgs/rtld/common/cap.c (revision 643e2e74e1c00e6b3d1896a6a67dbdb7308135c3)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include	<sys/types.h>
30 #include	<sys/mman.h>
31 #include	<dirent.h>
32 #include	<stdio.h>
33 #include	<stdlib.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  */
45 static int
46 compare(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  */
69 int
70 hwcap_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 			static Conv_cap_val_hw1_buf_t cap_buf;
96 
97 			rej->rej_type = SGS_REJ_HWCAP_1;
98 			rej->rej_str =
99 			    conv_cap_val_hw1(val, M_MACH, 0, &cap_buf);
100 			return (0);
101 		}
102 
103 		/*
104 		 * Retain this hardware capabilities pointer for possible later
105 		 * inspection should this object be processed as a filtee.
106 		 */
107 		fmap->fm_hwptr = cptr->c_un.c_val;
108 	}
109 	return (1);
110 }
111 
112 static void
113 remove_fdesc(Fdesc *fdp)
114 {
115 #if	defined(MAP_ALIGN)
116 	if (fdp->fd_fmap.fm_maddr &&
117 	    ((fdp->fd_fmap.fm_mflags & MAP_ALIGN) == 0)) {
118 #else
119 	if (fdp->fd_fmap.fm_maddr) {
120 #endif
121 		(void) munmap(fdp->fd_fmap.fm_maddr, fdp->fd_fmap.fm_msize);
122 
123 		/*
124 		 * Note, this file descriptor might be duplicating information
125 		 * from the global fmap descriptor.  If so, clean up the global
126 		 * descriptor to prevent a duplicate (unnecessary) unmap.
127 		 */
128 		if (fmap->fm_maddr == fdp->fd_fmap.fm_maddr) {
129 			fmap->fm_maddr = 0;
130 			fmap_setup();
131 		}
132 	}
133 	if (fdp->fd_fd)
134 		(void) close(fdp->fd_fd);
135 	if (fdp->fd_pname && (fdp->fd_pname != fdp->fd_nname))
136 		free((void *)fdp->fd_pname);
137 	if (fdp->fd_nname)
138 		free((void *)fdp->fd_nname);
139 }
140 
141 /*
142  * When $HWCAP is used to represent dependencies, take the associated directory
143  * and analyze all the files it contains.
144  */
145 static int
146 hwcap_dir(Alist **fdalpp, Lm_list *lml, const char *name, Rt_map *clmp,
147     uint_t flags, Rej_desc *rej, int *in_nfavl)
148 {
149 	char		path[PATH_MAX], *dst;
150 	const char	*src;
151 	DIR		*dir;
152 	struct dirent	*dirent;
153 	Aliste		idx;
154 	Alist		*fdalp = NULL;
155 	Fdesc		*fdp;
156 	int		error = 0;
157 
158 	/*
159 	 * Access the directory in preparation for reading its entries.  If
160 	 * successful, establish the initial pathname.
161 	 */
162 	if ((dir = opendir(name)) == 0) {
163 		Rej_desc	_rej = { 0 };
164 
165 		_rej.rej_type = SGS_REJ_STR;
166 		_rej.rej_name = name;
167 		_rej.rej_str = strerror(errno);
168 		DBG_CALL(Dbg_file_rejected(lml, &_rej, M_MACH));
169 		rejection_inherit(rej, &_rej);
170 		return (0);
171 	}
172 
173 	for (dst = path, src = name; *src; dst++, src++)
174 		*dst = *src;
175 	*dst++ = '/';
176 
177 	/*
178 	 * Read each entry from the directory and determine whether it is a
179 	 * valid ELF file.
180 	 */
181 	while ((dirent = readdir(dir)) != NULL) {
182 		const char	*file = dirent->d_name, *oname;
183 		char		*_dst;
184 		Fdesc		fdesc = { 0 };
185 		Rej_desc	_rej = { 0 };
186 
187 		/*
188 		 * Ignore "." and ".." entries.
189 		 */
190 		if ((file[0] == '.') && ((file[1] == '\0') ||
191 		    ((file[1] == '.') && (file[2] == '\0'))))
192 			continue;
193 
194 		/*
195 		 * Complete the full pathname, and verify its usability.  Note,
196 		 * an auditor can supply an alternative name.
197 		 */
198 		for (_dst = dst, src = file, file = dst; *src; _dst++, src++)
199 			*_dst = *src;
200 		*_dst = '\0';
201 
202 		if ((oname = strdup(path)) == NULL) {
203 			error = 1;
204 			break;
205 		}
206 
207 		if (load_trace(lml, &oname, clmp) == 0) {
208 			free((void *)oname);
209 			continue;
210 		}
211 		name = oname;
212 
213 		/*
214 		 * Note, all directory entries are processed by find_path(),
215 		 * even entries that are directories themselves.  This single
216 		 * point for control keeps the number of stat()'s down, and
217 		 * provides a single point for error diagnostics.
218 		 */
219 		if (find_path(lml, name, clmp, flags, &fdesc,
220 		    &_rej, in_nfavl) == 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 == NULL) || error) {
263 		if (fdalp) {
264 			for (ALIST_TRAVERSE(fdalp, idx, 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, fdalp->al_nitems, fdalp->al_size, compare);
276 
277 	*fdalpp = fdalp;
278 	return (1);
279 }
280 
281 static Pnode *
282 _hwcap_filtees(Pnode **pnpp, Aliste nlmco, Lm_cntl *nlmc, Rt_map *flmp,
283     const char *ref, const char *dir, int mode, uint_t flags, int *in_nfavl)
284 {
285 	Alist		*fdalp = NULL;
286 	Aliste		idx;
287 	Pnode		*fpnp = 0, *lpnp, *npnp = (*pnpp)->p_next;
288 	Fdesc		*fdp;
289 	Lm_list		*lml = LIST(flmp);
290 	int		unused = 0;
291 	Rej_desc	rej = { 0 };
292 
293 	if (hwcap_dir(&fdalp, lml, dir, flmp, flags, &rej, in_nfavl) == 0) {
294 		remove_rej(&rej);
295 		return (0);
296 	}
297 
298 	/*
299 	 * Now complete the mapping of each of the ordered objects, adding
300 	 * each object to a new Pnode.
301 	 */
302 	for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
303 		Rt_map	*nlmp;
304 		Grp_hdl	*ghp = 0;
305 		Pnode	*pnp;
306 		int	audit = 0;
307 
308 		if (unused) {
309 			/*
310 			 * Flush out objects remaining.
311 			 */
312 			remove_fdesc(fdp);
313 			continue;
314 		}
315 
316 		/*
317 		 * Complete mapping the file, obtaining a handle, and continue
318 		 * to analyze the object, establishing dependencies and
319 		 * relocating.  Remove the file descriptor at this point, as it
320 		 * is no longer required.
321 		 */
322 		DBG_CALL(Dbg_file_filtee(lml, NAME(flmp), fdp->fd_nname, 0));
323 
324 		nlmp = load_path(lml, nlmco, &fdp->fd_nname, flmp, mode,
325 		    (flags | FLG_RT_HANDLE), &ghp, fdp, &rej, in_nfavl);
326 		remove_fdesc(fdp);
327 		if (nlmp == 0)
328 			continue;
329 
330 		/*
331 		 * Create a new Pnode to represent this filtee, and substitute
332 		 * the calling Pnode (which was used to represent the hardware
333 		 * capability directory).
334 		 */
335 		if ((pnp = calloc(1, sizeof (Pnode))) == 0) {
336 			if (ghp) {
337 				remove_lmc(lml, flmp, nlmc, nlmco,
338 				    fdp->fd_nname);
339 			}
340 			return (0);
341 		}
342 		if ((pnp->p_name = strdup(NAME(nlmp))) == NULL) {
343 			if (ghp) {
344 				remove_lmc(lml, flmp, nlmc, nlmco,
345 				    fdp->fd_nname);
346 			}
347 			free(pnp);
348 			return (0);
349 		}
350 		pnp->p_len = strlen(NAME(nlmp));
351 		pnp->p_info = (void *)ghp;
352 		pnp->p_next = npnp;
353 
354 		if (fpnp == 0) {
355 			Pnode	*opnp = (*pnpp);
356 
357 			/*
358 			 * If this is the first pnode, reuse the original after
359 			 * freeing any of its pathnames.
360 			 */
361 			if (opnp->p_name)
362 				free((void *)opnp->p_name);
363 			if (opnp->p_oname)
364 				free((void *)opnp->p_oname);
365 			*opnp = *pnp;
366 			free((void *)pnp);
367 			fpnp = lpnp = pnp = opnp;
368 		} else {
369 			lpnp->p_next = pnp;
370 			lpnp = pnp;
371 		}
372 
373 		/*
374 		 * Establish the filter handle to prevent any recursion.
375 		 */
376 		if (nlmp && ghp) {
377 			ghp->gh_flags |= GPH_FILTEE;
378 			pnp->p_info = (void *)ghp;
379 		}
380 
381 		/*
382 		 * Audit the filter/filtee established.  A return of 0
383 		 * indicates the auditor wishes to ignore this filtee.
384 		 */
385 		if (nlmp && (lml->lm_tflags | FLAGS1(flmp)) &
386 		    LML_TFLG_AUD_OBJFILTER) {
387 			if (audit_objfilter(flmp, ref, nlmp, 0) == 0) {
388 				audit = 1;
389 				nlmp = 0;
390 			}
391 		}
392 
393 		/*
394 		 * Finish processing the objects associated with this request.
395 		 */
396 		if (nlmp && ghp &&
397 		    ((analyze_lmc(lml, nlmco, nlmp, in_nfavl) == 0) ||
398 		    (relocate_lmc(lml, nlmco, flmp, nlmp, in_nfavl) == 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 
442 Pnode *
443 hwcap_filtees(Pnode **pnpp, Aliste nlmco, Lm_cntl *nlmc, Dyninfo *dip,
444     Rt_map *flmp, const char *ref, int mode, uint_t flags, int *in_nfavl)
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, in_nfavl)) != 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  */
474 Rt_map *
475 load_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, int *in_nfavl)
477 {
478 	Alist		*fdalp = NULL;
479 	Aliste		idx;
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, in_nfavl) == 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, idx, fdp)) {
495 		if ((found == 0) && ((lmp = load_path(lml, lmco, &fdp->fd_nname,
496 		    clmp, mode, flags, hdl, fdp, rej, in_nfavl)) != 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