diff options
author | Eric Andersen <andersen@codepoet.org> | 2001-04-23 17:43:54 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2001-04-23 17:43:54 +0000 |
commit | 66f269d2a51dae6a2cb10f1a9ae4aaeba815219b (patch) | |
tree | e2094832990caf6d849ba90e4b1a82a6264f86b3 /ldso/util/ldd.c | |
parent | c4a3f3f81ea90e3df93c352ac0e2161a4bfd3327 (diff) |
Initial checkin for ld.so. This is a combination of effort from Manuel Novoa
III and me. I've been working on stripping out arch dependant stuff and
replacing it with generic stuff whenever possible.
-Erik
Diffstat (limited to 'ldso/util/ldd.c')
-rw-r--r-- | ldso/util/ldd.c | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/ldso/util/ldd.c b/ldso/util/ldd.c new file mode 100644 index 000000000..62c479ba3 --- /dev/null +++ b/ldso/util/ldd.c @@ -0,0 +1,310 @@ +/* + * ldd - print shared library dependencies + * + * usage: ldd [-vVdr] prog ... + * -v: print ldd version + * -V: print ld.so version + * -d: Perform relocations and report any missing functions. (ELF only). + * -r: Perform relocations for both data objects and functions, and + * report any missing objects (ELF only). + * prog ...: programs to check + * + * Copyright 1993-2000, David Engel + * + * This program may be used for any purpose as long as this + * copyright notice is kept. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <a.out.h> +#include <errno.h> +#include <sys/wait.h> +#include <linux/elf.h> +#include "../config.h" +#include "readelf.h" + +#ifdef __GNUC__ +void warn(char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +void error(char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +#endif + +char *prog = NULL; + +void warn(char *fmt, ...) +{ + va_list ap; + + fflush(stdout); /* don't mix output and error messages */ + fprintf(stderr, "%s: warning: ", prog); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fprintf(stderr, "\n"); + + return; +} + +void error(char *fmt, ...) +{ + va_list ap; + + fflush(stdout); /* don't mix output and error messages */ + fprintf(stderr, "%s: ", prog); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + fprintf(stderr, "\n"); + + exit(1); +} + +void *xmalloc(size_t size) +{ + void *ptr; + if ((ptr = malloc(size)) == NULL) + error("out of memory"); + return ptr; +} + +char *xstrdup(char *str) +{ + char *ptr; + if ((ptr = strdup(str)) == NULL) + error("out of memory"); + return ptr; +} + +/* see if prog is a binary file */ +int is_bin(char *argv0, char *prog) +{ + int res = 0; + FILE *file; + struct exec exec; + char *cp; + int libtype; + + /* must be able to open it for reading */ + if ((file = fopen(prog, "rb")) == NULL) + fprintf(stderr, "%s: can't open %s (%s)\n", argv0, prog, + strerror(errno)); + else + { + /* then see if it's Z, Q or OMAGIC */ + if (fread(&exec, sizeof exec, 1, file) < 1) + fprintf(stderr, "%s: can't read header from %s\n", argv0, prog); + else if (N_MAGIC(exec) != ZMAGIC && N_MAGIC(exec) != QMAGIC && + N_MAGIC(exec) != OMAGIC) + { + struct elfhdr elf_hdr; + + rewind(file); + fread(&elf_hdr, sizeof elf_hdr, 1, file); + if (elf_hdr.e_ident[0] != 0x7f || + strncmp(elf_hdr.e_ident+1, "ELF", 3) != 0) + fprintf(stderr, "%s: %s is not a.out or ELF\n", argv0, prog); + else + { + struct elf_phdr phdr; + int i; + + /* Check its an exectuable, library or other */ + switch (elf_hdr.e_type) + { + case ET_EXEC: + res = 3; + /* then determine if it is dynamic ELF */ + for (i=0; i<elf_hdr.e_phnum; i++) + { + fread(&phdr, sizeof phdr, 1, file); + if (phdr.p_type == PT_DYNAMIC) + { + res = 2; + break; + } + } + break; + case ET_DYN: + if ((cp = readsoname(prog, file, LIB_ANY, &libtype, + elf_hdr.e_ident[EI_CLASS])) != NULL) + free(cp); + if (libtype == LIB_ELF_LIBC5) + res = 5; + else + res = 4; + break; + default: + res = 0; + break; + } + } + } + else + res = 1; /* looks good */ + + fclose(file); + } + return res; +} + +int main(int argc, char **argv, char **envp) +{ + int i; + int vprinted = 0; + int resolve = 0; + int bind = 0; + int bintype; + char *ld_preload; + int status = 0; + + /* this must be volatile to work with -O, GCC bug? */ + volatile loadptr loader = (loadptr)LDSO_ADDR; + + prog = argv[0]; + + while ((i = getopt(argc, argv, "drvV")) != EOF) + switch (i) + { + case 'v': + /* print our version number */ + printf("%s: version %s\n", argv[0], VERSION); + vprinted = 1; + break; + case 'd': + bind = 1; + break; + case 'r': + resolve = 1; + break; + case 'V': + /* print the version number of ld.so */ + if (uselib(LDSO_IMAGE)) + { + fprintf(stderr, "%s: can't load dynamic linker %s (%s)\n", + argv[0], LDSO_IMAGE, strerror(errno)); + exit(1); + } + loader(FUNC_VERS, NULL); + vprinted = 1; + break; + } + + /* must specify programs if -v or -V not used */ + if (optind >= argc && !vprinted) + { + printf("usage: %s [-vVdr] prog ...\n", argv[0]); + exit(0); + } + + /* setup the environment for ELF binaries */ + putenv("LD_TRACE_LOADED_OBJECTS=1"); + if (resolve || bind) + putenv("LD_BIND_NOW=1"); + if (resolve) + putenv("LD_WARN=1"); + ld_preload = getenv("LD_PRELOAD"); + + /* print the dependencies for each program */ + for (i = optind; i < argc; i++) + { + pid_t pid; + char buff[1024]; + + /* make sure it's a binary file */ + if (!(bintype = is_bin(argv[0], argv[i]))) + { + status = 1; + continue; + } + + /* print the program name if doing more than one */ + if (optind < argc-1) + { + printf("%s:\n", argv[i]); + fflush(stdout); + } + + /* no need to fork/exec for static ELF program */ + if (bintype == 3) + { + printf("\tstatically linked (ELF)\n"); + continue; + } + + /* now fork and exec prog with argc = 0 */ + if ((pid = fork()) < 0) + { + fprintf(stderr, "%s: can't fork (%s)\n", argv[0], strerror(errno)); + exit(1); + } + else if (pid == 0) + { + switch (bintype) + { + case 1: /* a.out */ + /* save the name in the enviroment, ld.so may need it */ + snprintf(buff, sizeof buff, "%s=%s", LDD_ARGV0, argv[i]); + putenv(buff); + execl(argv[i], NULL); + break; + case 2: /* ELF program */ + execl(argv[i], argv[i], NULL); + break; + case 4: /* ELF libc6 library */ + /* try to use /lib/ld-linux.so.2 first */ +#if !defined(__mc68000__) + execl("/lib/ld-linux.so.2", "/lib/ld-linux.so.2", + "--list", argv[i], NULL); +#else + execl("/lib/ld.so.1", "/lib/ld.so.1", + "--list", argv[i], NULL); +#endif + /* fall through */ + case 5: /* ELF libc5 library */ + /* if that fails, add library to LD_PRELOAD and + then execute lddstub */ + if (ld_preload && *ld_preload) + snprintf(buff, sizeof buff, "LD_PRELOAD=%s:%s%s", + ld_preload, *argv[i] == '/' ? "" : "./", argv[i]); + else + snprintf(buff, sizeof buff, "LD_PRELOAD=%s%s", + *argv[i] == '/' ? "" : "./", argv[i]); + putenv(buff); + execl(LDDSTUB, argv[i], NULL); + break; + default: + fprintf(stderr, "%s: internal error, bintype = %d\n", + argv[0], bintype); + exit(1); + } + fprintf(stderr, "%s: can't execute %s (%s)\n", argv[0], argv[i], + strerror(errno)); + exit(1); + } + else + { + /* then wait for it to complete */ + int status; + if (waitpid(pid, &status, 0) != pid) + { + fprintf(stderr, "%s: error waiting for %s (%s)\n", argv[0], + argv[i], strerror(errno)); + } + else if (WIFSIGNALED(status)) + { + fprintf(stderr, "%s: %s exited with signal %d\n", argv[0], + argv[i], WTERMSIG(status)); + } + } + } + + exit(status); +} |