From patchwork Mon May 22 11:32:39 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 1784461 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=sourceware.org; envelope-from=libc-alpha-bounces+incoming=patchwork.ozlabs.org@sourceware.org; receiver=) Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.a=rsa-sha256 header.s=default header.b=PIFO17dQ; dkim-atps=neutral Received: from sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4QPwL65cLGz20PY for ; Mon, 22 May 2023 21:33:02 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 8E24E3858D35 for ; Mon, 22 May 2023 11:33:00 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 8E24E3858D35 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1684755180; bh=N/ucQ8g5+vbSf0KnHjZCd4rRcw42I1Gn9yEFBf8EsOM=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=PIFO17dQerZOB11SDl81RWNsBJGcTVCAl4NvhQI3A1ckIMemkCHsvO+yRx26VYriH ZT99oQh1Xe7mbnDoMQLXZW6IujEMoTHICFQbMv5CG2fnrClyVBYmLiieICdjwX+kLQ 7ELG5C9RVzlAMWGenc03XSEk7DjAvZZv4P1HzNAw= 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 [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id B70B13858D35 for ; Mon, 22 May 2023 11:32:43 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org B70B13858D35 Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-433-7WLjOT5cP0K79WvPPB-94g-1; Mon, 22 May 2023 07:32:42 -0400 X-MC-Unique: 7WLjOT5cP0K79WvPPB-94g-1 Received: from smtp.corp.redhat.com (int-mx09.intmail.prod.int.rdu2.redhat.com [10.11.54.9]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id C46E43C02B67 for ; Mon, 22 May 2023 11:32:41 +0000 (UTC) Received: from oldenburg3.str.redhat.com (unknown [10.39.192.96]) by smtp.corp.redhat.com (Postfix) with ESMTPS id ECF03482060 for ; Mon, 22 May 2023 11:32:40 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH v2] elf: Make more functions available for binding during dlclose (bug 30425) Date: Mon, 22 May 2023 13:32:39 +0200 Message-ID: <87mt1w4n54.fsf@oldenburg3.str.redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/28.2 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.9 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.8 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_H2, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) 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+incoming=patchwork.ozlabs.org@sourceware.org Sender: "Libc-alpha" Previously, after destructors for a DSO have been invoked, ld.so refused to bind against that DSO in all cases. Relax this restriction somewhat if the referencing object is itself a DSO that is being unloaded. This assumes that the symbol reference is not going to be stored anywhere. The situation in the test case can arise fairly easily with C++ and objects that are built with different optimization levels and therefore define different functions with vague linkage. --- v2: Expanded comment in elf/dl-lookup.c. elf/Makefile | 8 ++++++++ elf/dl-lookup.c | 21 +++++++++++++++++-- elf/tst-dlclose-lazy-mod1.c | 35 ++++++++++++++++++++++++++++++++ elf/tst-dlclose-lazy-mod2.c | 49 +++++++++++++++++++++++++++++++++++++++++++++ elf/tst-dlclose-lazy.c | 36 +++++++++++++++++++++++++++++++++ 5 files changed, 147 insertions(+), 2 deletions(-) base-commit: e1b02c5ed4099a53db8f356303fc0ef88db8a131 diff --git a/elf/Makefile b/elf/Makefile index e262f3e6b1..be487b9cfb 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -394,6 +394,7 @@ tests += \ tst-debug1 \ tst-deep1 \ tst-dl-is_dso \ + tst-dlclose-lazy \ tst-dlmodcount \ tst-dlmopen-dlerror \ tst-dlmopen-gethostbyname \ @@ -813,6 +814,8 @@ modules-names += \ tst-dl_find_object-mod7 \ tst-dl_find_object-mod8 \ tst-dl_find_object-mod9 \ + tst-dlclose-lazy-mod1 \ + tst-dlclose-lazy-mod2 \ tst-dlmopen-dlerror-mod \ tst-dlmopen-gethostbyname-mod \ tst-dlmopen-twice-mod1 \ @@ -2997,3 +3000,8 @@ $(objpfx)tst-sprof-basic.out: tst-sprof-basic.sh $(objpfx)tst-sprof-basic '$(run-program-env)' > $@; \ $(evaluate-test) generated += tst-sprof-mod.so.profile + +LDFLAGS-tst-dlclose-lazy-mod1.so = -Wl,-z,lazy,--no-as-needed +$(objpfx)tst-dlclose-lazy-mod1.so: $(objpfx)tst-dlclose-lazy-mod2.so +$(objpfx)tst-dlclose-lazy.out: \ + $(objpfx)tst-dlclose-lazy-mod1.so $(objpfx)tst-dlclose-lazy-mod2.so diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c index 05f36a2507..a8f48fed12 100644 --- a/elf/dl-lookup.c +++ b/elf/dl-lookup.c @@ -366,8 +366,25 @@ do_lookup_x (const char *undef_name, unsigned int new_hash, if ((type_class & ELF_RTYPE_CLASS_COPY) && map->l_type == lt_executable) continue; - /* Do not look into objects which are going to be removed. */ - if (map->l_removed) + /* Do not look into objects which are going to be removed, + except when the referencing object itself is being removed. + + The second part covers the situation when an object lazily + binds to another object while running its destructor, but the + destructor of the other object has already run, so that + dlclose has set l_removed. It may not always be obvious how + to avoid such a scenario to programmers creating DSOs, + particularly if C++ vague linkage is involved and triggers + symbol interposition. + + Accepting these to-be-removed objects makes the lazy and + BIND_NOW cases more similar. (With BIND_NOW, the symbol is + resolved early, before the destructor call, so the issue does + not arise.). Behavior matches the constructor scenario: the + implementation allows binding to symbols of objects whose + constructors have not run. In fact, not doing this would be + mostly incompatible with symbol interposition. */ + if (map->l_removed && !(undef_map != NULL && undef_map->l_removed)) continue; /* Print some debugging info if wanted. */ diff --git a/elf/tst-dlclose-lazy-mod1.c b/elf/tst-dlclose-lazy-mod1.c new file mode 100644 index 0000000000..5535a6b9e0 --- /dev/null +++ b/elf/tst-dlclose-lazy-mod1.c @@ -0,0 +1,35 @@ +/* Lazy binding during dlclose. Directly loaded module. + Copyright (C) 2023 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 function is called from exported_function below. It is only + defined in this module. */ +void __attribute__ ((weak)) +lazily_bound_exported_function (void) +{ +} + +/* Called from tst-dlclose-lazy-mod2.so. */ +void +exported_function (int call_it) +{ + if (call_it) + /* This used crash if called during dlcose because after invoking + the destructor as part of dlclose, symbols were no longer + available for binding (bug 30425). */ + lazily_bound_exported_function (); +} diff --git a/elf/tst-dlclose-lazy-mod2.c b/elf/tst-dlclose-lazy-mod2.c new file mode 100644 index 0000000000..767f69ffdb --- /dev/null +++ b/elf/tst-dlclose-lazy-mod2.c @@ -0,0 +1,49 @@ +/* Lazy binding during dlclose. Indirectly loaded module. + Copyright (C) 2023 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 + +void +exported_function (int ignored) +{ + /* This function is interposed from tst-dlclose-lazy-mod1.so and + thus never called. */ + abort (); +} + +static void __attribute__ ((constructor)) +init (void) +{ + puts ("info: tst-dlclose-lazy-mod2.so constructor called"); + + /* Trigger lazy binding to the definition in + tst-dlclose-lazy-mod1.so, but not for + lazily_bound_exported_function in that module. */ + exported_function (0); +} + +static void __attribute__ ((destructor)) +fini (void) +{ + puts ("info: tst-dlclose-lazy-mod2.so destructor called"); + + /* Trigger the lazily_bound_exported_function call in + exported_function in tst-dlclose-lazy-mod1.so. */ + exported_function (1); +} diff --git a/elf/tst-dlclose-lazy.c b/elf/tst-dlclose-lazy.c new file mode 100644 index 0000000000..8207d134f3 --- /dev/null +++ b/elf/tst-dlclose-lazy.c @@ -0,0 +1,36 @@ +/* Test lazy binding during dlclose (bug 30425). + Copyright (C) 2023 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 + +int +main (void) +{ + /* Load tst-dlclose-lazy-mod1.so, indirectly loading + tst-dlclose-lazy-mod2.so. */ + void *handle = xdlopen ("tst-dlclose-lazy-mod1.so", RTLD_GLOBAL | RTLD_LAZY); + + /* Invoke the destructor of tst-dlclose-lazy-mod2.so, which calls + into tst-dlclose-lazy-mod1.so after its destructor has been + called. */ + xdlclose (handle); + + return 0; +}