summaryrefslogtreecommitdiff
path: root/elf/dl-hwcaps.c
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2020-12-04 09:13:43 +0100
committerFlorian Weimer <fweimer@redhat.com>2020-12-04 09:35:43 +0100
commit600d9e0c87940da9b0fdeff492bf888df852d40c (patch)
tree747a7d39be81da06ecab9311e3f9e8a4a2726e3c /elf/dl-hwcaps.c
parentb44ac4f4c7a8bbe5eaa2701aa9452eaf2c96e1dd (diff)
downloadglibc-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.c78
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. */