From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by finch.gentoo.org (Postfix) with ESMTPS id 49E681382C5 for ; Fri, 16 Apr 2021 19:03:40 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 932C1E0817; Fri, 16 Apr 2021 19:03:39 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id 6BADEE0817 for ; Fri, 16 Apr 2021 19:03:39 +0000 (UTC) Received: from oystercatcher.gentoo.org (oystercatcher.gentoo.org [148.251.78.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 16DB5340BF6 for ; Fri, 16 Apr 2021 19:03:38 +0000 (UTC) Received: from localhost.localdomain (localhost [IPv6:::1]) by oystercatcher.gentoo.org (Postfix) with ESMTP id 7A64C648 for ; Fri, 16 Apr 2021 19:03:36 +0000 (UTC) From: "Mike Frysinger" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "Mike Frysinger" Message-ID: <1618599595.c68cfa4e47d59e9903509a1d90da178a982eb27e.vapier@gentoo> Subject: [gentoo-commits] proj/pax-utils:master commit in: / X-VCS-Repository: proj/pax-utils X-VCS-Files: paxldso.c X-VCS-Directories: / X-VCS-Committer: vapier X-VCS-Committer-Name: Mike Frysinger X-VCS-Revision: c68cfa4e47d59e9903509a1d90da178a982eb27e X-VCS-Branch: master Date: Fri, 16 Apr 2021 19:03:36 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply X-Archives-Salt: 199960db-2576-4be5-b2cf-225c6403c392 X-Archives-Hash: cf9adbdd0a3e398d9d8738b265bf6e11 commit: c68cfa4e47d59e9903509a1d90da178a982eb27e Author: Mike Frysinger chromium org> AuthorDate: Fri Apr 16 18:59:55 2021 +0000 Commit: Mike Frysinger gentoo org> CommitDate: Fri Apr 16 18:59:55 2021 +0000 URL: https://gitweb.gentoo.org/proj/pax-utils.git/commit/?id=c68cfa4e paxldso: add support for "new" glibc cache format Glibc has supported a "new" cache format for a while now (almost 20 years), but since it generated both, we didn't bother to implement it, only relying on the old cache format. With glibc-2.32+ though, it now only outputs the new format. Rework the code so we support the old, new, and compat cache formats (where the new format is appended to the old one). Bug: https://bugs.gentoo.org/739014 Signed-off-by: Mike Frysinger gentoo.org> paxldso.c | 235 +++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 179 insertions(+), 56 deletions(-) diff --git a/paxldso.c b/paxldso.c index d89210d..150f490 100644 --- a/paxldso.c +++ b/paxldso.c @@ -14,19 +14,28 @@ #if PAX_LDSO_CACHE -static void *ldcache = NULL; +/* Memory region containing a specific cache. Will be a subset of the mmap. */ +static const void *ldcache = NULL; static size_t ldcache_size = 0; +/* Entire memory mapped cache file. */ +static void *ldcache_mmap_base = NULL; +static size_t ldcache_mmap_size = 0; + static char *ldso_cache_buf = NULL; static size_t ldso_cache_buf_size = 0; #if defined(__GLIBC__) || defined(__UCLIBC__) /* 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 LDSO_CACHE_MAGIC_OLD "ld.so-" +#define LDSO_CACHE_MAGIC_OLD_LEN (sizeof LDSO_CACHE_MAGIC_OLD - 1) +#define LDSO_CACHE_VER_OLD "1.7.0" +#define LDSO_CACHE_VER_OLD_LEN (sizeof LDSO_CACHE_VER_OLD - 1) +#define LDSO_CACHE_MAGIC_NEW "glibc-ld.so.cache" +#define LDSO_CACHE_MAGIC_NEW_LEN (sizeof LDSO_CACHE_MAGIC_NEW - 1) +#define LDSO_CACHE_VER_NEW "1.1" +#define LDSO_CACHE_VER_NEW_LEN (sizeof LDSO_CACHE_VER_NEW - 1) #define FLAG_ANY -1 #define FLAG_TYPE_MASK 0x00ff #define FLAG_LIBC4 0x0000 @@ -48,14 +57,45 @@ static size_t ldso_cache_buf_size = 0; #define FLAG_MIPS_LIB32_NAN2008 0x0c00 #define FLAG_MIPS64_LIBN32_NAN2008 0x0d00 #define FLAG_MIPS64_LIBN64_NAN2008 0x0e00 +#define FLAG_RISCV_FLOAT_ABI_SOFT 0x0f00 +#define FLAG_RISCV_FLOAT_ABI_DOUBLE 0x1000 typedef struct { int flags; unsigned int sooffset; unsigned int liboffset; -} libentry_t; +} libentry_old_t; + +typedef struct { + const char magic[LDSO_CACHE_MAGIC_OLD_LEN]; + const char version[LDSO_CACHE_VER_OLD_LEN]; + unsigned int nlibs; + libentry_old_t libs[0]; +} header_old_t; + +typedef struct { + int32_t flags; + uint32_t sooffset; + uint32_t liboffset; + uint32_t osversion; + uint64_t hwcap; +} libentry_new_t; -static bool is_compatible(elfobj *elf, libentry_t *libent) +typedef struct { + const char magic[LDSO_CACHE_MAGIC_NEW_LEN]; + const char version[LDSO_CACHE_VER_NEW_LEN]; + uint32_t nlibs; + uint32_t len_strings; + uint8_t flags; + uint8_t _pad_flags[3]; + uint32_t extension_offset; + uint32_t _pad_ext[3]; + libentry_new_t libs[0]; +} header_new_t; + +static bool ldcache_is_new; + +static bool is_compatible(elfobj *elf, const libentry_old_t *libent) { int flags = libent->flags & FLAG_REQUIRED_MASK; @@ -136,74 +176,124 @@ static bool is_compatible(elfobj *elf, libentry_t *libent) return false; } -char *ldso_cache_lookup_lib(elfobj *elf, const char *fname) +static void ldso_cache_load(void) { - unsigned int nlib; - char *ret = NULL; - char *strs; + int fd; + const char *cachefile; + struct stat st; + const header_old_t *header_old; + const header_new_t *header_new; - typedef struct { - char magic[LDSO_CACHE_MAGIC_LEN]; - char version[LDSO_CACHE_VER_LEN]; - unsigned int nlibs; - } header_t; - header_t *header; + if (ldcache_mmap_base != NULL) + return; - libentry_t *libent; + cachefile = root_rel_path(ldcache_path); - if (fname == NULL) - return NULL; + if (fstatat(root_fd, cachefile, &st, 0)) + return; - if (ldcache == NULL) { - int fd; - const char *cachefile = root_rel_path(ldcache_path); - struct stat st; + fd = openat(root_fd, cachefile, O_RDONLY); + if (fd == -1) + return; - if (fstatat(root_fd, cachefile, &st, 0)) - return NULL; + /* cache these values so we only map/unmap the cache file once */ + ldcache_mmap_size = st.st_size; + ldcache_mmap_base = mmap(0, ldcache_mmap_size, PROT_READ, MAP_SHARED, fd, 0); + close(fd); - 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_mmap_base == MAP_FAILED) { + ldcache_mmap_base = NULL; + return; + } - if (ldcache == MAP_FAILED) { - ldcache = NULL; - return NULL; + ldcache_size = ldcache_mmap_size; + ldcache = ldcache_mmap_base; + header_old = ldcache; + header_new = ldcache; +#define memeq(mem1, mem2) (memcmp(mem1, mem2, sizeof(mem2) - 1) == 0) + if (memeq(header_new->magic, LDSO_CACHE_MAGIC_NEW) && + memeq(header_new->version, LDSO_CACHE_VER_NEW)) { + ldcache_is_new = true; + } else if (memeq(header_old->magic, LDSO_CACHE_MAGIC_OLD) && + memeq(header_old->version, LDSO_CACHE_VER_OLD)) { + /* See if the new cache format is appended after the old cache. */ + uintptr_t end = + (uintptr_t)ldcache + sizeof(header_old_t) + + (header_old->nlibs * sizeof(libentry_old_t)); + header_new = (const void *)ALIGN_UP(end, __alignof__(header_new_t)); + if (memeq(header_new->magic, LDSO_CACHE_MAGIC_NEW) && + memeq(header_new->version, LDSO_CACHE_VER_NEW)) { + ldcache_is_new = true; + ldcache_size -= ((uintptr_t)header_new - (uintptr_t)ldcache); + ldcache = header_new; + } else { + ldcache_is_new = false; } + } else { + munmap(ldcache_mmap_base, ldcache_mmap_size); + ldcache_mmap_base = NULL; + return; + } +#undef memq - 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; - } + ldso_cache_buf_size = 4096; + ldso_cache_buf = xrealloc(ldso_cache_buf, ldso_cache_buf_size); +} - ldso_cache_buf_size = 4096; - ldso_cache_buf = xrealloc(ldso_cache_buf, ldso_cache_buf_size); - } else - header = ldcache; +char *ldso_cache_lookup_lib(elfobj *elf, const char *fname) +{ + unsigned int nlib, nlibs; + char *ret = NULL; + const char *strs; + const libentry_old_t *libent_old; + const libentry_new_t *libent_new; - libent = ldcache + sizeof(header_t); - strs = (char *) &libent[header->nlibs]; + if (fname == NULL) + return NULL; - for (nlib = 0; nlib < header->nlibs; ++nlib) { + ldso_cache_load(); + if (ldcache == NULL) + return NULL; + + if (ldcache_is_new) { + const header_new_t *header = ldcache; + libent_old = NULL; + libent_new = &header->libs[0]; + strs = (const char *)header; + nlibs = header->nlibs; + } else { + const header_old_t *header = ldcache; + libent_old = &header->libs[0]; + libent_new = NULL; + strs = (const char *)&libent_old[header->nlibs]; + nlibs = header->nlibs; + } + + /* + * TODO: Should add memory range checking in case cache file is corrupt. + * TODO: We search the cache from start to finish, but since we know the cache + * is sorted, we really should be doing a binary search to speed it up. + */ + for (nlib = 0; nlib < nlibs; ++nlib) { const char *lib; size_t lib_len; - if (!is_compatible(elf, &libent[nlib])) + /* The first few fields are the same between new/old formats. */ + const libentry_old_t *libent; + if (ldcache_is_new) { + libent = (void *)&libent_new[nlib]; + } else { + libent = &libent_old[nlib]; + } + + if (!is_compatible(elf, libent)) continue; - if (strcmp(fname, strs + libent[nlib].sooffset) != 0) + if (strcmp(fname, strs + libent->sooffset) != 0) continue; /* Return first hit because that is how the ldso rolls */ - lib = strs + libent[nlib].liboffset; + lib = strs + libent->liboffset; lib_len = strlen(lib) + 1; if (lib_len > ldso_cache_buf_size) { ldso_cache_buf = xrealloc(ldso_cache_buf, ldso_cache_buf_size + 4096); @@ -223,8 +313,8 @@ static void ldso_cache_cleanup(void) { free(ldso_cache_buf); - if (ldcache != NULL) - munmap(ldcache, ldcache_size); + if (ldcache_mmap_base != NULL) + munmap(ldcache_mmap_base, ldcache_mmap_size); } #else @@ -379,12 +469,45 @@ const char argv0[] = "paxldso"; int main(int argc, char *argv[]) { elfobj *elf = readelf(argv[0]); + ldso_cache_load(); + printf("cache file memory base is %p\n", ldcache_mmap_base); + printf("cache memory base is %p\n", ldcache); for (int i = 1; i < argc; ++i) { const char *search = argv[i]; const char *lib = ldso_cache_lookup_lib(elf, search); printf("%s -> %s\n", search, lib); } unreadelf(elf); + + if (ldcache) { + unsigned int nlib; + const char *strs, *s; + + if (ldcache_is_new) { + const header_new_t *header = ldcache; + const libentry_new_t *libents = &header->libs[0]; + strs = (const char *)header; + printf("dumping new cache format\n"); + + for (nlib = 0; nlib < header->nlibs; ++nlib) { + const libentry_new_t *libent = &libents[nlib]; + s = strs + libent->sooffset; + printf("%p: %s\n", libent, s); + } + } else { + const header_old_t *header = ldcache; + const libentry_old_t *libents = &header->libs[0]; + strs = (const char *)&libents[header->nlibs]; + printf("dumping old cache format\n"); + + for (nlib = 0; nlib < header->nlibs; ++nlib) { + const libentry_old_t *libent = &libents[nlib]; + s = strs + libent->sooffset; + printf("%p: %s\n", libent, s); + } + } + } + paxldso_cleanup(); }