mirror of
https://sourceware.org/git/glibc.git
synced 2025-07-29 11:41:21 +03:00
elf: Add glibc-hwcaps subdirectory support to ld.so cache processing
This recognizes the DL_CACHE_HWCAP_EXTENSION flag in cache entries, and picks the supported cache entry with the highest priority. The elf/tst-glibc-hwcaps-prepend-cache test documents a non-desired aspect of the current cache implementation: If the cache selects a DSO that does not exist on disk, _dl_map_object falls back to open_path, which may or may not find an alternative implementation. This is an existing limitation that also applies to the legacy hwcaps processing for ld.so.cache. Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
This commit is contained in:
194
elf/dl-cache.c
194
elf/dl-cache.c
@ -35,6 +35,144 @@ static struct cache_file *cache;
|
||||
static struct cache_file_new *cache_new;
|
||||
static size_t cachesize;
|
||||
|
||||
#ifdef SHARED
|
||||
/* This is used to cache the priorities of glibc-hwcaps
|
||||
subdirectories. The elements of _dl_cache_priorities correspond to
|
||||
the strings in the cache_extension_tag_glibc_hwcaps section. */
|
||||
static uint32_t *glibc_hwcaps_priorities;
|
||||
static uint32_t glibc_hwcaps_priorities_length;
|
||||
static uint32_t glibc_hwcaps_priorities_allocated;
|
||||
|
||||
/* True if the full malloc was used to allocated the array. */
|
||||
static bool glibc_hwcaps_priorities_malloced;
|
||||
|
||||
/* Deallocate the glibc_hwcaps_priorities array. */
|
||||
static void
|
||||
glibc_hwcaps_priorities_free (void)
|
||||
{
|
||||
/* When the minimal malloc is in use, free does not do anything,
|
||||
so it does not make sense to call it. */
|
||||
if (glibc_hwcaps_priorities_malloced)
|
||||
free (glibc_hwcaps_priorities);
|
||||
glibc_hwcaps_priorities = NULL;
|
||||
glibc_hwcaps_priorities_allocated = 0;
|
||||
}
|
||||
|
||||
/* Ordered comparison of a hwcaps string from the cache on the left
|
||||
(identified by its string table index) and a _dl_hwcaps_priorities
|
||||
element on the right. */
|
||||
static int
|
||||
glibc_hwcaps_compare (uint32_t left_index, struct dl_hwcaps_priority *right)
|
||||
{
|
||||
const char *left_name = (const char *) cache + left_index;
|
||||
uint32_t left_name_length = strlen (left_name);
|
||||
uint32_t to_compare;
|
||||
if (left_name_length < right->name_length)
|
||||
to_compare = left_name_length;
|
||||
else
|
||||
to_compare = right->name_length;
|
||||
int cmp = memcmp (left_name, right->name, to_compare);
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
if (left_name_length < right->name_length)
|
||||
return -1;
|
||||
else if (left_name_length > right->name_length)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize the glibc_hwcaps_priorities array and its length,
|
||||
glibc_hwcaps_priorities_length. */
|
||||
static void
|
||||
glibc_hwcaps_priorities_init (void)
|
||||
{
|
||||
struct cache_extension_all_loaded ext;
|
||||
if (!cache_extension_load (cache_new, cache, cachesize, &ext))
|
||||
return;
|
||||
|
||||
uint32_t length = (ext.sections[cache_extension_tag_glibc_hwcaps].size
|
||||
/ sizeof (uint32_t));
|
||||
if (length > glibc_hwcaps_priorities_allocated)
|
||||
{
|
||||
glibc_hwcaps_priorities_free ();
|
||||
|
||||
uint32_t *new_allocation = malloc (length * sizeof (uint32_t));
|
||||
if (new_allocation == NULL)
|
||||
/* This effectively disables hwcaps on memory allocation
|
||||
errors. */
|
||||
return;
|
||||
|
||||
glibc_hwcaps_priorities = new_allocation;
|
||||
glibc_hwcaps_priorities_allocated = length;
|
||||
glibc_hwcaps_priorities_malloced = __rtld_malloc_is_complete ();
|
||||
}
|
||||
|
||||
/* Compute the priorities for the subdirectories by merging the
|
||||
array in the cache with the dl_hwcaps_priorities array. */
|
||||
const uint32_t *left = ext.sections[cache_extension_tag_glibc_hwcaps].base;
|
||||
const uint32_t *left_end = left + length;
|
||||
struct dl_hwcaps_priority *right = _dl_hwcaps_priorities;
|
||||
struct dl_hwcaps_priority *right_end = right + _dl_hwcaps_priorities_length;
|
||||
uint32_t *result = glibc_hwcaps_priorities;
|
||||
|
||||
while (left < left_end && right < right_end)
|
||||
{
|
||||
if (*left < cachesize)
|
||||
{
|
||||
int cmp = glibc_hwcaps_compare (*left, right);
|
||||
if (cmp == 0)
|
||||
{
|
||||
*result = right->priority;
|
||||
++result;
|
||||
++left;
|
||||
++right;
|
||||
}
|
||||
else if (cmp < 0)
|
||||
{
|
||||
*result = 0;
|
||||
++result;
|
||||
++left;
|
||||
}
|
||||
else
|
||||
++right;
|
||||
}
|
||||
else
|
||||
{
|
||||
*result = 0;
|
||||
++result;
|
||||
}
|
||||
}
|
||||
while (left < left_end)
|
||||
{
|
||||
*result = 0;
|
||||
++result;
|
||||
++left;
|
||||
}
|
||||
|
||||
glibc_hwcaps_priorities_length = length;
|
||||
}
|
||||
|
||||
/* Return the priority of the cache_extension_tag_glibc_hwcaps section
|
||||
entry at INDEX. Zero means do not use. Otherwise, lower values
|
||||
indicate greater preference. */
|
||||
static uint32_t
|
||||
glibc_hwcaps_priority (uint32_t index)
|
||||
{
|
||||
/* This does not need to repeated initialization attempts because
|
||||
this function is only called if there is glibc-hwcaps data in the
|
||||
cache, so the first call initializes the glibc_hwcaps_priorities
|
||||
array. */
|
||||
if (glibc_hwcaps_priorities_length == 0)
|
||||
glibc_hwcaps_priorities_init ();
|
||||
|
||||
if (index < glibc_hwcaps_priorities_length)
|
||||
return glibc_hwcaps_priorities[index];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
#endif /* SHARED */
|
||||
|
||||
/* True if PTR is a valid string table index. */
|
||||
static inline bool
|
||||
_dl_cache_verify_ptr (uint32_t ptr, size_t string_table_size)
|
||||
@ -74,6 +212,9 @@ search_cache (const char *string_table, uint32_t string_table_size,
|
||||
int left = 0;
|
||||
int right = nlibs - 1;
|
||||
const char *best = NULL;
|
||||
#ifdef SHARED
|
||||
uint32_t best_priority = 0;
|
||||
#endif
|
||||
|
||||
while (left <= right)
|
||||
{
|
||||
@ -129,6 +270,11 @@ search_cache (const char *string_table, uint32_t string_table_size,
|
||||
{
|
||||
if (best == NULL || flags == GLRO (dl_correct_cache_id))
|
||||
{
|
||||
/* Named/extension hwcaps get slightly different
|
||||
treatment: We keep searching for a better
|
||||
match. */
|
||||
bool named_hwcap = false;
|
||||
|
||||
if (entry_size >= sizeof (struct file_entry_new))
|
||||
{
|
||||
/* The entry is large enough to include
|
||||
@ -136,7 +282,18 @@ search_cache (const char *string_table, uint32_t string_table_size,
|
||||
struct file_entry_new *libnew
|
||||
= (struct file_entry_new *) lib;
|
||||
|
||||
if (libnew->hwcap & hwcap_exclude)
|
||||
#ifdef SHARED
|
||||
named_hwcap = dl_cache_hwcap_extension (libnew);
|
||||
#endif
|
||||
|
||||
/* The entries with named/extension hwcaps
|
||||
have been exhausted. Return the best
|
||||
match encountered so far if there is
|
||||
one. */
|
||||
if (!named_hwcap && best != NULL)
|
||||
break;
|
||||
|
||||
if ((libnew->hwcap & hwcap_exclude) && !named_hwcap)
|
||||
continue;
|
||||
if (GLRO (dl_osversion)
|
||||
&& libnew->osversion > GLRO (dl_osversion))
|
||||
@ -146,14 +303,41 @@ search_cache (const char *string_table, uint32_t string_table_size,
|
||||
&& ((libnew->hwcap & _DL_HWCAP_PLATFORM)
|
||||
!= platform))
|
||||
continue;
|
||||
|
||||
#ifdef SHARED
|
||||
/* For named hwcaps, determine the priority
|
||||
and see if beats what has been found so
|
||||
far. */
|
||||
if (named_hwcap)
|
||||
{
|
||||
uint32_t entry_priority
|
||||
= glibc_hwcaps_priority (libnew->hwcap);
|
||||
if (entry_priority == 0)
|
||||
/* Not usable at all. Skip. */
|
||||
continue;
|
||||
else if (best == NULL
|
||||
|| entry_priority < best_priority)
|
||||
/* This entry is of higher priority
|
||||
than the previous one, or it is the
|
||||
first entry. */
|
||||
best_priority = entry_priority;
|
||||
else
|
||||
/* An entry has already been found,
|
||||
but it is a better match. */
|
||||
continue;
|
||||
}
|
||||
#endif /* SHARED */
|
||||
}
|
||||
|
||||
best = string_table + lib->value;
|
||||
|
||||
if (flags == GLRO (dl_correct_cache_id))
|
||||
if (flags == GLRO (dl_correct_cache_id)
|
||||
&& !named_hwcap)
|
||||
/* We've found an exact match for the shared
|
||||
object and no general `ELF' release. Stop
|
||||
searching. */
|
||||
searching, but not if a named (extension)
|
||||
hwcap is used. In this case, an entry with
|
||||
a higher priority may come up later. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -346,5 +530,9 @@ _dl_unload_cache (void)
|
||||
__munmap (cache, cachesize);
|
||||
cache = NULL;
|
||||
}
|
||||
#ifdef SHARED
|
||||
/* This marks the glibc_hwcaps_priorities array as out-of-date. */
|
||||
glibc_hwcaps_priorities_length = 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user