commit de1a9197af7f67a89f929dcadb8ceca8c3846b1c Author: Florian Weimer 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 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. */ };