You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
365 lines
12 KiB
365 lines
12 KiB
commit de1a9197af7f67a89f929dcadb8ceca8c3846b1c
|
|
Author: Florian Weimer <fweimer@redhat.com>
|
|
Date: Fri Oct 30 11:57:59 2020 +0100
|
|
|
|
elf: Unify old and new format cache handling code in ld.so
|
|
|
|
struct file_entry_new starts with the fields of struct file_entry,
|
|
so the code can be shared if the size computation is made dynamic.
|
|
|
|
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
|
|
|
diff --git a/elf/dl-cache.c b/elf/dl-cache.c
|
|
index ef37ca18fa9fb6e0..366a051dfcd26132 100644
|
|
--- a/elf/dl-cache.c
|
|
+++ b/elf/dl-cache.c
|
|
@@ -35,103 +35,141 @@ static struct cache_file *cache;
|
|
static struct cache_file_new *cache_new;
|
|
static size_t cachesize;
|
|
|
|
-/* 1 if cache_data + PTR points into the cache. */
|
|
-#define _dl_cache_verify_ptr(ptr) (ptr < cache_data_size)
|
|
-
|
|
-#define SEARCH_CACHE(cache) \
|
|
-/* We use binary search since the table is sorted in the cache file. \
|
|
- The first matching entry in the table is returned. \
|
|
- It is important to use the same algorithm as used while generating \
|
|
- the cache file. */ \
|
|
-do \
|
|
- { \
|
|
- left = 0; \
|
|
- right = cache->nlibs - 1; \
|
|
- \
|
|
- while (left <= right) \
|
|
- { \
|
|
- __typeof__ (cache->libs[0].key) key; \
|
|
- \
|
|
- middle = (left + right) / 2; \
|
|
- \
|
|
- key = cache->libs[middle].key; \
|
|
- \
|
|
- /* Make sure string table indices are not bogus before using \
|
|
- them. */ \
|
|
- if (! _dl_cache_verify_ptr (key)) \
|
|
- { \
|
|
- cmpres = 1; \
|
|
- break; \
|
|
- } \
|
|
- \
|
|
- /* Actually compare the entry with the key. */ \
|
|
- cmpres = _dl_cache_libcmp (name, cache_data + key); \
|
|
- if (__glibc_unlikely (cmpres == 0)) \
|
|
- { \
|
|
- /* Found it. LEFT now marks the last entry for which we \
|
|
- know the name is correct. */ \
|
|
- left = middle; \
|
|
- \
|
|
- /* There might be entries with this name before the one we \
|
|
- found. So we have to find the beginning. */ \
|
|
- while (middle > 0) \
|
|
- { \
|
|
- __typeof__ (cache->libs[0].key) key; \
|
|
- \
|
|
- key = cache->libs[middle - 1].key; \
|
|
- /* Make sure string table indices are not bogus before \
|
|
- using them. */ \
|
|
- if (! _dl_cache_verify_ptr (key) \
|
|
- /* Actually compare the entry. */ \
|
|
- || _dl_cache_libcmp (name, cache_data + key) != 0) \
|
|
- break; \
|
|
- --middle; \
|
|
- } \
|
|
- \
|
|
- do \
|
|
- { \
|
|
- int flags; \
|
|
- __typeof__ (cache->libs[0]) *lib = &cache->libs[middle]; \
|
|
- \
|
|
- /* Only perform the name test if necessary. */ \
|
|
- if (middle > left \
|
|
- /* We haven't seen this string so far. Test whether the \
|
|
- index is ok and whether the name matches. Otherwise \
|
|
- we are done. */ \
|
|
- && (! _dl_cache_verify_ptr (lib->key) \
|
|
- || (_dl_cache_libcmp (name, cache_data + lib->key) \
|
|
- != 0))) \
|
|
- break; \
|
|
- \
|
|
- flags = lib->flags; \
|
|
- if (_dl_cache_check_flags (flags) \
|
|
- && _dl_cache_verify_ptr (lib->value)) \
|
|
- { \
|
|
- if (best == NULL || flags == GLRO(dl_correct_cache_id)) \
|
|
- { \
|
|
- HWCAP_CHECK; \
|
|
- best = cache_data + lib->value; \
|
|
- \
|
|
- if (flags == GLRO(dl_correct_cache_id)) \
|
|
- /* We've found an exact match for the shared \
|
|
- object and no general `ELF' release. Stop \
|
|
- searching. */ \
|
|
- break; \
|
|
- } \
|
|
- } \
|
|
- } \
|
|
- while (++middle <= right); \
|
|
- break; \
|
|
- } \
|
|
- \
|
|
- if (cmpres < 0) \
|
|
- left = middle + 1; \
|
|
- else \
|
|
- right = middle - 1; \
|
|
- } \
|
|
- } \
|
|
-while (0)
|
|
+/* True if PTR is a valid string table index. */
|
|
+static inline bool
|
|
+_dl_cache_verify_ptr (uint32_t ptr, size_t string_table_size)
|
|
+{
|
|
+ return ptr < string_table_size;
|
|
+}
|
|
+
|
|
+/* Compute the address of the element INDEX of the array at LIBS.
|
|
+ Conceptually, this is &LIBS[INDEX], but use ENTRY_SIZE for the size
|
|
+ of *LIBS. */
|
|
+static inline const struct file_entry *
|
|
+_dl_cache_file_entry (const struct file_entry *libs, size_t entry_size,
|
|
+ size_t index)
|
|
+{
|
|
+ return (const void *) libs + index * entry_size;
|
|
+}
|
|
+
|
|
+/* We use binary search since the table is sorted in the cache file.
|
|
+ The first matching entry in the table is returned. It is important
|
|
+ to use the same algorithm as used while generating the cache file.
|
|
+ STRING_TABLE_SIZE indicates the maximum offset in STRING_TABLE at
|
|
+ which data is mapped; it is not exact. */
|
|
+static const char *
|
|
+search_cache (const char *string_table, uint32_t string_table_size,
|
|
+ struct file_entry *libs, uint32_t nlibs, uint32_t entry_size,
|
|
+ const char *name)
|
|
+{
|
|
+ /* Used by the HWCAP check in the struct file_entry_new case. */
|
|
+ uint64_t platform = _dl_string_platform (GLRO (dl_platform));
|
|
+ if (platform != (uint64_t) -1)
|
|
+ platform = 1ULL << platform;
|
|
+ uint64_t hwcap_mask = GET_HWCAP_MASK ();
|
|
+#define _DL_HWCAP_TLS_MASK (1LL << 63)
|
|
+ uint64_t hwcap_exclude = ~((GLRO (dl_hwcap) & hwcap_mask)
|
|
+ | _DL_HWCAP_PLATFORM | _DL_HWCAP_TLS_MASK);
|
|
+
|
|
+ int left = 0;
|
|
+ int right = nlibs - 1;
|
|
+ const char *best = NULL;
|
|
+
|
|
+ while (left <= right)
|
|
+ {
|
|
+ int middle = (left + right) / 2;
|
|
+ uint32_t key = _dl_cache_file_entry (libs, entry_size, middle)->key;
|
|
+
|
|
+ /* Make sure string table indices are not bogus before using
|
|
+ them. */
|
|
+ if (!_dl_cache_verify_ptr (key, string_table_size))
|
|
+ return NULL;
|
|
+
|
|
+ /* Actually compare the entry with the key. */
|
|
+ int cmpres = _dl_cache_libcmp (name, string_table + key);
|
|
+ if (__glibc_unlikely (cmpres == 0))
|
|
+ {
|
|
+ /* Found it. LEFT now marks the last entry for which we
|
|
+ know the name is correct. */
|
|
+ left = middle;
|
|
+
|
|
+ /* There might be entries with this name before the one we
|
|
+ found. So we have to find the beginning. */
|
|
+ while (middle > 0)
|
|
+ {
|
|
+ key = _dl_cache_file_entry (libs, entry_size, middle - 1)->key;
|
|
+ /* Make sure string table indices are not bogus before
|
|
+ using them. */
|
|
+ if (!_dl_cache_verify_ptr (key, string_table_size)
|
|
+ /* Actually compare the entry. */
|
|
+ || _dl_cache_libcmp (name, string_table + key) != 0)
|
|
+ break;
|
|
+ --middle;
|
|
+ }
|
|
+
|
|
+ do
|
|
+ {
|
|
+ int flags;
|
|
+ const struct file_entry *lib
|
|
+ = _dl_cache_file_entry (libs, entry_size, middle);
|
|
+
|
|
+ /* Only perform the name test if necessary. */
|
|
+ if (middle > left
|
|
+ /* We haven't seen this string so far. Test whether the
|
|
+ index is ok and whether the name matches. Otherwise
|
|
+ we are done. */
|
|
+ && (! _dl_cache_verify_ptr (lib->key, string_table_size)
|
|
+ || (_dl_cache_libcmp (name, string_table + lib->key)
|
|
+ != 0)))
|
|
+ break;
|
|
+
|
|
+ flags = lib->flags;
|
|
+ if (_dl_cache_check_flags (flags)
|
|
+ && _dl_cache_verify_ptr (lib->value, string_table_size))
|
|
+ {
|
|
+ if (best == NULL || flags == GLRO (dl_correct_cache_id))
|
|
+ {
|
|
+ if (entry_size >= sizeof (struct file_entry_new))
|
|
+ {
|
|
+ /* The entry is large enough to include
|
|
+ HWCAP data. Check it. */
|
|
+ struct file_entry_new *libnew
|
|
+ = (struct file_entry_new *) lib;
|
|
+
|
|
+ if (libnew->hwcap & hwcap_exclude)
|
|
+ continue;
|
|
+ if (GLRO (dl_osversion)
|
|
+ && libnew->osversion > GLRO (dl_osversion))
|
|
+ continue;
|
|
+ if (_DL_PLATFORMS_COUNT
|
|
+ && (libnew->hwcap & _DL_HWCAP_PLATFORM) != 0
|
|
+ && ((libnew->hwcap & _DL_HWCAP_PLATFORM)
|
|
+ != platform))
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ best = string_table + lib->value;
|
|
+
|
|
+ if (flags == GLRO (dl_correct_cache_id))
|
|
+ /* We've found an exact match for the shared
|
|
+ object and no general `ELF' release. Stop
|
|
+ searching. */
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ while (++middle <= right);
|
|
+ break;
|
|
+ }
|
|
|
|
+ if (cmpres < 0)
|
|
+ left = middle + 1;
|
|
+ else
|
|
+ right = middle - 1;
|
|
+ }
|
|
+
|
|
+ return best;
|
|
+}
|
|
|
|
int
|
|
_dl_cache_libcmp (const char *p1, const char *p2)
|
|
@@ -182,12 +220,6 @@ _dl_cache_libcmp (const char *p1, const char *p2)
|
|
char *
|
|
_dl_load_cache_lookup (const char *name)
|
|
{
|
|
- int left, right, middle;
|
|
- int cmpres;
|
|
- const char *cache_data;
|
|
- uint32_t cache_data_size;
|
|
- const char *best;
|
|
-
|
|
/* Print a message if the loading of libs is traced. */
|
|
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
|
|
_dl_debug_printf (" search cache=%s\n", LD_SO_CACHE);
|
|
@@ -247,51 +279,22 @@ _dl_load_cache_lookup (const char *name)
|
|
/* Previously looked for the cache file and didn't find it. */
|
|
return NULL;
|
|
|
|
- best = NULL;
|
|
-
|
|
+ const char *best;
|
|
if (cache_new != (void *) -1)
|
|
{
|
|
- uint64_t platform;
|
|
-
|
|
- /* This is where the strings start. */
|
|
- cache_data = (const char *) cache_new;
|
|
-
|
|
- /* Now we can compute how large the string table is. */
|
|
- cache_data_size = (const char *) cache + cachesize - cache_data;
|
|
-
|
|
- platform = _dl_string_platform (GLRO(dl_platform));
|
|
- if (platform != (uint64_t) -1)
|
|
- platform = 1ULL << platform;
|
|
-
|
|
- uint64_t hwcap_mask = GET_HWCAP_MASK();
|
|
-
|
|
-#define _DL_HWCAP_TLS_MASK (1LL << 63)
|
|
- uint64_t hwcap_exclude = ~((GLRO(dl_hwcap) & hwcap_mask)
|
|
- | _DL_HWCAP_PLATFORM | _DL_HWCAP_TLS_MASK);
|
|
-
|
|
- /* Only accept hwcap if it's for the right platform. */
|
|
-#define HWCAP_CHECK \
|
|
- if (lib->hwcap & hwcap_exclude) \
|
|
- continue; \
|
|
- if (GLRO(dl_osversion) && lib->osversion > GLRO(dl_osversion)) \
|
|
- continue; \
|
|
- if (_DL_PLATFORMS_COUNT \
|
|
- && (lib->hwcap & _DL_HWCAP_PLATFORM) != 0 \
|
|
- && (lib->hwcap & _DL_HWCAP_PLATFORM) != platform) \
|
|
- continue
|
|
- SEARCH_CACHE (cache_new);
|
|
+ const char *string_table = (const char *) cache_new;
|
|
+ best = search_cache (string_table, cachesize,
|
|
+ &cache_new->libs[0].entry, cache_new->nlibs,
|
|
+ sizeof (cache_new->libs[0]), name);
|
|
}
|
|
else
|
|
{
|
|
- /* This is where the strings start. */
|
|
- cache_data = (const char *) &cache->libs[cache->nlibs];
|
|
-
|
|
- /* Now we can compute how large the string table is. */
|
|
- cache_data_size = (const char *) cache + cachesize - cache_data;
|
|
-
|
|
-#undef HWCAP_CHECK
|
|
-#define HWCAP_CHECK do {} while (0)
|
|
- SEARCH_CACHE (cache);
|
|
+ const char *string_table = (const char *) &cache->libs[cache->nlibs];
|
|
+ uint32_t string_table_size
|
|
+ = (const char *) cache + cachesize - string_table;
|
|
+ best = search_cache (string_table, string_table_size,
|
|
+ &cache->libs[0], cache->nlibs,
|
|
+ sizeof (cache->libs[0]), name);
|
|
}
|
|
|
|
/* Print our result if wanted. */
|
|
diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h
|
|
index cf43f1cf3b441bc7..3c5730dfe42c7c88 100644
|
|
--- a/sysdeps/generic/dl-cache.h
|
|
+++ b/sysdeps/generic/dl-cache.h
|
|
@@ -59,8 +59,8 @@
|
|
*/
|
|
struct file_entry
|
|
{
|
|
- int flags; /* This is 1 for an ELF library. */
|
|
- unsigned int key, value; /* String table indices. */
|
|
+ int32_t flags; /* This is 1 for an ELF library. */
|
|
+ uint32_t key, value; /* String table indices. */
|
|
};
|
|
|
|
struct cache_file
|
|
@@ -77,8 +77,17 @@ struct cache_file
|
|
|
|
struct file_entry_new
|
|
{
|
|
- int32_t flags; /* This is 1 for an ELF library. */
|
|
- uint32_t key, value; /* String table indices. */
|
|
+ union
|
|
+ {
|
|
+ /* Fields shared with struct file_entry. */
|
|
+ struct file_entry entry;
|
|
+ /* Also expose these fields directly. */
|
|
+ struct
|
|
+ {
|
|
+ int32_t flags; /* This is 1 for an ELF library. */
|
|
+ uint32_t key, value; /* String table indices. */
|
|
+ };
|
|
+ };
|
|
uint32_t osversion; /* Required OS version. */
|
|
uint64_t hwcap; /* Hwcap entry. */
|
|
};
|