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.
2456726c7eSRobert Mustacchi * Copyright 2022 Oxide Computer Company
257c478bd9Sstevel@tonic-gate */
267c478bd9Sstevel@tonic-gate
277c478bd9Sstevel@tonic-gate #include <sys/types.h>
287c478bd9Sstevel@tonic-gate #include <sys/mman.h>
297c478bd9Sstevel@tonic-gate #include <dirent.h>
307c478bd9Sstevel@tonic-gate #include <stdio.h>
318521e5e6Srie #include <stdlib.h>
327c478bd9Sstevel@tonic-gate #include <string.h>
337c478bd9Sstevel@tonic-gate #include <limits.h>
345aefb655Srie #include <debug.h>
355aefb655Srie #include <conv.h>
3608278a5eSRod Evans #include <elfcap.h>
377c478bd9Sstevel@tonic-gate #include "_rtld.h"
3808278a5eSRod Evans #include "_elf.h"
397c478bd9Sstevel@tonic-gate #include "_audit.h"
407c478bd9Sstevel@tonic-gate #include "msg.h"
417c478bd9Sstevel@tonic-gate
427c478bd9Sstevel@tonic-gate /*
4308278a5eSRod Evans * qsort(3c) capability comparison function.
447c478bd9Sstevel@tonic-gate */
457c478bd9Sstevel@tonic-gate static int
compare(const void * vp_a,const void * vp_b)461c1abfbcSRod Evans compare(const void *vp_a, const void *vp_b)
477c478bd9Sstevel@tonic-gate {
481c1abfbcSRod Evans Fdesc *fdp_a = (Fdesc *)vp_a, *fdp_b = (Fdesc *)vp_b;
4908278a5eSRod Evans char *strcap_a, *strcap_b;
5008278a5eSRod Evans Xword hwcap_a, hwcap_b;
517c478bd9Sstevel@tonic-gate
5208278a5eSRod Evans /*
5308278a5eSRod Evans * First, investigate any platform capability.
5408278a5eSRod Evans */
551c1abfbcSRod Evans strcap_a = fdp_a->fd_scapset.sc_plat;
561c1abfbcSRod Evans strcap_b = fdp_b->fd_scapset.sc_plat;
5708278a5eSRod Evans
5808278a5eSRod Evans if (strcap_a && (strcap_b == NULL))
597c478bd9Sstevel@tonic-gate return (-1);
6008278a5eSRod Evans if (strcap_b && (strcap_a == NULL))
617c478bd9Sstevel@tonic-gate return (1);
627c478bd9Sstevel@tonic-gate
6308278a5eSRod Evans /*
6408278a5eSRod Evans * Second, investigate any machine capability.
6508278a5eSRod Evans */
661c1abfbcSRod Evans strcap_a = fdp_a->fd_scapset.sc_mach;
671c1abfbcSRod Evans strcap_b = fdp_b->fd_scapset.sc_mach;
6808278a5eSRod Evans
6908278a5eSRod Evans if (strcap_a && (strcap_b == NULL))
7008278a5eSRod Evans return (-1);
7108278a5eSRod Evans if (strcap_b && (strcap_a == NULL))
7208278a5eSRod Evans return (1);
7356726c7eSRobert Mustacchi /*
7456726c7eSRobert Mustacchi * Third, investigate any CA_SUNW_HW_3 hardware capabilities.
7556726c7eSRobert Mustacchi */
7656726c7eSRobert Mustacchi hwcap_a = fdp_a->fd_scapset.sc_hw_3;
7756726c7eSRobert Mustacchi hwcap_b = fdp_b->fd_scapset.sc_hw_3;
7856726c7eSRobert Mustacchi
7956726c7eSRobert Mustacchi if (hwcap_a > hwcap_b)
8056726c7eSRobert Mustacchi return (-1);
8156726c7eSRobert Mustacchi if (hwcap_a < hwcap_b)
8256726c7eSRobert Mustacchi return (1);
8308278a5eSRod Evans
8408278a5eSRod Evans /*
8556726c7eSRobert Mustacchi * Fourth, investigate any CA_SUNW_HW_2 hardware capabilities.
8608278a5eSRod Evans */
871c1abfbcSRod Evans hwcap_a = fdp_a->fd_scapset.sc_hw_2;
881c1abfbcSRod Evans hwcap_b = fdp_b->fd_scapset.sc_hw_2;
8908278a5eSRod Evans
9008278a5eSRod Evans if (hwcap_a > hwcap_b)
917c478bd9Sstevel@tonic-gate return (-1);
9208278a5eSRod Evans if (hwcap_a < hwcap_b)
937c478bd9Sstevel@tonic-gate return (1);
9408278a5eSRod Evans
9508278a5eSRod Evans /*
9608278a5eSRod Evans * Finally, investigate any CA_SUNW_HW_1 hardware capabilities.
9708278a5eSRod Evans */
981c1abfbcSRod Evans hwcap_a = fdp_a->fd_scapset.sc_hw_1;
991c1abfbcSRod Evans hwcap_b = fdp_b->fd_scapset.sc_hw_1;
10008278a5eSRod Evans
10108278a5eSRod Evans if (hwcap_a > hwcap_b)
10208278a5eSRod Evans return (-1);
10308278a5eSRod Evans if (hwcap_a < hwcap_b)
10408278a5eSRod Evans return (1);
10508278a5eSRod Evans
1061c1abfbcSRod Evans /*
1071c1abfbcSRod Evans * Normally, a capabilities directory contains one or more capabilities
1081c1abfbcSRod Evans * files, each with different capabilities. The role of ld.so.1 is to
1091c1abfbcSRod Evans * select the best candidate from these variants. However, we've come
1101c1abfbcSRod Evans * across cases where files containing the same capabilities have been
1111c1abfbcSRod Evans * placed in the same capabilities directory. As we can't tell which
1121c1abfbcSRod Evans * file is the best, we select neither, and diagnose this suspicious
1131c1abfbcSRod Evans * scenario.
1141c1abfbcSRod Evans */
1151c1abfbcSRod Evans DBG_CALL(Dbg_cap_identical(fdp_a->fd_lml, fdp_a->fd_nname,
1161c1abfbcSRod Evans fdp_b->fd_nname));
1171c1abfbcSRod Evans
1181c1abfbcSRod Evans fdp_a->fd_flags |= FLG_FD_IGNORE;
1191c1abfbcSRod Evans fdp_b->fd_flags |= FLG_FD_IGNORE;
1201c1abfbcSRod Evans
1217c478bd9Sstevel@tonic-gate return (0);
1227c478bd9Sstevel@tonic-gate }
1237c478bd9Sstevel@tonic-gate
1247c478bd9Sstevel@tonic-gate /*
12508278a5eSRod Evans * Determine whether HWCAP1 capabilities value is supported.
12608278a5eSRod Evans */
12708278a5eSRod Evans int
hwcap1_check(Syscapset * scapset,Xword val,Rej_desc * rej)12808278a5eSRod Evans hwcap1_check(Syscapset *scapset, Xword val, Rej_desc *rej)
12908278a5eSRod Evans {
13008278a5eSRod Evans Xword mval;
13108278a5eSRod Evans
13208278a5eSRod Evans /*
13308278a5eSRod Evans * Ensure that the kernel can cope with the required capabilities.
13408278a5eSRod Evans */
13508278a5eSRod Evans if ((rtld_flags2 & RT_FL2_HWCAP) &&
13608278a5eSRod Evans ((mval = (val & ~scapset->sc_hw_1)) != 0)) {
13708278a5eSRod Evans if (rej) {
13808278a5eSRod Evans static Conv_cap_val_hw1_buf_t cap_buf;
13908278a5eSRod Evans
14008278a5eSRod Evans rej->rej_type = SGS_REJ_HWCAP_1;
14108278a5eSRod Evans rej->rej_str = conv_cap_val_hw1(mval,
14208278a5eSRod Evans M_MACH, 0, &cap_buf);
14308278a5eSRod Evans }
14408278a5eSRod Evans return (0);
14508278a5eSRod Evans }
14608278a5eSRod Evans return (1);
14708278a5eSRod Evans }
14808278a5eSRod Evans
14908278a5eSRod Evans /*
15008278a5eSRod Evans * Determine whether HWCAP2 capabilities value is supported.
1517c478bd9Sstevel@tonic-gate */
1527c478bd9Sstevel@tonic-gate int
hwcap2_check(Syscapset * scapset,Xword val,Rej_desc * rej)15308278a5eSRod Evans hwcap2_check(Syscapset *scapset, Xword val, Rej_desc *rej)
1547c478bd9Sstevel@tonic-gate {
15556deab07SRod Evans Xword mval;
1567c478bd9Sstevel@tonic-gate
15756deab07SRod Evans /*
15856deab07SRod Evans * Ensure that the kernel can cope with the required capabilities.
15956deab07SRod Evans */
16008278a5eSRod Evans if ((mval = (val & ~scapset->sc_hw_2)) != 0) {
16108278a5eSRod Evans if (rej) {
16208278a5eSRod Evans static Conv_cap_val_hw2_buf_t cap_buf;
163bebb829dSRod Evans
16408278a5eSRod Evans rej->rej_type = SGS_REJ_HWCAP_2;
16508278a5eSRod Evans rej->rej_str = conv_cap_val_hw2(mval,
16608278a5eSRod Evans M_MACH, 0, &cap_buf);
16708278a5eSRod Evans }
16856deab07SRod Evans return (0);
1697c478bd9Sstevel@tonic-gate }
1707c478bd9Sstevel@tonic-gate return (1);
1717c478bd9Sstevel@tonic-gate }
1727c478bd9Sstevel@tonic-gate
17356726c7eSRobert Mustacchi /*
17456726c7eSRobert Mustacchi * Determine whether HWCAP3 capabilities value is supported.
17556726c7eSRobert Mustacchi */
17656726c7eSRobert Mustacchi int
hwcap3_check(Syscapset * scapset,Xword val,Rej_desc * rej)17756726c7eSRobert Mustacchi hwcap3_check(Syscapset *scapset, Xword val, Rej_desc *rej)
17856726c7eSRobert Mustacchi {
17956726c7eSRobert Mustacchi Xword mval;
18056726c7eSRobert Mustacchi
18156726c7eSRobert Mustacchi /*
18256726c7eSRobert Mustacchi * Ensure that the kernel can cope with the required capabilities.
18356726c7eSRobert Mustacchi */
18456726c7eSRobert Mustacchi if ((mval = (val & ~scapset->sc_hw_3)) != 0) {
18556726c7eSRobert Mustacchi if (rej) {
18656726c7eSRobert Mustacchi static Conv_cap_val_hw3_buf_t cap_buf;
18756726c7eSRobert Mustacchi
18856726c7eSRobert Mustacchi rej->rej_type = SGS_REJ_HWCAP_3;
18956726c7eSRobert Mustacchi rej->rej_str = conv_cap_val_hw3(mval,
19056726c7eSRobert Mustacchi M_MACH, 0, &cap_buf);
19156726c7eSRobert Mustacchi }
19256726c7eSRobert Mustacchi return (0);
19356726c7eSRobert Mustacchi }
19456726c7eSRobert Mustacchi return (1);
19556726c7eSRobert Mustacchi }
19656726c7eSRobert Mustacchi
19756deab07SRod Evans /*
19856deab07SRod Evans * Process any software capabilities.
19956deab07SRod Evans */
20056deab07SRod Evans /* ARGSUSED0 */
20156deab07SRod Evans int
sfcap1_check(Syscapset * scapset,Xword val,Rej_desc * rej)20208278a5eSRod Evans sfcap1_check(Syscapset *scapset, Xword val, Rej_desc *rej)
2037c478bd9Sstevel@tonic-gate {
20456deab07SRod Evans #if defined(_ELF64)
20556deab07SRod Evans /*
20656deab07SRod Evans * A 64-bit executable that started the process can be restricted to a
20756deab07SRod Evans * 32-bit address space. A 64-bit dependency that is restricted to a
20856deab07SRod Evans * 32-bit address space can not be loaded unless the executable has
20956deab07SRod Evans * established this requirement.
21056deab07SRod Evans */
21156deab07SRod Evans if ((val & SF1_SUNW_ADDR32) && ((rtld_flags2 & RT_FL2_ADDR32) == 0)) {
21208278a5eSRod Evans if (rej) {
21308278a5eSRod Evans static Conv_cap_val_sf1_buf_t cap_buf;
214fb1354edSrie
21508278a5eSRod Evans rej->rej_type = SGS_REJ_SFCAP_1;
21608278a5eSRod Evans rej->rej_str = conv_cap_val_sf1(SF1_SUNW_ADDR32,
21708278a5eSRod Evans M_MACH, 0, &cap_buf);
21808278a5eSRod Evans }
21956deab07SRod Evans return (0);
2204464de07SAli Bahrami }
22156deab07SRod Evans #endif
22256deab07SRod Evans return (1);
2237c478bd9Sstevel@tonic-gate }
2247c478bd9Sstevel@tonic-gate
2257c478bd9Sstevel@tonic-gate /*
22608278a5eSRod Evans * Process any platform capability.
22708278a5eSRod Evans */
22808278a5eSRod Evans int
platcap_check(Syscapset * scapset,const char * str,Rej_desc * rej)22908278a5eSRod Evans platcap_check(Syscapset *scapset, const char *str, Rej_desc *rej)
23008278a5eSRod Evans {
23108278a5eSRod Evans /*
23208278a5eSRod Evans * If the platform name hasn't been set, try and obtain it.
23308278a5eSRod Evans */
23408278a5eSRod Evans if ((scapset->sc_plat == NULL) &&
23508278a5eSRod Evans (scapset->sc_platsz == 0))
23608278a5eSRod Evans platform_name(scapset);
23708278a5eSRod Evans
23808278a5eSRod Evans if ((scapset->sc_plat == NULL) ||
23908278a5eSRod Evans (str && strcmp(scapset->sc_plat, str))) {
24008278a5eSRod Evans if (rej) {
24108278a5eSRod Evans /*
24208278a5eSRod Evans * Note, the platform name points to a string within an
24308278a5eSRod Evans * objects string table, and if that object can't be
24408278a5eSRod Evans * loaded, it will be unloaded and thus invalidate the
24508278a5eSRod Evans * string. Duplicate the string here for rejection
24608278a5eSRod Evans * message inheritance.
24708278a5eSRod Evans */
24808278a5eSRod Evans rej->rej_type = SGS_REJ_PLATCAP;
24908278a5eSRod Evans rej->rej_str = stravl_insert(str, 0, 0, 0);
25008278a5eSRod Evans }
25108278a5eSRod Evans return (0);
25208278a5eSRod Evans }
25308278a5eSRod Evans return (1);
25408278a5eSRod Evans }
25508278a5eSRod Evans
25608278a5eSRod Evans /*
25708278a5eSRod Evans * Process any machine capability.
25808278a5eSRod Evans */
25908278a5eSRod Evans int
machcap_check(Syscapset * scapset,const char * str,Rej_desc * rej)26008278a5eSRod Evans machcap_check(Syscapset *scapset, const char *str, Rej_desc *rej)
26108278a5eSRod Evans {
26208278a5eSRod Evans /*
26308278a5eSRod Evans * If the machine name hasn't been set, try and obtain it.
26408278a5eSRod Evans */
26508278a5eSRod Evans if ((scapset->sc_mach == NULL) &&
26608278a5eSRod Evans (scapset->sc_machsz == 0))
26708278a5eSRod Evans machine_name(scapset);
26808278a5eSRod Evans
26908278a5eSRod Evans if ((scapset->sc_mach == NULL) ||
27008278a5eSRod Evans (str && strcmp(scapset->sc_mach, str))) {
27108278a5eSRod Evans if (rej) {
27208278a5eSRod Evans /*
27308278a5eSRod Evans * Note, the machine name points to a string within an
27408278a5eSRod Evans * objects string table, and if that object can't be
27508278a5eSRod Evans * loaded, it will be unloaded and thus invalidate the
27608278a5eSRod Evans * string. Duplicate the string here for rejection
27708278a5eSRod Evans * message inheritance.
27808278a5eSRod Evans */
27908278a5eSRod Evans rej->rej_type = SGS_REJ_MACHCAP;
28008278a5eSRod Evans rej->rej_str = stravl_insert(str, 0, 0, 0);
28108278a5eSRod Evans }
28208278a5eSRod Evans return (0);
28308278a5eSRod Evans }
28408278a5eSRod Evans return (1);
28508278a5eSRod Evans }
28608278a5eSRod Evans
28708278a5eSRod Evans /*
28808278a5eSRod Evans * Generic front-end to capabilities validation.
28908278a5eSRod Evans */
29008278a5eSRod Evans static int
cap_check(Cap * cptr,char * strs,int alt,Fdesc * fdp,Rej_desc * rej)29108278a5eSRod Evans cap_check(Cap *cptr, char *strs, int alt, Fdesc *fdp, Rej_desc *rej)
29208278a5eSRod Evans {
29308278a5eSRod Evans Syscapset *scapset;
29408278a5eSRod Evans int totplat, ivlplat, totmach, ivlmach;
29508278a5eSRod Evans
29608278a5eSRod Evans /*
29708278a5eSRod Evans * If the caller has no capabilities, then the object is valid.
29808278a5eSRod Evans */
29908278a5eSRod Evans if (cptr == NULL)
30008278a5eSRod Evans return (1);
30108278a5eSRod Evans
30208278a5eSRod Evans if (alt)
30308278a5eSRod Evans scapset = alt_scapset;
30408278a5eSRod Evans else
30508278a5eSRod Evans scapset = org_scapset;
30608278a5eSRod Evans
30708278a5eSRod Evans totplat = ivlplat = totmach = ivlmach = 0;
30808278a5eSRod Evans
30908278a5eSRod Evans while (cptr->c_tag != CA_SUNW_NULL) {
31008278a5eSRod Evans Xword val = cptr->c_un.c_val;
31108278a5eSRod Evans char *str;
31208278a5eSRod Evans
31308278a5eSRod Evans switch (cptr->c_tag) {
31408278a5eSRod Evans case CA_SUNW_HW_1:
3154e12d685SRod Evans /*
3164e12d685SRod Evans * Remove any historic values that should not be
3174e12d685SRod Evans * involved with any validation.
3184e12d685SRod Evans */
3194e12d685SRod Evans val &= ~AV_HW1_IGNORE;
3204e12d685SRod Evans
32108278a5eSRod Evans if (hwcap1_check(scapset, val, rej) == 0)
32208278a5eSRod Evans return (0);
32308278a5eSRod Evans if (fdp)
32408278a5eSRod Evans fdp->fd_scapset.sc_hw_1 = val;
32508278a5eSRod Evans break;
32608278a5eSRod Evans case CA_SUNW_SF_1:
32708278a5eSRod Evans if (sfcap1_check(scapset, val, rej) == 0)
32808278a5eSRod Evans return (0);
32908278a5eSRod Evans if (fdp)
33008278a5eSRod Evans fdp->fd_scapset.sc_sf_1 = val;
33108278a5eSRod Evans break;
33208278a5eSRod Evans case CA_SUNW_HW_2:
33308278a5eSRod Evans if (hwcap2_check(scapset, val, rej) == 0)
33408278a5eSRod Evans return (0);
33508278a5eSRod Evans if (fdp)
33608278a5eSRod Evans fdp->fd_scapset.sc_hw_2 = val;
33708278a5eSRod Evans break;
33808278a5eSRod Evans case CA_SUNW_PLAT:
33908278a5eSRod Evans /*
34008278a5eSRod Evans * A capabilities group can define multiple platform
34108278a5eSRod Evans * names that are appropriate. Only if all the names
34208278a5eSRod Evans * are deemed invalid is the group determined
34308278a5eSRod Evans * inappropriate.
34408278a5eSRod Evans */
34508278a5eSRod Evans if (totplat == ivlplat) {
34608278a5eSRod Evans totplat++;
34708278a5eSRod Evans
34808278a5eSRod Evans str = strs + val;
34908278a5eSRod Evans
35008278a5eSRod Evans if (platcap_check(scapset, str, rej) == 0)
35108278a5eSRod Evans ivlplat++;
35208278a5eSRod Evans else if (fdp)
35308278a5eSRod Evans fdp->fd_scapset.sc_plat = str;
35408278a5eSRod Evans }
35508278a5eSRod Evans break;
35608278a5eSRod Evans case CA_SUNW_MACH:
35708278a5eSRod Evans /*
35808278a5eSRod Evans * A capabilities group can define multiple machine
35908278a5eSRod Evans * names that are appropriate. Only if all the names
36008278a5eSRod Evans * are deemed invalid is the group determined
36108278a5eSRod Evans * inappropriate.
36208278a5eSRod Evans */
36308278a5eSRod Evans if (totmach == ivlmach) {
36408278a5eSRod Evans totmach++;
36508278a5eSRod Evans
36608278a5eSRod Evans str = strs + val;
36708278a5eSRod Evans
36808278a5eSRod Evans if (machcap_check(scapset, str, rej) == 0)
36908278a5eSRod Evans ivlmach++;
37008278a5eSRod Evans else if (fdp)
37108278a5eSRod Evans fdp->fd_scapset.sc_mach = str;
37208278a5eSRod Evans }
37308278a5eSRod Evans break;
37408278a5eSRod Evans case CA_SUNW_ID:
37508278a5eSRod Evans /*
37608278a5eSRod Evans * Capabilities identifiers provide for diagnostics,
37708278a5eSRod Evans * but are not attributes that must be compared with
37808278a5eSRod Evans * the system. They are ignored.
37908278a5eSRod Evans */
38008278a5eSRod Evans break;
38156726c7eSRobert Mustacchi case CA_SUNW_HW_3:
38256726c7eSRobert Mustacchi if (hwcap3_check(scapset, val, rej) == 0)
38356726c7eSRobert Mustacchi return (0);
38456726c7eSRobert Mustacchi if (fdp)
38556726c7eSRobert Mustacchi fdp->fd_scapset.sc_hw_3 = val;
38656726c7eSRobert Mustacchi break;
38708278a5eSRod Evans default:
38808278a5eSRod Evans rej->rej_type = SGS_REJ_UNKCAP;
38908278a5eSRod Evans rej->rej_info = cptr->c_tag;
39008278a5eSRod Evans return (0);
39108278a5eSRod Evans }
39208278a5eSRod Evans cptr++;
39308278a5eSRod Evans }
39408278a5eSRod Evans
39508278a5eSRod Evans /*
39608278a5eSRod Evans * If any platform names, or machine names were found, and all were
39708278a5eSRod Evans * invalid, indicate that the object is inappropriate.
39808278a5eSRod Evans */
39908278a5eSRod Evans if ((totplat && (totplat == ivlplat)) ||
40008278a5eSRod Evans (totmach && (totmach == ivlmach)))
40108278a5eSRod Evans return (0);
40208278a5eSRod Evans
40308278a5eSRod Evans return (1);
40408278a5eSRod Evans }
40508278a5eSRod Evans
40667d74cc3SToomas Soome #define HWAVL_RECORDED(n) pnavl_recorded(&capavl, n, 0, NULL)
40708278a5eSRod Evans
40808278a5eSRod Evans /*
40908278a5eSRod Evans * Determine whether a link-map should use alternative system capabilities.
41008278a5eSRod Evans */
41108278a5eSRod Evans static void
cap_check_lmp_init(Rt_map * lmp)41208278a5eSRod Evans cap_check_lmp_init(Rt_map *lmp)
41308278a5eSRod Evans {
41408278a5eSRod Evans int alt = 0;
41508278a5eSRod Evans
41608278a5eSRod Evans /*
41708278a5eSRod Evans * If an alternative set of system capabilities have been established,
41808278a5eSRod Evans * and only specific files should use these alternative system
41908278a5eSRod Evans * capabilities, determine whether this file is one of those specified.
42008278a5eSRod Evans */
42108278a5eSRod Evans if (capavl) {
42208278a5eSRod Evans const char *file;
42308278a5eSRod Evans
42408278a5eSRod Evans /*
42508278a5eSRod Evans * The simplest way to reference a file is to use its file name
42608278a5eSRod Evans * (soname), however try all of the names that this file is
42708278a5eSRod Evans * known by.
42808278a5eSRod Evans */
42908278a5eSRod Evans if ((file = strrchr(NAME(lmp), '/')) != NULL)
43008278a5eSRod Evans file++;
43108278a5eSRod Evans else
43208278a5eSRod Evans file = NULL;
43308278a5eSRod Evans
43408278a5eSRod Evans if ((file && (HWAVL_RECORDED(file) != 0)) ||
43508278a5eSRod Evans (HWAVL_RECORDED(NAME(lmp)) != 0) ||
43608278a5eSRod Evans ((PATHNAME(lmp) != NAME(lmp)) &&
43708278a5eSRod Evans (HWAVL_RECORDED(PATHNAME(lmp)) != 0)))
43808278a5eSRod Evans alt = 1;
43908278a5eSRod Evans
44008278a5eSRod Evans if (alt == 0) {
44108278a5eSRod Evans Aliste idx;
44208278a5eSRod Evans const char *cp;
44308278a5eSRod Evans
44408278a5eSRod Evans for (APLIST_TRAVERSE(ALIAS(lmp), idx, cp)) {
44508278a5eSRod Evans if ((alt = HWAVL_RECORDED(cp)) != 0)
44608278a5eSRod Evans break;
44708278a5eSRod Evans }
44808278a5eSRod Evans }
44908278a5eSRod Evans }
45008278a5eSRod Evans
45108278a5eSRod Evans /*
45208278a5eSRod Evans * Indicate if this link-map should use alternative system capabilities,
45308278a5eSRod Evans * and that the alternative system capabilities check has been carried
45408278a5eSRod Evans * out.
45508278a5eSRod Evans */
45608278a5eSRod Evans if ((org_scapset != alt_scapset) && ((capavl == NULL) || alt))
45708278a5eSRod Evans FLAGS1(lmp) |= FL1_RT_ALTCAP;
45808278a5eSRod Evans FLAGS1(lmp) |= FL1_RT_ALTCHECK;
45908278a5eSRod Evans }
46008278a5eSRod Evans
46108278a5eSRod Evans /*
46208278a5eSRod Evans * Validate the capabilities requirements of a link-map.
46308278a5eSRod Evans *
46408278a5eSRod Evans * This routine is called for main, where a link-map is constructed from the
46508278a5eSRod Evans * mappings returned from exec(), and for any symbol capabilities comparisons.
46608278a5eSRod Evans */
46708278a5eSRod Evans int
cap_check_lmp(Rt_map * lmp,Rej_desc * rej)46808278a5eSRod Evans cap_check_lmp(Rt_map *lmp, Rej_desc *rej)
46908278a5eSRod Evans {
47008278a5eSRod Evans if ((FLAGS1(lmp) & FL1_RT_ALTCHECK) == 0)
47108278a5eSRod Evans cap_check_lmp_init(lmp);
47208278a5eSRod Evans
47308278a5eSRod Evans return (cap_check(CAP(lmp), STRTAB(lmp),
47408278a5eSRod Evans (FLAGS1(lmp) & FL1_RT_ALTCAP), NULL, rej));
47508278a5eSRod Evans }
47608278a5eSRod Evans
47708278a5eSRod Evans /*
47808278a5eSRod Evans * Validate the capabilities requirements of a file under inspection.
47908278a5eSRod Evans * This file is still under the early stages of loading, and has no link-map
48008278a5eSRod Evans * yet. The file must have an object capabilities definition (PT_SUNWCAP), to
48108278a5eSRod Evans * have gotten us here. The logic here is the same as cap_check_lmp().
48208278a5eSRod Evans */
48308278a5eSRod Evans int
cap_check_fdesc(Fdesc * fdp,Cap * cptr,char * strs,Rej_desc * rej)48408278a5eSRod Evans cap_check_fdesc(Fdesc *fdp, Cap *cptr, char *strs, Rej_desc *rej)
48508278a5eSRod Evans {
48608278a5eSRod Evans int alt = 0;
48708278a5eSRod Evans
48808278a5eSRod Evans /*
48908278a5eSRod Evans * If an alternative set of system capabilities have been established,
49008278a5eSRod Evans * and only specific files should use these alternative system
49108278a5eSRod Evans * capabilities, determine whether this file is one of those specified.
49208278a5eSRod Evans */
49308278a5eSRod Evans if (capavl) {
49408278a5eSRod Evans const char *file;
49508278a5eSRod Evans
49608278a5eSRod Evans /*
49708278a5eSRod Evans * The simplest way to reference a file is to use its file name
49808278a5eSRod Evans * (soname), however try all of the names that this file is
49908278a5eSRod Evans * known by.
50008278a5eSRod Evans */
5011c1abfbcSRod Evans if (fdp->fd_oname &&
5021c1abfbcSRod Evans ((file = strrchr(fdp->fd_oname, '/')) != NULL))
50308278a5eSRod Evans file++;
50408278a5eSRod Evans else
50508278a5eSRod Evans file = NULL;
50608278a5eSRod Evans
50708278a5eSRod Evans if ((file && (HWAVL_RECORDED(file) != 0)) ||
50808278a5eSRod Evans (fdp->fd_oname && (HWAVL_RECORDED(fdp->fd_oname) != 0)) ||
50908278a5eSRod Evans (fdp->fd_nname && (HWAVL_RECORDED(fdp->fd_nname) != 0)) ||
51008278a5eSRod Evans (fdp->fd_pname && (fdp->fd_pname != fdp->fd_nname) &&
51108278a5eSRod Evans (HWAVL_RECORDED(fdp->fd_pname) != 0)))
51208278a5eSRod Evans alt = 1;
51308278a5eSRod Evans }
51408278a5eSRod Evans
51508278a5eSRod Evans /*
51608278a5eSRod Evans * Indicate if this file descriptor should use alternative system
51708278a5eSRod Evans * capabilities, and that the alternative system capabilities check has
51808278a5eSRod Evans * been carried out.
51908278a5eSRod Evans */
52008278a5eSRod Evans if ((org_scapset != alt_scapset) && ((capavl == NULL) || alt))
52108278a5eSRod Evans fdp->fd_flags |= FLG_FD_ALTCAP;
52208278a5eSRod Evans fdp->fd_flags |= FLG_FD_ALTCHECK;
52308278a5eSRod Evans
52408278a5eSRod Evans /*
52508278a5eSRod Evans * Verify that the required capabilities are supported by the reference.
52608278a5eSRod Evans */
52708278a5eSRod Evans return (cap_check(cptr, strs, (fdp->fd_flags & FLG_FD_ALTCAP),
52808278a5eSRod Evans fdp, rej));
52908278a5eSRod Evans }
53008278a5eSRod Evans
53108278a5eSRod Evans /*
53208278a5eSRod Evans * Free a file descriptor list. As part of building this list, the original
53308278a5eSRod Evans * names for each capabilities candidate were duplicated for use in later
53408278a5eSRod Evans * diagnostics. These names need to be freed.
53508278a5eSRod Evans */
53608278a5eSRod Evans void
free_fd(Alist * fdalp)53708278a5eSRod Evans free_fd(Alist *fdalp)
53808278a5eSRod Evans {
53908278a5eSRod Evans if (fdalp) {
54008278a5eSRod Evans Aliste idx;
54108278a5eSRod Evans Fdesc *fdp;
54208278a5eSRod Evans
54308278a5eSRod Evans for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
54408278a5eSRod Evans if (fdp->fd_oname)
54508278a5eSRod Evans free((void *)fdp->fd_oname);
54608278a5eSRod Evans }
54708278a5eSRod Evans free(fdalp);
54808278a5eSRod Evans }
54908278a5eSRod Evans }
55008278a5eSRod Evans
55108278a5eSRod Evans /*
55208278a5eSRod Evans * When $CAPABILITY (or $HWCAP) is used to represent dependencies, take the
55308278a5eSRod Evans * associated directory and analyze all the files it contains.
5547c478bd9Sstevel@tonic-gate */
555cce0e03bSab static int
cap_dir(Alist ** fdalpp,Lm_list * lml,const char * dname,Rt_map * clmp,uint_t flags,Rej_desc * rej,int * in_nfavl)55608278a5eSRod Evans cap_dir(Alist **fdalpp, Lm_list *lml, const char *dname, Rt_map *clmp,
5579aa23310Srie uint_t flags, Rej_desc *rej, int *in_nfavl)
5587c478bd9Sstevel@tonic-gate {
5597c478bd9Sstevel@tonic-gate char path[PATH_MAX], *dst;
5607c478bd9Sstevel@tonic-gate const char *src;
5617c478bd9Sstevel@tonic-gate DIR *dir;
5627c478bd9Sstevel@tonic-gate struct dirent *dirent;
563cce0e03bSab Alist *fdalp = NULL;
5641c1abfbcSRod Evans Aliste idx;
5651c1abfbcSRod Evans Fdesc *fdp;
5667c478bd9Sstevel@tonic-gate int error = 0;
5677c478bd9Sstevel@tonic-gate
5687c478bd9Sstevel@tonic-gate /*
5697c478bd9Sstevel@tonic-gate * Access the directory in preparation for reading its entries. If
5707c478bd9Sstevel@tonic-gate * successful, establish the initial pathname.
5717c478bd9Sstevel@tonic-gate */
57256deab07SRod Evans if ((dir = opendir(dname)) == NULL) {
5737c478bd9Sstevel@tonic-gate Rej_desc _rej = { 0 };
5747c478bd9Sstevel@tonic-gate
5757c478bd9Sstevel@tonic-gate _rej.rej_type = SGS_REJ_STR;
57656deab07SRod Evans _rej.rej_name = dname;
5777c478bd9Sstevel@tonic-gate _rej.rej_str = strerror(errno);
578ba2be530Sab DBG_CALL(Dbg_file_rejected(lml, &_rej, M_MACH));
57931fdd7caSab rejection_inherit(rej, &_rej);
5807c478bd9Sstevel@tonic-gate return (0);
5817c478bd9Sstevel@tonic-gate }
5827c478bd9Sstevel@tonic-gate
58356deab07SRod Evans for (dst = path, src = dname; *src; dst++, src++)
5847c478bd9Sstevel@tonic-gate *dst = *src;
5857c478bd9Sstevel@tonic-gate *dst++ = '/';
5867c478bd9Sstevel@tonic-gate
5877c478bd9Sstevel@tonic-gate /*
5887c478bd9Sstevel@tonic-gate * Read each entry from the directory and determine whether it is a
5897c478bd9Sstevel@tonic-gate * valid ELF file.
5907c478bd9Sstevel@tonic-gate */
5917c478bd9Sstevel@tonic-gate while ((dirent = readdir(dir)) != NULL) {
59256deab07SRod Evans const char *file = dirent->d_name;
5937247f888Srie char *_dst;
59456deab07SRod Evans Fdesc fd = { 0 };
5957c478bd9Sstevel@tonic-gate Rej_desc _rej = { 0 };
59656deab07SRod Evans Pdesc pd = { 0 };
5977c478bd9Sstevel@tonic-gate
5987c478bd9Sstevel@tonic-gate /*
5997c478bd9Sstevel@tonic-gate * Ignore "." and ".." entries.
6007c478bd9Sstevel@tonic-gate */
6017c478bd9Sstevel@tonic-gate if ((file[0] == '.') && ((file[1] == '\0') ||
6027c478bd9Sstevel@tonic-gate ((file[1] == '.') && (file[2] == '\0'))))
6037c478bd9Sstevel@tonic-gate continue;
6047c478bd9Sstevel@tonic-gate
6057c478bd9Sstevel@tonic-gate /*
60656deab07SRod Evans * Complete the full pathname.
6077c478bd9Sstevel@tonic-gate */
6087c478bd9Sstevel@tonic-gate for (_dst = dst, src = file, file = dst; *src; _dst++, src++)
6097c478bd9Sstevel@tonic-gate *_dst = *src;
6107c478bd9Sstevel@tonic-gate *_dst = '\0';
6117c478bd9Sstevel@tonic-gate
61256deab07SRod Evans /*
61356deab07SRod Evans * Trace the inspection of this file, and determine any
61456deab07SRod Evans * auditor substitution.
61556deab07SRod Evans */
61656deab07SRod Evans pd.pd_pname = path;
61756deab07SRod Evans pd.pd_flags = PD_FLG_PNSLASH;
6188521e5e6Srie
61956deab07SRod Evans if (load_trace(lml, &pd, clmp, &fd) == NULL)
6207c478bd9Sstevel@tonic-gate continue;
6217c478bd9Sstevel@tonic-gate
6228521e5e6Srie /*
6238521e5e6Srie * Note, all directory entries are processed by find_path(),
6248521e5e6Srie * even entries that are directories themselves. This single
6258521e5e6Srie * point for control keeps the number of stat()'s down, and
6268521e5e6Srie * provides a single point for error diagnostics.
6278521e5e6Srie */
62856deab07SRod Evans if (find_path(lml, clmp, flags, &fd, &_rej, in_nfavl) == 0) {
62931fdd7caSab rejection_inherit(rej, &_rej);
6308521e5e6Srie continue;
6318521e5e6Srie }
6327c478bd9Sstevel@tonic-gate
63308278a5eSRod Evans DBG_CALL(Dbg_cap_candidate(lml, fd.fd_nname));
6348521e5e6Srie
6358521e5e6Srie /*
63608278a5eSRod Evans * If this object has already been loaded, save the capabilities
63708278a5eSRod Evans * for later sorting. Otherwise we have a new candidate.
6388521e5e6Srie */
63956deab07SRod Evans if (fd.fd_lmp)
64008278a5eSRod Evans fd.fd_scapset = CAPSET(fd.fd_lmp);
6411c1abfbcSRod Evans fd.fd_lml = lml;
6428521e5e6Srie
64308278a5eSRod Evans /*
64408278a5eSRod Evans * Duplicate the original name, as this may be required for
64508278a5eSRod Evans * later diagnostics. Keep a copy of the file descriptor for
64608278a5eSRod Evans * analysis once all capabilities candidates have been
64708278a5eSRod Evans * determined.
64808278a5eSRod Evans */
64908278a5eSRod Evans if (((fd.fd_oname = strdup(fd.fd_oname)) == NULL) ||
65008278a5eSRod Evans (alist_append(&fdalp, &fd, sizeof (Fdesc),
65108278a5eSRod Evans AL_CNT_CAP) == NULL)) {
6528521e5e6Srie error = 1;
6538521e5e6Srie break;
6547c478bd9Sstevel@tonic-gate }
6557c478bd9Sstevel@tonic-gate }
6567c478bd9Sstevel@tonic-gate (void) closedir(dir);
6577c478bd9Sstevel@tonic-gate
6587c478bd9Sstevel@tonic-gate /*
6597c478bd9Sstevel@tonic-gate * If no objects have been found, we're done. Also, if an allocation
6607c478bd9Sstevel@tonic-gate * error occurred while processing any object, remove any objects that
6617c478bd9Sstevel@tonic-gate * had already been added to the list and return.
6627c478bd9Sstevel@tonic-gate */
663cce0e03bSab if ((fdalp == NULL) || error) {
66456deab07SRod Evans if (fdalp)
66508278a5eSRod Evans free_fd(fdalp);
6667c478bd9Sstevel@tonic-gate return (0);
6677c478bd9Sstevel@tonic-gate }
6687c478bd9Sstevel@tonic-gate
6697c478bd9Sstevel@tonic-gate /*
6707c478bd9Sstevel@tonic-gate * Having processed and retained all candidates from this directory,
6717c478bd9Sstevel@tonic-gate * sort them, based on the precedence of their hardware capabilities.
6727c478bd9Sstevel@tonic-gate */
673cce0e03bSab qsort(fdalp->al_data, fdalp->al_nitems, fdalp->al_size, compare);
6747c478bd9Sstevel@tonic-gate
6751c1abfbcSRod Evans /*
6761c1abfbcSRod Evans * If any objects were found to have the same capabilities, then these
6771c1abfbcSRod Evans * objects must be rejected, as we can't tell which object is more
6781c1abfbcSRod Evans * appropriate.
6791c1abfbcSRod Evans */
6801c1abfbcSRod Evans for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
6811c1abfbcSRod Evans if (fdp->fd_flags & FLG_FD_IGNORE)
6821c1abfbcSRod Evans alist_delete(fdalp, &idx);
6831c1abfbcSRod Evans }
6841c1abfbcSRod Evans
6851c1abfbcSRod Evans if (fdalp->al_nitems == 0) {
6861c1abfbcSRod Evans free_fd(fdalp);
6871c1abfbcSRod Evans return (0);
6881c1abfbcSRod Evans }
6891c1abfbcSRod Evans
6907c478bd9Sstevel@tonic-gate *fdalpp = fdalp;
6917c478bd9Sstevel@tonic-gate return (1);
6927c478bd9Sstevel@tonic-gate }
6937c478bd9Sstevel@tonic-gate
69456deab07SRod 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)69508278a5eSRod Evans cap_filtees(Alist **alpp, Aliste oidx, const char *dir, Aliste nlmco,
6962020b2b6SRod Evans Rt_map *flmp, Rt_map *clmp, const char *ref, int mode, uint_t flags,
6972020b2b6SRod Evans int *in_nfavl)
6987c478bd9Sstevel@tonic-gate {
699cce0e03bSab Alist *fdalp = NULL;
700cce0e03bSab Aliste idx;
7017c478bd9Sstevel@tonic-gate Fdesc *fdp;
7027c478bd9Sstevel@tonic-gate Lm_list *lml = LIST(flmp);
7037c478bd9Sstevel@tonic-gate int unused = 0;
7047c478bd9Sstevel@tonic-gate Rej_desc rej = { 0 };
7057c478bd9Sstevel@tonic-gate
70608278a5eSRod Evans if (cap_dir(&fdalp, lml, dir, flmp, flags, &rej, in_nfavl) == 0)
7077c478bd9Sstevel@tonic-gate return (0);
7087c478bd9Sstevel@tonic-gate
7097c478bd9Sstevel@tonic-gate /*
7107c478bd9Sstevel@tonic-gate * Now complete the mapping of each of the ordered objects, adding
71156deab07SRod Evans * each object to a new pathname descriptor.
7127c478bd9Sstevel@tonic-gate */
713cce0e03bSab for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
7147c478bd9Sstevel@tonic-gate Rt_map *nlmp;
715481bba9eSRod Evans Grp_hdl *ghp = NULL;
71656deab07SRod Evans Pdesc *pdp;
717fb1354edSrie int audit = 0;
7187c478bd9Sstevel@tonic-gate
71956deab07SRod Evans if (unused)
7207c478bd9Sstevel@tonic-gate continue;
7217c478bd9Sstevel@tonic-gate
7227c478bd9Sstevel@tonic-gate /*
7237c478bd9Sstevel@tonic-gate * Complete mapping the file, obtaining a handle, and continue
7247c478bd9Sstevel@tonic-gate * to analyze the object, establishing dependencies and
7257c478bd9Sstevel@tonic-gate * relocating. Remove the file descriptor at this point, as it
7267c478bd9Sstevel@tonic-gate * is no longer required.
7277c478bd9Sstevel@tonic-gate */
7285aefb655Srie DBG_CALL(Dbg_file_filtee(lml, NAME(flmp), fdp->fd_nname, 0));
7297c478bd9Sstevel@tonic-gate
73056deab07SRod Evans nlmp = load_path(lml, nlmco, flmp, mode,
7312017c965SRod Evans (flags | FLG_RT_PUBHDL), &ghp, fdp, &rej, in_nfavl);
732481bba9eSRod Evans if (nlmp == NULL)
73302ca3e02Srie continue;
7347c478bd9Sstevel@tonic-gate
7357c478bd9Sstevel@tonic-gate /*
73656deab07SRod Evans * Create a new pathname descriptor to represent this filtee,
73756deab07SRod Evans * and insert this descriptor in the Alist following the
73856deab07SRod Evans * hardware descriptor that seeded this processing.
7397c478bd9Sstevel@tonic-gate */
74056deab07SRod Evans if ((pdp = alist_insert(alpp, 0, sizeof (Pdesc),
74156deab07SRod Evans AL_CNT_FILTEES, ++oidx)) == NULL) {
74256deab07SRod Evans if (ghp)
743481bba9eSRod Evans remove_lmc(lml, flmp, nlmco, NAME(nlmp));
744fb1354edSrie return (0);
7457c478bd9Sstevel@tonic-gate }
746fb1354edSrie
74756deab07SRod Evans pdp->pd_pname = NAME(nlmp);
74856deab07SRod Evans pdp->pd_plen = strlen(NAME(nlmp));
7497c478bd9Sstevel@tonic-gate
750fb1354edSrie /*
751fb1354edSrie * Establish the filter handle to prevent any recursion.
752fb1354edSrie */
753fb1354edSrie if (nlmp && ghp) {
754fb1354edSrie ghp->gh_flags |= GPH_FILTEE;
75556deab07SRod Evans pdp->pd_info = (void *)ghp;
756fb1354edSrie }
757fb1354edSrie
758fb1354edSrie /*
759fb1354edSrie * Audit the filter/filtee established. A return of 0
760fb1354edSrie * indicates the auditor wishes to ignore this filtee.
761fb1354edSrie */
762fb1354edSrie if (nlmp && (lml->lm_tflags | FLAGS1(flmp)) &
763fb1354edSrie LML_TFLG_AUD_OBJFILTER) {
764fb1354edSrie if (audit_objfilter(flmp, ref, nlmp, 0) == 0) {
765fb1354edSrie audit = 1;
766481bba9eSRod Evans nlmp = NULL;
767fb1354edSrie }
768fb1354edSrie }
769fb1354edSrie
770fb1354edSrie /*
771fb1354edSrie * Finish processing the objects associated with this request.
772fb1354edSrie */
77356deab07SRod Evans if (nlmp && ghp && (((nlmp = analyze_lmc(lml, nlmco, nlmp,
7742020b2b6SRod Evans clmp, in_nfavl)) == NULL) ||
7759aa23310Srie (relocate_lmc(lml, nlmco, flmp, nlmp, in_nfavl) == 0)))
77656deab07SRod Evans nlmp = NULL;
777fb1354edSrie
778fb1354edSrie /*
77902ca3e02Srie * If the filtee has been successfully processed, then create
78002ca3e02Srie * an association between the filter and the filtee. This
781fb1354edSrie * association provides sufficient information to tear down the
782fb1354edSrie * filter and filtee if necessary.
783fb1354edSrie */
7848af2c5b9Srie DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD));
7852017c965SRod Evans if (nlmp && ghp &&
7862017c965SRod Evans (hdl_add(ghp, flmp, GPD_FILTER, NULL) == NULL))
78756deab07SRod Evans nlmp = NULL;
788fb1354edSrie
7897c478bd9Sstevel@tonic-gate /*
7907c478bd9Sstevel@tonic-gate * If this object is marked an end-filtee, we're done.
7917c478bd9Sstevel@tonic-gate */
792fb1354edSrie if (nlmp && ghp && (FLAGS1(nlmp) & FL1_RT_ENDFILTE))
7937c478bd9Sstevel@tonic-gate unused = 1;
794fb1354edSrie
795fb1354edSrie /*
79602ca3e02Srie * If this filtee loading has failed, generate a diagnostic.
79756deab07SRod Evans * Null out the path name descriptor entry, and continue the
79856deab07SRod Evans * search.
799fb1354edSrie */
80056deab07SRod Evans if (nlmp == NULL) {
80156deab07SRod Evans DBG_CALL(Dbg_file_filtee(lml, 0, pdp->pd_pname, audit));
80256deab07SRod Evans
80302ca3e02Srie /*
80402ca3e02Srie * If attempting to load this filtee required a new
80502ca3e02Srie * link-map control list to which this request has
80602ca3e02Srie * added objects, then remove all the objects that
80702ca3e02Srie * have been associated to this request.
80802ca3e02Srie */
809481bba9eSRod Evans if (nlmco != ALIST_OFF_DATA)
810481bba9eSRod Evans remove_lmc(lml, flmp, nlmco, pdp->pd_pname);
811fb1354edSrie
81256deab07SRod Evans pdp->pd_plen = 0;
813481bba9eSRod Evans pdp->pd_info = NULL;
814fb1354edSrie }
8157c478bd9Sstevel@tonic-gate }
8167c478bd9Sstevel@tonic-gate
81708278a5eSRod Evans free_fd(fdalp);
81856deab07SRod Evans return (1);
8197c478bd9Sstevel@tonic-gate }
8207c478bd9Sstevel@tonic-gate
8217c478bd9Sstevel@tonic-gate /*
82208278a5eSRod Evans * Load an individual capabilities object.
8237c478bd9Sstevel@tonic-gate */
8247c478bd9Sstevel@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)82508278a5eSRod Evans load_cap(Lm_list *lml, Aliste lmco, const char *dir, Rt_map *clmp,
8269aa23310Srie uint_t mode, uint_t flags, Grp_hdl **hdl, Rej_desc *rej, int *in_nfavl)
8277c478bd9Sstevel@tonic-gate {
82856deab07SRod Evans Alist *fdalp = NULL;
82956deab07SRod Evans Aliste idx;
83056deab07SRod Evans Fdesc *fdp;
83156deab07SRod Evans int found = 0;
832481bba9eSRod Evans Rt_map *lmp = NULL;
8337c478bd9Sstevel@tonic-gate
8347c478bd9Sstevel@tonic-gate /*
83556deab07SRod Evans * Obtain the sorted list of hardware capabilities objects available.
8367c478bd9Sstevel@tonic-gate */
83708278a5eSRod Evans if (cap_dir(&fdalp, lml, dir, clmp, flags, rej, in_nfavl) == 0)
83856deab07SRod Evans return (NULL);
8397c478bd9Sstevel@tonic-gate
8407c478bd9Sstevel@tonic-gate /*
8417c478bd9Sstevel@tonic-gate * From the list of hardware capability objects, use the first and
8427c478bd9Sstevel@tonic-gate * discard the rest.
8437c478bd9Sstevel@tonic-gate */
844cce0e03bSab for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
84556deab07SRod Evans Fdesc fd = *fdp;
8467c478bd9Sstevel@tonic-gate
84756deab07SRod Evans if ((found == 0) && ((lmp = load_path(lml, lmco, clmp, mode,
848481bba9eSRod Evans flags, hdl, &fd, rej, in_nfavl)) != NULL))
84956deab07SRod Evans found++;
8507c478bd9Sstevel@tonic-gate }
8517c478bd9Sstevel@tonic-gate
85208278a5eSRod Evans free_fd(fdalp);
8537c478bd9Sstevel@tonic-gate return (lmp);
8547c478bd9Sstevel@tonic-gate }
85508278a5eSRod Evans
85608278a5eSRod Evans /*
85708278a5eSRod Evans * Use a case insensitive string match when looking up capability mask
85808278a5eSRod Evans * values by name, and omit the AV_ prefix.
85908278a5eSRod Evans */
86008278a5eSRod Evans #define ELFCAP_STYLE ELFCAP_STYLE_LC | ELFCAP_STYLE_F_ICMP
86108278a5eSRod Evans
86208278a5eSRod Evans /*
86308278a5eSRod Evans * To aid in the development and testing of capabilities, an alternative system
86408278a5eSRod Evans * capabilities group can be specified. This alternative set is initialized
86508278a5eSRod Evans * from the system capabilities that are normally used to validate all object
86608278a5eSRod Evans * loading. However, the user can disable, enable or override flags within
86708278a5eSRod Evans * this alternative set, and thus affect object loading.
86808278a5eSRod Evans *
86908278a5eSRod Evans * This technique is usually combined with defining the family of objects
87008278a5eSRod Evans * that should be compared against this alternative set. Without defining the
87108278a5eSRod Evans * family of objects, all objects loaded by ld.so.1 are validated against the
87208278a5eSRod Evans * alternative set. This can prevent the loading of critical system objects
87308278a5eSRod Evans * like libc, and thus prevent process execution.
87408278a5eSRod Evans */
87508278a5eSRod Evans typedef enum {
87608278a5eSRod Evans CAP_OVERRIDE = 0, /* override existing capabilities */
87708278a5eSRod Evans CAP_ENABLE = 1, /* enable capabilities */
87808278a5eSRod Evans CAP_DISABLE = 2 /* disable capabilities */
87908278a5eSRod Evans } cap_mode;
88008278a5eSRod Evans
88156726c7eSRobert Mustacchi /*
88256726c7eSRobert Mustacchi * The override indexes originally followed the values of CA_SUNW_HW_1, SF_1,
88356726c7eSRobert Mustacchi * etc.
88456726c7eSRobert Mustacchi */
88556726c7eSRobert Mustacchi typedef enum {
88656726c7eSRobert Mustacchi CAP_OVR_HW_1 = 0,
88756726c7eSRobert Mustacchi CAP_OVR_SF_1,
88856726c7eSRobert Mustacchi CAP_OVR_HW_2,
88956726c7eSRobert Mustacchi CAP_OVR_HW_3,
89056726c7eSRobert Mustacchi CAP_OVR_MAX
89156726c7eSRobert Mustacchi } cap_index_t;
89256726c7eSRobert Mustacchi
89308278a5eSRod Evans static struct {
89408278a5eSRod Evans elfcap_mask_t cs_val[3]; /* value settings, and indicator for */
89508278a5eSRod Evans int cs_set[3]; /* OVERRIDE, ENABLE and DISABLE */
89608278a5eSRod Evans elfcap_mask_t *cs_aval; /* alternative variable for final */
89708278a5eSRod Evans /* update */
89856726c7eSRobert Mustacchi } cap_settings[CAP_OVR_MAX] = {
89908278a5eSRod Evans { { 0, 0, 0 }, { 0, 0, 0 }, NULL }, /* CA_SUNW_HW_1 */
90008278a5eSRod Evans { { 0, 0, 0 }, { 0, 0, 0 }, NULL }, /* CA_SUNW_SF_1 */
90156726c7eSRobert Mustacchi { { 0, 0, 0 }, { 0, 0, 0 }, NULL }, /* CA_SUNW_HW_2 */
90256726c7eSRobert Mustacchi { { 0, 0, 0 }, { 0, 0, 0 }, NULL } /* CA_SUNW_HW_3 */
90308278a5eSRod Evans };
90408278a5eSRod Evans
90508278a5eSRod Evans static int
cap_modify(Xword tag,const char * str)90608278a5eSRod Evans cap_modify(Xword tag, const char *str)
90708278a5eSRod Evans {
90808278a5eSRod Evans char *caps, *ptr, *next;
90908278a5eSRod Evans cap_mode mode = CAP_OVERRIDE;
91056726c7eSRobert Mustacchi cap_index_t ndx;
91108278a5eSRod Evans
91208278a5eSRod Evans if ((caps = strdup(str)) == NULL)
91308278a5eSRod Evans return (0);
91408278a5eSRod Evans
915cc4ec439SRichard Lowe for (ptr = strtok_r(caps, MSG_ORIG(MSG_CAP_DELIMIT), &next);
916cc4ec439SRichard Lowe ptr != NULL;
917cc4ec439SRichard Lowe ptr = strtok_r(NULL, MSG_ORIG(MSG_CAP_DELIMIT), &next)) {
91808278a5eSRod Evans Xword val = 0;
91908278a5eSRod Evans
92008278a5eSRod Evans /*
92108278a5eSRod Evans * Determine whether this token should be enabled (+),
92208278a5eSRod Evans * disabled (-), or override any existing settings.
92308278a5eSRod Evans */
92408278a5eSRod Evans if (*ptr == '+') {
92508278a5eSRod Evans mode = CAP_ENABLE;
92608278a5eSRod Evans ptr++;
92708278a5eSRod Evans } else if (*ptr == '-') {
92808278a5eSRod Evans mode = CAP_DISABLE;
92908278a5eSRod Evans ptr++;
93008278a5eSRod Evans }
93108278a5eSRod Evans
93208278a5eSRod Evans /*
93308278a5eSRod Evans * Process the capabilities as directed by the calling tag.
93408278a5eSRod Evans */
93508278a5eSRod Evans switch (tag) {
93608278a5eSRod Evans case CA_SUNW_HW_1:
93708278a5eSRod Evans /*
93808278a5eSRod Evans * Determine whether the capabilities string matches
93908278a5eSRod Evans * a known hardware capability mask. Note, the caller
94008278a5eSRod Evans * indicates that these are hardware capabilities by
94108278a5eSRod Evans * passing in the CA_SUNW_HW_1 tag. However, the
94256726c7eSRobert Mustacchi * tokens could be CA_SUNW_HW_1, CA_SUNW_HW_2, or
94356726c7eSRobert Mustacchi * CA_SUNW_HW_3.
94408278a5eSRod Evans */
94556726c7eSRobert Mustacchi if ((val = (Xword)elfcap_hw3_from_str(ELFCAP_STYLE,
94656726c7eSRobert Mustacchi ptr, M_MACH)) != 0) {
94756726c7eSRobert Mustacchi ndx = CAP_OVR_HW_3;
94856726c7eSRobert Mustacchi break;
94956726c7eSRobert Mustacchi }
95008278a5eSRod Evans if ((val = (Xword)elfcap_hw2_from_str(ELFCAP_STYLE,
95108278a5eSRod Evans ptr, M_MACH)) != 0) {
95256726c7eSRobert Mustacchi ndx = CAP_OVR_HW_2;
95308278a5eSRod Evans break;
95408278a5eSRod Evans }
95508278a5eSRod Evans if ((val = (Xword)elfcap_hw1_from_str(ELFCAP_STYLE,
95608278a5eSRod Evans ptr, M_MACH)) != 0)
95756726c7eSRobert Mustacchi ndx = CAP_OVR_HW_1;
95808278a5eSRod Evans break;
95908278a5eSRod Evans case CA_SUNW_SF_1:
96008278a5eSRod Evans /*
96108278a5eSRod Evans * Determine whether the capabilities string matches a
96208278a5eSRod Evans * known software capability mask. Note, the callers
96308278a5eSRod Evans * indication of what capabilities to process are
96408278a5eSRod Evans * triggered by a tag of CA_SUNW_SF_1, but the tokens
96508278a5eSRod Evans * processed could be CA_SUNW_SF_1, CA_SUNW_SF_2, etc.
96608278a5eSRod Evans */
96708278a5eSRod Evans if ((val = (Xword)elfcap_sf1_from_str(ELFCAP_STYLE,
96808278a5eSRod Evans ptr, M_MACH)) != 0)
96956726c7eSRobert Mustacchi ndx = CAP_OVR_SF_1;
97008278a5eSRod Evans break;
97108278a5eSRod Evans }
97208278a5eSRod Evans
97308278a5eSRod Evans /*
97408278a5eSRod Evans * If a capabilities token has not been matched, interpret the
97508278a5eSRod Evans * string as a number. To provide for setting the various
97608278a5eSRod Evans * families (CA_SUNW_HW_1, CA_SUNW_HW_2), the number can be
97708278a5eSRod Evans * prefixed with the (bracketed) family index.
97808278a5eSRod Evans *
97908278a5eSRod Evans * LD_HWCAP=[1]0x40 sets CA_SUNW_HW_1 with 0x40
98008278a5eSRod Evans * LD_HWCAP=[2]0x80 sets CA_SUNW_HW_2 with 0x80
98156726c7eSRobert Mustacchi * LD_HWCAP=[3]0x44 sets CA_SUNW_HW_3 with 0x44
98208278a5eSRod Evans *
98308278a5eSRod Evans * Invalid indexes are ignored.
98408278a5eSRod Evans */
98508278a5eSRod Evans if (val == 0) {
9869d4bc394SRichard Lowe char *end;
9879d4bc394SRichard Lowe
98808278a5eSRod Evans if ((*ptr == '[') && (*(ptr + 2) == ']')) {
98908278a5eSRod Evans if (*(ptr + 1) == '1') {
99056726c7eSRobert Mustacchi ndx = CAP_OVR_HW_1;
99108278a5eSRod Evans ptr += 3;
99256726c7eSRobert Mustacchi } else if (*(ptr + 1) == '3') {
99356726c7eSRobert Mustacchi if (tag == CA_SUNW_HW_1) {
99456726c7eSRobert Mustacchi ndx = CAP_OVR_HW_3;
99556726c7eSRobert Mustacchi ptr += 3;
99656726c7eSRobert Mustacchi } else {
99756726c7eSRobert Mustacchi /* invalid index */
99856726c7eSRobert Mustacchi continue;
99956726c7eSRobert Mustacchi }
100008278a5eSRod Evans } else if (*(ptr + 1) == '2') {
100108278a5eSRod Evans if (tag == CA_SUNW_HW_1) {
100256726c7eSRobert Mustacchi ndx = CAP_OVR_HW_2;
100308278a5eSRod Evans ptr += 3;
100408278a5eSRod Evans } else {
100508278a5eSRod Evans /* invalid index */
100608278a5eSRod Evans continue;
100708278a5eSRod Evans }
100808278a5eSRod Evans } else {
100908278a5eSRod Evans /* invalid index */
101008278a5eSRod Evans continue;
101108278a5eSRod Evans }
101256726c7eSRobert Mustacchi } else {
101356726c7eSRobert Mustacchi ndx = tag - 1;
101456726c7eSRobert Mustacchi }
101508278a5eSRod Evans
101608278a5eSRod Evans errno = 0;
10179d4bc394SRichard Lowe if (((val = strtol(ptr, &end, 16)) == 0) && errno)
101808278a5eSRod Evans continue;
10199d4bc394SRichard Lowe
10209d4bc394SRichard Lowe /*
10219d4bc394SRichard Lowe * If the value wasn't an entirely valid hexadecimal
10229d4bc394SRichard Lowe * integer, assume it was intended as a capability
10239d4bc394SRichard Lowe * name and skip it.
10249d4bc394SRichard Lowe */
10259d4bc394SRichard Lowe if (*end != '\0') {
10269d4bc394SRichard Lowe eprintf(NULL, ERR_WARNING,
10279d4bc394SRichard Lowe MSG_INTL(MSG_CAP_IGN_UNKCAP), ptr);
10289d4bc394SRichard Lowe continue;
10299d4bc394SRichard Lowe }
103008278a5eSRod Evans }
10319d4bc394SRichard Lowe
103256726c7eSRobert Mustacchi cap_settings[ndx].cs_val[mode] |= val;
103356726c7eSRobert Mustacchi cap_settings[ndx].cs_set[mode]++;
103408278a5eSRod Evans
1035cc4ec439SRichard Lowe }
103608278a5eSRod Evans
103708278a5eSRod Evans /*
103808278a5eSRod Evans * If the "override" token was supplied, set the alternative
103908278a5eSRod Evans * system capabilities, then enable or disable others.
104008278a5eSRod Evans */
104156726c7eSRobert Mustacchi for (ndx = 0; ndx < CAP_OVR_MAX; ndx++) {
104208278a5eSRod Evans if (cap_settings[ndx].cs_set[CAP_OVERRIDE])
104308278a5eSRod Evans *(cap_settings[ndx].cs_aval) =
104408278a5eSRod Evans cap_settings[ndx].cs_val[CAP_OVERRIDE];
104508278a5eSRod Evans if (cap_settings[ndx].cs_set[CAP_ENABLE])
104608278a5eSRod Evans *(cap_settings[ndx].cs_aval) |=
104708278a5eSRod Evans cap_settings[ndx].cs_val[CAP_ENABLE];
104808278a5eSRod Evans if (cap_settings[ndx].cs_set[CAP_DISABLE])
104908278a5eSRod Evans *(cap_settings[ndx].cs_aval) &=
105008278a5eSRod Evans ~cap_settings[ndx].cs_val[CAP_DISABLE];
105108278a5eSRod Evans }
105208278a5eSRod Evans free(caps);
105308278a5eSRod Evans return (1);
105408278a5eSRod Evans }
105508278a5eSRod Evans #undef ELFCAP_STYLE
105608278a5eSRod Evans
105708278a5eSRod Evans /*
105808278a5eSRod Evans * Create an AVL tree of objects that are to be validated against an alternative
105908278a5eSRod Evans * system capabilities value.
106008278a5eSRod Evans */
106108278a5eSRod Evans static int
cap_files(const char * str)106208278a5eSRod Evans cap_files(const char *str)
106308278a5eSRod Evans {
106408278a5eSRod Evans char *caps, *name, *next;
106508278a5eSRod Evans
106608278a5eSRod Evans if ((caps = strdup(str)) == NULL)
106708278a5eSRod Evans return (0);
106808278a5eSRod Evans
1069cc4ec439SRichard Lowe for (name = strtok_r(caps, MSG_ORIG(MSG_CAP_DELIMIT), &next);
1070cc4ec439SRichard Lowe name != NULL;
1071cc4ec439SRichard Lowe name = strtok_r(NULL, MSG_ORIG(MSG_CAP_DELIMIT), &next)) {
107208278a5eSRod Evans avl_index_t where;
107308278a5eSRod Evans PathNode *pnp;
107408278a5eSRod Evans uint_t hash = sgs_str_hash(name);
107508278a5eSRod Evans
107608278a5eSRod Evans /*
107708278a5eSRod Evans * Determine whether this pathname has already been recorded.
107808278a5eSRod Evans */
107908278a5eSRod Evans if (pnavl_recorded(&capavl, name, hash, &where))
108008278a5eSRod Evans continue;
108108278a5eSRod Evans
1082*7eca354dSToomas Soome if ((pnp = calloc(1, sizeof (PathNode))) != NULL) {
108308278a5eSRod Evans pnp->pn_name = name;
108408278a5eSRod Evans pnp->pn_hash = hash;
108508278a5eSRod Evans avl_insert(capavl, pnp, where);
108608278a5eSRod Evans }
1087cc4ec439SRichard Lowe }
108808278a5eSRod Evans
108908278a5eSRod Evans return (1);
109008278a5eSRod Evans }
109108278a5eSRod Evans
109208278a5eSRod Evans /*
109308278a5eSRod Evans * Set alternative system capabilities. A user can establish alternative system
109408278a5eSRod Evans * capabilities from the environment, or from a configuration file. This
109508278a5eSRod Evans * routine is called in each instance. Environment variables only set the
109608278a5eSRod Evans * replaceable (rpl) variables. Configuration files can set both replaceable
109708278a5eSRod Evans * (rpl) and permanent (prm) variables.
109808278a5eSRod Evans */
109908278a5eSRod Evans int
cap_alternative(void)110008278a5eSRod Evans cap_alternative(void)
110108278a5eSRod Evans {
110208278a5eSRod Evans /*
110308278a5eSRod Evans * If no capabilities have been set, we're done.
110408278a5eSRod Evans */
110508278a5eSRod Evans if ((rpl_hwcap == NULL) && (rpl_sfcap == NULL) &&
110608278a5eSRod Evans (rpl_machcap == NULL) && (rpl_platcap == NULL) &&
110708278a5eSRod Evans (prm_hwcap == NULL) && (prm_sfcap == NULL) &&
110808278a5eSRod Evans (prm_machcap == NULL) && (prm_platcap == NULL))
110908278a5eSRod Evans return (1);
111008278a5eSRod Evans
111108278a5eSRod Evans /*
111208278a5eSRod Evans * If the user has requested to modify any capabilities, establish a
111308278a5eSRod Evans * unique set from the present system capabilities.
111408278a5eSRod Evans */
111508278a5eSRod Evans if ((alt_scapset = malloc(sizeof (Syscapset))) == NULL)
111608278a5eSRod Evans return (0);
111708278a5eSRod Evans *alt_scapset = *org_scapset;
111808278a5eSRod Evans
111956726c7eSRobert Mustacchi cap_settings[CAP_OVR_HW_1].cs_aval = &alt_scapset->sc_hw_1;
112056726c7eSRobert Mustacchi cap_settings[CAP_OVR_SF_1].cs_aval = &alt_scapset->sc_sf_1;
112156726c7eSRobert Mustacchi cap_settings[CAP_OVR_HW_2].cs_aval = &alt_scapset->sc_hw_2;
112256726c7eSRobert Mustacchi cap_settings[CAP_OVR_HW_3].cs_aval = &alt_scapset->sc_hw_3;
112308278a5eSRod Evans
112408278a5eSRod Evans /*
112508278a5eSRod Evans * Process any replaceable variables.
112608278a5eSRod Evans */
112708278a5eSRod Evans if (rpl_hwcap && (cap_modify(CA_SUNW_HW_1, rpl_hwcap) == 0))
112808278a5eSRod Evans return (0);
112908278a5eSRod Evans if (rpl_sfcap && (cap_modify(CA_SUNW_SF_1, rpl_sfcap) == 0))
113008278a5eSRod Evans return (0);
113108278a5eSRod Evans
113208278a5eSRod Evans if (rpl_platcap) {
113308278a5eSRod Evans alt_scapset->sc_plat = (char *)rpl_platcap;
113408278a5eSRod Evans alt_scapset->sc_platsz = strlen(rpl_platcap);
113508278a5eSRod Evans }
113608278a5eSRod Evans if (rpl_machcap) {
113708278a5eSRod Evans alt_scapset->sc_mach = (char *)rpl_machcap;
113808278a5eSRod Evans alt_scapset->sc_machsz = strlen(rpl_machcap);
113908278a5eSRod Evans }
114008278a5eSRod Evans
114108278a5eSRod Evans if (rpl_cap_files && (cap_files(rpl_cap_files) == 0))
114208278a5eSRod Evans return (0);
114308278a5eSRod Evans
114408278a5eSRod Evans /*
114508278a5eSRod Evans * Process any permanent variables.
114608278a5eSRod Evans */
114708278a5eSRod Evans if (prm_hwcap && (cap_modify(CA_SUNW_HW_1, prm_hwcap) == 0))
114808278a5eSRod Evans return (0);
114908278a5eSRod Evans if (prm_sfcap && (cap_modify(CA_SUNW_SF_1, prm_sfcap) == 0))
115008278a5eSRod Evans return (0);
115108278a5eSRod Evans
115208278a5eSRod Evans if (prm_platcap) {
115308278a5eSRod Evans alt_scapset->sc_plat = (char *)prm_platcap;
115408278a5eSRod Evans alt_scapset->sc_platsz = strlen(prm_platcap);
115508278a5eSRod Evans }
115608278a5eSRod Evans if (prm_machcap) {
115708278a5eSRod Evans alt_scapset->sc_mach = (char *)prm_machcap;
115808278a5eSRod Evans alt_scapset->sc_machsz = strlen(prm_machcap);
115908278a5eSRod Evans }
116008278a5eSRod Evans
116108278a5eSRod Evans if (prm_cap_files && (cap_files(prm_cap_files) == 0))
116208278a5eSRod Evans return (0);
116308278a5eSRod Evans
116408278a5eSRod Evans /*
116508278a5eSRod Evans * Reset the replaceable variables. If this is the environment variable
116608278a5eSRod Evans * processing, these variables are now available for configuration file
116708278a5eSRod Evans * initialization.
116808278a5eSRod Evans */
116908278a5eSRod Evans rpl_hwcap = rpl_sfcap = rpl_machcap = rpl_platcap =
117008278a5eSRod Evans rpl_cap_files = NULL;
117108278a5eSRod Evans
117208278a5eSRod Evans return (1);
117308278a5eSRod Evans }
117408278a5eSRod Evans
117508278a5eSRod Evans /*
117608278a5eSRod Evans * Take the index from a Capinfo entry and determine the associated capabilities
117708278a5eSRod Evans * set. Verify that the capabilities are available for this system.
117808278a5eSRod Evans */
117908278a5eSRod Evans static int
sym_cap_check(Cap * cptr,uint_t cndx,Syscapset * bestcapset,Rt_map * lmp,const char * name,uint_t ndx)118008278a5eSRod Evans sym_cap_check(Cap *cptr, uint_t cndx, Syscapset *bestcapset, Rt_map *lmp,
118108278a5eSRod Evans const char *name, uint_t ndx)
118208278a5eSRod Evans {
118308278a5eSRod Evans Syscapset *scapset;
118408278a5eSRod Evans int totplat, ivlplat, totmach, ivlmach, capfail = 0;
118508278a5eSRod Evans
118608278a5eSRod Evans /*
118708278a5eSRod Evans * Determine whether this file requires validation against alternative
118808278a5eSRod Evans * system capabilities.
118908278a5eSRod Evans */
119008278a5eSRod Evans if ((FLAGS1(lmp) & FL1_RT_ALTCHECK) == 0)
119108278a5eSRod Evans cap_check_lmp_init(lmp);
119208278a5eSRod Evans
119308278a5eSRod Evans if (FLAGS1(lmp) & FL1_RT_ALTCAP)
119408278a5eSRod Evans scapset = alt_scapset;
119508278a5eSRod Evans else
119608278a5eSRod Evans scapset = org_scapset;
119708278a5eSRod Evans
119808278a5eSRod Evans totplat = ivlplat = totmach = ivlmach = 0;
119908278a5eSRod Evans
120008278a5eSRod Evans /*
120108278a5eSRod Evans * A capabilities index points to a capabilities group that can consist
120208278a5eSRod Evans * of one or more capabilities, terminated with a CA_SUNW_NULL entry.
120308278a5eSRod Evans */
120408278a5eSRod Evans for (cptr += cndx; cptr->c_tag != CA_SUNW_NULL; cptr++) {
120508278a5eSRod Evans Xword val = cptr->c_un.c_val;
120608278a5eSRod Evans char *str;
120708278a5eSRod Evans
120808278a5eSRod Evans switch (cptr->c_tag) {
120908278a5eSRod Evans case CA_SUNW_HW_1:
12104e12d685SRod Evans /*
12114e12d685SRod Evans * Remove any historic values that should not be
12124e12d685SRod Evans * involved with any validation.
12134e12d685SRod Evans */
12144e12d685SRod Evans val &= ~AV_HW1_IGNORE;
12154e12d685SRod Evans
121608278a5eSRod Evans bestcapset->sc_hw_1 = val;
121708278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_HW_1,
121808278a5eSRod Evans name, ndx, M_MACH, bestcapset));
121908278a5eSRod Evans
122008278a5eSRod Evans if (hwcap1_check(scapset, val, NULL) == 0)
122108278a5eSRod Evans capfail++;
122208278a5eSRod Evans break;
122308278a5eSRod Evans case CA_SUNW_SF_1:
122408278a5eSRod Evans bestcapset->sc_sf_1 = val;
122508278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_SF_1,
122608278a5eSRod Evans name, ndx, M_MACH, bestcapset));
122708278a5eSRod Evans
122808278a5eSRod Evans if (sfcap1_check(scapset, val, NULL) == 0)
122908278a5eSRod Evans capfail++;
123008278a5eSRod Evans break;
123108278a5eSRod Evans case CA_SUNW_HW_2:
123208278a5eSRod Evans bestcapset->sc_hw_2 = val;
123308278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_HW_2,
123408278a5eSRod Evans name, ndx, M_MACH, bestcapset));
123508278a5eSRod Evans
123608278a5eSRod Evans if (hwcap2_check(scapset, val, NULL) == 0)
123708278a5eSRod Evans capfail++;
123808278a5eSRod Evans break;
123908278a5eSRod Evans case CA_SUNW_PLAT:
124008278a5eSRod Evans /*
124108278a5eSRod Evans * A capabilities set can define multiple platform names
124208278a5eSRod Evans * that are appropriate. Only if all the names are
124308278a5eSRod Evans * deemed invalid is the group determined inappropriate.
124408278a5eSRod Evans */
124508278a5eSRod Evans if (totplat == ivlplat) {
124608278a5eSRod Evans totplat++;
124708278a5eSRod Evans
124808278a5eSRod Evans str = STRTAB(lmp) + val;
124908278a5eSRod Evans bestcapset->sc_plat = str;
125008278a5eSRod Evans
125108278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_PLAT,
125208278a5eSRod Evans name, ndx, M_MACH, bestcapset));
125308278a5eSRod Evans
125408278a5eSRod Evans if (platcap_check(scapset, str, NULL) == 0)
125508278a5eSRod Evans ivlplat++;
125608278a5eSRod Evans }
125708278a5eSRod Evans break;
125808278a5eSRod Evans case CA_SUNW_MACH:
125908278a5eSRod Evans /*
126008278a5eSRod Evans * A capabilities set can define multiple machine names
126108278a5eSRod Evans * that are appropriate. Only if all the names are
126208278a5eSRod Evans * deemed invalid is the group determined inappropriate.
126308278a5eSRod Evans */
126408278a5eSRod Evans if (totmach == ivlmach) {
126508278a5eSRod Evans totmach++;
126608278a5eSRod Evans
126708278a5eSRod Evans str = STRTAB(lmp) + val;
126808278a5eSRod Evans bestcapset->sc_mach = str;
126908278a5eSRod Evans
127008278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_MACH,
127108278a5eSRod Evans name, ndx, M_MACH, bestcapset));
127208278a5eSRod Evans
127308278a5eSRod Evans if (machcap_check(scapset, str, NULL) == 0)
127408278a5eSRod Evans ivlmach++;
127508278a5eSRod Evans }
127608278a5eSRod Evans break;
127756726c7eSRobert Mustacchi case CA_SUNW_HW_3:
127856726c7eSRobert Mustacchi bestcapset->sc_hw_3 = val;
127956726c7eSRobert Mustacchi DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_HW_3,
128056726c7eSRobert Mustacchi name, ndx, M_MACH, bestcapset));
128156726c7eSRobert Mustacchi
128256726c7eSRobert Mustacchi if (hwcap3_check(scapset, val, NULL) == 0)
128356726c7eSRobert Mustacchi capfail++;
128456726c7eSRobert Mustacchi break;
128556726c7eSRobert Mustacchi
128608278a5eSRod Evans default:
128708278a5eSRod Evans break;
128808278a5eSRod Evans }
128908278a5eSRod Evans }
129008278a5eSRod Evans
129108278a5eSRod Evans /*
129208278a5eSRod Evans * If any platform definitions, or machine definitions were found, and
129308278a5eSRod Evans * all were invalid, indicate that the object is inappropriate.
129408278a5eSRod Evans */
129508278a5eSRod Evans if (capfail || (totplat && (totplat == ivlplat)) ||
129608278a5eSRod Evans (totmach && (totmach == ivlmach))) {
129708278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_REJECTED, name, ndx,
129808278a5eSRod Evans M_MACH, NULL));
129908278a5eSRod Evans return (0);
130008278a5eSRod Evans }
130108278a5eSRod Evans
130208278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_CANDIDATE, name, ndx,
130308278a5eSRod Evans M_MACH, NULL));
130408278a5eSRod Evans return (1);
130508278a5eSRod Evans }
130608278a5eSRod Evans
130708278a5eSRod Evans /*
130808278a5eSRod Evans * Determine whether a symbols capabilities are more significant than any that
130908278a5eSRod Evans * have already been validated. The precedence of capabilities are:
131008278a5eSRod Evans *
131108278a5eSRod Evans * PLATCAP -> MACHCAP -> HWCAP_2 -> HWCAP_1
131208278a5eSRod Evans *
131308278a5eSRod Evans *
131408278a5eSRod Evans * Presently we make no comparisons of software capabilities. However, should
131508278a5eSRod Evans * this symbol capability have required the SF1_SUNW_ADDR32 attribute, then
131608278a5eSRod Evans * this would have been validated as appropriate or not.
131708278a5eSRod Evans *
131808278a5eSRod Evans * bestcapset is the presently available 'best' capabilities group, and
131908278a5eSRod Evans * symcapset is the present capabilities group under investigation. Return 0
132008278a5eSRod Evans * if the bestcapset should remain in affect, or 1 if the symcapset is better.
132108278a5eSRod Evans */
132208278a5eSRod Evans inline static int
is_sym_the_best(Syscapset * bestcapset,Syscapset * symcapset)132308278a5eSRod Evans is_sym_the_best(Syscapset *bestcapset, Syscapset *symcapset)
132408278a5eSRod Evans {
132508278a5eSRod Evans /*
132608278a5eSRod Evans * Check any platform capability. If the new symbol isn't associated
132708278a5eSRod Evans * with a CA_SUNW_PLAT capability, and the best symbol is, then retain
132808278a5eSRod Evans * the best capabilities group. If the new symbol is associated with a
132908278a5eSRod Evans * CA_SUNW_PLAT capability, and the best symbol isn't, then the new
133008278a5eSRod Evans * symbol needs to be taken.
133108278a5eSRod Evans */
133208278a5eSRod Evans if (bestcapset->sc_plat && (symcapset->sc_plat == NULL))
133308278a5eSRod Evans return (0);
133408278a5eSRod Evans
133508278a5eSRod Evans if ((bestcapset->sc_plat == NULL) && symcapset->sc_plat)
133608278a5eSRod Evans return (1);
133708278a5eSRod Evans
133808278a5eSRod Evans /*
133908278a5eSRod Evans * Check any machine name capability. If the new symbol isn't
134008278a5eSRod Evans * associated with a CA_SUNW_MACH capability, and the best symbol is,
134108278a5eSRod Evans * then retain the best capabilities group. If the new symbol is
134208278a5eSRod Evans * associated with a CA_SUNW_MACH capability, and the best symbol isn't,
134308278a5eSRod Evans * then the new symbol needs to be taken.
134408278a5eSRod Evans */
134508278a5eSRod Evans if (bestcapset->sc_mach && (symcapset->sc_mach == NULL))
134608278a5eSRod Evans return (0);
134708278a5eSRod Evans
134808278a5eSRod Evans if ((bestcapset->sc_mach == NULL) && symcapset->sc_mach)
134908278a5eSRod Evans return (1);
135008278a5eSRod Evans
135108278a5eSRod Evans /*
135256726c7eSRobert Mustacchi * Check the hardware capabilities. If the best symbols CA_SUNW_HW_3
135308278a5eSRod Evans * capabilities are greater than the new symbols capabilities, then
135456726c7eSRobert Mustacchi * retain the best capabilities group. If the new symbols CA_SUNW_HW_3
135508278a5eSRod Evans * capabilities are greater than the best symbol, then the new symbol
135656726c7eSRobert Mustacchi * needs to be taken. Repeat the same process for CA_SUNW_HW_2.
135708278a5eSRod Evans */
135856726c7eSRobert Mustacchi if (bestcapset->sc_hw_3 > symcapset->sc_hw_3)
135956726c7eSRobert Mustacchi return (0);
136056726c7eSRobert Mustacchi
136156726c7eSRobert Mustacchi if (bestcapset->sc_hw_3 < symcapset->sc_hw_3)
136256726c7eSRobert Mustacchi return (1);
136356726c7eSRobert Mustacchi
136408278a5eSRod Evans if (bestcapset->sc_hw_2 > symcapset->sc_hw_2)
136508278a5eSRod Evans return (0);
136608278a5eSRod Evans
136708278a5eSRod Evans if (bestcapset->sc_hw_2 < symcapset->sc_hw_2)
136808278a5eSRod Evans return (1);
136908278a5eSRod Evans
137008278a5eSRod Evans /*
137108278a5eSRod Evans * Check the remaining hardware capabilities. If the best symbols
137208278a5eSRod Evans * CA_SUNW_HW_1 capabilities are greater than the new symbols
137308278a5eSRod Evans * capabilities, then retain the best capabilities group. If the new
137408278a5eSRod Evans * symbols CA_SUNW_HW_1 capabilities are greater than the best symbol,
137508278a5eSRod Evans * then the new symbol needs to be taken.
137608278a5eSRod Evans */
137708278a5eSRod Evans if (bestcapset->sc_hw_1 > symcapset->sc_hw_1)
137808278a5eSRod Evans return (0);
137908278a5eSRod Evans
138008278a5eSRod Evans if (bestcapset->sc_hw_1 < symcapset->sc_hw_1)
138108278a5eSRod Evans return (1);
138208278a5eSRod Evans
138308278a5eSRod Evans /*
138408278a5eSRod Evans * Both capabilities are the same. Retain the best on a first-come
138508278a5eSRod Evans * first-served basis.
138608278a5eSRod Evans */
138708278a5eSRod Evans return (0);
138808278a5eSRod Evans }
138908278a5eSRod Evans
139008278a5eSRod Evans /*
139108278a5eSRod Evans * Initiate symbol capabilities processing. If an initial symbol lookup
139208278a5eSRod Evans * results in binding to a symbol that has an associated SUNW_capinfo entry,
139308278a5eSRod Evans * we arrive here.
139408278a5eSRod Evans *
139508278a5eSRod Evans * The standard model is that this initial symbol is the lead capabilities
139608278a5eSRod Evans * symbol (defined as CAPINFO_SUNW_GLOB) of a capabilities family. This lead
139708278a5eSRod Evans * symbol's SUNW_capinfo information points to the SUNW_capchain entry that
139808278a5eSRod Evans * provides the family symbol indexes. We traverse this chain, looking at
139908278a5eSRod Evans * each family member, to discover the best capabilities instance. This
140008278a5eSRod Evans * instance name and symbol information is returned to establish the final
140108278a5eSRod Evans * symbol binding.
140208278a5eSRod Evans *
140308278a5eSRod Evans * If the symbol that got us here is not CAPINFO_SUNW_GLOB, then we've bound
140408278a5eSRod Evans * directly to a capabilities symbol which must be verified. This is not the
140508278a5eSRod Evans * model created by ld(1) using -z symbolcap, but might be created directly
140608278a5eSRod Evans * within a relocatable object by the compilation system.
140708278a5eSRod Evans */
140808278a5eSRod Evans int
cap_match(Sresult * srp,uint_t symndx,Sym * symtabptr,char * strtabptr)140908278a5eSRod Evans cap_match(Sresult *srp, uint_t symndx, Sym *symtabptr, char *strtabptr)
141008278a5eSRod Evans {
141108278a5eSRod Evans Rt_map *ilmp = srp->sr_dmap;
141208278a5eSRod Evans Sym *bsym = NULL;
141308278a5eSRod Evans const char *bname;
141408278a5eSRod Evans Syscapset bestcapset = { 0 };
141508278a5eSRod Evans Cap *cap;
141608278a5eSRod Evans Capchain *capchain;
141708278a5eSRod Evans uchar_t grpndx;
141808278a5eSRod Evans uint_t ochainndx, nchainndx, bndx;
141908278a5eSRod Evans
142008278a5eSRod Evans cap = CAP(ilmp);
142108278a5eSRod Evans capchain = CAPCHAIN(ilmp);
142208278a5eSRod Evans
142308278a5eSRod Evans grpndx = (uchar_t)ELF_C_GROUP(CAPINFO(ilmp)[symndx]);
142408278a5eSRod Evans
142508278a5eSRod Evans /*
142608278a5eSRod Evans * If this symbols capability group is not a lead symbol, then simply
142708278a5eSRod Evans * verify the symbol.
142808278a5eSRod Evans */
142908278a5eSRod Evans if (grpndx != CAPINFO_SUNW_GLOB) {
143008278a5eSRod Evans Syscapset symcapset = { 0 };
143108278a5eSRod Evans
143208278a5eSRod Evans return (sym_cap_check(cap, grpndx, &symcapset, ilmp,
143308278a5eSRod Evans srp->sr_name, symndx));
143408278a5eSRod Evans }
143508278a5eSRod Evans
143608278a5eSRod Evans /*
143708278a5eSRod Evans * If there is no capabilities chain, return the lead symbol.
143808278a5eSRod Evans */
143908278a5eSRod Evans if (capchain == NULL)
144008278a5eSRod Evans return (1);
144108278a5eSRod Evans
144208278a5eSRod Evans ochainndx = (uint_t)ELF_C_SYM(CAPINFO(ilmp)[symndx]);
144308278a5eSRod Evans
144408278a5eSRod Evans /*
144508278a5eSRod Evans * If there is only one member for this family, take it. Once a family
144608278a5eSRod Evans * has been processed, the best family instance is written to the head
144708278a5eSRod Evans * of the chain followed by a null entry. This caching ensures that the
144808278a5eSRod Evans * same family comparison doesn't have to be undertaken more than once.
144908278a5eSRod Evans */
145008278a5eSRod Evans if (capchain[ochainndx] && (capchain[ochainndx + 1] == 0)) {
145108278a5eSRod Evans Sym *fsym = symtabptr + capchain[ochainndx];
145208278a5eSRod Evans const char *fname = strtabptr + fsym->st_name;
145308278a5eSRod Evans
145408278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(ilmp, DBG_CAP_USED, fname,
145508278a5eSRod Evans capchain[ochainndx], M_MACH, NULL));
145608278a5eSRod Evans
145708278a5eSRod Evans srp->sr_sym = fsym;
145808278a5eSRod Evans srp->sr_name = fname;
145908278a5eSRod Evans return (1);
146008278a5eSRod Evans }
146108278a5eSRod Evans
146208278a5eSRod Evans /*
146308278a5eSRod Evans * As this symbol is the lead symbol of a capabilities family, it is
146408278a5eSRod Evans * considered the generic member, and therefore forms the basic
146508278a5eSRod Evans * fall-back for the capabilities family.
146608278a5eSRod Evans */
146708278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(ilmp, DBG_CAP_DEFAULT, srp->sr_name,
146808278a5eSRod Evans symndx, M_MACH, NULL));
146908278a5eSRod Evans bsym = srp->sr_sym;
147008278a5eSRod Evans bname = srp->sr_name;
147108278a5eSRod Evans bndx = symndx;
147208278a5eSRod Evans
147308278a5eSRod Evans /*
147408278a5eSRod Evans * Traverse the capabilities chain analyzing each family member.
147508278a5eSRod Evans */
147608278a5eSRod Evans for (nchainndx = ochainndx + 1, symndx = capchain[nchainndx]; symndx;
147708278a5eSRod Evans nchainndx++, symndx = capchain[nchainndx]) {
147808278a5eSRod Evans Sym *nsym = symtabptr + symndx;
147908278a5eSRod Evans const char *nname = strtabptr + nsym->st_name;
148008278a5eSRod Evans Syscapset symcapset = { 0 };
148108278a5eSRod Evans
148208278a5eSRod Evans if ((grpndx =
148308278a5eSRod Evans (uchar_t)ELF_C_GROUP(CAPINFO(ilmp)[symndx])) == 0)
148408278a5eSRod Evans continue;
148508278a5eSRod Evans
148608278a5eSRod Evans if (sym_cap_check(cap, grpndx, &symcapset, ilmp,
148708278a5eSRod Evans nname, symndx) == 0)
148808278a5eSRod Evans continue;
148908278a5eSRod Evans
149008278a5eSRod Evans /*
149108278a5eSRod Evans * Determine whether a symbol's capabilities are more
149208278a5eSRod Evans * significant than any that have already been validated.
149308278a5eSRod Evans */
149408278a5eSRod Evans if (is_sym_the_best(&bestcapset, &symcapset)) {
149508278a5eSRod Evans bestcapset = symcapset;
149608278a5eSRod Evans bsym = nsym;
149708278a5eSRod Evans bname = nname;
149808278a5eSRod Evans bndx = symndx;
149908278a5eSRod Evans }
150008278a5eSRod Evans }
150108278a5eSRod Evans
150208278a5eSRod Evans DBG_CALL(Dbg_syms_cap_lookup(ilmp, DBG_CAP_USED, bname, bndx,
150308278a5eSRod Evans M_MACH, NULL));
150408278a5eSRod Evans
150508278a5eSRod Evans /*
150608278a5eSRod Evans * Having found the best symbol, cache the results by overriding the
150708278a5eSRod Evans * first element of the associated chain.
150808278a5eSRod Evans */
150908278a5eSRod Evans capchain[ochainndx] = bndx;
151008278a5eSRod Evans capchain[ochainndx + 1] = 0;
151108278a5eSRod Evans
151208278a5eSRod Evans /*
151308278a5eSRod Evans * Update the symbol result information for return to the user.
151408278a5eSRod Evans */
151508278a5eSRod Evans srp->sr_sym = bsym;
151608278a5eSRod Evans srp->sr_name = bname;
151708278a5eSRod Evans return (1);
151808278a5eSRod Evans }
1519