aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@gentoo.org>2016-07-25 00:09:46 +0530
committerMike Frysinger <vapier@gentoo.org>2016-11-12 02:10:03 -0500
commit9ae5c22341a0484319c15d12cae1a46835c48379 (patch)
treea4ad7c8f02865184507b8651940e730bf3daae45 /paxldso.c
parentsplit out fs related helper funcs as lib code (diff)
downloadpax-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.c297
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();
+}