xref: /titanic_52/usr/src/cmd/sgs/rtld/common/cap.c (revision bdfc6d18da790deeec2e0eb09c625902defe2498)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 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	"conv.h"
37 #include	"_rtld.h"
38 #include	"_audit.h"
39 #include	"_elf.h"
40 #include	"msg.h"
41 #include	"debug.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_hwcap_1_str(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 		 * 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 	}
128 	if (fdp->fd_fd)
129 		(void) close(fdp->fd_fd);
130 	if (fdp->fd_nname)
131 		free((void *)fdp->fd_nname);
132 	if (fdp->fd_pname && (fdp->fd_pname != fdp->fd_nname))
133 		free((void *)fdp->fd_pname);
134 }
135 
136 /*
137  * When $HWCAP is used to represent filtees, take the associated filtee
138  * directory and analyze all the files it contains.
139  */
140 int
141 hwcap_dir(Alist ** fdalpp, Lm_list * lml, const char *name, Rt_map * clmp,
142     uint_t flags, Rej_desc *rej)
143 {
144 	char		path[PATH_MAX], *dst;
145 	const char	*src;
146 	DIR		*dir;
147 	struct dirent	*dirent;
148 	Aliste		off;
149 	Alist		*fdalp = 0;
150 	Fdesc		*fdp;
151 	int		error = 0;
152 
153 	/*
154 	 * Access the directory in preparation for reading its entries.  If
155 	 * successful, establish the initial pathname.
156 	 */
157 	if ((dir = opendir(name)) == 0) {
158 		Rej_desc	_rej = { 0 };
159 
160 		_rej.rej_type = SGS_REJ_STR;
161 		_rej.rej_name = name;
162 		_rej.rej_str = strerror(errno);
163 		DBG_CALL(Dbg_file_rejected(&_rej));
164 		rejection_inherit(rej, &_rej, 0);
165 		return (0);
166 	}
167 
168 	for (dst = path, src = name; *src; dst++, src++)
169 		*dst = *src;
170 	*dst++ = '/';
171 
172 	/*
173 	 * Read each entry from the directory and determine whether it is a
174 	 * valid ELF file.
175 	 */
176 	while ((dirent = readdir(dir)) != NULL) {
177 		const char	*file = dirent->d_name;
178 		char		*_dst;
179 		Fdesc		fdesc = { 0 };
180 		Rej_desc	_rej = { 0 };
181 
182 		/*
183 		 * Ignore "." and ".." entries.
184 		 */
185 		if ((file[0] == '.') && ((file[1] == '\0') ||
186 		    ((file[1] == '.') && (file[2] == '\0'))))
187 			continue;
188 
189 		/*
190 		 * Complete the full pathname, and verify its usability.
191 		 */
192 		for (_dst = dst, src = file, file = dst; *src; _dst++, src++)
193 			*_dst = *src;
194 		*_dst = '\0';
195 
196 		if ((name = strdup(path)) == 0) {
197 			error = 1;
198 			break;
199 		}
200 		if ((name = load_trace(lml, name, clmp)) == 0)
201 			continue;
202 
203 		if (find_path(lml, name, clmp, flags, &fdesc, &_rej) == 0) {
204 			rejection_inherit(rej, &_rej, &fdesc);
205 		} else {
206 			DBG_CALL(Dbg_cap_hw_candidate(name));
207 
208 			/*
209 			 * If this object has already been loaded, obtain the
210 			 * hardware capabilities for later sorting.  Otherwise
211 			 * we have a new candidate.
212 			 */
213 			if (fdesc.fd_lmp) {
214 				fdesc.fd_fmap.fm_hwptr = HWCAP(fdesc.fd_lmp);
215 			} else {
216 				fdesc.fd_nname = name;
217 				fdesc.fd_fmap = *fmap;
218 			}
219 			if (alist_append(&fdalp, &fdesc,
220 			    sizeof (Fdesc), 10) == 0) {
221 				remove_fdesc(&fdesc);
222 				error = 1;
223 				break;
224 			}
225 		}
226 
227 		/*
228 		 * Clear the global file mapping structure so that the mapping
229 		 * for this file won't be overriden.
230 		 */
231 		fmap->fm_mflags = MAP_PRIVATE;
232 		fmap->fm_maddr = 0;
233 		fmap->fm_msize = syspagsz;
234 		fmap->fm_hwptr = 0;
235 
236 	}
237 	(void) closedir(dir);
238 
239 	/*
240 	 * If no objects have been found, we're done.  Also, if an allocation
241 	 * error occurred while processing any object, remove any objects that
242 	 * had already been added to the list and return.
243 	 */
244 	if ((fdalp == 0) || error) {
245 		if (fdalp) {
246 			for (ALIST_TRAVERSE(fdalp, off, fdp))
247 				remove_fdesc(fdp);
248 			free(fdalp);
249 		}
250 		return (0);
251 	}
252 
253 	/*
254 	 * Having processed and retained all candidates from this directory,
255 	 * sort them, based on the precedence of their hardware capabilities.
256 	 */
257 	qsort(&(fdalp->al_data[0]), ((fdalp->al_next - (sizeof (Alist) -
258 	    sizeof (void *))) / fdalp->al_size), fdalp->al_size, compare);
259 
260 	*fdalpp = fdalp;
261 	return (1);
262 }
263 
264 static Pnode *
265 _hwcap_filtees(Pnode ** pnpp, Aliste nlmco, Rt_map * flmp, const char *ref,
266     const char *dir, int mode, uint_t flags)
267 {
268 	Alist		*fdalp = 0;
269 	Aliste		off;
270 	Pnode		*fpnp = 0, *lpnp, *npnp = (*pnpp)->p_next;
271 	Fdesc		*fdp;
272 	Lm_list		*lml = LIST(flmp);
273 	int		unused = 0;
274 	Rej_desc	rej = { 0 };
275 
276 	if (hwcap_dir(&fdalp, lml, dir, flmp, flags, &rej) == 0) {
277 		remove_rej(&rej);
278 		return (0);
279 	}
280 
281 	/*
282 	 * Now complete the mapping of each of the ordered objects, adding
283 	 * each object to a new Pnode.
284 	 */
285 	for (ALIST_TRAVERSE(fdalp, off, fdp)) {
286 		Rt_map	*nlmp;
287 		Grp_hdl	*ghp;
288 		Pnode	*pnp;
289 
290 		if (unused) {
291 			/*
292 			 * Flush out objects remaining.
293 			 */
294 			remove_fdesc(fdp);
295 			continue;
296 		}
297 
298 		/*
299 		 * Complete mapping the file, obtaining a handle, and continue
300 		 * to analyze the object, establishing dependencies and
301 		 * relocating.  Remove the file descriptor at this point, as it
302 		 * is no longer required.
303 		 */
304 		DBG_CALL(Dbg_file_filtee(NAME(flmp), fdp->fd_nname, 0));
305 
306 		nlmp = load_path(lml, nlmco, fdp->fd_nname, flmp, mode,
307 		    (flags | FLG_RT_HANDLE), &ghp, fdp, &rej);
308 		remove_fdesc(fdp);
309 
310 		if (nlmp == 0)
311 			continue;
312 
313 		/*
314 		 * Audit the filter/filtee established.  A return of 0
315 		 * indicates the auditor wishes to ignore this filtee.
316 		 */
317 		if ((lml->lm_tflags | FLAGS1(flmp)) &
318 		    LML_TFLG_AUD_OBJFILTER) {
319 			if (audit_objfilter(flmp, ref, nlmp, 0) == 0) {
320 				DBG_CALL(Dbg_file_filtee(0, NAME(nlmp), 1));
321 				(void) dlclose_core(ghp, flmp);
322 				continue;
323 			}
324 		}
325 
326 		ghp->gh_flags |= GPH_FILTEE;
327 
328 		/*
329 		 * Finish processing the objects associated with this request.
330 		 */
331 		if ((analyze_lmc(lml, nlmco, nlmp) == 0) ||
332 		    (relocate_lmc(lml, nlmco, nlmp) == 0)) {
333 			(void) dlclose_core(ghp, flmp);
334 			continue;
335 		}
336 
337 		/*
338 		 * Create a new Pnode to represent this filtee, and substitute
339 		 * the calling Pnode (which was used to represent the hardware
340 		 * capability directory).
341 		 */
342 		if ((pnp = calloc(1, sizeof (Pnode))) == 0) {
343 			(void) dlclose_core(ghp, flmp);
344 			continue;
345 		}
346 		if ((pnp->p_name = strdup(NAME(nlmp))) == 0) {
347 			(void) dlclose_core(ghp, flmp);
348 			free(pnp);
349 			continue;
350 		}
351 		pnp->p_len = strlen(NAME(nlmp));
352 		pnp->p_info = (void *)ghp;
353 		pnp->p_next = npnp;
354 
355 		/*
356 		 * Finally, if the filtee is part of a link-map control list
357 		 * that is equivalent, or less, than the filter control list,
358 		 * create an association between the filter and filtee.  This
359 		 * association provides sufficient information to tear down the
360 		 * filter and filtee if necessary.
361 		 */
362 		if ((CNTL(nlmp) <= CNTL(flmp)) &&
363 		    (hdl_add(ghp, flmp, GPD_FILTER) == 0)) {
364 			(void) dlclose_core(ghp, flmp);
365 			free((void *)pnp->p_name);
366 			free(pnp);
367 			continue;
368 		}
369 
370 		if (fpnp == 0) {
371 			Pnode	*opnp = (*pnpp);
372 			/*
373 			 * If this is the first pnode, reuse the original after
374 			 * freeing any of its pathnames.
375 			 */
376 			if (opnp->p_name)
377 				free((void *)opnp->p_name);
378 			if (opnp->p_oname)
379 				free((void *)opnp->p_oname);
380 			*opnp = *pnp;
381 			free((void *)pnp);
382 			fpnp = lpnp = opnp;
383 		} else {
384 			lpnp->p_next = pnp;
385 			lpnp = pnp;
386 		}
387 
388 		/*
389 		 * If this object is marked an end-filtee, we're done.
390 		 */
391 		if (FLAGS1(nlmp) & FL1_RT_ENDFILTE)
392 			unused = 1;
393 	}
394 
395 	free(fdalp);
396 	return (fpnp);
397 }
398 
399 Pnode *
400 hwcap_filtees(Pnode ** pnpp, Aliste nlmco, Dyninfo * dip, Rt_map * flmp,
401     const char *ref, int mode, uint_t flags)
402 {
403 	Pnode		*pnp = *pnpp;
404 	const char	*dir = pnp->p_name;
405 
406 	DBG_CALL(Dbg_cap_hw_filter(dir, NAME(flmp)));
407 
408 	if ((pnp = _hwcap_filtees(pnpp, nlmco, flmp, ref, dir, mode,
409 	    flags)) != 0)
410 		return (pnp);
411 
412 	/*
413 	 * If no hardware capability filtees have been found, provide suitable
414 	 * diagnostics and mark the incoming Pnode as unused.
415 	 */
416 	if ((LIST(flmp)->lm_flags & LML_FLG_TRC_ENABLE) &&
417 	    (dip->di_flags & FLG_DI_AUXFLTR) && (rtld_flags & RT_FL_WARNFLTR))
418 		(void) printf(MSG_INTL(MSG_LDD_HWCAP_NFOUND), dir);
419 
420 	DBG_CALL(Dbg_cap_hw_filter(dir, 0));
421 
422 	pnp = *pnpp;
423 	pnp->p_len = 0;
424 	return (pnp);
425 }
426 
427 /*
428  * Load an individual hardware capabilities object.
429  */
430 Rt_map *
431 load_hwcap(Lm_list * lml, Aliste lmco, const char *dir, Rt_map * clmp,
432     uint_t mode, uint_t flags, Grp_hdl ** hdl, Rej_desc *rej)
433 {
434 	Alist		*fdalp = 0;
435 	Aliste		off;
436 	Fdesc		*fdp;
437 	int		found = 0;
438 	Rt_map		*lmp = 0;
439 
440 	/*
441 	 * Obtain the sorted list of hardware capabilites objects available.
442 	 */
443 	if (hwcap_dir(&fdalp, lml, dir, clmp, flags, rej) == 0)
444 		return (0);
445 
446 	/*
447 	 * From the list of hardware capability objects, use the first and
448 	 * discard the rest.
449 	 */
450 	for (ALIST_TRAVERSE(fdalp, off, fdp)) {
451 		if ((found == 0) && ((lmp = load_path(lml, lmco, fdp->fd_nname,
452 		    clmp, mode, flags, hdl, fdp, rej)) != 0))
453 			found++;
454 
455 		/*
456 		 * Remove the used file descriptor and any objects remaining.
457 		 */
458 		remove_fdesc(fdp);
459 	}
460 
461 	free(fdalp);
462 	return (lmp);
463 }
464