xref: /illumos-gate/usr/src/cmd/sgs/rtld/common/cap.c (revision 65a89a64c60f3061bbe2381edaacc81660af9a95)
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 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include	"_synonyms.h"
30 
31 #include	<sys/types.h>
32 #include	<sys/mman.h>
33 #include	<dirent.h>
34 #include	<stdio.h>
35 #include	<string.h>
36 #include	<limits.h>
37 #include	"conv.h"
38 #include	"_rtld.h"
39 #include	"_audit.h"
40 #include	"_elf.h"
41 #include	"msg.h"
42 #include	"debug.h"
43 
44 /*
45  * qsort(3c) comparison function.
46  */
47 static int
48 compare(const void *fdesc1, const void *fdesc2)
49 {
50 	ulong_t	hwcap1 = ((Fdesc *)fdesc1)->fd_fmap.fm_hwptr;
51 	ulong_t	hwcap2 = ((Fdesc *)fdesc2)->fd_fmap.fm_hwptr;
52 
53 	if (hwcap1 && (hwcap2 == 0))
54 		return (-1);
55 	if ((hwcap1 == 0) && hwcap2)
56 		return (1);
57 	if ((hwcap1 == 0) && (hwcap2 == 0))
58 		return (0);
59 
60 	if (hwcap1 > hwcap2)
61 		return (-1);
62 	if (hwcap1 < hwcap2)
63 		return (1);
64 	return (0);
65 }
66 
67 /*
68  * If this object defines a set of hardware capability requirements, insure the
69  * kernal can cope with them.
70  */
71 int
72 hwcap_check(Rej_desc *rej, Ehdr *ehdr)
73 {
74 	Cap	*cptr;
75 	Phdr	*phdr;
76 	int	cnt;
77 
78 	/* LINTED */
79 	phdr = (Phdr *)((char *)ehdr + ehdr->e_phoff);
80 	for (cnt = 0; cnt < ehdr->e_phnum; cnt++, phdr++) {
81 		Lword	val;
82 
83 		if (phdr->p_type != PT_SUNWCAP)
84 			continue;
85 
86 		/* LINTED */
87 		cptr = (Cap *)((char *)ehdr + phdr->p_offset);
88 		while (cptr->c_tag != CA_SUNW_NULL) {
89 			if (cptr->c_tag == CA_SUNW_HW_1)
90 				break;
91 			cptr++;
92 		}
93 		if (cptr->c_tag == CA_SUNW_NULL)
94 			break;
95 
96 		if ((val = (cptr->c_un.c_val & ~hwcap)) != 0) {
97 			rej->rej_type = SGS_REJ_HWCAP_1;
98 			rej->rej_str = conv_hwcap_1_str(val, M_MACH);
99 			return (0);
100 		}
101 
102 		/*
103 		 * Retain this hardware capabilities pointer for possible later
104 		 * inspection should this object be processed as a filtee.
105 		 */
106 		fmap->fm_hwptr = cptr->c_un.c_val;
107 	}
108 	return (1);
109 }
110 
111 static void
112 remove_fdesc(Fdesc *fdp)
113 {
114 #if	defined(MAP_ALIGN)
115 	if (fdp->fd_fmap.fm_maddr &&
116 	    ((fdp->fd_fmap.fm_mflags & MAP_ALIGN) == 0)) {
117 #else
118 	if (fdp->fd_fmap.fm_maddr) {
119 #endif
120 		(void) munmap(fdp->fd_fmap.fm_maddr, fdp->fd_fmap.fm_msize);
121 
122 		/*
123 		 * Note, this file descriptor might be duplicating information
124 		 * from the global fmap descriptor.  If so, clean up the global
125 		 * descriptor to prevent a duplicate (unnecessary) unmap.
126 		 */
127 		if (fmap->fm_maddr == fdp->fd_fmap.fm_maddr)
128 			fmap->fm_maddr = 0;
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  */
142 int
143 hwcap_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(&_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(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 
265 static Pnode *
266 _hwcap_filtees(Pnode **pnpp, Aliste nlmco, Rt_map *flmp, const char *ref,
267     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(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 
312 		/*
313 		 * Create a new Pnode to represent this filtee, and substitute
314 		 * the calling Pnode (which was used to represent the hardware
315 		 * capability directory).
316 		 */
317 		if ((pnp = calloc(1, sizeof (Pnode))) == 0) {
318 			if (ghp)
319 				(void) dlclose_core(ghp, flmp);
320 			return (0);
321 		}
322 		if ((pnp->p_name = strdup(NAME(nlmp))) == 0) {
323 			if (ghp)
324 				(void) dlclose_core(ghp, flmp);
325 			free(pnp);
326 			return (0);
327 		}
328 		pnp->p_len = strlen(NAME(nlmp));
329 		pnp->p_info = (void *)ghp;
330 		pnp->p_next = npnp;
331 
332 		if (fpnp == 0) {
333 			Pnode	*opnp = (*pnpp);
334 
335 			/*
336 			 * If this is the first pnode, reuse the original after
337 			 * freeing any of its pathnames.
338 			 */
339 			if (opnp->p_name)
340 				free((void *)opnp->p_name);
341 			if (opnp->p_oname)
342 				free((void *)opnp->p_oname);
343 			*opnp = *pnp;
344 			free((void *)pnp);
345 			fpnp = lpnp = pnp = opnp;
346 		} else {
347 			lpnp->p_next = pnp;
348 			lpnp = pnp;
349 		}
350 
351 		/*
352 		 * Establish the filter handle to prevent any recursion.
353 		 */
354 		if (nlmp && ghp) {
355 			ghp->gh_flags |= GPH_FILTEE;
356 			pnp->p_info = (void *)ghp;
357 		}
358 
359 		/*
360 		 * Audit the filter/filtee established.  A return of 0
361 		 * indicates the auditor wishes to ignore this filtee.
362 		 */
363 		if (nlmp && (lml->lm_tflags | FLAGS1(flmp)) &
364 		    LML_TFLG_AUD_OBJFILTER) {
365 			if (audit_objfilter(flmp, ref, nlmp, 0) == 0) {
366 				audit = 1;
367 				nlmp = 0;
368 			}
369 		}
370 
371 		/*
372 		 * Finish processing the objects associated with this request.
373 		 */
374 		if (nlmp && ghp && ((analyze_lmc(lml, nlmco, nlmp) == 0) ||
375 		    (relocate_lmc(lml, nlmco, nlmp) == 0)))
376 			nlmp = 0;
377 
378 		/*
379 		 * Finally, if the filtee is part of a link-map control list
380 		 * that is equivalent, or less, than the filter control list,
381 		 * create an association between the filter and filtee.  This
382 		 * association provides sufficient information to tear down the
383 		 * filter and filtee if necessary.
384 		 */
385 		if (nlmp && ghp && (CNTL(nlmp) <= CNTL(flmp)) &&
386 		    (hdl_add(ghp, flmp, GPD_FILTER) == 0))
387 			nlmp = 0;
388 
389 		/*
390 		 * If this object is marked an end-filtee, we're done.
391 		 */
392 		if (nlmp && ghp && (FLAGS1(nlmp) & FL1_RT_ENDFILTE))
393 			unused = 1;
394 
395 		/*
396 		 * Generate a diagnostic if the filtee couldn't be loaded, null
397 		 * out the pnode entry, and continue the search.
398 		 */
399 		if (nlmp == 0) {
400 			pnp->p_info = 0;
401 			DBG_CALL(Dbg_file_filtee(0, pnp->p_name, audit));
402 			if (ghp)
403 				(void) dlclose_core(ghp, flmp);
404 
405 			pnp->p_len = 0;
406 		}
407 	}
408 
409 	free(fdalp);
410 	return (fpnp);
411 }
412 
413 Pnode *
414 hwcap_filtees(Pnode **pnpp, Aliste nlmco, Dyninfo *dip, Rt_map *flmp,
415     const char *ref, int mode, uint_t flags)
416 {
417 	Pnode		*pnp = *pnpp;
418 	const char	*dir = pnp->p_name;
419 
420 	DBG_CALL(Dbg_cap_hw_filter(dir, NAME(flmp)));
421 
422 	if ((pnp = _hwcap_filtees(pnpp, nlmco, flmp, ref, dir, mode,
423 	    flags)) != 0)
424 		return (pnp);
425 
426 	/*
427 	 * If no hardware capability filtees have been found, provide suitable
428 	 * diagnostics and mark the incoming Pnode as unused.
429 	 */
430 	if ((LIST(flmp)->lm_flags & LML_FLG_TRC_ENABLE) &&
431 	    (dip->di_flags & FLG_DI_AUXFLTR) && (rtld_flags & RT_FL_WARNFLTR))
432 		(void) printf(MSG_INTL(MSG_LDD_HWCAP_NFOUND), dir);
433 
434 	DBG_CALL(Dbg_cap_hw_filter(dir, 0));
435 
436 	pnp = *pnpp;
437 	pnp->p_len = 0;
438 	return (pnp);
439 }
440 
441 /*
442  * Load an individual hardware capabilities object.
443  */
444 Rt_map *
445 load_hwcap(Lm_list *lml, Aliste lmco, const char *dir, Rt_map *clmp,
446     uint_t mode, uint_t flags, Grp_hdl **hdl, Rej_desc *rej)
447 {
448 	Alist		*fdalp = 0;
449 	Aliste		off;
450 	Fdesc		*fdp;
451 	int		found = 0;
452 	Rt_map		*lmp = 0;
453 
454 	/*
455 	 * Obtain the sorted list of hardware capabilites objects available.
456 	 */
457 	if (hwcap_dir(&fdalp, lml, dir, clmp, flags, rej) == 0)
458 		return (0);
459 
460 	/*
461 	 * From the list of hardware capability objects, use the first and
462 	 * discard the rest.
463 	 */
464 	for (ALIST_TRAVERSE(fdalp, off, fdp)) {
465 		if ((found == 0) && ((lmp = load_path(lml, lmco, fdp->fd_nname,
466 		    clmp, mode, flags, hdl, fdp, rej)) != 0))
467 			found++;
468 
469 		/*
470 		 * Remove the used file descriptor and any objects remaining.
471 		 */
472 		remove_fdesc(fdp);
473 	}
474 
475 	free(fdalp);
476 	return (lmp);
477 }
478