e354a5
commit 84ba719b260551918965d0a433914de683087645
e354a5
Author: Florian Weimer <fweimer@redhat.com>
e354a5
Date:   Fri Dec 4 09:13:43 2020 +0100
e354a5
e354a5
    elf: Add endianness markup to ld.so.cache (bug 27008)
e354a5
    
e354a5
    Use a reserved byte in the new format cache header to indicate whether
e354a5
    the file is in little endian or big endian format.  Eventually, this
e354a5
    information could be used to provide a unified cache for qemu-user
e354a5
    and similiar scenarios.
e354a5
    
e354a5
    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
e354a5
e354a5
diff --git a/elf/cache.c b/elf/cache.c
e354a5
index edcdd4b7cc1a6a0b..28e4889d006d2f0b 100644
e354a5
--- a/elf/cache.c
e354a5
+++ b/elf/cache.c
e354a5
@@ -152,6 +152,14 @@ print_entry (const char *lib, int flag, unsigned int osversion,
e354a5
   printf (") => %s\n", key);
e354a5
 }
e354a5
 
e354a5
+/* Print an error and exit if the new-file cache is internally
e354a5
+   inconsistent.  */
e354a5
+static void
e354a5
+check_new_cache (struct cache_file_new *cache)
e354a5
+{
e354a5
+  if (! cache_file_new_matches_endian (cache))
e354a5
+    error (EXIT_FAILURE, 0, _("Cache file has wrong endianness.\n"));
e354a5
+}
e354a5
 
e354a5
 /* Print the whole cache file, if a file contains the new cache format
e354a5
    hidden in the old one, print the contents of the new format.  */
e354a5
@@ -193,6 +201,7 @@ print_cache (const char *cache_name)
e354a5
 	  || memcmp (cache_new->version, CACHE_VERSION,
e354a5
 		      sizeof CACHE_VERSION - 1))
e354a5
 	error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
e354a5
+      check_new_cache (cache_new);
e354a5
       format = 1;
e354a5
       /* This is where the strings start.  */
e354a5
       cache_data = (const char *) cache_new;
e354a5
@@ -222,6 +231,7 @@ print_cache (const char *cache_name)
e354a5
 	      && memcmp (cache_new->version, CACHE_VERSION,
e354a5
 			 sizeof CACHE_VERSION - 1) == 0)
e354a5
 	    {
e354a5
+	      check_new_cache (cache_new);
e354a5
 	      cache_data = (const char *) cache_new;
e354a5
 	      format = 1;
e354a5
 	    }
e354a5
@@ -361,6 +371,7 @@ save_cache (const char *cache_name)
e354a5
 
e354a5
       file_entries_new->nlibs = cache_entry_count;
e354a5
       file_entries_new->len_strings = total_strlen;
e354a5
+      file_entries_new->flags = cache_file_new_flags_endian_current;
e354a5
     }
e354a5
 
e354a5
   /* Pad for alignment of cache_file_new.  */
e354a5
diff --git a/elf/dl-cache.c b/elf/dl-cache.c
e354a5
index 366a051dfcd26132..de063faa8b2c88ae 100644
e354a5
--- a/elf/dl-cache.c
e354a5
+++ b/elf/dl-cache.c
e354a5
@@ -242,6 +242,11 @@ _dl_load_cache_lookup (const char *name)
e354a5
 	  && ((cachesize - sizeof *cache_new) / sizeof (struct file_entry_new)
e354a5
 	      >= ((struct cache_file_new *) file)->nlibs))
e354a5
 	{
e354a5
+	  if (! cache_file_new_matches_endian (file))
e354a5
+	    {
e354a5
+	      __munmap (file, cachesize);
e354a5
+	      file = (void *) -1;
e354a5
+	    }
e354a5
 	  cache_new = file;
e354a5
 	  cache = file;
e354a5
 	}
e354a5
@@ -263,7 +268,20 @@ _dl_load_cache_lookup (const char *name)
e354a5
 	  if (cachesize < (offset + sizeof (struct cache_file_new))
e354a5
 	      || memcmp (cache_new->magic, CACHEMAGIC_VERSION_NEW,
e354a5
 			 sizeof CACHEMAGIC_VERSION_NEW - 1) != 0)
e354a5
-	    cache_new = (void *) -1;
e354a5
+	      cache_new = (void *) -1;
e354a5
+	  else
e354a5
+	    {
e354a5
+	      if (! cache_file_new_matches_endian (cache_new))
e354a5
+		{
e354a5
+		  /* The old-format part of the cache is bogus as well
e354a5
+		     if the endianness does not match.  (But it is
e354a5
+		     unclear how the new header can be located if the
e354a5
+		     endianess does not match.)  */
e354a5
+		  cache = (void *) -1;
e354a5
+		  cache_new = (void *) -1;
e354a5
+		  __munmap (file, cachesize);
e354a5
+		}
e354a5
+	    }
e354a5
 	}
e354a5
       else
e354a5
 	{
e354a5
diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h
e354a5
index 3c5730dfe42c7c88..6ecfd6da0e59329c 100644
e354a5
--- a/sysdeps/generic/dl-cache.h
e354a5
+++ b/sysdeps/generic/dl-cache.h
e354a5
@@ -16,6 +16,11 @@
e354a5
    License along with the GNU C Library; if not, see
e354a5
    <http://www.gnu.org/licenses/>.  */
e354a5
 
e354a5
+#ifndef _DL_CACHE_H
e354a5
+#define _DL_CACHE_H
e354a5
+
e354a5
+#include <endian.h>
e354a5
+#include <stdbool.h>
e354a5
 #include <stdint.h>
e354a5
 
e354a5
 #ifndef _DL_CACHE_DEFAULT_ID
e354a5
@@ -92,17 +97,72 @@ struct file_entry_new
e354a5
   uint64_t hwcap;		/* Hwcap entry.	 */
e354a5
 };
e354a5
 
e354a5
+/* See flags member of struct cache_file_new below.  */
e354a5
+enum
e354a5
+  {
e354a5
+    /* No endianness information available.  An old ldconfig version
e354a5
+       without endianness support wrote the file.  */
e354a5
+    cache_file_new_flags_endian_unset = 0,
e354a5
+
e354a5
+    /* Cache is invalid and should be ignored.  */
e354a5
+    cache_file_new_flags_endian_invalid = 1,
e354a5
+
e354a5
+    /* Cache format is little endian.  */
e354a5
+    cache_file_new_flags_endian_little = 2,
e354a5
+
e354a5
+    /* Cache format is big endian.  */
e354a5
+    cache_file_new_flags_endian_big = 3,
e354a5
+
e354a5
+    /* Bit mask to extract the cache_file_new_flags_endian_*
e354a5
+       values.  */
e354a5
+    cache_file_new_flags_endian_mask = 3,
e354a5
+
e354a5
+    /* Expected value of the endian bits in the flags member for the
e354a5
+       current architecture.  */
e354a5
+    cache_file_new_flags_endian_current
e354a5
+      = (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
e354a5
+	 ? cache_file_new_flags_endian_little
e354a5
+	 : cache_file_new_flags_endian_big),
e354a5
+  };
e354a5
+
e354a5
 struct cache_file_new
e354a5
 {
e354a5
   char magic[sizeof CACHEMAGIC_NEW - 1];
e354a5
   char version[sizeof CACHE_VERSION - 1];
e354a5
   uint32_t nlibs;		/* Number of entries.  */
e354a5
   uint32_t len_strings;		/* Size of string table. */
e354a5
-  uint32_t unused[5];		/* Leave space for future extensions
e354a5
+
e354a5
+  /* flags & cache_file_new_flags_endian_mask is one of the values
e354a5
+     cache_file_new_flags_endian_unset, cache_file_new_flags_endian_invalid,
e354a5
+     cache_file_new_flags_endian_little, cache_file_new_flags_endian_big.
e354a5
+
e354a5
+     The remaining bits are unused and should be generated as zero and
e354a5
+     ignored by readers.  */
e354a5
+  uint8_t flags;
e354a5
+
e354a5
+  uint8_t padding_unsed[3];	/* Not used, for future extensions.  */
e354a5
+
e354a5
+  uint32_t unused[4];		/* Leave space for future extensions
e354a5
 				   and align to 8 byte boundary.  */
e354a5
   struct file_entry_new libs[0]; /* Entries describing libraries.  */
e354a5
   /* After this the string table of size len_strings is found.	*/
e354a5
 };
e354a5
+_Static_assert (sizeof (struct cache_file_new) == 48,
e354a5
+		"size of struct cache_file_new");
e354a5
+
e354a5
+/* Returns false if *CACHE has the wrong endianness for this
e354a5
+   architecture, and true if the endianness matches (or is
e354a5
+   unknown).  */
e354a5
+static inline bool
e354a5
+cache_file_new_matches_endian (const struct cache_file_new *cache)
e354a5
+{
e354a5
+  /* A zero value for cache->flags means that no endianness
e354a5
+     information is available.  */
e354a5
+  return cache->flags == 0
e354a5
+    || ((cache->flags & cache_file_new_flags_endian_big)
e354a5
+	== cache_file_new_flags_endian_current);
e354a5
+}
e354a5
+
e354a5
 
e354a5
 /* Used to align cache_file_new.  */
e354a5
 #define ALIGN_CACHE(addr)				\
e354a5
@@ -110,3 +170,5 @@ struct cache_file_new
e354a5
  & (~(__alignof__ (struct cache_file_new) - 1)))
e354a5
 
e354a5
 extern int _dl_cache_libcmp (const char *p1, const char *p2) attribute_hidden;
e354a5
+
e354a5
+#endif /* _DL_CACHE_H */