xref: /titanic_44/usr/src/cmd/sgs/rtld/common/cap.c (revision 9d4bc3946bbac2a517cb4f45a4390166c42fe195)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
55aefb655Srie  * Common Development and Distribution License (the "License").
65aefb655Srie  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21fb1354edSrie 
227c478bd9Sstevel@tonic-gate /*
231c1abfbcSRod Evans  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include	<sys/types.h>
277c478bd9Sstevel@tonic-gate #include	<sys/mman.h>
287c478bd9Sstevel@tonic-gate #include	<dirent.h>
297c478bd9Sstevel@tonic-gate #include	<stdio.h>
308521e5e6Srie #include	<stdlib.h>
317c478bd9Sstevel@tonic-gate #include	<string.h>
327c478bd9Sstevel@tonic-gate #include	<limits.h>
335aefb655Srie #include	<debug.h>
345aefb655Srie #include	<conv.h>
3508278a5eSRod Evans #include	<elfcap.h>
367c478bd9Sstevel@tonic-gate #include	"_rtld.h"
3708278a5eSRod Evans #include	"_elf.h"
387c478bd9Sstevel@tonic-gate #include	"_audit.h"
397c478bd9Sstevel@tonic-gate #include	"msg.h"
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate /*
4208278a5eSRod Evans  * qsort(3c) capability comparison function.
437c478bd9Sstevel@tonic-gate  */
447c478bd9Sstevel@tonic-gate static int
compare(const void * vp_a,const void * vp_b)451c1abfbcSRod Evans compare(const void *vp_a, const void *vp_b)
467c478bd9Sstevel@tonic-gate {
471c1abfbcSRod Evans 	Fdesc	*fdp_a = (Fdesc *)vp_a, *fdp_b = (Fdesc *)vp_b;
4808278a5eSRod Evans 	char	*strcap_a, *strcap_b;
4908278a5eSRod Evans 	Xword	hwcap_a, hwcap_b;
507c478bd9Sstevel@tonic-gate 
5108278a5eSRod Evans 	/*
5208278a5eSRod Evans 	 * First, investigate any platform capability.
5308278a5eSRod Evans 	 */
541c1abfbcSRod Evans 	strcap_a = fdp_a->fd_scapset.sc_plat;
551c1abfbcSRod Evans 	strcap_b = fdp_b->fd_scapset.sc_plat;
567c478bd9Sstevel@tonic-gate 
5708278a5eSRod Evans 	if (strcap_a && (strcap_b == NULL))
587c478bd9Sstevel@tonic-gate 		return (-1);
5908278a5eSRod Evans 	if (strcap_b && (strcap_a == NULL))
607c478bd9Sstevel@tonic-gate 		return (1);
6108278a5eSRod Evans 
6208278a5eSRod Evans 	/*
6308278a5eSRod Evans 	 * Second, investigate any machine capability.
6408278a5eSRod Evans 	 */
651c1abfbcSRod Evans 	strcap_a = fdp_a->fd_scapset.sc_mach;
661c1abfbcSRod Evans 	strcap_b = fdp_b->fd_scapset.sc_mach;
6708278a5eSRod Evans 
6808278a5eSRod Evans 	if (strcap_a && (strcap_b == NULL))
6908278a5eSRod Evans 		return (-1);
7008278a5eSRod Evans 	if (strcap_b && (strcap_a == NULL))
7108278a5eSRod Evans 		return (1);
7208278a5eSRod Evans 
7308278a5eSRod Evans 	/*
7408278a5eSRod Evans 	 * Third, investigate any CA_SUNW_HW_2 hardware capabilities.
7508278a5eSRod Evans 	 */
761c1abfbcSRod Evans 	hwcap_a = fdp_a->fd_scapset.sc_hw_2;
771c1abfbcSRod Evans 	hwcap_b = fdp_b->fd_scapset.sc_hw_2;
7808278a5eSRod Evans 
7908278a5eSRod Evans 	if (hwcap_a > hwcap_b)
8008278a5eSRod Evans 		return (-1);
8108278a5eSRod Evans 	if (hwcap_a < hwcap_b)
8208278a5eSRod Evans 		return (1);
8308278a5eSRod Evans 
8408278a5eSRod Evans 	/*
8508278a5eSRod Evans 	 * Finally, investigate any CA_SUNW_HW_1 hardware capabilities.
8608278a5eSRod Evans 	 */
871c1abfbcSRod Evans 	hwcap_a = fdp_a->fd_scapset.sc_hw_1;
881c1abfbcSRod Evans 	hwcap_b = fdp_b->fd_scapset.sc_hw_1;
8908278a5eSRod Evans 
9008278a5eSRod Evans 	if (hwcap_a > hwcap_b)
9108278a5eSRod Evans 		return (-1);
9208278a5eSRod Evans 	if (hwcap_a < hwcap_b)
9308278a5eSRod Evans 		return (1);
9408278a5eSRod Evans 
951c1abfbcSRod Evans 	/*
961c1abfbcSRod Evans 	 * Normally, a capabilities directory contains one or more capabilities
971c1abfbcSRod Evans 	 * files, each with different capabilities.  The role of ld.so.1 is to
981c1abfbcSRod Evans 	 * select the best candidate from these variants.  However, we've come
991c1abfbcSRod Evans 	 * across cases where files containing the same capabilities have been
1001c1abfbcSRod Evans 	 * placed in the same capabilities directory.  As we can't tell which
1011c1abfbcSRod Evans 	 * file is the best, we select neither, and diagnose this suspicious
1021c1abfbcSRod Evans 	 * scenario.
1031c1abfbcSRod Evans 	 */
1041c1abfbcSRod Evans 	DBG_CALL(Dbg_cap_identical(fdp_a->fd_lml, fdp_a->fd_nname,
1051c1abfbcSRod Evans 	    fdp_b->fd_nname));
1061c1abfbcSRod Evans 
1071c1abfbcSRod Evans 	fdp_a->fd_flags |= FLG_FD_IGNORE;
1081c1abfbcSRod Evans 	fdp_b->fd_flags |= FLG_FD_IGNORE;
1091c1abfbcSRod Evans 
1107c478bd9Sstevel@tonic-gate 	return (0);
1117c478bd9Sstevel@tonic-gate }
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate /*
11408278a5eSRod Evans  * Determine whether HWCAP1 capabilities value is supported.
1157c478bd9Sstevel@tonic-gate  */
1167c478bd9Sstevel@tonic-gate int
hwcap1_check(Syscapset * scapset,Xword val,Rej_desc * rej)11708278a5eSRod Evans hwcap1_check(Syscapset *scapset, Xword val, Rej_desc *rej)
1187c478bd9Sstevel@tonic-gate {
11956deab07SRod Evans 	Xword	mval;
1207c478bd9Sstevel@tonic-gate 
121bebb829dSRod Evans 	/*
12256deab07SRod Evans 	 * Ensure that the kernel can cope with the required capabilities.
123bebb829dSRod Evans 	 */
12408278a5eSRod Evans 	if ((rtld_flags2 & RT_FL2_HWCAP) &&
12508278a5eSRod Evans 	    ((mval = (val & ~scapset->sc_hw_1)) != 0)) {
12608278a5eSRod Evans 		if (rej) {
127de777a60Sab196087 			static Conv_cap_val_hw1_buf_t	cap_buf;
128de777a60Sab196087 
1297c478bd9Sstevel@tonic-gate 			rej->rej_type = SGS_REJ_HWCAP_1;
13008278a5eSRod Evans 			rej->rej_str = conv_cap_val_hw1(mval,
13108278a5eSRod Evans 			    M_MACH, 0, &cap_buf);
13208278a5eSRod Evans 		}
13308278a5eSRod Evans 		return (0);
13408278a5eSRod Evans 	}
13508278a5eSRod Evans 	return (1);
13608278a5eSRod Evans }
13708278a5eSRod Evans 
13808278a5eSRod Evans /*
13908278a5eSRod Evans  * Determine whether HWCAP2 capabilities value is supported.
14008278a5eSRod Evans  */
14108278a5eSRod Evans int
hwcap2_check(Syscapset * scapset,Xword val,Rej_desc * rej)14208278a5eSRod Evans hwcap2_check(Syscapset *scapset, Xword val, Rej_desc *rej)
14308278a5eSRod Evans {
14408278a5eSRod Evans 	Xword	mval;
14508278a5eSRod Evans 
14608278a5eSRod Evans 	/*
14708278a5eSRod Evans 	 * Ensure that the kernel can cope with the required capabilities.
14808278a5eSRod Evans 	 */
14908278a5eSRod Evans 	if ((mval = (val & ~scapset->sc_hw_2)) != 0) {
15008278a5eSRod Evans 		if (rej) {
15108278a5eSRod Evans 			static Conv_cap_val_hw2_buf_t	cap_buf;
15208278a5eSRod Evans 
15308278a5eSRod Evans 			rej->rej_type = SGS_REJ_HWCAP_2;
15408278a5eSRod Evans 			rej->rej_str = conv_cap_val_hw2(mval,
15508278a5eSRod Evans 			    M_MACH, 0, &cap_buf);
15608278a5eSRod Evans 		}
1577c478bd9Sstevel@tonic-gate 		return (0);
1587c478bd9Sstevel@tonic-gate 	}
1597c478bd9Sstevel@tonic-gate 	return (1);
1607c478bd9Sstevel@tonic-gate }
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate /*
16356deab07SRod Evans  * Process any software capabilities.
1647c478bd9Sstevel@tonic-gate  */
16556deab07SRod Evans /* ARGSUSED0 */
16656deab07SRod Evans int
sfcap1_check(Syscapset * scapset,Xword val,Rej_desc * rej)16708278a5eSRod Evans sfcap1_check(Syscapset *scapset, Xword val, Rej_desc *rej)
16856deab07SRod Evans {
16956deab07SRod Evans #if	defined(_ELF64)
17056deab07SRod Evans 	/*
17156deab07SRod Evans 	 * A 64-bit executable that started the process can be restricted to a
17256deab07SRod Evans 	 * 32-bit address space.  A 64-bit dependency that is restricted to a
17356deab07SRod Evans 	 * 32-bit address space can not be loaded unless the executable has
17456deab07SRod Evans 	 * established this requirement.
17556deab07SRod Evans 	 */
17656deab07SRod Evans 	if ((val & SF1_SUNW_ADDR32) && ((rtld_flags2 & RT_FL2_ADDR32) == 0)) {
17708278a5eSRod Evans 		if (rej) {
17856deab07SRod Evans 			static Conv_cap_val_sf1_buf_t	cap_buf;
1794464de07SAli Bahrami 
18056deab07SRod Evans 			rej->rej_type = SGS_REJ_SFCAP_1;
18108278a5eSRod Evans 			rej->rej_str = conv_cap_val_sf1(SF1_SUNW_ADDR32,
18208278a5eSRod Evans 			    M_MACH, 0, &cap_buf);
18308278a5eSRod Evans 		}
18456deab07SRod Evans 		return (0);
1857c478bd9Sstevel@tonic-gate 	}
18656deab07SRod Evans #endif
18756deab07SRod Evans 	return (1);
1884464de07SAli Bahrami }
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate /*
19108278a5eSRod Evans  * Process any platform capability.
19208278a5eSRod Evans  */
19308278a5eSRod Evans int
platcap_check(Syscapset * scapset,const char * str,Rej_desc * rej)19408278a5eSRod Evans platcap_check(Syscapset *scapset, const char *str, Rej_desc *rej)
19508278a5eSRod Evans {
19608278a5eSRod Evans 	/*
19708278a5eSRod Evans 	 * If the platform name hasn't been set, try and obtain it.
19808278a5eSRod Evans 	 */
19908278a5eSRod Evans 	if ((scapset->sc_plat == NULL) &&
20008278a5eSRod Evans 	    (scapset->sc_platsz == 0))
20108278a5eSRod Evans 		platform_name(scapset);
20208278a5eSRod Evans 
20308278a5eSRod Evans 	if ((scapset->sc_plat == NULL) ||
20408278a5eSRod Evans 	    (str && strcmp(scapset->sc_plat, str))) {
20508278a5eSRod Evans 		if (rej) {
20608278a5eSRod Evans 			/*
20708278a5eSRod Evans 			 * Note, the platform name points to a string within an
20808278a5eSRod Evans 			 * objects string table, and if that object can't be
20908278a5eSRod Evans 			 * loaded, it will be unloaded and thus invalidate the
21008278a5eSRod Evans 			 * string.  Duplicate the string here for rejection
21108278a5eSRod Evans 			 * message inheritance.
21208278a5eSRod Evans 			 */
21308278a5eSRod Evans 			rej->rej_type = SGS_REJ_PLATCAP;
21408278a5eSRod Evans 			rej->rej_str = stravl_insert(str, 0, 0, 0);
21508278a5eSRod Evans 		}
21608278a5eSRod Evans 		return (0);
21708278a5eSRod Evans 	}
21808278a5eSRod Evans 	return (1);
21908278a5eSRod Evans }
22008278a5eSRod Evans 
22108278a5eSRod Evans /*
22208278a5eSRod Evans  * Process any machine capability.
22308278a5eSRod Evans  */
22408278a5eSRod Evans int
machcap_check(Syscapset * scapset,const char * str,Rej_desc * rej)22508278a5eSRod Evans machcap_check(Syscapset *scapset, const char *str, Rej_desc *rej)
22608278a5eSRod Evans {
22708278a5eSRod Evans 	/*
22808278a5eSRod Evans 	 * If the machine name hasn't been set, try and obtain it.
22908278a5eSRod Evans 	 */
23008278a5eSRod Evans 	if ((scapset->sc_mach == NULL) &&
23108278a5eSRod Evans 	    (scapset->sc_machsz == 0))
23208278a5eSRod Evans 		machine_name(scapset);
23308278a5eSRod Evans 
23408278a5eSRod Evans 	if ((scapset->sc_mach == NULL) ||
23508278a5eSRod Evans 	    (str && strcmp(scapset->sc_mach, str))) {
23608278a5eSRod Evans 		if (rej) {
23708278a5eSRod Evans 			/*
23808278a5eSRod Evans 			 * Note, the machine name points to a string within an
23908278a5eSRod Evans 			 * objects string table, and if that object can't be
24008278a5eSRod Evans 			 * loaded, it will be unloaded and thus invalidate the
24108278a5eSRod Evans 			 * string.  Duplicate the string here for rejection
24208278a5eSRod Evans 			 * message inheritance.
24308278a5eSRod Evans 			 */
24408278a5eSRod Evans 			rej->rej_type = SGS_REJ_MACHCAP;
24508278a5eSRod Evans 			rej->rej_str = stravl_insert(str, 0, 0, 0);
24608278a5eSRod Evans 		}
24708278a5eSRod Evans 		return (0);
24808278a5eSRod Evans 	}
24908278a5eSRod Evans 	return (1);
25008278a5eSRod Evans }
25108278a5eSRod Evans 
25208278a5eSRod Evans /*
25308278a5eSRod Evans  * Generic front-end to capabilities validation.
2547c478bd9Sstevel@tonic-gate  */
255cce0e03bSab196087 static int
cap_check(Cap * cptr,char * strs,int alt,Fdesc * fdp,Rej_desc * rej)25608278a5eSRod Evans cap_check(Cap *cptr, char *strs, int alt, Fdesc *fdp, Rej_desc *rej)
25708278a5eSRod Evans {
25808278a5eSRod Evans 	Syscapset	*scapset;
25908278a5eSRod Evans 	int		totplat, ivlplat, totmach, ivlmach;
26008278a5eSRod Evans 
26108278a5eSRod Evans 	/*
26208278a5eSRod Evans 	 * If the caller has no capabilities, then the object is valid.
26308278a5eSRod Evans 	 */
26408278a5eSRod Evans 	if (cptr == NULL)
26508278a5eSRod Evans 		return (1);
26608278a5eSRod Evans 
26708278a5eSRod Evans 	if (alt)
26808278a5eSRod Evans 		scapset = alt_scapset;
26908278a5eSRod Evans 	else
27008278a5eSRod Evans 		scapset = org_scapset;
27108278a5eSRod Evans 
27208278a5eSRod Evans 	totplat = ivlplat = totmach = ivlmach = 0;
27308278a5eSRod Evans 
27408278a5eSRod Evans 	while (cptr->c_tag != CA_SUNW_NULL) {
27508278a5eSRod Evans 		Xword	val = cptr->c_un.c_val;
27608278a5eSRod Evans 		char	*str;
27708278a5eSRod Evans 
27808278a5eSRod Evans 		switch (cptr->c_tag) {
27908278a5eSRod Evans 		case CA_SUNW_HW_1:
2804e12d685SRod Evans 			/*
2814e12d685SRod Evans 			 * Remove any historic values that should not be
2824e12d685SRod Evans 			 * involved with any validation.
2834e12d685SRod Evans 			 */
2844e12d685SRod Evans 			val &= ~AV_HW1_IGNORE;
2854e12d685SRod Evans 
28608278a5eSRod Evans 			if (hwcap1_check(scapset, val, rej) == 0)
28708278a5eSRod Evans 				return (0);
28808278a5eSRod Evans 			if (fdp)
28908278a5eSRod Evans 				fdp->fd_scapset.sc_hw_1 = val;
29008278a5eSRod Evans 			break;
29108278a5eSRod Evans 		case CA_SUNW_SF_1:
29208278a5eSRod Evans 			if (sfcap1_check(scapset, val, rej) == 0)
29308278a5eSRod Evans 				return (0);
29408278a5eSRod Evans 			if (fdp)
29508278a5eSRod Evans 				fdp->fd_scapset.sc_sf_1 = val;
29608278a5eSRod Evans 			break;
29708278a5eSRod Evans 		case CA_SUNW_HW_2:
29808278a5eSRod Evans 			if (hwcap2_check(scapset, val, rej) == 0)
29908278a5eSRod Evans 				return (0);
30008278a5eSRod Evans 			if (fdp)
30108278a5eSRod Evans 				fdp->fd_scapset.sc_hw_2 = val;
30208278a5eSRod Evans 			break;
30308278a5eSRod Evans 		case CA_SUNW_PLAT:
30408278a5eSRod Evans 			/*
30508278a5eSRod Evans 			 * A capabilities group can define multiple platform
30608278a5eSRod Evans 			 * names that are appropriate.  Only if all the names
30708278a5eSRod Evans 			 * are deemed invalid is the group determined
30808278a5eSRod Evans 			 * inappropriate.
30908278a5eSRod Evans 			 */
31008278a5eSRod Evans 			if (totplat == ivlplat) {
31108278a5eSRod Evans 				totplat++;
31208278a5eSRod Evans 
31308278a5eSRod Evans 				str = strs + val;
31408278a5eSRod Evans 
31508278a5eSRod Evans 				if (platcap_check(scapset, str, rej) == 0)
31608278a5eSRod Evans 					ivlplat++;
31708278a5eSRod Evans 				else if (fdp)
31808278a5eSRod Evans 					fdp->fd_scapset.sc_plat = str;
31908278a5eSRod Evans 			}
32008278a5eSRod Evans 			break;
32108278a5eSRod Evans 		case CA_SUNW_MACH:
32208278a5eSRod Evans 			/*
32308278a5eSRod Evans 			 * A capabilities group can define multiple machine
32408278a5eSRod Evans 			 * names that are appropriate.  Only if all the names
32508278a5eSRod Evans 			 * are deemed invalid is the group determined
32608278a5eSRod Evans 			 * inappropriate.
32708278a5eSRod Evans 			 */
32808278a5eSRod Evans 			if (totmach == ivlmach) {
32908278a5eSRod Evans 				totmach++;
33008278a5eSRod Evans 
33108278a5eSRod Evans 				str = strs + val;
33208278a5eSRod Evans 
33308278a5eSRod Evans 				if (machcap_check(scapset, str, rej) == 0)
33408278a5eSRod Evans 					ivlmach++;
33508278a5eSRod Evans 				else if (fdp)
33608278a5eSRod Evans 					fdp->fd_scapset.sc_mach = str;
33708278a5eSRod Evans 			}
33808278a5eSRod Evans 			break;
33908278a5eSRod Evans 		case CA_SUNW_ID:
34008278a5eSRod Evans 			/*
34108278a5eSRod Evans 			 * Capabilities identifiers provide for diagnostics,
34208278a5eSRod Evans 			 * but are not attributes that must be compared with
34308278a5eSRod Evans 			 * the system.  They are ignored.
34408278a5eSRod Evans 			 */
34508278a5eSRod Evans 			break;
34608278a5eSRod Evans 		default:
34708278a5eSRod Evans 			rej->rej_type = SGS_REJ_UNKCAP;
34808278a5eSRod Evans 			rej->rej_info = cptr->c_tag;
34908278a5eSRod Evans 			return (0);
35008278a5eSRod Evans 		}
35108278a5eSRod Evans 		cptr++;
35208278a5eSRod Evans 	}
35308278a5eSRod Evans 
35408278a5eSRod Evans 	/*
35508278a5eSRod Evans 	 * If any platform names, or machine names were found, and all were
35608278a5eSRod Evans 	 * invalid, indicate that the object is inappropriate.
35708278a5eSRod Evans 	 */
35808278a5eSRod Evans 	if ((totplat && (totplat == ivlplat)) ||
35908278a5eSRod Evans 	    (totmach && (totmach == ivlmach)))
36008278a5eSRod Evans 		return (0);
36108278a5eSRod Evans 
36208278a5eSRod Evans 	return (1);
36308278a5eSRod Evans }
36408278a5eSRod Evans 
36508278a5eSRod Evans #define	HWAVL_RECORDED(n)	pnavl_recorded(&capavl, n, NULL, NULL)
36608278a5eSRod Evans 
36708278a5eSRod Evans /*
36808278a5eSRod Evans  * Determine whether a link-map should use alternative system capabilities.
36908278a5eSRod Evans  */
37008278a5eSRod Evans static void
cap_check_lmp_init(Rt_map * lmp)37108278a5eSRod Evans cap_check_lmp_init(Rt_map *lmp)
37208278a5eSRod Evans {
37308278a5eSRod Evans 	int	alt = 0;
37408278a5eSRod Evans 
37508278a5eSRod Evans 	/*
37608278a5eSRod Evans 	 * If an alternative set of system capabilities have been established,
37708278a5eSRod Evans 	 * and only specific files should use these alternative system
37808278a5eSRod Evans 	 * capabilities, determine whether this file is one of those specified.
37908278a5eSRod Evans 	 */
38008278a5eSRod Evans 	if (capavl) {
38108278a5eSRod Evans 		const char	*file;
38208278a5eSRod Evans 
38308278a5eSRod Evans 		/*
38408278a5eSRod Evans 		 * The simplest way to reference a file is to use its file name
38508278a5eSRod Evans 		 * (soname), however try all of the names that this file is
38608278a5eSRod Evans 		 * known by.
38708278a5eSRod Evans 		 */
38808278a5eSRod Evans 		if ((file = strrchr(NAME(lmp), '/')) != NULL)
38908278a5eSRod Evans 			file++;
39008278a5eSRod Evans 		else
39108278a5eSRod Evans 			file = NULL;
39208278a5eSRod Evans 
39308278a5eSRod Evans 		if ((file && (HWAVL_RECORDED(file) != 0)) ||
39408278a5eSRod Evans 		    (HWAVL_RECORDED(NAME(lmp)) != 0) ||
39508278a5eSRod Evans 		    ((PATHNAME(lmp) != NAME(lmp)) &&
39608278a5eSRod Evans 		    (HWAVL_RECORDED(PATHNAME(lmp)) != 0)))
39708278a5eSRod Evans 			alt = 1;
39808278a5eSRod Evans 
39908278a5eSRod Evans 		if (alt == 0) {
40008278a5eSRod Evans 			Aliste		idx;
40108278a5eSRod Evans 			const char	*cp;
40208278a5eSRod Evans 
40308278a5eSRod Evans 			for (APLIST_TRAVERSE(ALIAS(lmp), idx, cp)) {
40408278a5eSRod Evans 				if ((alt = HWAVL_RECORDED(cp)) != 0)
40508278a5eSRod Evans 					break;
40608278a5eSRod Evans 			}
40708278a5eSRod Evans 		}
40808278a5eSRod Evans 	}
40908278a5eSRod Evans 
41008278a5eSRod Evans 	/*
41108278a5eSRod Evans 	 * Indicate if this link-map should use alternative system capabilities,
41208278a5eSRod Evans 	 * and that the alternative system capabilities check has been carried
41308278a5eSRod Evans 	 * out.
41408278a5eSRod Evans 	 */
41508278a5eSRod Evans 	if ((org_scapset != alt_scapset) && ((capavl == NULL) || alt))
41608278a5eSRod Evans 		FLAGS1(lmp) |= FL1_RT_ALTCAP;
41708278a5eSRod Evans 	FLAGS1(lmp) |= FL1_RT_ALTCHECK;
41808278a5eSRod Evans }
41908278a5eSRod Evans 
42008278a5eSRod Evans /*
42108278a5eSRod Evans  * Validate the capabilities requirements of a link-map.
42208278a5eSRod Evans  *
42308278a5eSRod Evans  * This routine is called for main, where a link-map is constructed from the
42408278a5eSRod Evans  * mappings returned from exec(), and for any symbol capabilities comparisons.
42508278a5eSRod Evans  */
42608278a5eSRod Evans int
cap_check_lmp(Rt_map * lmp,Rej_desc * rej)42708278a5eSRod Evans cap_check_lmp(Rt_map *lmp, Rej_desc *rej)
42808278a5eSRod Evans {
42908278a5eSRod Evans 	if ((FLAGS1(lmp) & FL1_RT_ALTCHECK) == 0)
43008278a5eSRod Evans 		cap_check_lmp_init(lmp);
43108278a5eSRod Evans 
43208278a5eSRod Evans 	return (cap_check(CAP(lmp), STRTAB(lmp),
43308278a5eSRod Evans 	    (FLAGS1(lmp) & FL1_RT_ALTCAP), NULL, rej));
43408278a5eSRod Evans }
43508278a5eSRod Evans 
43608278a5eSRod Evans /*
43708278a5eSRod Evans  * Validate the capabilities requirements of a file under inspection.
43808278a5eSRod Evans  * This file is still under the early stages of loading, and has no link-map
43908278a5eSRod Evans  * yet.  The file must have an object capabilities definition (PT_SUNWCAP), to
44008278a5eSRod Evans  * have gotten us here.  The logic here is the same as cap_check_lmp().
44108278a5eSRod Evans  */
44208278a5eSRod Evans int
cap_check_fdesc(Fdesc * fdp,Cap * cptr,char * strs,Rej_desc * rej)44308278a5eSRod Evans cap_check_fdesc(Fdesc *fdp, Cap *cptr, char *strs, Rej_desc *rej)
44408278a5eSRod Evans {
44508278a5eSRod Evans 	int	alt = 0;
44608278a5eSRod Evans 
44708278a5eSRod Evans 	/*
44808278a5eSRod Evans 	 * If an alternative set of system capabilities have been established,
44908278a5eSRod Evans 	 * and only specific files should use these alternative system
45008278a5eSRod Evans 	 * capabilities, determine whether this file is one of those specified.
45108278a5eSRod Evans 	 */
45208278a5eSRod Evans 	if (capavl) {
45308278a5eSRod Evans 		const char	*file;
45408278a5eSRod Evans 
45508278a5eSRod Evans 		/*
45608278a5eSRod Evans 		 * The simplest way to reference a file is to use its file name
45708278a5eSRod Evans 		 * (soname), however try all of the names that this file is
45808278a5eSRod Evans 		 * known by.
45908278a5eSRod Evans 		 */
4601c1abfbcSRod Evans 		if (fdp->fd_oname &&
4611c1abfbcSRod Evans 		    ((file = strrchr(fdp->fd_oname, '/')) != NULL))
46208278a5eSRod Evans 			file++;
46308278a5eSRod Evans 		else
46408278a5eSRod Evans 			file = NULL;
46508278a5eSRod Evans 
46608278a5eSRod Evans 		if ((file && (HWAVL_RECORDED(file) != 0)) ||
46708278a5eSRod Evans 		    (fdp->fd_oname && (HWAVL_RECORDED(fdp->fd_oname) != 0)) ||
46808278a5eSRod Evans 		    (fdp->fd_nname && (HWAVL_RECORDED(fdp->fd_nname) != 0)) ||
46908278a5eSRod Evans 		    (fdp->fd_pname && (fdp->fd_pname != fdp->fd_nname) &&
47008278a5eSRod Evans 		    (HWAVL_RECORDED(fdp->fd_pname) != 0)))
47108278a5eSRod Evans 			alt = 1;
47208278a5eSRod Evans 	}
47308278a5eSRod Evans 
47408278a5eSRod Evans 	/*
47508278a5eSRod Evans 	 * Indicate if this file descriptor should use alternative system
47608278a5eSRod Evans 	 * capabilities, and that the alternative system capabilities check has
47708278a5eSRod Evans 	 * been carried out.
47808278a5eSRod Evans 	 */
47908278a5eSRod Evans 	if ((org_scapset != alt_scapset) && ((capavl == NULL) || alt))
48008278a5eSRod Evans 		fdp->fd_flags |= FLG_FD_ALTCAP;
48108278a5eSRod Evans 	fdp->fd_flags |= FLG_FD_ALTCHECK;
48208278a5eSRod Evans 
48308278a5eSRod Evans 	/*
48408278a5eSRod Evans 	 * Verify that the required capabilities are supported by the reference.
48508278a5eSRod Evans 	 */
48608278a5eSRod Evans 	return (cap_check(cptr, strs, (fdp->fd_flags & FLG_FD_ALTCAP),
48708278a5eSRod Evans 	    fdp, rej));
48808278a5eSRod Evans }
48908278a5eSRod Evans 
49008278a5eSRod Evans /*
49108278a5eSRod Evans  * Free a file descriptor list.  As part of building this list, the original
49208278a5eSRod Evans  * names for each capabilities candidate were duplicated for use in later
49308278a5eSRod Evans  * diagnostics.  These names need to be freed.
49408278a5eSRod Evans  */
49508278a5eSRod Evans void
free_fd(Alist * fdalp)49608278a5eSRod Evans free_fd(Alist *fdalp)
49708278a5eSRod Evans {
49808278a5eSRod Evans 	if (fdalp) {
49908278a5eSRod Evans 		Aliste	idx;
50008278a5eSRod Evans 		Fdesc	*fdp;
50108278a5eSRod Evans 
50208278a5eSRod Evans 		for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
50308278a5eSRod Evans 			if (fdp->fd_oname)
50408278a5eSRod Evans 				free((void *)fdp->fd_oname);
50508278a5eSRod Evans 		}
50608278a5eSRod Evans 		free(fdalp);
50708278a5eSRod Evans 	}
50808278a5eSRod Evans }
50908278a5eSRod Evans 
51008278a5eSRod Evans /*
51108278a5eSRod Evans  * When $CAPABILITY (or $HWCAP) is used to represent dependencies, take the
51208278a5eSRod Evans  * associated directory and analyze all the files it contains.
51308278a5eSRod Evans  */
51408278a5eSRod Evans static int
cap_dir(Alist ** fdalpp,Lm_list * lml,const char * dname,Rt_map * clmp,uint_t flags,Rej_desc * rej,int * in_nfavl)51508278a5eSRod Evans cap_dir(Alist **fdalpp, Lm_list *lml, const char *dname, Rt_map *clmp,
5169aa23310Srie     uint_t flags, Rej_desc *rej, int *in_nfavl)
5177c478bd9Sstevel@tonic-gate {
5187c478bd9Sstevel@tonic-gate 	char		path[PATH_MAX], *dst;
5197c478bd9Sstevel@tonic-gate 	const char	*src;
5207c478bd9Sstevel@tonic-gate 	DIR		*dir;
5217c478bd9Sstevel@tonic-gate 	struct dirent	*dirent;
522cce0e03bSab196087 	Alist		*fdalp = NULL;
5231c1abfbcSRod Evans 	Aliste		idx;
5241c1abfbcSRod Evans 	Fdesc		*fdp;
5257c478bd9Sstevel@tonic-gate 	int		error = 0;
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 	/*
5287c478bd9Sstevel@tonic-gate 	 * Access the directory in preparation for reading its entries.  If
5297c478bd9Sstevel@tonic-gate 	 * successful, establish the initial pathname.
5307c478bd9Sstevel@tonic-gate 	 */
53156deab07SRod Evans 	if ((dir = opendir(dname)) == NULL) {
5327c478bd9Sstevel@tonic-gate 		Rej_desc	_rej = { 0 };
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate 		_rej.rej_type = SGS_REJ_STR;
53556deab07SRod Evans 		_rej.rej_name = dname;
5367c478bd9Sstevel@tonic-gate 		_rej.rej_str = strerror(errno);
537ba2be530Sab196087 		DBG_CALL(Dbg_file_rejected(lml, &_rej, M_MACH));
53831fdd7caSab196087 		rejection_inherit(rej, &_rej);
5397c478bd9Sstevel@tonic-gate 		return (0);
5407c478bd9Sstevel@tonic-gate 	}
5417c478bd9Sstevel@tonic-gate 
54256deab07SRod Evans 	for (dst = path, src = dname; *src; dst++, src++)
5437c478bd9Sstevel@tonic-gate 		*dst = *src;
5447c478bd9Sstevel@tonic-gate 	*dst++ = '/';
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 	/*
5477c478bd9Sstevel@tonic-gate 	 * Read each entry from the directory and determine whether it is a
5487c478bd9Sstevel@tonic-gate 	 * valid ELF file.
5497c478bd9Sstevel@tonic-gate 	 */
5507c478bd9Sstevel@tonic-gate 	while ((dirent = readdir(dir)) != NULL) {
55156deab07SRod Evans 		const char	*file = dirent->d_name;
5527247f888Srie 		char		*_dst;
55356deab07SRod Evans 		Fdesc		fd = { 0 };
5547c478bd9Sstevel@tonic-gate 		Rej_desc	_rej = { 0 };
55556deab07SRod Evans 		Pdesc		pd = { 0 };
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 		/*
5587c478bd9Sstevel@tonic-gate 		 * Ignore "." and ".." entries.
5597c478bd9Sstevel@tonic-gate 		 */
5607c478bd9Sstevel@tonic-gate 		if ((file[0] == '.') && ((file[1] == '\0') ||
5617c478bd9Sstevel@tonic-gate 		    ((file[1] == '.') && (file[2] == '\0'))))
5627c478bd9Sstevel@tonic-gate 			continue;
5637c478bd9Sstevel@tonic-gate 
5647c478bd9Sstevel@tonic-gate 		/*
56556deab07SRod Evans 		 * Complete the full pathname.
5667c478bd9Sstevel@tonic-gate 		 */
5677c478bd9Sstevel@tonic-gate 		for (_dst = dst, src = file, file = dst; *src; _dst++, src++)
5687c478bd9Sstevel@tonic-gate 			*_dst = *src;
5697c478bd9Sstevel@tonic-gate 		*_dst = '\0';
5707c478bd9Sstevel@tonic-gate 
57156deab07SRod Evans 		/*
57256deab07SRod Evans 		 * Trace the inspection of this file, and determine any
57356deab07SRod Evans 		 * auditor substitution.
57456deab07SRod Evans 		 */
57556deab07SRod Evans 		pd.pd_pname = path;
57656deab07SRod Evans 		pd.pd_flags = PD_FLG_PNSLASH;
5777c478bd9Sstevel@tonic-gate 
57856deab07SRod Evans 		if (load_trace(lml, &pd, clmp, &fd) == NULL)
5798521e5e6Srie 			continue;
5808521e5e6Srie 
5818521e5e6Srie 		/*
5828521e5e6Srie 		 * Note, all directory entries are processed by find_path(),
5838521e5e6Srie 		 * even entries that are directories themselves.  This single
5848521e5e6Srie 		 * point for control keeps the number of stat()'s down, and
5858521e5e6Srie 		 * provides a single point for error diagnostics.
5868521e5e6Srie 		 */
58756deab07SRod Evans 		if (find_path(lml, clmp, flags, &fd, &_rej, in_nfavl) == 0) {
58831fdd7caSab196087 			rejection_inherit(rej, &_rej);
5898521e5e6Srie 			continue;
5908521e5e6Srie 		}
5918521e5e6Srie 
59208278a5eSRod Evans 		DBG_CALL(Dbg_cap_candidate(lml, fd.fd_nname));
5937c478bd9Sstevel@tonic-gate 
5947c478bd9Sstevel@tonic-gate 		/*
59508278a5eSRod Evans 		 * If this object has already been loaded, save the capabilities
59608278a5eSRod Evans 		 * for later sorting.  Otherwise we have a new candidate.
5977c478bd9Sstevel@tonic-gate 		 */
59856deab07SRod Evans 		if (fd.fd_lmp)
59908278a5eSRod Evans 			fd.fd_scapset = CAPSET(fd.fd_lmp);
6001c1abfbcSRod Evans 		fd.fd_lml = lml;
601c174926fSrie 
60208278a5eSRod Evans 		/*
60308278a5eSRod Evans 		 * Duplicate the original name, as this may be required for
60408278a5eSRod Evans 		 * later diagnostics.  Keep a copy of the file descriptor for
60508278a5eSRod Evans 		 * analysis once all capabilities candidates have been
60608278a5eSRod Evans 		 * determined.
60708278a5eSRod Evans 		 */
60808278a5eSRod Evans 		if (((fd.fd_oname = strdup(fd.fd_oname)) == NULL) ||
60908278a5eSRod Evans 		    (alist_append(&fdalp, &fd, sizeof (Fdesc),
61008278a5eSRod Evans 		    AL_CNT_CAP) == NULL)) {
6117c478bd9Sstevel@tonic-gate 			error = 1;
6127c478bd9Sstevel@tonic-gate 			break;
6137c478bd9Sstevel@tonic-gate 		}
6147c478bd9Sstevel@tonic-gate 	}
6157c478bd9Sstevel@tonic-gate 	(void) closedir(dir);
6167c478bd9Sstevel@tonic-gate 
6177c478bd9Sstevel@tonic-gate 	/*
6187c478bd9Sstevel@tonic-gate 	 * If no objects have been found, we're done.  Also, if an allocation
6197c478bd9Sstevel@tonic-gate 	 * error occurred while processing any object, remove any objects that
6207c478bd9Sstevel@tonic-gate 	 * had already been added to the list and return.
6217c478bd9Sstevel@tonic-gate 	 */
622cce0e03bSab196087 	if ((fdalp == NULL) || error) {
62356deab07SRod Evans 		if (fdalp)
62408278a5eSRod Evans 			free_fd(fdalp);
6257c478bd9Sstevel@tonic-gate 		return (0);
6267c478bd9Sstevel@tonic-gate 	}
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate 	/*
6297c478bd9Sstevel@tonic-gate 	 * Having processed and retained all candidates from this directory,
6307c478bd9Sstevel@tonic-gate 	 * sort them, based on the precedence of their hardware capabilities.
6317c478bd9Sstevel@tonic-gate 	 */
632cce0e03bSab196087 	qsort(fdalp->al_data, fdalp->al_nitems, fdalp->al_size, compare);
6337c478bd9Sstevel@tonic-gate 
6341c1abfbcSRod Evans 	/*
6351c1abfbcSRod Evans 	 * If any objects were found to have the same capabilities, then these
6361c1abfbcSRod Evans 	 * objects must be rejected, as we can't tell which object is more
6371c1abfbcSRod Evans 	 * appropriate.
6381c1abfbcSRod Evans 	 */
6391c1abfbcSRod Evans 	for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
6401c1abfbcSRod Evans 		if (fdp->fd_flags & FLG_FD_IGNORE)
6411c1abfbcSRod Evans 			alist_delete(fdalp, &idx);
6421c1abfbcSRod Evans 	}
6431c1abfbcSRod Evans 
6441c1abfbcSRod Evans 	if (fdalp->al_nitems == 0) {
6451c1abfbcSRod Evans 		free_fd(fdalp);
6461c1abfbcSRod Evans 		return (0);
6471c1abfbcSRod Evans 	}
6481c1abfbcSRod Evans 
6497c478bd9Sstevel@tonic-gate 	*fdalpp = fdalp;
6507c478bd9Sstevel@tonic-gate 	return (1);
6517c478bd9Sstevel@tonic-gate }
6527c478bd9Sstevel@tonic-gate 
65356deab07SRod Evans int
cap_filtees(Alist ** alpp,Aliste oidx,const char * dir,Aliste nlmco,Rt_map * flmp,Rt_map * clmp,const char * ref,int mode,uint_t flags,int * in_nfavl)65408278a5eSRod Evans cap_filtees(Alist **alpp, Aliste oidx, const char *dir, Aliste nlmco,
6552020b2b6SRod Evans     Rt_map *flmp, Rt_map *clmp, const char *ref, int mode, uint_t flags,
6562020b2b6SRod Evans     int *in_nfavl)
6577c478bd9Sstevel@tonic-gate {
658cce0e03bSab196087 	Alist		*fdalp = NULL;
659cce0e03bSab196087 	Aliste		idx;
6607c478bd9Sstevel@tonic-gate 	Fdesc		*fdp;
6617c478bd9Sstevel@tonic-gate 	Lm_list		*lml = LIST(flmp);
6627c478bd9Sstevel@tonic-gate 	int		unused = 0;
6637c478bd9Sstevel@tonic-gate 	Rej_desc	rej = { 0 };
6647c478bd9Sstevel@tonic-gate 
66508278a5eSRod Evans 	if (cap_dir(&fdalp, lml, dir, flmp, flags, &rej, in_nfavl) == 0)
6667c478bd9Sstevel@tonic-gate 		return (0);
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate 	/*
6697c478bd9Sstevel@tonic-gate 	 * Now complete the mapping of each of the ordered objects, adding
67056deab07SRod Evans 	 * each object to a new pathname descriptor.
6717c478bd9Sstevel@tonic-gate 	 */
672cce0e03bSab196087 	for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
6737c478bd9Sstevel@tonic-gate 		Rt_map	*nlmp;
674481bba9eSRod Evans 		Grp_hdl	*ghp = NULL;
67556deab07SRod Evans 		Pdesc	*pdp;
676fb1354edSrie 		int	audit = 0;
6777c478bd9Sstevel@tonic-gate 
67856deab07SRod Evans 		if (unused)
6797c478bd9Sstevel@tonic-gate 			continue;
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate 		/*
6827c478bd9Sstevel@tonic-gate 		 * Complete mapping the file, obtaining a handle, and continue
6837c478bd9Sstevel@tonic-gate 		 * to analyze the object, establishing dependencies and
6847c478bd9Sstevel@tonic-gate 		 * relocating.  Remove the file descriptor at this point, as it
6857c478bd9Sstevel@tonic-gate 		 * is no longer required.
6867c478bd9Sstevel@tonic-gate 		 */
6875aefb655Srie 		DBG_CALL(Dbg_file_filtee(lml, NAME(flmp), fdp->fd_nname, 0));
6887c478bd9Sstevel@tonic-gate 
68956deab07SRod Evans 		nlmp = load_path(lml, nlmco, flmp, mode,
6902017c965SRod Evans 		    (flags | FLG_RT_PUBHDL), &ghp, fdp, &rej, in_nfavl);
691481bba9eSRod Evans 		if (nlmp == NULL)
69202ca3e02Srie 			continue;
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate 		/*
69556deab07SRod Evans 		 * Create a new pathname descriptor to represent this filtee,
69656deab07SRod Evans 		 * and insert this descriptor in the Alist following the
69756deab07SRod Evans 		 * hardware descriptor that seeded this processing.
6987c478bd9Sstevel@tonic-gate 		 */
69956deab07SRod Evans 		if ((pdp = alist_insert(alpp, 0, sizeof (Pdesc),
70056deab07SRod Evans 		    AL_CNT_FILTEES, ++oidx)) == NULL) {
70156deab07SRod Evans 			if (ghp)
702481bba9eSRod Evans 				remove_lmc(lml, flmp, nlmco, NAME(nlmp));
703fb1354edSrie 			return (0);
7047c478bd9Sstevel@tonic-gate 		}
7057c478bd9Sstevel@tonic-gate 
70656deab07SRod Evans 		pdp->pd_pname = NAME(nlmp);
70756deab07SRod Evans 		pdp->pd_plen = strlen(NAME(nlmp));
7087c478bd9Sstevel@tonic-gate 
7097c478bd9Sstevel@tonic-gate 		/*
710fb1354edSrie 		 * Establish the filter handle to prevent any recursion.
711fb1354edSrie 		 */
712fb1354edSrie 		if (nlmp && ghp) {
713fb1354edSrie 			ghp->gh_flags |= GPH_FILTEE;
71456deab07SRod Evans 			pdp->pd_info = (void *)ghp;
715fb1354edSrie 		}
716fb1354edSrie 
717fb1354edSrie 		/*
718fb1354edSrie 		 * Audit the filter/filtee established.  A return of 0
719fb1354edSrie 		 * indicates the auditor wishes to ignore this filtee.
720fb1354edSrie 		 */
721fb1354edSrie 		if (nlmp && (lml->lm_tflags | FLAGS1(flmp)) &
722fb1354edSrie 		    LML_TFLG_AUD_OBJFILTER) {
723fb1354edSrie 			if (audit_objfilter(flmp, ref, nlmp, 0) == 0) {
724fb1354edSrie 				audit = 1;
725481bba9eSRod Evans 				nlmp = NULL;
726fb1354edSrie 			}
727fb1354edSrie 		}
728fb1354edSrie 
729fb1354edSrie 		/*
730fb1354edSrie 		 * Finish processing the objects associated with this request.
731fb1354edSrie 		 */
73256deab07SRod Evans 		if (nlmp && ghp && (((nlmp = analyze_lmc(lml, nlmco, nlmp,
7332020b2b6SRod Evans 		    clmp, in_nfavl)) == NULL) ||
7349aa23310Srie 		    (relocate_lmc(lml, nlmco, flmp, nlmp, in_nfavl) == 0)))
73556deab07SRod Evans 			nlmp = NULL;
736fb1354edSrie 
737fb1354edSrie 		/*
73802ca3e02Srie 		 * If the filtee has been successfully processed, then create
73902ca3e02Srie 		 * an association between the filter and the filtee.  This
740fb1354edSrie 		 * association provides sufficient information to tear down the
741fb1354edSrie 		 * filter and filtee if necessary.
742fb1354edSrie 		 */
7438af2c5b9Srie 		DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD));
7442017c965SRod Evans 		if (nlmp && ghp &&
7452017c965SRod Evans 		    (hdl_add(ghp, flmp, GPD_FILTER, NULL) == NULL))
74656deab07SRod Evans 			nlmp = NULL;
747fb1354edSrie 
748fb1354edSrie 		/*
7497c478bd9Sstevel@tonic-gate 		 * If this object is marked an end-filtee, we're done.
7507c478bd9Sstevel@tonic-gate 		 */
751fb1354edSrie 		if (nlmp && ghp && (FLAGS1(nlmp) & FL1_RT_ENDFILTE))
7527c478bd9Sstevel@tonic-gate 			unused = 1;
753fb1354edSrie 
754fb1354edSrie 		/*
75502ca3e02Srie 		 * If this filtee loading has failed, generate a diagnostic.
75656deab07SRod Evans 		 * Null out the path name descriptor entry, and continue the
75756deab07SRod Evans 		 * search.
758fb1354edSrie 		 */
75956deab07SRod Evans 		if (nlmp == NULL) {
76056deab07SRod Evans 			DBG_CALL(Dbg_file_filtee(lml, 0, pdp->pd_pname, audit));
76156deab07SRod Evans 
76202ca3e02Srie 			/*
76302ca3e02Srie 			 * If attempting to load this filtee required a new
76402ca3e02Srie 			 * link-map control list to which this request has
76502ca3e02Srie 			 * added objects, then remove all the objects that
76602ca3e02Srie 			 * have been associated to this request.
76702ca3e02Srie 			 */
768481bba9eSRod Evans 			if (nlmco != ALIST_OFF_DATA)
769481bba9eSRod Evans 				remove_lmc(lml, flmp, nlmco, pdp->pd_pname);
77002ca3e02Srie 
77156deab07SRod Evans 			pdp->pd_plen = 0;
772481bba9eSRod Evans 			pdp->pd_info = NULL;
773fb1354edSrie 		}
7747c478bd9Sstevel@tonic-gate 	}
7757c478bd9Sstevel@tonic-gate 
77608278a5eSRod Evans 	free_fd(fdalp);
77756deab07SRod Evans 	return (1);
7787c478bd9Sstevel@tonic-gate }
7797c478bd9Sstevel@tonic-gate 
7807c478bd9Sstevel@tonic-gate /*
78108278a5eSRod Evans  * Load an individual capabilities object.
7827c478bd9Sstevel@tonic-gate  */
7837c478bd9Sstevel@tonic-gate Rt_map *
load_cap(Lm_list * lml,Aliste lmco,const char * dir,Rt_map * clmp,uint_t mode,uint_t flags,Grp_hdl ** hdl,Rej_desc * rej,int * in_nfavl)78408278a5eSRod Evans load_cap(Lm_list *lml, Aliste lmco, const char *dir, Rt_map *clmp,
7859aa23310Srie     uint_t mode, uint_t flags, Grp_hdl **hdl, Rej_desc *rej, int *in_nfavl)
7867c478bd9Sstevel@tonic-gate {
787cce0e03bSab196087 	Alist	*fdalp = NULL;
788cce0e03bSab196087 	Aliste	idx;
7897c478bd9Sstevel@tonic-gate 	Fdesc	*fdp;
7907c478bd9Sstevel@tonic-gate 	int	found = 0;
791481bba9eSRod Evans 	Rt_map	*lmp = NULL;
7927c478bd9Sstevel@tonic-gate 
7937c478bd9Sstevel@tonic-gate 	/*
79456deab07SRod Evans 	 * Obtain the sorted list of hardware capabilities objects available.
7957c478bd9Sstevel@tonic-gate 	 */
79608278a5eSRod Evans 	if (cap_dir(&fdalp, lml, dir, clmp, flags, rej, in_nfavl) == 0)
79756deab07SRod Evans 		return (NULL);
7987c478bd9Sstevel@tonic-gate 
7997c478bd9Sstevel@tonic-gate 	/*
8007c478bd9Sstevel@tonic-gate 	 * From the list of hardware capability objects, use the first and
8017c478bd9Sstevel@tonic-gate 	 * discard the rest.
8027c478bd9Sstevel@tonic-gate 	 */
803cce0e03bSab196087 	for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
80456deab07SRod Evans 		Fdesc	fd = *fdp;
8057c478bd9Sstevel@tonic-gate 
80656deab07SRod Evans 		if ((found == 0) && ((lmp = load_path(lml, lmco, clmp, mode,
807481bba9eSRod Evans 		    flags, hdl, &fd, rej, in_nfavl)) != NULL))
80856deab07SRod Evans 			found++;
8097c478bd9Sstevel@tonic-gate 	}
8107c478bd9Sstevel@tonic-gate 
81108278a5eSRod Evans 	free_fd(fdalp);
8127c478bd9Sstevel@tonic-gate 	return (lmp);
8137c478bd9Sstevel@tonic-gate }
81408278a5eSRod Evans 
81508278a5eSRod Evans /*
81608278a5eSRod Evans  * Use a case insensitive string match when looking up capability mask
81708278a5eSRod Evans  * values by name, and omit the AV_ prefix.
81808278a5eSRod Evans  */
81908278a5eSRod Evans #define	ELFCAP_STYLE	ELFCAP_STYLE_LC | ELFCAP_STYLE_F_ICMP
82008278a5eSRod Evans 
82108278a5eSRod Evans /*
82208278a5eSRod Evans  * To aid in the development and testing of capabilities, an alternative system
82308278a5eSRod Evans  * capabilities group can be specified.  This alternative set is initialized
82408278a5eSRod Evans  * from the system capabilities that are normally used to validate all object
82508278a5eSRod Evans  * loading.  However, the user can disable, enable or override flags within
82608278a5eSRod Evans  * this alternative set, and thus affect object loading.
82708278a5eSRod Evans  *
82808278a5eSRod Evans  * This technique is usually combined with defining the family of objects
82908278a5eSRod Evans  * that should be compared against this alternative set.  Without defining the
83008278a5eSRod Evans  * family of objects, all objects loaded by ld.so.1 are validated against the
83108278a5eSRod Evans  * alternative set.  This can prevent the loading of critical system objects
83208278a5eSRod Evans  * like libc, and thus prevent process execution.
83308278a5eSRod Evans  */
83408278a5eSRod Evans typedef enum {
83508278a5eSRod Evans 	CAP_OVERRIDE =	0,		/* override existing capabilities */
83608278a5eSRod Evans 	CAP_ENABLE =	1,		/* enable capabilities */
83708278a5eSRod Evans 	CAP_DISABLE =	2		/* disable capabilities */
83808278a5eSRod Evans } cap_mode;
83908278a5eSRod Evans 
84008278a5eSRod Evans static struct {
84108278a5eSRod Evans 	elfcap_mask_t	cs_val[3];	/* value settings, and indicator for */
84208278a5eSRod Evans 	int		cs_set[3];	/*	OVERRIDE, ENABLE and DISABLE */
84308278a5eSRod Evans 	elfcap_mask_t	*cs_aval;	/* alternative variable for final */
84408278a5eSRod Evans 					/*	update */
84508278a5eSRod Evans } cap_settings[3] = {
84608278a5eSRod Evans 	{ { 0, 0, 0 }, { 0, 0, 0 }, NULL },		/* CA_SUNW_HW_1 */
84708278a5eSRod Evans 	{ { 0, 0, 0 }, { 0, 0, 0 }, NULL },		/* CA_SUNW_SF_1 */
84808278a5eSRod Evans 	{ { 0, 0, 0 }, { 0, 0, 0 }, NULL }		/* CA_SUNW_HW_2 */
84908278a5eSRod Evans };
85008278a5eSRod Evans 
85108278a5eSRod Evans static int
cap_modify(Xword tag,const char * str)85208278a5eSRod Evans cap_modify(Xword tag, const char *str)
85308278a5eSRod Evans {
85408278a5eSRod Evans 	char		*caps, *ptr, *next;
85508278a5eSRod Evans 	cap_mode	mode = CAP_OVERRIDE;
85608278a5eSRod Evans 	Xword		ndx;
85708278a5eSRod Evans 
85808278a5eSRod Evans 	if ((caps = strdup(str)) == NULL)
85908278a5eSRod Evans 		return (0);
86008278a5eSRod Evans 
861cc4ec439SRichard Lowe 	for (ptr = strtok_r(caps, MSG_ORIG(MSG_CAP_DELIMIT), &next);
862cc4ec439SRichard Lowe 	    ptr != NULL;
863cc4ec439SRichard Lowe 	    ptr = strtok_r(NULL, MSG_ORIG(MSG_CAP_DELIMIT), &next)) {
86408278a5eSRod Evans 		Xword		val = 0;
86508278a5eSRod Evans 
86608278a5eSRod Evans 		/*
86708278a5eSRod Evans 		 * Determine whether this token should be enabled (+),
86808278a5eSRod Evans 		 * disabled (-), or override any existing settings.
86908278a5eSRod Evans 		 */
87008278a5eSRod Evans 		if (*ptr == '+') {
87108278a5eSRod Evans 			mode = CAP_ENABLE;
87208278a5eSRod Evans 			ptr++;
87308278a5eSRod Evans 		} else if (*ptr == '-') {
87408278a5eSRod Evans 			mode = CAP_DISABLE;
87508278a5eSRod Evans 			ptr++;
87608278a5eSRod Evans 		}
87708278a5eSRod Evans 
87808278a5eSRod Evans 		/*
87908278a5eSRod Evans 		 * Process the capabilities as directed by the calling tag.
88008278a5eSRod Evans 		 */
88108278a5eSRod Evans 		switch (tag) {
88208278a5eSRod Evans 		case CA_SUNW_HW_1:
88308278a5eSRod Evans 			/*
88408278a5eSRod Evans 			 * Determine whether the capabilities string matches
88508278a5eSRod Evans 			 * a known hardware capability mask.  Note, the caller
88608278a5eSRod Evans 			 * indicates that these are hardware capabilities by
88708278a5eSRod Evans 			 * passing in the CA_SUNW_HW_1 tag.  However, the
88808278a5eSRod Evans 			 * tokens could be CA_SUNW_HW_1 or CA_SUNW_HW_2.
88908278a5eSRod Evans 			 */
89008278a5eSRod Evans 			if ((val = (Xword)elfcap_hw2_from_str(ELFCAP_STYLE,
89108278a5eSRod Evans 			    ptr, M_MACH)) != 0) {
89208278a5eSRod Evans 				ndx = CA_SUNW_HW_2;
89308278a5eSRod Evans 				break;
89408278a5eSRod Evans 			}
89508278a5eSRod Evans 			if ((val = (Xword)elfcap_hw1_from_str(ELFCAP_STYLE,
89608278a5eSRod Evans 			    ptr, M_MACH)) != 0)
89708278a5eSRod Evans 				ndx = CA_SUNW_HW_1;
89808278a5eSRod Evans 			break;
89908278a5eSRod Evans 		case CA_SUNW_SF_1:
90008278a5eSRod Evans 			/*
90108278a5eSRod Evans 			 * Determine whether the capabilities string matches a
90208278a5eSRod Evans 			 * known software capability mask.  Note, the callers
90308278a5eSRod Evans 			 * indication of what capabilities to process are
90408278a5eSRod Evans 			 * triggered by a tag of CA_SUNW_SF_1, but the tokens
90508278a5eSRod Evans 			 * processed could be CA_SUNW_SF_1, CA_SUNW_SF_2, etc.
90608278a5eSRod Evans 			 */
90708278a5eSRod Evans 			if ((val = (Xword)elfcap_sf1_from_str(ELFCAP_STYLE,
90808278a5eSRod Evans 			    ptr, M_MACH)) != 0)
90908278a5eSRod Evans 				ndx = CA_SUNW_SF_1;
91008278a5eSRod Evans 			break;
91108278a5eSRod Evans 		}
91208278a5eSRod Evans 
91308278a5eSRod Evans 		/*
91408278a5eSRod Evans 		 * If a capabilities token has not been matched, interpret the
91508278a5eSRod Evans 		 * string as a number.  To provide for setting the various
91608278a5eSRod Evans 		 * families (CA_SUNW_HW_1, CA_SUNW_HW_2), the number can be
91708278a5eSRod Evans 		 * prefixed with the (bracketed) family index.
91808278a5eSRod Evans 		 *
91908278a5eSRod Evans 		 *	LD_HWCAP=[1]0x40    sets CA_SUNW_HW_1 with 0x40
92008278a5eSRod Evans 		 *	LD_HWCAP=[2]0x80    sets CA_SUNW_HW_2 with 0x80
92108278a5eSRod Evans 		 *
92208278a5eSRod Evans 		 * Invalid indexes are ignored.
92308278a5eSRod Evans 		 */
92408278a5eSRod Evans 		if (val == 0) {
925*9d4bc394SRichard Lowe 			char *end;
926*9d4bc394SRichard Lowe 
92708278a5eSRod Evans 			if ((*ptr == '[') && (*(ptr + 2) == ']')) {
92808278a5eSRod Evans 				if (*(ptr + 1) == '1') {
92908278a5eSRod Evans 					ndx = tag;
93008278a5eSRod Evans 					ptr += 3;
93108278a5eSRod Evans 				} else if (*(ptr + 1) == '2') {
93208278a5eSRod Evans 					if (tag == CA_SUNW_HW_1) {
93308278a5eSRod Evans 						ndx = CA_SUNW_HW_2;
93408278a5eSRod Evans 						ptr += 3;
93508278a5eSRod Evans 					} else {
93608278a5eSRod Evans 						/* invalid index */
93708278a5eSRod Evans 						continue;
93808278a5eSRod Evans 					}
93908278a5eSRod Evans 				} else {
94008278a5eSRod Evans 					/* invalid index */
94108278a5eSRod Evans 					continue;
94208278a5eSRod Evans 				}
94308278a5eSRod Evans 			} else
94408278a5eSRod Evans 				ndx = tag;
94508278a5eSRod Evans 
94608278a5eSRod Evans 			errno = 0;
947*9d4bc394SRichard Lowe 			if (((val = strtol(ptr, &end, 16)) == 0) && errno)
948*9d4bc394SRichard Lowe 				continue;
949*9d4bc394SRichard Lowe 
950*9d4bc394SRichard Lowe 			/*
951*9d4bc394SRichard Lowe 			 * If the value wasn't an entirely valid hexadecimal
952*9d4bc394SRichard Lowe 			 * integer, assume it was intended as a capability
953*9d4bc394SRichard Lowe 			 * name and skip it.
954*9d4bc394SRichard Lowe 			 */
955*9d4bc394SRichard Lowe 			if (*end != '\0') {
956*9d4bc394SRichard Lowe 				eprintf(NULL, ERR_WARNING,
957*9d4bc394SRichard Lowe 				    MSG_INTL(MSG_CAP_IGN_UNKCAP), ptr);
95808278a5eSRod Evans 				continue;
95908278a5eSRod Evans 			}
960*9d4bc394SRichard Lowe 		}
961*9d4bc394SRichard Lowe 
96208278a5eSRod Evans 		cap_settings[ndx - 1].cs_val[mode] |= val;
96308278a5eSRod Evans 		cap_settings[ndx - 1].cs_set[mode]++;
96408278a5eSRod Evans 
965cc4ec439SRichard Lowe 	}
96608278a5eSRod Evans 
96708278a5eSRod Evans 	/*
96808278a5eSRod Evans 	 * If the "override" token was supplied, set the alternative
96908278a5eSRod Evans 	 * system capabilities, then enable or disable others.
97008278a5eSRod Evans 	 */
97108278a5eSRod Evans 	for (ndx = 0; ndx < CA_SUNW_HW_2; ndx++) {
97208278a5eSRod Evans 		if (cap_settings[ndx].cs_set[CAP_OVERRIDE])
97308278a5eSRod Evans 			*(cap_settings[ndx].cs_aval) =
97408278a5eSRod Evans 			    cap_settings[ndx].cs_val[CAP_OVERRIDE];
97508278a5eSRod Evans 		if (cap_settings[ndx].cs_set[CAP_ENABLE])
97608278a5eSRod Evans 			*(cap_settings[ndx].cs_aval) |=
97708278a5eSRod Evans 			    cap_settings[ndx].cs_val[CAP_ENABLE];
97808278a5eSRod Evans 		if (cap_settings[ndx].cs_set[CAP_DISABLE])
97908278a5eSRod Evans 			*(cap_settings[ndx].cs_aval) &=
98008278a5eSRod Evans 			    ~cap_settings[ndx].cs_val[CAP_DISABLE];
98108278a5eSRod Evans 	}
98208278a5eSRod Evans 	free(caps);
98308278a5eSRod Evans 	return (1);
98408278a5eSRod Evans }
98508278a5eSRod Evans #undef	ELFCAP_STYLE
98608278a5eSRod Evans 
98708278a5eSRod Evans /*
98808278a5eSRod Evans  * Create an AVL tree of objects that are to be validated against an alternative
98908278a5eSRod Evans  * system capabilities value.
99008278a5eSRod Evans  */
99108278a5eSRod Evans static int
cap_files(const char * str)99208278a5eSRod Evans cap_files(const char *str)
99308278a5eSRod Evans {
99408278a5eSRod Evans 	char	*caps, *name, *next;
99508278a5eSRod Evans 
99608278a5eSRod Evans 	if ((caps = strdup(str)) == NULL)
99708278a5eSRod Evans 		return (0);
99808278a5eSRod Evans 
999cc4ec439SRichard Lowe 	for (name = strtok_r(caps, MSG_ORIG(MSG_CAP_DELIMIT), &next);
1000cc4ec439SRichard Lowe 	    name != NULL;
1001cc4ec439SRichard Lowe 	    name = strtok_r(NULL, MSG_ORIG(MSG_CAP_DELIMIT), &next)) {
100208278a5eSRod Evans 		avl_index_t	where;
100308278a5eSRod Evans 		PathNode	*pnp;
100408278a5eSRod Evans 		uint_t		hash = sgs_str_hash(name);
100508278a5eSRod Evans 
100608278a5eSRod Evans 		/*
100708278a5eSRod Evans 		 * Determine whether this pathname has already been recorded.
100808278a5eSRod Evans 		 */
100908278a5eSRod Evans 		if (pnavl_recorded(&capavl, name, hash, &where))
101008278a5eSRod Evans 			continue;
101108278a5eSRod Evans 
101208278a5eSRod Evans 		if ((pnp = calloc(sizeof (PathNode), 1)) != NULL) {
101308278a5eSRod Evans 			pnp->pn_name = name;
101408278a5eSRod Evans 			pnp->pn_hash = hash;
101508278a5eSRod Evans 			avl_insert(capavl, pnp, where);
101608278a5eSRod Evans 		}
1017cc4ec439SRichard Lowe 	}
101808278a5eSRod Evans 
101908278a5eSRod Evans 	return (1);
102008278a5eSRod Evans }
102108278a5eSRod Evans 
102208278a5eSRod Evans /*
102308278a5eSRod Evans  * Set alternative system capabilities.  A user can establish alternative system
102408278a5eSRod Evans  * capabilities from the environment, or from a configuration file.  This
102508278a5eSRod Evans  * routine is called in each instance.  Environment variables only set the
102608278a5eSRod Evans  * replaceable (rpl) variables.  Configuration files can set both replaceable
102708278a5eSRod Evans  * (rpl) and permanent (prm) variables.
102808278a5eSRod Evans  */
102908278a5eSRod Evans int
cap_alternative(void)103008278a5eSRod Evans cap_alternative(void)
103108278a5eSRod Evans {
103208278a5eSRod Evans 	/*
103308278a5eSRod Evans 	 * If no capabilities have been set, we're done.
103408278a5eSRod Evans 	 */
103508278a5eSRod Evans 	if ((rpl_hwcap == NULL) && (rpl_sfcap == NULL) &&
103608278a5eSRod Evans 	    (rpl_machcap == NULL) && (rpl_platcap == NULL) &&
103708278a5eSRod Evans 	    (prm_hwcap == NULL) && (prm_sfcap == NULL) &&
103808278a5eSRod Evans 	    (prm_machcap == NULL) && (prm_platcap == NULL))
103908278a5eSRod Evans 		return (1);
104008278a5eSRod Evans 
104108278a5eSRod Evans 	/*
104208278a5eSRod Evans 	 * If the user has requested to modify any capabilities, establish a
104308278a5eSRod Evans 	 * unique set from the present system capabilities.
104408278a5eSRod Evans 	 */
104508278a5eSRod Evans 	if ((alt_scapset = malloc(sizeof (Syscapset))) == NULL)
104608278a5eSRod Evans 		return (0);
104708278a5eSRod Evans 	*alt_scapset = *org_scapset;
104808278a5eSRod Evans 
104908278a5eSRod Evans 	cap_settings[CA_SUNW_HW_1 - 1].cs_aval = &alt_scapset->sc_hw_1;
105008278a5eSRod Evans 	cap_settings[CA_SUNW_SF_1 - 1].cs_aval = &alt_scapset->sc_sf_1;
105108278a5eSRod Evans 	cap_settings[CA_SUNW_HW_2 - 1].cs_aval = &alt_scapset->sc_hw_2;
105208278a5eSRod Evans 
105308278a5eSRod Evans 	/*
105408278a5eSRod Evans 	 * Process any replaceable variables.
105508278a5eSRod Evans 	 */
105608278a5eSRod Evans 	if (rpl_hwcap && (cap_modify(CA_SUNW_HW_1, rpl_hwcap) == 0))
105708278a5eSRod Evans 		return (0);
105808278a5eSRod Evans 	if (rpl_sfcap && (cap_modify(CA_SUNW_SF_1, rpl_sfcap) == 0))
105908278a5eSRod Evans 		return (0);
106008278a5eSRod Evans 
106108278a5eSRod Evans 	if (rpl_platcap) {
106208278a5eSRod Evans 		alt_scapset->sc_plat = (char *)rpl_platcap;
106308278a5eSRod Evans 		alt_scapset->sc_platsz = strlen(rpl_platcap);
106408278a5eSRod Evans 	}
106508278a5eSRod Evans 	if (rpl_machcap) {
106608278a5eSRod Evans 		alt_scapset->sc_mach = (char *)rpl_machcap;
106708278a5eSRod Evans 		alt_scapset->sc_machsz = strlen(rpl_machcap);
106808278a5eSRod Evans 	}
106908278a5eSRod Evans 
107008278a5eSRod Evans 	if (rpl_cap_files && (cap_files(rpl_cap_files) == 0))
107108278a5eSRod Evans 		return (0);
107208278a5eSRod Evans 
107308278a5eSRod Evans 	/*
107408278a5eSRod Evans 	 * Process any permanent variables.
107508278a5eSRod Evans 	 */
107608278a5eSRod Evans 	if (prm_hwcap && (cap_modify(CA_SUNW_HW_1, prm_hwcap) == 0))
107708278a5eSRod Evans 		return (0);
107808278a5eSRod Evans 	if (prm_sfcap && (cap_modify(CA_SUNW_SF_1, prm_sfcap) == 0))
107908278a5eSRod Evans 		return (0);
108008278a5eSRod Evans 
108108278a5eSRod Evans 	if (prm_platcap) {
108208278a5eSRod Evans 		alt_scapset->sc_plat = (char *)prm_platcap;
108308278a5eSRod Evans 		alt_scapset->sc_platsz = strlen(prm_platcap);
108408278a5eSRod Evans 	}
108508278a5eSRod Evans 	if (prm_machcap) {
108608278a5eSRod Evans 		alt_scapset->sc_mach = (char *)prm_machcap;
108708278a5eSRod Evans 		alt_scapset->sc_machsz = strlen(prm_machcap);
108808278a5eSRod Evans 	}
108908278a5eSRod Evans 
109008278a5eSRod Evans 	if (prm_cap_files && (cap_files(prm_cap_files) == 0))
109108278a5eSRod Evans 		return (0);
109208278a5eSRod Evans 
109308278a5eSRod Evans 	/*
109408278a5eSRod Evans 	 * Reset the replaceable variables.  If this is the environment variable
109508278a5eSRod Evans 	 * processing, these variables are now available for configuration file
109608278a5eSRod Evans 	 * initialization.
109708278a5eSRod Evans 	 */
109808278a5eSRod Evans 	rpl_hwcap = rpl_sfcap = rpl_machcap = rpl_platcap =
109908278a5eSRod Evans 	    rpl_cap_files = NULL;
110008278a5eSRod Evans 
110108278a5eSRod Evans 	return (1);
110208278a5eSRod Evans }
110308278a5eSRod Evans 
110408278a5eSRod Evans /*
110508278a5eSRod Evans  * Take the index from a Capinfo entry and determine the associated capabilities
110608278a5eSRod Evans  * set.  Verify that the capabilities are available for this system.
110708278a5eSRod Evans  */
110808278a5eSRod Evans static int
sym_cap_check(Cap * cptr,uint_t cndx,Syscapset * bestcapset,Rt_map * lmp,const char * name,uint_t ndx)110908278a5eSRod Evans sym_cap_check(Cap *cptr, uint_t cndx, Syscapset *bestcapset, Rt_map *lmp,
111008278a5eSRod Evans     const char *name, uint_t ndx)
111108278a5eSRod Evans {
111208278a5eSRod Evans 	Syscapset	*scapset;
111308278a5eSRod Evans 	int		totplat, ivlplat, totmach, ivlmach, capfail = 0;
111408278a5eSRod Evans 
111508278a5eSRod Evans 	/*
111608278a5eSRod Evans 	 * Determine whether this file requires validation against alternative
111708278a5eSRod Evans 	 * system capabilities.
111808278a5eSRod Evans 	 */
111908278a5eSRod Evans 	if ((FLAGS1(lmp) & FL1_RT_ALTCHECK) == 0)
112008278a5eSRod Evans 		cap_check_lmp_init(lmp);
112108278a5eSRod Evans 
112208278a5eSRod Evans 	if (FLAGS1(lmp) & FL1_RT_ALTCAP)
112308278a5eSRod Evans 		scapset = alt_scapset;
112408278a5eSRod Evans 	else
112508278a5eSRod Evans 		scapset = org_scapset;
112608278a5eSRod Evans 
112708278a5eSRod Evans 	totplat = ivlplat = totmach = ivlmach = 0;
112808278a5eSRod Evans 
112908278a5eSRod Evans 	/*
113008278a5eSRod Evans 	 * A capabilities index points to a capabilities group that can consist
113108278a5eSRod Evans 	 * of one or more capabilities, terminated with a CA_SUNW_NULL entry.
113208278a5eSRod Evans 	 */
113308278a5eSRod Evans 	for (cptr += cndx; cptr->c_tag != CA_SUNW_NULL; cptr++) {
113408278a5eSRod Evans 		Xword	val = cptr->c_un.c_val;
113508278a5eSRod Evans 		char	*str;
113608278a5eSRod Evans 
113708278a5eSRod Evans 		switch (cptr->c_tag) {
113808278a5eSRod Evans 		case CA_SUNW_HW_1:
11394e12d685SRod Evans 			/*
11404e12d685SRod Evans 			 * Remove any historic values that should not be
11414e12d685SRod Evans 			 * involved with any validation.
11424e12d685SRod Evans 			 */
11434e12d685SRod Evans 			val &= ~AV_HW1_IGNORE;
11444e12d685SRod Evans 
114508278a5eSRod Evans 			bestcapset->sc_hw_1 = val;
114608278a5eSRod Evans 			DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_HW_1,
114708278a5eSRod Evans 			    name, ndx, M_MACH, bestcapset));
114808278a5eSRod Evans 
114908278a5eSRod Evans 			if (hwcap1_check(scapset, val, NULL) == 0)
115008278a5eSRod Evans 				capfail++;
115108278a5eSRod Evans 			break;
115208278a5eSRod Evans 		case CA_SUNW_SF_1:
115308278a5eSRod Evans 			bestcapset->sc_sf_1 = val;
115408278a5eSRod Evans 			DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_SF_1,
115508278a5eSRod Evans 			    name, ndx, M_MACH, bestcapset));
115608278a5eSRod Evans 
115708278a5eSRod Evans 			if (sfcap1_check(scapset, val, NULL) == 0)
115808278a5eSRod Evans 				capfail++;
115908278a5eSRod Evans 			break;
116008278a5eSRod Evans 		case CA_SUNW_HW_2:
116108278a5eSRod Evans 			bestcapset->sc_hw_2 = val;
116208278a5eSRod Evans 			DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_HW_2,
116308278a5eSRod Evans 			    name, ndx, M_MACH, bestcapset));
116408278a5eSRod Evans 
116508278a5eSRod Evans 			if (hwcap2_check(scapset, val, NULL) == 0)
116608278a5eSRod Evans 				capfail++;
116708278a5eSRod Evans 			break;
116808278a5eSRod Evans 		case CA_SUNW_PLAT:
116908278a5eSRod Evans 			/*
117008278a5eSRod Evans 			 * A capabilities set can define multiple platform names
117108278a5eSRod Evans 			 * that are appropriate.  Only if all the names are
117208278a5eSRod Evans 			 * deemed invalid is the group determined inappropriate.
117308278a5eSRod Evans 			 */
117408278a5eSRod Evans 			if (totplat == ivlplat) {
117508278a5eSRod Evans 				totplat++;
117608278a5eSRod Evans 
117708278a5eSRod Evans 				str = STRTAB(lmp) + val;
117808278a5eSRod Evans 				bestcapset->sc_plat = str;
117908278a5eSRod Evans 
118008278a5eSRod Evans 				DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_PLAT,
118108278a5eSRod Evans 				    name, ndx, M_MACH, bestcapset));
118208278a5eSRod Evans 
118308278a5eSRod Evans 				if (platcap_check(scapset, str, NULL) == 0)
118408278a5eSRod Evans 					ivlplat++;
118508278a5eSRod Evans 			}
118608278a5eSRod Evans 			break;
118708278a5eSRod Evans 		case CA_SUNW_MACH:
118808278a5eSRod Evans 			/*
118908278a5eSRod Evans 			 * A capabilities set can define multiple machine names
119008278a5eSRod Evans 			 * that are appropriate.  Only if all the names are
119108278a5eSRod Evans 			 * deemed invalid is the group determined inappropriate.
119208278a5eSRod Evans 			 */
119308278a5eSRod Evans 			if (totmach == ivlmach) {
119408278a5eSRod Evans 				totmach++;
119508278a5eSRod Evans 
119608278a5eSRod Evans 				str = STRTAB(lmp) + val;
119708278a5eSRod Evans 				bestcapset->sc_mach = str;
119808278a5eSRod Evans 
119908278a5eSRod Evans 				DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_MACH,
120008278a5eSRod Evans 				    name, ndx, M_MACH, bestcapset));
120108278a5eSRod Evans 
120208278a5eSRod Evans 				if (machcap_check(scapset, str, NULL) == 0)
120308278a5eSRod Evans 					ivlmach++;
120408278a5eSRod Evans 			}
120508278a5eSRod Evans 			break;
120608278a5eSRod Evans 		default:
120708278a5eSRod Evans 			break;
120808278a5eSRod Evans 		}
120908278a5eSRod Evans 	}
121008278a5eSRod Evans 
121108278a5eSRod Evans 	/*
121208278a5eSRod Evans 	 * If any platform definitions, or machine definitions were found, and
121308278a5eSRod Evans 	 * all were invalid, indicate that the object is inappropriate.
121408278a5eSRod Evans 	 */
121508278a5eSRod Evans 	if (capfail || (totplat && (totplat == ivlplat)) ||
121608278a5eSRod Evans 	    (totmach && (totmach == ivlmach))) {
121708278a5eSRod Evans 		DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_REJECTED, name, ndx,
121808278a5eSRod Evans 		    M_MACH, NULL));
121908278a5eSRod Evans 		return (0);
122008278a5eSRod Evans 	}
122108278a5eSRod Evans 
122208278a5eSRod Evans 	DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_CANDIDATE, name, ndx,
122308278a5eSRod Evans 	    M_MACH, NULL));
122408278a5eSRod Evans 	return (1);
122508278a5eSRod Evans }
122608278a5eSRod Evans 
122708278a5eSRod Evans /*
122808278a5eSRod Evans  * Determine whether a symbols capabilities are more significant than any that
122908278a5eSRod Evans  * have already been validated.  The precedence of capabilities are:
123008278a5eSRod Evans  *
123108278a5eSRod Evans  *   PLATCAP -> MACHCAP -> HWCAP_2 -> HWCAP_1
123208278a5eSRod Evans  *
123308278a5eSRod Evans  *
123408278a5eSRod Evans  * Presently we make no comparisons of software capabilities.  However, should
123508278a5eSRod Evans  * this symbol capability have required the SF1_SUNW_ADDR32 attribute, then
123608278a5eSRod Evans  * this would have been validated as appropriate or not.
123708278a5eSRod Evans  *
123808278a5eSRod Evans  * bestcapset is the presently available 'best' capabilities group, and
123908278a5eSRod Evans  * symcapset is the present capabilities group under investigation.  Return 0
124008278a5eSRod Evans  * if the bestcapset should remain in affect, or 1 if the symcapset is better.
124108278a5eSRod Evans  */
124208278a5eSRod Evans inline static int
is_sym_the_best(Syscapset * bestcapset,Syscapset * symcapset)124308278a5eSRod Evans is_sym_the_best(Syscapset *bestcapset, Syscapset *symcapset)
124408278a5eSRod Evans {
124508278a5eSRod Evans 	/*
124608278a5eSRod Evans 	 * Check any platform capability.  If the new symbol isn't associated
124708278a5eSRod Evans 	 * with a CA_SUNW_PLAT capability, and the best symbol is, then retain
124808278a5eSRod Evans 	 * the best capabilities group.  If the new symbol is associated with a
124908278a5eSRod Evans 	 * CA_SUNW_PLAT capability, and the best symbol isn't, then the new
125008278a5eSRod Evans 	 * symbol needs to be taken.
125108278a5eSRod Evans 	 */
125208278a5eSRod Evans 	if (bestcapset->sc_plat && (symcapset->sc_plat == NULL))
125308278a5eSRod Evans 		return (0);
125408278a5eSRod Evans 
125508278a5eSRod Evans 	if ((bestcapset->sc_plat == NULL) && symcapset->sc_plat)
125608278a5eSRod Evans 		return (1);
125708278a5eSRod Evans 
125808278a5eSRod Evans 	/*
125908278a5eSRod Evans 	 * Check any machine name capability.  If the new symbol isn't
126008278a5eSRod Evans 	 * associated with a CA_SUNW_MACH capability, and the best symbol is,
126108278a5eSRod Evans 	 * then retain the best capabilities group.  If the new symbol is
126208278a5eSRod Evans 	 * associated with a CA_SUNW_MACH capability, and the best symbol isn't,
126308278a5eSRod Evans 	 * then the new symbol needs to be taken.
126408278a5eSRod Evans 	 */
126508278a5eSRod Evans 	if (bestcapset->sc_mach && (symcapset->sc_mach == NULL))
126608278a5eSRod Evans 		return (0);
126708278a5eSRod Evans 
126808278a5eSRod Evans 	if ((bestcapset->sc_mach == NULL) && symcapset->sc_mach)
126908278a5eSRod Evans 		return (1);
127008278a5eSRod Evans 
127108278a5eSRod Evans 	/*
127208278a5eSRod Evans 	 * Check the hardware capabilities.  If the best symbols CA_SUNW_HW_2
127308278a5eSRod Evans 	 * capabilities are greater than the new symbols capabilities, then
127408278a5eSRod Evans 	 * retain the best capabilities group.  If the new symbols CA_SUNW_HW_2
127508278a5eSRod Evans 	 * capabilities are greater than the best symbol, then the new symbol
127608278a5eSRod Evans 	 * needs to be taken.
127708278a5eSRod Evans 	 */
127808278a5eSRod Evans 	if (bestcapset->sc_hw_2 > symcapset->sc_hw_2)
127908278a5eSRod Evans 		return (0);
128008278a5eSRod Evans 
128108278a5eSRod Evans 	if (bestcapset->sc_hw_2 < symcapset->sc_hw_2)
128208278a5eSRod Evans 		return (1);
128308278a5eSRod Evans 
128408278a5eSRod Evans 	/*
128508278a5eSRod Evans 	 * Check the remaining hardware capabilities.  If the best symbols
128608278a5eSRod Evans 	 * CA_SUNW_HW_1 capabilities are greater than the new symbols
128708278a5eSRod Evans 	 * capabilities, then retain the best capabilities group.  If the new
128808278a5eSRod Evans 	 * symbols CA_SUNW_HW_1 capabilities are greater than the best symbol,
128908278a5eSRod Evans 	 * then the new symbol needs to be taken.
129008278a5eSRod Evans 	 */
129108278a5eSRod Evans 	if (bestcapset->sc_hw_1 > symcapset->sc_hw_1)
129208278a5eSRod Evans 		return (0);
129308278a5eSRod Evans 
129408278a5eSRod Evans 	if (bestcapset->sc_hw_1 < symcapset->sc_hw_1)
129508278a5eSRod Evans 		return (1);
129608278a5eSRod Evans 
129708278a5eSRod Evans 	/*
129808278a5eSRod Evans 	 * Both capabilities are the same.  Retain the best on a first-come
129908278a5eSRod Evans 	 * first-served basis.
130008278a5eSRod Evans 	 */
130108278a5eSRod Evans 	return (0);
130208278a5eSRod Evans }
130308278a5eSRod Evans 
130408278a5eSRod Evans /*
130508278a5eSRod Evans  * Initiate symbol capabilities processing.  If an initial symbol lookup
130608278a5eSRod Evans  * results in binding to a symbol that has an associated SUNW_capinfo entry,
130708278a5eSRod Evans  * we arrive here.
130808278a5eSRod Evans  *
130908278a5eSRod Evans  * The standard model is that this initial symbol is the lead capabilities
131008278a5eSRod Evans  * symbol (defined as CAPINFO_SUNW_GLOB) of a capabilities family.  This lead
131108278a5eSRod Evans  * symbol's SUNW_capinfo information points to the SUNW_capchain entry that
131208278a5eSRod Evans  * provides the family symbol indexes.  We traverse this chain, looking at
131308278a5eSRod Evans  * each family member, to discover the best capabilities instance.  This
131408278a5eSRod Evans  * instance name and symbol information is returned to establish the final
131508278a5eSRod Evans  * symbol binding.
131608278a5eSRod Evans  *
131708278a5eSRod Evans  * If the symbol that got us here is not CAPINFO_SUNW_GLOB, then we've bound
131808278a5eSRod Evans  * directly to a capabilities symbol which must be verified.  This is not the
131908278a5eSRod Evans  * model created by ld(1) using -z symbolcap, but might be created directly
132008278a5eSRod Evans  * within a relocatable object by the compilation system.
132108278a5eSRod Evans  */
132208278a5eSRod Evans int
cap_match(Sresult * srp,uint_t symndx,Sym * symtabptr,char * strtabptr)132308278a5eSRod Evans cap_match(Sresult *srp, uint_t symndx, Sym *symtabptr, char *strtabptr)
132408278a5eSRod Evans {
132508278a5eSRod Evans 	Rt_map		*ilmp = srp->sr_dmap;
132608278a5eSRod Evans 	Sym		*bsym = NULL;
132708278a5eSRod Evans 	const char	*bname;
132808278a5eSRod Evans 	Syscapset	bestcapset = { 0 };
132908278a5eSRod Evans 	Cap		*cap;
133008278a5eSRod Evans 	Capchain	*capchain;
133108278a5eSRod Evans 	uchar_t		grpndx;
133208278a5eSRod Evans 	uint_t		ochainndx, nchainndx, bndx;
133308278a5eSRod Evans 
133408278a5eSRod Evans 	cap = CAP(ilmp);
133508278a5eSRod Evans 	capchain = CAPCHAIN(ilmp);
133608278a5eSRod Evans 
133708278a5eSRod Evans 	grpndx = (uchar_t)ELF_C_GROUP(CAPINFO(ilmp)[symndx]);
133808278a5eSRod Evans 
133908278a5eSRod Evans 	/*
134008278a5eSRod Evans 	 * If this symbols capability group is not a lead symbol, then simply
134108278a5eSRod Evans 	 * verify the symbol.
134208278a5eSRod Evans 	 */
134308278a5eSRod Evans 	if (grpndx != CAPINFO_SUNW_GLOB) {
134408278a5eSRod Evans 		Syscapset	symcapset = { 0 };
134508278a5eSRod Evans 
134608278a5eSRod Evans 		return (sym_cap_check(cap, grpndx, &symcapset, ilmp,
134708278a5eSRod Evans 		    srp->sr_name, symndx));
134808278a5eSRod Evans 	}
134908278a5eSRod Evans 
135008278a5eSRod Evans 	/*
135108278a5eSRod Evans 	 * If there is no capabilities chain, return the lead symbol.
135208278a5eSRod Evans 	 */
135308278a5eSRod Evans 	if (capchain == NULL)
135408278a5eSRod Evans 		return (1);
135508278a5eSRod Evans 
135608278a5eSRod Evans 	ochainndx = (uint_t)ELF_C_SYM(CAPINFO(ilmp)[symndx]);
135708278a5eSRod Evans 
135808278a5eSRod Evans 	/*
135908278a5eSRod Evans 	 * If there is only one member for this family, take it.  Once a family
136008278a5eSRod Evans 	 * has been processed, the best family instance is written to the head
136108278a5eSRod Evans 	 * of the chain followed by a null entry.  This caching ensures that the
136208278a5eSRod Evans 	 * same family comparison doesn't have to be undertaken more than once.
136308278a5eSRod Evans 	 */
136408278a5eSRod Evans 	if (capchain[ochainndx] && (capchain[ochainndx + 1] == 0)) {
136508278a5eSRod Evans 		Sym		*fsym = symtabptr + capchain[ochainndx];
136608278a5eSRod Evans 		const char	*fname = strtabptr + fsym->st_name;
136708278a5eSRod Evans 
136808278a5eSRod Evans 		DBG_CALL(Dbg_syms_cap_lookup(ilmp, DBG_CAP_USED, fname,
136908278a5eSRod Evans 		    capchain[ochainndx], M_MACH, NULL));
137008278a5eSRod Evans 
137108278a5eSRod Evans 		srp->sr_sym = fsym;
137208278a5eSRod Evans 		srp->sr_name = fname;
137308278a5eSRod Evans 		return (1);
137408278a5eSRod Evans 	}
137508278a5eSRod Evans 
137608278a5eSRod Evans 	/*
137708278a5eSRod Evans 	 * As this symbol is the lead symbol of a capabilities family, it is
137808278a5eSRod Evans 	 * considered the generic member, and therefore forms the basic
137908278a5eSRod Evans 	 * fall-back for the capabilities family.
138008278a5eSRod Evans 	 */
138108278a5eSRod Evans 	DBG_CALL(Dbg_syms_cap_lookup(ilmp, DBG_CAP_DEFAULT, srp->sr_name,
138208278a5eSRod Evans 	    symndx, M_MACH, NULL));
138308278a5eSRod Evans 	bsym = srp->sr_sym;
138408278a5eSRod Evans 	bname = srp->sr_name;
138508278a5eSRod Evans 	bndx = symndx;
138608278a5eSRod Evans 
138708278a5eSRod Evans 	/*
138808278a5eSRod Evans 	 * Traverse the capabilities chain analyzing each family member.
138908278a5eSRod Evans 	 */
139008278a5eSRod Evans 	for (nchainndx = ochainndx + 1, symndx = capchain[nchainndx]; symndx;
139108278a5eSRod Evans 	    nchainndx++, symndx = capchain[nchainndx]) {
139208278a5eSRod Evans 		Sym		*nsym = symtabptr + symndx;
139308278a5eSRod Evans 		const char	*nname = strtabptr + nsym->st_name;
139408278a5eSRod Evans 		Syscapset	symcapset = { 0 };
139508278a5eSRod Evans 
139608278a5eSRod Evans 		if ((grpndx =
139708278a5eSRod Evans 		    (uchar_t)ELF_C_GROUP(CAPINFO(ilmp)[symndx])) == 0)
139808278a5eSRod Evans 			continue;
139908278a5eSRod Evans 
140008278a5eSRod Evans 		if (sym_cap_check(cap, grpndx, &symcapset, ilmp,
140108278a5eSRod Evans 		    nname, symndx) == 0)
140208278a5eSRod Evans 			continue;
140308278a5eSRod Evans 
140408278a5eSRod Evans 		/*
140508278a5eSRod Evans 		 * Determine whether a symbol's capabilities are more
140608278a5eSRod Evans 		 * significant than any that have already been validated.
140708278a5eSRod Evans 		 */
140808278a5eSRod Evans 		if (is_sym_the_best(&bestcapset, &symcapset)) {
140908278a5eSRod Evans 			bestcapset = symcapset;
141008278a5eSRod Evans 			bsym = nsym;
141108278a5eSRod Evans 			bname = nname;
141208278a5eSRod Evans 			bndx = symndx;
141308278a5eSRod Evans 		}
141408278a5eSRod Evans 	}
141508278a5eSRod Evans 
141608278a5eSRod Evans 	DBG_CALL(Dbg_syms_cap_lookup(ilmp, DBG_CAP_USED, bname, bndx,
141708278a5eSRod Evans 	    M_MACH, NULL));
141808278a5eSRod Evans 
141908278a5eSRod Evans 	/*
142008278a5eSRod Evans 	 * Having found the best symbol, cache the results by overriding the
142108278a5eSRod Evans 	 * first element of the associated chain.
142208278a5eSRod Evans 	 */
142308278a5eSRod Evans 	capchain[ochainndx] = bndx;
142408278a5eSRod Evans 	capchain[ochainndx + 1] = 0;
142508278a5eSRod Evans 
142608278a5eSRod Evans 	/*
142708278a5eSRod Evans 	 * Update the symbol result information for return to the user.
142808278a5eSRod Evans 	 */
142908278a5eSRod Evans 	srp->sr_sym = bsym;
143008278a5eSRod Evans 	srp->sr_name = bname;
143108278a5eSRod Evans 	return (1);
143208278a5eSRod Evans }
1433