diff options
-rw-r--r-- | ldso/util/Makefile | 19 | ||||
-rw-r--r-- | ldso/util/ldd.c | 621 |
2 files changed, 344 insertions, 296 deletions
diff --git a/ldso/util/Makefile b/ldso/util/Makefile index dd69c9b10..646fd69b6 100644 --- a/ldso/util/Makefile +++ b/ldso/util/Makefile @@ -6,24 +6,25 @@ all: ldconfig ldd readelf readsoname.o: readsoname.c readsoname2.c $(TARGET_CC) $(TARGET_CFLAGS) -c $< -o $@ $(STRIPTOOL) -x -R .note -R .comment $*.o - + ldconfig.o: ldconfig.c - $(TARGET_CC) $(TARGET_CFLAGS) -DUCLIBC_ROOT_DIR=\"$(ROOT_DIR)\" -c $< -o $@ - $(STRIPTOOL) -x -R .note -R .comment $*.o - -ldd.o: ldd.c - $(TARGET_CC) $(TARGET_CFLAGS) -DUCLIBC_ROOT_DIR=\"$(ROOT_DIR)\" \ - -DLDSO_ADDR="0x62f00020" -c $< -o $@ + $(TARGET_CC) $(TARGET_CFLAGS) -DUCLIBC_ROOT_DIR=\"$(ROOT_DIR)\" \ + -c $< -o $@ $(STRIPTOOL) -x -R .note -R .comment $*.o readelf: readelf.c $(TARGET_CC) $(TARGET_CFLAGS) -static readelf.c -o $@ + $(STRIPTOOL) -x -R .note -R .comment $@ ldconfig: ldconfig.o readsoname.o $(TARGET_CC) $(TARGET_CFLAGS) -static $^ -o $@ + $(STRIPTOOL) -x -R .note -R .comment $@ -ldd: ldd.o readsoname.o - $(TARGET_CC) $(TARGET_CFLAGS) -static $^ -o $@ +ldd: ldd.c + $(TARGET_CC) $(TARGET_CFLAGS) -DUCLIBC_ROOT_DIR=\"$(ROOT_DIR)\" \ + -DUCLIBC_BUILD_DIR=\"$(shell cd $(TOPDIR); pwd)\" \ + -static ldd.c -o $@ + $(STRIPTOOL) -x -R .note -R .comment $@ clean: rm -f ldconfig ldd readelf *.o *~ core diff --git a/ldso/util/ldd.c b/ldso/util/ldd.c index 29800bc20..0224b0c29 100644 --- a/ldso/util/ldd.c +++ b/ldso/util/ldd.c @@ -1,341 +1,388 @@ +/* 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; } + |