From patchwork Mon Nov 9 18:40:16 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 40969 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 07CC13836C37; Mon, 9 Nov 2020 18:40:29 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 07CC13836C37 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1604947229; bh=qwCBkR3tNL2VnUyFrU+n7nlkt2feMMNv/OgQlTlADdQ=; 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=oE9lNbctxDsJMng3l+xIXGG20/klSe7JIhoIyl+TqRZsZ5n4Rz8qX5cQV+OdPcFxm Tn266ILQceQgLkNSWUzDmGm/SyltnZfsxgpgPizdAr46zWyiw9lZXi+/efH3f2MMEo EzchDn1tqTwRqtD5Ul60nA7GO3ejjrTiOkJ3IX1k= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by sourceware.org (Postfix) with ESMTP id A773D386EC70 for ; Mon, 9 Nov 2020 18:40:26 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org A773D386EC70 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-171-9GTzTBH1NUa_3N20fwJ9Hw-1; Mon, 09 Nov 2020 13:40:19 -0500 X-MC-Unique: 9GTzTBH1NUa_3N20fwJ9Hw-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 0A0A3801FDE for ; Mon, 9 Nov 2020 18:40:19 +0000 (UTC) Received: from oldenburg2.str.redhat.com (ovpn-113-222.ams2.redhat.com [10.36.113.222]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 7BDB46EF4A for ; Mon, 9 Nov 2020 18:40:18 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH 01/11] support: Add support_copy_file In-Reply-To: References: Message-Id: Date: Mon, 09 Nov 2020 19:40:16 +0100 User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (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=-12.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, 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" --- support/Makefile | 1 + support/support.h | 5 +++++ support/support_copy_file.c | 43 +++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 support/support_copy_file.c Reviewed-by: Adhemerval Zanella diff --git a/support/Makefile b/support/Makefile index 4154863511..f5f59bf8d2 100644 --- a/support/Makefile +++ b/support/Makefile @@ -46,6 +46,7 @@ libsupport-routines = \ support_capture_subprocess \ support_capture_subprocess_check \ support_chroot \ + support_copy_file \ support_copy_file_range \ support_descriptor_supports_holes \ support_descriptors \ diff --git a/support/support.h b/support/support.h index 905b5a76d8..abda3a69f1 100644 --- a/support/support.h +++ b/support/support.h @@ -119,6 +119,11 @@ extern const char support_install_rootsbindir[]; /* Corresponds to the install's compiled locale directory. */ extern const char support_complocaledir_prefix[]; +/* Copies the file at the path FROM to TO. If TO does not exist, it + is created. If TO is a regular file, it is truncated before + copying. The file mode is copied, but the permissions are not. */ +extern void support_copy_file (const char *from, const char *to); + extern ssize_t support_copy_file_range (int, off64_t *, int, off64_t *, size_t, unsigned int); diff --git a/support/support_copy_file.c b/support/support_copy_file.c new file mode 100644 index 0000000000..bf8f83803a --- /dev/null +++ b/support/support_copy_file.c @@ -0,0 +1,43 @@ +/* Copy a file from one path to another. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include + +void +support_copy_file (const char *from, const char *to) +{ + struct stat64 st; + xstat (from, &st); + int fd_from = xopen (from, O_RDONLY, 0); + mode_t mode = st.st_mode & 0777; + int fd_to = xopen (to, O_WRONLY | O_TRUNC | O_CREAT, mode); + ssize_t ret = support_copy_file_range (fd_from, NULL, fd_to, NULL, + st.st_size, 0); + if (ret < 0) + FAIL_EXIT1 ("copying from \"%s\" to \"%s\": %m", from, to); + if (ret != st.st_size) + FAIL_EXIT1 ("copying from \"%s\" to \"%s\": only %zd of %llu bytes copied", + from, to, ret, (unsigned long long int) st.st_size); + if (fchmod (fd_to, mode) < 0) + FAIL_EXIT1 ("fchmod on %s: %m", to); + xclose (fd_to); + xclose (fd_from); +} From patchwork Mon Nov 9 18:40:29 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 40970 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 97DC5386EC70; Mon, 9 Nov 2020 18:40:38 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 97DC5386EC70 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1604947238; bh=Mnkile/JvGAQT3T8N8H1xFrQrUD3BkAiPpiVRwBOl3k=; 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=eeBPqLA+/JRvvay1vqVJDsd/8+/ETPOQda/hM82mJXr+hiq5onv0u2Xp+L6/6KBXq lg1s6m/kAUFHqXphNg5RPl4i/U7zjQVQNUA45chKDli1Ml8xXrPPEWECcJNvOBeOTb NVVxtSzuR2/AZK8PCK9mYKtWbu5pOxfsIxd83wqE= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [63.128.21.124]) by sourceware.org (Postfix) with ESMTP id 3A765385EC57 for ; Mon, 9 Nov 2020 18:40:35 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 3A765385EC57 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-208-DnZrQFj8M3WyAF9n8aa44A-1; Mon, 09 Nov 2020 13:40:32 -0500 X-MC-Unique: DnZrQFj8M3WyAF9n8aa44A-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id C34F3805F13 for ; Mon, 9 Nov 2020 18:40:31 +0000 (UTC) Received: from oldenburg2.str.redhat.com (ovpn-113-222.ams2.redhat.com [10.36.113.222]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 15D825B4CD for ; Mon, 9 Nov 2020 18:40:30 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH 02/11] elf: Introduce enum opt_format in the ldconfig implementation In-Reply-To: References: Message-Id: Date: Mon, 09 Nov 2020 19:40:29 +0100 User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.2 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_H5, 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" --- elf/cache.c | 24 ++++++++++++------------ elf/ldconfig.c | 9 ++++----- sysdeps/generic/ldconfig.h | 9 ++++++++- 3 files changed, 24 insertions(+), 18 deletions(-) Reviewed-by: Adhemerval Zanella diff --git a/elf/cache.c b/elf/cache.c index 1eb1455883..c241c17ef9 100644 --- a/elf/cache.c +++ b/elf/cache.c @@ -321,13 +321,13 @@ save_cache (const char *cache_name) struct cache_file *file_entries = NULL; size_t file_entries_size = 0; - if (opt_format != 2) + if (opt_format != opt_format_new) { /* struct cache_file_new is 64-bit aligned on some arches while only 32-bit aligned on other arches. Duplicate last old cache entry so that new cache in ld.so.cache can be used by both. */ - if (opt_format != 0) + if (opt_format != opt_format_old) cache_entry_old_count = (cache_entry_old_count + 1) & ~1; /* And the list of all entries in the old format. */ @@ -345,7 +345,7 @@ save_cache (const char *cache_name) struct cache_file_new *file_entries_new = NULL; size_t file_entries_new_size = 0; - if (opt_format != 0) + if (opt_format != opt_format_old) { /* And the list of all entries in the new format. */ file_entries_new_size = sizeof (struct cache_file_new) @@ -370,7 +370,7 @@ save_cache (const char *cache_name) table, we have to adjust all string indices for this so that old libc5/glibc 2 dynamic linkers just ignore them. */ unsigned int str_offset; - if (opt_format != 0) + if (opt_format != opt_format_old) str_offset = file_entries_new_size; else str_offset = 0; @@ -385,13 +385,13 @@ save_cache (const char *cache_name) entry = entry->next, ++idx_new) { /* First the library. */ - if (opt_format != 2 && entry->hwcap == 0) + if (opt_format != opt_format_new && entry->hwcap == 0) { file_entries->libs[idx_old].flags = entry->flags; /* XXX: Actually we can optimize here and remove duplicates. */ file_entries->libs[idx_old].key = str_offset + pad; } - if (opt_format != 0) + if (opt_format != opt_format_old) { /* We could subtract file_entries_new_size from str_offset - not doing so makes the code easier, the string table @@ -407,9 +407,9 @@ save_cache (const char *cache_name) str = mempcpy (str, entry->lib, len); str_offset += len; /* Then the path. */ - if (opt_format != 2 && entry->hwcap == 0) + if (opt_format != opt_format_new && entry->hwcap == 0) file_entries->libs[idx_old].value = str_offset + pad; - if (opt_format != 0) + if (opt_format != opt_format_old) file_entries_new->libs[idx_new].value = str_offset; len = strlen (entry->path) + 1; str = mempcpy (str, entry->path, len); @@ -420,7 +420,7 @@ save_cache (const char *cache_name) } /* Duplicate last old cache entry if needed. */ - if (opt_format != 2 + if (opt_format != opt_format_new && idx_old < cache_entry_old_count) file_entries->libs[idx_old] = file_entries->libs[idx_old - 1]; @@ -438,16 +438,16 @@ save_cache (const char *cache_name) temp_name); /* Write contents. */ - if (opt_format != 2) + if (opt_format != opt_format_new) { if (write (fd, file_entries, file_entries_size) != (ssize_t) file_entries_size) error (EXIT_FAILURE, errno, _("Writing of cache data failed")); } - if (opt_format != 0) + if (opt_format != opt_format_old) { /* Align cache. */ - if (opt_format != 2) + if (opt_format != opt_format_new) { char zero[pad]; memset (zero, '\0', pad); diff --git a/elf/ldconfig.c b/elf/ldconfig.c index 3768267bac..006198fe59 100644 --- a/elf/ldconfig.c +++ b/elf/ldconfig.c @@ -100,8 +100,7 @@ static int opt_print_cache; int opt_verbose; /* Format to support. */ -/* 0: only libc5/glibc2; 1: both; 2: only glibc 2.2. */ -int opt_format = 2; +enum opt_format opt_format = opt_format_new; /* Build cache. */ static int opt_build_cache = 1; @@ -281,11 +280,11 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case 'c': if (strcmp (arg, "old") == 0) - opt_format = 0; + opt_format = opt_format_old; else if (strcmp (arg, "compat") == 0) - opt_format = 1; + opt_format = opt_format_compat; else if (strcmp (arg, "new") == 0) - opt_format = 2; + opt_format = opt_format_new; break; default: return ARGP_ERR_UNKNOWN; diff --git a/sysdeps/generic/ldconfig.h b/sysdeps/generic/ldconfig.h index b64aab0064..cfec9d4668 100644 --- a/sysdeps/generic/ldconfig.h +++ b/sysdeps/generic/ldconfig.h @@ -90,7 +90,14 @@ extern char *chroot_canon (const char *chroot, const char *name); /* Declared in ldconfig.c. */ extern int opt_verbose; -extern int opt_format; +enum opt_format + { + opt_format_old = 0, /* Use struct cache_file. */ + opt_format_compat = 1, /* Use both, old format followed by new. */ + opt_format_new = 2, /* Use struct cache_file_new. */ + }; + +extern enum opt_format opt_format; /* Prototypes for a few program-wide used functions. */ #include From patchwork Mon Nov 9 18:40:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 40971 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 354E0388E81A; Mon, 9 Nov 2020 18:40:51 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 354E0388E81A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1604947251; bh=w5KctgcCwj+yMryA356wrIqlnq9kAI10qsNV9oirQc4=; 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=WapyrueFrXoxBBy89rBpS13EQimKAgMdFaScE24F2t3mQDMiI21ZnQm1saFSaP7kD gfbnEMxEsxticPYz0MAyCJJHc8FVQdAfmW8Zhg+1ZOzWiCwnbOe4DEjJbvQAQxwXEh Jr/5BQuqU3jhqxdxm8/guTLbLoVGA+czfSjqdem8= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [63.128.21.124]) by sourceware.org (Postfix) with ESMTP id 42F2E385EC57 for ; Mon, 9 Nov 2020 18:40:46 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 42F2E385EC57 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-6-UfYzDOarO22SgM_ywLSNtA-1; Mon, 09 Nov 2020 13:40:43 -0500 X-MC-Unique: UfYzDOarO22SgM_ywLSNtA-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 65AE61084D65 for ; Mon, 9 Nov 2020 18:40:41 +0000 (UTC) Received: from oldenburg2.str.redhat.com (ovpn-113-222.ams2.redhat.com [10.36.113.222]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 454F96EF4A for ; Mon, 9 Nov 2020 18:40:40 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH 03/11] elf: Add glibc-hwcaps support for LD_LIBRARY_PATH In-Reply-To: References: Message-Id: <965e3bbd05f0f8227a71e73b9a1f1e4c517bdb14.1604946656.git.fweimer@redhat.com> Date: Mon, 09 Nov 2020 19:40:38 +0100 User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (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=-12.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H5, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_PASS, TXREP, T_FILL_THIS_FORM_SHORT 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" This hacks non-power-set processing into _dl_important_hwcaps. Once the legacy hwcaps handling goes away, the subdirectory handling needs to be reworked, but it is premature to do this while both approaches are still supported. Reviewed-by: Adhemerval Zanella --- elf/Makefile | 67 ++++++++++++++-- elf/dl-hwcaps-subdirs.c | 29 +++++++ elf/dl-hwcaps.c | 138 +++++++++++++++++++++++++++----- elf/dl-hwcaps.h | 105 +++++++++++++++++++++++++ elf/dl-hwcaps_split.c | 77 ++++++++++++++++++ elf/dl-load.c | 7 +- elf/dl-main.h | 11 ++- elf/dl-support.c | 5 +- elf/dl-usage.c | 68 +++++++++++++++- elf/markermodMARKER-VALUE.c | 29 +++++++ elf/rtld.c | 18 +++++ elf/tst-dl-hwcaps_split.c | 139 +++++++++++++++++++++++++++++++++ elf/tst-glibc-hwcaps-mask.c | 31 ++++++++ elf/tst-glibc-hwcaps-prepend.c | 32 ++++++++ elf/tst-glibc-hwcaps.c | 28 +++++++ sysdeps/generic/ldsodefs.h | 20 +++-- 16 files changed, 771 insertions(+), 33 deletions(-) create mode 100644 elf/dl-hwcaps-subdirs.c create mode 100644 elf/dl-hwcaps_split.c create mode 100644 elf/markermodMARKER-VALUE.c create mode 100644 elf/tst-dl-hwcaps_split.c create mode 100644 elf/tst-glibc-hwcaps-mask.c create mode 100644 elf/tst-glibc-hwcaps-prepend.c create mode 100644 elf/tst-glibc-hwcaps.c diff --git a/elf/Makefile b/elf/Makefile index f10cc59e7c..d2f7e99863 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -59,7 +59,8 @@ elide-routines.os = $(all-dl-routines) dl-support enbl-secure dl-origin \ # ld.so uses those routines, plus some special stuff for being the program # interpreter and operating independent of libc. rtld-routines = rtld $(all-dl-routines) dl-sysdep dl-environ dl-minimal \ - dl-error-minimal dl-conflict dl-hwcaps dl-usage + dl-error-minimal dl-conflict dl-hwcaps dl-hwcaps_split dl-hwcaps-subdirs \ + dl-usage all-rtld-routines = $(rtld-routines) $(sysdep-rtld-routines) CFLAGS-dl-runtime.c += -fexceptions -fasynchronous-unwind-tables @@ -210,14 +211,14 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \ tst-filterobj tst-filterobj-dlopen tst-auxobj tst-auxobj-dlopen \ tst-audit14 tst-audit15 tst-audit16 \ tst-single_threaded tst-single_threaded-pthread \ - tst-tls-ie tst-tls-ie-dlmopen \ - argv0test + tst-tls-ie tst-tls-ie-dlmopen argv0test \ + tst-glibc-hwcaps tst-glibc-hwcaps-prepend tst-glibc-hwcaps-mask # reldep9 tests-internal += loadtest unload unload2 circleload1 \ neededtest neededtest2 neededtest3 neededtest4 \ tst-tls3 tst-tls6 tst-tls7 tst-tls8 tst-dlmopen2 \ tst-ptrguard1 tst-stackguard1 tst-libc_dlvsym \ - tst-create_format1 tst-tls-surplus + tst-create_format1 tst-tls-surplus tst-dl-hwcaps_split tests-container += tst-pldd tst-dlopen-tlsmodid-container \ tst-dlopen-self-container test-srcs = tst-pathopt @@ -329,7 +330,10 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ tst-single_threaded-mod3 tst-single_threaded-mod4 \ tst-tls-ie-mod0 tst-tls-ie-mod1 tst-tls-ie-mod2 \ tst-tls-ie-mod3 tst-tls-ie-mod4 tst-tls-ie-mod5 \ - tst-tls-ie-mod6 + tst-tls-ie-mod6 libmarkermod1-1 libmarkermod1-2 libmarkermod1-3 \ + libmarkermod2-1 libmarkermod2-2 \ + libmarkermod3-1 libmarkermod3-2 libmarkermod3-3 \ + libmarkermod4-1 libmarkermod4-2 libmarkermod4-3 libmarkermod4-4 \ # Most modules build with _ISOMAC defined, but those filtered out # depend on internal headers. @@ -1812,3 +1816,56 @@ $(objpfx)argv0test.out: tst-rtld-argv0.sh $(objpfx)ld.so \ '$(test-wrapper-env)' '$(run_program_env)' \ '$(rpath-link)' 'test-argv0' > $@; \ $(evaluate-test) + +# Most likely search subdirectories, for each supported architecture. +# Used to obtain test coverage wide test coverage. +glibc-hwcaps-first-subdirs-for-tests = + +# The test modules are parameterized by preprocessor macros. +LDFLAGS-libmarkermod1-1.so += -Wl,-soname,libmarkermod1.so +LDFLAGS-libmarkermod2-1.so += -Wl,-soname,libmarkermod2.so +LDFLAGS-libmarkermod3-1.so += -Wl,-soname,libmarkermod3.so +LDFLAGS-libmarkermod4-1.so += -Wl,-soname,libmarkermod4.so +$(objpfx)libmarkermod%.os : markermodMARKER-VALUE.c + $(compile-command.c) \ + -DMARKER=marker$(firstword $(subst -, ,$*)) \ + -DVALUE=$(lastword $(subst -, ,$*)) +$(objpfx)libmarkermod1.so: $(objpfx)libmarkermod1-1.so + cp $< $@ +$(objpfx)libmarkermod2.so: $(objpfx)libmarkermod2-1.so + cp $< $@ +$(objpfx)libmarkermod3.so: $(objpfx)libmarkermod3-1.so + cp $< $@ +$(objpfx)libmarkermod4.so: $(objpfx)libmarkermod4-1.so + cp $< $@ + +# tst-glibc-hwcaps-prepend checks that --glibc-hwcaps-prepend is +# preferred over auto-detected subdirectories. +$(objpfx)tst-glibc-hwcaps-prepend: $(objpfx)libmarkermod1-1.so +$(objpfx)glibc-hwcaps/prepend-markermod1/libmarkermod1.so: \ + $(objpfx)libmarkermod1-2.so + $(make-target-directory) + cp $< $@ +$(objpfx)glibc-hwcaps/%/libmarkermod1.so: $(objpfx)libmarkermod1-3.so + $(make-target-directory) + cp $< $@ +$(objpfx)tst-glibc-hwcaps-prepend.out: \ + $(objpfx)tst-glibc-hwcaps-prepend $(objpfx)libmarkermod1.so \ + $(patsubst %,$(objpfx)glibc-hwcaps/%/libmarkermod1.so,prepend-markermod1 \ + $(glibc-hwcaps-first-subdirs-for-tests)) + $(test-wrapper) $(rtld-prefix) \ + --glibc-hwcaps-prepend prepend-markermod1 \ + $< > $@; \ + $(evaluate-test) + +# tst-glibc-hwcaps-mask checks that --glibc-hwcaps-mask can be used to +# suppress all auto-detected subdirectories. +$(objpfx)tst-glibc-hwcaps-mask: $(objpfx)libmarkermod1-1.so +$(objpfx)tst-glibc-hwcaps-mask.out: \ + $(objpfx)tst-glibc-hwcaps-mask $(objpfx)libmarkermod1.so \ + $(patsubst %,$(objpfx)glibc-hwcaps/%/libmarkermod1.so,\ + $(glibc-hwcaps-first-subdirs-for-tests)) + $(test-wrapper) $(rtld-prefix) \ + --glibc-hwcaps-mask does-not-exist \ + $< > $@; \ + $(evaluate-test) diff --git a/elf/dl-hwcaps-subdirs.c b/elf/dl-hwcaps-subdirs.c new file mode 100644 index 0000000000..60c6d59731 --- /dev/null +++ b/elf/dl-hwcaps-subdirs.c @@ -0,0 +1,29 @@ +/* Architecture-specific glibc-hwcaps subdirectories. Generic version. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +/* In the generic version, there are no subdirectories defined. */ + +const char _dl_hwcaps_subdirs[] = ""; + +uint32_t +_dl_hwcaps_subdirs_active (void) +{ + return 0; +} diff --git a/elf/dl-hwcaps.c b/elf/dl-hwcaps.c index 44dbac099f..f611f3a1a6 100644 --- a/elf/dl-hwcaps.c +++ b/elf/dl-hwcaps.c @@ -26,20 +26,97 @@ #include #include +/* This is the result of counting the substrings in a colon-separated + hwcaps string. */ +struct hwcaps_counts +{ + /* Number of substrings. */ + size_t count; + + /* Sum of the individual substring lengths (without separators or + null terminators). */ + size_t total_length; + + /* Maximum length of an individual substring. */ + size_t maximum_length; +}; + +/* Update *COUNTS according to the contents of HWCAPS. Skip over + entries whose bit is not set in MASK. */ +static void +update_hwcaps_counts (struct hwcaps_counts *counts, const char *hwcaps, + uint32_t bitmask, const char *mask) +{ + struct dl_hwcaps_split_masked sp; + _dl_hwcaps_split_masked_init (&sp, hwcaps, bitmask, mask); + while (_dl_hwcaps_split_masked (&sp)) + { + ++counts->count; + counts->total_length += sp.split.length; + if (sp.split.length > counts->maximum_length) + counts->maximum_length = sp.split.length; + } +} + +/* State for copy_hwcaps. Must be initialized to point to + the storage areas for the array and the strings themselves. */ +struct copy_hwcaps +{ + struct r_strlenpair *next_pair; + char *next_string; +}; + +/* Copy HWCAPS into the string pairs and strings, advancing *TARGET. + Skip over entries whose bit is not set in MASK. */ +static void +copy_hwcaps (struct copy_hwcaps *target, const char *hwcaps, + uint32_t bitmask, const char *mask) +{ + struct dl_hwcaps_split_masked sp; + _dl_hwcaps_split_masked_init (&sp, hwcaps, bitmask, mask); + while (_dl_hwcaps_split_masked (&sp)) + { + target->next_pair->str = target->next_string; + char *slash = __mempcpy (__mempcpy (target->next_string, + GLIBC_HWCAPS_PREFIX, + strlen (GLIBC_HWCAPS_PREFIX)), + sp.split.segment, sp.split.length); + *slash = '/'; + target->next_pair->len + = strlen (GLIBC_HWCAPS_PREFIX) + sp.split.length + 1; + ++target->next_pair; + target->next_string = slash + 1; + } +} + /* Return an array of useful/necessary hardware capability names. */ const struct r_strlenpair * -_dl_important_hwcaps (size_t *sz, size_t *max_capstrlen) +_dl_important_hwcaps (const char *glibc_hwcaps_prepend, + const char *glibc_hwcaps_mask, + size_t *sz, size_t *max_capstrlen) { uint64_t hwcap_mask = GET_HWCAP_MASK(); /* Determine how many important bits are set. */ uint64_t masked = GLRO(dl_hwcap) & hwcap_mask; size_t cnt = GLRO (dl_platform) != NULL; size_t n, m; - size_t total; struct r_strlenpair *result; struct r_strlenpair *rp; char *cp; + /* glibc-hwcaps subdirectories. These are exempted from the power + set construction below. */ + uint32_t hwcaps_subdirs_active = _dl_hwcaps_subdirs_active (); + struct hwcaps_counts hwcaps_counts = { 0, }; + update_hwcaps_counts (&hwcaps_counts, glibc_hwcaps_prepend, -1, NULL); + update_hwcaps_counts (&hwcaps_counts, _dl_hwcaps_subdirs, + hwcaps_subdirs_active, glibc_hwcaps_mask); + + /* Each hwcaps subdirectory has a GLIBC_HWCAPS_PREFIX string prefix + and a "/" suffix once stored in the result. */ + size_t total = (hwcaps_counts.count * (strlen (GLIBC_HWCAPS_PREFIX) + 1) + + hwcaps_counts.total_length); + /* Count the number of bits set in the masked value. */ for (n = 0; (~((1ULL << n) - 1) & masked) != 0; ++n) if ((masked & (1ULL << n)) != 0) @@ -74,10 +151,10 @@ _dl_important_hwcaps (size_t *sz, size_t *max_capstrlen) /* Determine the total size of all strings together. */ if (cnt == 1) - total = temp[0].len + 1; + total += temp[0].len + 1; else { - total = temp[0].len + temp[cnt - 1].len + 2; + total += temp[0].len + temp[cnt - 1].len + 2; if (cnt > 2) { total <<= 1; @@ -94,26 +171,48 @@ _dl_important_hwcaps (size_t *sz, size_t *max_capstrlen) } } - /* The result structure: we use a very compressed way to store the - various combinations of capability names. */ - *sz = 1 << cnt; - result = (struct r_strlenpair *) malloc (*sz * sizeof (*result) + total); - if (result == NULL) + *sz = hwcaps_counts.count + (1 << cnt); + + /* This is the overall result, including both glibc-hwcaps + subdirectories and the legacy hwcaps subdirectories using the + power set construction. */ + struct r_strlenpair *overall_result + = malloc (*sz * sizeof (*result) + total); + if (overall_result == NULL) _dl_signal_error (ENOMEM, NULL, NULL, N_("cannot create capability list")); + /* Fill in the glibc-hwcaps subdirectories. */ + { + struct copy_hwcaps target; + target.next_pair = overall_result; + target.next_string = (char *) (overall_result + *sz); + copy_hwcaps (&target, glibc_hwcaps_prepend, -1, NULL); + copy_hwcaps (&target, _dl_hwcaps_subdirs, + hwcaps_subdirs_active, glibc_hwcaps_mask); + /* Set up the write target for the power set construction. */ + result = target.next_pair; + cp = target.next_string; + } + + + /* Power set construction begins here. We use a very compressed way + to store the various combinations of capability names. */ + if (cnt == 1) { - result[0].str = (char *) (result + *sz); + result[0].str = cp; result[0].len = temp[0].len + 1; - result[1].str = (char *) (result + *sz); + result[1].str = cp; result[1].len = 0; - cp = __mempcpy ((char *) (result + *sz), temp[0].str, temp[0].len); + cp = __mempcpy (cp, temp[0].str, temp[0].len); *cp = '/'; - *sz = 2; - *max_capstrlen = result[0].len; + if (result[0].len > hwcaps_counts.maximum_length) + *max_capstrlen = result[0].len; + else + *max_capstrlen = hwcaps_counts.maximum_length; - return result; + return overall_result; } /* Fill in the information. This follows the following scheme @@ -124,7 +223,7 @@ _dl_important_hwcaps (size_t *sz, size_t *max_capstrlen) #3: 0, 3 1001 This allows the representation of all possible combinations of capability names in the string. First generate the strings. */ - result[1].str = result[0].str = cp = (char *) (result + *sz); + result[1].str = result[0].str = cp; #define add(idx) \ cp = __mempcpy (__mempcpy (cp, temp[idx].str, temp[idx].len), "/", 1); if (cnt == 2) @@ -191,7 +290,10 @@ _dl_important_hwcaps (size_t *sz, size_t *max_capstrlen) while (--n != 0); /* The maximum string length. */ - *max_capstrlen = result[0].len; + if (result[0].len > hwcaps_counts.maximum_length) + *max_capstrlen = result[0].len; + else + *max_capstrlen = hwcaps_counts.maximum_length; - return result; + return overall_result; } diff --git a/elf/dl-hwcaps.h b/elf/dl-hwcaps.h index b66da59b89..ab39d8a46d 100644 --- a/elf/dl-hwcaps.h +++ b/elf/dl-hwcaps.h @@ -16,6 +16,11 @@ License along with the GNU C Library; if not, see . */ +#ifndef _DL_HWCAPS_H +#define _DL_HWCAPS_H + +#include + #include #if HAVE_TUNABLES @@ -28,3 +33,103 @@ # define GET_HWCAP_MASK() (0) # endif #endif + +#define GLIBC_HWCAPS_SUBDIRECTORY "glibc-hwcaps" +#define GLIBC_HWCAPS_PREFIX GLIBC_HWCAPS_SUBDIRECTORY "/" + +/* Used by _dl_hwcaps_split below, to split strings at ':' + separators. */ +struct dl_hwcaps_split +{ + const char *segment; /* Start of the current segment. */ + size_t length; /* Number of bytes until ':' or NUL. */ +}; + +/* Prepare *S to parse SUBJECT, for future _dl_hwcaps_split calls. If + SUBJECT is NULL, it is treated as the empty string. */ +static inline void +_dl_hwcaps_split_init (struct dl_hwcaps_split *s, const char *subject) +{ + s->segment = subject; + /* The initial call to _dl_hwcaps_split will not skip anything. */ + s->length = 0; +} + +/* Extract the next non-empty string segment, up to ':' or the null + terminator. Return true if one more segment was found, or false if + the end of the string was reached. On success, S->segment is the + start of the segment found, and S->length is its length. + (Typically, S->segment[S->length] is not null.) */ +_Bool _dl_hwcaps_split (struct dl_hwcaps_split *s) attribute_hidden; + +/* Similar to dl_hwcaps_split, but with bit-based and name-based + masking. */ +struct dl_hwcaps_split_masked +{ + struct dl_hwcaps_split split; + + /* For used by the iterator implementation. */ + const char *mask; + uint32_t bitmask; +}; + +/* Prepare *S for iteration with _dl_hwcaps_split_masked. Only HWCAP + names in SUBJECT whose bit is set in BITMASK and whose name is in + MASK will be returned. SUBJECT must not contain empty HWCAP names. + If MASK is NULL, no name-based masking is applied. Likewise for + BITMASK if BITMASK is -1 (infinite number of bits). */ +static inline void +_dl_hwcaps_split_masked_init (struct dl_hwcaps_split_masked *s, + const char *subject, + uint32_t bitmask, const char *mask) +{ + _dl_hwcaps_split_init (&s->split, subject); + s->bitmask = bitmask; + s->mask = mask; +} + +/* Like _dl_hwcaps_split, but apply masking. */ +_Bool _dl_hwcaps_split_masked (struct dl_hwcaps_split_masked *s) + attribute_hidden; + +/* Returns true if the colon-separated HWCAP list HWCAPS contains the + capability NAME (with length NAME_LENGTH). If HWCAPS is NULL, the + function returns true. */ +_Bool _dl_hwcaps_contains (const char *hwcaps, const char *name, + size_t name_length) attribute_hidden; + +/* Colon-separated string of glibc-hwcaps subdirectories, without the + "glibc-hwcaps/" prefix. The most preferred subdirectory needs to + be listed first. Up to 32 subdirectories are supported, limited by + the width of the uint32_t mask. */ +extern const char _dl_hwcaps_subdirs[] attribute_hidden; + +/* Returns a bitmap of active subdirectories in _dl_hwcaps_subdirs. + Bit 0 (the LSB) corresponds to the first substring in + _dl_hwcaps_subdirs, bit 1 to the second substring, and so on. + There is no direct correspondence between HWCAP bitmasks and this + bitmask. */ +uint32_t _dl_hwcaps_subdirs_active (void) attribute_hidden; + +/* Returns a bitmask that marks the last ACTIVE subdirectories in a + _dl_hwcaps_subdirs_active string (containing SUBDIRS directories in + total) as active. Intended for use in _dl_hwcaps_subdirs_active + implementations (if a contiguous tail of the list in + _dl_hwcaps_subdirs is selected). */ +static inline uint32_t +_dl_hwcaps_subdirs_build_bitmask (int subdirs, int active) +{ + /* Leading subdirectories that are not active. */ + int inactive = subdirs - active; + if (inactive == 32) + return 0; + + uint32_t mask; + if (subdirs != 32) + mask = (1U << subdirs) - 1; + else + mask = -1; + return mask ^ ((1U << inactive) - 1); +} + +#endif /* _DL_HWCAPS_H */ diff --git a/elf/dl-hwcaps_split.c b/elf/dl-hwcaps_split.c new file mode 100644 index 0000000000..95225e9f40 --- /dev/null +++ b/elf/dl-hwcaps_split.c @@ -0,0 +1,77 @@ +/* Hardware capability support for run-time dynamic loader. String splitting. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include + +_Bool +_dl_hwcaps_split (struct dl_hwcaps_split *s) +{ + if (s->segment == NULL) + return false; + + /* Skip over the previous segment. */ + s->segment += s->length; + + /* Consume delimiters. This also avoids returning an empty + segment. */ + while (*s->segment == ':') + ++s->segment; + if (*s->segment == '\0') + return false; + + /* This could use strchrnul, but we would have to link the function + into ld.so for that. */ + const char *colon = strchr (s->segment, ':'); + if (colon == NULL) + s->length = strlen (s->segment); + else + s->length = colon - s->segment; + return true; +} + +_Bool +_dl_hwcaps_split_masked (struct dl_hwcaps_split_masked *s) +{ + while (true) + { + if (!_dl_hwcaps_split (&s->split)) + return false; + bool active = s->bitmask & 1; + s->bitmask >>= 1; + if (active && _dl_hwcaps_contains (s->mask, + s->split.segment, s->split.length)) + return true; + } +} + +_Bool +_dl_hwcaps_contains (const char *hwcaps, const char *name, size_t name_length) +{ + if (hwcaps == NULL) + return true; + + struct dl_hwcaps_split split; + _dl_hwcaps_split_init (&split, hwcaps); + while (_dl_hwcaps_split (&split)) + if (split.length == name_length + && memcmp (split.segment, name, name_length) == 0) + return true; + return false; +} diff --git a/elf/dl-load.c b/elf/dl-load.c index f3201e7c14..9020f1646f 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -682,7 +682,9 @@ cache_rpath (struct link_map *l, void -_dl_init_paths (const char *llp, const char *source) +_dl_init_paths (const char *llp, const char *source, + const char *glibc_hwcaps_prepend, + const char *glibc_hwcaps_mask) { size_t idx; const char *strp; @@ -697,7 +699,8 @@ _dl_init_paths (const char *llp, const char *source) #ifdef SHARED /* Get the capabilities. */ - capstr = _dl_important_hwcaps (&ncapstr, &max_capstrlen); + capstr = _dl_important_hwcaps (glibc_hwcaps_prepend, glibc_hwcaps_mask, + &ncapstr, &max_capstrlen); #endif /* First set up the rest of the default search directory entries. */ diff --git a/elf/dl-main.h b/elf/dl-main.h index b51256d3b4..566713a0d1 100644 --- a/elf/dl-main.h +++ b/elf/dl-main.h @@ -84,6 +84,14 @@ struct dl_main_state /* The preload list passed as a command argument. */ const char *preloadarg; + /* Additional glibc-hwcaps subdirectories to search first. + Colon-separated list. */ + const char *glibc_hwcaps_prepend; + + /* Mask for the internal glibc-hwcaps subdirectories. + Colon-separated list. */ + const char *glibc_hwcaps_mask; + enum rtld_mode mode; /* True if any of the debugging options is enabled. */ @@ -98,7 +106,8 @@ struct dl_main_state static inline void call_init_paths (const struct dl_main_state *state) { - _dl_init_paths (state->library_path, state->library_path_source); + _dl_init_paths (state->library_path, state->library_path_source, + state->glibc_hwcaps_prepend, state->glibc_hwcaps_mask); } /* Print ld.so usage information and exit. */ diff --git a/elf/dl-support.c b/elf/dl-support.c index afbc94df54..3264262f4e 100644 --- a/elf/dl-support.c +++ b/elf/dl-support.c @@ -323,7 +323,10 @@ _dl_non_dynamic_init (void) /* Initialize the data structures for the search paths for shared objects. */ - _dl_init_paths (getenv ("LD_LIBRARY_PATH"), "LD_LIBRARY_PATH"); + _dl_init_paths (getenv ("LD_LIBRARY_PATH"), "LD_LIBRARY_PATH", + /* No glibc-hwcaps selection support in statically + linked binaries. */ + NULL, NULL); /* Remember the last search directory added at startup. */ _dl_init_all_dirs = GL(dl_all_dirs); diff --git a/elf/dl-usage.c b/elf/dl-usage.c index 796ad38b43..e22a9c3942 100644 --- a/elf/dl-usage.c +++ b/elf/dl-usage.c @@ -83,7 +83,7 @@ print_search_path_for_help (struct dl_main_state *state) { if (__rtld_search_dirs.dirs == NULL) /* The run-time search paths have not yet been initialized. */ - _dl_init_paths (state->library_path, state->library_path_source); + call_init_paths (state); _dl_printf ("\nShared library search path:\n"); @@ -132,6 +132,67 @@ print_hwcap_1_finish (bool *first) _dl_printf (")\n"); } +/* Print the header for print_hwcaps_subdirectories. */ +static void +print_hwcaps_subdirectories_header (bool *nothing_printed) +{ + if (*nothing_printed) + { + _dl_printf ("\n\ +Subdirectories of glibc-hwcaps directories, in priority order:\n"); + *nothing_printed = false; + } +} + +/* Print the HWCAP name itself, indented. */ +static void +print_hwcaps_subdirectories_name (const struct dl_hwcaps_split *split) +{ + _dl_write (STDOUT_FILENO, " ", 2); + _dl_write (STDOUT_FILENO, split->segment, split->length); +} + +/* Print the list of recognized glibc-hwcaps subdirectories. */ +static void +print_hwcaps_subdirectories (const struct dl_main_state *state) +{ + bool nothing_printed = true; + struct dl_hwcaps_split split; + + /* The prepended glibc-hwcaps subdirectories. */ + _dl_hwcaps_split_init (&split, state->glibc_hwcaps_prepend); + while (_dl_hwcaps_split (&split)) + { + print_hwcaps_subdirectories_header (¬hing_printed); + print_hwcaps_subdirectories_name (&split); + bool first = true; + print_hwcap_1 (&first, true, "searched"); + print_hwcap_1_finish (&first); + } + + /* The built-in glibc-hwcaps subdirectories. Do the filtering + manually, so that more precise diagnostics are possible. */ + uint32_t mask = _dl_hwcaps_subdirs_active (); + _dl_hwcaps_split_init (&split, _dl_hwcaps_subdirs); + while (_dl_hwcaps_split (&split)) + { + print_hwcaps_subdirectories_header (¬hing_printed); + print_hwcaps_subdirectories_name (&split); + bool first = true; + print_hwcap_1 (&first, mask & 1, "supported"); + bool listed = _dl_hwcaps_contains (state->glibc_hwcaps_mask, + split.segment, split.length); + print_hwcap_1 (&first, !listed, "masked"); + print_hwcap_1 (&first, (mask & 1) && listed, "searched"); + print_hwcap_1_finish (&first); + mask >>= 1; + } + + if (nothing_printed) + _dl_printf ("\n\ +No subdirectories of glibc-hwcaps directories are searched.\n"); +} + /* Write a list of hwcap subdirectories to standard output. See _dl_important_hwcaps in dl-hwcaps.c. */ static void @@ -186,6 +247,10 @@ setting environment variables (which would be inherited by subprocesses).\n\ --inhibit-cache Do not use " LD_SO_CACHE "\n\ --library-path PATH use given PATH instead of content of the environment\n\ variable LD_LIBRARY_PATH\n\ + --glibc-hwcaps-prepend LIST\n\ + search glibc-hwcaps subdirectories in LIST\n\ + --glibc-hwcaps-mask LIST\n\ + only search built-in subdirectories if in LIST\n\ --inhibit-rpath LIST ignore RUNPATH and RPATH information in object names\n\ in LIST\n\ --audit LIST use objects named in LIST as auditors\n\ @@ -198,6 +263,7 @@ This program interpreter self-identifies as: " RTLD "\n\ ", argv0); print_search_path_for_help (state); + print_hwcaps_subdirectories (state); print_legacy_hwcap_directories (); _exit (EXIT_SUCCESS); } diff --git a/elf/markermodMARKER-VALUE.c b/elf/markermodMARKER-VALUE.c new file mode 100644 index 0000000000..99bdcf71a4 --- /dev/null +++ b/elf/markermodMARKER-VALUE.c @@ -0,0 +1,29 @@ +/* Source file template for building shared objects with marker functions. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* MARKER and VALUE must be set on the compiler command line. */ + +#ifndef MARKER +# error MARKER not defined +#endif + +int +MARKER (void) +{ + return VALUE; +} diff --git a/elf/rtld.c b/elf/rtld.c index 5d117d0d2c..e3ec66c030 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -289,6 +289,8 @@ dl_main_state_init (struct dl_main_state *state) state->library_path_source = NULL; state->preloadlist = NULL; state->preloadarg = NULL; + state->glibc_hwcaps_prepend = NULL; + state->glibc_hwcaps_mask = NULL; state->mode = rtld_mode_normal; state->any_debug = false; state->version_info = false; @@ -1244,6 +1246,22 @@ dl_main (const ElfW(Phdr) *phdr, { argv0 = _dl_argv[2]; + _dl_skip_args += 2; + _dl_argc -= 2; + _dl_argv += 2; + } + else if (strcmp (_dl_argv[1], "--glibc-hwcaps-prepend") == 0 + && _dl_argc > 2) + { + state.glibc_hwcaps_prepend = _dl_argv[2]; + _dl_skip_args += 2; + _dl_argc -= 2; + _dl_argv += 2; + } + else if (strcmp (_dl_argv[1], "--glibc-hwcaps-mask") == 0 + && _dl_argc > 2) + { + state.glibc_hwcaps_mask = _dl_argv[2]; _dl_skip_args += 2; _dl_argc -= 2; _dl_argv += 2; diff --git a/elf/tst-dl-hwcaps_split.c b/elf/tst-dl-hwcaps_split.c new file mode 100644 index 0000000000..929c99a23b --- /dev/null +++ b/elf/tst-dl-hwcaps_split.c @@ -0,0 +1,139 @@ +/* Unit tests for dl-hwcaps.c. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include + +static void +check_split_masked (const char *input, int32_t bitmask, const char *mask, + const char *expected[], size_t expected_length) +{ + struct dl_hwcaps_split_masked split; + _dl_hwcaps_split_masked_init (&split, input, bitmask, mask); + size_t index = 0; + while (_dl_hwcaps_split_masked (&split)) + { + TEST_VERIFY_EXIT (index < expected_length); + TEST_COMPARE_BLOB (expected[index], strlen (expected[index]), + split.split.segment, split.split.length); + ++index; + } + TEST_COMPARE (index, expected_length); +} + +static void +check_split (const char *input, + const char *expected[], size_t expected_length) +{ + struct dl_hwcaps_split split; + _dl_hwcaps_split_init (&split, input); + size_t index = 0; + while (_dl_hwcaps_split (&split)) + { + TEST_VERIFY_EXIT (index < expected_length); + TEST_COMPARE_BLOB (expected[index], strlen (expected[index]), + split.segment, split.length); + ++index; + } + TEST_COMPARE (index, expected_length); + + /* Reuse the test cases with masking that does not actually remove + anything. */ + check_split_masked (input, -1, NULL, expected, expected_length); + check_split_masked (input, -1, input, expected, expected_length); +} + +static int +do_test (void) +{ + /* Splitting tests, without masking. */ + check_split (NULL, NULL, 0); + check_split ("", NULL, 0); + check_split (":", NULL, 0); + check_split ("::", NULL, 0); + + { + const char *expected[] = { "first" }; + check_split ("first", expected, array_length (expected)); + check_split (":first", expected, array_length (expected)); + check_split ("first:", expected, array_length (expected)); + check_split (":first:", expected, array_length (expected)); + } + + { + const char *expected[] = { "first", "second" }; + check_split ("first:second", expected, array_length (expected)); + check_split ("first::second", expected, array_length (expected)); + check_split (":first:second", expected, array_length (expected)); + check_split ("first:second:", expected, array_length (expected)); + check_split (":first:second:", expected, array_length (expected)); + } + + /* Splitting tests with masking. */ + { + const char *expected[] = { "first" }; + check_split_masked ("first", 3, "first:second", + expected, array_length (expected)); + check_split_masked ("first:second", 3, "first:", + expected, array_length (expected)); + check_split_masked ("first:second", 1, NULL, + expected, array_length (expected)); + } + { + const char *expected[] = { "second" }; + check_split_masked ("first:second", 3, "second", + expected, array_length (expected)); + check_split_masked ("first:second:third", -1, "second:", + expected, array_length (expected)); + check_split_masked ("first:second", 2, NULL, + expected, array_length (expected)); + check_split_masked ("first:second:third", 2, "first:second", + expected, array_length (expected)); + } + + /* Tests for _dl_hwcaps_contains. */ + TEST_VERIFY (_dl_hwcaps_contains (NULL, "first", strlen ("first"))); + TEST_VERIFY (_dl_hwcaps_contains (NULL, "", 0)); + TEST_VERIFY (! _dl_hwcaps_contains ("", "first", strlen ("first"))); + TEST_VERIFY (! _dl_hwcaps_contains ("firs", "first", strlen ("first"))); + TEST_VERIFY (_dl_hwcaps_contains ("firs", "first", strlen ("first") - 1)); + for (int i = 0; i < strlen ("first"); ++i) + TEST_VERIFY (! _dl_hwcaps_contains ("first", "first", i)); + TEST_VERIFY (_dl_hwcaps_contains ("first", "first", strlen ("first"))); + TEST_VERIFY (_dl_hwcaps_contains ("first:", "first", strlen ("first"))); + TEST_VERIFY (_dl_hwcaps_contains ("first:second", + "first", strlen ("first"))); + TEST_VERIFY (_dl_hwcaps_contains (":first:second", "first", + strlen ("first"))); + TEST_VERIFY (_dl_hwcaps_contains ("first:second", "second", + strlen ("second"))); + TEST_VERIFY (_dl_hwcaps_contains ("first:second:", "second", + strlen ("second"))); + for (int i = 0; i < strlen ("second"); ++i) + TEST_VERIFY (!_dl_hwcaps_contains ("first:second:", "sec", i)); + + return 0; +} + +#include + +/* Rebuild the sources here because the object file is built for + inclusion into the dynamic loader. */ +#include "dl-hwcaps_split.c" diff --git a/elf/tst-glibc-hwcaps-mask.c b/elf/tst-glibc-hwcaps-mask.c new file mode 100644 index 0000000000..27b09b358c --- /dev/null +++ b/elf/tst-glibc-hwcaps-mask.c @@ -0,0 +1,31 @@ +/* Test that --glibc-hwcaps-mask works. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +extern int marker1 (void); + +static int +do_test (void) +{ + /* The marker1 function in elf/markermod1.so returns 1. */ + TEST_COMPARE (marker1 (), 1); + return 0; +} + +#include diff --git a/elf/tst-glibc-hwcaps-prepend.c b/elf/tst-glibc-hwcaps-prepend.c new file mode 100644 index 0000000000..57d7319f14 --- /dev/null +++ b/elf/tst-glibc-hwcaps-prepend.c @@ -0,0 +1,32 @@ +/* Test that --glibc-hwcaps-prepend works. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +extern int marker1 (void); + +static int +do_test (void) +{ + /* The marker1 function in + glibc-hwcaps/prepend-markermod1/markermod1.so returns 2. */ + TEST_COMPARE (marker1 (), 2); + return 0; +} + +#include diff --git a/elf/tst-glibc-hwcaps.c b/elf/tst-glibc-hwcaps.c new file mode 100644 index 0000000000..28f47cf891 --- /dev/null +++ b/elf/tst-glibc-hwcaps.c @@ -0,0 +1,28 @@ +/* Stub test for glibc-hwcaps. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +static int +do_test (void) +{ + puts ("info: generic tst-glibc-hwcaps (tests nothing)"); + return 0; +} + +#include diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index 382eeb9be0..0b2babc70c 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -1047,8 +1047,13 @@ extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns) attribute_hidden; /* Initialize the basic data structure for the search paths. SOURCE - is either "LD_LIBRARY_PATH" or "--library-path". */ -extern void _dl_init_paths (const char *library_path, const char *source) + is either "LD_LIBRARY_PATH" or "--library-path". + GLIBC_HWCAPS_PREPEND adds additional glibc-hwcaps subdirectories to + search. GLIBC_HWCAPS_MASK is used to filter the built-in + subdirectories if not NULL. */ +extern void _dl_init_paths (const char *library_path, const char *source, + const char *glibc_hwcaps_prepend, + const char *glibc_hwcaps_mask) attribute_hidden; /* Gather the information needed to install the profiling tables and start @@ -1072,9 +1077,14 @@ extern void _dl_show_auxv (void) attribute_hidden; extern char *_dl_next_ld_env_entry (char ***position) attribute_hidden; /* Return an array with the names of the important hardware - capabilities. The length of the array is written to *SZ, and the - maximum of all strings length is written to *MAX_CAPSTRLEN. */ -const struct r_strlenpair *_dl_important_hwcaps (size_t *sz, + capabilities. PREPEND is a colon-separated list of glibc-hwcaps + directories to search first. MASK is a colon-separated list used + to filter the built-in glibc-hwcaps subdirectories. The length of + the array is written to *SZ, and the maximum of all strings length + is written to *MAX_CAPSTRLEN. */ +const struct r_strlenpair *_dl_important_hwcaps (const char *prepend, + const char *mask, + size_t *sz, size_t *max_capstrlen) attribute_hidden; From patchwork Mon Nov 9 18:40:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 40972 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 489413896C12; Mon, 9 Nov 2020 18:40:52 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 489413896C12 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1604947252; bh=QNn2ckXMcas1R/BtK0lUqBYs925AetaG1eb13Vc7IIE=; 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=BUf0NYPNhvR4PSi9Pq4EFNKQ2VVavoQX671OTYBqiQubkQ8OTveeKoLXdk331i2a+ QkhfzBHUWthDH7U/WTyc1I3DBchuxncf3V3urXu9FARso1OeyonEuM2kmHLYwhDSFZ 7fpR8UUjLX9Z8wh8ri3sIx0Mb8tzk5gAjSDsFAZc= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by sourceware.org (Postfix) with ESMTP id C92ED388CC0E for ; Mon, 9 Nov 2020 18:40:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org C92ED388CC0E 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-199-Jq-6_TCsPbaHJffVdS0eOQ-1; Mon, 09 Nov 2020 13:40:47 -0500 X-MC-Unique: Jq-6_TCsPbaHJffVdS0eOQ-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id A30E4186DD2A for ; Mon, 9 Nov 2020 18:40:46 +0000 (UTC) Received: from oldenburg2.str.redhat.com (ovpn-113-222.ams2.redhat.com [10.36.113.222]) by smtp.corp.redhat.com (Postfix) with ESMTPS id ED0CD10001B3 for ; Mon, 9 Nov 2020 18:40:45 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH 04/11] elf: Add endianness markup to ld.so.cache In-Reply-To: References: Message-Id: Date: Mon, 09 Nov 2020 19:40:44 +0100 User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, 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" Use a reserved byte in the new format cache header to indicate whether the file is in little endian or big endian format. Eventually, this information could be used to provide a unified cache for qemu-user and similiar scenarios. Reviewed-by: Adhemerval Zanella --- elf/cache.c | 11 +++++++ elf/dl-cache.c | 20 +++++++++++- sysdeps/generic/dl-cache.h | 62 +++++++++++++++++++++++++++++++++++++- 3 files changed, 91 insertions(+), 2 deletions(-) diff --git a/elf/cache.c b/elf/cache.c index c241c17ef9..ffecbe6d82 100644 --- a/elf/cache.c +++ b/elf/cache.c @@ -152,6 +152,14 @@ print_entry (const char *lib, int flag, unsigned int osversion, printf (") => %s\n", key); } +/* Print an error and exit if the new-file cache is internally + inconsistent. */ +static void +check_new_cache (struct cache_file_new *cache) +{ + if (! cache_file_new_matches_endian (cache)) + error (EXIT_FAILURE, 0, _("Cache file has wrong endianness.\n")); +} /* Print the whole cache file, if a file contains the new cache format hidden in the old one, print the contents of the new format. */ @@ -193,6 +201,7 @@ print_cache (const char *cache_name) || memcmp (cache_new->version, CACHE_VERSION, sizeof CACHE_VERSION - 1)) error (EXIT_FAILURE, 0, _("File is not a cache file.\n")); + check_new_cache (cache_new); format = 1; /* This is where the strings start. */ cache_data = (const char *) cache_new; @@ -222,6 +231,7 @@ print_cache (const char *cache_name) && memcmp (cache_new->version, CACHE_VERSION, sizeof CACHE_VERSION - 1) == 0) { + check_new_cache (cache_new); cache_data = (const char *) cache_new; format = 1; } @@ -361,6 +371,7 @@ save_cache (const char *cache_name) file_entries_new->nlibs = cache_entry_count; file_entries_new->len_strings = total_strlen; + file_entries_new->flags = cache_file_new_flags_endian_current; } /* Pad for alignment of cache_file_new. */ diff --git a/elf/dl-cache.c b/elf/dl-cache.c index 45894ecd2f..02c46ffb0c 100644 --- a/elf/dl-cache.c +++ b/elf/dl-cache.c @@ -242,6 +242,11 @@ _dl_load_cache_lookup (const char *name) && ((cachesize - sizeof *cache_new) / sizeof (struct file_entry_new) >= ((struct cache_file_new *) file)->nlibs)) { + if (! cache_file_new_matches_endian (file)) + { + __munmap (file, cachesize); + file = (void *) -1; + } cache_new = file; cache = file; } @@ -263,7 +268,20 @@ _dl_load_cache_lookup (const char *name) if (cachesize < (offset + sizeof (struct cache_file_new)) || memcmp (cache_new->magic, CACHEMAGIC_VERSION_NEW, sizeof CACHEMAGIC_VERSION_NEW - 1) != 0) - cache_new = (void *) -1; + cache_new = (void *) -1; + else + { + if (! cache_file_new_matches_endian (cache_new)) + { + /* The old-format part of the cache is bogus as well + if the endianness does not match. (But it is + unclear how the new header can be located if the + endianess does not match.) */ + cache = (void *) -1; + cache_new = (void *) -1; + __munmap (file, cachesize); + } + } } else { diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h index 4ddd96b005..46026e0988 100644 --- a/sysdeps/generic/dl-cache.h +++ b/sysdeps/generic/dl-cache.h @@ -16,6 +16,11 @@ License along with the GNU C Library; if not, see . */ +#ifndef _DL_CACHE_H +#define _DL_CACHE_H + +#include +#include #include #ifndef _DL_CACHE_DEFAULT_ID @@ -92,21 +97,76 @@ struct file_entry_new uint64_t hwcap; /* Hwcap entry. */ }; +/* See flags member of struct cache_file_new below. */ +enum + { + /* No endianness information available. An old ldconfig version + without endianness support wrote the file. */ + cache_file_new_flags_endian_unset = 0, + + /* Cache is invalid and should be ignored. */ + cache_file_new_flags_endian_invalid = 1, + + /* Cache format is little endian. */ + cache_file_new_flags_endian_little = 2, + + /* Cache format is big endian. */ + cache_file_new_flags_endian_big = 3, + + /* Bit mask to extract the cache_file_new_flags_endian_* + values. */ + cache_file_new_flags_endian_mask = 3, + + /* Expected value of the endian bits in the flags member for the + current architecture. */ + cache_file_new_flags_endian_current + = (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + ? cache_file_new_flags_endian_little + : cache_file_new_flags_endian_big), + }; + struct cache_file_new { char magic[sizeof CACHEMAGIC_NEW - 1]; char version[sizeof CACHE_VERSION - 1]; uint32_t nlibs; /* Number of entries. */ uint32_t len_strings; /* Size of string table. */ - uint32_t unused[5]; /* Leave space for future extensions + + /* flags & cache_file_new_flags_endian_mask is one of the values + cache_file_new_flags_endian_unset, cache_file_new_flags_endian_invalid, + cache_file_new_flags_endian_little, cache_file_new_flags_endian_big. + + The remaining bits are unused and should be generated as zero and + ignored by readers. */ + uint8_t flags; + + uint8_t padding_unsed[3]; /* Not used, for future extensions. */ + + uint32_t unused[4]; /* Leave space for future extensions and align to 8 byte boundary. */ struct file_entry_new libs[0]; /* Entries describing libraries. */ /* After this the string table of size len_strings is found. */ }; +/* Returns false if *CACHE has the wrong endianness for this + architecture, and true if the endianness matches (or is + unknown). */ +static inline bool +cache_file_new_matches_endian (const struct cache_file_new *cache) +{ + /* A zero value for cache->flags means that no endianness + information is available. */ + return cache->flags == 0 + || ((cache->flags & cache_file_new_flags_endian_big) + == cache_file_new_flags_endian_current); +} + + /* Used to align cache_file_new. */ #define ALIGN_CACHE(addr) \ (((addr) + __alignof__ (struct cache_file_new) -1) \ & (~(__alignof__ (struct cache_file_new) - 1))) extern int _dl_cache_libcmp (const char *p1, const char *p2) attribute_hidden; + +#endif /* _DL_CACHE_H */ From patchwork Mon Nov 9 18:40:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 40973 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 CE2603896C10; Mon, 9 Nov 2020 18:40:57 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org CE2603896C10 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1604947257; bh=kOIM/m6n9ypRw8kYlySjChIKINrkxddkMGM5GHqM0uQ=; 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=L01cRyQ09gRvlSP6f5sQDdrMoA+6DP24NlF/MjBk4gldNx0OTLbWU5ewrUX0cqqtQ Z0dVCvUKKQYFGiFKpN6DTWJbUXCWPf29AtuuwsXdPph28Nv+ncOwr1KEdgTpVotNUP tu6IhcLzktz9I3mgkSFaj1g1T1VfXntR0BQGFZpg= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [63.128.21.124]) by sourceware.org (Postfix) with ESMTP id 3B5FE388CC1C for ; Mon, 9 Nov 2020 18:40:55 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 3B5FE388CC1C 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-360-cJ1DUL2pM-66zaEkWzWvmA-1; Mon, 09 Nov 2020 13:40:53 -0500 X-MC-Unique: cJ1DUL2pM-66zaEkWzWvmA-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 7E3131084D65 for ; Mon, 9 Nov 2020 18:40:52 +0000 (UTC) Received: from oldenburg2.str.redhat.com (ovpn-113-222.ams2.redhat.com [10.36.113.222]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 8EDB95C5B7 for ; Mon, 9 Nov 2020 18:40:51 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH 05/11] elf: Add extension mechanism to ld.so.cache In-Reply-To: References: Message-Id: Date: Mon, 09 Nov 2020 19:40:49 +0100 User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H5, 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" A previously unused new-format header field is used to record the address of an extension directory. This change adds a demo extension which records the version of ldconfig which builds a file. Reviewed-by: Adhemerval Zanella --- elf/cache.c | 89 +++++++++++++++++++++++++++ sysdeps/generic/dl-cache.h | 123 ++++++++++++++++++++++++++++++++++++- 2 files changed, 211 insertions(+), 1 deletion(-) diff --git a/elf/cache.c b/elf/cache.c index ffecbe6d82..549e04ce21 100644 --- a/elf/cache.c +++ b/elf/cache.c @@ -15,6 +15,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, see . */ +#include #include #include #include @@ -33,6 +34,7 @@ #include #include +#include struct cache_entry { @@ -161,6 +163,21 @@ check_new_cache (struct cache_file_new *cache) error (EXIT_FAILURE, 0, _("Cache file has wrong endianness.\n")); } +/* Print the extension information at the cache at start address + FILE_BASE, of length FILE_SIZE bytes. The new-format cache header + is at CACHE, and the file name for diagnostics is CACHE_NAME. */ +static void +print_extensions (struct cache_extension_all_loaded *ext) +{ + if (ext->sections[cache_extension_tag_generator].base != NULL) + { + fputs (_("Cache generated by: "), stdout); + fwrite (ext->sections[cache_extension_tag_generator].base, 1, + ext->sections[cache_extension_tag_generator].size, stdout); + putchar ('\n'); + } +} + /* Print the whole cache file, if a file contains the new cache format hidden in the old one, print the contents of the new format. */ void @@ -250,6 +267,11 @@ print_cache (const char *cache_name) } else if (format == 1) { + struct cache_extension_all_loaded ext; + if (!cache_extension_load (cache_new, cache, cache_size, &ext)) + error (EXIT_FAILURE, 0, + _("Malformed extension data in cache file %s\n"), cache_name); + printf (_("%d libs found in cache `%s'\n"), cache_new->nlibs, cache_name); @@ -260,6 +282,7 @@ print_cache (const char *cache_name) cache_new->libs[i].osversion, cache_new->libs[i].hwcap, cache_data + cache_new->libs[i].value); + print_extensions (&ext); } /* Cleanup. */ munmap (cache, cache_size); @@ -301,6 +324,45 @@ compare (const struct cache_entry *e1, const struct cache_entry *e2) return res; } +/* Size of the cache extension directory. All tags are assumed to be + present. */ +enum + { + cache_extension_size = (offsetof (struct cache_extension, sections) + + (cache_extension_count + * sizeof (struct cache_extension_section))) + }; + +/* Write the cache extensions to FD. The extension directory is + assumed to be located at CACHE_EXTENSION_OFFSET. */ +static void +write_extensions (int fd, uint32_t cache_extension_offset) +{ + assert ((cache_extension_offset % 4) == 0); + + struct cache_extension *ext = xmalloc (cache_extension_size); + ext->magic = cache_extension_magic; + ext->count = cache_extension_count; + + for (int i = 0; i < cache_extension_count; ++i) + { + ext->sections[i].tag = i; + ext->sections[i].flags = 0; + } + + const char *generator + = "ldconfig " PKGVERSION RELEASE " release version " VERSION; + ext->sections[cache_extension_tag_generator].offset + = cache_extension_offset + cache_extension_size; + ext->sections[cache_extension_tag_generator].size = strlen (generator); + + if (write (fd, ext, cache_extension_size) != cache_extension_size + || write (fd, generator, strlen (generator)) != strlen (generator)) + error (EXIT_FAILURE, errno, _("Writing of cache extension data failed")); + + free (ext); +} + /* Save the contents of the cache. */ void save_cache (const char *cache_name) @@ -435,6 +497,25 @@ save_cache (const char *cache_name) && idx_old < cache_entry_old_count) file_entries->libs[idx_old] = file_entries->libs[idx_old - 1]; + /* Compute the location of the extension directory. This + implementation puts the directory after the string table. The + size computation matches the write calls below. The extension + directory does not exist with format 0, so the value does not + matter. */ + uint32_t extension_offset = 0; + if (opt_format != opt_format_new) + extension_offset += file_entries_size; + if (opt_format != opt_format_old) + { + if (opt_format != opt_format_new) + extension_offset += pad; + extension_offset += file_entries_new_size; + } + extension_offset += total_strlen; + extension_offset = roundup (extension_offset, 4); /* Provide alignment. */ + if (opt_format != opt_format_old) + file_entries_new->extension_offset = extension_offset; + /* Write out the cache. */ /* Write cache first to a temporary file and rename it later. */ @@ -473,6 +554,14 @@ save_cache (const char *cache_name) if (write (fd, strings, total_strlen) != (ssize_t) total_strlen) error (EXIT_FAILURE, errno, _("Writing of cache data failed")); + if (opt_format != opt_format_old) + { + /* Align file position to 4. */ + off64_t old_offset = lseek64 (fd, extension_offset, SEEK_SET); + assert ((unsigned long long int) (extension_offset - old_offset) < 4); + write_extensions (fd, extension_offset); + } + /* Make sure user can always read cache file */ if (chmod (temp_name, S_IROTH|S_IRGRP|S_IRUSR|S_IWUSR)) error (EXIT_FAILURE, errno, diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h index 46026e0988..cdc24c8009 100644 --- a/sysdeps/generic/dl-cache.h +++ b/sysdeps/generic/dl-cache.h @@ -21,7 +21,9 @@ #include #include +#include #include +#include #ifndef _DL_CACHE_DEFAULT_ID # define _DL_CACHE_DEFAULT_ID 3 @@ -142,7 +144,11 @@ struct cache_file_new uint8_t padding_unsed[3]; /* Not used, for future extensions. */ - uint32_t unused[4]; /* Leave space for future extensions + /* File offset of the extension directory. See struct + cache_extension below. Must be a multiple of four. */ + uint32_t extension_offset; + + uint32_t unused[3]; /* Leave space for future extensions and align to 8 byte boundary. */ struct file_entry_new libs[0]; /* Entries describing libraries. */ /* After this the string table of size len_strings is found. */ @@ -162,6 +168,121 @@ cache_file_new_matches_endian (const struct cache_file_new *cache) } +/* Randomly chosen magic value, which allows for additional + consistency verification. */ +enum { cache_extension_magic = (uint32_t) -358342284 }; + +/* Tag values for different kinds of extension sections. Similar to + SHT_* constants. */ +enum cache_extension_tag + { + /* Array of bytes containing the glibc version that generated this + cache file. */ + cache_extension_tag_generator, + + /* Total number of known cache extension tags. */ + cache_extension_count + }; + +/* Element in the array following struct cache_extension. Similar to + an ELF section header. */ +struct cache_extension_section +{ + /* Type of the extension section. A enum cache_extension_tag value. */ + uint32_t tag; + + /* Extension-specific flags. Currently generated as zero. */ + uint32_t flags; + + /* Offset from the start of the file for the data in this extension + section. Specific extensions can have alignment constraints. */ + uint32_t offset; + + /* Length in bytes of the extension data. Specific extensions may + have size requirements. */ + uint32_t size; +}; + +/* The extension directory in the cache. An array of struct + cache_extension_section entries. */ +struct cache_extension +{ + uint32_t magic; /* Always cache_extension_magic. */ + uint32_t count; /* Number of following entries. */ + + /* count section descriptors of type struct cache_extension_section + follow. */ + struct cache_extension_section sections[]; +}; + +/* A relocated version of struct cache_extension_section. */ +struct cache_extension_loaded +{ + /* Address and size of this extension section. base is NULL if the + section is missing from the file. */ + const void *base; + size_t size; + + /* Flags from struct cache_extension_section. */ + uint32_t flags; +}; + +/* All supported extension sections, relocated. Filled in by + cache_extension_load below. */ +struct cache_extension_all_loaded +{ + struct cache_extension_loaded sections[cache_extension_count]; +}; + +static bool __attribute__ ((unused)) +cache_extension_load (const struct cache_file_new *cache, + const void *file_base, size_t file_size, + struct cache_extension_all_loaded *loaded) +{ + memset (loaded, 0, sizeof (*loaded)); + if (cache->extension_offset == 0) + /* No extensions present. This is not a format error. */ + return true; + if ((cache->extension_offset % 4) != 0) + /* Extension offset is misaligned. */ + return false; + size_t size_tmp; + if (__builtin_add_overflow (cache->extension_offset, + sizeof (struct cache_extension), &size_tmp) + || size_tmp > file_size) + /* Extension extends beyond the end of the file. */ + return false; + const struct cache_extension *ext = file_base + cache->extension_offset; + if (ext->magic != cache_extension_magic) + return false; + if (__builtin_mul_overflow (ext->count, + sizeof (struct cache_extension_section), + &size_tmp) + || __builtin_add_overflow (cache->extension_offset + + sizeof (struct cache_extension), size_tmp, + &size_tmp) + || size_tmp > file_size) + /* Extension array extends beyond the end of the file. */ + return false; + for (uint32_t i = 0; i < ext->count; ++i) + { + if (__builtin_add_overflow (ext->sections[i].offset, + ext->sections[i].size, &size_tmp) + || size_tmp > file_size) + /* Extension data extends beyond the end of the file. */ + return false; + + uint32_t tag = ext->sections[i].tag; + if (tag >= cache_extension_count) + /* Tag is out of range and unrecognized. */ + continue; + loaded->sections[tag].base = file_base + ext->sections[i].offset; + loaded->sections[tag].size = ext->sections[i].size; + loaded->sections[tag].flags = ext->sections[i].flags; + } + return true; +} + /* Used to align cache_file_new. */ #define ALIGN_CACHE(addr) \ (((addr) + __alignof__ (struct cache_file_new) -1) \ From patchwork Mon Nov 9 18:40:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 40974 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 619FD3896C0C; Mon, 9 Nov 2020 18:41:05 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 619FD3896C0C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1604947265; bh=3sA3cZ7wyNTlGA5t7zBa8riCxjbilOW7KJo4WYEz3P0=; 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=IRswm/ee4M+ecsGQcqDBRv0NPy+I6YFtVtwGGd9VpHss2K1A3IVfkXNKzBnJnu4ra 3V58aV1aJmOMcMmoTJv9c54jBSH2Xo3ZDDkMIF/Q2esh9VNYkmca99HnhL5Y2jegwX TMY68Q5PsuC2PqArCyiK2rGX1z9H43dhDZ2u/2/A= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [63.128.21.124]) by sourceware.org (Postfix) with ESMTP id 8274C388CC1C for ; Mon, 9 Nov 2020 18:41:01 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 8274C388CC1C 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-536-CPpVIZjdOEemH5MYnC5jiw-1; Mon, 09 Nov 2020 13:40:59 -0500 X-MC-Unique: CPpVIZjdOEemH5MYnC5jiw-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 89D7D1009E22 for ; Mon, 9 Nov 2020 18:40:58 +0000 (UTC) Received: from oldenburg2.str.redhat.com (ovpn-113-222.ams2.redhat.com [10.36.113.222]) by smtp.corp.redhat.com (Postfix) with ESMTPS id A3B4D60BF1 for ; Mon, 9 Nov 2020 18:40:57 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH 06/11] elf: Implement a string table for ldconfig, with tail merging In-Reply-To: References: Message-Id: Date: Mon, 09 Nov 2020 19:40:55 +0100 User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H5, 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" This will be used in ldconfig to reduce the ld.so.cache size slightly. Tail merging is an optimization where a pointer points into another string if the first string is a suffix of the second string. The hash function FNV-1a was chosen because it is simple and achieves good dispersion even for short strings (so that the hash table bucket count can be a power of two). It is clearly superior to the hsearch hash and the ELF hash in this regard. The hash table uses chaining for collision resolution. Reviewed-by: Adhemerval Zanella --- elf/Makefile | 2 +- elf/stringtable.c | 209 +++++++++++++++++++++++++++++++++++++++++ elf/stringtable.h | 64 +++++++++++++ elf/stringtable_free.c | 33 +++++++ elf/tst-stringtable.c | 141 +++++++++++++++++++++++++++ 5 files changed, 448 insertions(+), 1 deletion(-) create mode 100644 elf/stringtable.c create mode 100644 elf/stringtable.h create mode 100644 elf/stringtable_free.c create mode 100644 elf/tst-stringtable.c diff --git a/elf/Makefile b/elf/Makefile index d2f7e99863..5a8f116a67 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -172,7 +172,7 @@ tests-container := \ tests := tst-tls9 tst-leaks1 \ tst-array1 tst-array2 tst-array3 tst-array4 tst-array5 \ - tst-auxv + tst-auxv tst-stringtable tests-internal := tst-tls1 tst-tls2 $(tests-static-internal) tests-static := $(tests-static-normal) $(tests-static-internal) diff --git a/elf/stringtable.c b/elf/stringtable.c new file mode 100644 index 0000000000..e5b38148a6 --- /dev/null +++ b/elf/stringtable.c @@ -0,0 +1,209 @@ +/* String tables for ld.so.cache construction. Implementation. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +#include +#include +#include +#include +#include +#include +#include + +static void +stringtable_init (struct stringtable *table) +{ + table->count = 0; + + /* This needs to be a power of two. 128 is sufficient to keep track + of 42 DSOs without resizing (assuming two strings per DSOs). + glibc itself comes with more than 20 DSOs, so 64 would likely to + be too small. */ + table->allocated = 64; + + table->entries = xcalloc (table->allocated, sizeof (table->entries[0])); +} + +/* 32-bit FNV-1a hash function. */ +static uint32_t +fnv1a (const char *string, size_t length) +{ + const unsigned char *p = (const unsigned char *) string; + uint32_t hash = 2166136261U; + for (size_t i = 0; i < length; ++i) + { + hash ^= p[i]; + hash *= 16777619U; + } + return hash; +} + +/* Double the capacity of the hash table. */ +static void +stringtable_rehash (struct stringtable *table) +{ + /* Cannot overflow because the old allocation size (in bytes) is + larger. */ + uint32_t new_allocated = table->allocated * 2; + struct stringtable_entry **new_entries + = xcalloc (new_allocated, sizeof (table->entries[0])); + + uint32_t mask = new_allocated - 1; + for (uint32_t i = 0; i < table->allocated; ++i) + for (struct stringtable_entry *e = table->entries[i]; e != NULL; ) + { + struct stringtable_entry *next = e->next; + uint32_t hash = fnv1a (e->string, e->length); + uint32_t new_index = hash & mask; + e->next = new_entries[new_index]; + new_entries[new_index] = e; + e = next; + } + + free (table->entries); + table->entries = new_entries; + table->allocated = new_allocated; +} + +struct stringtable_entry * +stringtable_add (struct stringtable *table, const char *string) +{ + /* Check for a zero-initialized table. */ + if (table->allocated == 0) + stringtable_init (table); + + size_t length = strlen (string); + if (length > (1U << 30)) + error (EXIT_FAILURE, 0, _("String table string is too long")); + uint32_t hash = fnv1a (string, length); + + /* Return a previously-existing entry. */ + for (struct stringtable_entry *e + = table->entries[hash & (table->allocated - 1)]; + e != NULL; e = e->next) + if (e->length == length && memcmp (e->string, string, length) == 0) + return e; + + /* Increase the size of the table if necessary. Keep utilization + below two thirds. */ + if (table->count >= (1U << 30)) + error (EXIT_FAILURE, 0, _("String table has too many entries")); + if (table->count * 3 > table->allocated * 2) + stringtable_rehash (table); + + /* Add the new table entry. */ + ++table->count; + struct stringtable_entry *e + = xmalloc (offsetof (struct stringtable_entry, string) + length + 1); + uint32_t index = hash & (table->allocated - 1); + e->next = table->entries[index]; + table->entries[index] = e; + e->length = length; + e->offset = 0; + memcpy (e->string, string, length + 1); + return e; +} + +/* Sort reversed strings in lexicographic order. This is used for tail + merging. */ +static int +finalize_compare (const void *l, const void *r) +{ + struct stringtable_entry *left = *(struct stringtable_entry **) l; + struct stringtable_entry *right = *(struct stringtable_entry **) r; + size_t to_compare; + if (left->length < right->length) + to_compare = left->length; + else + to_compare = right->length; + for (ssize_t i = to_compare - 1; i >= 0; --i) + { + unsigned char lch = left->string[i]; + unsigned char rch = right->string[i]; + if (lch != rch) + return lch - rch; + } + if (left->length == right->length) + return 0; + else if (left->length < right->length) + /* Longer strings should come first. */ + return 1; + else + return -1; +} + +void +stringtable_finalize (struct stringtable *table, + struct stringtable_finalized *result) +{ + if (table->count == 0) + { + result->strings = xstrdup (""); + result->size = 0; + return; + } + + /* Optimize the order of the strings. */ + struct stringtable_entry **array = xcalloc (table->count, sizeof (*array)); + { + size_t j = 0; + for (uint32_t i = 0; i < table->allocated; ++i) + for (struct stringtable_entry *e = table->entries[i]; e != NULL; + e = e->next) + { + array[j] = e; + ++j; + } + assert (j == table->count); + } + qsort (array, table->count, sizeof (*array), finalize_compare); + + /* Assign offsets, using tail merging (sharing suffixes) if possible. */ + array[0]->offset = 0; + for (uint32_t j = 1; j < table->count; ++j) + { + struct stringtable_entry *previous = array[j - 1]; + struct stringtable_entry *current = array[j]; + if (previous->length >= current->length + && memcmp (&previous->string[previous->length - current->length], + current->string, current->length) == 0) + current->offset = (previous->offset + previous->length + - current->length); + else if (__builtin_add_overflow (previous->offset, + previous->length + 1, + ¤t->offset)) + error (EXIT_FAILURE, 0, _("String table is too large")); + } + + /* Allocate the result string. */ + { + struct stringtable_entry *last = array[table->count - 1]; + if (__builtin_add_overflow (last->offset, last->length + 1, + &result->size)) + error (EXIT_FAILURE, 0, _("String table is too large")); + } + /* The strings are copied from the hash table, so the array is no + longer needed. */ + free (array); + result->strings = xcalloc (result->size, 1); + + /* Copy the strings. */ + for (uint32_t i = 0; i < table->allocated; ++i) + for (struct stringtable_entry *e = table->entries[i]; e != NULL; + e = e->next) + if (result->strings[e->offset] == '\0') + memcpy (&result->strings[e->offset], e->string, e->length + 1); +} diff --git a/elf/stringtable.h b/elf/stringtable.h new file mode 100644 index 0000000000..7d57d1bda9 --- /dev/null +++ b/elf/stringtable.h @@ -0,0 +1,64 @@ +/* String tables for ld.so.cache construction. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +#ifndef _STRINGTABLE_H +#define _STRINGTABLE_H + +#include +#include + +/* An entry in the string table. Only the length and string fields are + expected to be used outside the string table code. */ +struct stringtable_entry +{ + struct stringtable_entry *next; /* For collision resolution. */ + uint32_t length; /* Length of then string. */ + uint32_t offset; /* From start of finalized table. */ + char string[]; /* Null-terminated string. */ +}; + +/* A string table. Zero-initialization produces a valid atable. */ +struct stringtable +{ + struct stringtable_entry **entries; /* Array of hash table buckets. */ + uint32_t count; /* Number of elements in the table. */ + uint32_t allocated; /* Length of the entries array. */ +}; + +/* Adds STRING to TABLE. May return the address of an existing entry. */ +struct stringtable_entry *stringtable_add (struct stringtable *table, + const char *string); + +/* Result of stringtable_finalize. SIZE bytes at STRINGS should be + written to the file. */ +struct stringtable_finalized +{ + char *strings; + size_t size; +}; + +/* Assigns offsets to string table entries and computes the serialized + form of the string table. */ +void stringtable_finalize (struct stringtable *table, + struct stringtable_finalized *result); + +/* Deallocate the string table (but not the TABLE pointer itself). + (The table can be re-used for adding more strings without + initialization.) */ +void stringtable_free (struct stringtable *table); + +#endif /* _STRINGTABLE_H */ diff --git a/elf/stringtable_free.c b/elf/stringtable_free.c new file mode 100644 index 0000000000..8588a25470 --- /dev/null +++ b/elf/stringtable_free.c @@ -0,0 +1,33 @@ +/* String tables for ld.so.cache construction. Deallocation (for tests only). + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +#include +#include + +void +stringtable_free (struct stringtable *table) +{ + for (uint32_t i = 0; i < table->allocated; ++i) + for (struct stringtable_entry *e = table->entries[i]; e != NULL; ) + { + struct stringtable_entry *next = e->next; + free (e); + e = next; + } + free (table->entries); + *table = (struct stringtable) { 0, }; +} diff --git a/elf/tst-stringtable.c b/elf/tst-stringtable.c new file mode 100644 index 0000000000..2d044b7151 --- /dev/null +++ b/elf/tst-stringtable.c @@ -0,0 +1,141 @@ +/* Unit test for ldconfig string tables. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +#include +#include +#include +#include +#include + +static int +do_test (void) +{ + /* Empty string table. */ + { + struct stringtable s = { 0, }; + struct stringtable_finalized f; + stringtable_finalize (&s, &f); + TEST_COMPARE_STRING (f.strings, ""); + TEST_COMPARE (f.size, 0); + free (f.strings); + stringtable_free (&s); + } + + /* String table with one empty string. */ + { + struct stringtable s = { 0, }; + struct stringtable_entry *e = stringtable_add (&s, ""); + TEST_COMPARE_STRING (e->string, ""); + TEST_COMPARE (e->length, 0); + TEST_COMPARE (s.count, 1); + + struct stringtable_finalized f; + stringtable_finalize (&s, &f); + TEST_COMPARE (e->offset, 0); + TEST_COMPARE_STRING (f.strings, ""); + TEST_COMPARE (f.size, 1); + free (f.strings); + stringtable_free (&s); + } + + /* String table with one non-empty string. */ + { + struct stringtable s = { 0, }; + struct stringtable_entry *e = stringtable_add (&s, "name"); + TEST_COMPARE_STRING (e->string, "name"); + TEST_COMPARE (e->length, 4); + TEST_COMPARE (s.count, 1); + + struct stringtable_finalized f; + stringtable_finalize (&s, &f); + TEST_COMPARE (e->offset, 0); + TEST_COMPARE_STRING (f.strings, "name"); + TEST_COMPARE (f.size, 5); + free (f.strings); + stringtable_free (&s); + } + + /* Two strings, one is a prefix of the other. Tail-merging can only + happen in one way in this case. */ + { + struct stringtable s = { 0, }; + struct stringtable_entry *suffix = stringtable_add (&s, "suffix"); + TEST_COMPARE_STRING (suffix->string, "suffix"); + TEST_COMPARE (suffix->length, 6); + TEST_COMPARE (s.count, 1); + + struct stringtable_entry *prefix + = stringtable_add (&s, "prefix-suffix"); + TEST_COMPARE_STRING (prefix->string, "prefix-suffix"); + TEST_COMPARE (prefix->length, strlen ("prefix-suffix")); + TEST_COMPARE (s.count, 2); + + struct stringtable_finalized f; + stringtable_finalize (&s, &f); + TEST_COMPARE (prefix->offset, 0); + TEST_COMPARE (suffix->offset, strlen ("prefix-")); + TEST_COMPARE_STRING (f.strings, "prefix-suffix"); + TEST_COMPARE (f.size, sizeof ("prefix-suffix")); + free (f.strings); + stringtable_free (&s); + } + + /* String table with various shared prefixes. Triggers hash + resizing. */ + { + enum { count = 1500 }; + char *strings[2 * count]; + struct stringtable_entry *entries[2 * count]; + struct stringtable s = { 0, }; + for (int i = 0; i < count; ++i) + { + strings[i] = xasprintf ("%d", i); + entries[i] = stringtable_add (&s, strings[i]); + TEST_COMPARE (entries[i]->length, strlen (strings[i])); + TEST_COMPARE_STRING (entries[i]->string, strings[i]); + strings[i + count] = xasprintf ("prefix/%d", i); + entries[i + count] = stringtable_add (&s, strings[i + count]); + TEST_COMPARE (entries[i + count]->length, strlen (strings[i + count])); + TEST_COMPARE_STRING (entries[i + count]->string, strings[i + count]); + } + + struct stringtable_finalized f; + stringtable_finalize (&s, &f); + + for (int i = 0; i < 2 * count; ++i) + { + TEST_COMPARE (entries[i]->length, strlen (strings[i])); + TEST_COMPARE_STRING (entries[i]->string, strings[i]); + TEST_COMPARE_STRING (f.strings + entries[i]->offset, strings[i]); + free (strings[i]); + } + + free (f.strings); + stringtable_free (&s); + } + + return 0; +} + +#include + +/* Re-compile the string table implementation here. It is not + possible to link against the actual build because it was built for + use in ldconfig. */ +#define _(arg) arg +#include "stringtable.c" +#include "stringtable_free.c" From patchwork Mon Nov 9 18:41:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 40975 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 65CC63896C0B; Mon, 9 Nov 2020 18:41:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 65CC63896C0B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1604947269; bh=h+QNEeXy/YuXn1d/Uturb7J6J6EILF+eGPKiAx+RmkA=; 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=LOf+3HZJBZdnnuBcpbCrVqsXAK7YF91dmLFIXKkkzUdR9VkiecjWGp2CqSV2jCvpX UvgbAGuUsF6iazDPCUgcGH/HotibL29Y60i5XQ/bPp8hlxByw3EJvSBuxWp6iQBZgn fd4fdtPeFCNr6JGLJi0KUNw4D/7zcIvwVuDj4sF8= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [63.128.21.124]) by sourceware.org (Postfix) with ESMTP id E53A63894C07 for ; Mon, 9 Nov 2020 18:41:06 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org E53A63894C07 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-344-BzAM8Q4jOS-QaIZUIlninA-1; Mon, 09 Nov 2020 13:41:04 -0500 X-MC-Unique: BzAM8Q4jOS-QaIZUIlninA-1 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id C4BA9186DD2E for ; Mon, 9 Nov 2020 18:41:03 +0000 (UTC) Received: from oldenburg2.str.redhat.com (ovpn-113-222.ams2.redhat.com [10.36.113.222]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 1634A5D9CC for ; Mon, 9 Nov 2020 18:41:02 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH 07/11] elf: Implement tail merging of strings in ldconfig In-Reply-To: References: Message-Id: <2323ee8769cd254461f0b43a5a8d094eda5f39af.1604946656.git.fweimer@redhat.com> Date: Mon, 09 Nov 2020 19:41:01 +0100 User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.2 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_H5, 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" This simplifies the string table construction in elf/cache.c because there is no more need to keep track of offsets explicitly; the string table implementation does this internally. This change slightly reduces the size of the cache on disk. The file format does not change as a result. The strings are null-terminated, without explicit length, so tail merging is transparent to readers. Reviewed-by: Adhemerval Zanella --- elf/Makefile | 3 ++- elf/cache.c | 76 ++++++++++++++++++++++++++-------------------------- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/elf/Makefile b/elf/Makefile index 5a8f116a67..e26ac16b44 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -118,7 +118,8 @@ others-static += ldconfig others += ldconfig install-rootsbin += ldconfig -ldconfig-modules := cache readlib xmalloc xstrdup chroot_canon static-stubs +ldconfig-modules := cache readlib xmalloc xstrdup chroot_canon static-stubs \ + stringtable extra-objs += $(ldconfig-modules:=.o) others-extras = $(ldconfig-modules) endif diff --git a/elf/cache.c b/elf/cache.c index 549e04ce21..5a6ee20e86 100644 --- a/elf/cache.c +++ b/elf/cache.c @@ -35,11 +35,15 @@ #include #include #include +#include + +/* Used to store library names, paths, and other strings. */ +static struct stringtable strings; struct cache_entry { - char *lib; /* Library name. */ - char *path; /* Path to find library. */ + struct stringtable_entry *lib; /* Library name. */ + struct stringtable_entry *path; /* Path to find library. */ int flags; /* Flags to indicate kind of library. */ unsigned int osversion; /* Required OS version. */ uint64_t hwcap; /* Important hardware capabilities. */ @@ -300,7 +304,7 @@ static int compare (const struct cache_entry *e1, const struct cache_entry *e2) { /* We need to swap entries here to get the correct sort order. */ - int res = _dl_cache_libcmp (e2->lib, e1->lib); + int res = _dl_cache_libcmp (e2->lib->string, e1->lib->string); if (res == 0) { if (e1->flags < e2->flags) @@ -369,26 +373,24 @@ save_cache (const char *cache_name) { /* The cache entries are sorted already, save them in this order. */ - /* Count the length of all strings. */ - /* The old format doesn't contain hwcap entries and doesn't contain - libraries in subdirectories with hwcaps entries. Count therefore - also all entries with hwcap == 0. */ - size_t total_strlen = 0; struct cache_entry *entry; /* Number of cache entries. */ int cache_entry_count = 0; - /* Number of normal cache entries. */ + /* The old format doesn't contain hwcap entries and doesn't contain + libraries in subdirectories with hwcaps entries. Count therefore + also all entries with hwcap == 0. */ int cache_entry_old_count = 0; for (entry = entries; entry != NULL; entry = entry->next) { - /* Account the final NULs. */ - total_strlen += strlen (entry->lib) + strlen (entry->path) + 2; ++cache_entry_count; if (entry->hwcap == 0) ++cache_entry_old_count; } + struct stringtable_finalized strings_finalized; + stringtable_finalize (&strings, &strings_finalized); + /* Create the on disk cache structure. */ struct cache_file *file_entries = NULL; size_t file_entries_size = 0; @@ -432,7 +434,7 @@ save_cache (const char *cache_name) sizeof CACHE_VERSION - 1); file_entries_new->nlibs = cache_entry_count; - file_entries_new->len_strings = total_strlen; + file_entries_new->len_strings = strings_finalized.size; file_entries_new->flags = cache_file_new_flags_endian_current; } @@ -449,20 +451,20 @@ save_cache (const char *cache_name) str_offset = 0; /* An array for all strings. */ - char *strings = xmalloc (total_strlen); - char *str = strings; int idx_old; int idx_new; for (idx_old = 0, idx_new = 0, entry = entries; entry != NULL; entry = entry->next, ++idx_new) { - /* First the library. */ if (opt_format != opt_format_new && entry->hwcap == 0) { file_entries->libs[idx_old].flags = entry->flags; /* XXX: Actually we can optimize here and remove duplicates. */ file_entries->libs[idx_old].key = str_offset + pad; + file_entries->libs[idx_new].key = str_offset + entry->lib->offset; + file_entries->libs[idx_new].value + = str_offset + entry->path->offset; } if (opt_format != opt_format_old) { @@ -473,20 +475,12 @@ save_cache (const char *cache_name) file_entries_new->libs[idx_new].flags = entry->flags; file_entries_new->libs[idx_new].osversion = entry->osversion; file_entries_new->libs[idx_new].hwcap = entry->hwcap; - file_entries_new->libs[idx_new].key = str_offset; + file_entries_new->libs[idx_new].key + = str_offset + entry->lib->offset; + file_entries_new->libs[idx_new].value + = str_offset + entry->path->offset; } - size_t len = strlen (entry->lib) + 1; - str = mempcpy (str, entry->lib, len); - str_offset += len; - /* Then the path. */ - if (opt_format != opt_format_new && entry->hwcap == 0) - file_entries->libs[idx_old].value = str_offset + pad; - if (opt_format != opt_format_old) - file_entries_new->libs[idx_new].value = str_offset; - len = strlen (entry->path) + 1; - str = mempcpy (str, entry->path, len); - str_offset += len; /* Ignore entries with hwcap for old format. */ if (entry->hwcap == 0) ++idx_old; @@ -511,7 +505,7 @@ save_cache (const char *cache_name) extension_offset += pad; extension_offset += file_entries_new_size; } - extension_offset += total_strlen; + extension_offset += strings_finalized.size; extension_offset = roundup (extension_offset, 4); /* Provide alignment. */ if (opt_format != opt_format_old) file_entries_new->extension_offset = extension_offset; @@ -551,7 +545,8 @@ save_cache (const char *cache_name) error (EXIT_FAILURE, errno, _("Writing of cache data failed")); } - if (write (fd, strings, total_strlen) != (ssize_t) total_strlen) + if (write (fd, strings_finalized.strings, strings_finalized.size) + != (ssize_t) strings_finalized.size) error (EXIT_FAILURE, errno, _("Writing of cache data failed")); if (opt_format != opt_format_old) @@ -580,7 +575,7 @@ save_cache (const char *cache_name) /* Free all allocated memory. */ free (file_entries_new); free (file_entries); - free (strings); + free (strings_finalized.strings); while (entries) { @@ -596,14 +591,19 @@ void add_to_cache (const char *path, const char *lib, int flags, unsigned int osversion, uint64_t hwcap) { - size_t liblen = strlen (lib) + 1; - size_t len = liblen + strlen (path) + 1; - struct cache_entry *new_entry - = xmalloc (sizeof (struct cache_entry) + liblen + len); - - new_entry->lib = memcpy ((char *) (new_entry + 1), lib, liblen); - new_entry->path = new_entry->lib + liblen; - snprintf (new_entry->path, len, "%s/%s", path, lib); + struct cache_entry *new_entry = xmalloc (sizeof (*new_entry)); + + struct stringtable_entry *path_interned; + { + char *p; + if (asprintf (&p, "%s/%s", path, lib) < 0) + error (EXIT_FAILURE, errno, _("Could not create library path")); + path_interned = stringtable_add (&strings, p); + free (p); + } + + new_entry->lib = stringtable_add (&strings, lib); + new_entry->path = path_interned; new_entry->flags = flags; new_entry->osversion = osversion; new_entry->hwcap = hwcap; From patchwork Mon Nov 9 18:41:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 40976 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 92FFE3894C07; Mon, 9 Nov 2020 18:41:18 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 92FFE3894C07 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1604947278; bh=rfG4C4+eshy9NYYx1GwGiqEfVi471VMMxuRxY3QdV3o=; 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=qMRr9O+jU8ogEyHATfsgdUsVRHKspVqKRfoNO0LUg+RnHDjm0vtzYN7N59h9+xYYX ZvnF3w+YSkb4PixxDB6yJBxH7axOpF+NH33l57VhnAb4aK9+ouI0HqX/yH2DKBlrxY V22T85GK55Ws6v4AzEcG6h73BB2/0x/u+OEQu+6s= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by sourceware.org (Postfix) with ESMTP id 14F843894C07 for ; Mon, 9 Nov 2020 18:41:14 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 14F843894C07 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-569-WtIsfi4UOr6Uej0Q1_DUIQ-1; Mon, 09 Nov 2020 13:41:11 -0500 X-MC-Unique: WtIsfi4UOr6Uej0Q1_DUIQ-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 750E7186DD23 for ; Mon, 9 Nov 2020 18:41:09 +0000 (UTC) Received: from oldenburg2.str.redhat.com (ovpn-113-222.ams2.redhat.com [10.36.113.222]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 582DC5C1D0 for ; Mon, 9 Nov 2020 18:41:08 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH 08/11] elf: Process glibc-hwcaps subdirectories in ldconfig In-Reply-To: References: Message-Id: <33bdcc0622c2754ab21f9a3bf66cc262191cdddb.1604946656.git.fweimer@redhat.com> Date: Mon, 09 Nov 2020 19:41:06 +0100 User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, 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" Libraries from these subdirectories are added to the cache with a special hwcap bit DL_CACHE_HWCAP_EXTENSION, so that they are ignored by older dynamic loaders. Reviewed-by: Adhemerval Zanella --- elf/cache.c | 257 ++++++++++++++++++++++++++++++++----- elf/ldconfig.c | 155 +++++++++++++++++++--- sysdeps/generic/dl-cache.h | 50 ++++++++ sysdeps/generic/ldconfig.h | 18 ++- 4 files changed, 427 insertions(+), 53 deletions(-) diff --git a/elf/cache.c b/elf/cache.c index 5a6ee20e86..b03c5319f8 100644 --- a/elf/cache.c +++ b/elf/cache.c @@ -40,6 +40,105 @@ /* Used to store library names, paths, and other strings. */ static struct stringtable strings; +/* Keeping track of "glibc-hwcaps" subdirectories. During cache + construction, a linear search by name is performed to deduplicate + entries. */ +struct glibc_hwcaps_subdirectory +{ + struct glibc_hwcaps_subdirectory *next; + + /* Interned string with the subdirectory name. */ + struct stringtable_entry *name; + + /* Array index in the cache_extension_tag_glibc_hwcaps section in + the stored cached file. This is computed after all the + subdirectories have been processed, so that subdirectory names in + the extension section can be sorted. */ + uint32_t section_index; + + /* True if the subdirectory is actually used for anything. */ + bool used; +}; + +const char * +glibc_hwcaps_subdirectory_name (const struct glibc_hwcaps_subdirectory *dir) +{ + return dir->name->string; +} + +/* Linked list of known hwcaps subdirecty names. */ +static struct glibc_hwcaps_subdirectory *hwcaps; + +struct glibc_hwcaps_subdirectory * +new_glibc_hwcaps_subdirectory (const char *name) +{ + struct stringtable_entry *name_interned = stringtable_add (&strings, name); + for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next) + if (p->name == name_interned) + return p; + struct glibc_hwcaps_subdirectory *p = xmalloc (sizeof (*p)); + p->next = hwcaps; + p->name = name_interned; + p->section_index = 0; + p->used = false; + hwcaps = p; + return p; +} + +/* Helper for sorting struct glibc_hwcaps_subdirectory elements by + name. */ +static int +assign_glibc_hwcaps_indices_compare (const void *l, const void *r) +{ + const struct glibc_hwcaps_subdirectory *left + = *(struct glibc_hwcaps_subdirectory **)l; + const struct glibc_hwcaps_subdirectory *right + = *(struct glibc_hwcaps_subdirectory **)r; + return strcmp (glibc_hwcaps_subdirectory_name (left), + glibc_hwcaps_subdirectory_name (right)); +} + +/* Count the number of hwcaps subdirectories which are actually + used. */ +static size_t +glibc_hwcaps_count (void) +{ + size_t count = 0; + for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next) + if (p->used) + ++count; + return count; +} + +/* Compute the section_index fields for all */ +static void +assign_glibc_hwcaps_indices (void) +{ + /* Convert the linked list into an array, so that we can use qsort. + Only copy the subdirectories which are actually used. */ + size_t count = glibc_hwcaps_count (); + struct glibc_hwcaps_subdirectory **array + = xmalloc (sizeof (*array) * count); + { + size_t i = 0; + for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next) + if (p->used) + { + array[i] = p; + ++i; + } + assert (i == count); + } + + qsort (array, count, sizeof (*array), assign_glibc_hwcaps_indices_compare); + + /* Assign the array indices. */ + for (size_t i = 0; i < count; ++i) + array[i]->section_index = i; + + free (array); +} + struct cache_entry { struct stringtable_entry *lib; /* Library name. */ @@ -48,6 +147,10 @@ struct cache_entry unsigned int osversion; /* Required OS version. */ uint64_t hwcap; /* Important hardware capabilities. */ int bits_hwcap; /* Number of bits set in hwcap. */ + + /* glibc-hwcaps subdirectory. If not NULL, hwcap must be zero. */ + struct glibc_hwcaps_subdirectory *hwcaps; + struct cache_entry *next; /* Next entry in list. */ }; @@ -60,7 +163,7 @@ static const char *flag_descr[] = /* Print a single entry. */ static void print_entry (const char *lib, int flag, unsigned int osversion, - uint64_t hwcap, const char *key) + uint64_t hwcap, const char *hwcap_string, const char *key) { printf ("\t%s (", lib); switch (flag & FLAG_TYPE_MASK) @@ -132,7 +235,9 @@ print_entry (const char *lib, int flag, unsigned int osversion, printf (",%d", flag & FLAG_REQUIRED_MASK); break; } - if (hwcap != 0) + if (hwcap_string != NULL) + printf (", hwcap: \"%s\"", hwcap_string); + else if (hwcap != 0) printf (", hwcap: %#.16" PRIx64, hwcap); if (osversion != 0) { @@ -158,6 +263,29 @@ print_entry (const char *lib, int flag, unsigned int osversion, printf (") => %s\n", key); } +/* Returns the string with the name of the glibcs-hwcaps subdirectory + associated with ENTRY->hwcap. file_base must be the base address + for string table indices. */ +static const char * +glibc_hwcaps_string (struct cache_extension_all_loaded *ext, + const void *file_base, size_t file_size, + struct file_entry_new *entry) +{ + const uint32_t *hwcaps_array + = ext->sections[cache_extension_tag_glibc_hwcaps].base; + if (dl_cache_hwcap_extension (entry) && hwcaps_array != NULL) + { + uint32_t index = (uint32_t) entry->hwcap; + if (index < ext->sections[cache_extension_tag_glibc_hwcaps].size / 4) + { + uint32_t string_table_index = hwcaps_array[index]; + if (string_table_index < file_size) + return file_base + string_table_index; + } + } + return NULL; +} + /* Print an error and exit if the new-file cache is internally inconsistent. */ static void @@ -167,9 +295,7 @@ check_new_cache (struct cache_file_new *cache) error (EXIT_FAILURE, 0, _("Cache file has wrong endianness.\n")); } -/* Print the extension information at the cache at start address - FILE_BASE, of length FILE_SIZE bytes. The new-format cache header - is at CACHE, and the file name for diagnostics is CACHE_NAME. */ +/* Print the extension information in *EXT. */ static void print_extensions (struct cache_extension_all_loaded *ext) { @@ -266,7 +392,7 @@ print_cache (const char *cache_name) /* Print everything. */ for (unsigned int i = 0; i < cache->nlibs; i++) print_entry (cache_data + cache->libs[i].key, - cache->libs[i].flags, 0, 0, + cache->libs[i].flags, 0, 0, NULL, cache_data + cache->libs[i].value); } else if (format == 1) @@ -281,11 +407,16 @@ print_cache (const char *cache_name) /* Print everything. */ for (unsigned int i = 0; i < cache_new->nlibs; i++) - print_entry (cache_data + cache_new->libs[i].key, - cache_new->libs[i].flags, - cache_new->libs[i].osversion, - cache_new->libs[i].hwcap, - cache_data + cache_new->libs[i].value); + { + const char *hwcaps_string + = glibc_hwcaps_string (&ext, cache, cache_size, + &cache_new->libs[i]); + print_entry (cache_data + cache_new->libs[i].key, + cache_new->libs[i].flags, + cache_new->libs[i].osversion, + cache_new->libs[i].hwcap, hwcaps_string, + cache_data + cache_new->libs[i].value); + } print_extensions (&ext); } /* Cleanup. */ @@ -311,8 +442,23 @@ compare (const struct cache_entry *e1, const struct cache_entry *e2) return 1; else if (e1->flags > e2->flags) return -1; + /* Keep the glibc-hwcaps extension entries before the regular + entries, and sort them by their names. search_cache in + dl-cache.c stops searching once the first non-extension entry + is found, so the extension entries need to come first. */ + else if (e1->hwcaps != NULL && e2->hwcaps == NULL) + return -1; + else if (e1->hwcaps == NULL && e2->hwcaps != NULL) + return 1; + else if (e1->hwcaps != NULL && e2->hwcaps != NULL) + { + res = strcmp (glibc_hwcaps_subdirectory_name (e1->hwcaps), + glibc_hwcaps_subdirectory_name (e2->hwcaps)); + if (res != 0) + return res; + } /* Sort by most specific hwcap. */ - else if (e2->bits_hwcap > e1->bits_hwcap) + if (e2->bits_hwcap > e1->bits_hwcap) return 1; else if (e2->bits_hwcap < e1->bits_hwcap) return -1; @@ -337,30 +483,65 @@ enum * sizeof (struct cache_extension_section))) }; -/* Write the cache extensions to FD. The extension directory is - assumed to be located at CACHE_EXTENSION_OFFSET. */ +/* Write the cache extensions to FD. The string table is shifted by + STRING_TABLE_OFFSET. The extension directory is assumed to be + located at CACHE_EXTENSION_OFFSET. assign_glibc_hwcaps_indices + must have been called. */ static void -write_extensions (int fd, uint32_t cache_extension_offset) +write_extensions (int fd, uint32_t str_offset, + uint32_t cache_extension_offset) { assert ((cache_extension_offset % 4) == 0); + /* The length and contents of the glibc-hwcaps section. */ + uint32_t hwcaps_count = glibc_hwcaps_count (); + uint32_t hwcaps_offset = cache_extension_offset + cache_extension_size; + uint32_t hwcaps_size = hwcaps_count * sizeof (uint32_t); + uint32_t *hwcaps_array = xmalloc (hwcaps_size); + for (struct glibc_hwcaps_subdirectory *p = hwcaps; p != NULL; p = p->next) + if (p->used) + hwcaps_array[p->section_index] = str_offset + p->name->offset; + + /* This is the offset of the generator string. */ + uint32_t generator_offset = hwcaps_offset; + if (hwcaps_count == 0) + /* There is no section for the hwcaps subdirectories. */ + generator_offset -= sizeof (struct cache_extension_section); + else + /* The string table indices for the hwcaps subdirectories shift + the generator string backwards. */ + generator_offset += hwcaps_size; + struct cache_extension *ext = xmalloc (cache_extension_size); ext->magic = cache_extension_magic; - ext->count = cache_extension_count; - for (int i = 0; i < cache_extension_count; ++i) - { - ext->sections[i].tag = i; - ext->sections[i].flags = 0; - } + /* Extension index current being filled. */ + size_t xid = 0; const char *generator = "ldconfig " PKGVERSION RELEASE " release version " VERSION; - ext->sections[cache_extension_tag_generator].offset - = cache_extension_offset + cache_extension_size; - ext->sections[cache_extension_tag_generator].size = strlen (generator); + ext->sections[xid].tag = cache_extension_tag_generator; + ext->sections[xid].flags = 0; + ext->sections[xid].offset = generator_offset; + ext->sections[xid].size = strlen (generator); + + if (hwcaps_count > 0) + { + ++xid; + ext->sections[xid].tag = cache_extension_tag_glibc_hwcaps; + ext->sections[xid].flags = 0; + ext->sections[xid].offset = hwcaps_offset; + ext->sections[xid].size = hwcaps_size; + } + + ++xid; + ext->count = xid; + assert (xid <= cache_extension_count); - if (write (fd, ext, cache_extension_size) != cache_extension_size + size_t ext_size = (offsetof (struct cache_extension, sections) + + xid * sizeof (struct cache_extension_section)); + if (write (fd, ext, ext_size) != ext_size + || write (fd, hwcaps_array, hwcaps_size) != hwcaps_size || write (fd, generator, strlen (generator)) != strlen (generator)) error (EXIT_FAILURE, errno, _("Writing of cache extension data failed")); @@ -373,6 +554,8 @@ save_cache (const char *cache_name) { /* The cache entries are sorted already, save them in this order. */ + assign_glibc_hwcaps_indices (); + struct cache_entry *entry; /* Number of cache entries. */ int cache_entry_count = 0; @@ -474,7 +657,11 @@ save_cache (const char *cache_name) struct. */ file_entries_new->libs[idx_new].flags = entry->flags; file_entries_new->libs[idx_new].osversion = entry->osversion; - file_entries_new->libs[idx_new].hwcap = entry->hwcap; + if (entry->hwcaps == NULL) + file_entries_new->libs[idx_new].hwcap = entry->hwcap; + else + file_entries_new->libs[idx_new].hwcap + = DL_CACHE_HWCAP_EXTENSION | entry->hwcaps->section_index; file_entries_new->libs[idx_new].key = str_offset + entry->lib->offset; file_entries_new->libs[idx_new].value @@ -554,7 +741,7 @@ save_cache (const char *cache_name) /* Align file position to 4. */ off64_t old_offset = lseek64 (fd, extension_offset, SEEK_SET); assert ((unsigned long long int) (extension_offset - old_offset) < 4); - write_extensions (fd, extension_offset); + write_extensions (fd, str_offset, extension_offset); } /* Make sure user can always read cache file */ @@ -588,27 +775,35 @@ save_cache (const char *cache_name) /* Add one library to the cache. */ void -add_to_cache (const char *path, const char *lib, int flags, - unsigned int osversion, uint64_t hwcap) +add_to_cache (const char *path, const char *filename, const char *soname, + int flags, unsigned int osversion, uint64_t hwcap, + struct glibc_hwcaps_subdirectory *hwcaps) { struct cache_entry *new_entry = xmalloc (sizeof (*new_entry)); struct stringtable_entry *path_interned; { char *p; - if (asprintf (&p, "%s/%s", path, lib) < 0) + if (asprintf (&p, "%s/%s", path, filename) < 0) error (EXIT_FAILURE, errno, _("Could not create library path")); path_interned = stringtable_add (&strings, p); free (p); } - new_entry->lib = stringtable_add (&strings, lib); + new_entry->lib = stringtable_add (&strings, soname); new_entry->path = path_interned; new_entry->flags = flags; new_entry->osversion = osversion; new_entry->hwcap = hwcap; + new_entry->hwcaps = hwcaps; new_entry->bits_hwcap = 0; + if (hwcaps != NULL) + { + assert (hwcap == 0); + hwcaps->used = true; + } + /* Count the number of bits set in the masked value. */ for (size_t i = 0; (~((1ULL << i) - 1) & hwcap) != 0 && i < 8 * sizeof (hwcap); ++i) diff --git a/elf/ldconfig.c b/elf/ldconfig.c index 006198fe59..10927a8c7f 100644 --- a/elf/ldconfig.c +++ b/elf/ldconfig.c @@ -16,6 +16,7 @@ along with this program; if not, see . */ #define PROCINFO_CLASS static +#include #include #include #include @@ -41,6 +42,7 @@ #include #include +#include #include @@ -85,6 +87,10 @@ struct dir_entry dev_t dev; const char *from_file; int from_line; + + /* Non-NULL for subdirectories under a glibc-hwcaps subdirectory. */ + struct glibc_hwcaps_subdirectory *hwcaps; + struct dir_entry *next; }; @@ -338,17 +344,20 @@ new_sub_entry (const struct dir_entry *entry, const char *path, new_entry->from_line = entry->from_line; new_entry->path = xstrdup (path); new_entry->flag = entry->flag; + new_entry->hwcaps = NULL; new_entry->next = NULL; new_entry->ino = st->st_ino; new_entry->dev = st->st_dev; return new_entry; } -/* Add a single directory entry. */ -static void +/* Add a single directory entry. Return true if the directory is + actually added (because it is not a duplicate). */ +static bool add_single_dir (struct dir_entry *entry, int verbose) { struct dir_entry *ptr, *prev; + bool added = true; ptr = dir_entries; prev = ptr; @@ -368,6 +377,7 @@ add_single_dir (struct dir_entry *entry, int verbose) ptr->flag = entry->flag; free (entry->path); free (entry); + added = false; break; } prev = ptr; @@ -378,6 +388,73 @@ add_single_dir (struct dir_entry *entry, int verbose) dir_entries = entry; else if (ptr == NULL) prev->next = entry; + return added; +} + +/* Check if PATH contains a "glibc-hwcaps" subdirectory. If so, queue + its subdirectories for glibc-hwcaps processing. */ +static void +add_glibc_hwcaps_subdirectories (struct dir_entry *entry, const char *path) +{ + /* glibc-hwcaps subdirectories do not nest. */ + assert (entry->hwcaps == NULL); + + char *glibc_hwcaps; + if (asprintf (&glibc_hwcaps, "%s/" GLIBC_HWCAPS_SUBDIRECTORY, path) < 0) + error (EXIT_FAILURE, errno, _("Could not form glibc-hwcaps path")); + + DIR *dir = opendir (glibc_hwcaps); + if (dir != NULL) + { + while (true) + { + errno = 0; + struct dirent64 *e = readdir64 (dir); + if (e == NULL) + { + if (errno == 0) + break; + else + error (EXIT_FAILURE, errno, _("Listing directory %s"), path); + } + + /* Ignore hidden subdirectories, including "." and "..", and + regular files. File names containing a ':' cannot be + looked up by the dynamic loader, so skip those as + well. */ + if (e->d_name[0] == '.' || e->d_type == DT_REG + || strchr (e->d_name, ':') != NULL) + continue; + + /* See if this entry eventually resolves to a directory. */ + struct stat64 st; + if (fstatat64 (dirfd (dir), e->d_name, &st, 0) < 0) + /* Ignore unreadable entries. */ + continue; + + if (S_ISDIR (st.st_mode)) + { + /* This is a directory, so it needs to be scanned for + libraries, associated with the hwcaps implied by the + subdirectory name. */ + char *new_path; + if (asprintf (&new_path, "%s/" GLIBC_HWCAPS_SUBDIRECTORY "/%s", + /* Use non-canonicalized path here. */ + entry->path, e->d_name) < 0) + error (EXIT_FAILURE, errno, + _("Could not form glibc-hwcaps path")); + struct dir_entry *new_entry = new_sub_entry (entry, new_path, + &st); + free (new_path); + new_entry->hwcaps = new_glibc_hwcaps_subdirectory (e->d_name); + add_single_dir (new_entry, 0); + } + } + + closedir (dir); + } + + free (glibc_hwcaps); } /* Add one directory to the list of directories to process. */ @@ -386,6 +463,7 @@ add_dir_1 (const char *line, const char *from_file, int from_line) { unsigned int i; struct dir_entry *entry = xmalloc (sizeof (struct dir_entry)); + entry->hwcaps = NULL; entry->next = NULL; entry->from_file = strdup (from_file); @@ -443,7 +521,9 @@ add_dir_1 (const char *line, const char *from_file, int from_line) entry->ino = stat_buf.st_ino; entry->dev = stat_buf.st_dev; - add_single_dir (entry, 1); + if (add_single_dir (entry, 1)) + /* Add glibc-hwcaps subdirectories if present. */ + add_glibc_hwcaps_subdirectories (entry, path); } if (opt_chroot) @@ -695,15 +775,27 @@ struct dlib_entry static void search_dir (const struct dir_entry *entry) { - uint64_t hwcap = path_hwcap (entry->path); - if (opt_verbose) + uint64_t hwcap; + if (entry->hwcaps == NULL) { - if (hwcap != 0) - printf ("%s: (hwcap: %#.16" PRIx64 ")", entry->path, hwcap); - else - printf ("%s:", entry->path); - printf (_(" (from %s:%d)\n"), entry->from_file, entry->from_line); + hwcap = path_hwcap (entry->path); + if (opt_verbose) + { + if (hwcap != 0) + printf ("%s: (hwcap: %#.16" PRIx64 ")", entry->path, hwcap); + else + printf ("%s:", entry->path); + } } + else + { + hwcap = 0; + if (opt_verbose) + printf ("%s: (hwcap: \"%s\")", entry->path, + glibc_hwcaps_subdirectory_name (entry->hwcaps)); + } + if (opt_verbose) + printf (_(" (from %s:%d)\n"), entry->from_file, entry->from_line); char *dir_name; char *real_file_name; @@ -745,13 +837,15 @@ search_dir (const struct dir_entry *entry) && direntry->d_type != DT_DIR) continue; /* Does this file look like a shared library or is it a hwcap - subdirectory? The dynamic linker is also considered as + subdirectory (if not already processing a glibc-hwcaps + subdirectory)? The dynamic linker is also considered as shared library. */ if (((strncmp (direntry->d_name, "lib", 3) != 0 && strncmp (direntry->d_name, "ld-", 3) != 0) || strstr (direntry->d_name, ".so") == NULL) && (direntry->d_type == DT_REG - || !is_hwcap_platform (direntry->d_name))) + || (entry->hwcaps == NULL + && !is_hwcap_platform (direntry->d_name)))) continue; size_t len = strlen (direntry->d_name); @@ -799,7 +893,7 @@ search_dir (const struct dir_entry *entry) } struct stat64 stat_buf; - int is_dir; + bool is_dir; int is_link = S_ISLNK (lstat_buf.st_mode); if (is_link) { @@ -837,7 +931,10 @@ search_dir (const struct dir_entry *entry) else is_dir = S_ISDIR (lstat_buf.st_mode); - if (is_dir && is_hwcap_platform (direntry->d_name)) + /* No descending into subdirectories if this directory is a + glibc-hwcaps subdirectory (which are not recursive). */ + if (entry->hwcaps == NULL + && is_dir && is_hwcap_platform (direntry->d_name)) { if (!is_link && direntry->d_type != DT_UNKNOWN @@ -1028,13 +1125,31 @@ search_dir (const struct dir_entry *entry) struct dlib_entry *dlib_ptr; for (dlib_ptr = dlibs; dlib_ptr != NULL; dlib_ptr = dlib_ptr->next) { - /* Don't create links to links. */ - if (dlib_ptr->is_link == 0) - create_links (dir_name, entry->path, dlib_ptr->name, - dlib_ptr->soname); + /* The cached file name is the soname for non-glibc-hwcaps + subdirectories (relying on symbolic links; this helps with + library updates that change the file name), and the actual + file for glibc-hwcaps subdirectories. */ + const char *filename; + if (entry->hwcaps == NULL) + { + /* Don't create links to links. */ + if (dlib_ptr->is_link == 0) + create_links (dir_name, entry->path, dlib_ptr->name, + dlib_ptr->soname); + filename = dlib_ptr->soname; + } + else + { + /* Do not create links in glibc-hwcaps subdirectories, but + still log the cache addition. */ + if (opt_verbose) + printf ("\t%s -> %s\n", dlib_ptr->soname, dlib_ptr->name); + filename = dlib_ptr->name; + } if (opt_build_cache) - add_to_cache (entry->path, dlib_ptr->soname, dlib_ptr->flag, - dlib_ptr->osversion, hwcap); + add_to_cache (entry->path, filename, dlib_ptr->soname, + dlib_ptr->flag, dlib_ptr->osversion, + hwcap, entry->hwcaps); } /* Free all resources. */ diff --git a/sysdeps/generic/dl-cache.h b/sysdeps/generic/dl-cache.h index cdc24c8009..85a6bdd93c 100644 --- a/sysdeps/generic/dl-cache.h +++ b/sysdeps/generic/dl-cache.h @@ -99,6 +99,23 @@ struct file_entry_new uint64_t hwcap; /* Hwcap entry. */ }; +/* This bit in the hwcap field of struct file_entry_new indicates that + the lower 32 bits contain an index into the + cache_extension_tag_glibc_hwcaps section. Older glibc versions do + not know about this HWCAP bit, so they will ignore these + entries. */ +#define DL_CACHE_HWCAP_EXTENSION (1ULL << 62) + +/* Return true if the ENTRY->hwcap value indicates that + DL_CACHE_HWCAP_EXTENSION is used. */ +static inline bool +dl_cache_hwcap_extension (struct file_entry_new *entry) +{ + /* If DL_CACHE_HWCAP_EXTENSION is set, but other bits as well, this + is a different kind of extension. */ + return (entry->hwcap >> 32) == (DL_CACHE_HWCAP_EXTENSION >> 32); +} + /* See flags member of struct cache_file_new below. */ enum { @@ -180,6 +197,17 @@ enum cache_extension_tag cache file. */ cache_extension_tag_generator, + /* glibc-hwcaps subdirectory information. An array of uint32_t + values, which are indices into the string table. The strings + are sorted lexicographically (according to strcmp). The extra + level of indirection (instead of using string table indices + directly) allows the dynamic loader to compute the preference + order of the hwcaps names more efficiently. + + For this section, 4-byte alignment is required, and the section + size must be a multiple of 4. */ + cache_extension_tag_glibc_hwcaps, + /* Total number of known cache extension tags. */ cache_extension_count }; @@ -234,6 +262,27 @@ struct cache_extension_all_loaded struct cache_extension_loaded sections[cache_extension_count]; }; +/* Performs basic data validation based on section tag, and removes + the sections which are invalid. */ +static void +cache_extension_verify (struct cache_extension_all_loaded *loaded) +{ + { + /* Section must not be empty, it must be aligned at 4 bytes, and + the size must be a multiple of 4. */ + struct cache_extension_loaded *hwcaps + = &loaded->sections[cache_extension_tag_glibc_hwcaps]; + if (hwcaps->size == 0 + || ((uintptr_t) hwcaps->base % 4) != 0 + || (hwcaps->size % 4) != 0) + { + hwcaps->base = NULL; + hwcaps->size = 0; + hwcaps->flags = 0; + } + } +} + static bool __attribute__ ((unused)) cache_extension_load (const struct cache_file_new *cache, const void *file_base, size_t file_size, @@ -280,6 +329,7 @@ cache_extension_load (const struct cache_file_new *cache, loaded->sections[tag].size = ext->sections[i].size; loaded->sections[tag].flags = ext->sections[i].flags; } + cache_extension_verify (loaded); return true; } diff --git a/sysdeps/generic/ldconfig.h b/sysdeps/generic/ldconfig.h index cfec9d4668..1ad1528890 100644 --- a/sysdeps/generic/ldconfig.h +++ b/sysdeps/generic/ldconfig.h @@ -57,8 +57,22 @@ extern void init_cache (void); extern void save_cache (const char *cache_name); -extern void add_to_cache (const char *path, const char *lib, int flags, - unsigned int osversion, uint64_t hwcap); +struct glibc_hwcaps_subdirectory; + +/* Return a struct describing the subdirectory for NAME. Reuse an + existing struct if it exists. */ +struct glibc_hwcaps_subdirectory *new_glibc_hwcaps_subdirectory + (const char *name); + +/* Returns the name that was specified when + add_glibc_hwcaps_subdirectory was called. */ +const char *glibc_hwcaps_subdirectory_name + (const struct glibc_hwcaps_subdirectory *); + +extern void add_to_cache (const char *path, const char *filename, + const char *soname, + int flags, unsigned int osversion, uint64_t hwcap, + struct glibc_hwcaps_subdirectory *); extern void init_aux_cache (void); From patchwork Mon Nov 9 18:41:13 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 40977 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 9BD413896C1E; Mon, 9 Nov 2020 18:41:23 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 9BD413896C1E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1604947283; bh=hHPTVSXKrvGtf5mmt4/mi1FMJK9u3oiqgAj1uVy2hho=; 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=X7+Mr6RcjgzO2RvqRCVwxDJwY1avmHEqw0XzQOsEuqzX1/jMy49M+lYqr1fACgVXe 4I/ouCj7ML0kg2j4EGi33zSqbUX0sde736co9oTI6gk5NrfInNp5CrTiitFgmYo+kO VChZZ4eY1SSmoJJA5GB7NbQ+SB3Fv0VXghAEKvSc= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by sourceware.org (Postfix) with ESMTP id 1CD823894C17 for ; Mon, 9 Nov 2020 18:41:20 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 1CD823894C17 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-409-fFTAOlX6NTavImY1fQWhVQ-1; Mon, 09 Nov 2020 13:41:17 -0500 X-MC-Unique: fFTAOlX6NTavImY1fQWhVQ-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id B55996414F for ; Mon, 9 Nov 2020 18:41:16 +0000 (UTC) Received: from oldenburg2.str.redhat.com (ovpn-113-222.ams2.redhat.com [10.36.113.222]) by smtp.corp.redhat.com (Postfix) with ESMTPS id A69CD55761 for ; Mon, 9 Nov 2020 18:41:15 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH 09/11] elf: Add glibc-hwcaps subdirectory support to ld.so cache processing In-Reply-To: References: Message-Id: Date: Mon, 09 Nov 2020 19:41:13 +0100 User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, 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" This recognizes the DL_CACHE_HWCAP_EXTENSION flag and picks up the supported cache entry with the highest priority. --- elf/Makefile | 18 ++ elf/dl-cache.c | 182 +++++++++++++++++- elf/dl-hwcaps.c | 78 ++++++++ elf/dl-hwcaps.h | 19 ++ elf/tst-glibc-hwcaps-cache.c | 45 +++++ .../etc/ld.so.conf | 2 + elf/tst-glibc-hwcaps-cache.root/postclean.req | 0 elf/tst-glibc-hwcaps-cache.script | 2 + elf/tst-glibc-hwcaps-prepend-cache.c | 133 +++++++++++++ .../postclean.req | 0 10 files changed, 476 insertions(+), 3 deletions(-) create mode 100644 elf/tst-glibc-hwcaps-cache.c create mode 100644 elf/tst-glibc-hwcaps-cache.root/etc/ld.so.conf create mode 100644 elf/tst-glibc-hwcaps-cache.root/postclean.req create mode 100644 elf/tst-glibc-hwcaps-cache.script create mode 100644 elf/tst-glibc-hwcaps-prepend-cache.c create mode 100644 elf/tst-glibc-hwcaps-prepend-cache.root/postclean.req diff --git a/elf/Makefile b/elf/Makefile index e26ac16b44..80e94b9ee2 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -171,6 +171,12 @@ tests-container := \ tst-ldconfig-bad-aux-cache \ tst-ldconfig-ld_so_conf-update +ifeq (no,$(build-hardcoded-path-in-tests)) +# This is an ld.so.cache test, and RPATH/RUNPATH in the executable +# interferes with its test objectives. +tests-container += tst-glibc-hwcaps-prepend-cache +endif + tests := tst-tls9 tst-leaks1 \ tst-array1 tst-array2 tst-array3 tst-array4 tst-array5 \ tst-auxv tst-stringtable @@ -1859,6 +1865,14 @@ $(objpfx)tst-glibc-hwcaps-prepend.out: \ $< > $@; \ $(evaluate-test) +# Like tst-glibc-hwcaps-prepend, but uses a container and loads the +# library via ld.so.cache. Test setup is contained in the test +# itself. +$(objpfx)tst-glibc-hwcaps-prepend-cache: $(libdl) +$(objpfx)tst-glibc-hwcaps-prepend-cache.out: \ + $(objpfx)tst-glibc-hwcaps-prepend-cache $(objpfx)libmarkermod1-1.so \ + $(objpfx)libmarkermod1-2.so $(objpfx)libmarkermod1-3.so + # tst-glibc-hwcaps-mask checks that --glibc-hwcaps-mask can be used to # suppress all auto-detected subdirectories. $(objpfx)tst-glibc-hwcaps-mask: $(objpfx)libmarkermod1-1.so @@ -1870,3 +1884,7 @@ $(objpfx)tst-glibc-hwcaps-mask.out: \ --glibc-hwcaps-mask does-not-exist \ $< > $@; \ $(evaluate-test) + +# Generic dependency for sysdeps implementation of +# tst-glibc-hwcaps-cache. +$(objpfx)tst-glibc-hwcaps-cache.out: $(objpfx)tst-glibc-hwcaps diff --git a/elf/dl-cache.c b/elf/dl-cache.c index 02c46ffb0c..13efc6f95f 100644 --- a/elf/dl-cache.c +++ b/elf/dl-cache.c @@ -35,6 +35,132 @@ static struct cache_file *cache; static struct cache_file_new *cache_new; static size_t cachesize; +#ifdef SHARED +/* This is used to cache the priorities of glibc-hwcaps + subdirectories. The elements of _dl_cache_priorities correspond to + the strings in the cache_extension_tag_glibc_hwcaps section. */ +static uint32_t *glibc_hwcaps_priorities; +static uint32_t glibc_hwcaps_priorities_length; +static uint32_t glibc_hwcaps_priorities_allocated; + +/* True if the full malloc was used to allocated the array. */ +static bool glibc_hwcaps_priorities_malloced; + +/* Deallocate the glibc_hwcaps_priorities array. */ +static void +glibc_hwcaps_priorities_free (void) +{ + /* When the minimal malloc is in use, free does not do anything, + so it does not make sense to call it. */ + if (glibc_hwcaps_priorities_malloced) + free (glibc_hwcaps_priorities); + glibc_hwcaps_priorities = NULL; + glibc_hwcaps_priorities_allocated = 0; +} + +/* Return the priority of the cache_extension_tag_glibc_hwcaps section + entry at INDEX. Zero means do not use. Otherwise, lower values + indicate greater preference. */ +static uint32_t __attribute__ ((noinline, noclone)) +glibc_hwcaps_priority (uint32_t index) +{ + /* Using a zero-length array as an indicator that nothing has been + loaded is not a problem: It does not lead to repeated + initialization attempts because caches without an extension + section are processed without calling this function (unless the + file is corrupted). */ + if (glibc_hwcaps_priorities_length == 0) + { + struct cache_extension_all_loaded ext; + if (!cache_extension_load (cache_new, cache, cachesize, &ext)) + return 0; + + uint32_t length + = (ext.sections[cache_extension_tag_glibc_hwcaps].size + / sizeof (uint32_t)); + if (length > glibc_hwcaps_priorities_allocated) + { + glibc_hwcaps_priorities_free (); + + glibc_hwcaps_priorities = malloc (length * sizeof (uint32_t)); + if (glibc_hwcaps_priorities == NULL) + /* Disable hwcaps on memory allocation error. */ + return 0; + + glibc_hwcaps_priorities_allocated = length; + glibc_hwcaps_priorities_malloced = __rtld_malloc_is_complete (); + } + + /* Compute the priorities for the subdirectories by merging the + array in the cache with the dl_hwcaps_priorities array. */ + const uint32_t *left + = ext.sections[cache_extension_tag_glibc_hwcaps].base; + const uint32_t *left_end = left + length; + struct dl_hwcaps_priority *right = _dl_hwcaps_priorities; + struct dl_hwcaps_priority *right_end + = right + _dl_hwcaps_priorities_length; + uint32_t *result = glibc_hwcaps_priorities; + + while (left < left_end && right < right_end) + { + uint32_t string_table_index = *left; + if (string_table_index < cachesize) + { + const char *left_name + = (const char *) cache + string_table_index; + uint32_t left_name_length = strlen (left_name); + uint32_t to_compare; + if (left_name_length < right->name_length) + to_compare = left_name_length; + else + to_compare = right->name_length; + int cmp = memcmp (left_name, right->name, to_compare); + if (cmp == 0) + { + if (left_name_length < right->name_length) + cmp = -1; + else if (left_name_length > right->name_length) + cmp = 1; + } + if (cmp == 0) + { + *result = right->priority; + ++result; + ++left; + ++right; + } + else if (cmp < 0) + { + *result = 0; + ++result; + ++left; + } + else + ++right; + } + else + { + *result = 0; + ++result; + } + } + while (left < left_end) + { + *result = 0; + ++result; + ++left; + } + + glibc_hwcaps_priorities_length = length; + } + + if (index < glibc_hwcaps_priorities_length) + return glibc_hwcaps_priorities[index]; + else + return 0; +} +#endif /* SHARED */ + /* True if PTR is a valid string table index. */ static inline bool _dl_cache_verify_ptr (uint32_t ptr, size_t string_table_size) @@ -74,6 +200,9 @@ search_cache (const char *string_table, uint32_t string_table_size, int left = 0; int right = nlibs - 1; const char *best = NULL; +#ifdef SHARED + uint32_t best_priority = 0; +#endif while (left <= right) { @@ -129,6 +258,11 @@ search_cache (const char *string_table, uint32_t string_table_size, { if (best == NULL || flags == GLRO (dl_correct_cache_id)) { + /* Named/extension hwcaps get slightly different + treatment: We keep searching for a better + match. */ + bool named_hwcap = false; + if (entry_size >= sizeof (struct file_entry_new)) { /* The entry is large enough to include @@ -136,7 +270,18 @@ search_cache (const char *string_table, uint32_t string_table_size, struct file_entry_new *libnew = (struct file_entry_new *) lib; - if (libnew->hwcap & hwcap_exclude) +#ifdef SHARED + named_hwcap = dl_cache_hwcap_extension (libnew); +#endif + + /* The entries with named/extension hwcaps + have been exhausted. Return the best + match encountered so far if there is + one. */ + if (!named_hwcap && best != NULL) + break; + + if ((libnew->hwcap & hwcap_exclude) && !named_hwcap) continue; if (GLRO (dl_osversion) && libnew->osversion > GLRO (dl_osversion)) @@ -146,14 +291,41 @@ search_cache (const char *string_table, uint32_t string_table_size, && ((libnew->hwcap & _DL_HWCAP_PLATFORM) != platform)) continue; + +#ifdef SHARED + /* For named hwcaps, determine the priority + and see if beats what has been found so + far. */ + if (named_hwcap) + { + uint32_t entry_priority + = glibc_hwcaps_priority (libnew->hwcap); + if (entry_priority == 0) + /* Not usable at all. Skip. */ + continue; + else if (best == NULL + || entry_priority < best_priority) + /* This entry is of higher priority + than the previous one, or it is the + first entry. */ + best_priority = entry_priority; + else + /* An entry has already been found, + but it is a better match. */ + continue; + } +#endif /* SHARED */ } best = string_table + lib->value; - if (flags == GLRO (dl_correct_cache_id)) + if (flags == GLRO (dl_correct_cache_id) + && !named_hwcap) /* We've found an exact match for the shared object and no general `ELF' release. Stop - searching. */ + searching, but not if a named (extension) + hwcap is used. In this case, an entry with + a higher priority may come up later. */ break; } } @@ -346,5 +518,9 @@ _dl_unload_cache (void) __munmap (cache, cachesize); cache = NULL; } +#ifdef SHARED + /* This marks the glibc_hwcaps_priorities array as out-of-date. */ + glibc_hwcaps_priorities_length = 0; +#endif } #endif diff --git a/elf/dl-hwcaps.c b/elf/dl-hwcaps.c index f611f3a1a6..51cc787b54 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, + int32_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. */ diff --git a/elf/dl-hwcaps.h b/elf/dl-hwcaps.h index ab39d8a46d..ddfdde278e 100644 --- a/elf/dl-hwcaps.h +++ b/elf/dl-hwcaps.h @@ -132,4 +132,23 @@ _dl_hwcaps_subdirs_build_bitmask (int subdirs, int active) return mask ^ ((1U << inactive) - 1); } +/* Pre-computed glibc-hwcaps subdirectory priorities. Used in + dl-cache.c to quickly find the proprities for the stored HWCAP + names. */ +struct dl_hwcaps_priority +{ + /* The name consists of name_length bytes at name (not necessarily + null-terminated). */ + const char *name; + uint32_t name_length; + + /* Priority of this name. A positive number. */ + uint32_t priority; +}; + +/* Pre-computed hwcaps priorities. Set up by + _dl_important_hwcaps. */ +extern struct dl_hwcaps_priority *_dl_hwcaps_priorities attribute_hidden; +extern uint32_t _dl_hwcaps_priorities_length attribute_hidden; + #endif /* _DL_HWCAPS_H */ diff --git a/elf/tst-glibc-hwcaps-cache.c b/elf/tst-glibc-hwcaps-cache.c new file mode 100644 index 0000000000..4bad56afc0 --- /dev/null +++ b/elf/tst-glibc-hwcaps-cache.c @@ -0,0 +1,45 @@ +/* Wrapper to invoke tst-glibc-hwcaps in a container, to test ld.so.cache. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* This program is just a wrapper that runs ldconfig followed by + tst-glibc-hwcaps. The actual test is provided via an + implementation in a sysdeps subdirectory. */ + +#include +#include +#include +#include + +int +main (int argc, char **argv) +{ + /* Run ldconfig to populate the cache. */ + { + char *command = xasprintf ("%s/ldconfig", support_install_rootsbindir); + if (system (command) != 0) + return 1; + free (command); + } + + /* Reuse tst-glibc-hwcaps. Since this code is running in a + container, we can launch it directly. */ + char *path = xasprintf ("%s/elf/tst-glibc-hwcaps", support_objdir_root); + execv (path, argv); + printf ("error: execv of %s failed: %m\n", path); + return 1; +} diff --git a/elf/tst-glibc-hwcaps-cache.root/etc/ld.so.conf b/elf/tst-glibc-hwcaps-cache.root/etc/ld.so.conf new file mode 100644 index 0000000000..e1e74dbda2 --- /dev/null +++ b/elf/tst-glibc-hwcaps-cache.root/etc/ld.so.conf @@ -0,0 +1,2 @@ +# This file was created to suppress a warning from ldconfig: +# /sbin/ldconfig: Warning: ignoring configuration file that cannot be opened: /etc/ld.so.conf: No such file or directory diff --git a/elf/tst-glibc-hwcaps-cache.root/postclean.req b/elf/tst-glibc-hwcaps-cache.root/postclean.req new file mode 100644 index 0000000000..e69de29bb2 diff --git a/elf/tst-glibc-hwcaps-cache.script b/elf/tst-glibc-hwcaps-cache.script new file mode 100644 index 0000000000..46cb5fd553 --- /dev/null +++ b/elf/tst-glibc-hwcaps-cache.script @@ -0,0 +1,2 @@ +# test-container does not support scripts in sysdeps directories, so +# collect everything in one file. diff --git a/elf/tst-glibc-hwcaps-prepend-cache.c b/elf/tst-glibc-hwcaps-prepend-cache.c new file mode 100644 index 0000000000..eedbf5f6df --- /dev/null +++ b/elf/tst-glibc-hwcaps-prepend-cache.c @@ -0,0 +1,133 @@ +/* Test that --glibc-hwcaps-prepend works, using dlopen and /etc/ld.so.cache. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Invoke /sbin/ldconfig with some error checking. */ +static void +run_ldconfig (void) +{ + char *command = xasprintf ("%s/ldconfig", support_install_rootsbindir); + TEST_COMPARE (system (command), 0); + free (command); +} + +/* The library under test. */ +#define SONAME "libmarkermod1.so" + +static int +do_test (void) +{ + if (dlopen (SONAME, RTLD_NOW) != NULL) + FAIL_EXIT1 (SONAME " is already on the search path"); + + /* Install the default implementation of libmarkermod1.so. */ + xmkdirp ("/etc", 0777); + support_write_file_string ("/etc/ld.so.conf", "/glibc-test/lib\n"); + xmkdirp ("/glibc-test/lib/glibc-hwcaps/prepend2", 0777); + xmkdirp ("/glibc-test/lib/glibc-hwcaps/prepend3", 0777); + { + char *src = xasprintf ("%s/elf/libmarkermod1-1.so", support_objdir_root); + support_copy_file (src, "/glibc-test/lib/" SONAME); + free (src); + } + run_ldconfig (); + { + /* The default implementation can now be loaded. */ + void *handle = xdlopen (SONAME, RTLD_NOW); + int (*marker1) (void) = xdlsym (handle, "marker1"); + TEST_COMPARE (marker1 (), 1); + xdlclose (handle); + } + + /* Add the first override to the directory that is searched last. */ + { + char *src = xasprintf ("%s/elf/libmarkermod1-2.so", support_objdir_root); + support_copy_file (src, "/glibc-test/lib/glibc-hwcaps/prepend2/" + SONAME); + free (src); + } + { + /* This is still the first implementation. The cache has not been + updated. */ + void *handle = xdlopen (SONAME, RTLD_NOW); + int (*marker1) (void) = xdlsym (handle, "marker1"); + TEST_COMPARE (marker1 (), 1); + xdlclose (handle); + } + run_ldconfig (); + { + /* After running ldconfig, it is the second implementation. */ + void *handle = xdlopen (SONAME, RTLD_NOW); + int (*marker1) (void) = xdlsym (handle, "marker1"); + TEST_COMPARE (marker1 (), 2); + xdlclose (handle); + } + + /* Add the second override to the directory that is searched first. */ + { + char *src = xasprintf ("%s/elf/libmarkermod1-3.so", support_objdir_root); + support_copy_file (src, "/glibc-test/lib/glibc-hwcaps/prepend3/" + SONAME); + free (src); + } + { + /* This is still the second implementation. */ + void *handle = xdlopen (SONAME, RTLD_NOW); + int (*marker1) (void) = xdlsym (handle, "marker1"); + TEST_COMPARE (marker1 (), 2); + xdlclose (handle); + } + run_ldconfig (); + { + /* After running ldconfig, it is the third implementation. */ + void *handle = xdlopen (SONAME, RTLD_NOW); + int (*marker1) (void) = xdlsym (handle, "marker1"); + TEST_COMPARE (marker1 (), 3); + xdlclose (handle); + } + + return 0; +} + +static void +prepare (int argc, char **argv) +{ + const char *no_restart = "no-restart"; + if (argc == 2 && strcmp (argv[1], no_restart) == 0) + return; + /* Re-execute the test with an explicit loader invocation. */ + execl (support_objdir_elf_ldso, + support_objdir_elf_ldso, + "--glibc-hwcaps-prepend", "prepend3:prepend2", + argv[0], no_restart, + NULL); + printf ("error: execv of %s failed: %m\n", argv[0]); + _exit (1); +} + +#define PREPARE prepare +#include diff --git a/elf/tst-glibc-hwcaps-prepend-cache.root/postclean.req b/elf/tst-glibc-hwcaps-prepend-cache.root/postclean.req new file mode 100644 index 0000000000..e69de29bb2 From patchwork Mon Nov 9 18:41:19 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 40978 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 2EDE63858025; Mon, 9 Nov 2020 18:41:28 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2EDE63858025 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1604947288; bh=YUy/jEFczieQt4/suvqEysw+/BSld0NZGF1rT5zNBME=; 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=FHWbh/HDxYc279LsPvqJVB3/odpNyW/DzcyZzPm4+Ca4m5LNhd+58mB3vxiYBSlDP DWT0sL6NgYL8dCSV54PUpQDai77OH4YuDn7vTwWXtmfLXiakMbM4/JFCuEs4HZlK8C YtdnhYmfgbArQtTVtUoP3Oj1wRVXJIngWuuQHHGc= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by sourceware.org (Postfix) with ESMTP id D5D9938708E1 for ; Mon, 9 Nov 2020 18:41:25 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org D5D9938708E1 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-575-MAn_h-9xNsyBYzXbiyNmDQ-1; Mon, 09 Nov 2020 13:41:22 -0500 X-MC-Unique: MAn_h-9xNsyBYzXbiyNmDQ-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id CAABE107AFC7 for ; Mon, 9 Nov 2020 18:41:21 +0000 (UTC) Received: from oldenburg2.str.redhat.com (ovpn-113-222.ams2.redhat.com [10.36.113.222]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 1C65460BF1 for ; Mon, 9 Nov 2020 18:41:20 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH 10/11] x86_64: Add glibc-hwcaps support In-Reply-To: References: Message-Id: <3ae02ef23f2981bb4a7602b4259c91b84ef8e939.1604946656.git.fweimer@redhat.com> Date: Mon, 09 Nov 2020 19:41:19 +0100 User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, 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" The subdirectories match those in the x86-64 psABI: https://gitlab.com/x86-psABIs/x86-64-ABI/-/commit/77566eb03bc6a326811cb7e9a6b9396884b67c7c Reviewed-by: Adhemerval Zanella --- elf/Makefile | 2 +- elf/tst-glibc-hwcaps-cache.script | 10 ++++ sysdeps/x86_64/Makefile | 39 +++++++++++++++ sysdeps/x86_64/dl-hwcaps-subdirs.c | 66 ++++++++++++++++++++++++++ sysdeps/x86_64/tst-glibc-hwcaps.c | 76 ++++++++++++++++++++++++++++++ 5 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 sysdeps/x86_64/dl-hwcaps-subdirs.c create mode 100644 sysdeps/x86_64/tst-glibc-hwcaps.c diff --git a/elf/Makefile b/elf/Makefile index 80e94b9ee2..0a44b19a0e 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -1826,7 +1826,7 @@ $(objpfx)argv0test.out: tst-rtld-argv0.sh $(objpfx)ld.so \ # Most likely search subdirectories, for each supported architecture. # Used to obtain test coverage wide test coverage. -glibc-hwcaps-first-subdirs-for-tests = +glibc-hwcaps-first-subdirs-for-tests = x86-64-v2 # The test modules are parameterized by preprocessor macros. LDFLAGS-libmarkermod1-1.so += -Wl,-soname,libmarkermod1.so diff --git a/elf/tst-glibc-hwcaps-cache.script b/elf/tst-glibc-hwcaps-cache.script index 46cb5fd553..2aef9fb177 100644 --- a/elf/tst-glibc-hwcaps-cache.script +++ b/elf/tst-glibc-hwcaps-cache.script @@ -1,2 +1,12 @@ # test-container does not support scripts in sysdeps directories, so # collect everything in one file. + +mkdirp 0770 $L/glibc-hwcaps/x86-64-v2 +cp $B/elf/libmarkermod2-2.so $L/glibc-hwcaps/x86-64-v2/libmarkermod2.so +mkdirp 0770 $L/glibc-hwcaps/x86-64-v3 +cp $B/elf/libmarkermod3-2.so $L/glibc-hwcaps/x86-64-v2/libmarkermod3.so +cp $B/elf/libmarkermod3-3.so $L/glibc-hwcaps/x86-64-v3/libmarkermod3.so +mkdirp 0770 $L/glibc-hwcaps/x86-64-v4 +cp $B/elf/libmarkermod4-2.so $L/glibc-hwcaps/x86-64-v2/libmarkermod4.so +cp $B/elf/libmarkermod4-3.so $L/glibc-hwcaps/x86-64-v3/libmarkermod4.so +cp $B/elf/libmarkermod4-4.so $L/glibc-hwcaps/x86-64-v4/libmarkermod4.so diff --git a/sysdeps/x86_64/Makefile b/sysdeps/x86_64/Makefile index 42b97c5cc7..d1d7cb9d2e 100644 --- a/sysdeps/x86_64/Makefile +++ b/sysdeps/x86_64/Makefile @@ -144,8 +144,47 @@ CFLAGS-tst-auditmod10b.c += $(AVX512-CFLAGS) CFLAGS-tst-avx512-aux.c += $(AVX512-CFLAGS) CFLAGS-tst-avx512mod.c += $(AVX512-CFLAGS) endif + +$(objpfx)tst-glibc-hwcaps: $(objpfx)libmarkermod2-1.so \ + $(objpfx)libmarkermod3-1.so $(objpfx)libmarkermod4-1.so +$(objpfx)tst-glibc-hwcaps.out: \ + $(objpfx)libmarkermod2.so \ + $(objpfx)glibc-hwcaps/x86-64-v2/libmarkermod2.so \ + $(objpfx)libmarkermod3.so \ + $(objpfx)glibc-hwcaps/x86-64-v2/libmarkermod3.so \ + $(objpfx)glibc-hwcaps/x86-64-v3/libmarkermod3.so \ + $(objpfx)libmarkermod4.so \ + $(objpfx)glibc-hwcaps/x86-64-v2/libmarkermod4.so \ + $(objpfx)glibc-hwcaps/x86-64-v3/libmarkermod4.so \ + $(objpfx)glibc-hwcaps/x86-64-v4/libmarkermod4.so \ + +$(objpfx)glibc-hwcaps/x86-64-v2/libmarkermod2.so: $(objpfx)libmarkermod2-2.so + $(make-target-directory) + cp $< $@ +$(objpfx)glibc-hwcaps/x86-64-v2/libmarkermod3.so: $(objpfx)libmarkermod3-2.so + $(make-target-directory) + cp $< $@ +$(objpfx)glibc-hwcaps/x86-64-v3/libmarkermod3.so: $(objpfx)libmarkermod3-3.so + $(make-target-directory) + cp $< $@ +$(objpfx)glibc-hwcaps/x86-64-v2/libmarkermod4.so: $(objpfx)libmarkermod4-2.so + $(make-target-directory) + cp $< $@ +$(objpfx)glibc-hwcaps/x86-64-v3/libmarkermod4.so: $(objpfx)libmarkermod4-3.so + $(make-target-directory) + cp $< $@ +$(objpfx)glibc-hwcaps/x86-64-v4/libmarkermod4.so: $(objpfx)libmarkermod4-4.so + $(make-target-directory) + cp $< $@ + +ifeq (no,$(build-hardcoded-path-in-tests)) +# This is an ld.so.cache test, and RPATH/RUNPATH in the executable +# interferes with its test objectives. +tests-container += tst-glibc-hwcaps-cache endif +endif # $(subdir) == elf + ifeq ($(subdir),csu) gen-as-const-headers += tlsdesc.sym rtld-offsets.sym endif diff --git a/sysdeps/x86_64/dl-hwcaps-subdirs.c b/sysdeps/x86_64/dl-hwcaps-subdirs.c new file mode 100644 index 0000000000..8810a822ef --- /dev/null +++ b/sysdeps/x86_64/dl-hwcaps-subdirs.c @@ -0,0 +1,66 @@ +/* Architecture-specific glibc-hwcaps subdirectories. x86 version. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +const char _dl_hwcaps_subdirs[] = "x86-64-v4:x86-64-v3:x86-64-v2"; +enum { subdirs_count = 3 }; /* Number of components in _dl_hwcaps_subdirs. */ + +uint32_t +_dl_hwcaps_subdirs_active (void) +{ + int active = 0; + + /* Test in reverse preference order. */ + + /* x86-64-v2. */ + if (!(CPU_FEATURE_USABLE (CMPXCHG16B) + && CPU_FEATURE_USABLE (LAHF64_SAHF64) + && CPU_FEATURE_USABLE (POPCNT) + && CPU_FEATURE_USABLE (SSE3) + && CPU_FEATURE_USABLE (SSE4_1) + && CPU_FEATURE_USABLE (SSE4_2) + && CPU_FEATURE_USABLE (SSSE3))) + return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active); + ++active; + + /* x86-64-v3. */ + if (!(CPU_FEATURE_USABLE (AVX) + && CPU_FEATURE_USABLE (AVX2) + && CPU_FEATURE_USABLE (BMI1) + && CPU_FEATURE_USABLE (BMI2) + && CPU_FEATURE_USABLE (F16C) + && CPU_FEATURE_USABLE (FMA) + && CPU_FEATURE_USABLE (LZCNT) + && CPU_FEATURE_USABLE (MOVBE) + && CPU_FEATURE_USABLE (OSXSAVE))) + return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active); + ++active; + + /* x86-64-v4. */ + if (!(CPU_FEATURE_USABLE (AVX512F) + && CPU_FEATURE_USABLE (AVX512BW) + && CPU_FEATURE_USABLE (AVX512CD) + && CPU_FEATURE_USABLE (AVX512DQ) + && CPU_FEATURE_USABLE (AVX512VL))) + return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active); + ++active; + + return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active); +} diff --git a/sysdeps/x86_64/tst-glibc-hwcaps.c b/sysdeps/x86_64/tst-glibc-hwcaps.c new file mode 100644 index 0000000000..3075a8286d --- /dev/null +++ b/sysdeps/x86_64/tst-glibc-hwcaps.c @@ -0,0 +1,76 @@ +/* glibc-hwcaps subdirectory test. x86_64 version. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include + +extern int marker2 (void); +extern int marker3 (void); +extern int marker4 (void); + +/* Return the x86-64-vN level, 1 for the baseline. */ +static int +compute_level (void) +{ + const struct cpu_features *cpu_features + = __x86_get_cpu_features (COMMON_CPUID_INDEX_MAX); + + if (!(CPU_FEATURE_USABLE_P (cpu_features, CMPXCHG16B) + && CPU_FEATURE_USABLE_P (cpu_features, LAHF64_SAHF64) + && CPU_FEATURE_USABLE_P (cpu_features, POPCNT) + && CPU_FEATURE_USABLE_P (cpu_features, MMX) + && CPU_FEATURE_USABLE_P (cpu_features, SSE) + && CPU_FEATURE_USABLE_P (cpu_features, SSE2) + && CPU_FEATURE_USABLE_P (cpu_features, SSE3) + && CPU_FEATURE_USABLE_P (cpu_features, SSSE3) + && CPU_FEATURE_USABLE_P (cpu_features, SSE4_1) + && CPU_FEATURE_USABLE_P (cpu_features, SSE4_2))) + return 1; + if (!(CPU_FEATURE_USABLE_P (cpu_features, AVX) + && CPU_FEATURE_USABLE_P (cpu_features, AVX2) + && CPU_FEATURE_USABLE_P (cpu_features, BMI1) + && CPU_FEATURE_USABLE_P (cpu_features, BMI2) + && CPU_FEATURE_USABLE_P (cpu_features, F16C) + && CPU_FEATURE_USABLE_P (cpu_features, FMA) + && CPU_FEATURE_USABLE_P (cpu_features, LZCNT) + && CPU_FEATURE_USABLE_P (cpu_features, MOVBE) + && CPU_FEATURE_USABLE_P (cpu_features, OSXSAVE))) + return 2; + if (!(CPU_FEATURE_USABLE_P (cpu_features, AVX512F) + && CPU_FEATURE_USABLE_P (cpu_features, AVX512BW) + && CPU_FEATURE_USABLE_P (cpu_features, AVX512CD) + && CPU_FEATURE_USABLE_P (cpu_features, AVX512DQ) + && CPU_FEATURE_USABLE_P (cpu_features, AVX512VL))) + return 3; + return 4; +} + +static int +do_test (void) +{ + int level = compute_level (); + printf ("info: detected x86-64 micro-architecture level: %d\n", level); + TEST_COMPARE (marker2 (), MIN (level, 2)); + TEST_COMPARE (marker3 (), MIN (level, 3)); + TEST_COMPARE (marker4 (), MIN (level, 4)); + return 0; +} + +#include From patchwork Mon Nov 9 18:41:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 40979 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 2A4FA3896C38; Mon, 9 Nov 2020 18:41:35 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2A4FA3896C38 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1604947295; bh=iQECaJCSdKgxXMu8eSxK7MuZS2stXm5EqNbW2Hc5EdU=; 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=PM1GUlqZ9P7zO9mIGdceR1QOWU/EsFaemlg6kuF3gIXdcIxbtfwPUz2R3E30PczmS Yixxv+7CEa9ZBh5IvT6lfvPw30P88e/DlAKsgjkRrWG58j/SORbZnR2ZBDwK9F81/u TJZDOSr8Vnqa2wx39f1MWk5+AKJFHaiiyoCn8Dh8= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by sourceware.org (Postfix) with ESMTP id F21E93894C17 for ; Mon, 9 Nov 2020 18:41:32 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org F21E93894C17 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-251-RWUjJ8tOPee7w0-kYDYjlA-1; Mon, 09 Nov 2020 13:41:30 -0500 X-MC-Unique: RWUjJ8tOPee7w0-kYDYjlA-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 0759A8015A8 for ; Mon, 9 Nov 2020 18:41:29 +0000 (UTC) Received: from oldenburg2.str.redhat.com (ovpn-113-222.ams2.redhat.com [10.36.113.222]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 4CA3E19D7C for ; Mon, 9 Nov 2020 18:41:28 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH 11/11] powerpc64le: Add glibc-hwcaps support In-Reply-To: References: Message-Id: <38719cefeb0737c1640f749177e6f0902890cf3d.1604946656.git.fweimer@redhat.com> Date: Mon, 09 Nov 2020 19:41:26 +0100 User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, 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" The "power10" and "power9" subdirectories are selected in a way that matches the -mcpu=power10 and -mcpu=power9 options of GCC. Reviewed-by: Adhemerval Zanella --- elf/Makefile | 2 +- elf/tst-glibc-hwcaps-cache.script | 6 +++ sysdeps/powerpc/powerpc64/le/Makefile | 28 ++++++++++ .../powerpc/powerpc64/le/dl-hwcaps-subdirs.c | 46 ++++++++++++++++ .../powerpc/powerpc64/le/tst-glibc-hwcaps.c | 54 +++++++++++++++++++ 5 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c create mode 100644 sysdeps/powerpc/powerpc64/le/tst-glibc-hwcaps.c diff --git a/elf/Makefile b/elf/Makefile index 0a44b19a0e..60e8662e5d 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -1826,7 +1826,7 @@ $(objpfx)argv0test.out: tst-rtld-argv0.sh $(objpfx)ld.so \ # Most likely search subdirectories, for each supported architecture. # Used to obtain test coverage wide test coverage. -glibc-hwcaps-first-subdirs-for-tests = x86-64-v2 +glibc-hwcaps-first-subdirs-for-tests = power9 x86-64-v2 # The test modules are parameterized by preprocessor macros. LDFLAGS-libmarkermod1-1.so += -Wl,-soname,libmarkermod1.so diff --git a/elf/tst-glibc-hwcaps-cache.script b/elf/tst-glibc-hwcaps-cache.script index 2aef9fb177..a3e3317bbf 100644 --- a/elf/tst-glibc-hwcaps-cache.script +++ b/elf/tst-glibc-hwcaps-cache.script @@ -10,3 +10,9 @@ mkdirp 0770 $L/glibc-hwcaps/x86-64-v4 cp $B/elf/libmarkermod4-2.so $L/glibc-hwcaps/x86-64-v2/libmarkermod4.so cp $B/elf/libmarkermod4-3.so $L/glibc-hwcaps/x86-64-v3/libmarkermod4.so cp $B/elf/libmarkermod4-4.so $L/glibc-hwcaps/x86-64-v4/libmarkermod4.so + +mkdirp 0770 $L/glibc-hwcaps/power9 +cp $B/elf/libmarkermod2-2.so $L/glibc-hwcaps/power9/libmarkermod2.so +mkdirp 0770 $L/glibc-hwcaps/power10 +cp $B/elf/libmarkermod3-2.so $L/glibc-hwcaps/power9/libmarkermod3.so +cp $B/elf/libmarkermod3-3.so $L/glibc-hwcaps/power10/libmarkermod3.so diff --git a/sysdeps/powerpc/powerpc64/le/Makefile b/sysdeps/powerpc/powerpc64/le/Makefile index 033dc77b01..7c036b45fc 100644 --- a/sysdeps/powerpc/powerpc64/le/Makefile +++ b/sysdeps/powerpc/powerpc64/le/Makefile @@ -188,3 +188,31 @@ ifeq ($(subdir),nptl) CFLAGS-tst-thread_local1.cc += -mno-float128 CFLAGS-tst-minstack-throw.cc += -mno-float128 endif + +ifeq ($(subdir),elf) +$(objpfx)tst-glibc-hwcaps: \ + $(objpfx)libmarkermod2-1.so $(objpfx)libmarkermod3-1.so +$(objpfx)tst-glibc-hwcaps.out: \ + $(objpfx)libmarkermod2.so \ + $(objpfx)glibc-hwcaps/power9/libmarkermod2.so \ + $(objpfx)libmarkermod3.so \ + $(objpfx)glibc-hwcaps/power9/libmarkermod3.so \ + $(objpfx)glibc-hwcaps/power10/libmarkermod3.so \ + +$(objpfx)glibc-hwcaps/power9/libmarkermod2.so: $(objpfx)libmarkermod2-2.so + $(make-target-directory) + cp $< $@ +$(objpfx)glibc-hwcaps/power9/libmarkermod3.so: $(objpfx)libmarkermod3-2.so + $(make-target-directory) + cp $< $@ +$(objpfx)glibc-hwcaps/power10/libmarkermod3.so: $(objpfx)libmarkermod3-3.so + $(make-target-directory) + cp $< $@ + +ifeq (no,$(build-hardcoded-path-in-tests)) +# This is an ld.so.cache test, and RPATH/RUNPATH in the executable +# interferes with its test objectives. +tests-container += tst-glibc-hwcaps-cache +endif + +endif # $(subdir) == elf diff --git a/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c b/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c new file mode 100644 index 0000000000..0ce76c2fe5 --- /dev/null +++ b/sysdeps/powerpc/powerpc64/le/dl-hwcaps-subdirs.c @@ -0,0 +1,46 @@ +/* Architecture-specific glibc-hwcaps subdirectories. powerpc64le version. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +const char _dl_hwcaps_subdirs[] = "power10:power9"; +enum { subdirs_count = 2 }; /* Number of components in _dl_hwcaps_subdirs. */ + +uint32_t +_dl_hwcaps_subdirs_active (void) +{ + int active = 0; + + /* Test in reverse preference order. Altivec and VSX are implied by + the powerpc64le ABI definition. */ + + /* POWER9. GCC enables float128 hardware support for -mcpu=power9. */ + if ((GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_00) == 0 + || (GLRO (dl_hwcap2) & PPC_FEATURE2_HAS_IEEE128) == 0) + return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active); + ++active; + + /* POWER10. GCC defines __MMA__ for -mcpu=power10. */ + if ((GLRO (dl_hwcap2) & PPC_FEATURE2_ARCH_3_1) == 0 + || (GLRO (dl_hwcap2) & PPC_FEATURE2_MMA) == 0) + return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active); + ++active; + + return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active); +} diff --git a/sysdeps/powerpc/powerpc64/le/tst-glibc-hwcaps.c b/sysdeps/powerpc/powerpc64/le/tst-glibc-hwcaps.c new file mode 100644 index 0000000000..e510fca80a --- /dev/null +++ b/sysdeps/powerpc/powerpc64/le/tst-glibc-hwcaps.c @@ -0,0 +1,54 @@ +/* glibc-hwcaps subdirectory test. powerpc64le version. + Copyright (C) 2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include + +extern int marker2 (void); +extern int marker3 (void); + +/* Return the POWER level, 8 for the baseline. */ +static int +compute_level (void) +{ + const char *platform = (const char *) getauxval (AT_PLATFORM); + if (strcmp (platform, "power8") == 0) + return 8; + if (strcmp (platform, "power9") == 0) + return 9; + if (strcmp (platform, "power10") == 0) + return 10; + printf ("warning: unrecognized AT_PLATFORM value: %s\n", platform); + /* Assume that the new platform supports POWER10. */ + return 10; +} + +static int +do_test (void) +{ + int level = compute_level (); + printf ("info: detected POWER level: %d\n", level); + TEST_COMPARE (marker2 (), MIN (level - 7, 2)); + TEST_COMPARE (marker3 (), MIN (level - 7, 3)); + return 0; +} + +#include