From patchwork Mon Jun 22 15:15:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 39756 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 57CD8394880C; Mon, 22 Jun 2020 15:15:17 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 57CD8394880C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1592838917; bh=b5h+Ycx79hf/2E4NnLmMCVhwqVmG6EDTz5y4wA4ZOYM=; h=To:Subject:In-Reply-To:References:Date:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=t512WUy0Q1y6e8thww9U3E4t5L9MSolHevLW9FWpVeAJ7qHsAAXrTuBGQPjCklkch Sj9Suk5KocqlhH8skzUihfTDSdJmF19vOUx1DWTZS773og4b8KZ3b4PP154tlMF8wu EPuIkK00u72UwZ2YLWyuNyYulvN6M9SKQ3lvwr3I= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-1.mimecast.com (us-smtp-2.mimecast.com [207.211.31.81]) by sourceware.org (Postfix) with ESMTP id E006D394880C for ; Mon, 22 Jun 2020 15:15:14 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org E006D394880C Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-183-_DVrYpqsONiQSG-Cok0lhQ-1; Mon, 22 Jun 2020 11:15:12 -0400 X-MC-Unique: _DVrYpqsONiQSG-Cok0lhQ-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id B47A81030C26 for ; Mon, 22 Jun 2020 15:15:11 +0000 (UTC) Received: from oldenburg2.str.redhat.com (ovpn-112-185.ams2.redhat.com [10.36.112.185]) by smtp.corp.redhat.com (Postfix) with ESMTPS id D7C6E7C1EF for ; Mon, 22 Jun 2020 15:15:10 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH 25/30] elf: Unify old and new format cache handling code in ld.so In-Reply-To: References: Message-Id: <1b11e77444d365db44afd18b87bfe1fffd6922ad.1592836143.git.fweimer@redhat.com> Date: Mon, 22 Jun 2020 17:15:09 +0200 User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.3 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.0 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Florian Weimer via Libc-alpha From: Florian Weimer Reply-To: Florian Weimer Errors-To: libc-alpha-bounces@sourceware.org Sender: "Libc-alpha" 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. --- elf/dl-cache.c | 287 +++++++++++++++++++------------------ sysdeps/generic/dl-cache.h | 17 ++- 2 files changed, 158 insertions(+), 146 deletions(-) diff --git a/elf/dl-cache.c b/elf/dl-cache.c index 3aa8ed6c13..02c46ffb0c 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); @@ -265,51 +297,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 b154740da9..fec209509d 100644 --- a/sysdeps/generic/dl-cache.h +++ b/sysdeps/generic/dl-cache.h @@ -66,8 +66,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 @@ -84,8 +84,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. */ };