From patchwork Mon Dec 9 11:18:03 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 102664 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 A699F385828E for ; Mon, 9 Dec 2024 11:22:42 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org A699F385828E Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=c0aoO/gK X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@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 ESMTP id 143FB3858289 for ; Mon, 9 Dec 2024 11:18:21 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 143FB3858289 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 143FB3858289 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1733743101; cv=none; b=ex5+AMtp+8qTRMIQzLJ/NSH2F5JICBLjjy1nzl8Dbc6+j1QSxuCNRHdl2fbFwDiyoibDhwf+yD/QhCT5k/QBWtBP0CJSmwzKzaaiT015mX2vPCWmA1qMw/vGOwXyRpF6+zDg+JBr+hFM3kelT39AvsSYAJjbsUS7hPVBV5eIAAQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1733743101; c=relaxed/simple; bh=vy9U1ApXEzFlMQCjQOnKVn8Md4yAccYPNu1Kh6BouW8=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=PNNAmk4L3VgEKCVFN5vh4MSry626PInOOnggZX2WNPbqD6wxsy2k4U4bDo6SFyxwBvgbD8MES2rEp6vxchkoy0OgyDjGA6fQ8QwoKbWgjWXJHtLqEZtqB6gre/3KSgKpNEE81p/Bj4VYkSRqJm19LT0QdFoC1eJajj6Te3AQ5ds= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 143FB3858289 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1733743100; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=kTs3ZhuoAuC4ypAC9z6n5W3Lm18sr1hoC5VjmFpRKtQ=; b=c0aoO/gKPm+A4AmuFKAmn/pril6r/N4m172721x0ne2I62RiGyER4bC0eHw7J4rvck3/hV GDeF5FAAnPTeO23xqCtFSDpjRR+G2yL17K1dXQ6vORE2cyW9xJ6j0e3fdMm5u5laQ1tU36 A+Z+5aLoTcyTDfe4o4sgkCSuXye99Vs= Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-527-YcGvfauoPLShgVMRgHbl4g-1; Mon, 09 Dec 2024 06:18:19 -0500 X-MC-Unique: YcGvfauoPLShgVMRgHbl4g-1 X-Mimecast-MFC-AGG-ID: YcGvfauoPLShgVMRgHbl4g Received: by mail-wr1-f72.google.com with SMTP id ffacd0b85a97d-385e3cbf308so763914f8f.2 for ; Mon, 09 Dec 2024 03:18:19 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1733743097; x=1734347897; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=kTs3ZhuoAuC4ypAC9z6n5W3Lm18sr1hoC5VjmFpRKtQ=; b=KJF3lphaVRgAlb+8MmcJzPRoIpvHd/D2oB67KUcx0ZQyjoVRdnc0fHKVXXpEUw5kgS qxl9YltijaZVF2JTTseUlmmmelErmbczrvLv2o6le9R5nfibtNIjPCwRrCZjlY4b6Suv leYZhroqkzZSxmw8YlbzdvQsegHWbz96AOkj2Z9zpjntbLGajQENUC8Z9m4TToCauWnf UTNe5PlTbCpT/G9J5ucSWunq1h6Mbh3pX033PeROUThj76jCjwiTXgulp7Q0oIPaRNzV JdlvQI9jFmwdyMfVBpMij8BkKt7M12haoV+dy53cQW+YEeAoyXDtVxpBAeaF3/tJ0NOC MTYQ== X-Gm-Message-State: AOJu0YxAOLsFyY1g9cG+daOW1bB+Zaf165ARJUDpDhnqiy7xH1OC0EDB f5E5pWw/I2C+JGKCrIHYzxLK6v2xMa7gBpLBzO4t+Ghp4R6bzWilKAnzGHYTcOtPX3Wi7+2W5Nq Uhfl688J3WHF/7GxkkKsaAwtINkbRwru2yZZCbup6Mf/StyxQIpvjH1X9LhqwkduZOpunJ0erSw /nQJCHZsyUX+PfzKhUfI+ETTsfJkJ5cans6e+d8fbveMk= X-Gm-Gg: ASbGncv2yRd8HK30eTF6afPc4xJMsh/UYM/rQ+OB6ypAYKd4bJm3hJw6t/2HT425eE6 pdcFJn+5OJ3CV0yKDb/T83RbkxLcvoixOIGenptfNidtxb3Gc+U4idwRWuYmt8+GC2NwogE1Ixb Sq89eZYw5I9XI9s/W9jDwVbnpz3dEgngmhhV8VdLCOoHTRgpVARf8nWHWnl+ZvelhXXzzntgCsV G713Mv2x0e7JewRjsjYCdSBWBTO6Bg+673AuffegzG+u7wESo5Yvz5QVD9oH0MiMwJ4Peci3RPA 6g== X-Received: by 2002:a5d:6f03:0:b0:385:e961:6589 with SMTP id ffacd0b85a97d-3862b351329mr7604870f8f.20.1733743097161; Mon, 09 Dec 2024 03:18:17 -0800 (PST) X-Google-Smtp-Source: AGHT+IFUn59dLOKO0Z5SSCXsbpYdPfrzcnfslLpWXMxLLfoBCaCiR7xvv0z9OirwuFYJpTZrxch8WA== X-Received: by 2002:a5d:6f03:0:b0:385:e961:6589 with SMTP id ffacd0b85a97d-3862b351329mr7604848f8f.20.1733743096598; Mon, 09 Dec 2024 03:18:16 -0800 (PST) Received: from localhost (197.209.200.146.dyn.plus.net. [146.200.209.197]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-38638415730sm5698640f8f.21.2024.12.09.03.18.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 09 Dec 2024 03:18:16 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess , Hannes Domani Subject: [PATCHv5 6/7] gdb: disable internal b/p when a solib is unloaded Date: Mon, 9 Dec 2024 11:18:03 +0000 Message-Id: X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: _WsVENX7wnTAqrp6GgAwJd3rUBL5W2sqJ-QuaIHus5s_1733743099 X-Mimecast-Originator: redhat.com content-type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-12.0 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP 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: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~patchwork=sourceware.org@sourceware.org Bug PR gdb/32079 highlights an issue where GDB will try to remove a breakpoint for a shared library that has been unloaded. This will trigger an error from GDB like: (gdb) next 61 dlclose (handle[dl]); (gdb) next warning: error removing breakpoint 0 at 0x7ffff78169b9 warning: error removing breakpoint 0 at 0x7ffff7730b57 warning: error removing breakpoint 0 at 0x7ffff7730ad3 54 for (dl = 0; dl < 4; ++dl) (gdb) What happens is that as the inferior steps over the dlclose() call GDB notices that the library has been unloaded and calls disable_breakpoints_in_unloaded_shlib. However, this function only operates on user breakpoints and tracepoints. In the example above what is happening is that the test loads multiple copies of libc into different linker namespsaces. When we 'next' over the dlclose call one of the copies of libc is unloaded. As GDB placed longjmp master breakpoints within the copy of libc that was just unloaded, the warnings we see are GDB trying (and failing) to remove these breakpoints. I think the solution is for disable_breakpoints_in_unloaded_shlib to handle all breakpoints, even internal ones like the longjmp master breakpoints. If we do this then the breakpoint will be marked as shlib_disabled and also will be marked as not inserted. Later when we call breakpoint_re_set() and the longjmp breakpoints are deleted we will no longer try to remove them. This solution is inspired by a patch suggested in the bug report: https://sourceware.org/bugzilla/show_bug.cgi?id=32079#c3 There are some differences with my approach compared to the patch suggested in the bug. First I have no need to delete the breakpoint inside disable_breakpoints_in_unloaded_shlib as an earlier patch in this series arranged for breakpoint_re_set to be called when shared libraries are removed. Calling breakpoint_re_set will take care of deleting the breakpoint for us. For details see the earlier commit titled: gdb: fixes for code_breakpoint::disabled_by_cond logic Next, rather than only handling bp_longjmp and bp_longjmp_master, I allow all breakpoints to be handled. I also only give the warning about disabling breakpoints for user breakpoints, I don't see the point of warning the user about internal b/p changes. The biggest change though is that I've added some code to detect shared libraries that appear to be mapped multiple times. Here's the 'info sharedlibrary' output for the binary for gdb.base/dlmopen.exp after all the shared libraries have been loaded: 61 dlclose (handle[dl]); (gdb) info sharedlibrary From To Syms Read Shared Object Library 0x00007ffff7fca000 0x00007ffff7ff03f5 Yes /lib64/ld-linux-x86-64.so.2 0x00007ffff7edb3d0 0x00007ffff7f4f898 Yes (*) /lib64/libm.so.6 0x00007ffff7d0f800 0x00007ffff7e6eacd Yes (*) /lib64/libc.so.6 0x00007ffff7fb8040 0x00007ffff7fb80f9 Yes /tmp/build/gdb/testsuite/outputs/gdb.base/dlmopen/dlmopen-lib-dep.so 0x00007ffff7c003d0 0x00007ffff7c74898 Yes (*) /lib64/libm.so.6 0x00007ffff7a34800 0x00007ffff7b93acd Yes (*) /lib64/libc.so.6 0x00007ffff7fca000 0x00007ffff7ff03f5 Yes /lib64/ld-linux-x86-64.so.2 0x00007ffff7ce2040 0x00007ffff7ce2116 Yes /tmp/build/gdb/testsuite/outputs/gdb.base/dlmopen/dlmopen-lib.1.so 0x00007ffff7cdd040 0x00007ffff7cdd0f9 Yes /tmp/build/gdb/testsuite/outputs/gdb.base/dlmopen/dlmopen-lib-dep.so 0x00007ffff79283d0 0x00007ffff799c898 Yes (*) /lib64/libm.so.6 0x00007ffff775c800 0x00007ffff78bbacd Yes (*) /lib64/libc.so.6 0x00007ffff7fca000 0x00007ffff7ff03f5 Yes /lib64/ld-linux-x86-64.so.2 0x00007ffff7cd8040 0x00007ffff7cd8116 Yes /tmp/build/gdb/testsuite/outputs/gdb.base/dlmopen/dlmopen-lib.2.so (*): Shared library is missing debugging information. (gdb) The important thing to spot here is /lib64/ld-linux-x86-64.so.2. This library appears 3 times in the list but is at the same address in each case. This indicates three copies of this library mapped into three different linker namespaces, but the linker has spotted a single instance of the library can be shared. When the inferior calls dlclose() GDB will still see an event for this library being unloaded. If we disable all the b/p within this library on the first event then we end up leaving software b/p in the code as the library isn't really unmapped at this point. What we need to do is spot that the library is mapped multiple times and only perform the b/p disable logic if this is the last instance of the library. With this done the issues in PR gdb/32079 are resolved. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32079 Tested-By: Hannes Domani --- gdb/breakpoint.c | 39 ++++++++++++--- gdb/testsuite/gdb.base/dlmopen.exp | 76 ++++++++++++++++++++++++------ 2 files changed, 95 insertions(+), 20 deletions(-) diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 2ef4a3b2b8d..c6bd6f68363 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -8077,17 +8077,44 @@ disable_breakpoints_in_shlibs (program_space *pspace) static void disable_breakpoints_in_unloaded_shlib (program_space *pspace, const solib &solib) { + /* A shared library can appear twice on the runtime-linkers shared + library list, but the shared library is actually mapped in only once. + This can happen when linker namespaces are used. When one of those + mappings is removed this function is called. If we then disable and + mark as removed all the b/p within the library then we'll end up + leaving software breakpoints in the code (as the library was never + actually unmapped). + + To prevent this, we check the shared library list. If we find some + other shared library with the same objfile then we can leave the + breakpoints alone. + + If a shared library is mapped a second time, but at a different + address, then the shared library will be given a new objfile. */ + for (const struct solib &so : pspace->solibs ()) + { + if (&solib == &so) + continue; + + /* SOLIB and SO are two different solib objects, but, if they share + the same objfile then there is really only a single mapped + instance of the library. + + If SOLIB has no objfile then it will have no target sections (as + target sections are derived from the objfile) and so the + solib_contains_address_p call below will always return false, thus + we don't need to worry about the case where neither SOLIB and SO + have no objfile. */ + if (solib.objfile != nullptr && solib.objfile == so.objfile) + return; + } + bool disabled_shlib_breaks = false; for (breakpoint &b : all_breakpoints ()) { bool bp_modified = false; - if (b.type != bp_jit_event - && !is_breakpoint (&b) - && !is_tracepoint (&b)) - continue; - for (bp_location &loc : b.locations ()) { if (pspace != loc.pspace || loc.shlib_disabled) @@ -8121,7 +8148,7 @@ disable_breakpoints_in_unloaded_shlib (program_space *pspace, const solib &solib bp_modified = true; - if (!disabled_shlib_breaks) + if (!disabled_shlib_breaks && user_breakpoint_p (&b)) { target_terminal::ours_for_output (); warning (_("Temporarily disabling breakpoints " diff --git a/gdb/testsuite/gdb.base/dlmopen.exp b/gdb/testsuite/gdb.base/dlmopen.exp index 264c5180f3f..f0a29280d8b 100644 --- a/gdb/testsuite/gdb.base/dlmopen.exp +++ b/gdb/testsuite/gdb.base/dlmopen.exp @@ -144,27 +144,75 @@ gdb_breakpoint $srcfile:$bp_main test_dlmopen # Try the same again when attaching after dlmopen(). -require can_spawn_for_attach +if { [can_spawn_for_attach] } { -clean_restart $binfile + clean_restart $binfile -# Start the test program. -set test_spawn_id [spawn_wait_for_attach $binfile] -set testpid [spawn_id_get_pid $test_spawn_id] + # Start the test program. + set test_spawn_id [spawn_wait_for_attach $binfile] + set testpid [spawn_id_get_pid $test_spawn_id] -# Attach. -if { ![gdb_attach $testpid] } { - return + # Attach. + if { ![gdb_attach $testpid] } { + return + } + + with_test_prefix "attach" { + # Remove the pause. We no longer need it. + gdb_test "print wait_for_gdb = 0" "\\\$1 = 0" + + # Set the same breakpoints again. This time, however, we do not allow the + # breakpoint to be pending since the library has already been loaded. + gdb_breakpoint $srcfile_lib:$bp_inc + gdb_breakpoint $srcfile:$bp_main + + test_dlmopen + } } -with_test_prefix "attach" { +# Check that we can 'next' over the dlclose calls without GDB giving any +# warnings or errors. +with_test_prefix "next over dlclose" { + clean_restart $binfile + + if { ![runto_main] } { + return + } + + set dlclose_lineno [gdb_get_line_number "dlclose" $srcfile] + gdb_breakpoint $srcfile:$dlclose_lineno + gdb_breakpoint $srcfile:$bp_main + # Remove the pause. We no longer need it. gdb_test "print wait_for_gdb = 0" "\\\$1 = 0" - # Set the same breakpoints again. This time, however, we do not allow the - # breakpoint to be pending since the library has already been loaded. - gdb_breakpoint $srcfile_lib:$bp_inc - gdb_breakpoint $srcfile:$bp_main + set loc_re [multi_line \ + "\[^\r\n\]+/[string_to_regexp $srcfile]:$dlclose_lineno" \ + "$dlclose_lineno\\s+dlclose \[^\r\n\]+"] + + # This loop mirrors the loop in dlmopen.c where the inferior performs + # four calls to dlclose. Here we continue to the dlclose, and then use + # 'next' to step over the call. As part of the 'next' GDB will insert + # breakpoints to catch longjmps into the multiple copies of libc which + # have been loaded. Each dlclose call will cause a copy of libc to be + # unloaded, which should disable the longjmp breakpoint that GDB + # previously inserted. + # + # At one point a bug in GDB meant that we failed to correctly disable + # the longjmp breakpoints and GDB would try to remove the breakpoint + # from a library after it had been unloaded, which would trigger a + # warning. + for { set i 0 } { $i < 4 } { incr i } { + gdb_continue_to_breakpoint "continue to dlclose $i" $loc_re + gdb_test "next" "^$decimal\\s+for \\(dl = 0;\[^\r\n\]+\\)" \ + "next over dlclose $i" + } - test_dlmopen + # Just to confirm that we are where we think we are, continue to the + # final 'return' line in main. If this isn't hit then we likely went + # wrong earlier. + gdb_continue_to_breakpoint "continue to final return" \ + [multi_line \ + "\[^\r\n\]+/[string_to_regexp $srcfile]:$bp_main" \ + "$bp_main\\s+return 0;\[^\r\n\]+"] }