aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@gentoo.org>2016-07-27 12:47:39 +0800
committerMike Frysinger <vapier@gentoo.org>2016-11-12 02:10:13 -0500
commite20faa493f9dd326f7bc0fbf2c0abe1a4fdae0e4 (patch)
treef44b7088d6e62ca401b302a2efaa0205842a7384
parentpaxldso: clean up local vars & types a bit (diff)
downloadpax-utils-e20faa493f9dd326f7bc0fbf2c0abe1a4fdae0e4.tar.gz
pax-utils-e20faa493f9dd326f7bc0fbf2c0abe1a4fdae0e4.tar.bz2
pax-utils-e20faa493f9dd326f7bc0fbf2c0abe1a4fdae0e4.zip
paxldso: properly decode the flags bits in the cachev1.1.7
This fixes handling of multiple ABIs for a single arch like x86/x86_64/x32 or mips o32/n32/n64. It also fixes decoding of arm float libs.
-rw-r--r--paxldso.c148
1 files changed, 114 insertions, 34 deletions
diff --git a/paxldso.c b/paxldso.c
index 5ea0bfc..2227387 100644
--- a/paxldso.c
+++ b/paxldso.c
@@ -20,30 +20,121 @@ static size_t ldcache_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 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
+#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
+#define FLAG_ARM_LIBSF 0x0b00
+#define FLAG_MIPS_LIB32_NAN2008 0x0c00
+#define FLAG_MIPS64_LIBN32_NAN2008 0x0d00
+#define FLAG_MIPS64_LIBN64_NAN2008 0x0e00
+
+typedef struct {
+ int flags;
+ unsigned int sooffset;
+ unsigned int liboffset;
+} libentry_t;
+
+static bool is_compatible(elfobj *elf, libentry_t *libent)
+{
+ int flags = libent->flags & FLAG_REQUIRED_MASK;
+
+ /* We assume that ((flags & FLAG_TYPE_MASK) == FLAG_ELF_LIBC6)
+ * since anything older is very very old and no one cares.
+ *
+ * Otherwise we really only need to check here for cases where
+ * an arch has more than one ABI per bitsize (e.g. x86, x32, and
+ * x86_64). The default case should be fine otherwise.
+ */
+
+ if (elf->elf_class == ELFCLASS32) {
+ Elf32_Ehdr *ehdr = EHDR32(elf->ehdr);
+
+ switch (EGET(ehdr->e_machine)) {
+ case EM_AARCH64:
+ break;
+ case EM_ARM:
+ if ((flags == FLAG_ARM_LIBHF && (ehdr->e_flags & EF_ARM_ABI_FLOAT_HARD)) ||
+ (flags == FLAG_ARM_LIBSF && (ehdr->e_flags & EF_ARM_ABI_FLOAT_SOFT)) ||
+ (flags == 0 && !(ehdr->e_flags & (EF_ARM_ABI_FLOAT_HARD | EF_ARM_ABI_FLOAT_SOFT))))
+ return true;
+ break;
+ case EM_IA_64:
+ break;
+ case EM_MIPS: {
+ int ef_flags = (ehdr->e_flags & (EF_MIPS_ABI2 | EF_MIPS_NAN2008));
+ if ((flags == 0 && ef_flags == 0) ||
+ (flags == FLAG_MIPS64_LIBN32 && ef_flags == EF_MIPS_ABI2) ||
+ (flags == FLAG_MIPS_LIB32_NAN2008 && ef_flags == EF_MIPS_NAN2008) ||
+ (flags == FLAG_MIPS64_LIBN32_NAN2008 && ef_flags == (EF_MIPS_ABI2 | EF_MIPS_NAN2008)))
+ return true;
+ break;
+ }
+ case EM_X86_64:
+ if (flags == FLAG_X8664_LIBX32)
+ return true;
+ break;
+ default:
+ /* A sane enough default. */
+ if (flags == 0)
+ return true;
+ break;
+ }
+ } else {
+ Elf64_Ehdr *ehdr = EHDR64(elf->ehdr);
+
+ switch (EGET(ehdr->e_machine)) {
+ case EM_AARCH64:
+ if (flags == FLAG_AARCH64_LIB64)
+ return true;
+ break;
+ case EM_ARM:
+ break;
+ case EM_IA_64:
+ if (flags == FLAG_IA64_LIB64)
+ return true;
+ break;
+ case EM_MIPS: {
+ int ef_flags = (ehdr->e_flags & EF_MIPS_NAN2008);
+ if ((flags == FLAG_MIPS64_LIBN64 && ef_flags == 0) ||
+ (flags == FLAG_MIPS64_LIBN64_NAN2008 && ef_flags == EF_MIPS_NAN2008))
+ return true;
+ break;
+ }
+ case EM_X86_64:
+ if (flags == FLAG_X8664_LIB64)
+ return true;
+ break;
+ default:
+ /* A sane enough default. */
+ if (flags != 0)
+ return true;
+ break;
+ }
+ }
-#if defined(__GLIBC__) || defined(__UCLIBC__)
+ return false;
+}
char *ldso_cache_lookup_lib(elfobj *elf, const char *fname)
{
@@ -58,11 +149,6 @@ char *ldso_cache_lookup_lib(elfobj *elf, const char *fname)
} header_t;
header_t *header;
- typedef struct {
- int flags;
- unsigned int sooffset;
- unsigned int liboffset;
- } libentry_t;
libentry_t *libent;
if (fname == NULL)
@@ -110,13 +196,7 @@ char *ldso_cache_lookup_lib(elfobj *elf, const char *fname)
const char *lib;
size_t lib_len;
- /* 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[nlib].flags & FLAG_REQUIRED_MASK))
- continue;
- if (elf->elf_class == ELFCLASS32 && (libent[nlib].flags & FLAG_REQUIRED_MASK))
+ if (!is_compatible(elf, &libent[nlib]))
continue;
if (strcmp(fname, strs + libent[nlib].sooffset) != 0)