diff options
author | Florian Weimer <fweimer@redhat.com> | 2020-12-04 09:13:43 +0100 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2020-12-04 09:35:43 +0100 |
commit | 600d9e0c87940da9b0fdeff492bf888df852d40c (patch) | |
tree | 747a7d39be81da06ecab9311e3f9e8a4a2726e3c /elf/dl-hwcaps.c | |
parent | b44ac4f4c7a8bbe5eaa2701aa9452eaf2c96e1dd (diff) | |
download | glibc-600d9e0c87940da9b0fdeff492bf888df852d40c.tar glibc-600d9e0c87940da9b0fdeff492bf888df852d40c.tar.gz glibc-600d9e0c87940da9b0fdeff492bf888df852d40c.tar.bz2 glibc-600d9e0c87940da9b0fdeff492bf888df852d40c.zip |
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>
Diffstat (limited to 'elf/dl-hwcaps.c')
-rw-r--r-- | elf/dl-hwcaps.c | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/elf/dl-hwcaps.c b/elf/dl-hwcaps.c index f611f3a1a6..5a71f80154 100644 --- a/elf/dl-hwcaps.c +++ b/elf/dl-hwcaps.c @@ -89,6 +89,81 @@ copy_hwcaps (struct copy_hwcaps *target, const char *hwcaps, } } +struct dl_hwcaps_priority *_dl_hwcaps_priorities; +uint32_t _dl_hwcaps_priorities_length; + +/* Allocate _dl_hwcaps_priorities and fill it with data. */ +static void +compute_priorities (size_t total_count, const char *prepend, + uint32_t bitmask, const char *mask) +{ + _dl_hwcaps_priorities = malloc (total_count + * sizeof (*_dl_hwcaps_priorities)); + if (_dl_hwcaps_priorities == NULL) + _dl_signal_error (ENOMEM, NULL, NULL, + N_("cannot create HWCAP priorities")); + _dl_hwcaps_priorities_length = total_count; + + /* First the prepended subdirectories. */ + size_t i = 0; + { + struct dl_hwcaps_split sp; + _dl_hwcaps_split_init (&sp, prepend); + while (_dl_hwcaps_split (&sp)) + { + _dl_hwcaps_priorities[i].name = sp.segment; + _dl_hwcaps_priorities[i].name_length = sp.length; + _dl_hwcaps_priorities[i].priority = i + 1; + ++i; + } + } + + /* Then the built-in subdirectories that are actually active. */ + { + struct dl_hwcaps_split_masked sp; + _dl_hwcaps_split_masked_init (&sp, _dl_hwcaps_subdirs, bitmask, mask); + while (_dl_hwcaps_split_masked (&sp)) + { + _dl_hwcaps_priorities[i].name = sp.split.segment; + _dl_hwcaps_priorities[i].name_length = sp.split.length; + _dl_hwcaps_priorities[i].priority = i + 1; + ++i; + } + } + assert (i == total_count); +} + +/* Sort the _dl_hwcaps_priorities array by name. */ +static void +sort_priorities_by_name (void) +{ + /* Insertion sort. There is no need to link qsort into the dynamic + loader for such a short array. */ + for (size_t i = 1; i < _dl_hwcaps_priorities_length; ++i) + for (size_t j = i; j > 0; --j) + { + struct dl_hwcaps_priority *previous = _dl_hwcaps_priorities + j - 1; + struct dl_hwcaps_priority *current = _dl_hwcaps_priorities + j; + + /* Bail out if current is greater or equal to the previous + value. */ + uint32_t to_compare; + if (current->name_length < previous->name_length) + to_compare = current->name_length; + else + to_compare = previous->name_length; + int cmp = memcmp (current->name, previous->name, to_compare); + if (cmp >= 0 + || (cmp == 0 && current->name_length >= previous->name_length)) + break; + + /* Swap *previous and *current. */ + struct dl_hwcaps_priority tmp = *previous; + *previous = *current; + *current = tmp; + } +} + /* Return an array of useful/necessary hardware capability names. */ const struct r_strlenpair * _dl_important_hwcaps (const char *glibc_hwcaps_prepend, @@ -111,6 +186,9 @@ _dl_important_hwcaps (const char *glibc_hwcaps_prepend, update_hwcaps_counts (&hwcaps_counts, glibc_hwcaps_prepend, -1, NULL); update_hwcaps_counts (&hwcaps_counts, _dl_hwcaps_subdirs, hwcaps_subdirs_active, glibc_hwcaps_mask); + compute_priorities (hwcaps_counts.count, glibc_hwcaps_prepend, + hwcaps_subdirs_active, glibc_hwcaps_mask); + sort_priorities_by_name (); /* Each hwcaps subdirectory has a GLIBC_HWCAPS_PREFIX string prefix and a "/" suffix once stored in the result. */ |