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>
This commit is contained in:
Florian Weimer 2020-12-04 09:13:43 +01:00
parent dad90d5282
commit 84ba719b26
3 changed files with 93 additions and 2 deletions

View File

@ -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. */

View File

@ -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
{

View File

@ -16,6 +16,11 @@
License along with the GNU C Library; if not, see
<https://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 */