diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 8f5f978074f..59f416f6118 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -2129,7 +2129,7 @@ linux_handle_extended_wait (struct lwp_info *lp, int status) open_proc_mem_file (lp->ptid); ourstatus->set_execd - (make_unique_xstrdup (linux_proc_pid_to_exec_file (pid))); + (make_unique_xstrdup (linux_target->pid_to_exec_file (pid))); /* The thread that execed must have been resumed, but, when a thread execs, it changes its tid to the tgid, and the old @@ -4000,7 +4000,14 @@ linux_nat_target::thread_name (struct thread_info *thr) const char * linux_nat_target::pid_to_exec_file (int pid) { - return linux_proc_pid_to_exec_file (pid); + /* If there's no sysroot. Or the sysroot is just 'target:' and the + inferior is in the same mount namespce, then we can consider the + filesystem local. */ + bool local_fs = (gdb_sysroot.empty () + || (gdb_sysroot == TARGET_SYSROOT_PREFIX + && linux_ns_same (pid, LINUX_NS_MNT))); + + return linux_proc_pid_to_exec_file (pid, local_fs); } /* Object representing an /proc/PID/mem open file. We keep one such diff --git a/gdb/nat/linux-procfs.c b/gdb/nat/linux-procfs.c index a72df2a09d7..d4f9af32bb9 100644 --- a/gdb/nat/linux-procfs.c +++ b/gdb/nat/linux-procfs.c @@ -424,7 +424,7 @@ linux_proc_task_list_dir_exists (pid_t pid) /* See linux-procfs.h. */ const char * -linux_proc_pid_to_exec_file (int pid) +linux_proc_pid_to_exec_file (int pid, bool is_local_fs) { static char buf[PATH_MAX]; char name[PATH_MAX]; @@ -437,9 +437,31 @@ linux_proc_pid_to_exec_file (int pid) else buf[len] = '\0'; - /* Use /proc/PID/exe if the actual file can't be read, but /proc/PID/exe - can be. */ - if (access (buf, R_OK) != 0 && access (name, R_OK) == 0) + /* If the inferior and GDB can see the same filesystem, and NAME + cannot be read, maybe the file has been deleted, then we can + potentially use /proc/PID/exe instead. + + GDB always interprets the results of this function within the + current sysroot (which is 'target:' by default). This means + that, if we return /proc/PID/exe, GDB will try to find this file + within the sysroot. + + This doesn't make sense if GDB is using a sysroot like: + '/some/random/directory/', not only is it possible that NAME + could be found within the sysroot, it is unlikely that + /proc/PID/exe will exist within the sysroot. + + Similarly, if the sysroot is 'target:', but the inferior is + running within a separate MNT namespace, then it is more than + likely that the inferior will also be running in a separate PID + namespace, in this case PID is the pid on the host system, + /proc/PID/exe will not be correct within the inferiors MNT/PID + namespace. + + So, we can fallback to use /proc/PID/exe only if IS_LOCAL_FS is + true, this indicates that GDB and the inferior are using the same + MNT namespace, and GDB's sysroot is either empty, or 'target:'. */ + if (is_local_fs && access (buf, R_OK) != 0 && access (name, R_OK) == 0) strcpy (buf, name); return buf; diff --git a/gdb/nat/linux-procfs.h b/gdb/nat/linux-procfs.h index 0c806221faa..4b02d31abdf 100644 --- a/gdb/nat/linux-procfs.h +++ b/gdb/nat/linux-procfs.h @@ -87,9 +87,19 @@ extern int linux_proc_task_list_dir_exists (pid_t pid); /* Return the full absolute name of the executable file that was run to create the process PID. The returned value persists until this - function is next called. */ + function is next called. -extern const char *linux_proc_pid_to_exec_file (int pid); + LOCAL_FS should be true if the file returned from the function will + be searched for in the same filesystem as GDB itself is running. + In practice, this means LOCAL_FS should be true if PID and GDB are + running in the same MNT namespace and GDB's sysroot is either the + empty string, or is 'target:'. + + When used from gdbserver, where there is no sysroot, the only check + that matters is that PID and gdbserver are running in the same MNT + namespace. */ + +extern const char *linux_proc_pid_to_exec_file (int pid, bool local_fs); /* Display possible problems on this system. Display them only once per GDB execution. */ diff --git a/gdb/testsuite/gdb.base/attach-deleted-exec.exp b/gdb/testsuite/gdb.base/attach-deleted-exec.exp index 82a7bb409fe..45fac0db473 100644 --- a/gdb/testsuite/gdb.base/attach-deleted-exec.exp +++ b/gdb/testsuite/gdb.base/attach-deleted-exec.exp @@ -67,5 +67,49 @@ if { [regexp $re_nfs $filename] } { gdb_assert { [string equal $filename /proc/${testpid}/exe] } $test } +# Restart GDB. +clean_restart + +# Setup an empty sysroot. GDB will fail to find the executable within +# the sysroot. Additionally, the presence of a sysroot should prevent +# GDB from trying to load the executable from /proc/PID/exe. +set sysroot [standard_output_file "sysroot"] +gdb_test_no_output "set sysroot $sysroot" \ + "setup sysroot" + +# Attach to the inferior. GDB should complain about failing to find +# the executable. It is the name of the executable that GDB doesn't +# find that we're interesting in here. For native targets GDB should +# be looking for BINFILE, not /proc/PID/exe. +# +# For extended-remote targets things are unfortunately harder. Native +# GDB looks for BINFILE because it understands that GDB will be +# looking in the sysroot. But remote GDB doesn't know if GDB is using +# a sysroot or not. As such, gdbserver will return /proc/PID/exe if +# it knows that the file has been deleted locally. This isn't great +# if GDB then plans to look in a sysroot, but equally, if the remote +# file has been deleted, then the name GDB will return, will have had +# " (deleted" appended, so we're unlikely to get a hit in the sysroot +# either way. +if { [target_info gdb_protocol] == "extended-remote" } { + set filename_re "/proc/$testpid/exe" +} else { + set filename_re "\[^\r\n\]+/${testfile} \\(deleted\\)" +} + +verbose -log "APB: warning: No executable has been specified, and target executable $filename_re could not be found\\. Try using the \"file\" command\\." + +gdb_test "attach $testpid" \ + [multi_line \ + "Attaching to process $decimal" \ + "warning: No executable has been specified, and target executable $filename_re could not be found\\. Try using the \"file\" command\\." \ + ".*"] \ + "attach to inferior" + +# Check GDB hasn't managed to load an executable. +gdb_test "info inferior" \ + "\\*\[^)\]+\\)\\s*" \ + "confirm no executable is loaded." + # Cleanup. kill_wait_spawned_process $test_spawn_id diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc index e507558c64e..39642705b0d 100644 --- a/gdbserver/linux-low.cc +++ b/gdbserver/linux-low.cc @@ -751,7 +751,7 @@ linux_process_target::handle_extended_wait (lwp_info **orig_event_lwp, /* Set the event status. */ event_lwp->waitstatus.set_execd (make_unique_xstrdup - (linux_proc_pid_to_exec_file (event_thr->id.lwp ()))); + (pid_to_exec_file (event_thr->id.lwp ()))); /* Mark the exec status as pending. */ event_lwp->stopped = 1; @@ -6033,7 +6033,7 @@ linux_process_target::supports_pid_to_exec_file () const char * linux_process_target::pid_to_exec_file (int pid) { - return linux_proc_pid_to_exec_file (pid); + return linux_proc_pid_to_exec_file (pid, linux_ns_same (pid, LINUX_NS_MNT)); } bool