+/* vi: set sw=4 ts=4: */
/*
- * ldd - print shared library dependencies
+ * A small little ldd implementation for uClibc
*
- * 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 (C) 2001 by Lineo, inc.
+ * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
*
- * Copyright 1993-2000, David Engel
+ * Several functions in this file (specifically, elf_find_section_type(),
+ * elf_find_phdr_type(), and elf_find_dynamic(), were stolen from elflib.c from
+ * elfvector (http://www.BitWagon.com/elfvector.html) by John F. Reiser
+ * <jreiser@BitWagon.com>, and which is copyright 2000 BitWagon Software LLC
+ * (GPL2).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
- * This program may be used for any purpose as long as this
- * copyright notice is kept.
*/
+
+#include <elf.h>
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stdarg.h>
#include <string.h>
-#include <getopt.h>
#include <unistd.h>
-#include <errno.h>
-#include <sys/wait.h>
-#include <elf.h>
-#include "../d-link/linuxelf.h"
-#include "../config.h"
-#include "readsoname.h"
-
-struct exec
-{
- unsigned long a_info; /* Use macros N_MAGIC, etc for access */
- unsigned a_text; /* length of text, in bytes */
- unsigned a_data; /* length of data, in bytes */
- unsigned a_bss; /* length of uninitialized data area for file, in bytes */
- unsigned a_syms; /* length of symbol table data in file, in bytes */
- unsigned a_entry; /* start address */
- unsigned a_trsize; /* length of relocation info for text, in bytes */
- unsigned a_drsize; /* length of relocation info for data, in bytes */
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+struct library {
+ char *name;
+ int resolved;
+ char *path;
+ struct library *next;
};
+struct library *lib_list = NULL;
+
-#if !defined (N_MAGIC)
-#define N_MAGIC(exec) ((exec).a_info & 0xffff)
-#endif
-/* Code indicating object file or impure executable. */
-#define OMAGIC 0407
-/* Code indicating pure executable. */
-#define NMAGIC 0410
-/* Code indicating demand-paged executable. */
-#define ZMAGIC 0413
-/* This indicates a demand-paged executable with the header in the text.
- The first page is unmapped to help trap NULL pointer references */
-#define QMAGIC 0314
-/* Code indicating core file. */
-#define CMAGIC 0421
+Elf32_Shdr * elf_find_section_type( int key, Elf32_Ehdr *ehdr)
+{
+ int j;
+ Elf32_Shdr *shdr = (Elf32_Shdr *)(ehdr->e_shoff + (char *)ehdr);
+ for (j = ehdr->e_shnum; --j>=0; ++shdr) {
+ if (shdr->sh_type == key) {
+ return shdr;
+ }
+ }
+ return NULL;
+}
+Elf32_Phdr * elf_find_phdr_type( int type, Elf32_Ehdr *ehdr)
+{
+ int j;
+ Elf32_Phdr *phdr = (Elf32_Phdr *)(ehdr->e_phoff + (char *)ehdr);
+ for (j = ehdr->e_phnum; --j>=0; ++phdr) {
+ if (type==phdr->p_type) {
+ return phdr;
+ }
+ }
+ return NULL;
+}
-extern int uselib(const char *library);
+/* Returns value if return_val==1, ptr otherwise */
+void * elf_find_dynamic(int const key, Elf32_Dyn *dynp,
+ Elf32_Ehdr *ehdr, int return_val)
+{
+ Elf32_Phdr *pt_text = elf_find_phdr_type(PT_LOAD, ehdr);
+ unsigned tx_reloc = pt_text->p_vaddr - pt_text->p_offset;
+ for (; DT_NULL!=dynp->d_tag; ++dynp) {
+ if (dynp->d_tag == key) {
+ if (return_val == 1)
+ return (void *)dynp->d_un.d_val;
+ else
+ return (void *)(dynp->d_un.d_val - tx_reloc + (char *)ehdr );
+ }
+ }
+ return NULL;
+}
-#ifdef __GNUC__
-void warn(char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
-void error(char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
-#endif
+int check_elf_header(Elf32_Ehdr const *const ehdr)
+{
+ if (! ehdr || strncmp((void *)ehdr, ELFMAG, SELFMAG) != 0 ||
+ ehdr->e_ident[EI_CLASS] != ELFCLASS32 ||
+ ehdr->e_ident[EI_VERSION] != EV_CURRENT)
+ {
+ return 1;
+ }
+ return 0;
+}
-char *prog = NULL;
+char * last_char_is(const char *s, int c)
+{
+ char *sret;
+ if (!s)
+ return NULL;
+ sret = (char *)s+strlen(s)-1;
+ if (sret>=s && *sret == c) {
+ return sret;
+ } else {
+ return NULL;
+ }
+}
-void warn(char *fmt, ...)
+extern char *concat_path_file(const char *path, const char *filename)
{
- va_list ap;
+ char *outbuf;
+ char *lc;
+
+ if (!path)
+ path="";
+ lc = last_char_is(path, '/');
+ if (filename[0] == '/')
+ filename++;
+ outbuf = malloc(strlen(path)+strlen(filename)+1+(lc==NULL));
+ if (!outbuf) {
+ fprintf(stderr, "out of memory\n");
+ exit(EXIT_FAILURE);
+ }
+ sprintf(outbuf, "%s%s%s", path, (lc==NULL)? "/" : "", filename);
+
+ return outbuf;
+}
- fflush(stdout); /* don't mix output and error messages */
- fprintf(stderr, "%s: warning: ", prog);
+char *do_which(char *name, char *path_list)
+{
+ char *path_n;
+ char *path_tmp;
+ struct stat filestat;
+ int i, count=1;
+
+ if (!path_list) {
+ fprintf(stderr, "yipe!\n");
+ exit(EXIT_FAILURE);
+ }
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
+ /* We need a writable copy of this string */
+ path_tmp = strdup(path_list);
+ if (!path_tmp) {
+ fprintf(stderr, "yipe!\n");
+ exit(EXIT_FAILURE);
+ }
- fprintf(stderr, "\n");
+ /* Replace colons with zeros in path_parsed and count them */
+ for(i=strlen(path_tmp); i > 0; i--)
+ if (path_tmp[i]==':') {
+ path_tmp[i]=0;
+ count++;
+ }
- return;
+ path_n = path_tmp;
+ for (i = 0; i < count; i++) {
+ char *buf;
+ buf = concat_path_file(path_n, name);
+ if (stat (buf, &filestat) == 0 && filestat.st_mode & S_IRUSR) {
+ return(buf);
+ }
+ free(buf);
+ path_n += (strlen(path_n) + 1);
+ }
+ return NULL;
}
-void error(char *fmt, ...)
+void locate_library_file(Elf32_Ehdr* ehdr, Elf32_Dyn* dynamic, char *strtab, int is_suid, struct library *lib)
{
- va_list ap;
+ char *buf;
+ char *path;
+ struct stat filestat;
- fflush(stdout); /* don't mix output and error messages */
- fprintf(stderr, "%s: ", prog);
+ lib->path = "not found";
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
+ /* If this is a fully resolved name, our job is easy */
+ if (stat (lib->name, &filestat) == 0) {
+ lib->path = lib->name;
+ return;
+ }
- fprintf(stderr, "\n");
+ /* This function must match the behavior of _dl_load_shared_library
+ * in readelflib1.c or things won't work out as expected... */
+
+ /* The ABI specifies that RPATH is searched before LD_*_PATH or the
+ * default path (such as /usr/lib). So first, lets check the rpath
+ * directories */
+ path = (char *)elf_find_dynamic(DT_RPATH, dynamic, ehdr, 0);
+ if (path) {
+ buf = do_which(lib->name, path);
+ if (buf) {
+ lib->path = buf;
+ return;
+ }
+ }
- exit(1);
-}
+ /* Next check LD_{ELF_}LIBRARY_PATH if specified and allowed.
+ * Since this app doesn't actually run an executable I will skip
+ * the suid check, and just use LD_{ELF_}LIBRARY_PATH if set */
+ if (is_suid==1)
+ path = NULL;
+ else
+ path = getenv("LD_LIBRARY_PATH");
+ if (path) {
+ buf = do_which(lib->name, path);
+ if (buf) {
+ lib->path = buf;
+ return;
+ }
+ }
-void *xmalloc(size_t size)
-{
- void *ptr;
- if ((ptr = malloc(size)) == NULL)
- error("out of memory");
- return ptr;
-}
+ /* Fixme -- add code to check the Cache here */
-char *xstrdup(char *str)
-{
- char *ptr;
- if ((ptr = strdup(str)) == NULL)
- error("out of memory");
- return ptr;
+
+ buf = do_which(lib->name, UCLIBC_ROOT_DIR "/usr/lib/:" UCLIBC_ROOT_DIR
+ "/lib/:" UCLIBC_BUILD_DIR "/lib/:/usr/lib:/lib");
+ if (buf) {
+ lib->path = buf;
+ }
}
-/* see if prog is a binary file */
-int is_bin(char *argv0, char *prog)
+static int add_library(Elf32_Ehdr* ehdr, Elf32_Dyn* dynamic, char *strtab, int is_setuid, const char *s)
{
- 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)
- {
- 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
- {
- 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;
+ struct library *cur, *prev, *newlib=lib_list;
+
+ if (!s || !strlen(s))
+ return 1;
+
+ for (cur = lib_list; cur; cur=cur->next) {
+ if(strcmp(cur->name, s)==0) {
+ /* Lib is already in the list */
+ return 0;
}
- }
}
- else
- res = 1; /* looks good */
- fclose(file);
- }
- return res;
+ /* Ok, this lib needs to be added to the list */
+ newlib = malloc(sizeof(struct library));
+ if (!newlib)
+ return 1;
+ newlib->name = malloc(strlen(s));
+ strcpy(newlib->name, s);
+ newlib->resolved = 0;
+ newlib->next = NULL;
+
+ /* Now try and locate where this library might be living... */
+ locate_library_file(ehdr, dynamic, strtab, is_setuid, newlib);
+
+ //printf("adding '%s' to '%s'\n", newlib->name, newlib->path);
+ if (!lib_list) {
+ lib_list = newlib;
+ } else {
+ for (cur = prev = lib_list; cur->next; prev=cur, cur=cur->next); /* nothing */
+ cur = newlib;
+ prev->next = cur;
+ }
+ return 0;
}
-int main(int argc, char **argv, char **envp)
+
+static void find_needed_libraries(Elf32_Ehdr* ehdr, Elf32_Dyn* dynamic, char *strtab, int is_setuid)
{
- 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: uClibc version\n", argv[0]);
- 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;
+ Elf32_Dyn *dyns;
+
+ for (dyns=dynamic; dyns->d_tag!=DT_NULL; ++dyns) {
+ if (dyns->d_tag == DT_NEEDED) {
+ add_library(ehdr, dynamic, strtab, is_setuid, (char*)strtab + dyns->d_un.d_val);
+ }
+ }
+}
+
+static void find_elf_interpreter(Elf32_Ehdr* ehdr, Elf32_Dyn* dynamic, char *strtab, int is_setuid)
+{
+ Elf32_Phdr *phdr;
+ phdr = elf_find_phdr_type(PT_INTERP, ehdr);
+ if (phdr) {
+ add_library(ehdr, dynamic, strtab, is_setuid, (char*)ehdr + phdr->p_offset);
}
+}
- /* 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;
+/* map the .so, and locate interesting pieces */
+int find_dependancies(char* filename)
+{
+ int is_suid = 0;
+ FILE *thefile;
+ struct stat statbuf;
+ char *dynstr=NULL;
+ Elf32_Ehdr *ehdr = NULL;
+ Elf32_Shdr *dynsec = NULL;
+ Elf32_Dyn *dynamic = NULL;
+
+
+ if (!filename) {
+ fprintf(stderr, "No filename specified.\n");
+ exit(EXIT_FAILURE);
+ }
+ if (!(thefile = fopen(filename, "r"))) {
+ perror(filename);
+ exit(EXIT_FAILURE);
+ }
+ if (fstat(fileno(thefile), &statbuf) < 0) {
+ perror(filename);
+ exit(EXIT_FAILURE);
}
- /* print the program name if doing more than one */
- if (optind < argc-1)
- {
- printf("%s:\n", argv[i]);
- fflush(stdout);
+ if (statbuf.st_size < sizeof(Elf32_Ehdr))
+ goto foo;
+
+ /* mmap the file to make reading stuff from it effortless */
+ ehdr = (Elf32_Ehdr *)mmap(0, statbuf.st_size,
+ PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(thefile), 0);
+
+foo:
+ /* Check if this looks like a legit ELF file */
+ if (check_elf_header(ehdr)) {
+ fprintf(stderr, "%s: not an ELF file.\n", filename);
+ exit(EXIT_FAILURE);
+ }
+ /* Check if this is the right kind of ELF file */
+ if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
+ fprintf(stderr, "%s: not a dynamic executable\n", filename);
+ exit(EXIT_FAILURE);
+ }
+ if (ehdr->e_type == ET_EXEC) {
+ if (statbuf.st_mode & S_ISUID)
+ is_suid = 1;
+ if ((statbuf.st_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
+ is_suid = 1;
+ /* FIXME */
+ if (is_suid)
+ fprintf(stderr, "%s: is setuid\n", filename);
}
- /* no need to fork/exec for static ELF program */
- if (bintype == 3)
- {
- printf("\tstatically linked (ELF)\n");
- continue;
+ dynsec = elf_find_section_type(SHT_DYNAMIC, ehdr);
+ if (dynsec) {
+ dynamic = (Elf32_Dyn*)(dynsec->sh_offset + (int)ehdr);
+ dynstr = (char *)elf_find_dynamic(DT_STRTAB, dynamic, ehdr, 0);
+ find_needed_libraries(ehdr, dynamic, dynstr, is_suid);
}
+ find_elf_interpreter(ehdr, dynamic, dynstr, is_suid);
+
+ return 0;
+}
- /* 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);
+
+
+int main( int argc, char** argv)
+{
+ int got_em_all=1;
+ char *filename = argv[1];
+ struct library *cur;
+
+
+ if (!filename) {
+ fprintf(stderr, "No filename specified.\n");
+ exit(EXIT_FAILURE);
}
- 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);
+
+ find_dependancies(filename);
+
+ while(got_em_all) {
+ got_em_all=0;
+ /* Keep walking the list till everybody is resolved */
+ for (cur = lib_list; cur; cur=cur->next) {
+ if (cur->resolved == 0 && cur->path) {
+ got_em_all=1;
+ //printf("checking sub-depends for '%s\n", cur->path);
+ find_dependancies(cur->path);
+ cur->resolved = 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));
- }
+
+
+ /* Print the list */
+ for (cur = lib_list; cur; cur=cur->next) {
+ printf("\t%s => %s\n", cur->name, cur->path);
}
- }
- exit(status);
+ return 0;
}
+