| 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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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. */ |
| }; |