diff options
author | Mike Frysinger <vapier@gentoo.org> | 2016-07-25 00:09:46 +0530 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2016-11-12 02:10:03 -0500 |
commit | 9ae5c22341a0484319c15d12cae1a46835c48379 (patch) | |
tree | a4ad7c8f02865184507b8651940e730bf3daae45 /paxldso.c | |
parent | split out fs related helper funcs as lib code (diff) | |
download | pax-utils-9ae5c22341a0484319c15d12cae1a46835c48379.tar.gz pax-utils-9ae5c22341a0484319c15d12cae1a46835c48379.tar.bz2 pax-utils-9ae5c22341a0484319c15d12cae1a46835c48379.zip |
split out ld.so.cache & ld.so.conf parsing logic
These are getting a bit big & unwieldy for keeping inlined in scanelf.
Split them out to a dedicated file instead.
Diffstat (limited to 'paxldso.c')
-rw-r--r-- | paxldso.c | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/paxldso.c b/paxldso.c new file mode 100644 index 0000000..638db77 --- /dev/null +++ b/paxldso.c @@ -0,0 +1,297 @@ +/* + * Copyright 2003-2016 Gentoo Foundation + * Distributed under the terms of the GNU General Public License v2 + * + * Copyright 2003-2012 Ned Ludd - <solar@gentoo.org> + * Copyright 2004-2016 Mike Frysinger - <vapier@gentoo.org> + */ + +#include "paxinc.h" + +/* + * ld.so.cache logic + */ + +#if PAX_LDSO_CACHE + +static void *ldcache = NULL; +static size_t ldcache_size = 0; + +/* Defines can be seen in glibc's sysdeps/generic/ldconfig.h */ +#define LDSO_CACHE_MAGIC "ld.so-" +#define LDSO_CACHE_MAGIC_LEN (sizeof LDSO_CACHE_MAGIC -1) +#define LDSO_CACHE_VER "1.7.0" +#define LDSO_CACHE_VER_LEN (sizeof LDSO_CACHE_VER -1) +#define FLAG_ANY -1 +#define FLAG_TYPE_MASK 0x00ff +#define FLAG_LIBC4 0x0000 +#define FLAG_ELF 0x0001 +#define FLAG_ELF_LIBC5 0x0002 +#define FLAG_ELF_LIBC6 0x0003 +#define FLAG_REQUIRED_MASK 0xff00 +#define FLAG_SPARC_LIB64 0x0100 +#define FLAG_IA64_LIB64 0x0200 +#define FLAG_X8664_LIB64 0x0300 +#define FLAG_S390_LIB64 0x0400 +#define FLAG_POWERPC_LIB64 0x0500 +#define FLAG_MIPS64_LIBN32 0x0600 +#define FLAG_MIPS64_LIBN64 0x0700 +#define FLAG_X8664_LIBX32 0x0800 +#define FLAG_ARM_LIBHF 0x0900 +#define FLAG_AARCH64_LIB64 0x0a00 + +#if defined(__GLIBC__) || defined(__UCLIBC__) + +char *ldso_cache_lookup_lib(elfobj *elf, const char *fname) +{ + int fd; + char *strs; + static char buf[__PAX_UTILS_PATH_MAX] = ""; + const char *cachefile = root_rel_path("/etc/ld.so.cache"); + struct stat st; + + typedef struct { + char magic[LDSO_CACHE_MAGIC_LEN]; + char version[LDSO_CACHE_VER_LEN]; + int nlibs; + } header_t; + header_t *header; + + typedef struct { + int flags; + int sooffset; + int liboffset; + } libentry_t; + libentry_t *libent; + + if (fname == NULL) + return NULL; + + if (ldcache == NULL) { + if (fstatat(root_fd, cachefile, &st, 0)) + return NULL; + + fd = openat(root_fd, cachefile, O_RDONLY); + if (fd == -1) + return NULL; + + /* cache these values so we only map/unmap the cache file once */ + ldcache_size = st.st_size; + header = ldcache = mmap(0, ldcache_size, PROT_READ, MAP_SHARED, fd, 0); + close(fd); + + if (ldcache == MAP_FAILED) { + ldcache = NULL; + return NULL; + } + + if (memcmp(header->magic, LDSO_CACHE_MAGIC, LDSO_CACHE_MAGIC_LEN) || + memcmp(header->version, LDSO_CACHE_VER, LDSO_CACHE_VER_LEN)) + { + munmap(ldcache, ldcache_size); + ldcache = NULL; + return NULL; + } + } else + header = ldcache; + + libent = ldcache + sizeof(header_t); + strs = (char *) &libent[header->nlibs]; + + for (fd = 0; fd < header->nlibs; ++fd) { + /* This should be more fine grained, but for now we assume that + * diff arches will not be cached together, and we ignore the + * the different multilib mips cases. + */ + if (elf->elf_class == ELFCLASS64 && !(libent[fd].flags & FLAG_REQUIRED_MASK)) + continue; + if (elf->elf_class == ELFCLASS32 && (libent[fd].flags & FLAG_REQUIRED_MASK)) + continue; + + if (strcmp(fname, strs + libent[fd].sooffset) != 0) + continue; + + /* Return first hit because that is how the ldso rolls */ + strncpy(buf, strs + libent[fd].liboffset, sizeof(buf)); + break; + } + + return buf; +} + +#elif defined(__NetBSD__) + +char *ldso_cache_lookup_lib(elfobj *elf, const char *fname) +{ + static char buf[__PAX_UTILS_PATH_MAX] = ""; + static struct stat st; + size_t n; + char *ldpath; + + array_for_each(ldpath, n, ldpath) { + if ((unsigned) snprintf(buf, sizeof(buf), "%s/%s", ldpath, fname) >= sizeof(buf)) + continue; /* if the pathname is too long, or something went wrong, ignore */ + + if (stat(buf, &st) != 0) + continue; /* if the lib doesn't exist in *ldpath, look further */ + + /* NetBSD doesn't actually do sanity checks, it just loads the file + * and if that doesn't work, continues looking in other directories. + * This cannot easily be safely emulated, unfortunately. For now, + * just assume that if it exists, it's a valid library. */ + + return buf; + } + + /* not found in any path */ + return NULL; +} + +#endif + +static void ldso_cache_cleanup(void) +{ + if (ldcache != NULL) + munmap(ldcache, ldcache_size); +} + +#else +# define ldso_cache_cleanup() +#endif /* PAX_LDSO_CACHE */ + +/* + * ld.so.conf logic + */ + +#if PAX_LDSO_CONFIG + +static array_t _ldpaths = array_init_decl; +array_t *ldpaths = &_ldpaths; + +#if defined(__GLIBC__) || defined(__UCLIBC__) || defined(__NetBSD__) + +int ldso_config_load(const char *fname) +{ + FILE *fp = NULL; + char *p, *path; + size_t len; + int curr_fd = -1; + + fp = fopenat_r(root_fd, root_rel_path(fname)); + if (fp == NULL) + return -1; + + path = NULL; + len = 0; + while (getline(&path, &len, fp) != -1) { + if ((p = strrchr(path, '\r')) != NULL) + *p = 0; + if ((p = strchr(path, '\n')) != NULL) + *p = 0; + + /* recursive includes of the same file will make this segfault. */ + if ((memcmp(path, "include", 7) == 0) && isblank(path[7])) { + glob_t gl; + size_t x; + const char *gpath; + + /* re-use existing path buffer ... need to be creative */ + if (path[8] != '/') + gpath = memcpy(path + 3, "/etc/", 5); + else + gpath = path + 8; + if (root_fd != AT_FDCWD) { + if (curr_fd == -1) { + curr_fd = open(".", O_RDONLY|O_CLOEXEC); + if (fchdir(root_fd)) + errp("unable to change to root dir"); + } + gpath = root_rel_path(gpath); + } + + if (glob(gpath, 0, NULL, &gl) == 0) { + for (x = 0; x < gl.gl_pathc; ++x) { + /* try to avoid direct loops */ + if (strcmp(gl.gl_pathv[x], fname) == 0) + continue; + ldso_config_load(gl.gl_pathv[x]); + } + globfree(&gl); + } + + /* failed globs are ignored by glibc */ + continue; + } + + if (*path != '/') + continue; + + xarraypush_str(ldpaths, path); + } + free(path); + + fclose(fp); + + if (curr_fd != -1) { + if (fchdir(curr_fd)) + {/* don't care */} + close(curr_fd); + } + + return 0; +} + +#elif defined(__FreeBSD__) || defined(__DragonFly__) + +int ldso_config_load(const char *fname) +{ + FILE *fp = NULL; + char *b = NULL, *p; + struct elfhints_hdr hdr; + + fp = fopenat_r(root_fd, root_rel_path(fname)); + if (fp == NULL) + return -1; + + if (fread(&hdr, 1, sizeof(hdr), fp) != sizeof(hdr) || + hdr.magic != ELFHINTS_MAGIC || hdr.version != 1 || + fseek(fp, hdr.strtab + hdr.dirlist, SEEK_SET) == -1) + { + fclose(fp); + return -1; + } + + b = xmalloc(hdr.dirlistlen + 1); + if (fread(b, 1, hdr.dirlistlen+1, fp) != hdr.dirlistlen+1) { + fclose(fp); + free(b); + return -1; + } + + while ((p = strsep(&b, ":"))) { + if (*p == '\0') + continue; + xarraypush_str(ldpaths, p); + } + + free(b); + fclose(fp); + return 0; +} + +#endif + +static void ldso_config_cleanup(void) +{ + xarrayfree(ldpaths); +} + +#else +# define ldso_config_cleanup() +#endif /* PAX_LDSO_CONFIG */ + +void paxldso_cleanup(void) +{ + ldso_cache_cleanup(); + ldso_config_cleanup(); +} |