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