diff options
author | Eric Andersen <andersen@codepoet.org> | 2001-07-11 01:28:18 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2001-07-11 01:28:18 +0000 |
commit | b5675ab467771c01084f78ae48fb510acc9c7e1a (patch) | |
tree | f37049ff77c27a55fb3b2866b3987256e18a770f /ldso/util/ldd.c | |
parent | 1b2d16f45f8a2b1d90200d3c81691731c6b5fb52 (diff) |
Rewrite of ldd so it works as expected, and does not invoke the
shared lib loader at all. This will allow us to throw out the
support code for that from ld-linux.so.0
-Erik
Diffstat (limited to 'ldso/util/ldd.c')
-rw-r--r-- | ldso/util/ldd.c | 621 |
1 files changed, 334 insertions, 287 deletions
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; } + |