1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright (c) 1988 AT&T 24*0Sstevel@tonic-gate * All Rights Reserved 25*0Sstevel@tonic-gate * 26*0Sstevel@tonic-gate * 27*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 28*0Sstevel@tonic-gate * Use is subject to license terms. 29*0Sstevel@tonic-gate */ 30*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 31*0Sstevel@tonic-gate 32*0Sstevel@tonic-gate /* 33*0Sstevel@tonic-gate * Print the list of shared objects required by a dynamic executable or shared 34*0Sstevel@tonic-gate * object. 35*0Sstevel@tonic-gate * 36*0Sstevel@tonic-gate * usage is: ldd [-d | -r] [-c] [-e envar] [-i] [-f] [-L] [-l] [-s] 37*0Sstevel@tonic-gate * [-U | -u] [-v] file(s) 38*0Sstevel@tonic-gate * 39*0Sstevel@tonic-gate * ldd opens the file and verifies the information in the elf header. 40*0Sstevel@tonic-gate * If the file is a dynamic executable, we set up some environment variables 41*0Sstevel@tonic-gate * and exec(2) the file. If the file is a shared object, we preload the 42*0Sstevel@tonic-gate * file with a dynamic executable stub. The runtime linker (ld.so.1) actually 43*0Sstevel@tonic-gate * provides the diagnostic output, according to the environment variables set. 44*0Sstevel@tonic-gate * 45*0Sstevel@tonic-gate * If neither -d nor -r is specified, we set only LD_TRACE_LOADED_OBJECTS_[AE]. 46*0Sstevel@tonic-gate * The runtime linker will print the pathnames of all dynamic objects it 47*0Sstevel@tonic-gate * loads, and then exit. Note that we distiguish between ELF and AOUT objects 48*0Sstevel@tonic-gate * when setting this environment variable - AOUT executables cause the mapping 49*0Sstevel@tonic-gate * of sbcp, the dependencies of which the user isn't interested in. 50*0Sstevel@tonic-gate * 51*0Sstevel@tonic-gate * If -d or -r is specified, we also set LD_WARN=1; the runtime linker will 52*0Sstevel@tonic-gate * perform its normal relocations and issue warning messages for unresolved 53*0Sstevel@tonic-gate * references. It will then exit. 54*0Sstevel@tonic-gate * If -r is specified, we set LD_BIND_NOW=1, so that the runtime linker 55*0Sstevel@tonic-gate * will perform all relocations, otherwise (under -d) the runtime linker 56*0Sstevel@tonic-gate * will not perform PLT (function) type relocations. 57*0Sstevel@tonic-gate * 58*0Sstevel@tonic-gate * If -c is specified we also set LD_NOCONFIG=1, thus disabling any 59*0Sstevel@tonic-gate * configuration file use. 60*0Sstevel@tonic-gate * 61*0Sstevel@tonic-gate * If -e is specified the associated environment variable is set for the 62*0Sstevel@tonic-gate * child process that will produce ldd's diagnostics. 63*0Sstevel@tonic-gate * 64*0Sstevel@tonic-gate * If -i is specified, we set LD_INIT=1. The order of inititialization 65*0Sstevel@tonic-gate * sections to be executed is printed. We also set LD_WARN=1. 66*0Sstevel@tonic-gate * 67*0Sstevel@tonic-gate * If -f is specified, we will run ldd as root on executables that have 68*0Sstevel@tonic-gate * an unsercure runtime linker that does not live under the "/usr/lib" 69*0Sstevel@tonic-gate * directory. By default we will not let this happen. 70*0Sstevel@tonic-gate * 71*0Sstevel@tonic-gate * If -l is specified it generates a warning for any auxiliary filter not found. 72*0Sstevel@tonic-gate * Prior to 2.8 this forced any filters to load (all) their filtees. This is 73*0Sstevel@tonic-gate * now the default, however missing auxiliary filters don't generate any error 74*0Sstevel@tonic-gate * diagniostic. See also -L. 75*0Sstevel@tonic-gate * 76*0Sstevel@tonic-gate * If -L is specified we revert to lazy loading, thus any filtee or lazy 77*0Sstevel@tonic-gate * dependency loading is deferred until relocations cause loading. Without 78*0Sstevel@tonic-gate * this option we set LD_LOADFLTR=1, thus forcing any filters to load (all) 79*0Sstevel@tonic-gate * their filtees, and LD_NOLAZYLOAD=1 thus forcing immediate processing of 80*0Sstevel@tonic-gate * any lazy loaded dependencies. 81*0Sstevel@tonic-gate * 82*0Sstevel@tonic-gate * If -s is specified we also set LD_TRACE_SEARCH_PATH=1, thus enabling 83*0Sstevel@tonic-gate * the runtime linker to indicate the search algorithm used. 84*0Sstevel@tonic-gate * 85*0Sstevel@tonic-gate * If -v is specified we also set LD_VERBOSE=1, thus enabling the runtime 86*0Sstevel@tonic-gate * linker to indicate all object dependencies (not just the first object 87*0Sstevel@tonic-gate * loaded) together with any versionig requirements. 88*0Sstevel@tonic-gate * 89*0Sstevel@tonic-gate * If -U or -u is specified unused dependencies are detected. -u causes 90*0Sstevel@tonic-gate * LD_UNUSED=1 to be set, which causes dependencies that are unused within the 91*0Sstevel@tonic-gate * process to be detected. -U causes LD_UNREF=1 to be set, which causes 92*0Sstevel@tonic-gate * unreferenced objects, and unreferenced cyclic dependencies to be detected. 93*0Sstevel@tonic-gate * These options assert that at least -d is set as relocation references are 94*0Sstevel@tonic-gate * what determine an objects use. 95*0Sstevel@tonic-gate */ 96*0Sstevel@tonic-gate #include <fcntl.h> 97*0Sstevel@tonic-gate #include <stdio.h> 98*0Sstevel@tonic-gate #include <string.h> 99*0Sstevel@tonic-gate #include <libelf.h> 100*0Sstevel@tonic-gate #include <gelf.h> 101*0Sstevel@tonic-gate #include <stdlib.h> 102*0Sstevel@tonic-gate #include <unistd.h> 103*0Sstevel@tonic-gate #include <wait.h> 104*0Sstevel@tonic-gate #include <locale.h> 105*0Sstevel@tonic-gate #include <errno.h> 106*0Sstevel@tonic-gate #include <signal.h> 107*0Sstevel@tonic-gate #include "machdep.h" 108*0Sstevel@tonic-gate #include "sgs.h" 109*0Sstevel@tonic-gate #include "conv.h" 110*0Sstevel@tonic-gate #include "a.out.h" 111*0Sstevel@tonic-gate #include "msg.h" 112*0Sstevel@tonic-gate 113*0Sstevel@tonic-gate static int elf_check(int, char *, char *, Elf *, int); 114*0Sstevel@tonic-gate static int aout_check(int, char *, char *, int, int); 115*0Sstevel@tonic-gate static int run(int, char *, char *, const char *, int); 116*0Sstevel@tonic-gate 117*0Sstevel@tonic-gate 118*0Sstevel@tonic-gate /* 119*0Sstevel@tonic-gate * The following size definitions provide for allocating space for the string, 120*0Sstevel@tonic-gate * or the string position at which any modifications to the variable will occur. 121*0Sstevel@tonic-gate */ 122*0Sstevel@tonic-gate #define LD_LOAD_SIZE 27 123*0Sstevel@tonic-gate #define LD_PATH_SIZE 23 124*0Sstevel@tonic-gate #define LD_BIND_SIZE 13 125*0Sstevel@tonic-gate #define LD_VERB_SIZE 12 126*0Sstevel@tonic-gate #define LD_WARN_SIZE 9 127*0Sstevel@tonic-gate #define LD_CONF_SIZE 13 128*0Sstevel@tonic-gate #define LD_FLTR_SIZE 13 129*0Sstevel@tonic-gate #define LD_LAZY_SIZE 15 130*0Sstevel@tonic-gate #define LD_INIT_SIZE 9 131*0Sstevel@tonic-gate #define LD_UREF_SIZE 10 132*0Sstevel@tonic-gate #define LD_USED_SIZE 11 133*0Sstevel@tonic-gate 134*0Sstevel@tonic-gate static char bind[] = "LD_BIND_NOW= ", 135*0Sstevel@tonic-gate load_elf[] = "LD_TRACE_LOADED_OBJECTS_E= ", 136*0Sstevel@tonic-gate load_aout[] = "LD_TRACE_LOADED_OBJECTS_A= ", 137*0Sstevel@tonic-gate path[] = "LD_TRACE_SEARCH_PATHS= ", 138*0Sstevel@tonic-gate verb[] = "LD_VERBOSE= ", 139*0Sstevel@tonic-gate warn[] = "LD_WARN= ", 140*0Sstevel@tonic-gate conf[] = "LD_NOCONFIG= ", 141*0Sstevel@tonic-gate fltr[] = "LD_LOADFLTR= ", 142*0Sstevel@tonic-gate lazy[] = "LD_NOLAZYLOAD=1", 143*0Sstevel@tonic-gate init[] = "LD_INIT= ", 144*0Sstevel@tonic-gate uref[] = "LD_UNREF= ", 145*0Sstevel@tonic-gate used[] = "LD_UNUSED= "; 146*0Sstevel@tonic-gate static char *load; 147*0Sstevel@tonic-gate 148*0Sstevel@tonic-gate static const char *prefile_32, *prefile_64, *prefile; 149*0Sstevel@tonic-gate static List eopts = { 0, 0 }; 150*0Sstevel@tonic-gate 151*0Sstevel@tonic-gate /* 152*0Sstevel@tonic-gate * Append an item to the specified list, and return a pointer to the list 153*0Sstevel@tonic-gate * node created. 154*0Sstevel@tonic-gate */ 155*0Sstevel@tonic-gate Listnode * 156*0Sstevel@tonic-gate list_append(List *lst, const void *item) 157*0Sstevel@tonic-gate { 158*0Sstevel@tonic-gate Listnode *lnp; 159*0Sstevel@tonic-gate 160*0Sstevel@tonic-gate if ((lnp = malloc(sizeof (Listnode))) == (Listnode *)0) 161*0Sstevel@tonic-gate return (0); 162*0Sstevel@tonic-gate 163*0Sstevel@tonic-gate lnp->data = (void *)item; 164*0Sstevel@tonic-gate lnp->next = NULL; 165*0Sstevel@tonic-gate 166*0Sstevel@tonic-gate if (lst->head == NULL) 167*0Sstevel@tonic-gate lst->tail = lst->head = lnp; 168*0Sstevel@tonic-gate else { 169*0Sstevel@tonic-gate lst->tail->next = lnp; 170*0Sstevel@tonic-gate lst->tail = lst->tail->next; 171*0Sstevel@tonic-gate } 172*0Sstevel@tonic-gate return (lnp); 173*0Sstevel@tonic-gate } 174*0Sstevel@tonic-gate 175*0Sstevel@tonic-gate int 176*0Sstevel@tonic-gate main(int argc, char **argv) 177*0Sstevel@tonic-gate { 178*0Sstevel@tonic-gate char *str, *cname = argv[0]; 179*0Sstevel@tonic-gate 180*0Sstevel@tonic-gate Elf *elf; 181*0Sstevel@tonic-gate int cflag = 0, dflag = 0, fflag = 0, iflag = 0, Lflag = 0; 182*0Sstevel@tonic-gate int lflag = 0, rflag = 0, sflag = 0, Uflag = 0, uflag = 0; 183*0Sstevel@tonic-gate int vflag = 0, nfile, var, error = 0; 184*0Sstevel@tonic-gate 185*0Sstevel@tonic-gate Listnode *lnp; 186*0Sstevel@tonic-gate 187*0Sstevel@tonic-gate /* 188*0Sstevel@tonic-gate * Establish locale. 189*0Sstevel@tonic-gate */ 190*0Sstevel@tonic-gate (void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY)); 191*0Sstevel@tonic-gate (void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS)); 192*0Sstevel@tonic-gate 193*0Sstevel@tonic-gate /* 194*0Sstevel@tonic-gate * verify command line syntax and process arguments 195*0Sstevel@tonic-gate */ 196*0Sstevel@tonic-gate opterr = 0; /* disable getopt error mesg */ 197*0Sstevel@tonic-gate 198*0Sstevel@tonic-gate while ((var = getopt(argc, argv, MSG_ORIG(MSG_STR_GETOPT))) != EOF) { 199*0Sstevel@tonic-gate switch (var) { 200*0Sstevel@tonic-gate case 'c' : /* enable config search */ 201*0Sstevel@tonic-gate cflag = 1; 202*0Sstevel@tonic-gate break; 203*0Sstevel@tonic-gate case 'd' : /* perform data relocations */ 204*0Sstevel@tonic-gate dflag = 1; 205*0Sstevel@tonic-gate if (rflag) 206*0Sstevel@tonic-gate error++; 207*0Sstevel@tonic-gate break; 208*0Sstevel@tonic-gate case 'e' : 209*0Sstevel@tonic-gate if (list_append(&eopts, optarg) == 0) { 210*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), 211*0Sstevel@tonic-gate cname); 212*0Sstevel@tonic-gate exit(1); 213*0Sstevel@tonic-gate } 214*0Sstevel@tonic-gate break; 215*0Sstevel@tonic-gate case 'f' : 216*0Sstevel@tonic-gate fflag = 1; 217*0Sstevel@tonic-gate break; 218*0Sstevel@tonic-gate case 'L' : 219*0Sstevel@tonic-gate Lflag = 1; 220*0Sstevel@tonic-gate break; 221*0Sstevel@tonic-gate case 'l' : 222*0Sstevel@tonic-gate lflag = 1; 223*0Sstevel@tonic-gate break; 224*0Sstevel@tonic-gate case 'i' : /* print the order of .init */ 225*0Sstevel@tonic-gate iflag = 1; 226*0Sstevel@tonic-gate break; 227*0Sstevel@tonic-gate case 'r' : /* perform all relocations */ 228*0Sstevel@tonic-gate rflag = 1; 229*0Sstevel@tonic-gate if (dflag) 230*0Sstevel@tonic-gate error++; 231*0Sstevel@tonic-gate break; 232*0Sstevel@tonic-gate case 's' : /* enable search path output */ 233*0Sstevel@tonic-gate sflag = 1; 234*0Sstevel@tonic-gate break; 235*0Sstevel@tonic-gate case 'U' : /* list unreferenced */ 236*0Sstevel@tonic-gate Uflag = 1; /* dependencies */ 237*0Sstevel@tonic-gate if (uflag) 238*0Sstevel@tonic-gate error++; 239*0Sstevel@tonic-gate break; 240*0Sstevel@tonic-gate case 'u' : /* list unused dependencies */ 241*0Sstevel@tonic-gate uflag = 1; 242*0Sstevel@tonic-gate if (Uflag) 243*0Sstevel@tonic-gate error++; 244*0Sstevel@tonic-gate break; 245*0Sstevel@tonic-gate case 'v' : /* enable verbose output */ 246*0Sstevel@tonic-gate vflag = 1; 247*0Sstevel@tonic-gate break; 248*0Sstevel@tonic-gate default : 249*0Sstevel@tonic-gate error++; 250*0Sstevel@tonic-gate break; 251*0Sstevel@tonic-gate } 252*0Sstevel@tonic-gate if (error) 253*0Sstevel@tonic-gate break; 254*0Sstevel@tonic-gate } 255*0Sstevel@tonic-gate if (error) { 256*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_ARG_USAGE), cname); 257*0Sstevel@tonic-gate exit(1); 258*0Sstevel@tonic-gate } 259*0Sstevel@tonic-gate 260*0Sstevel@tonic-gate /* 261*0Sstevel@tonic-gate * Determine if any of the LD_PRELOAD family is already set in the 262*0Sstevel@tonic-gate * environment, if so we'll continue to analyze each object with the 263*0Sstevel@tonic-gate * appropriate setting. 264*0Sstevel@tonic-gate */ 265*0Sstevel@tonic-gate if (((prefile_32 = getenv(MSG_ORIG(MSG_LD_PRELOAD_32))) == NULL) || 266*0Sstevel@tonic-gate (*prefile_32 == '\0')) { 267*0Sstevel@tonic-gate prefile_32 = MSG_ORIG(MSG_STR_EMPTY); 268*0Sstevel@tonic-gate } 269*0Sstevel@tonic-gate if (((prefile_64 = getenv(MSG_ORIG(MSG_LD_PRELOAD_64))) == NULL) || 270*0Sstevel@tonic-gate (*prefile_64 == '\0')) { 271*0Sstevel@tonic-gate prefile_64 = MSG_ORIG(MSG_STR_EMPTY); 272*0Sstevel@tonic-gate } 273*0Sstevel@tonic-gate if (((prefile = getenv(MSG_ORIG(MSG_LD_PRELOAD))) == NULL) || 274*0Sstevel@tonic-gate (*prefile == '\0')) { 275*0Sstevel@tonic-gate prefile = MSG_ORIG(MSG_STR_EMPTY); 276*0Sstevel@tonic-gate } 277*0Sstevel@tonic-gate 278*0Sstevel@tonic-gate /* 279*0Sstevel@tonic-gate * Determine if any environment requests are for the LD_PRELOAD family, 280*0Sstevel@tonic-gate * and if so override any environment settings we've established above. 281*0Sstevel@tonic-gate */ 282*0Sstevel@tonic-gate for (LIST_TRAVERSE(&eopts, lnp, str)) { 283*0Sstevel@tonic-gate if ((strncmp(str, MSG_ORIG(MSG_LD_PRELOAD_32), 284*0Sstevel@tonic-gate MSG_LD_PRELOAD_32_SIZE)) == 0) { 285*0Sstevel@tonic-gate str += MSG_LD_PRELOAD_32_SIZE; 286*0Sstevel@tonic-gate if ((*str++ == '=') && (*str != '\0')) 287*0Sstevel@tonic-gate prefile_32 = str; 288*0Sstevel@tonic-gate continue; 289*0Sstevel@tonic-gate } 290*0Sstevel@tonic-gate if ((strncmp(str, MSG_ORIG(MSG_LD_PRELOAD_64), 291*0Sstevel@tonic-gate MSG_LD_PRELOAD_64_SIZE)) == 0) { 292*0Sstevel@tonic-gate str += MSG_LD_PRELOAD_64_SIZE; 293*0Sstevel@tonic-gate if ((*str++ == '=') && (*str != '\0')) 294*0Sstevel@tonic-gate prefile_64 = str; 295*0Sstevel@tonic-gate continue; 296*0Sstevel@tonic-gate } 297*0Sstevel@tonic-gate if ((strncmp(str, MSG_ORIG(MSG_LD_PRELOAD), 298*0Sstevel@tonic-gate MSG_LD_PRELOAD_SIZE)) == 0) { 299*0Sstevel@tonic-gate str += MSG_LD_PRELOAD_SIZE; 300*0Sstevel@tonic-gate if ((*str++ == '=') && (*str != '\0')) 301*0Sstevel@tonic-gate prefile = str; 302*0Sstevel@tonic-gate continue; 303*0Sstevel@tonic-gate } 304*0Sstevel@tonic-gate } 305*0Sstevel@tonic-gate 306*0Sstevel@tonic-gate /* 307*0Sstevel@tonic-gate * Set the appropriate relocation environment variables (Note unsetting 308*0Sstevel@tonic-gate * the environment variables is done just in case the user already 309*0Sstevel@tonic-gate * has these in their environment ... sort of thing the test folks 310*0Sstevel@tonic-gate * would do :-) 311*0Sstevel@tonic-gate */ 312*0Sstevel@tonic-gate warn[LD_WARN_SIZE - 1] = (dflag || rflag || Uflag || uflag) ? '1' : 313*0Sstevel@tonic-gate '\0'; 314*0Sstevel@tonic-gate bind[LD_BIND_SIZE - 1] = (rflag) ? '1' : '\0'; 315*0Sstevel@tonic-gate path[LD_PATH_SIZE - 1] = (sflag) ? '1' : '\0'; 316*0Sstevel@tonic-gate verb[LD_VERB_SIZE - 1] = (vflag) ? '1' : '\0'; 317*0Sstevel@tonic-gate fltr[LD_FLTR_SIZE - 1] = (Lflag) ? '\0' : (lflag) ? '2' : '1'; 318*0Sstevel@tonic-gate init[LD_INIT_SIZE - 1] = (iflag) ? '1' : '\0'; 319*0Sstevel@tonic-gate conf[LD_CONF_SIZE - 1] = (cflag) ? '1' : '\0'; 320*0Sstevel@tonic-gate lazy[LD_LAZY_SIZE - 1] = (Lflag) ? '\0' : '1'; 321*0Sstevel@tonic-gate uref[LD_UREF_SIZE - 1] = (Uflag) ? '1' : '\0'; 322*0Sstevel@tonic-gate used[LD_USED_SIZE - 1] = (uflag) ? '1' : '\0'; 323*0Sstevel@tonic-gate 324*0Sstevel@tonic-gate /* 325*0Sstevel@tonic-gate * coordinate libelf's version information 326*0Sstevel@tonic-gate */ 327*0Sstevel@tonic-gate if (elf_version(EV_CURRENT) == EV_NONE) { 328*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_ELF_LIBELF), cname, 329*0Sstevel@tonic-gate EV_CURRENT); 330*0Sstevel@tonic-gate exit(1); 331*0Sstevel@tonic-gate } 332*0Sstevel@tonic-gate 333*0Sstevel@tonic-gate /* 334*0Sstevel@tonic-gate * Loop through remaining arguments. Note that from here on there 335*0Sstevel@tonic-gate * are no exit conditions so that we can process a list of files, 336*0Sstevel@tonic-gate * any error condition is retained for a final exit status. 337*0Sstevel@tonic-gate */ 338*0Sstevel@tonic-gate nfile = argc - optind; 339*0Sstevel@tonic-gate for (; optind < argc; optind++) { 340*0Sstevel@tonic-gate char *fname = argv[optind]; 341*0Sstevel@tonic-gate 342*0Sstevel@tonic-gate /* 343*0Sstevel@tonic-gate * Open file (do this before checking access so that we can 344*0Sstevel@tonic-gate * provide the user with better diagnostics). 345*0Sstevel@tonic-gate */ 346*0Sstevel@tonic-gate if ((var = open(fname, O_RDONLY)) == -1) { 347*0Sstevel@tonic-gate int err = errno; 348*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), cname, 349*0Sstevel@tonic-gate fname, strerror(err)); 350*0Sstevel@tonic-gate error = 1; 351*0Sstevel@tonic-gate continue; 352*0Sstevel@tonic-gate } 353*0Sstevel@tonic-gate 354*0Sstevel@tonic-gate /* 355*0Sstevel@tonic-gate * Get the files elf descriptor and process it as an elf or 356*0Sstevel@tonic-gate * a.out (4.x) file. 357*0Sstevel@tonic-gate */ 358*0Sstevel@tonic-gate elf = elf_begin(var, ELF_C_READ, (Elf *)0); 359*0Sstevel@tonic-gate switch (elf_kind(elf)) { 360*0Sstevel@tonic-gate case ELF_K_AR : 361*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_USP_NODYNORSO), 362*0Sstevel@tonic-gate cname, fname); 363*0Sstevel@tonic-gate error = 1; 364*0Sstevel@tonic-gate break; 365*0Sstevel@tonic-gate case ELF_K_COFF: 366*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_USP_UNKNOWN), 367*0Sstevel@tonic-gate cname, fname); 368*0Sstevel@tonic-gate error = 1; 369*0Sstevel@tonic-gate break; 370*0Sstevel@tonic-gate case ELF_K_ELF: 371*0Sstevel@tonic-gate if (elf_check(nfile, fname, cname, elf, fflag) != NULL) 372*0Sstevel@tonic-gate error = 1; 373*0Sstevel@tonic-gate break; 374*0Sstevel@tonic-gate default: 375*0Sstevel@tonic-gate /* 376*0Sstevel@tonic-gate * This is either an unknown file or an aout format 377*0Sstevel@tonic-gate */ 378*0Sstevel@tonic-gate if (aout_check(nfile, fname, cname, var, fflag) != NULL) 379*0Sstevel@tonic-gate error = 1; 380*0Sstevel@tonic-gate break; 381*0Sstevel@tonic-gate } 382*0Sstevel@tonic-gate (void) elf_end(elf); 383*0Sstevel@tonic-gate (void) close(var); 384*0Sstevel@tonic-gate } 385*0Sstevel@tonic-gate return (error); 386*0Sstevel@tonic-gate } 387*0Sstevel@tonic-gate 388*0Sstevel@tonic-gate 389*0Sstevel@tonic-gate 390*0Sstevel@tonic-gate static int 391*0Sstevel@tonic-gate is_runnable(GElf_Ehdr *ehdr) 392*0Sstevel@tonic-gate { 393*0Sstevel@tonic-gate if ((ehdr->e_ident[EI_CLASS] == M_CLASS) && 394*0Sstevel@tonic-gate (ehdr->e_ident[EI_DATA] == M_DATA)) 395*0Sstevel@tonic-gate return (ELFCLASS32); 396*0Sstevel@tonic-gate 397*0Sstevel@tonic-gate #if defined(sparc) 398*0Sstevel@tonic-gate if ((ehdr->e_machine == EM_SPARCV9) && 399*0Sstevel@tonic-gate (ehdr->e_ident[EI_DATA] == M_DATA) && 400*0Sstevel@tonic-gate (conv_sys_eclass() == ELFCLASS64)) 401*0Sstevel@tonic-gate return (ELFCLASS64); 402*0Sstevel@tonic-gate #elif defined(i386) || defined(__amd64) 403*0Sstevel@tonic-gate if ((ehdr->e_machine == EM_AMD64) && 404*0Sstevel@tonic-gate (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) && 405*0Sstevel@tonic-gate (conv_sys_eclass() == ELFCLASS64)) 406*0Sstevel@tonic-gate return (ELFCLASS64); 407*0Sstevel@tonic-gate #endif 408*0Sstevel@tonic-gate 409*0Sstevel@tonic-gate return (ELFCLASSNONE); 410*0Sstevel@tonic-gate } 411*0Sstevel@tonic-gate 412*0Sstevel@tonic-gate 413*0Sstevel@tonic-gate static int 414*0Sstevel@tonic-gate elf_check(int nfile, char *fname, char *cname, Elf *elf, int fflag) 415*0Sstevel@tonic-gate { 416*0Sstevel@tonic-gate GElf_Ehdr ehdr; 417*0Sstevel@tonic-gate GElf_Phdr phdr; 418*0Sstevel@tonic-gate int dynamic = 0, interp = 0, cnt, class; 419*0Sstevel@tonic-gate 420*0Sstevel@tonic-gate /* 421*0Sstevel@tonic-gate * verify information in file header 422*0Sstevel@tonic-gate */ 423*0Sstevel@tonic-gate if (gelf_getehdr(elf, &ehdr) == NULL) { 424*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_ELF_GETEHDR), 425*0Sstevel@tonic-gate cname, fname, elf_errmsg(-1)); 426*0Sstevel@tonic-gate return (1); 427*0Sstevel@tonic-gate } 428*0Sstevel@tonic-gate 429*0Sstevel@tonic-gate /* 430*0Sstevel@tonic-gate * check class and encoding 431*0Sstevel@tonic-gate */ 432*0Sstevel@tonic-gate if ((class = is_runnable(&ehdr)) == ELFCLASSNONE) { 433*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_ELF_CLASSDATA), 434*0Sstevel@tonic-gate cname, fname); 435*0Sstevel@tonic-gate return (1); 436*0Sstevel@tonic-gate } 437*0Sstevel@tonic-gate 438*0Sstevel@tonic-gate /* 439*0Sstevel@tonic-gate * check type 440*0Sstevel@tonic-gate */ 441*0Sstevel@tonic-gate if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN) && 442*0Sstevel@tonic-gate (ehdr.e_type != ET_REL)) { 443*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_ELF_BADMAGIC), 444*0Sstevel@tonic-gate cname, fname); 445*0Sstevel@tonic-gate return (1); 446*0Sstevel@tonic-gate } 447*0Sstevel@tonic-gate if ((class == ELFCLASS32) && (ehdr.e_machine != M_MACH)) { 448*0Sstevel@tonic-gate if (ehdr.e_machine != M_MACHPLUS) { 449*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_ELF_MACHTYPE), 450*0Sstevel@tonic-gate cname, fname); 451*0Sstevel@tonic-gate return (1); 452*0Sstevel@tonic-gate } 453*0Sstevel@tonic-gate if ((ehdr.e_flags & M_FLAGSPLUS) == 0) { 454*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_ELF_MACHFLAGS), 455*0Sstevel@tonic-gate cname, fname); 456*0Sstevel@tonic-gate return (1); 457*0Sstevel@tonic-gate } 458*0Sstevel@tonic-gate } 459*0Sstevel@tonic-gate 460*0Sstevel@tonic-gate /* 461*0Sstevel@tonic-gate * Check that the file is executable. Dynamic executables must be 462*0Sstevel@tonic-gate * executable to be exec'ed. Shared objects need not be executable to 463*0Sstevel@tonic-gate * be mapped with a dynamic executable, however, by convention they're 464*0Sstevel@tonic-gate * supposed to be executable. 465*0Sstevel@tonic-gate */ 466*0Sstevel@tonic-gate if (access(fname, X_OK) != 0) { 467*0Sstevel@tonic-gate if (ehdr.e_type == ET_EXEC) { 468*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_USP_NOTEXEC_1), 469*0Sstevel@tonic-gate cname, fname); 470*0Sstevel@tonic-gate return (1); 471*0Sstevel@tonic-gate } 472*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_USP_NOTEXEC_2), cname, 473*0Sstevel@tonic-gate fname); 474*0Sstevel@tonic-gate } 475*0Sstevel@tonic-gate 476*0Sstevel@tonic-gate /* 477*0Sstevel@tonic-gate * Determine whether we have a dynamic section or interpretor. 478*0Sstevel@tonic-gate */ 479*0Sstevel@tonic-gate for (cnt = 0; cnt < (int)ehdr.e_phnum; cnt++) { 480*0Sstevel@tonic-gate if (dynamic && interp) 481*0Sstevel@tonic-gate break; 482*0Sstevel@tonic-gate 483*0Sstevel@tonic-gate if (gelf_getphdr(elf, cnt, &phdr) == NULL) { 484*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_ELF_GETPHDR), 485*0Sstevel@tonic-gate cname, fname, elf_errmsg(-1)); 486*0Sstevel@tonic-gate return (1); 487*0Sstevel@tonic-gate } 488*0Sstevel@tonic-gate 489*0Sstevel@tonic-gate if (phdr.p_type == PT_DYNAMIC) { 490*0Sstevel@tonic-gate dynamic = 1; 491*0Sstevel@tonic-gate continue; 492*0Sstevel@tonic-gate } 493*0Sstevel@tonic-gate 494*0Sstevel@tonic-gate if (phdr.p_type != PT_INTERP) 495*0Sstevel@tonic-gate continue; 496*0Sstevel@tonic-gate 497*0Sstevel@tonic-gate interp = 1; 498*0Sstevel@tonic-gate 499*0Sstevel@tonic-gate /* 500*0Sstevel@tonic-gate * If fflag is not set, and euid == root, and the interpreter 501*0Sstevel@tonic-gate * does not live under /lib, /usr/lib or /etc/lib then don't 502*0Sstevel@tonic-gate * allow ldd to execute the image. This prevents someone 503*0Sstevel@tonic-gate * creating a `trojan horse' by substituting their own 504*0Sstevel@tonic-gate * interpreter that could preform privileged operations 505*0Sstevel@tonic-gate * when ldd is against it. 506*0Sstevel@tonic-gate */ 507*0Sstevel@tonic-gate if ((fflag == 0) && (geteuid() == 0) && 508*0Sstevel@tonic-gate (strcmp(fname, conv_lddstub(class)) != 0)) { 509*0Sstevel@tonic-gate char *interpreter; 510*0Sstevel@tonic-gate 511*0Sstevel@tonic-gate /* 512*0Sstevel@tonic-gate * Does the interpreter live under a trusted directory. 513*0Sstevel@tonic-gate */ 514*0Sstevel@tonic-gate interpreter = elf_getident(elf, 0) + phdr.p_offset; 515*0Sstevel@tonic-gate 516*0Sstevel@tonic-gate if ((strncmp(interpreter, MSG_ORIG(MSG_PTH_USRLIB), 517*0Sstevel@tonic-gate MSG_PTH_USRLIB_SIZE) != 0) && 518*0Sstevel@tonic-gate (strncmp(interpreter, MSG_ORIG(MSG_PTH_LIB), 519*0Sstevel@tonic-gate MSG_PTH_LIB_SIZE) != 0) && 520*0Sstevel@tonic-gate (strncmp(interpreter, MSG_ORIG(MSG_PTH_ETCLIB), 521*0Sstevel@tonic-gate MSG_PTH_ETCLIB_SIZE) != 0)) { 522*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_USP_ELFINS), 523*0Sstevel@tonic-gate cname, fname, interpreter); 524*0Sstevel@tonic-gate return (1); 525*0Sstevel@tonic-gate } 526*0Sstevel@tonic-gate } 527*0Sstevel@tonic-gate } 528*0Sstevel@tonic-gate 529*0Sstevel@tonic-gate /* 530*0Sstevel@tonic-gate * Catch the case of a static executable (ie, an ET_EXEC that has a set 531*0Sstevel@tonic-gate * of program headers but no PT_DYNAMIC). 532*0Sstevel@tonic-gate */ 533*0Sstevel@tonic-gate if (ehdr.e_phnum && !dynamic) { 534*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_USP_NODYNORSO), cname, 535*0Sstevel@tonic-gate fname); 536*0Sstevel@tonic-gate return (1); 537*0Sstevel@tonic-gate } 538*0Sstevel@tonic-gate 539*0Sstevel@tonic-gate load = load_elf; 540*0Sstevel@tonic-gate 541*0Sstevel@tonic-gate /* 542*0Sstevel@tonic-gate * Run the required program (shared and relocatable objects require the 543*0Sstevel@tonic-gate * use of lddstub). 544*0Sstevel@tonic-gate */ 545*0Sstevel@tonic-gate if ((ehdr.e_type == ET_EXEC) && interp) 546*0Sstevel@tonic-gate return (run(nfile, cname, fname, (const char *)fname, class)); 547*0Sstevel@tonic-gate else 548*0Sstevel@tonic-gate return (run(nfile, cname, fname, conv_lddstub(class), class)); 549*0Sstevel@tonic-gate } 550*0Sstevel@tonic-gate 551*0Sstevel@tonic-gate 552*0Sstevel@tonic-gate static int 553*0Sstevel@tonic-gate aout_check(int nfile, char *fname, char *cname, int fd, int fflag) 554*0Sstevel@tonic-gate { 555*0Sstevel@tonic-gate struct exec aout; 556*0Sstevel@tonic-gate int err; 557*0Sstevel@tonic-gate 558*0Sstevel@tonic-gate if (lseek(fd, 0, SEEK_SET) != 0) { 559*0Sstevel@tonic-gate err = errno; 560*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_SYS_LSEEK), cname, fname, 561*0Sstevel@tonic-gate strerror(err)); 562*0Sstevel@tonic-gate return (1); 563*0Sstevel@tonic-gate } 564*0Sstevel@tonic-gate if (read(fd, (char *)&aout, sizeof (struct exec)) != 565*0Sstevel@tonic-gate sizeof (struct exec)) { 566*0Sstevel@tonic-gate err = errno; 567*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_SYS_READ), cname, fname, 568*0Sstevel@tonic-gate strerror(err)); 569*0Sstevel@tonic-gate return (1); 570*0Sstevel@tonic-gate } 571*0Sstevel@tonic-gate if (aout.a_machtype != M_SPARC) { 572*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_USP_UNKNOWN), cname, fname); 573*0Sstevel@tonic-gate return (1); 574*0Sstevel@tonic-gate } 575*0Sstevel@tonic-gate if (N_BADMAG(aout) || !aout.a_dynamic) { 576*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_USP_NODYNORSO), cname, 577*0Sstevel@tonic-gate fname); 578*0Sstevel@tonic-gate return (1); 579*0Sstevel@tonic-gate } 580*0Sstevel@tonic-gate if (!fflag && (geteuid() == 0)) { 581*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_USP_AOUTINS), cname, fname); 582*0Sstevel@tonic-gate return (1); 583*0Sstevel@tonic-gate } 584*0Sstevel@tonic-gate 585*0Sstevel@tonic-gate /* 586*0Sstevel@tonic-gate * Run the required program. 587*0Sstevel@tonic-gate */ 588*0Sstevel@tonic-gate if ((aout.a_magic == ZMAGIC) && 589*0Sstevel@tonic-gate (aout.a_entry <= sizeof (struct exec))) { 590*0Sstevel@tonic-gate load = load_elf; 591*0Sstevel@tonic-gate return (run(nfile, cname, fname, conv_lddstub(ELFCLASS32), 592*0Sstevel@tonic-gate ELFCLASS32)); 593*0Sstevel@tonic-gate } else { 594*0Sstevel@tonic-gate load = load_aout; 595*0Sstevel@tonic-gate return (run(nfile, cname, fname, (const char *)fname, 596*0Sstevel@tonic-gate ELFCLASS32)); 597*0Sstevel@tonic-gate } 598*0Sstevel@tonic-gate } 599*0Sstevel@tonic-gate 600*0Sstevel@tonic-gate 601*0Sstevel@tonic-gate /* 602*0Sstevel@tonic-gate * Run the required program, setting the preload and trace environment 603*0Sstevel@tonic-gate * variables accordingly. 604*0Sstevel@tonic-gate */ 605*0Sstevel@tonic-gate static int 606*0Sstevel@tonic-gate run(int nfile, char *cname, char *fname, const char *ename, int class) 607*0Sstevel@tonic-gate { 608*0Sstevel@tonic-gate const char *preload = 0; 609*0Sstevel@tonic-gate int pid, status; 610*0Sstevel@tonic-gate 611*0Sstevel@tonic-gate if ((pid = fork()) == -1) { 612*0Sstevel@tonic-gate int err = errno; 613*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_SYS_FORK), cname, 614*0Sstevel@tonic-gate strerror(err)); 615*0Sstevel@tonic-gate return (1); 616*0Sstevel@tonic-gate } 617*0Sstevel@tonic-gate 618*0Sstevel@tonic-gate if (pid) { /* parent */ 619*0Sstevel@tonic-gate while (wait(&status) != pid) 620*0Sstevel@tonic-gate ; 621*0Sstevel@tonic-gate if (WIFSIGNALED(status) && ((WSIGMASK & status) != SIGPIPE)) { 622*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_SYS_EXEC), cname, 623*0Sstevel@tonic-gate fname); 624*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_SYS_EXEC_SIG), 625*0Sstevel@tonic-gate (WSIGMASK & status), ((status & WCOREFLG) ? 626*0Sstevel@tonic-gate MSG_INTL(MSG_SYS_EXEC_CORE) : 627*0Sstevel@tonic-gate MSG_ORIG(MSG_STR_EMPTY))); 628*0Sstevel@tonic-gate status = 1; 629*0Sstevel@tonic-gate } else if (WHIBYTE(status)) { 630*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_SYS_EXEC), cname, 631*0Sstevel@tonic-gate fname); 632*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_SYS_EXEC_STAT), 633*0Sstevel@tonic-gate WHIBYTE(status)); 634*0Sstevel@tonic-gate status = 1; 635*0Sstevel@tonic-gate } 636*0Sstevel@tonic-gate } else { /* child */ 637*0Sstevel@tonic-gate Listnode *lnp; 638*0Sstevel@tonic-gate char *str; 639*0Sstevel@tonic-gate size_t size; 640*0Sstevel@tonic-gate 641*0Sstevel@tonic-gate /* 642*0Sstevel@tonic-gate * When using ldd(1) to analyze a shared object we preload the 643*0Sstevel@tonic-gate * shared object with lddstub. Any additional preload 644*0Sstevel@tonic-gate * requirements are added after the object being analyzed, this 645*0Sstevel@tonic-gate * allows us to skip the first object but produce diagnostics 646*0Sstevel@tonic-gate * for each other preloaded object. 647*0Sstevel@tonic-gate */ 648*0Sstevel@tonic-gate if (fname != ename) { 649*0Sstevel@tonic-gate char *str; 650*0Sstevel@tonic-gate const char *files = prefile; 651*0Sstevel@tonic-gate const char *format = MSG_ORIG(MSG_STR_FMT1); 652*0Sstevel@tonic-gate 653*0Sstevel@tonic-gate for (str = fname; *str; str++) 654*0Sstevel@tonic-gate if (*str == '/') { 655*0Sstevel@tonic-gate format = MSG_ORIG(MSG_STR_FMT2); 656*0Sstevel@tonic-gate break; 657*0Sstevel@tonic-gate } 658*0Sstevel@tonic-gate 659*0Sstevel@tonic-gate preload = MSG_ORIG(MSG_LD_PRELOAD); 660*0Sstevel@tonic-gate 661*0Sstevel@tonic-gate /* 662*0Sstevel@tonic-gate * Determine which preload files and preload environment 663*0Sstevel@tonic-gate * variable to use. 664*0Sstevel@tonic-gate */ 665*0Sstevel@tonic-gate if (class == ELFCLASS64) { 666*0Sstevel@tonic-gate if (prefile_64 != MSG_ORIG(MSG_STR_EMPTY)) { 667*0Sstevel@tonic-gate files = prefile_64; 668*0Sstevel@tonic-gate preload = MSG_ORIG(MSG_LD_PRELOAD_64); 669*0Sstevel@tonic-gate } 670*0Sstevel@tonic-gate } else { 671*0Sstevel@tonic-gate if (prefile_32 != MSG_ORIG(MSG_STR_EMPTY)) { 672*0Sstevel@tonic-gate files = prefile_32; 673*0Sstevel@tonic-gate preload = MSG_ORIG(MSG_LD_PRELOAD_32); 674*0Sstevel@tonic-gate } 675*0Sstevel@tonic-gate } 676*0Sstevel@tonic-gate 677*0Sstevel@tonic-gate if ((str = (char *)malloc(strlen(preload) + 678*0Sstevel@tonic-gate strlen(fname) + strlen(files) + 5)) == 0) { 679*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), 680*0Sstevel@tonic-gate cname); 681*0Sstevel@tonic-gate exit(1); 682*0Sstevel@tonic-gate } 683*0Sstevel@tonic-gate 684*0Sstevel@tonic-gate (void) sprintf(str, format, preload, fname, files); 685*0Sstevel@tonic-gate if (putenv(str) != 0) { 686*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_ENV_FAILED), 687*0Sstevel@tonic-gate cname); 688*0Sstevel@tonic-gate exit(1); 689*0Sstevel@tonic-gate } 690*0Sstevel@tonic-gate load[LD_LOAD_SIZE - 1] = '2'; 691*0Sstevel@tonic-gate } else 692*0Sstevel@tonic-gate load[LD_LOAD_SIZE - 1] = '1'; 693*0Sstevel@tonic-gate 694*0Sstevel@tonic-gate 695*0Sstevel@tonic-gate /* 696*0Sstevel@tonic-gate * Establish new environment variables to affect the child 697*0Sstevel@tonic-gate * process. 698*0Sstevel@tonic-gate */ 699*0Sstevel@tonic-gate if ((putenv(warn) != 0) || (putenv(bind) != 0) || 700*0Sstevel@tonic-gate (putenv(path) != 0) || (putenv(verb) != 0) || 701*0Sstevel@tonic-gate (putenv(fltr) != 0) || (putenv(conf) != 0) || 702*0Sstevel@tonic-gate (putenv(init) != 0) || (putenv(lazy) != 0) || 703*0Sstevel@tonic-gate (putenv(uref) != 0) || (putenv(used) != 0) || 704*0Sstevel@tonic-gate (putenv(load) != 0)) { 705*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_ENV_FAILED), cname); 706*0Sstevel@tonic-gate exit(1); 707*0Sstevel@tonic-gate } 708*0Sstevel@tonic-gate 709*0Sstevel@tonic-gate /* 710*0Sstevel@tonic-gate * Establish explicit environment requires (but don't override 711*0Sstevel@tonic-gate * any preload request established to process a shared object). 712*0Sstevel@tonic-gate */ 713*0Sstevel@tonic-gate size = 0; 714*0Sstevel@tonic-gate for (LIST_TRAVERSE(&eopts, lnp, str)) { 715*0Sstevel@tonic-gate if (preload) { 716*0Sstevel@tonic-gate if (size == 0) 717*0Sstevel@tonic-gate size = strlen(preload); 718*0Sstevel@tonic-gate if ((strncmp(preload, str, size) == 0) && 719*0Sstevel@tonic-gate (str[size] == '=')) { 720*0Sstevel@tonic-gate continue; 721*0Sstevel@tonic-gate } 722*0Sstevel@tonic-gate } 723*0Sstevel@tonic-gate if (putenv(str) != 0) { 724*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_ENV_FAILED), 725*0Sstevel@tonic-gate cname); 726*0Sstevel@tonic-gate exit(1); 727*0Sstevel@tonic-gate } 728*0Sstevel@tonic-gate } 729*0Sstevel@tonic-gate 730*0Sstevel@tonic-gate /* 731*0Sstevel@tonic-gate * Execute the object and let ld.so.1 do the rest. 732*0Sstevel@tonic-gate */ 733*0Sstevel@tonic-gate if (nfile > 1) 734*0Sstevel@tonic-gate (void) printf(MSG_ORIG(MSG_STR_FMT3), fname); 735*0Sstevel@tonic-gate (void) fflush(stdout); 736*0Sstevel@tonic-gate if ((execl(ename, ename, (char *)0)) == -1) { 737*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_SYS_EXEC), cname, 738*0Sstevel@tonic-gate fname); 739*0Sstevel@tonic-gate perror(ename); 740*0Sstevel@tonic-gate _exit(0); 741*0Sstevel@tonic-gate /* NOTREACHED */ 742*0Sstevel@tonic-gate } 743*0Sstevel@tonic-gate } 744*0Sstevel@tonic-gate return (status); 745*0Sstevel@tonic-gate } 746*0Sstevel@tonic-gate 747*0Sstevel@tonic-gate const char * 748*0Sstevel@tonic-gate _ldd_msg(Msg mid) 749*0Sstevel@tonic-gate { 750*0Sstevel@tonic-gate return (gettext(MSG_ORIG(mid))); 751*0Sstevel@tonic-gate } 752