/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include "_rtld.h" #include "_audit.h" #include "msg.h" /* * qsort(3c) comparison function. */ static int compare(const void *fdesc1, const void *fdesc2) { Xword hwcap1 = ((Fdesc *)fdesc1)->fd_hwcap; Xword hwcap2 = ((Fdesc *)fdesc2)->fd_hwcap; if (hwcap1 && (hwcap2 == 0)) return (-1); if ((hwcap1 == 0) && hwcap2) return (1); if ((hwcap1 == 0) && (hwcap2 == 0)) return (0); if (hwcap1 > hwcap2) return (-1); if (hwcap1 < hwcap2) return (1); return (0); } /* * Process any hardware capabilities. */ int hwcap_check(Xword val, Rej_desc *rej) { Xword mval; /* * Ensure that the kernel can cope with the required capabilities. */ if ((rtld_flags2 & RT_FL2_HWCAP) && ((mval = (val & ~hwcap)) != 0)) { static Conv_cap_val_hw1_buf_t cap_buf; rej->rej_type = SGS_REJ_HWCAP_1; rej->rej_str = conv_cap_val_hw1(mval, M_MACH, 0, &cap_buf); return (0); } return (1); } /* * Process any software capabilities. */ /* ARGSUSED0 */ int sfcap_check(Xword val, Rej_desc *rej) { #if defined(_ELF64) /* * A 64-bit executable that started the process can be restricted to a * 32-bit address space. A 64-bit dependency that is restricted to a * 32-bit address space can not be loaded unless the executable has * established this requirement. */ if ((val & SF1_SUNW_ADDR32) && ((rtld_flags2 & RT_FL2_ADDR32) == 0)) { static Conv_cap_val_sf1_buf_t cap_buf; rej->rej_type = SGS_REJ_SFCAP_1; rej->rej_str = conv_cap_val_sf1(SF1_SUNW_ADDR32, M_MACH, 0, &cap_buf); return (0); } #endif return (1); } /* * When $HWCAP is used to represent dependencies, take the associated directory * and analyze all the files it contains. */ static int hwcap_dir(Alist **fdalpp, Lm_list *lml, const char *dname, Rt_map *clmp, uint_t flags, Rej_desc *rej, int *in_nfavl) { char path[PATH_MAX], *dst; const char *src; DIR *dir; struct dirent *dirent; Alist *fdalp = NULL; int error = 0; /* * Access the directory in preparation for reading its entries. If * successful, establish the initial pathname. */ if ((dir = opendir(dname)) == NULL) { Rej_desc _rej = { 0 }; _rej.rej_type = SGS_REJ_STR; _rej.rej_name = dname; _rej.rej_str = strerror(errno); DBG_CALL(Dbg_file_rejected(lml, &_rej, M_MACH)); rejection_inherit(rej, &_rej); return (0); } for (dst = path, src = dname; *src; dst++, src++) *dst = *src; *dst++ = '/'; /* * Read each entry from the directory and determine whether it is a * valid ELF file. */ while ((dirent = readdir(dir)) != NULL) { const char *file = dirent->d_name; char *_dst; Fdesc fd = { 0 }; Rej_desc _rej = { 0 }; Pdesc pd = { 0 }; /* * Ignore "." and ".." entries. */ if ((file[0] == '.') && ((file[1] == '\0') || ((file[1] == '.') && (file[2] == '\0')))) continue; /* * Complete the full pathname. */ for (_dst = dst, src = file, file = dst; *src; _dst++, src++) *_dst = *src; *_dst = '\0'; /* * Trace the inspection of this file, and determine any * auditor substitution. */ pd.pd_pname = path; pd.pd_flags = PD_FLG_PNSLASH; if (load_trace(lml, &pd, clmp, &fd) == NULL) continue; /* * Note, all directory entries are processed by find_path(), * even entries that are directories themselves. This single * point for control keeps the number of stat()'s down, and * provides a single point for error diagnostics. */ if (find_path(lml, clmp, flags, &fd, &_rej, in_nfavl) == 0) { rejection_inherit(rej, &_rej); continue; } DBG_CALL(Dbg_cap_hw_candidate(lml, fd.fd_nname)); /* * If this object has already been loaded, obtain the hardware * capabilities for later sorting. Otherwise we have a new * candidate. */ if (fd.fd_lmp) fd.fd_hwcap = HWCAP(fd.fd_lmp); if (alist_append(&fdalp, &fd, sizeof (Fdesc), AL_CNT_HWCAP) == NULL) { error = 1; break; } } (void) closedir(dir); /* * If no objects have been found, we're done. Also, if an allocation * error occurred while processing any object, remove any objects that * had already been added to the list and return. */ if ((fdalp == NULL) || error) { if (fdalp) free(fdalp); return (0); } /* * Having processed and retained all candidates from this directory, * sort them, based on the precedence of their hardware capabilities. */ qsort(fdalp->al_data, fdalp->al_nitems, fdalp->al_size, compare); *fdalpp = fdalp; return (1); } int hwcap_filtees(Alist **alpp, Aliste oidx, const char *dir, Aliste nlmco, Rt_map *flmp, const char *ref, int mode, uint_t flags, int *in_nfavl) { Alist *fdalp = NULL; Aliste idx; Fdesc *fdp; Lm_list *lml = LIST(flmp); int unused = 0; Rej_desc rej = { 0 }; if (hwcap_dir(&fdalp, lml, dir, flmp, flags, &rej, in_nfavl) == 0) return (0); /* * Now complete the mapping of each of the ordered objects, adding * each object to a new pathname descriptor. */ for (ALIST_TRAVERSE(fdalp, idx, fdp)) { Rt_map *nlmp; Grp_hdl *ghp = NULL; Pdesc *pdp; int audit = 0; if (unused) continue; /* * Complete mapping the file, obtaining a handle, and continue * to analyze the object, establishing dependencies and * relocating. Remove the file descriptor at this point, as it * is no longer required. */ DBG_CALL(Dbg_file_filtee(lml, NAME(flmp), fdp->fd_nname, 0)); nlmp = load_path(lml, nlmco, flmp, mode, (flags | FLG_RT_PUBHDL), &ghp, fdp, &rej, in_nfavl); if (nlmp == NULL) continue; /* * Create a new pathname descriptor to represent this filtee, * and insert this descriptor in the Alist following the * hardware descriptor that seeded this processing. */ if ((pdp = alist_insert(alpp, 0, sizeof (Pdesc), AL_CNT_FILTEES, ++oidx)) == NULL) { if (ghp) remove_lmc(lml, flmp, nlmco, NAME(nlmp)); return (0); } pdp->pd_pname = NAME(nlmp); pdp->pd_plen = strlen(NAME(nlmp)); /* * Establish the filter handle to prevent any recursion. */ if (nlmp && ghp) { ghp->gh_flags |= GPH_FILTEE; pdp->pd_info = (void *)ghp; } /* * Audit the filter/filtee established. A return of 0 * indicates the auditor wishes to ignore this filtee. */ if (nlmp && (lml->lm_tflags | FLAGS1(flmp)) & LML_TFLG_AUD_OBJFILTER) { if (audit_objfilter(flmp, ref, nlmp, 0) == 0) { audit = 1; nlmp = NULL; } } /* * Finish processing the objects associated with this request. */ if (nlmp && ghp && (((nlmp = analyze_lmc(lml, nlmco, nlmp, in_nfavl)) == NULL) || (relocate_lmc(lml, nlmco, flmp, nlmp, in_nfavl) == 0))) nlmp = NULL; /* * If the filtee has been successfully processed, then create * an association between the filter and the filtee. This * association provides sufficient information to tear down the * filter and filtee if necessary. */ DBG_CALL(Dbg_file_hdl_title(DBG_HDL_ADD)); if (nlmp && ghp && (hdl_add(ghp, flmp, GPD_FILTER, NULL) == NULL)) nlmp = NULL; /* * If this object is marked an end-filtee, we're done. */ if (nlmp && ghp && (FLAGS1(nlmp) & FL1_RT_ENDFILTE)) unused = 1; /* * If this filtee loading has failed, generate a diagnostic. * Null out the path name descriptor entry, and continue the * search. */ if (nlmp == NULL) { DBG_CALL(Dbg_file_filtee(lml, 0, pdp->pd_pname, audit)); /* * If attempting to load this filtee required a new * link-map control list to which this request has * added objects, then remove all the objects that * have been associated to this request. */ if (nlmco != ALIST_OFF_DATA) remove_lmc(lml, flmp, nlmco, pdp->pd_pname); pdp->pd_plen = 0; pdp->pd_info = NULL; } } free(fdalp); return (1); } /* * Load an individual hardware capabilities object. */ Rt_map * load_hwcap(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) { Alist *fdalp = NULL; Aliste idx; Fdesc *fdp; int found = 0; Rt_map *lmp = NULL; /* * Obtain the sorted list of hardware capabilities objects available. */ if (hwcap_dir(&fdalp, lml, dir, clmp, flags, rej, in_nfavl) == 0) return (NULL); /* * From the list of hardware capability objects, use the first and * discard the rest. */ for (ALIST_TRAVERSE(fdalp, idx, fdp)) { Fdesc fd = *fdp; if ((found == 0) && ((lmp = load_path(lml, lmco, clmp, mode, flags, hdl, &fd, rej, in_nfavl)) != NULL)) found++; } free(fdalp); return (lmp); }