#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <_libelf.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include <locale.h>
#include <errno.h>
#include <signal.h>
#include "machdep.h"
#include "sgs.h"
#include "conv.h"
#include "msg.h"
static int elf_check(int, char *, char *, Elf *, int);
static int run(int, char *, char *, const char *, int);
static char bind[] = "LD_BIND_NOW= ",
load_elf[] = "LD_TRACE_LOADED_OBJECTS_E= ",
path[] = "LD_TRACE_SEARCH_PATHS= ",
verb[] = "LD_VERBOSE= ",
warn[] = "LD_WARN= ",
conf[] = "LD_NOCONFIG= ",
fltr[] = "LD_LOADFLTR= ",
lazy[] = "LD_NOLAZYLOAD=1",
init[] = "LD_INIT= ",
uref[] = "LD_UNREF= ",
used[] = "LD_UNUSED= ",
weak[] = "LD_NOUNRESWEAK= ",
nope[] = "LD_NOPAREXT= ",
defr[] = "LD_DEFERRED= ";
static char *load;
static const char *prefile_32, *prefile_64, *prefile;
static APlist *eopts = NULL;
int
main(int argc, char **argv, char **envp)
{
char *str, *cname = argv[0];
Elf *elf;
int cflag = 0, dflag = 0, fflag = 0, iflag = 0, Lflag = 0;
int lflag = 0, rflag = 0, sflag = 0, Uflag = 0, uflag = 0;
int Dflag = 0, pflag = 0, vflag = 0, wflag = 0;
int nfile, var, error = 0;
Aliste idx;
(void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY));
(void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS));
opterr = 0;
while ((var = getopt(argc, argv, MSG_ORIG(MSG_STR_GETOPT))) != EOF) {
switch (var) {
case 'c' :
cflag = 1;
break;
case 'D' :
Dflag = 1;
break;
case 'd' :
dflag = 1;
if (rflag)
error++;
break;
case 'e' :
if (aplist_append(&eopts, optarg, 10) == NULL) {
(void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC),
cname);
exit(1);
}
break;
case 'f' :
fflag = 1;
break;
case 'L' :
Lflag = 1;
break;
case 'l' :
lflag = 1;
break;
case 'i' :
iflag = 1;
break;
case 'p' :
pflag = 1;
break;
case 'r' :
rflag = 1;
if (dflag)
error++;
break;
case 's' :
sflag = 1;
break;
case 'U' :
Uflag = 1;
if (uflag)
error++;
break;
case 'u' :
uflag = 1;
if (Uflag)
error++;
break;
case 'v' :
vflag = 1;
break;
case 'w' :
wflag = 1;
break;
default :
error++;
break;
}
if (error)
break;
}
if (error) {
(void) fprintf(stderr, MSG_INTL(MSG_ARG_USAGE), cname);
exit(1);
}
if (((prefile_32 = getenv(MSG_ORIG(MSG_LD_PRELOAD_32))) == NULL) ||
(*prefile_32 == '\0')) {
prefile_32 = MSG_ORIG(MSG_STR_EMPTY);
}
if (((prefile_64 = getenv(MSG_ORIG(MSG_LD_PRELOAD_64))) == NULL) ||
(*prefile_64 == '\0')) {
prefile_64 = MSG_ORIG(MSG_STR_EMPTY);
}
if (((prefile = getenv(MSG_ORIG(MSG_LD_PRELOAD))) == NULL) ||
(*prefile == '\0')) {
prefile = MSG_ORIG(MSG_STR_EMPTY);
}
for (APLIST_TRAVERSE(eopts, idx, str)) {
if ((strncmp(str, MSG_ORIG(MSG_LD_PRELOAD_32),
MSG_LD_PRELOAD_32_SIZE)) == 0) {
str += MSG_LD_PRELOAD_32_SIZE;
if ((*str++ == '=') && (*str != '\0'))
prefile_32 = str;
continue;
}
if ((strncmp(str, MSG_ORIG(MSG_LD_PRELOAD_64),
MSG_LD_PRELOAD_64_SIZE)) == 0) {
str += MSG_LD_PRELOAD_64_SIZE;
if ((*str++ == '=') && (*str != '\0'))
prefile_64 = str;
continue;
}
if ((strncmp(str, MSG_ORIG(MSG_LD_PRELOAD),
MSG_LD_PRELOAD_SIZE)) == 0) {
str += MSG_LD_PRELOAD_SIZE;
if ((*str++ == '=') && (*str != '\0'))
prefile = str;
continue;
}
}
warn[sizeof (warn) - 2] = (dflag || rflag || Uflag || uflag) ? '1' :
'\0';
bind[sizeof (bind) - 2] = (rflag) ? '1' : '\0';
path[sizeof (path) - 2] = (sflag) ? '1' : '\0';
verb[sizeof (verb) - 2] = (vflag) ? '1' : '\0';
fltr[sizeof (fltr) - 2] = (Lflag) ? '\0' : (lflag) ? '2' : '1';
init[sizeof (init) - 2] = (iflag) ? '1' : '\0';
conf[sizeof (conf) - 2] = (cflag) ? '1' : '\0';
lazy[sizeof (lazy) - 2] = (Lflag) ? '\0' : '1';
uref[sizeof (uref) - 2] = (Uflag) ? '1' : '\0';
used[sizeof (used) - 2] = (uflag) ? '1' : '\0';
weak[sizeof (weak) - 2] = (wflag) ? '1' : '\0';
nope[sizeof (nope) - 2] = (pflag) ? '1' : '\0';
defr[sizeof (defr) - 2] = (Dflag) ? '\0' : '1';
if (elf_version(EV_CURRENT) == EV_NONE) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_LIBELF), cname,
EV_CURRENT);
exit(1);
}
nfile = argc - optind;
for (; optind < argc; optind++) {
char *fname = argv[optind];
if ((var = open(fname, O_RDONLY)) == -1) {
int err = errno;
(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), cname,
fname, strerror(err));
error = 1;
continue;
}
elf = elf_begin(var, ELF_C_READ, (Elf *)0);
switch (elf_kind(elf)) {
case ELF_K_AR :
(void) fprintf(stderr, MSG_INTL(MSG_USP_NODYNORSO),
cname, fname);
error = 1;
break;
case ELF_K_ELF:
if (elf_check(nfile, fname, cname, elf, fflag) != 0)
error = 1;
break;
case ELF_K_COFF:
default:
(void) fprintf(stderr, MSG_INTL(MSG_USP_UNKNOWN),
cname, fname);
error = 1;
break;
}
(void) elf_end(elf);
(void) close(var);
}
return (error);
}
static int
elf_check(int nfile, char *fname, char *cname, Elf *elf, int fflag)
{
Conv_inv_buf_t inv_buf;
GElf_Ehdr ehdr;
GElf_Phdr phdr;
int dynamic = 0, interp = 0, cnt, class;
if (gelf_getehdr(elf, &ehdr) == NULL) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_GETEHDR),
cname, fname, elf_errmsg(-1));
return (1);
}
if ((ehdr.e_machine != M_MACH_32) && (ehdr.e_machine != M_MACH_64) &&
(ehdr.e_machine != M_MACHPLUS)) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_MACHTYPE), cname, fname,
conv_ehdr_mach(ehdr.e_machine, 0, &inv_buf));
return (1);
}
if (ehdr.e_ident[EI_DATA] != M_DATA) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_DATA), cname, fname,
conv_ehdr_data(ehdr.e_ident[EI_DATA], 0, &inv_buf));
return (1);
}
switch (class = ehdr.e_ident[EI_CLASS]) {
case ELFCLASS32:
if ((ehdr.e_machine != M_MACH) &&
((ehdr.e_flags & M_FLAGSPLUS) == 0)) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_MACHFLAGS),
cname, fname);
return (1);
}
break;
case ELFCLASS64:
if (conv_sys_eclass() == ELFCLASS32) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_KCLASS32),
cname, fname, conv_ehdr_class(class, 0, &inv_buf));
return (1);
}
break;
default:
(void) fprintf(stderr, MSG_INTL(MSG_ELF_CLASS), cname, fname,
conv_ehdr_class(class, 0, &inv_buf));
return (1);
}
if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN) &&
(ehdr.e_type != ET_REL)) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_BADMAGIC),
cname, fname);
return (1);
}
if ((access(fname, X_OK) != 0) && (ehdr.e_type == ET_EXEC)) {
(void) fprintf(stderr, MSG_INTL(MSG_USP_NOTEXEC),
cname, fname);
return (1);
}
for (cnt = 0; cnt < (int)ehdr.e_phnum; cnt++) {
if (dynamic && interp)
break;
if (gelf_getphdr(elf, cnt, &phdr) == NULL) {
(void) fprintf(stderr, MSG_INTL(MSG_ELF_GETPHDR),
cname, fname, elf_errmsg(-1));
return (1);
}
if (phdr.p_type == PT_DYNAMIC) {
dynamic = 1;
continue;
}
if (phdr.p_type != PT_INTERP)
continue;
interp = 1;
if ((fflag == 0) && (geteuid() == 0) &&
(strcmp(fname, conv_lddstub(class)) != 0)) {
char *interpreter;
interpreter = elf_getident(elf, 0) + phdr.p_offset;
if ((strncmp(interpreter, MSG_ORIG(MSG_PTH_USRLIB),
MSG_PTH_USRLIB_SIZE) != 0) &&
(strncmp(interpreter, MSG_ORIG(MSG_PTH_LIB),
MSG_PTH_LIB_SIZE) != 0) &&
(strncmp(interpreter, MSG_ORIG(MSG_PTH_ETCLIB),
MSG_PTH_ETCLIB_SIZE) != 0)) {
(void) fprintf(stderr, MSG_INTL(MSG_USP_ELFINS),
cname, fname, interpreter);
return (1);
}
}
}
if (ehdr.e_phnum && !dynamic) {
(void) fprintf(stderr, MSG_INTL(MSG_USP_NODYNORSO), cname,
fname);
return (1);
}
if (dynamic && (_gelf_getdyndtflags_1(elf) & DF_1_NOHDR)) {
(void) fprintf(stderr, MSG_INTL(MSG_USP_NOHDR), cname,
fname);
return (1);
}
load = load_elf;
if ((ehdr.e_type == ET_EXEC) && interp)
return (run(nfile, cname, fname, (const char *)fname, class));
else
return (run(nfile, cname, fname, conv_lddstub(class), class));
}
static int
run(int nfile, char *cname, char *fname, const char *ename, int class)
{
const char *preload = 0;
int pid, status;
if ((pid = fork()) == -1) {
int err = errno;
(void) fprintf(stderr, MSG_INTL(MSG_SYS_FORK), cname,
strerror(err));
return (1);
}
if (pid) {
while (wait(&status) != pid)
;
if (WIFSIGNALED(status) && ((WSIGMASK & status) != SIGPIPE)) {
(void) fprintf(stderr, MSG_INTL(MSG_SYS_EXEC), cname,
fname);
(void) fprintf(stderr, MSG_INTL(MSG_SYS_EXEC_SIG),
(WSIGMASK & status), ((status & WCOREFLG) ?
MSG_INTL(MSG_SYS_EXEC_CORE) :
MSG_ORIG(MSG_STR_EMPTY)));
status = 1;
} else if (WHIBYTE(status)) {
(void) fprintf(stderr, MSG_INTL(MSG_SYS_EXEC), cname,
fname);
(void) fprintf(stderr, MSG_INTL(MSG_SYS_EXEC_STAT),
WHIBYTE(status));
status = 1;
}
} else {
Aliste idx;
char *str;
size_t size;
if (fname != ename) {
char *str;
const char *files = prefile;
const char *format = MSG_ORIG(MSG_STR_FMT1);
for (str = fname; *str; str++)
if (*str == '/') {
format = MSG_ORIG(MSG_STR_FMT2);
break;
}
preload = MSG_ORIG(MSG_LD_PRELOAD);
if (class == ELFCLASS64) {
if (prefile_64 != MSG_ORIG(MSG_STR_EMPTY)) {
files = prefile_64;
preload = MSG_ORIG(MSG_LD_PRELOAD_64);
}
} else {
if (prefile_32 != MSG_ORIG(MSG_STR_EMPTY)) {
files = prefile_32;
preload = MSG_ORIG(MSG_LD_PRELOAD_32);
}
}
if ((str = (char *)malloc(strlen(preload) +
strlen(fname) + strlen(files) + 5)) == 0) {
(void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC),
cname);
exit(1);
}
(void) sprintf(str, format, preload, fname, files);
if (putenv(str) != 0) {
(void) fprintf(stderr, MSG_INTL(MSG_ENV_FAILED),
cname);
exit(1);
}
load[sizeof (load_elf) - 2] = '2';
} else
load[sizeof (load_elf) - 2] = '1';
if ((putenv(warn) != 0) || (putenv(bind) != 0) ||
(putenv(path) != 0) || (putenv(verb) != 0) ||
(putenv(fltr) != 0) || (putenv(conf) != 0) ||
(putenv(init) != 0) || (putenv(lazy) != 0) ||
(putenv(uref) != 0) || (putenv(used) != 0) ||
(putenv(weak) != 0) || (putenv(load) != 0) ||
(putenv(nope) != 0) || (putenv(defr) != 0)) {
(void) fprintf(stderr, MSG_INTL(MSG_ENV_FAILED), cname);
exit(1);
}
size = 0;
for (APLIST_TRAVERSE(eopts, idx, str)) {
if (preload) {
if (size == 0)
size = strlen(preload);
if ((strncmp(preload, str, size) == 0) &&
(str[size] == '=')) {
continue;
}
}
if (putenv(str) != 0) {
(void) fprintf(stderr, MSG_INTL(MSG_ENV_FAILED),
cname);
exit(1);
}
}
if (nfile > 1)
(void) printf(MSG_ORIG(MSG_STR_FMT3), fname);
(void) fflush(stdout);
if ((execl(ename, ename, (char *)0)) == -1) {
(void) fprintf(stderr, MSG_INTL(MSG_SYS_EXEC), cname,
fname);
perror(ename);
_exit(0);
}
}
return (status);
}
const char *
_ldd_msg(Msg mid)
{
return (gettext(MSG_ORIG(mid)));
}