diff options
-rw-r--r-- | utils/Makefile | 12 | ||||
-rw-r--r-- | utils/chroot_realpath.c | 163 | ||||
-rw-r--r-- | utils/ldconfig.c | 143 | ||||
-rw-r--r-- | utils/readsoname2.c | 32 |
4 files changed, 315 insertions, 35 deletions
diff --git a/utils/Makefile b/utils/Makefile index 640bedfb7..f35b94eac 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -31,6 +31,12 @@ else TARGET_ICONV = endif +ifeq ($(strip $(LDSO_CACHE_SUPPORT)),y) +HOST_LDSO_CACHE_FLAG = -D__LDSO_CACHE_SUPPORT__=1 +else +HOST_LDSO_CACHE_FLAG = +endif + # NOTE: We build the utils AFTER we have a uClibc-targeted toolchain. ifeq ($(strip $(HAVE_SHARED)),y) @@ -84,13 +90,13 @@ endif ldd.host: ldd.c $(HOSTCC) $(HOSTCFLAGS) -Wl,-s \ - -DUCLIBC_RUNTIME_PREFIX=\"$(RUNTIME_PREFIX)\" \ + -DUCLIBC_RUNTIME_PREFIX=\"$(RUNTIME_PREFIX)\" $(HOST_LDSO_CACHE_FLAG) \ -DUCLIBC_LDSO=$(UCLIBC_LDSO) -I. -I../ldso/include \ $^ -o $@ -ldconfig.host: ldconfig.c +ldconfig.host: ldconfig.c chroot_realpath.c $(HOSTCC) $(HOSTCFLAGS) -Wl,-s \ - -DUCLIBC_RUNTIME_PREFIX=\"$(RUNTIME_PREFIX)\" \ + -DUCLIBC_RUNTIME_PREFIX=\"$(RUNTIME_PREFIX)\" $(HOST_LDSO_CACHE_FLAG) \ -DUCLIBC_LDSO=$(UCLIBC_LDSO) -I. -I../ldso/include \ $^ -o $@ diff --git a/utils/chroot_realpath.c b/utils/chroot_realpath.c new file mode 100644 index 000000000..43ce9a528 --- /dev/null +++ b/utils/chroot_realpath.c @@ -0,0 +1,163 @@ +/* + * chroot_realpath.c -- reslove pathname as if inside chroot + * Based on realpath.c Copyright (C) 1993 Rick Sladkey <jrs@world.std.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library Public License as published by + * the Free Software Foundation; either version 2, 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 Library Public License for more details. + * + * 2005/09/12: Dan Howell (modified from realpath.c to emulate chroot) + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <limits.h> /* for PATH_MAX */ +#include <sys/param.h> /* for MAXPATHLEN */ +#include <errno.h> +#ifndef __set_errno +#define __set_errno(val) ((errno) = (val)) +#endif + +#include <sys/stat.h> /* for S_IFLNK */ + +#ifndef PATH_MAX +#define PATH_MAX _POSIX_PATH_MAX +#endif + +#define MAX_READLINKS 32 + +char *chroot_realpath(const char *chroot, const char *path, char resolved_path[]) +{ + char copy_path[PATH_MAX]; + char link_path[PATH_MAX]; + char got_path[PATH_MAX]; + char *got_path_root = got_path; + char *new_path = got_path; + char *max_path; + int readlinks = 0; + int n; + int chroot_len; + + /* Trivial case. */ + if (chroot == NULL || *chroot == '\0' || + (*chroot == '/' && chroot[1] == '\0')) { + strcpy(resolved_path, path); + return resolved_path; + } + + chroot_len = strlen(chroot); + + if (chroot_len + strlen(path) >= PATH_MAX - 3) { + __set_errno(ENAMETOOLONG); + return NULL; + } + + /* Make a copy of the source path since we may need to modify it. */ + strcpy(copy_path, path); + path = copy_path; + max_path = copy_path + PATH_MAX - chroot_len - 3; + + /* Start with the chroot path. */ + strcpy(new_path, chroot); + new_path += chroot_len; + while (*new_path == '/' && new_path > got_path) + new_path--; + got_path_root = new_path; + *new_path++ = '/'; + + /* Expand each slash-separated pathname component. */ + while (*path != '\0') { + /* Ignore stray "/". */ + if (*path == '/') { + path++; + continue; + } + if (*path == '.') { + /* Ignore ".". */ + if (path[1] == '\0' || path[1] == '/') { + path++; + continue; + } + if (path[1] == '.') { + if (path[2] == '\0' || path[2] == '/') { + path += 2; + /* Ignore ".." at root. */ + if (new_path == got_path_root + 1) + continue; + /* Handle ".." by backing up. */ + while ((--new_path)[-1] != '/'); + continue; + } + } + } + /* Safely copy the next pathname component. */ + while (*path != '\0' && *path != '/') { + if (path > max_path) { + __set_errno(ENAMETOOLONG); + return NULL; + } + *new_path++ = *path++; + } + if (*path == '\0') + /* Don't follow symlink for last pathname component. */ + break; +#ifdef S_IFLNK + /* Protect against infinite loops. */ + if (readlinks++ > MAX_READLINKS) { + __set_errno(ELOOP); + return NULL; + } + /* See if latest pathname component is a symlink. */ + *new_path = '\0'; + n = readlink(got_path, link_path, PATH_MAX - 1); + if (n < 0) { + /* EINVAL means the file exists but isn't a symlink. */ + if (errno != EINVAL) { + /* Make sure it's null terminated. */ + *new_path = '\0'; + strcpy(resolved_path, got_path); + return NULL; + } + } else { + /* Note: readlink doesn't add the null byte. */ + link_path[n] = '\0'; + if (*link_path == '/') + /* Start over for an absolute symlink. */ + new_path = got_path_root; + else + /* Otherwise back up over this component. */ + while (*(--new_path) != '/'); + /* Safe sex check. */ + if (strlen(path) + n >= PATH_MAX - 2) { + __set_errno(ENAMETOOLONG); + return NULL; + } + /* Insert symlink contents into path. */ + strcat(link_path, path); + strcpy(copy_path, link_path); + path = copy_path; + } +#endif /* S_IFLNK */ + *new_path++ = '/'; + } + /* Delete trailing slash but don't whomp a lone slash. */ + if (new_path != got_path + 1 && new_path[-1] == '/') + new_path--; + /* Make sure it's null terminated. */ + *new_path = '\0'; + strcpy(resolved_path, got_path); + return resolved_path; +} diff --git a/utils/ldconfig.c b/utils/ldconfig.c index 202a58dd6..85ce015d5 100644 --- a/utils/ldconfig.c +++ b/utils/ldconfig.c @@ -22,6 +22,8 @@ * * This program may be used for any purpose as long as this * copyright notice is kept. + * + * 2005/09/16: Dan Howell (modified for cross-development) */ #include <stdio.h> @@ -37,6 +39,7 @@ #include <errno.h> #include <sys/stat.h> #include <sys/mman.h> +#include "bswap.h" #include "dl-defs.h" #define BUFFER_SIZE 4096 @@ -56,6 +59,7 @@ struct exec #if !defined (N_MAGIC) #define N_MAGIC(exec) ((exec).a_info & 0xffff) #endif +#define N_MAGIC_SWAP(exec) (bswap_32((exec).a_info) & 0xffff) /* Code indicating object file or impure executable. */ #define OMAGIC 0407 /* Code indicating pure executable. */ @@ -97,6 +101,8 @@ void cache_dolib(const char *dir, const char *so, int libtype); char *conffile = LDSO_CONF; /* default conf file */ char *cachefile = LDSO_CACHE; /* default cache file */ #endif +char *chroot_dir = NULL; +int byteswap = 0; struct needed_tab { @@ -117,6 +123,8 @@ struct needed_tab needed_tab[] = { { NULL, LIB_ELF } }; +extern char *chroot_realpath(const char *chroot, const char *path, char resolved_path[]); + /* These two are used internally -- you shouldn't need to use them */ static void verror_msg(const char *s, va_list p) @@ -242,6 +250,8 @@ char *is_shlib(const char *dir, const char *name, int *type, ElfW(Ehdr) *elf_hdr; struct stat statbuf; char buff[BUFFER_SIZE]; + char real[BUFFER_SIZE]; + static int byteswapflag = -1; /* start with byte-order unknown */ /* see if name is of the form *.so* */ if (name[strlen(name)-1] != '~' && (cp = strstr(name, ".so"))) @@ -256,8 +266,12 @@ char *is_shlib(const char *dir, const char *name, int *type, sprintf(buff, "%s%s%s", dir, (*dir && strcmp(dir, "/")) ? "/" : "", name); + /* get real path in case of chroot */ + if (!chroot_realpath(chroot_dir, buff, real)) + warn("can't resolve %s in chroot %s", buff, chroot_dir); + /* first, make sure it's a regular file */ - if (lstat(buff, &statbuf)) + if (lstat(real, &statbuf)) warn("skipping %s", buff); else if (!S_ISREG(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) warnx("%s is not a regular file or symlink, skipping", buff); @@ -267,14 +281,15 @@ char *is_shlib(const char *dir, const char *name, int *type, *islink = S_ISLNK(statbuf.st_mode); /* then try opening it */ - if (!(file = fopen(buff, "rb"))) + if (!(file = fopen(real, "rb"))) warn("skipping %s", buff); else { /* now make sure it's a shared library */ if (fread(&exec, sizeof exec, 1, file) < 1) warnx("can't read header from %s, skipping", buff); - else if (N_MAGIC(exec) != ZMAGIC && N_MAGIC(exec) != QMAGIC) + else if (N_MAGIC(exec) != ZMAGIC && N_MAGIC(exec) != QMAGIC && + N_MAGIC_SWAP(exec) != ZMAGIC && N_MAGIC_SWAP(exec) != QMAGIC) { elf_hdr = (ElfW(Ehdr) *) &exec; if (elf_hdr->e_ident[0] != 0x7f || @@ -294,6 +309,9 @@ char *is_shlib(const char *dir, const char *name, int *type, *type = LIB_ELF; good = readsoname(buff, file, expected_type, type, elf_hdr->e_ident[EI_CLASS]); + if (byteswapflag == -1) + /* byte-order detected */ + byteswapflag = byteswap; if (good == NULL || *islink) { if (good != NULL) @@ -313,6 +331,12 @@ char *is_shlib(const char *dir, const char *name, int *type, } else { + /* Determine byte-order */ + byteswap = (N_MAGIC(exec) == ZMAGIC || N_MAGIC(exec) == QMAGIC) ? 0 : 1; + if (byteswapflag == -1) + /* byte-order detected */ + byteswapflag = byteswap; + if (*islink) good = xstrdup(name); else @@ -330,6 +354,14 @@ char *is_shlib(const char *dir, const char *name, int *type, *type = LIB_DLL; } fclose(file); + + if (byteswapflag >= 0 && byteswap != byteswapflag) + { + byteswapflag = -2; + warnx("mixed byte-order detected, using host byte-order..."); + } + if (byteswapflag == -2) + byteswap = 0; } } } @@ -343,18 +375,24 @@ void link_shlib(const char *dir, const char *file, const char *so) int change = 1; char libname[BUFFER_SIZE]; char linkname[BUFFER_SIZE]; + char reallibname[BUFFER_SIZE]; + char reallinkname[BUFFER_SIZE]; struct stat libstat; struct stat linkstat; /* construct the full path names */ sprintf(libname, "%s/%s", dir, file); sprintf(linkname, "%s/%s", dir, so); + if (!chroot_realpath(chroot_dir, libname, reallibname)) + warn("can't resolve %s in chroot %s", libname, chroot_dir); + if (!chroot_realpath(chroot_dir, linkname, reallinkname)) + warn("can't resolve %s in chroot %s", linkname, chroot_dir); /* see if a link already exists */ - if (!stat(linkname, &linkstat)) + if (!stat(reallinkname, &linkstat)) { /* now see if it's the one we want */ - if (stat(libname, &libstat)) + if (stat(reallibname, &libstat)) warn("can't stat %s", libname); else if (libstat.st_dev == linkstat.st_dev && libstat.st_ino == linkstat.st_ino) @@ -364,14 +402,14 @@ void link_shlib(const char *dir, const char *file, const char *so) /* then update the link, if required */ if (change > 0 && !nolinks) { - if (!lstat(linkname, &linkstat)) + if (!lstat(reallinkname, &linkstat)) { if (!S_ISLNK(linkstat.st_mode)) { warnx("%s is not a symlink", linkname); change = -1; } - else if (remove(linkname)) + else if (remove(reallinkname)) { warn("can't unlink %s", linkname); change = -1; @@ -379,7 +417,7 @@ void link_shlib(const char *dir, const char *file, const char *so) } if (change > 0) { - if (symlink(file, linkname)) + if (symlink(file, reallinkname)) { warn("can't link %s to %s", linkname, file); change = -1; @@ -441,6 +479,7 @@ void scan_dir(const char *rawname) char *so, *path, *path_n; struct lib *lp, *libs = NULL; int i, libtype, islink, expected_type = LIB_ANY; + char realname[BUFFER_SIZE]; /* We need a writable copy of this string */ path = strdup(rawname); @@ -500,8 +539,12 @@ void scan_dir(const char *rawname) if (verbose > 0) printf("%s:\n", name); + /* get real path in case of chroot */ + if (!chroot_realpath(chroot_dir, name, realname)) + warn("can't resolve %s in chroot %s", name, chroot_dir); + /* if we can't open it, we can't do anything */ - if ((dir = opendir(name)) == NULL) + if ((dir = opendir(realname)) == NULL) { warn("skipping %s", name); free(path); @@ -596,8 +639,12 @@ char *get_extpath(void) char *res = NULL, *cp; FILE *file; struct stat stat; + char realconffile[BUFFER_SIZE]; + + if (!chroot_realpath(chroot_dir, conffile, realconffile)) + return NULL; - if ((file = fopen(conffile, "r")) != NULL) + if ((file = fopen(realconffile, "r")) != NULL) { fstat(fileno(file), &stat); res = xmalloc(stat.st_size + 1); @@ -678,22 +725,38 @@ void cache_write(void) { int cachefd; int stroffset = 0; + char realcachefile[BUFFER_SIZE]; char tempfile[BUFFER_SIZE]; + header_t swap_magic; + header_t *magic_ptr; + libentry_t swap_lib; + libentry_t *lib_ptr; liblist_t *cur_lib; if (!magic.nlibs) return; - sprintf(tempfile, "%s~", cachefile); + if (!chroot_realpath(chroot_dir, cachefile, realcachefile)) + err(EXIT_FATAL,"can't resolve %s in chroot %s (%s)", + cachefile, chroot_dir, strerror(errno)); + + sprintf(tempfile, "%s~", realcachefile); if (unlink(tempfile) && errno != ENOENT) - err(EXIT_FATAL,"can't unlink %s (%s)", tempfile, strerror(errno)); + err(EXIT_FATAL,"can't unlink %s~ (%s)", cachefile, strerror(errno)); if ((cachefd = creat(tempfile, 0644)) < 0) - err(EXIT_FATAL,"can't create %s (%s)", tempfile, strerror(errno)); - - if (write(cachefd, &magic, sizeof (header_t)) != sizeof (header_t)) - err(EXIT_FATAL,"can't write %s (%s)", tempfile, strerror(errno)); + err(EXIT_FATAL,"can't create %s~ (%s)", cachefile, strerror(errno)); + + if (byteswap) { + swap_magic = magic; + swap_magic.nlibs = bswap_32(swap_magic.nlibs); + magic_ptr = &swap_magic; + } else { + magic_ptr = &magic; + } + if (write(cachefd, magic_ptr, sizeof (header_t)) != sizeof (header_t)) + err(EXIT_FATAL,"can't write %s~ (%s)", cachefile, strerror(errno)); for (cur_lib = lib_head; cur_lib != NULL; cur_lib = cur_lib->next) { @@ -701,29 +764,37 @@ void cache_write(void) stroffset += strlen(cur_lib->soname) + 1; cur_lib->liboffset = stroffset; stroffset += strlen(cur_lib->libname) + 1; - if (write(cachefd, cur_lib, sizeof (libentry_t)) != - sizeof (libentry_t)) - err(EXIT_FATAL,"can't write %s (%s)", tempfile, strerror(errno)); + if (byteswap) { + swap_lib.flags = bswap_32(cur_lib->flags); + swap_lib.sooffset = bswap_32(cur_lib->sooffset); + swap_lib.liboffset = bswap_32(cur_lib->liboffset); + lib_ptr = &swap_lib; + } else { + lib_ptr = (libentry_t *)cur_lib; + } + if (write(cachefd, lib_ptr, sizeof (libentry_t)) != + sizeof (libentry_t)) + err(EXIT_FATAL,"can't write %s~ (%s)", cachefile, strerror(errno)); } for (cur_lib = lib_head; cur_lib != NULL; cur_lib = cur_lib->next) { if (write(cachefd, cur_lib->soname, strlen(cur_lib->soname) + 1) != strlen(cur_lib->soname) + 1) - err(EXIT_FATAL,"can't write %s (%s)", tempfile, strerror(errno)); + err(EXIT_FATAL,"can't write %s~ (%s)", cachefile, strerror(errno)); if (write(cachefd, cur_lib->libname, strlen(cur_lib->libname) + 1) != strlen(cur_lib->libname) + 1) - err(EXIT_FATAL,"can't write %s (%s)", tempfile, strerror(errno)); + err(EXIT_FATAL,"can't write %s~ (%s)", cachefile, strerror(errno)); } if (close(cachefd)) - err(EXIT_FATAL,"can't close %s (%s)", tempfile, strerror(errno)); + err(EXIT_FATAL,"can't close %s~ (%s)", cachefile, strerror(errno)); if (chmod(tempfile, 0644)) - err(EXIT_FATAL,"can't chmod %s (%s)", tempfile, strerror(errno)); + err(EXIT_FATAL,"can't chmod %s~ (%s)", cachefile, strerror(errno)); - if (rename(tempfile, cachefile)) - err(EXIT_FATAL,"can't rename %s (%s)", tempfile, strerror(errno)); + if (rename(tempfile, realcachefile)) + err(EXIT_FATAL,"can't rename %s~ (%s)", cachefile, strerror(errno)); } void cache_print(void) @@ -734,8 +805,13 @@ void cache_print(void) char *strs; header_t *header; libentry_t *libent; + char realcachefile[BUFFER_SIZE]; + + if (!chroot_realpath(chroot_dir, cachefile, realcachefile)) + err(EXIT_FATAL,"can't resolve %s in chroot %s (%s)", + cachefile, chroot_dir, strerror(errno)); - if (stat(cachefile, &st) || (fd = open(cachefile, O_RDONLY))<0) + if (stat(realcachefile, &st) || (fd = open(realcachefile, O_RDONLY))<0) err(EXIT_FATAL,"can't read %s (%s)", cachefile, strerror(errno)); if ((c = mmap(0,st.st_size, PROT_READ, MAP_SHARED ,fd, 0)) == (caddr_t)-1) err(EXIT_FATAL,"can't map %s (%s)", cachefile, strerror(errno)); @@ -828,7 +904,6 @@ int main(int argc, char **argv) int nodefault = 0; char *cp, *dir, *so; int libtype, islink; - char *chroot_dir = NULL; int printcache = 0; #ifdef __LDSO_CACHE_SUPPORT__ char *extpath; @@ -891,10 +966,16 @@ int main(int argc, char **argv) } if (chroot_dir && *chroot_dir) { - if (chroot(chroot_dir) < 0) - err(EXIT_FATAL,"couldn't chroot to %s (%s)", chroot_dir, strerror(errno)); - if (chdir("/") < 0) - err(EXIT_FATAL,"couldn't chdir to / (%s)", strerror(errno)); + if (chroot(chroot_dir) < 0) { + if (chdir(chroot_dir) < 0) + err(EXIT_FATAL,"couldn't chroot to %s (%s)", chroot_dir, strerror(errno)); + } + else + { + if (chdir("/") < 0) + err(EXIT_FATAL,"couldn't chdir to / (%s)", strerror(errno)); + chroot_dir = NULL; + } } /* allow me to introduce myself, hi, my name is ... */ diff --git a/utils/readsoname2.c b/utils/readsoname2.c index 1bf47b7c6..9452c0c4d 100644 --- a/utils/readsoname2.c +++ b/utils/readsoname2.c @@ -26,7 +26,7 @@ char *readsonameXX(char *name, FILE *infile, int expected_type, int *type) if (fstat(fileno(infile), &st)) return NULL; - header = mmap(0, st.st_size, PROT_READ, MAP_SHARED, fileno(infile), 0); + header = mmap(0, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(infile), 0); if (header == (caddr_t)-1) return NULL; @@ -34,6 +34,19 @@ char *readsonameXX(char *name, FILE *infile, int expected_type, int *type) if ((char *)(epnt+1) > (char *)(header + st.st_size)) goto skip; +#if __BYTE_ORDER == __LITTLE_ENDIAN + byteswap = (epnt->e_ident[5] == ELFDATA2MSB) ? 1 : 0; +#elif __BYTE_ORDER == __BIG_ENDIAN + byteswap = (epnt->e_ident[5] == ELFDATA2LSB) ? 1 : 0; +#else +#error Unknown host byte order! +#endif + /* Be very lazy, and only byteswap the stuff we use */ + if (byteswap==1) { + epnt->e_phoff=bswap_32(epnt->e_phoff); + epnt->e_phnum=bswap_16(epnt->e_phnum); + } + ppnt = (ElfW(Phdr) *)&header[epnt->e_phoff]; if ((char *)ppnt < (char *)header || (char *)(ppnt+epnt->e_phnum) > (char *)(header + st.st_size)) @@ -41,6 +54,14 @@ char *readsonameXX(char *name, FILE *infile, int expected_type, int *type) for(i = 0; i < epnt->e_phnum; i++) { + /* Be very lazy, and only byteswap the stuff we use */ + if (byteswap==1) { + ppnt->p_type=bswap_32(ppnt->p_type); + ppnt->p_vaddr=bswap_32(ppnt->p_vaddr); + ppnt->p_offset=bswap_32(ppnt->p_offset); + ppnt->p_filesz=bswap_32(ppnt->p_filesz); + } + if (loadaddr == -1 && ppnt->p_type == PT_LOAD) loadaddr = (ppnt->p_vaddr & ~(page_size-1)) - (ppnt->p_offset & ~(page_size-1)); @@ -58,11 +79,20 @@ char *readsonameXX(char *name, FILE *infile, int expected_type, int *type) (char *)(dpnt+dynamic_size) > (char *)(header + st.st_size)) goto skip; + if (byteswap==1) { + dpnt->d_tag=bswap_32(dpnt->d_tag); + dpnt->d_un.d_val=bswap_32(dpnt->d_un.d_val); + } + while (dpnt->d_tag != DT_NULL) { if (dpnt->d_tag == DT_STRTAB) strtab_val = dpnt->d_un.d_val; dpnt++; + if (byteswap==1) { + dpnt->d_tag=bswap_32(dpnt->d_tag); + dpnt->d_un.d_val=bswap_32(dpnt->d_un.d_val); + } }; if (!strtab_val) |