cap.c revision 11827:d7ef53deac3f
119370Spst/*
219370Spst * CDDL HEADER START
319370Spst *
419370Spst * The contents of this file are subject to the terms of the
519370Spst * Common Development and Distribution License (the "License").
619370Spst * You may not use this file except in compliance with the License.
719370Spst *
819370Spst * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
919370Spst * or http://www.opensolaris.org/os/licensing.
1019370Spst * See the License for the specific language governing permissions
1119370Spst * and limitations under the License.
1219370Spst *
1319370Spst * When distributing Covered Code, include this CDDL HEADER in each
1419370Spst * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1519370Spst * If applicable, add the following below this CDDL HEADER, with the
1619370Spst * fields enclosed by brackets "[]" replaced with your own identifying
1719370Spst * information: Portions Copyright [yyyy] [name of copyright owner]
1819370Spst *
1919370Spst * CDDL HEADER END
2019370Spst */
2119370Spst
2219370Spst/*
2319370Spst * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2419370Spst * Use is subject to license terms.
2519370Spst */
2619370Spst
2719370Spst#include	<sys/types.h>
2819370Spst#include	<sys/mman.h>
2919370Spst#include	<dirent.h>
3019370Spst#include	<stdio.h>
3119370Spst#include	<stdlib.h>
3219370Spst#include	<string.h>
3319370Spst#include	<limits.h>
3419370Spst#include	<debug.h>
3519370Spst#include	<conv.h>
3619370Spst#include	<elfcap.h>
3719370Spst#include	"_rtld.h"
3819370Spst#include	"_elf.h"
3919370Spst#include	"_audit.h"
4019370Spst#include	"msg.h"
4119370Spst
4219370Spst/*
4319370Spst * qsort(3c) capability comparison function.
4419370Spst */
4519370Spststatic int
4619370Spstcompare(const void *fdp_a, const void *fdp_b)
4719370Spst{
4819370Spst	char	*strcap_a, *strcap_b;
4919370Spst	Xword	hwcap_a, hwcap_b;
5019370Spst
51	/*
52	 * First, investigate any platform capability.
53	 */
54	strcap_a = ((Fdesc *)fdp_a)->fd_scapset.sc_plat;
55	strcap_b = ((Fdesc *)fdp_b)->fd_scapset.sc_plat;
56
57	if (strcap_a && (strcap_b == NULL))
58		return (-1);
59	if (strcap_b && (strcap_a == NULL))
60		return (1);
61
62	/*
63	 * Second, investigate any machine capability.
64	 */
65	strcap_a = ((Fdesc *)fdp_a)->fd_scapset.sc_mach;
66	strcap_b = ((Fdesc *)fdp_b)->fd_scapset.sc_mach;
67
68	if (strcap_a && (strcap_b == NULL))
69		return (-1);
70	if (strcap_b && (strcap_a == NULL))
71		return (1);
72
73	/*
74	 * Third, investigate any CA_SUNW_HW_2 hardware capabilities.
75	 */
76	hwcap_a = ((Fdesc *)fdp_a)->fd_scapset.sc_hw_2;
77	hwcap_b = ((Fdesc *)fdp_b)->fd_scapset.sc_hw_2;
78
79	if (hwcap_a > hwcap_b)
80		return (-1);
81	if (hwcap_a < hwcap_b)
82		return (1);
83
84	/*
85	 * Finally, investigate any CA_SUNW_HW_1 hardware capabilities.
86	 */
87	hwcap_a = ((Fdesc *)fdp_a)->fd_scapset.sc_hw_1;
88	hwcap_b = ((Fdesc *)fdp_b)->fd_scapset.sc_hw_1;
89
90	if (hwcap_a > hwcap_b)
91		return (-1);
92	if (hwcap_a < hwcap_b)
93		return (1);
94
95	return (0);
96}
97
98/*
99 * Determine whether HWCAP1 capabilities value is supported.
100 */
101int
102hwcap1_check(Syscapset *scapset, Xword val, Rej_desc *rej)
103{
104	Xword	mval;
105
106	/*
107	 * Ensure that the kernel can cope with the required capabilities.
108	 */
109	if ((rtld_flags2 & RT_FL2_HWCAP) &&
110	    ((mval = (val & ~scapset->sc_hw_1)) != 0)) {
111		if (rej) {
112			static Conv_cap_val_hw1_buf_t	cap_buf;
113
114			rej->rej_type = SGS_REJ_HWCAP_1;
115			rej->rej_str = conv_cap_val_hw1(mval,
116			    M_MACH, 0, &cap_buf);
117		}
118		return (0);
119	}
120	return (1);
121}
122
123/*
124 * Determine whether HWCAP2 capabilities value is supported.
125 */
126int
127hwcap2_check(Syscapset *scapset, Xword val, Rej_desc *rej)
128{
129	Xword	mval;
130
131	/*
132	 * Ensure that the kernel can cope with the required capabilities.
133	 */
134	if ((mval = (val & ~scapset->sc_hw_2)) != 0) {
135		if (rej) {
136			static Conv_cap_val_hw2_buf_t	cap_buf;
137
138			rej->rej_type = SGS_REJ_HWCAP_2;
139			rej->rej_str = conv_cap_val_hw2(mval,
140			    M_MACH, 0, &cap_buf);
141		}
142		return (0);
143	}
144	return (1);
145}
146
147/*
148 * Process any software capabilities.
149 */
150/* ARGSUSED0 */
151int
152sfcap1_check(Syscapset *scapset, Xword val, Rej_desc *rej)
153{
154#if	defined(_ELF64)
155	/*
156	 * A 64-bit executable that started the process can be restricted to a
157	 * 32-bit address space.  A 64-bit dependency that is restricted to a
158	 * 32-bit address space can not be loaded unless the executable has
159	 * established this requirement.
160	 */
161	if ((val & SF1_SUNW_ADDR32) && ((rtld_flags2 & RT_FL2_ADDR32) == 0)) {
162		if (rej) {
163			static Conv_cap_val_sf1_buf_t	cap_buf;
164
165			rej->rej_type = SGS_REJ_SFCAP_1;
166			rej->rej_str = conv_cap_val_sf1(SF1_SUNW_ADDR32,
167			    M_MACH, 0, &cap_buf);
168		}
169		return (0);
170	}
171#endif
172	return (1);
173}
174
175/*
176 * Process any platform capability.
177 */
178int
179platcap_check(Syscapset *scapset, const char *str, Rej_desc *rej)
180{
181	/*
182	 * If the platform name hasn't been set, try and obtain it.
183	 */
184	if ((scapset->sc_plat == NULL) &&
185	    (scapset->sc_platsz == 0))
186		platform_name(scapset);
187
188	if ((scapset->sc_plat == NULL) ||
189	    (str && strcmp(scapset->sc_plat, str))) {
190		if (rej) {
191			/*
192			 * Note, the platform name points to a string within an
193			 * objects string table, and if that object can't be
194			 * loaded, it will be unloaded and thus invalidate the
195			 * string.  Duplicate the string here for rejection
196			 * message inheritance.
197			 */
198			rej->rej_type = SGS_REJ_PLATCAP;
199			rej->rej_str = stravl_insert(str, 0, 0, 0);
200		}
201		return (0);
202	}
203	return (1);
204}
205
206/*
207 * Process any machine capability.
208 */
209int
210machcap_check(Syscapset *scapset, const char *str, Rej_desc *rej)
211{
212	/*
213	 * If the machine name hasn't been set, try and obtain it.
214	 */
215	if ((scapset->sc_mach == NULL) &&
216	    (scapset->sc_machsz == 0))
217		machine_name(scapset);
218
219	if ((scapset->sc_mach == NULL) ||
220	    (str && strcmp(scapset->sc_mach, str))) {
221		if (rej) {
222			/*
223			 * Note, the machine name points to a string within an
224			 * objects string table, and if that object can't be
225			 * loaded, it will be unloaded and thus invalidate the
226			 * string.  Duplicate the string here for rejection
227			 * message inheritance.
228			 */
229			rej->rej_type = SGS_REJ_MACHCAP;
230			rej->rej_str = stravl_insert(str, 0, 0, 0);
231		}
232		return (0);
233	}
234	return (1);
235}
236
237/*
238 * Generic front-end to capabilities validation.
239 */
240static int
241cap_check(Cap *cptr, char *strs, int alt, Fdesc *fdp, Rej_desc *rej)
242{
243	Syscapset	*scapset;
244	int		totplat, ivlplat, totmach, ivlmach;
245
246	/*
247	 * If the caller has no capabilities, then the object is valid.
248	 */
249	if (cptr == NULL)
250		return (1);
251
252	if (alt)
253		scapset = alt_scapset;
254	else
255		scapset = org_scapset;
256
257	totplat = ivlplat = totmach = ivlmach = 0;
258
259	while (cptr->c_tag != CA_SUNW_NULL) {
260		Xword	val = cptr->c_un.c_val;
261		char	*str;
262
263		switch (cptr->c_tag) {
264		case CA_SUNW_HW_1:
265			if (hwcap1_check(scapset, val, rej) == 0)
266				return (0);
267			if (fdp)
268				fdp->fd_scapset.sc_hw_1 = val;
269			break;
270		case CA_SUNW_SF_1:
271			if (sfcap1_check(scapset, val, rej) == 0)
272				return (0);
273			if (fdp)
274				fdp->fd_scapset.sc_sf_1 = val;
275			break;
276		case CA_SUNW_HW_2:
277			if (hwcap2_check(scapset, val, rej) == 0)
278				return (0);
279			if (fdp)
280				fdp->fd_scapset.sc_hw_2 = val;
281			break;
282		case CA_SUNW_PLAT:
283			/*
284			 * A capabilities group can define multiple platform
285			 * names that are appropriate.  Only if all the names
286			 * are deemed invalid is the group determined
287			 * inappropriate.
288			 */
289			if (totplat == ivlplat) {
290				totplat++;
291
292				str = strs + val;
293
294				if (platcap_check(scapset, str, rej) == 0)
295					ivlplat++;
296				else if (fdp)
297					fdp->fd_scapset.sc_plat = str;
298			}
299			break;
300		case CA_SUNW_MACH:
301			/*
302			 * A capabilities group can define multiple machine
303			 * names that are appropriate.  Only if all the names
304			 * are deemed invalid is the group determined
305			 * inappropriate.
306			 */
307			if (totmach == ivlmach) {
308				totmach++;
309
310				str = strs + val;
311
312				if (machcap_check(scapset, str, rej) == 0)
313					ivlmach++;
314				else if (fdp)
315					fdp->fd_scapset.sc_mach = str;
316			}
317			break;
318		case CA_SUNW_ID:
319			/*
320			 * Capabilities identifiers provide for diagnostics,
321			 * but are not attributes that must be compared with
322			 * the system.  They are ignored.
323			 */
324			break;
325		default:
326			rej->rej_type = SGS_REJ_UNKCAP;
327			rej->rej_info = cptr->c_tag;
328			return (0);
329		}
330		cptr++;
331	}
332
333	/*
334	 * If any platform names, or machine names were found, and all were
335	 * invalid, indicate that the object is inappropriate.
336	 */
337	if ((totplat && (totplat == ivlplat)) ||
338	    (totmach && (totmach == ivlmach)))
339		return (0);
340
341	return (1);
342}
343
344#define	HWAVL_RECORDED(n)	pnavl_recorded(&capavl, n, NULL, NULL)
345
346/*
347 * Determine whether a link-map should use alternative system capabilities.
348 */
349static void
350cap_check_lmp_init(Rt_map *lmp)
351{
352	int	alt = 0;
353
354	/*
355	 * If an alternative set of system capabilities have been established,
356	 * and only specific files should use these alternative system
357	 * capabilities, determine whether this file is one of those specified.
358	 */
359	if (capavl) {
360		const char	*file;
361
362		/*
363		 * The simplest way to reference a file is to use its file name
364		 * (soname), however try all of the names that this file is
365		 * known by.
366		 */
367		if ((file = strrchr(NAME(lmp), '/')) != NULL)
368			file++;
369		else
370			file = NULL;
371
372		if ((file && (HWAVL_RECORDED(file) != 0)) ||
373		    (HWAVL_RECORDED(NAME(lmp)) != 0) ||
374		    ((PATHNAME(lmp) != NAME(lmp)) &&
375		    (HWAVL_RECORDED(PATHNAME(lmp)) != 0)))
376			alt = 1;
377
378		if (alt == 0) {
379			Aliste		idx;
380			const char	*cp;
381
382			for (APLIST_TRAVERSE(ALIAS(lmp), idx, cp)) {
383				if ((alt = HWAVL_RECORDED(cp)) != 0)
384					break;
385			}
386		}
387	}
388
389	/*
390	 * Indicate if this link-map should use alternative system capabilities,
391	 * and that the alternative system capabilities check has been carried
392	 * out.
393	 */
394	if ((org_scapset != alt_scapset) && ((capavl == NULL) || alt))
395		FLAGS1(lmp) |= FL1_RT_ALTCAP;
396	FLAGS1(lmp) |= FL1_RT_ALTCHECK;
397}
398
399/*
400 * Validate the capabilities requirements of a link-map.
401 *
402 * This routine is called for main, where a link-map is constructed from the
403 * mappings returned from exec(), and for any symbol capabilities comparisons.
404 */
405int
406cap_check_lmp(Rt_map *lmp, Rej_desc *rej)
407{
408	if ((FLAGS1(lmp) & FL1_RT_ALTCHECK) == 0)
409		cap_check_lmp_init(lmp);
410
411	return (cap_check(CAP(lmp), STRTAB(lmp),
412	    (FLAGS1(lmp) & FL1_RT_ALTCAP), NULL, rej));
413}
414
415/*
416 * Validate the capabilities requirements of a file under inspection.
417 * This file is still under the early stages of loading, and has no link-map
418 * yet.  The file must have an object capabilities definition (PT_SUNWCAP), to
419 * have gotten us here.  The logic here is the same as cap_check_lmp().
420 */
421int
422cap_check_fdesc(Fdesc *fdp, Cap *cptr, char *strs, Rej_desc *rej)
423{
424	int	alt = 0;
425
426	/*
427	 * If an alternative set of system capabilities have been established,
428	 * and only specific files should use these alternative system
429	 * capabilities, determine whether this file is one of those specified.
430	 */
431	if (capavl) {
432		const char	*file;
433
434		/*
435		 * The simplest way to reference a file is to use its file name
436		 * (soname), however try all of the names that this file is
437		 * known by.
438		 */
439		if ((file = strrchr(fdp->fd_oname, '/')) != NULL)
440			file++;
441		else
442			file = NULL;
443
444		if ((file && (HWAVL_RECORDED(file) != 0)) ||
445		    (fdp->fd_oname && (HWAVL_RECORDED(fdp->fd_oname) != 0)) ||
446		    (fdp->fd_nname && (HWAVL_RECORDED(fdp->fd_nname) != 0)) ||
447		    (fdp->fd_pname && (fdp->fd_pname != fdp->fd_nname) &&
448		    (HWAVL_RECORDED(fdp->fd_pname) != 0)))
449			alt = 1;
450	}
451
452	/*
453	 * Indicate if this file descriptor should use alternative system
454	 * capabilities, and that the alternative system capabilities check has
455	 * been carried out.
456	 */
457	if ((org_scapset != alt_scapset) && ((capavl == NULL) || alt))
458		fdp->fd_flags |= FLG_FD_ALTCAP;
459	fdp->fd_flags |= FLG_FD_ALTCHECK;
460
461	/*
462	 * Verify that the required capabilities are supported by the reference.
463	 */
464	return (cap_check(cptr, strs, (fdp->fd_flags & FLG_FD_ALTCAP),
465	    fdp, rej));
466}
467
468/*
469 * Free a file descriptor list.  As part of building this list, the original
470 * names for each capabilities candidate were duplicated for use in later
471 * diagnostics.  These names need to be freed.
472 */
473void
474free_fd(Alist *fdalp)
475{
476	if (fdalp) {
477		Aliste	idx;
478		Fdesc	*fdp;
479
480		for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
481			if (fdp->fd_oname)
482				free((void *)fdp->fd_oname);
483		}
484		free(fdalp);
485	}
486}
487
488/*
489 * When $CAPABILITY (or $HWCAP) is used to represent dependencies, take the
490 * associated directory and analyze all the files it contains.
491 */
492static int
493cap_dir(Alist **fdalpp, Lm_list *lml, const char *dname, Rt_map *clmp,
494    uint_t flags, Rej_desc *rej, int *in_nfavl)
495{
496	char		path[PATH_MAX], *dst;
497	const char	*src;
498	DIR		*dir;
499	struct dirent	*dirent;
500	Alist		*fdalp = NULL;
501	int		error = 0;
502
503	/*
504	 * Access the directory in preparation for reading its entries.  If
505	 * successful, establish the initial pathname.
506	 */
507	if ((dir = opendir(dname)) == NULL) {
508		Rej_desc	_rej = { 0 };
509
510		_rej.rej_type = SGS_REJ_STR;
511		_rej.rej_name = dname;
512		_rej.rej_str = strerror(errno);
513		DBG_CALL(Dbg_file_rejected(lml, &_rej, M_MACH));
514		rejection_inherit(rej, &_rej);
515		return (0);
516	}
517
518	for (dst = path, src = dname; *src; dst++, src++)
519		*dst = *src;
520	*dst++ = '/';
521
522	/*
523	 * Read each entry from the directory and determine whether it is a
524	 * valid ELF file.
525	 */
526	while ((dirent = readdir(dir)) != NULL) {
527		const char	*file = dirent->d_name;
528		char		*_dst;
529		Fdesc		fd = { 0 };
530		Rej_desc	_rej = { 0 };
531		Pdesc		pd = { 0 };
532
533		/*
534		 * Ignore "." and ".." entries.
535		 */
536		if ((file[0] == '.') && ((file[1] == '\0') ||
537		    ((file[1] == '.') && (file[2] == '\0'))))
538			continue;
539
540		/*
541		 * Complete the full pathname.
542		 */
543		for (_dst = dst, src = file, file = dst; *src; _dst++, src++)
544			*_dst = *src;
545		*_dst = '\0';
546
547		/*
548		 * Trace the inspection of this file, and determine any
549		 * auditor substitution.
550		 */
551		pd.pd_pname = path;
552		pd.pd_flags = PD_FLG_PNSLASH;
553
554		if (load_trace(lml, &pd, clmp, &fd) == NULL)
555			continue;
556
557		/*
558		 * Note, all directory entries are processed by find_path(),
559		 * even entries that are directories themselves.  This single
560		 * point for control keeps the number of stat()'s down, and
561		 * provides a single point for error diagnostics.
562		 */
563		if (find_path(lml, clmp, flags, &fd, &_rej, in_nfavl) == 0) {
564			rejection_inherit(rej, &_rej);
565			continue;
566		}
567
568		DBG_CALL(Dbg_cap_candidate(lml, fd.fd_nname));
569
570		/*
571		 * If this object has already been loaded, save the capabilities
572		 * for later sorting.  Otherwise we have a new candidate.
573		 */
574		if (fd.fd_lmp)
575			fd.fd_scapset = CAPSET(fd.fd_lmp);
576
577		/*
578		 * Duplicate the original name, as this may be required for
579		 * later diagnostics.  Keep a copy of the file descriptor for
580		 * analysis once all capabilities candidates have been
581		 * determined.
582		 */
583		if (((fd.fd_oname = strdup(fd.fd_oname)) == NULL) ||
584		    (alist_append(&fdalp, &fd, sizeof (Fdesc),
585		    AL_CNT_CAP) == NULL)) {
586			error = 1;
587			break;
588		}
589	}
590	(void) closedir(dir);
591
592	/*
593	 * If no objects have been found, we're done.  Also, if an allocation
594	 * error occurred while processing any object, remove any objects that
595	 * had already been added to the list and return.
596	 */
597	if ((fdalp == NULL) || error) {
598		if (fdalp)
599			free_fd(fdalp);
600		return (0);
601	}
602
603	/*
604	 * Having processed and retained all candidates from this directory,
605	 * sort them, based on the precedence of their hardware capabilities.
606	 */
607	qsort(fdalp->al_data, fdalp->al_nitems, fdalp->al_size, compare);
608
609	*fdalpp = fdalp;
610	return (1);
611}
612
613int
614cap_filtees(Alist **alpp, Aliste oidx, const char *dir, Aliste nlmco,
615    Rt_map *flmp, const char *ref, int mode, uint_t flags, int *in_nfavl)
616{
617	Alist		*fdalp = NULL;
618	Aliste		idx;
619	Fdesc		*fdp;
620	Lm_list		*lml = LIST(flmp);
621	int		unused = 0;
622	Rej_desc	rej = { 0 };
623
624	if (cap_dir(&fdalp, lml, dir, flmp, flags, &rej, in_nfavl) == 0)
625		return (0);
626
627	/*
628	 * Now complete the mapping of each of the ordered objects, adding
629	 * each object to a new pathname descriptor.
630	 */
631	for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
632		Rt_map	*nlmp;
633		Grp_hdl	*ghp = NULL;
634		Pdesc	*pdp;
635		int	audit = 0;
636
637		if (unused)
638			continue;
639
640		/*
641		 * Complete mapping the file, obtaining a handle, and continue
642		 * to analyze the object, establishing dependencies and
643		 * relocating.  Remove the file descriptor at this point, as it
644		 * is no longer required.
645		 */
646		DBG_CALL(Dbg_file_filtee(lml, NAME(flmp), fdp->fd_nname, 0));
647
648		nlmp = load_path(lml, nlmco, flmp, mode,
649		    (flags | FLG_RT_PUBHDL), &ghp, fdp, &rej, in_nfavl);
650		if (nlmp == NULL)
651			continue;
652
653		/*
654		 * Create a new pathname descriptor to represent this filtee,
655		 * and insert this descriptor in the Alist following the
656		 * hardware descriptor that seeded this processing.
657		 */
658		if ((pdp = alist_insert(alpp, 0, sizeof (Pdesc),
659		    AL_CNT_FILTEES, ++oidx)) == NULL) {
660			if (ghp)
661				remove_lmc(lml, flmp, nlmco, NAME(nlmp));
662			return (0);
663		}
664
665		pdp->pd_pname = NAME(nlmp);
666		pdp->pd_plen = strlen(NAME(nlmp));
667
668		/*
669		 * Establish the filter handle to prevent any recursion.
670		 */
671		if (nlmp && ghp) {
672			ghp->gh_flags |= GPH_FILTEE;
673			pdp->pd_info = (void *)ghp;
674		}
675
676		/*
677		 * Audit the filter/filtee established.  A return of 0
678		 * indicates the auditor wishes to ignore this filtee.
679		 */
680		if (nlmp && (lml->lm_tflags | FLAGS1(flmp)) &
681		    LML_TFLG_AUD_OBJFILTER) {
682			if (audit_objfilter(flmp, ref, nlmp, 0) == 0) {
683				audit = 1;
684				nlmp = NULL;
685			}
686		}
687
688		/*
689		 * Finish processing the objects associated with this request.
690		 */
691		if (nlmp && ghp && (((nlmp = analyze_lmc(lml, nlmco, nlmp,
692		    in_nfavl)) == NULL) ||
693		    (relocate_lmc(lml, nlmco, flmp, nlmp, in_nfavl) == 0)))
694			nlmp = NULL;
695
696		/*
697		 * If the filtee has been successfully processed, then create
698		 * an association between the filter and the filtee.  This
699		 * association provides sufficient information to tear down the
700		 * filter and filtee if necessary.
701		 */
702		DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD));
703		if (nlmp && ghp &&
704		    (hdl_add(ghp, flmp, GPD_FILTER, NULL) == NULL))
705			nlmp = NULL;
706
707		/*
708		 * If this object is marked an end-filtee, we're done.
709		 */
710		if (nlmp && ghp && (FLAGS1(nlmp) & FL1_RT_ENDFILTE))
711			unused = 1;
712
713		/*
714		 * If this filtee loading has failed, generate a diagnostic.
715		 * Null out the path name descriptor entry, and continue the
716		 * search.
717		 */
718		if (nlmp == NULL) {
719			DBG_CALL(Dbg_file_filtee(lml, 0, pdp->pd_pname, audit));
720
721			/*
722			 * If attempting to load this filtee required a new
723			 * link-map control list to which this request has
724			 * added objects, then remove all the objects that
725			 * have been associated to this request.
726			 */
727			if (nlmco != ALIST_OFF_DATA)
728				remove_lmc(lml, flmp, nlmco, pdp->pd_pname);
729
730			pdp->pd_plen = 0;
731			pdp->pd_info = NULL;
732		}
733	}
734
735	free_fd(fdalp);
736	return (1);
737}
738
739/*
740 * Load an individual capabilities object.
741 */
742Rt_map *
743load_cap(Lm_list *lml, Aliste lmco, const char *dir, Rt_map *clmp,
744    uint_t mode, uint_t flags, Grp_hdl **hdl, Rej_desc *rej, int *in_nfavl)
745{
746	Alist	*fdalp = NULL;
747	Aliste	idx;
748	Fdesc	*fdp;
749	int	found = 0;
750	Rt_map	*lmp = NULL;
751
752	/*
753	 * Obtain the sorted list of hardware capabilities objects available.
754	 */
755	if (cap_dir(&fdalp, lml, dir, clmp, flags, rej, in_nfavl) == 0)
756		return (NULL);
757
758	/*
759	 * From the list of hardware capability objects, use the first and
760	 * discard the rest.
761	 */
762	for (ALIST_TRAVERSE(fdalp, idx, fdp)) {
763		Fdesc	fd = *fdp;
764
765		if ((found == 0) && ((lmp = load_path(lml, lmco, clmp, mode,
766		    flags, hdl, &fd, rej, in_nfavl)) != NULL))
767			found++;
768	}
769
770	free_fd(fdalp);
771	return (lmp);
772}
773
774/*
775 * Use a case insensitive string match when looking up capability mask
776 * values by name, and omit the AV_ prefix.
777 */
778#define	ELFCAP_STYLE	ELFCAP_STYLE_LC | ELFCAP_STYLE_F_ICMP
779
780/*
781 * To aid in the development and testing of capabilities, an alternative system
782 * capabilities group can be specified.  This alternative set is initialized
783 * from the system capabilities that are normally used to validate all object
784 * loading.  However, the user can disable, enable or override flags within
785 * this alternative set, and thus affect object loading.
786 *
787 * This technique is usually combined with defining the family of objects
788 * that should be compared against this alternative set.  Without defining the
789 * family of objects, all objects loaded by ld.so.1 are validated against the
790 * alternative set.  This can prevent the loading of critical system objects
791 * like libc, and thus prevent process execution.
792 */
793typedef enum {
794	CAP_OVERRIDE =	0,		/* override existing capabilities */
795	CAP_ENABLE =	1,		/* enable capabilities */
796	CAP_DISABLE =	2		/* disable capabilities */
797} cap_mode;
798
799static struct {
800	elfcap_mask_t	cs_val[3];	/* value settings, and indicator for */
801	int		cs_set[3];	/*	OVERRIDE, ENABLE and DISABLE */
802	elfcap_mask_t	*cs_aval;	/* alternative variable for final */
803					/*	update */
804} cap_settings[3] = {
805	{ { 0, 0, 0 }, { 0, 0, 0 }, NULL },		/* CA_SUNW_HW_1 */
806	{ { 0, 0, 0 }, { 0, 0, 0 }, NULL },		/* CA_SUNW_SF_1 */
807	{ { 0, 0, 0 }, { 0, 0, 0 }, NULL }		/* CA_SUNW_HW_2 */
808};
809
810static int
811cap_modify(Xword tag, const char *str)
812{
813	char		*caps, *ptr, *next;
814	cap_mode	mode = CAP_OVERRIDE;
815	Xword		ndx;
816
817	if ((caps = strdup(str)) == NULL)
818		return (0);
819
820	ptr = strtok_r(caps, MSG_ORIG(MSG_CAP_DELIMIT), &next);
821	do {
822		Xword		val = 0;
823
824		/*
825		 * Determine whether this token should be enabled (+),
826		 * disabled (-), or override any existing settings.
827		 */
828		if (*ptr == '+') {
829			mode = CAP_ENABLE;
830			ptr++;
831		} else if (*ptr == '-') {
832			mode = CAP_DISABLE;
833			ptr++;
834		}
835
836		/*
837		 * Process the capabilities as directed by the calling tag.
838		 */
839		switch (tag) {
840		case CA_SUNW_HW_1:
841			/*
842			 * Determine whether the capabilities string matches
843			 * a known hardware capability mask.  Note, the caller
844			 * indicates that these are hardware capabilities by
845			 * passing in the CA_SUNW_HW_1 tag.  However, the
846			 * tokens could be CA_SUNW_HW_1 or CA_SUNW_HW_2.
847			 */
848			if ((val = (Xword)elfcap_hw2_from_str(ELFCAP_STYLE,
849			    ptr, M_MACH)) != 0) {
850				ndx = CA_SUNW_HW_2;
851				break;
852			}
853			if ((val = (Xword)elfcap_hw1_from_str(ELFCAP_STYLE,
854			    ptr, M_MACH)) != 0)
855				ndx = CA_SUNW_HW_1;
856			break;
857		case CA_SUNW_SF_1:
858			/*
859			 * Determine whether the capabilities string matches a
860			 * known software capability mask.  Note, the callers
861			 * indication of what capabilities to process are
862			 * triggered by a tag of CA_SUNW_SF_1, but the tokens
863			 * processed could be CA_SUNW_SF_1, CA_SUNW_SF_2, etc.
864			 */
865			if ((val = (Xword)elfcap_sf1_from_str(ELFCAP_STYLE,
866			    ptr, M_MACH)) != 0)
867				ndx = CA_SUNW_SF_1;
868			break;
869		}
870
871		/*
872		 * If a capabilities token has not been matched, interpret the
873		 * string as a number.  To provide for setting the various
874		 * families (CA_SUNW_HW_1, CA_SUNW_HW_2), the number can be
875		 * prefixed with the (bracketed) family index.
876		 *
877		 *	LD_HWCAP=[1]0x40    sets CA_SUNW_HW_1 with 0x40
878		 *	LD_HWCAP=[2]0x80    sets CA_SUNW_HW_2 with 0x80
879		 *
880		 * Invalid indexes are ignored.
881		 */
882		if (val == 0) {
883			if ((*ptr == '[') && (*(ptr + 2) == ']')) {
884				if (*(ptr + 1) == '1') {
885					ndx = tag;
886					ptr += 3;
887				} else if (*(ptr + 1) == '2') {
888					if (tag == CA_SUNW_HW_1) {
889						ndx = CA_SUNW_HW_2;
890						ptr += 3;
891					} else {
892						/* invalid index */
893						continue;
894					}
895				} else {
896					/* invalid index */
897					continue;
898				}
899			} else
900				ndx = tag;
901
902			errno = 0;
903			if (((val = strtol(ptr, NULL, 16)) == 0) && errno)
904				continue;
905		}
906		cap_settings[ndx - 1].cs_val[mode] |= val;
907		cap_settings[ndx - 1].cs_set[mode]++;
908
909	} while ((ptr = strtok_r(NULL,
910	    MSG_ORIG(MSG_CAP_DELIMIT), &next)) != NULL);
911
912	/*
913	 * If the "override" token was supplied, set the alternative
914	 * system capabilities, then enable or disable others.
915	 */
916	for (ndx = 0; ndx < CA_SUNW_HW_2; ndx++) {
917		if (cap_settings[ndx].cs_set[CAP_OVERRIDE])
918			*(cap_settings[ndx].cs_aval) =
919			    cap_settings[ndx].cs_val[CAP_OVERRIDE];
920		if (cap_settings[ndx].cs_set[CAP_ENABLE])
921			*(cap_settings[ndx].cs_aval) |=
922			    cap_settings[ndx].cs_val[CAP_ENABLE];
923		if (cap_settings[ndx].cs_set[CAP_DISABLE])
924			*(cap_settings[ndx].cs_aval) &=
925			    ~cap_settings[ndx].cs_val[CAP_DISABLE];
926	}
927	free(caps);
928	return (1);
929}
930#undef	ELFCAP_STYLE
931
932/*
933 * Create an AVL tree of objects that are to be validated against an alternative
934 * system capabilities value.
935 */
936static int
937cap_files(const char *str)
938{
939	char	*caps, *name, *next;
940
941	if ((caps = strdup(str)) == NULL)
942		return (0);
943
944	name = strtok_r(caps, MSG_ORIG(MSG_CAP_DELIMIT), &next);
945	do {
946		avl_index_t	where;
947		PathNode	*pnp;
948		uint_t		hash = sgs_str_hash(name);
949
950		/*
951		 * Determine whether this pathname has already been recorded.
952		 */
953		if (pnavl_recorded(&capavl, name, hash, &where))
954			continue;
955
956		if ((pnp = calloc(sizeof (PathNode), 1)) != NULL) {
957			pnp->pn_name = name;
958			pnp->pn_hash = hash;
959			avl_insert(capavl, pnp, where);
960		}
961	} while ((name = strtok_r(NULL,
962	    MSG_ORIG(MSG_CAP_DELIMIT), &next)) != NULL);
963
964	return (1);
965}
966
967/*
968 * Set alternative system capabilities.  A user can establish alternative system
969 * capabilities from the environment, or from a configuration file.  This
970 * routine is called in each instance.  Environment variables only set the
971 * replaceable (rpl) variables.  Configuration files can set both replaceable
972 * (rpl) and permanent (prm) variables.
973 */
974int
975cap_alternative(void)
976{
977	/*
978	 * If no capabilities have been set, we're done.
979	 */
980	if ((rpl_hwcap == NULL) && (rpl_sfcap == NULL) &&
981	    (rpl_machcap == NULL) && (rpl_platcap == NULL) &&
982	    (prm_hwcap == NULL) && (prm_sfcap == NULL) &&
983	    (prm_machcap == NULL) && (prm_platcap == NULL))
984		return (1);
985
986	/*
987	 * If the user has requested to modify any capabilities, establish a
988	 * unique set from the present system capabilities.
989	 */
990	if ((alt_scapset = malloc(sizeof (Syscapset))) == NULL)
991		return (0);
992	*alt_scapset = *org_scapset;
993
994	cap_settings[CA_SUNW_HW_1 - 1].cs_aval = &alt_scapset->sc_hw_1;
995	cap_settings[CA_SUNW_SF_1 - 1].cs_aval = &alt_scapset->sc_sf_1;
996	cap_settings[CA_SUNW_HW_2 - 1].cs_aval = &alt_scapset->sc_hw_2;
997
998	/*
999	 * Process any replaceable variables.
1000	 */
1001	if (rpl_hwcap && (cap_modify(CA_SUNW_HW_1, rpl_hwcap) == 0))
1002		return (0);
1003	if (rpl_sfcap && (cap_modify(CA_SUNW_SF_1, rpl_sfcap) == 0))
1004		return (0);
1005
1006	if (rpl_platcap) {
1007		alt_scapset->sc_plat = (char *)rpl_platcap;
1008		alt_scapset->sc_platsz = strlen(rpl_platcap);
1009	}
1010	if (rpl_machcap) {
1011		alt_scapset->sc_mach = (char *)rpl_machcap;
1012		alt_scapset->sc_machsz = strlen(rpl_machcap);
1013	}
1014
1015	if (rpl_cap_files && (cap_files(rpl_cap_files) == 0))
1016		return (0);
1017
1018	/*
1019	 * Process any permanent variables.
1020	 */
1021	if (prm_hwcap && (cap_modify(CA_SUNW_HW_1, prm_hwcap) == 0))
1022		return (0);
1023	if (prm_sfcap && (cap_modify(CA_SUNW_SF_1, prm_sfcap) == 0))
1024		return (0);
1025
1026	if (prm_platcap) {
1027		alt_scapset->sc_plat = (char *)prm_platcap;
1028		alt_scapset->sc_platsz = strlen(prm_platcap);
1029	}
1030	if (prm_machcap) {
1031		alt_scapset->sc_mach = (char *)prm_machcap;
1032		alt_scapset->sc_machsz = strlen(prm_machcap);
1033	}
1034
1035	if (prm_cap_files && (cap_files(prm_cap_files) == 0))
1036		return (0);
1037
1038	/*
1039	 * Reset the replaceable variables.  If this is the environment variable
1040	 * processing, these variables are now available for configuration file
1041	 * initialization.
1042	 */
1043	rpl_hwcap = rpl_sfcap = rpl_machcap = rpl_platcap =
1044	    rpl_cap_files = NULL;
1045
1046	return (1);
1047}
1048
1049/*
1050 * Take the index from a Capinfo entry and determine the associated capabilities
1051 * set.  Verify that the capabilities are available for this system.
1052 */
1053static int
1054sym_cap_check(Cap *cptr, uint_t cndx, Syscapset *bestcapset, Rt_map *lmp,
1055    const char *name, uint_t ndx)
1056{
1057	Syscapset	*scapset;
1058	int		totplat, ivlplat, totmach, ivlmach, capfail = 0;
1059
1060	/*
1061	 * Determine whether this file requires validation against alternative
1062	 * system capabilities.
1063	 */
1064	if ((FLAGS1(lmp) & FL1_RT_ALTCHECK) == 0)
1065		cap_check_lmp_init(lmp);
1066
1067	if (FLAGS1(lmp) & FL1_RT_ALTCAP)
1068		scapset = alt_scapset;
1069	else
1070		scapset = org_scapset;
1071
1072	totplat = ivlplat = totmach = ivlmach = 0;
1073
1074	/*
1075	 * A capabilities index points to a capabilities group that can consist
1076	 * of one or more capabilities, terminated with a CA_SUNW_NULL entry.
1077	 */
1078	for (cptr += cndx; cptr->c_tag != CA_SUNW_NULL; cptr++) {
1079		Xword	val = cptr->c_un.c_val;
1080		char	*str;
1081
1082		switch (cptr->c_tag) {
1083		case CA_SUNW_HW_1:
1084			bestcapset->sc_hw_1 = val;
1085			DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_HW_1,
1086			    name, ndx, M_MACH, bestcapset));
1087
1088			if (hwcap1_check(scapset, val, NULL) == 0)
1089				capfail++;
1090			break;
1091		case CA_SUNW_SF_1:
1092			bestcapset->sc_sf_1 = val;
1093			DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_SF_1,
1094			    name, ndx, M_MACH, bestcapset));
1095
1096			if (sfcap1_check(scapset, val, NULL) == 0)
1097				capfail++;
1098			break;
1099		case CA_SUNW_HW_2:
1100			bestcapset->sc_hw_2 = val;
1101			DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_HW_2,
1102			    name, ndx, M_MACH, bestcapset));
1103
1104			if (hwcap2_check(scapset, val, NULL) == 0)
1105				capfail++;
1106			break;
1107		case CA_SUNW_PLAT:
1108			/*
1109			 * A capabilities set can define multiple platform names
1110			 * that are appropriate.  Only if all the names are
1111			 * deemed invalid is the group determined inappropriate.
1112			 */
1113			if (totplat == ivlplat) {
1114				totplat++;
1115
1116				str = STRTAB(lmp) + val;
1117				bestcapset->sc_plat = str;
1118
1119				DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_PLAT,
1120				    name, ndx, M_MACH, bestcapset));
1121
1122				if (platcap_check(scapset, str, NULL) == 0)
1123					ivlplat++;
1124			}
1125			break;
1126		case CA_SUNW_MACH:
1127			/*
1128			 * A capabilities set can define multiple machine names
1129			 * that are appropriate.  Only if all the names are
1130			 * deemed invalid is the group determined inappropriate.
1131			 */
1132			if (totmach == ivlmach) {
1133				totmach++;
1134
1135				str = STRTAB(lmp) + val;
1136				bestcapset->sc_mach = str;
1137
1138				DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_MACH,
1139				    name, ndx, M_MACH, bestcapset));
1140
1141				if (machcap_check(scapset, str, NULL) == 0)
1142					ivlmach++;
1143			}
1144			break;
1145		default:
1146			break;
1147		}
1148	}
1149
1150	/*
1151	 * If any platform definitions, or machine definitions were found, and
1152	 * all were invalid, indicate that the object is inappropriate.
1153	 */
1154	if (capfail || (totplat && (totplat == ivlplat)) ||
1155	    (totmach && (totmach == ivlmach))) {
1156		DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_REJECTED, name, ndx,
1157		    M_MACH, NULL));
1158		return (0);
1159	}
1160
1161	DBG_CALL(Dbg_syms_cap_lookup(lmp, DBG_CAP_CANDIDATE, name, ndx,
1162	    M_MACH, NULL));
1163	return (1);
1164}
1165
1166/*
1167 * Determine whether a symbols capabilities are more significant than any that
1168 * have already been validated.  The precedence of capabilities are:
1169 *
1170 *   PLATCAP -> MACHCAP -> HWCAP_2 -> HWCAP_1
1171 *
1172 *
1173 * Presently we make no comparisons of software capabilities.  However, should
1174 * this symbol capability have required the SF1_SUNW_ADDR32 attribute, then
1175 * this would have been validated as appropriate or not.
1176 *
1177 * bestcapset is the presently available 'best' capabilities group, and
1178 * symcapset is the present capabilities group under investigation.  Return 0
1179 * if the bestcapset should remain in affect, or 1 if the symcapset is better.
1180 */
1181inline static int
1182is_sym_the_best(Syscapset *bestcapset, Syscapset *symcapset)
1183{
1184	/*
1185	 * Check any platform capability.  If the new symbol isn't associated
1186	 * with a CA_SUNW_PLAT capability, and the best symbol is, then retain
1187	 * the best capabilities group.  If the new symbol is associated with a
1188	 * CA_SUNW_PLAT capability, and the best symbol isn't, then the new
1189	 * symbol needs to be taken.
1190	 */
1191	if (bestcapset->sc_plat && (symcapset->sc_plat == NULL))
1192		return (0);
1193
1194	if ((bestcapset->sc_plat == NULL) && symcapset->sc_plat)
1195		return (1);
1196
1197	/*
1198	 * Check any machine name capability.  If the new symbol isn't
1199	 * associated with a CA_SUNW_MACH capability, and the best symbol is,
1200	 * then retain the best capabilities group.  If the new symbol is
1201	 * associated with a CA_SUNW_MACH capability, and the best symbol isn't,
1202	 * then the new symbol needs to be taken.
1203	 */
1204	if (bestcapset->sc_mach && (symcapset->sc_mach == NULL))
1205		return (0);
1206
1207	if ((bestcapset->sc_mach == NULL) && symcapset->sc_mach)
1208		return (1);
1209
1210	/*
1211	 * Check the hardware capabilities.  If the best symbols CA_SUNW_HW_2
1212	 * capabilities are greater than the new symbols capabilities, then
1213	 * retain the best capabilities group.  If the new symbols CA_SUNW_HW_2
1214	 * capabilities are greater than the best symbol, then the new symbol
1215	 * needs to be taken.
1216	 */
1217	if (bestcapset->sc_hw_2 > symcapset->sc_hw_2)
1218		return (0);
1219
1220	if (bestcapset->sc_hw_2 < symcapset->sc_hw_2)
1221		return (1);
1222
1223	/*
1224	 * Check the remaining hardware capabilities.  If the best symbols
1225	 * CA_SUNW_HW_1 capabilities are greater than the new symbols
1226	 * capabilities, then retain the best capabilities group.  If the new
1227	 * symbols CA_SUNW_HW_1 capabilities are greater than the best symbol,
1228	 * then the new symbol needs to be taken.
1229	 */
1230	if (bestcapset->sc_hw_1 > symcapset->sc_hw_1)
1231		return (0);
1232
1233	if (bestcapset->sc_hw_1 < symcapset->sc_hw_1)
1234		return (1);
1235
1236	/*
1237	 * Both capabilities are the same.  Retain the best on a first-come
1238	 * first-served basis.
1239	 */
1240	return (0);
1241}
1242
1243/*
1244 * Initiate symbol capabilities processing.  If an initial symbol lookup
1245 * results in binding to a symbol that has an associated SUNW_capinfo entry,
1246 * we arrive here.
1247 *
1248 * The standard model is that this initial symbol is the lead capabilities
1249 * symbol (defined as CAPINFO_SUNW_GLOB) of a capabilities family.  This lead
1250 * symbol's SUNW_capinfo information points to the SUNW_capchain entry that
1251 * provides the family symbol indexes.  We traverse this chain, looking at
1252 * each family member, to discover the best capabilities instance.  This
1253 * instance name and symbol information is returned to establish the final
1254 * symbol binding.
1255 *
1256 * If the symbol that got us here is not CAPINFO_SUNW_GLOB, then we've bound
1257 * directly to a capabilities symbol which must be verified.  This is not the
1258 * model created by ld(1) using -z symbolcap, but might be created directly
1259 * within a relocatable object by the compilation system.
1260 */
1261int
1262cap_match(Sresult *srp, uint_t symndx, Sym *symtabptr, char *strtabptr)
1263{
1264	Rt_map		*ilmp = srp->sr_dmap;
1265	Sym		*bsym = NULL;
1266	const char	*bname;
1267	Syscapset	bestcapset = { 0 };
1268	Cap		*cap;
1269	Capchain	*capchain;
1270	uchar_t		grpndx;
1271	uint_t		ochainndx, nchainndx, bndx;
1272
1273	cap = CAP(ilmp);
1274	capchain = CAPCHAIN(ilmp);
1275
1276	grpndx = (uchar_t)ELF_C_GROUP(CAPINFO(ilmp)[symndx]);
1277
1278	/*
1279	 * If this symbols capability group is not a lead symbol, then simply
1280	 * verify the symbol.
1281	 */
1282	if (grpndx != CAPINFO_SUNW_GLOB) {
1283		Syscapset	symcapset = { 0 };
1284
1285		return (sym_cap_check(cap, grpndx, &symcapset, ilmp,
1286		    srp->sr_name, symndx));
1287	}
1288
1289	/*
1290	 * If there is no capabilities chain, return the lead symbol.
1291	 */
1292	if (capchain == NULL)
1293		return (1);
1294
1295	ochainndx = (uint_t)ELF_C_SYM(CAPINFO(ilmp)[symndx]);
1296
1297	/*
1298	 * If there is only one member for this family, take it.  Once a family
1299	 * has been processed, the best family instance is written to the head
1300	 * of the chain followed by a null entry.  This caching ensures that the
1301	 * same family comparison doesn't have to be undertaken more than once.
1302	 */
1303	if (capchain[ochainndx] && (capchain[ochainndx + 1] == 0)) {
1304		Sym		*fsym = symtabptr + capchain[ochainndx];
1305		const char	*fname = strtabptr + fsym->st_name;
1306
1307		DBG_CALL(Dbg_syms_cap_lookup(ilmp, DBG_CAP_USED, fname,
1308		    capchain[ochainndx], M_MACH, NULL));
1309
1310		srp->sr_sym = fsym;
1311		srp->sr_name = fname;
1312		return (1);
1313	}
1314
1315	/*
1316	 * As this symbol is the lead symbol of a capabilities family, it is
1317	 * considered the generic member, and therefore forms the basic
1318	 * fall-back for the capabilities family.
1319	 */
1320	DBG_CALL(Dbg_syms_cap_lookup(ilmp, DBG_CAP_DEFAULT, srp->sr_name,
1321	    symndx, M_MACH, NULL));
1322	bsym = srp->sr_sym;
1323	bname = srp->sr_name;
1324	bndx = symndx;
1325
1326	/*
1327	 * Traverse the capabilities chain analyzing each family member.
1328	 */
1329	for (nchainndx = ochainndx + 1, symndx = capchain[nchainndx]; symndx;
1330	    nchainndx++, symndx = capchain[nchainndx]) {
1331		Sym		*nsym = symtabptr + symndx;
1332		const char	*nname = strtabptr + nsym->st_name;
1333		Syscapset	symcapset = { 0 };
1334
1335		if ((grpndx =
1336		    (uchar_t)ELF_C_GROUP(CAPINFO(ilmp)[symndx])) == 0)
1337			continue;
1338
1339		if (sym_cap_check(cap, grpndx, &symcapset, ilmp,
1340		    nname, symndx) == 0)
1341			continue;
1342
1343		/*
1344		 * Determine whether a symbol's capabilities are more
1345		 * significant than any that have already been validated.
1346		 */
1347		if (is_sym_the_best(&bestcapset, &symcapset)) {
1348			bestcapset = symcapset;
1349			bsym = nsym;
1350			bname = nname;
1351			bndx = symndx;
1352		}
1353	}
1354
1355	DBG_CALL(Dbg_syms_cap_lookup(ilmp, DBG_CAP_USED, bname, bndx,
1356	    M_MACH, NULL));
1357
1358	/*
1359	 * Having found the best symbol, cache the results by overriding the
1360	 * first element of the associated chain.
1361	 */
1362	capchain[ochainndx] = bndx;
1363	capchain[ochainndx + 1] = 0;
1364
1365	/*
1366	 * Update the symbol result information for return to the user.
1367	 */
1368	srp->sr_sym = bsym;
1369	srp->sr_name = bname;
1370	return (1);
1371}
1372