Message ID | 20221021174205.5389-2-asaffisher.dev@gmail.com |
---|---|
State | New |
Headers |
Return-Path: <gdb-patches-bounces+patchwork=sourceware.org@sourceware.org> 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 D2F1C385277E for <patchwork@sourceware.org>; Fri, 21 Oct 2022 17:42:38 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D2F1C385277E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1666374158; bh=V2zRdGSmaYeIqPbCIt0h9Qco99f0gY02zNpwRGuY2qc=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=Q+X30jQ3CZXSZNpZsRUuH4NRzthlVPHkd8fKxQ7JDZbI11/MKSm1MSujhUa3nGPL5 sZFKIWFc2hc22YgIEnHMpQlt4IVTzw3imi6oRItsX6AikolDiXkuZgfWfihrhyWHZx J3xN7XmPk7lXEyvwoyZ07TG1yl1Vnzr9qGuAAneQ= X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from mail-ed1-x530.google.com (mail-ed1-x530.google.com [IPv6:2a00:1450:4864:20::530]) by sourceware.org (Postfix) with ESMTPS id F3D563856974 for <gdb-patches@sourceware.org>; Fri, 21 Oct 2022 17:42:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org F3D563856974 Received: by mail-ed1-x530.google.com with SMTP id g27so8584734edf.11 for <gdb-patches@sourceware.org>; Fri, 21 Oct 2022 10:42:11 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; 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=V2zRdGSmaYeIqPbCIt0h9Qco99f0gY02zNpwRGuY2qc=; b=buGs2IE69BORHhrcS81VP80Qsq7nxJTSHCFSw7iTMVTkQIgyonnoaGoRD2PScfz0cc Bg/tYmZsaN5JCncFByNHGKZZcDlfhuD4fHnkJWJoPeR8sr/Ru8X6u9nNdTtWA9JPCe1m eQp+aOesyQ5tDNcgSCniqzCG+nmBu3hWumtQSsJnIh5PwEVut5AKzPO2iYKbmJaX9W1W 8Qde+sUtoDV4cMzbP5tiYBSeDrhgZGwXru+WYuLBikkSYssH91GvNS1tczXIn7C3Nq5E Np79lcrBmzkKhgqQ9+eK13H/XpAk1UP+QZbexFWvff+5nbR616MzWuOtjSUkAeTRYsZc uPVg== X-Gm-Message-State: ACrzQf29UnDLYsQCWPEa2QFCDgDkzf17nUsBRRUDtBqLctUD/yY8cvSd J7XjW8OPDdTiuNICJ2RBs81egGpI3zaAyg== X-Google-Smtp-Source: AMsMyM556Z5IRhoXCGBgvDo+Wg/52fHHFmT623h0nYqtH5SBsdGDQODxHWvzv7+r489KC5c4+44lMg== X-Received: by 2002:a50:ff09:0:b0:456:fd61:83b3 with SMTP id a9-20020a50ff09000000b00456fd6183b3mr18055083edu.166.1666374129654; Fri, 21 Oct 2022 10:42:09 -0700 (PDT) Received: from codespaces-99e6ae.cwehd3ikdxye1e5jw03kh0ngkf.ax.internal.cloudapp.net ([104.40.192.11]) by smtp.gmail.com with ESMTPSA id 1-20020a170906200100b007933047f930sm3593150ejo.157.2022.10.21.10.42.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Oct 2022 10:42:09 -0700 (PDT) To: gdb-patches@sourceware.org Subject: [PATCH v3 2/2] Make GDB resolve dlopen of memory mapped shared libraries Date: Fri, 21 Oct 2022 17:42:05 +0000 Message-Id: <20221021174205.5389-2-asaffisher.dev@gmail.com> X-Mailer: git-send-email 2.38.0 In-Reply-To: <20221021174205.5389-1-asaffisher.dev@gmail.com> References: <20221021174205.5389-1-asaffisher.dev@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-12.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, 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.29 Precedence: list List-Id: Gdb-patches mailing list <gdb-patches.sourceware.org> List-Unsubscribe: <https://sourceware.org/mailman/options/gdb-patches>, <mailto:gdb-patches-request@sourceware.org?subject=unsubscribe> List-Archive: <https://sourceware.org/pipermail/gdb-patches/> List-Post: <mailto:gdb-patches@sourceware.org> List-Help: <mailto:gdb-patches-request@sourceware.org?subject=help> List-Subscribe: <https://sourceware.org/mailman/listinfo/gdb-patches>, <mailto:gdb-patches-request@sourceware.org?subject=subscribe> From: Asaf Fisher via Gdb-patches <gdb-patches@sourceware.org> Reply-To: Asaf Fisher <asaffisher.dev@gmail.com> Cc: Asaf Fisher <asaffisher.dev@gmail.com> Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" <gdb-patches-bounces+patchwork=sourceware.org@sourceware.org> |
Series |
[v3,1/2] Add test to check GDB handles dlopen of /proc/self/fd/[num] correctly
|
|
Commit Message
Asaf Fisher
Oct. 21, 2022, 5:42 p.m. UTC
Introduced `check_proc_self_file` that checks if a path used by inferior in dlopen is in the form of `/proc/self/...` and if so resolves it to `/proc/[pid]/...` Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=29586 --- gdb/solib-svr4.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-)
Comments
Asaf Fisher via Gdb-patches <gdb-patches@sourceware.org> writes: > Introduced `check_proc_self_file` that checks if a path used by > inferior in dlopen is in the form of `/proc/self/...` and if so resolves > it to `/proc/[pid]/...` > > Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=29586 Hi Asaf, Thanks for providing a fix for this, and thanks for submitting the copyright assignment paperwork. I took a look through your patch and ended up tweaking it a little. I've attached a revised version below, which I'd love to hear your feedback on. When making changes my goal was to extend your fix to work with gdbserver just like it works with native targets. You can test different gdbserver setups like this: make check-gdb TESTS="gdb.base/solib-proc-self.exp" \ RUNTESTFLAGS="--target_board=native-extended-gdbserver" This will setup gdbserver and connect GDB to it with 'target extended-remote', you can also test using the 'native-gdbserver' board, which will connect to gdbserver as just 'target remote'. Ideally any new test will pass in all three configurations (the default plus the two listed above), and initially, your change only fixed the native target case. By moving the fix elsewhere in the shared library loading process I think I new have all three cases working. I also extended the test to test using a 'target:' sysroot. By default when testing with the two gdbserver boards above, we set the sysroot to "" (the empty string), this tells GDB that remote files can be found on the local machine, and avoids all files accesses having to go over the remote protocol. However, given the nature of this change, I figured it was worth testing with the 'target:' sysroot too, this means we re-run the tests bu sending all file accesses over the remote protocol. That case is also fixed with the patch below. Where I'd most appreciate your feedback is for the algorithm by which the /proc/self path is spotted and converted to /proc/PID. I've cut the code back a bit from what you had originally, mostly because I couldn't find a way to test that the extra complexity was required. If you have any additional test cases that could show that the slimmed down code is not good enough, then that would be great. Thanks, Andrew --- commit 0bf4c98bc225c89a0a1ddcea727eca178ad69710 Author: Asaf Fisher <asaffisher.dev@gmail.com> Date: Fri Oct 21 17:42:05 2022 +0000 gdb: handle loading shared libraries from /proc/self/fd/ Bug PR gdb/29586 describes a situations where a shared library is created in memory, then written to a memory mapped file. The memory mapped file will show up as a file descriptor within /proc/self/fd/, and this path is then used with dlopen in order to call functions within the in-memory shared library. When attempting to debug this GDB hangs. The problem is that, GDB stops at the shared-library event, reads the shared library path from the inferior, which is "/proc/self/fd/<NUM>", and then GDB attempts to open this file. Unfortunately, this means GDB tries to open a file within GDB's /proc/self/fd/ directory, not within the inferior's directory. In the case of our hang it turns out that the file descriptor that is opened is a pipe, and GDB hangs trying to read from the pipe. However, the behaviour is really just undefined, depending on which file descriptor the inferior tries to open, GDB will open, or fail to open, random files within its /proc/self/fd directory. The solution proposed in this commit is to hook into solib_find_1, and spot when GDB is looking for any file in /proc/self/, if this is the case, then the filename is rewritten as /proc/<PID>, where <PID> is the process-id of the current inferior. The test passes for the unix, native-gdbserver, and native-extended-gdbserver targets. When testing with either of the gdbserver targets, the test is run using the default empty sysroot, and also using the 'target:' sysroot. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=29586 Co-authored-by: Andrew Burgess <aburgess@redhat.com> diff --git a/gdb/solib.c b/gdb/solib.c index 7cfdd81114c..cf2d0d3bc3a 100644 --- a/gdb/solib.c +++ b/gdb/solib.c @@ -83,6 +83,35 @@ show_solib_search_path (struct ui_file *file, int from_tty, # define DOS_BASED_FILE_SYSTEM 0 #endif +/* Fix references to files in /proc/self/fd/ when opening a shared library. + + SO_NAME is the name of the shared library being loaded. This function + returns a possibly modified name which should be used as the path to the + shared library. + + If SO_NAME starts with /proc/self, then the returned name will be + modified to start with /proc/PID where 'PID' is the pid of the current + inferior. */ + +static gdb::unique_xmalloc_ptr<char> +filter_proc_self_filenames (gdb::unique_xmalloc_ptr<char> so_name) +{ + static const char *proc_self_prefix = "/proc/self"; + + /* Is the path really a /proc/self? */ + if (!startswith (so_name.get (), proc_self_prefix)) + return so_name; + + /* Get the part of the path after /proc/self. For example given + '/proc/self/fd' we find the '/fd' part. */ + gdb_assert (strlen (so_name.get ()) >= strlen (proc_self_prefix)); + const char *tail = so_name.get () + strlen (proc_self_prefix); + + /* Build a replacement path. */ + int inferior_pid = inferior_ptid.pid (); + return xstrprintf ("/proc/%d%s", inferior_pid, tail); +} + /* Return the full pathname of a binary file (the main executable or a shared library file), or NULL if not found. If FD is non-NULL, *FD is set to either -1 or an open file handle for the binary file. @@ -172,6 +201,12 @@ solib_find_1 (const char *in_pathname, int *fd, bool is_solib) } } + /* If the path starts /proc/self then rewrite this as /proc/PID using the + current inferior's pid. Failing to do this will cause GDB to try and + open files within its proc directory, rather than the inferiors. */ + temp_pathname.reset (xstrdup (in_pathname)); + temp_pathname = filter_proc_self_filenames (std::move (temp_pathname)); + /* Note, we're interested in IS_TARGET_ABSOLUTE_PATH, not IS_ABSOLUTE_PATH. The latter is for host paths only, while IN_PATHNAME is a target path. For example, if we're supposed to @@ -184,9 +219,7 @@ solib_find_1 (const char *in_pathname, int *fd, bool is_solib) 3rd attempt, c:/foo/bar.dll ==> /sysroot/foo/bar.dll */ - if (!IS_TARGET_ABSOLUTE_PATH (fskind, in_pathname) || sysroot == NULL) - temp_pathname.reset (xstrdup (in_pathname)); - else + if (IS_TARGET_ABSOLUTE_PATH (fskind, in_pathname) && sysroot != NULL) { bool need_dir_separator; @@ -213,7 +246,7 @@ solib_find_1 (const char *in_pathname, int *fd, bool is_solib) /* Cat the prefixed pathname together. */ temp_pathname.reset (concat (sysroot, need_dir_separator ? SLASH_STRING : "", - in_pathname, (char *) NULL)); + temp_pathname.get (), (char *) NULL)); } /* Handle files to be accessed via the target. */ diff --git a/gdb/testsuite/gdb.base/solib-proc-self.cc b/gdb/testsuite/gdb.base/solib-proc-self.cc new file mode 100644 index 00000000000..f2439d738a3 --- /dev/null +++ b/gdb/testsuite/gdb.base/solib-proc-self.cc @@ -0,0 +1,73 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2022 Free Software Foundation, Inc. + + 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; either version 3 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 <http://www.gnu.org/licenses/>. */ + +#include <sys/mman.h> +#include <stdio.h> +#include <stdlib.h> +#include <iostream> +#include <fstream> +#include <sstream> +#include <vector> +#include <unistd.h> + +#ifdef __WIN32__ +#include <windows.h> +#define dlopen(name, mode) LoadLibrary (name) +#define dlclose(handle) FreeLibrary (handle) +#define dlerror() "an error occurred" +#else +#include <dlfcn.h> +#endif + +int +main () +{ + /* Read the shared libraries contents into a buffer. */ + std::ifstream read_so_file = std::ifstream (SHLIB_NAME); + read_so_file.seekg (0, std::ios::end); + std::streamsize size = read_so_file.tellg (); + read_so_file.seekg (0, std::ios::beg); + std::vector<char> buffer (size); + if (!read_so_file.read (buffer.data (), size)) + { + fprintf (stderr, "Failed to load solib\n"); + exit (1); + } + + /* Create a memory mapped file, then write the shared library to that + new memory mapped file. */ + int mem_fd = memfd_create ("test", 0); + write (mem_fd, buffer.data (), buffer.size ()); + + /* Generate the /proc/self/fd/[num] path for the memory mapped file. */ + std::string proc_self_fd_path; /* break-here */ + std::stringstream proc_self_fd_path_stream + = std::stringstream (proc_self_fd_path); + proc_self_fd_path_stream << "/proc/self/fd/" << mem_fd; + + /* Call dlopen on it. */ + void *handle = dlopen (proc_self_fd_path_stream.str ().c_str (), RTLD_LAZY); + if (!handle) + { + fprintf (stderr, "%s\n", dlerror ()); + exit (1); + } + /* It worked. */ + dlclose (handle); + + return 0; +} diff --git a/gdb/testsuite/gdb.base/solib-proc-self.exp b/gdb/testsuite/gdb.base/solib-proc-self.exp new file mode 100644 index 00000000000..1c845822490 --- /dev/null +++ b/gdb/testsuite/gdb.base/solib-proc-self.exp @@ -0,0 +1,130 @@ +# Copyright 2022 Free Software Foundation, Inc. + +# 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; either version 3 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 <http://www.gnu.org/licenses/>. */ + +# Test connecting and disconnecting at shared library events. + +if {[skip_shlib_tests]} { + untested "could not run to main" + return 0 +} + +standard_testfile .cc + +# Reuse an existing library, we don't care about the library contents +# for this test. +set libfile so-disc-shr +set libsrc "${srcdir}/${subdir}/${libfile}.c" +set libname "${libfile}.so" +set libobj [standard_output_file ${libname}] + +# Compile the shared library. +if { [gdb_compile_shlib $libsrc $libobj {debug}] != ""} { + return -1 +} + +# Compile the test executable. +if [ build_executable "failed to prepare" $testfile $srcfile \ + [list shlib_load debug c++ additional_flags=-DSHLIB_NAME="${libobj}"]] { + return -1 +} + +# Start GDB and run to the point where the test program tries to dlopen a file +# from within /proc/self/fd/. Catch the shared library event and check that +# we actually try to load a file from /proc/<INFERIOR-PID>/fd/. +# +# If SYSROOT is not the empty string, then this is set as the value of GDB's +# sysroot immediately after starting GDB. The only value that is (currently) +# supported, other than the empty string, is 'target:'. +proc do_test { {sysroot ""} } { + clean_restart $::binfile + + if {$sysroot != ""} { + gdb_test_no_output "set sysroot ${sysroot}" + } + + gdb_load_shlib $::libobj + + if ![runto_main] then { + return 0 + } + + # Get inferior's PID for later. + set inferior_pid [get_inferior_pid] + + # Turn on the solib-events so we can see that gdb resolves everything + # correctly. + gdb_test_no_output "set stop-on-solib-events 1" + + # Run to the 'break-here' marker. + gdb_breakpoint [gdb_get_line_number "break-here"] + gdb_continue_to_breakpoint "break-here" ".* break-here .*" + + set memfd "" + gdb_test_multiple "p mem_fd" "Get file descriptor" { + -re -wrap "\\\$$::decimal = (\[^\r\n\]*)" { + set memfd $expect_out(1,string) + pass $gdb_test_name + } + } + + gdb_test "continue" "Stopped due to shared library event.*" \ + "continue to load" + + # Check if inferior resolved the /proc/self/fd/[num] to /proc/[pid]/fd/[num]. + gdb_test "continue" \ + [multi_line \ + "Stopped due to shared library event:" \ + " Inferior loaded (?:target:)?/proc/${inferior_pid}/fd/$memfd"] +} + +# First run of the test. +do_test + +# Possible second run of the test. If we are using a remote target then we +# should consider setting the sysroot to 'target:' and re-running the test. +if {[target_info exists gdb_protocol] + && ([target_info gdb_protocol] == "remote" + || [target_info gdb_protocol] == "extended-remote")} { + # GDB will already be running after the first call to do_test, so we can + # take a peek at the current sysroot setting, and decide if we should + # repeat the test with a different setting. + + set new_sysroot "" + gdb_test_multiple "show sysroot" "" { + -wrap -re "The current system root is \"\"\\." { + pass $gdb_test_name + + # Repeat the test with 'target:' sysroot. + set new_sysroot "target:" + } + -wrap -re "The current system root is \"target:\"\\." { + pass $gdb_test_name + + # Nothing else to do, we already tested with target: sysroot. + } + -re "$gdb_prompt $" { + pass $gdb_test_name + + # If already testing with any other sysroot, we probably should + # not try to adjust things, so don't do any further testing. + } + } + + with_test_prefix "sysroot $new_sysroot" { + if { $new_sysroot != "" } { + do_test $new_sysroot + } + } +}
Hi Asaf and Andrew, Thanks for working on this! I have included comments below in the patch. On 10/11/2022 19:37, Andrew Burgess via Gdb-patches wrote: > Asaf Fisher via Gdb-patches<gdb-patches@sourceware.org> writes: > >> Introduced `check_proc_self_file` that checks if a path used by >> inferior in dlopen is in the form of `/proc/self/...` and if so resolves >> it to `/proc/[pid]/...` >> >> Bug:https://sourceware.org/bugzilla/show_bug.cgi?id=29586 > Hi Asaf, > > Thanks for providing a fix for this, and thanks for submitting the > copyright assignment paperwork. > > I took a look through your patch and ended up tweaking it a little. > I've attached a revised version below, which I'd love to hear your > feedback on. > > When making changes my goal was to extend your fix to work with > gdbserver just like it works with native targets. You can test > different gdbserver setups like this: > > make check-gdb TESTS="gdb.base/solib-proc-self.exp" \ > RUNTESTFLAGS="--target_board=native-extended-gdbserver" > > This will setup gdbserver and connect GDB to it with 'target > extended-remote', you can also test using the 'native-gdbserver' board, > which will connect to gdbserver as just 'target remote'. > > Ideally any new test will pass in all three configurations (the default > plus the two listed above), and initially, your change only fixed the > native target case. By moving the fix elsewhere in the shared library > loading process I think I new have all three cases working. > > I also extended the test to test using a 'target:' sysroot. By default > when testing with the two gdbserver boards above, we set the sysroot to > "" (the empty string), this tells GDB that remote files can be found on > the local machine, and avoids all files accesses having to go over the > remote protocol. However, given the nature of this change, I figured it > was worth testing with the 'target:' sysroot too, this means we re-run > the tests bu sending all file accesses over the remote protocol. That > case is also fixed with the patch below. > > Where I'd most appreciate your feedback is for the algorithm by which > the /proc/self path is spotted and converted to /proc/PID. I've cut the > code back a bit from what you had originally, mostly because I couldn't > find a way to test that the extra complexity was required. If you have > any additional test cases that could show that the slimmed down code is > not good enough, then that would be great. > > Thanks, > Andrew > > --- > > commit 0bf4c98bc225c89a0a1ddcea727eca178ad69710 > Author: Asaf Fisher<asaffisher.dev@gmail.com> > Date: Fri Oct 21 17:42:05 2022 +0000 > > gdb: handle loading shared libraries from /proc/self/fd/ > > Bug PR gdb/29586 describes a situations where a shared library is > created in memory, then written to a memory mapped file. The memory > mapped file will show up as a file descriptor within /proc/self/fd/, > and this path is then used with dlopen in order to call functions > within the in-memory shared library. > > When attempting to debug this GDB hangs. The problem is that, GDB > stops at the shared-library event, reads the shared library path from > the inferior, which is "/proc/self/fd/<NUM>", and then GDB attempts to > open this file. > > Unfortunately, this means GDB tries to open a file within GDB's > /proc/self/fd/ directory, not within the inferior's directory. In the > case of our hang it turns out that the file descriptor that is opened > is a pipe, and GDB hangs trying to read from the pipe. > > However, the behaviour is really just undefined, depending on which > file descriptor the inferior tries to open, GDB will open, or fail to > open, random files within its /proc/self/fd directory. > > The solution proposed in this commit is to hook into solib_find_1, and > spot when GDB is looking for any file in /proc/self/, if this is the > case, then the filename is rewritten as /proc/<PID>, where <PID> is > the process-id of the current inferior. > > The test passes for the unix, native-gdbserver, and > native-extended-gdbserver targets. When testing with either of the > gdbserver targets, the test is run using the default empty sysroot, > and also using the 'target:' sysroot. > > Bug:https://sourceware.org/bugzilla/show_bug.cgi?id=29586 > gdb.base/solib-proc-self.exp > Co-authored-by: Andrew Burgess<aburgess@redhat.com> > > diff --git a/gdb/solib.c b/gdb/solib.c > index 7cfdd81114c..cf2d0d3bc3a 100644 > --- a/gdb/solib.c > +++ b/gdb/solib.c > @@ -83,6 +83,35 @@ show_solib_search_path (struct ui_file *file, int from_tty, > # define DOS_BASED_FILE_SYSTEM 0 > #endif > > +/* Fix references to files in /proc/self/fd/ when opening a shared library. > + > + SO_NAME is the name of the shared library being loaded. This function > + returns a possibly modified name which should be used as the path to the > + shared library. > + > + If SO_NAME starts with /proc/self, then the returned name will be > + modified to start with /proc/PID where 'PID' is the pid of the current > + inferior. */ > + > +static gdb::unique_xmalloc_ptr<char> > +filter_proc_self_filenames (gdb::unique_xmalloc_ptr<char> so_name) > +{ > + static const char *proc_self_prefix = "/proc/self"; > + > + /* Is the path really a /proc/self? */ > + if (!startswith (so_name.get (), proc_self_prefix)) > + return so_name; > + > + /* Get the part of the path after /proc/self. For example given > + '/proc/self/fd' we find the '/fd' part. */ > + gdb_assert (strlen (so_name.get ()) >= strlen (proc_self_prefix)); I am not sure how this assert can ever fail as the test just before checked that so_name starts with proc_self_prefix. How can it have a smaller length? > + const char *tail = so_name.get () + strlen (proc_self_prefix); > + > + /* Build a replacement path. */ > + int inferior_pid = inferior_ptid.pid (); > + return xstrprintf ("/proc/%d%s", inferior_pid, tail); > +} > + > /* Return the full pathname of a binary file (the main executable or a > shared library file), or NULL if not found. If FD is non-NULL, *FD > is set to either -1 or an open file handle for the binary file. > @@ -172,6 +201,12 @@ solib_find_1 (const char *in_pathname, int *fd, bool is_solib) > } > } > > + /* If the path starts /proc/self then rewrite this as /proc/PID using the > + current inferior's pid. Failing to do this will cause GDB to try and > + open files within its proc directory, rather than the inferiors. */ > + temp_pathname.reset (xstrdup (in_pathname)); > + temp_pathname = filter_proc_self_filenames (std::move (temp_pathname)); > + > /* Note, we're interested in IS_TARGET_ABSOLUTE_PATH, not > IS_ABSOLUTE_PATH. The latter is for host paths only, while > IN_PATHNAME is a target path. For example, if we're supposed to > @@ -184,9 +219,7 @@ solib_find_1 (const char *in_pathname, int *fd, bool is_solib) > 3rd attempt, c:/foo/bar.dll ==> /sysroot/foo/bar.dll > */ > > - if (!IS_TARGET_ABSOLUTE_PATH (fskind, in_pathname) || sysroot == NULL) > - temp_pathname.reset (xstrdup (in_pathname)); > - else > + if (IS_TARGET_ABSOLUTE_PATH (fskind, in_pathname) && sysroot != NULL) Just a nit, but you could s/NULL/nullptr while at updating this line > { > bool need_dir_separator; > > @@ -213,7 +246,7 @@ solib_find_1 (const char *in_pathname, int *fd, bool is_solib) > /* Cat the prefixed pathname together. */ > temp_pathname.reset (concat (sysroot, > need_dir_separator ? SLASH_STRING : "", > - in_pathname, (char *) NULL)); > + temp_pathname.get (), (char *) NULL)); Same here. Also, could nullptr make it so the "(char *)" cast becomes useless? > } > > /* Handle files to be accessed via the target. */ > diff --git a/gdb/testsuite/gdb.base/solib-proc-self.cc b/gdb/testsuite/gdb.base/solib-proc-self.cc > new file mode 100644 > index 00000000000..f2439d738a3 > --- /dev/null > +++ b/gdb/testsuite/gdb.base/solib-proc-self.cc > @@ -0,0 +1,73 @@ > +/* This testcase is part of GDB, the GNU debugger. > + > + Copyright 2022 Free Software Foundation, Inc. > + > + 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; either version 3 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<http://www.gnu.org/licenses/>. */ > + > +#include <sys/mman.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <iostream> > +#include <fstream> > +#include <sstream> > +#include <vector> > +#include <unistd.h> > + > +#ifdef __WIN32__ > +#include <windows.h> > +#define dlopen(name, mode) LoadLibrary (name) > +#define dlclose(handle) FreeLibrary (handle) > +#define dlerror() "an error occurred" > +#else > +#include <dlfcn.h> > +#endif > + > +int > +main () > +{ > + /* Read the shared libraries contents into a buffer. */ > + std::ifstream read_so_file = std::ifstream (SHLIB_NAME); > + read_so_file.seekg (0, std::ios::end); > + std::streamsize size = read_so_file.tellg (); > + read_so_file.seekg (0, std::ios::beg); > + std::vector<char> buffer (size); > + if (!read_so_file.read (buffer.data (), size)) > + { > + fprintf (stderr, "Failed to load solib\n"); > + exit (1); > + } > + > + /* Create a memory mapped file, then write the shared library to that > + new memory mapped file. */ > + int mem_fd = memfd_create ("test", 0); > + write (mem_fd, buffer.data (), buffer.size ()); > + > + /* Generate the /proc/self/fd/[num] path for the memory mapped file. */ > + std::string proc_self_fd_path; /* break-here */ > + std::stringstream proc_self_fd_path_stream > + = std::stringstream (proc_self_fd_path); > + proc_self_fd_path_stream << "/proc/self/fd/" << mem_fd; > + > + /* Call dlopen on it. */ > + void *handle = dlopen (proc_self_fd_path_stream.str ().c_str (), RTLD_LAZY); > + if (!handle) > + { > + fprintf (stderr, "%s\n", dlerror ()); > + exit (1); > + } > + /* It worked. */ > + dlclose (handle); > + > + return 0; > +} > diff --git a/gdb/testsuite/gdb.base/solib-proc-self.exp b/gdb/testsuite/gdb.base/solib-proc-self.exp > new file mode 100644 > index 00000000000..1c845822490 > --- /dev/null > +++ b/gdb/testsuite/gdb.base/solib-proc-self.exp > @@ -0,0 +1,130 @@ > +# Copyright 2022 Free Software Foundation, Inc. > + > +# 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; either version 3 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<http://www.gnu.org/licenses/>. */ > + > +# Test connecting and disconnecting at shared library events. > + > +if {[skip_shlib_tests]} { > + untested "could not run to main" > + return 0 > +} > + > +standard_testfile .cc > + > +# Reuse an existing library, we don't care about the library contents > +# for this test. > +set libfile so-disc-shr > +set libsrc "${srcdir}/${subdir}/${libfile}.c" > +set libname "${libfile}.so" > +set libobj [standard_output_file ${libname}] > + > +# Compile the shared library. > +if { [gdb_compile_shlib $libsrc $libobj {debug}] != ""} { > + return -1 > +} > + > +# Compile the test executable. > +if [ build_executable "failed to prepare" $testfile $srcfile \ > + [list shlib_load debug c++ additional_flags=-DSHLIB_NAME="${libobj}"]] { > + return -1 > +} > + > +# Start GDB and run to the point where the test program tries to dlopen a file > +# from within /proc/self/fd/. Catch the shared library event and check that > +# we actually try to load a file from /proc/<INFERIOR-PID>/fd/. > +# > +# If SYSROOT is not the empty string, then this is set as the value of GDB's > +# sysroot immediately after starting GDB. The only value that is (currently) > +# supported, other than the empty string, is 'target:'. > +proc do_test { {sysroot ""} } { > + clean_restart $::binfile > + > + if {$sysroot != ""} { > + gdb_test_no_output "set sysroot ${sysroot}" > + } > + > + gdb_load_shlib $::libobj > + > + if ![runto_main] then { > + return 0 > + } > + > + # Get inferior's PID for later. > + set inferior_pid [get_inferior_pid] > + > + # Turn on the solib-events so we can see that gdb resolves everything > + # correctly. > + gdb_test_no_output "set stop-on-solib-events 1" > + > + # Run to the 'break-here' marker. > + gdb_breakpoint [gdb_get_line_number "break-here"] > + gdb_continue_to_breakpoint "break-here" ".* break-here .*" > + > + set memfd "" > + gdb_test_multiple "p mem_fd" "Get file descriptor" { > + -re -wrap "\\\$$::decimal = (\[^\r\n\]*)" { > + set memfd $expect_out(1,string) > + pass $gdb_test_name > + } > + } > + > + gdb_test "continue" "Stopped due to shared library event.*" \ > + "continue to load" I've been running this patch (as modified by Andrew) and see a failure in the tests. (gdb) PASS: gdb.base/solib-proc-self.exp: continue to breakpoint: break-here p mem_fd $1 = 4 (gdb) PASS: gdb.base/solib-proc-self.exp: Get file descriptor continue Continuing. Stopped due to shared library event: Inferior loaded /proc/7331/fd/4 (gdb) PASS: gdb.base/solib-proc-self.exp: continue to load continue Continuing. Stopped due to shared library event (no libraries added or removed) (gdb) FAIL: gdb.base/solib-proc-self.exp: continue I believe this "continue" should be removed. The test passes cleanly without it. Best, Lancelot. > + > + # Check if inferior resolved the /proc/self/fd/[num] to /proc/[pid]/fd/[num]. > + gdb_test "continue" \ > + [multi_line \ > + "Stopped due to shared library event:" \ > + " Inferior loaded (?:target:)?/proc/${inferior_pid}/fd/$memfd"] > +} > + > +# First run of the test. > +do_test > + > +# Possible second run of the test. If we are using a remote target then we > +# should consider setting the sysroot to 'target:' and re-running the test. > +if {[target_info exists gdb_protocol] > + && ([target_info gdb_protocol] == "remote" > + || [target_info gdb_protocol] == "extended-remote")} { > + # GDB will already be running after the first call to do_test, so we can > + # take a peek at the current sysroot setting, and decide if we should > + # repeat the test with a different setting. > + > + set new_sysroot "" > + gdb_test_multiple "show sysroot" "" { > + -wrap -re "The current system root is \"\"\\." { > + pass $gdb_test_name > + > + # Repeat the test with 'target:' sysroot. > + set new_sysroot "target:" > + } > + -wrap -re "The current system root is \"target:\"\\." { > + pass $gdb_test_name > + > + # Nothing else to do, we already tested with target: sysroot. > + } > + -re "$gdb_prompt $" { > + pass $gdb_test_name > + > + # If already testing with any other sysroot, we probably should > + # not try to adjust things, so don't do any further testing. > + } > + } > + > + with_test_prefix "sysroot $new_sysroot" { > + if { $new_sysroot != "" } { > + do_test $new_sysroot > + } > + } > +} >
I’ll be able to review it right after the 22nd of November. On Fri, Nov 11, 2022 at 2:35 PM Dr Lancelot SIX <lsix@lancelotsix.com> wrote: > Hi Asaf and Andrew, > > Thanks for working on this! I have included comments below in the patch. > On 10/11/2022 19:37, Andrew Burgess via Gdb-patches wrote: > > Asaf Fisher via Gdb-patches <gdb-patches@sourceware.org> <gdb-patches@sourceware.org> writes: > > > Introduced `check_proc_self_file` that checks if a path used by > inferior in dlopen is in the form of `/proc/self/...` and if so resolves > it to `/proc/[pid]/...` > > Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=29586 > > Hi Asaf, > > Thanks for providing a fix for this, and thanks for submitting the > copyright assignment paperwork. > > I took a look through your patch and ended up tweaking it a little. > I've attached a revised version below, which I'd love to hear your > feedback on. > > When making changes my goal was to extend your fix to work with > gdbserver just like it works with native targets. You can test > different gdbserver setups like this: > > make check-gdb TESTS="gdb.base/solib-proc-self.exp" \ > RUNTESTFLAGS="--target_board=native-extended-gdbserver" > > This will setup gdbserver and connect GDB to it with 'target > extended-remote', you can also test using the 'native-gdbserver' board, > which will connect to gdbserver as just 'target remote'. > > Ideally any new test will pass in all three configurations (the default > plus the two listed above), and initially, your change only fixed the > native target case. By moving the fix elsewhere in the shared library > loading process I think I new have all three cases working. > > I also extended the test to test using a 'target:' sysroot. By default > when testing with the two gdbserver boards above, we set the sysroot to > "" (the empty string), this tells GDB that remote files can be found on > the local machine, and avoids all files accesses having to go over the > remote protocol. However, given the nature of this change, I figured it > was worth testing with the 'target:' sysroot too, this means we re-run > the tests bu sending all file accesses over the remote protocol. That > case is also fixed with the patch below. > > Where I'd most appreciate your feedback is for the algorithm by which > the /proc/self path is spotted and converted to /proc/PID. I've cut the > code back a bit from what you had originally, mostly because I couldn't > find a way to test that the extra complexity was required. If you have > any additional test cases that could show that the slimmed down code is > not good enough, then that would be great. > > Thanks, > Andrew > > --- > > commit 0bf4c98bc225c89a0a1ddcea727eca178ad69710 > Author: Asaf Fisher <asaffisher.dev@gmail.com> <asaffisher.dev@gmail.com> > Date: Fri Oct 21 17:42:05 2022 +0000 > > gdb: handle loading shared libraries from /proc/self/fd/ > > Bug PR gdb/29586 describes a situations where a shared library is > created in memory, then written to a memory mapped file. The memory > mapped file will show up as a file descriptor within /proc/self/fd/, > and this path is then used with dlopen in order to call functions > within the in-memory shared library. > > When attempting to debug this GDB hangs. The problem is that, GDB > stops at the shared-library event, reads the shared library path from > the inferior, which is "/proc/self/fd/<NUM>", and then GDB attempts to > open this file. > > Unfortunately, this means GDB tries to open a file within GDB's > /proc/self/fd/ directory, not within the inferior's directory. In the > case of our hang it turns out that the file descriptor that is opened > is a pipe, and GDB hangs trying to read from the pipe. > > However, the behaviour is really just undefined, depending on which > file descriptor the inferior tries to open, GDB will open, or fail to > open, random files within its /proc/self/fd directory. > > The solution proposed in this commit is to hook into solib_find_1, and > spot when GDB is looking for any file in /proc/self/, if this is the > case, then the filename is rewritten as /proc/<PID>, where <PID> is > the process-id of the current inferior. > > The test passes for the unix, native-gdbserver, and > native-extended-gdbserver targets. When testing with either of the > gdbserver targets, the test is run using the default empty sysroot, > and also using the 'target:' sysroot. > > Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=29586 > > gdb.base/solib-proc-self.exp > Co-authored-by: Andrew Burgess <aburgess@redhat.com> <aburgess@redhat.com> > > > diff --git a/gdb/solib.c b/gdb/solib.c > index 7cfdd81114c..cf2d0d3bc3a 100644 > --- a/gdb/solib.c > +++ b/gdb/solib.c > @@ -83,6 +83,35 @@ show_solib_search_path (struct ui_file *file, int from_tty, > # define DOS_BASED_FILE_SYSTEM 0 > #endif > > +/* Fix references to files in /proc/self/fd/ when opening a shared library. > + > + SO_NAME is the name of the shared library being loaded. This function > + returns a possibly modified name which should be used as the path to the > + shared library. > + > + If SO_NAME starts with /proc/self, then the returned name will be > + modified to start with /proc/PID where 'PID' is the pid of the current > + inferior. */ > + > +static gdb::unique_xmalloc_ptr<char> > +filter_proc_self_filenames (gdb::unique_xmalloc_ptr<char> so_name) > +{ > + static const char *proc_self_prefix = "/proc/self"; > + > + /* Is the path really a /proc/self? */ > + if (!startswith (so_name.get (), proc_self_prefix)) > + return so_name; > + > + /* Get the part of the path after /proc/self. For example given > + '/proc/self/fd' we find the '/fd' part. */ > + gdb_assert (strlen (so_name.get ()) >= strlen (proc_self_prefix)); > > I am not sure how this assert can ever fail as the test just before > checked that so_name starts with proc_self_prefix. How can it have a > smaller length? > > + const char *tail = so_name.get () + strlen (proc_self_prefix); > + > + /* Build a replacement path. */ > + int inferior_pid = inferior_ptid.pid (); > + return xstrprintf ("/proc/%d%s", inferior_pid, tail); > +} > + > /* Return the full pathname of a binary file (the main executable or a > shared library file), or NULL if not found. If FD is non-NULL, *FD > is set to either -1 or an open file handle for the binary file. > @@ -172,6 +201,12 @@ solib_find_1 (const char *in_pathname, int *fd, bool is_solib) > } > } > > + /* If the path starts /proc/self then rewrite this as /proc/PID using the > + current inferior's pid. Failing to do this will cause GDB to try and > + open files within its proc directory, rather than the inferiors. */ > + temp_pathname.reset (xstrdup (in_pathname)); > + temp_pathname = filter_proc_self_filenames (std::move (temp_pathname)); > + > /* Note, we're interested in IS_TARGET_ABSOLUTE_PATH, not > IS_ABSOLUTE_PATH. The latter is for host paths only, while > IN_PATHNAME is a target path. For example, if we're supposed to > @@ -184,9 +219,7 @@ solib_find_1 (const char *in_pathname, int *fd, bool is_solib) > 3rd attempt, c:/foo/bar.dll ==> /sysroot/foo/bar.dll > */ > > - if (!IS_TARGET_ABSOLUTE_PATH (fskind, in_pathname) || sysroot == NULL) > - temp_pathname.reset (xstrdup (in_pathname)); > - else > + if (IS_TARGET_ABSOLUTE_PATH (fskind, in_pathname) && sysroot != NULL) > > Just a nit, but you could s/NULL/nullptr while at updating this line > > { > bool need_dir_separator; > > @@ -213,7 +246,7 @@ solib_find_1 (const char *in_pathname, int *fd, bool is_solib) > /* Cat the prefixed pathname together. */ > temp_pathname.reset (concat (sysroot, > need_dir_separator ? SLASH_STRING : "", > - in_pathname, (char *) NULL)); > + temp_pathname.get (), (char *) NULL)); > > Same here. Also, could nullptr make it so the "(char *)" cast becomes > useless? > > } > > /* Handle files to be accessed via the target. */ > diff --git a/gdb/testsuite/gdb.base/solib-proc-self.cc b/gdb/testsuite/gdb.base/solib-proc-self.cc > new file mode 100644 > index 00000000000..f2439d738a3 > --- /dev/null > +++ b/gdb/testsuite/gdb.base/solib-proc-self.cc > @@ -0,0 +1,73 @@ > +/* This testcase is part of GDB, the GNU debugger. > + > + Copyright 2022 Free Software Foundation, Inc. > + > + 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; either version 3 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 <http://www.gnu.org/licenses/> <http://www.gnu.org/licenses/>. */ > + > +#include <sys/mman.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <iostream> > +#include <fstream> > +#include <sstream> > +#include <vector> > +#include <unistd.h> > + > +#ifdef __WIN32__ > +#include <windows.h> > +#define dlopen(name, mode) LoadLibrary (name) > +#define dlclose(handle) FreeLibrary (handle) > +#define dlerror() "an error occurred" > +#else > +#include <dlfcn.h> > +#endif > + > +int > +main () > +{ > + /* Read the shared libraries contents into a buffer. */ > + std::ifstream read_so_file = std::ifstream (SHLIB_NAME); > + read_so_file.seekg (0, std::ios::end); > + std::streamsize size = read_so_file.tellg (); > + read_so_file.seekg (0, std::ios::beg); > + std::vector<char> buffer (size); > + if (!read_so_file.read (buffer.data (), size)) > + { > + fprintf (stderr, "Failed to load solib\n"); > + exit (1); > + } > + > + /* Create a memory mapped file, then write the shared library to that > + new memory mapped file. */ > + int mem_fd = memfd_create ("test", 0); > + write (mem_fd, buffer.data (), buffer.size ()); > + > + /* Generate the /proc/self/fd/[num] path for the memory mapped file. */ > + std::string proc_self_fd_path; /* break-here */ > + std::stringstream proc_self_fd_path_stream > + = std::stringstream (proc_self_fd_path); > + proc_self_fd_path_stream << "/proc/self/fd/" << mem_fd; > + > + /* Call dlopen on it. */ > + void *handle = dlopen (proc_self_fd_path_stream.str ().c_str (), RTLD_LAZY); > + if (!handle) > + { > + fprintf (stderr, "%s\n", dlerror ()); > + exit (1); > + } > + /* It worked. */ > + dlclose (handle); > + > + return 0; > +} > diff --git a/gdb/testsuite/gdb.base/solib-proc-self.exp b/gdb/testsuite/gdb.base/solib-proc-self.exp > new file mode 100644 > index 00000000000..1c845822490 > --- /dev/null > +++ b/gdb/testsuite/gdb.base/solib-proc-self.exp > @@ -0,0 +1,130 @@ > +# Copyright 2022 Free Software Foundation, Inc. > + > +# 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; either version 3 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 <http://www.gnu.org/licenses/> <http://www.gnu.org/licenses/>. */ > + > +# Test connecting and disconnecting at shared library events. > + > +if {[skip_shlib_tests]} { > + untested "could not run to main" > + return 0 > +} > + > +standard_testfile .cc > + > +# Reuse an existing library, we don't care about the library contents > +# for this test. > +set libfile so-disc-shr > +set libsrc "${srcdir}/${subdir}/${libfile}.c" > +set libname "${libfile}.so" > +set libobj [standard_output_file ${libname}] > + > +# Compile the shared library. > +if { [gdb_compile_shlib $libsrc $libobj {debug}] != ""} { > + return -1 > +} > + > +# Compile the test executable. > +if [ build_executable "failed to prepare" $testfile $srcfile \ > + [list shlib_load debug c++ additional_flags=-DSHLIB_NAME="${libobj}"]] { > + return -1 > +} > + > +# Start GDB and run to the point where the test program tries to dlopen a file > +# from within /proc/self/fd/. Catch the shared library event and check that > +# we actually try to load a file from /proc/<INFERIOR-PID>/fd/. > +# > +# If SYSROOT is not the empty string, then this is set as the value of GDB's > +# sysroot immediately after starting GDB. The only value that is (currently) > +# supported, other than the empty string, is 'target:'. > +proc do_test { {sysroot ""} } { > + clean_restart $::binfile > + > + if {$sysroot != ""} { > + gdb_test_no_output "set sysroot ${sysroot}" > + } > + > + gdb_load_shlib $::libobj > + > + if ![runto_main] then { > + return 0 > + } > + > + # Get inferior's PID for later. > + set inferior_pid [get_inferior_pid] > + > + # Turn on the solib-events so we can see that gdb resolves everything > + # correctly. > + gdb_test_no_output "set stop-on-solib-events 1" > + > + # Run to the 'break-here' marker. > + gdb_breakpoint [gdb_get_line_number "break-here"] > + gdb_continue_to_breakpoint "break-here" ".* break-here .*" > + > + set memfd "" > + gdb_test_multiple "p mem_fd" "Get file descriptor" { > + -re -wrap "\\\$$::decimal = (\[^\r\n\]*)" { > + set memfd $expect_out(1,string) > + pass $gdb_test_name > + } > + } > + > + gdb_test "continue" "Stopped due to shared library event.*" \ > + "continue to load" > > I've been running this patch (as modified by Andrew) and see a failure in > the tests. > > (gdb) PASS: gdb.base/solib-proc-self.exp: continue to breakpoint: break-here > p mem_fd > $1 = 4 > (gdb) PASS: gdb.base/solib-proc-self.exp: Get file descriptor > continue > Continuing. > Stopped due to shared library event: > Inferior loaded /proc/7331/fd/4 > (gdb) PASS: gdb.base/solib-proc-self.exp: continue to load > continue > Continuing. > Stopped due to shared library event (no libraries added or removed) > (gdb) FAIL: gdb.base/solib-proc-self.exp: continue > > > I believe this "continue" should be removed. The test passes cleanly > without it. > > Best, > Lancelot. > > + > + # Check if inferior resolved the /proc/self/fd/[num] to /proc/[pid]/fd/[num]. > + gdb_test "continue" \ > + [multi_line \ > + "Stopped due to shared library event:" \ > + " Inferior loaded (?:target:)?/proc/${inferior_pid}/fd/$memfd"] > +} > + > +# First run of the test. > +do_test > + > +# Possible second run of the test. If we are using a remote target then we > +# should consider setting the sysroot to 'target:' and re-running the test. > +if {[target_info exists gdb_protocol] > + && ([target_info gdb_protocol] == "remote" > + || [target_info gdb_protocol] == "extended-remote")} { > + # GDB will already be running after the first call to do_test, so we can > + # take a peek at the current sysroot setting, and decide if we should > + # repeat the test with a different setting. > + > + set new_sysroot "" > + gdb_test_multiple "show sysroot" "" { > + -wrap -re "The current system root is \"\"\\." { > + pass $gdb_test_name > + > + # Repeat the test with 'target:' sysroot. > + set new_sysroot "target:" > + } > + -wrap -re "The current system root is \"target:\"\\." { > + pass $gdb_test_name > + > + # Nothing else to do, we already tested with target: sysroot. > + } > + -re "$gdb_prompt $" { > + pass $gdb_test_name > + > + # If already testing with any other sysroot, we probably should > + # not try to adjust things, so don't do any further testing. > + } > + } > + > + with_test_prefix "sysroot $new_sysroot" { > + if { $new_sysroot != "" } { > + do_test $new_sysroot > + } > + } > +} > > >
Dr Lancelot SIX <lsix@lancelotsix.com> writes: > Hi Asaf and Andrew, > > Thanks for working on this! I have included comments below in the patch. > > On 10/11/2022 19:37, Andrew Burgess via Gdb-patches wrote: >> Asaf Fisher via Gdb-patches<gdb-patches@sourceware.org> writes: >> >>> Introduced `check_proc_self_file` that checks if a path used by >>> inferior in dlopen is in the form of `/proc/self/...` and if so resolves >>> it to `/proc/[pid]/...` >>> >>> Bug:https://sourceware.org/bugzilla/show_bug.cgi?id=29586 >> Hi Asaf, >> >> Thanks for providing a fix for this, and thanks for submitting the >> copyright assignment paperwork. >> >> I took a look through your patch and ended up tweaking it a little. >> I've attached a revised version below, which I'd love to hear your >> feedback on. >> >> When making changes my goal was to extend your fix to work with >> gdbserver just like it works with native targets. You can test >> different gdbserver setups like this: >> >> make check-gdb TESTS="gdb.base/solib-proc-self.exp" \ >> RUNTESTFLAGS="--target_board=native-extended-gdbserver" >> >> This will setup gdbserver and connect GDB to it with 'target >> extended-remote', you can also test using the 'native-gdbserver' board, >> which will connect to gdbserver as just 'target remote'. >> >> Ideally any new test will pass in all three configurations (the default >> plus the two listed above), and initially, your change only fixed the >> native target case. By moving the fix elsewhere in the shared library >> loading process I think I new have all three cases working. >> >> I also extended the test to test using a 'target:' sysroot. By default >> when testing with the two gdbserver boards above, we set the sysroot to >> "" (the empty string), this tells GDB that remote files can be found on >> the local machine, and avoids all files accesses having to go over the >> remote protocol. However, given the nature of this change, I figured it >> was worth testing with the 'target:' sysroot too, this means we re-run >> the tests bu sending all file accesses over the remote protocol. That >> case is also fixed with the patch below. >> >> Where I'd most appreciate your feedback is for the algorithm by which >> the /proc/self path is spotted and converted to /proc/PID. I've cut the >> code back a bit from what you had originally, mostly because I couldn't >> find a way to test that the extra complexity was required. If you have >> any additional test cases that could show that the slimmed down code is >> not good enough, then that would be great. >> >> Thanks, >> Andrew >> >> --- >> >> commit 0bf4c98bc225c89a0a1ddcea727eca178ad69710 >> Author: Asaf Fisher<asaffisher.dev@gmail.com> >> Date: Fri Oct 21 17:42:05 2022 +0000 >> >> gdb: handle loading shared libraries from /proc/self/fd/ >> >> Bug PR gdb/29586 describes a situations where a shared library is >> created in memory, then written to a memory mapped file. The memory >> mapped file will show up as a file descriptor within /proc/self/fd/, >> and this path is then used with dlopen in order to call functions >> within the in-memory shared library. >> >> When attempting to debug this GDB hangs. The problem is that, GDB >> stops at the shared-library event, reads the shared library path from >> the inferior, which is "/proc/self/fd/<NUM>", and then GDB attempts to >> open this file. >> >> Unfortunately, this means GDB tries to open a file within GDB's >> /proc/self/fd/ directory, not within the inferior's directory. In the >> case of our hang it turns out that the file descriptor that is opened >> is a pipe, and GDB hangs trying to read from the pipe. >> >> However, the behaviour is really just undefined, depending on which >> file descriptor the inferior tries to open, GDB will open, or fail to >> open, random files within its /proc/self/fd directory. >> >> The solution proposed in this commit is to hook into solib_find_1, and >> spot when GDB is looking for any file in /proc/self/, if this is the >> case, then the filename is rewritten as /proc/<PID>, where <PID> is >> the process-id of the current inferior. >> >> The test passes for the unix, native-gdbserver, and >> native-extended-gdbserver targets. When testing with either of the >> gdbserver targets, the test is run using the default empty sysroot, >> and also using the 'target:' sysroot. >> >> Bug:https://sourceware.org/bugzilla/show_bug.cgi?id=29586 >> gdb.base/solib-proc-self.exp >> Co-authored-by: Andrew Burgess<aburgess@redhat.com> >> >> diff --git a/gdb/solib.c b/gdb/solib.c >> index 7cfdd81114c..cf2d0d3bc3a 100644 >> --- a/gdb/solib.c >> +++ b/gdb/solib.c >> @@ -83,6 +83,35 @@ show_solib_search_path (struct ui_file *file, int from_tty, >> # define DOS_BASED_FILE_SYSTEM 0 >> #endif >> >> +/* Fix references to files in /proc/self/fd/ when opening a shared library. >> + >> + SO_NAME is the name of the shared library being loaded. This function >> + returns a possibly modified name which should be used as the path to the >> + shared library. >> + >> + If SO_NAME starts with /proc/self, then the returned name will be >> + modified to start with /proc/PID where 'PID' is the pid of the current >> + inferior. */ >> + >> +static gdb::unique_xmalloc_ptr<char> >> +filter_proc_self_filenames (gdb::unique_xmalloc_ptr<char> so_name) >> +{ >> + static const char *proc_self_prefix = "/proc/self"; >> + >> + /* Is the path really a /proc/self? */ >> + if (!startswith (so_name.get (), proc_self_prefix)) >> + return so_name; >> + >> + /* Get the part of the path after /proc/self. For example given >> + '/proc/self/fd' we find the '/fd' part. */ >> + gdb_assert (strlen (so_name.get ()) >= strlen (proc_self_prefix)); > > I am not sure how this assert can ever fail as the test just before > checked that so_name starts with proc_self_prefix. How can it have a > smaller length? > Removed. >> + const char *tail = so_name.get () + strlen (proc_self_prefix); >> + >> + /* Build a replacement path. */ >> + int inferior_pid = inferior_ptid.pid (); >> + return xstrprintf ("/proc/%d%s", inferior_pid, tail); >> +} >> + >> /* Return the full pathname of a binary file (the main executable or a >> shared library file), or NULL if not found. If FD is non-NULL, *FD >> is set to either -1 or an open file handle for the binary file. >> @@ -172,6 +201,12 @@ solib_find_1 (const char *in_pathname, int *fd, bool is_solib) >> } >> } >> >> + /* If the path starts /proc/self then rewrite this as /proc/PID using the >> + current inferior's pid. Failing to do this will cause GDB to try and >> + open files within its proc directory, rather than the inferiors. */ >> + temp_pathname.reset (xstrdup (in_pathname)); >> + temp_pathname = filter_proc_self_filenames (std::move (temp_pathname)); >> + >> /* Note, we're interested in IS_TARGET_ABSOLUTE_PATH, not >> IS_ABSOLUTE_PATH. The latter is for host paths only, while >> IN_PATHNAME is a target path. For example, if we're supposed to >> @@ -184,9 +219,7 @@ solib_find_1 (const char *in_pathname, int *fd, bool is_solib) >> 3rd attempt, c:/foo/bar.dll ==> /sysroot/foo/bar.dll >> */ >> >> - if (!IS_TARGET_ABSOLUTE_PATH (fskind, in_pathname) || sysroot == NULL) >> - temp_pathname.reset (xstrdup (in_pathname)); >> - else >> + if (IS_TARGET_ABSOLUTE_PATH (fskind, in_pathname) && sysroot != NULL) > > Just a nit, but you could s/NULL/nullptr while at updating this line > Fixed. >> { >> bool need_dir_separator; >> >> @@ -213,7 +246,7 @@ solib_find_1 (const char *in_pathname, int *fd, bool is_solib) >> /* Cat the prefixed pathname together. */ >> temp_pathname.reset (concat (sysroot, >> need_dir_separator ? SLASH_STRING : "", >> - in_pathname, (char *) NULL)); >> + temp_pathname.get (), (char *) NULL)); > Same here. Also, could nullptr make it so the "(char *)" cast becomes > useless? Fixed. >> } >> >> /* Handle files to be accessed via the target. */ >> diff --git a/gdb/testsuite/gdb.base/solib-proc-self.cc b/gdb/testsuite/gdb.base/solib-proc-self.cc >> new file mode 100644 >> index 00000000000..f2439d738a3 >> --- /dev/null >> +++ b/gdb/testsuite/gdb.base/solib-proc-self.cc >> @@ -0,0 +1,73 @@ >> +/* This testcase is part of GDB, the GNU debugger. >> + >> + Copyright 2022 Free Software Foundation, Inc. >> + >> + 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; either version 3 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<http://www.gnu.org/licenses/>. */ >> + >> +#include <sys/mman.h> >> +#include <stdio.h> >> +#include <stdlib.h> >> +#include <iostream> >> +#include <fstream> >> +#include <sstream> >> +#include <vector> >> +#include <unistd.h> >> + >> +#ifdef __WIN32__ >> +#include <windows.h> >> +#define dlopen(name, mode) LoadLibrary (name) >> +#define dlclose(handle) FreeLibrary (handle) >> +#define dlerror() "an error occurred" >> +#else >> +#include <dlfcn.h> >> +#endif >> + >> +int >> +main () >> +{ >> + /* Read the shared libraries contents into a buffer. */ >> + std::ifstream read_so_file = std::ifstream (SHLIB_NAME); >> + read_so_file.seekg (0, std::ios::end); >> + std::streamsize size = read_so_file.tellg (); >> + read_so_file.seekg (0, std::ios::beg); >> + std::vector<char> buffer (size); >> + if (!read_so_file.read (buffer.data (), size)) >> + { >> + fprintf (stderr, "Failed to load solib\n"); >> + exit (1); >> + } >> + >> + /* Create a memory mapped file, then write the shared library to that >> + new memory mapped file. */ >> + int mem_fd = memfd_create ("test", 0); >> + write (mem_fd, buffer.data (), buffer.size ()); >> + >> + /* Generate the /proc/self/fd/[num] path for the memory mapped file. */ >> + std::string proc_self_fd_path; /* break-here */ >> + std::stringstream proc_self_fd_path_stream >> + = std::stringstream (proc_self_fd_path); >> + proc_self_fd_path_stream << "/proc/self/fd/" << mem_fd; >> + >> + /* Call dlopen on it. */ >> + void *handle = dlopen (proc_self_fd_path_stream.str ().c_str (), RTLD_LAZY); >> + if (!handle) >> + { >> + fprintf (stderr, "%s\n", dlerror ()); >> + exit (1); >> + } >> + /* It worked. */ >> + dlclose (handle); >> + >> + return 0; >> +} >> diff --git a/gdb/testsuite/gdb.base/solib-proc-self.exp b/gdb/testsuite/gdb.base/solib-proc-self.exp >> new file mode 100644 >> index 00000000000..1c845822490 >> --- /dev/null >> +++ b/gdb/testsuite/gdb.base/solib-proc-self.exp >> @@ -0,0 +1,130 @@ >> +# Copyright 2022 Free Software Foundation, Inc. >> + >> +# 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; either version 3 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<http://www.gnu.org/licenses/>. */ >> + >> +# Test connecting and disconnecting at shared library events. >> + >> +if {[skip_shlib_tests]} { >> + untested "could not run to main" >> + return 0 >> +} >> + >> +standard_testfile .cc >> + >> +# Reuse an existing library, we don't care about the library contents >> +# for this test. >> +set libfile so-disc-shr >> +set libsrc "${srcdir}/${subdir}/${libfile}.c" >> +set libname "${libfile}.so" >> +set libobj [standard_output_file ${libname}] >> + >> +# Compile the shared library. >> +if { [gdb_compile_shlib $libsrc $libobj {debug}] != ""} { >> + return -1 >> +} >> + >> +# Compile the test executable. >> +if [ build_executable "failed to prepare" $testfile $srcfile \ >> + [list shlib_load debug c++ additional_flags=-DSHLIB_NAME="${libobj}"]] { >> + return -1 >> +} >> + >> +# Start GDB and run to the point where the test program tries to dlopen a file >> +# from within /proc/self/fd/. Catch the shared library event and check that >> +# we actually try to load a file from /proc/<INFERIOR-PID>/fd/. >> +# >> +# If SYSROOT is not the empty string, then this is set as the value of GDB's >> +# sysroot immediately after starting GDB. The only value that is (currently) >> +# supported, other than the empty string, is 'target:'. >> +proc do_test { {sysroot ""} } { >> + clean_restart $::binfile >> + >> + if {$sysroot != ""} { >> + gdb_test_no_output "set sysroot ${sysroot}" >> + } >> + >> + gdb_load_shlib $::libobj >> + >> + if ![runto_main] then { >> + return 0 >> + } >> + >> + # Get inferior's PID for later. >> + set inferior_pid [get_inferior_pid] >> + >> + # Turn on the solib-events so we can see that gdb resolves everything >> + # correctly. >> + gdb_test_no_output "set stop-on-solib-events 1" >> + >> + # Run to the 'break-here' marker. >> + gdb_breakpoint [gdb_get_line_number "break-here"] >> + gdb_continue_to_breakpoint "break-here" ".* break-here .*" >> + >> + set memfd "" >> + gdb_test_multiple "p mem_fd" "Get file descriptor" { >> + -re -wrap "\\\$$::decimal = (\[^\r\n\]*)" { >> + set memfd $expect_out(1,string) >> + pass $gdb_test_name >> + } >> + } >> + >> + gdb_test "continue" "Stopped due to shared library event.*" \ >> + "continue to load" > > I've been running this patch (as modified by Andrew) and see a failure > in the tests. > > (gdb) PASS: gdb.base/solib-proc-self.exp: continue to breakpoint: break-here > p mem_fd > $1 = 4 > (gdb) PASS: gdb.base/solib-proc-self.exp: Get file descriptor > continue > Continuing. > Stopped due to shared library event: > Inferior loaded /proc/7331/fd/4 > (gdb) PASS: gdb.base/solib-proc-self.exp: continue to load > continue > Continuing. > Stopped due to shared library event (no libraries added or removed) > (gdb) FAIL: gdb.base/solib-proc-self.exp: continue > > I believe this "continue" should be removed. The test passes cleanly > without it. I looked at this failure a little, turns out, for some reason, I'm seeing two solib stop events related to the dlopen call, here's what my gdb.log looks like: ... (gdb) PASS: gdb.base/solib-proc-self.exp: continue to breakpoint: break-here p mem_fd $1 = 4 (gdb) PASS: gdb.base/solib-proc-self.exp: Get file descriptor continue Continuing. Stopped due to shared library event (no libraries added or removed) (gdb) PASS: gdb.base/solib-proc-self.exp: continue to load continue Continuing. Stopped due to shared library event: Inferior loaded /proc/2891392/fd/4 (gdb) PASS: gdb.base/solib-proc-self.exp: continue Here's the inferior backtrace at the first (no libraries added or removed) stop: #0 0x00007ffff7fd85da in _dl_map_object_from_fd () from /lib64/ld-linux-x86-64.so.2 #1 0x00007ffff7fdb366 in _dl_map_object () from /lib64/ld-linux-x86-64.so.2 #2 0x00007ffff7fe5d55 in dl_open_worker () from /lib64/ld-linux-x86-64.so.2 #3 0x00007ffff7ba24f8 in _dl_catch_exception () from /lib64/libc.so.6 #4 0x00007ffff7fe58fe in _dl_open () from /lib64/ld-linux-x86-64.so.2 #5 0x00007ffff7f8c39c in dlopen_doit () from /lib64/libdl.so.2 #6 0x00007ffff7ba24f8 in _dl_catch_exception () from /lib64/libc.so.6 #7 0x00007ffff7ba25c3 in _dl_catch_error () from /lib64/libc.so.6 #8 0x00007ffff7f8cb09 in _dlerror_run () from /lib64/libdl.so.2 #9 0x00007ffff7f8c42a in dlopen@@GLIBC_2.2.5 () from /lib64/libdl.so.2 #10 0x0000000000402555 in main () at /tmp/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.base/solib-proc-self.cc:63 And the backtrace at the second stop: #0 0x00007ffff7fe60a8 in dl_open_worker () from /lib64/ld-linux-x86-64.so.2 #1 0x00007ffff7ba24f8 in _dl_catch_exception () from /lib64/libc.so.6 #2 0x00007ffff7fe58fe in _dl_open () from /lib64/ld-linux-x86-64.so.2 #3 0x00007ffff7f8c39c in dlopen_doit () from /lib64/libdl.so.2 #4 0x00007ffff7ba24f8 in _dl_catch_exception () from /lib64/libc.so.6 #5 0x00007ffff7ba25c3 in _dl_catch_error () from /lib64/libc.so.6 #6 0x00007ffff7f8cb09 in _dlerror_run () from /lib64/libdl.so.2 #7 0x00007ffff7f8c42a in dlopen@@GLIBC_2.2.5 () from /lib64/libdl.so.2 #8 0x0000000000402555 in main () at /tmp/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.base/solib-proc-self.cc:63 I'm going to fold the two continue calls together into a gdb_test_multiple, but I'll also dig into why I see two stop events, maybe there's a GDB issue here. Thanks, Andrew > > Best, > Lancelot. > >> + >> + # Check if inferior resolved the /proc/self/fd/[num] to /proc/[pid]/fd/[num]. >> + gdb_test "continue" \ >> + [multi_line \ >> + "Stopped due to shared library event:" \ >> + " Inferior loaded (?:target:)?/proc/${inferior_pid}/fd/$memfd"] >> +} >> + >> +# First run of the test. >> +do_test >> + >> +# Possible second run of the test. If we are using a remote target then we >> +# should consider setting the sysroot to 'target:' and re-running the test. >> +if {[target_info exists gdb_protocol] >> + && ([target_info gdb_protocol] == "remote" >> + || [target_info gdb_protocol] == "extended-remote")} { >> + # GDB will already be running after the first call to do_test, so we can >> + # take a peek at the current sysroot setting, and decide if we should >> + # repeat the test with a different setting. >> + >> + set new_sysroot "" >> + gdb_test_multiple "show sysroot" "" { >> + -wrap -re "The current system root is \"\"\\." { >> + pass $gdb_test_name >> + >> + # Repeat the test with 'target:' sysroot. >> + set new_sysroot "target:" >> + } >> + -wrap -re "The current system root is \"target:\"\\." { >> + pass $gdb_test_name >> + >> + # Nothing else to do, we already tested with target: sysroot. >> + } >> + -re "$gdb_prompt $" { >> + pass $gdb_test_name >> + >> + # If already testing with any other sysroot, we probably should >> + # not try to adjust things, so don't do any further testing. >> + } >> + } >> + >> + with_test_prefix "sysroot $new_sysroot" { >> + if { $new_sysroot != "" } { >> + do_test $new_sysroot >> + } >> + } >> +} >>
diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c index 6acaf87960b..02bd89ef9d6 100644 --- a/gdb/solib-svr4.c +++ b/gdb/solib-svr4.c @@ -34,6 +34,7 @@ #include "regcache.h" #include "gdbthread.h" #include "observable.h" +#include "gdbsupport/pathstuff.h" #include "solist.h" #include "solib.h" @@ -48,6 +49,9 @@ #include <map> +#define SLASH_SELF "/self" +#define PROC_SELF "/proc" SLASH_SELF + static struct link_map_offsets *svr4_fetch_link_map_offsets (void); static int svr4_have_link_map_offsets (void); static void svr4_relocate_main_executable (void); @@ -1259,6 +1263,55 @@ svr4_default_sos (svr4_info *info) return newobj; } +/* Check and fix a cenerio where the so path that we extract has a path to + /proc/self e.g. /proc/self/fd/[fd_num] If inferior dlopen a path that has + /proc/self, GDB must not open it directly becuase the files in /proc/self are + unique for each process. Instead we resolve /proc/self to + /proc/[inferior_pid]. This change will give GDB the correct path */ + +static size_t +check_proc_self_file(char *so_name, char *normalized_so_name, + size_t out_normalized_so_name_len) { + /* We dont want a path with /../ yak. */ + gdb::unique_xmalloc_ptr<char> normalized_path_obj = gdb_realpath(so_name); + gdb::string_view normalized_path = gdb::string_view( + normalized_path_obj.get(), + std::min(strlen(normalized_path_obj.get()), out_normalized_so_name_len)); + + /* Is the path really a /proc/self? */ + if (0 != normalized_path.rfind(PROC_SELF, 0)) return 0; + + /* Lets get the part of the path after /proc/self e.g. /proc/self/fd -> /fd */ + size_t slash_self_index = normalized_path.rfind(SLASH_SELF); + if (std::string::npos == slash_self_index) return 0; + size_t after_self_index = slash_self_index + strlen(SLASH_SELF); + gdb::string_view after_self_path = normalized_path.substr(after_self_index); + + /* Get inferior path */ + int inferior_pid = inferior_ptid.pid(); + std::string inferior_procfs_path = string_printf("/proc/%d", inferior_pid); + + /* Check if there's enoght space in the out buffer for the normalized path. */ + size_t normalized_so_name_length = + inferior_procfs_path.length() + after_self_path.length(); + if (out_normalized_so_name_len < normalized_so_name_length) return 0; + + /* Build the full path */ + inferior_procfs_path.append(std::string(after_self_path)); + + warning(_("Detected loaded library (%s) from /proc/self.\nAttempting to " + "replace `self` with inferior's PID. -> %s"), + normalized_path.begin(), inferior_procfs_path.c_str()); + + auto out_length = + std::min(inferior_procfs_path.length(), out_normalized_so_name_len); + + /* Copy the new path to the out buffer */ + strncpy(normalized_so_name, inferior_procfs_path.c_str(), out_length); + + return out_length; +} + /* Read the whole inferior libraries chain starting at address LM. Expect the first entry in the chain's previous entry to be PREV_LM. Add the entries to the tail referenced by LINK_PTR_PTR. Ignore the @@ -1318,8 +1371,10 @@ svr4_read_so_list (svr4_info *info, CORE_ADDR lm, CORE_ADDR prev_lm, warning (_("Can't read pathname for load map.")); continue; } - - strncpy (newobj->so_name, buffer.get (), SO_NAME_MAX_PATH_SIZE - 1); + /* Check if path is in /proc/self */ + if (0 == check_proc_self_file(buffer.get(), newobj->so_name, + SO_NAME_MAX_PATH_SIZE - 1)) + strncpy(newobj->so_name, buffer.get(), SO_NAME_MAX_PATH_SIZE - 1); newobj->so_name[SO_NAME_MAX_PATH_SIZE - 1] = '\0'; strcpy (newobj->so_original_name, newobj->so_name);