From: Fabian Kilger Date: Wed, 11 Jun 2025 20:52:16 +0000 (+0200) Subject: gdb: implement linux namespace support for fileio_lstat and vFile::lstat X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bd389c9515d240f55b117075b43184efdea41287;p=thirdparty%2Fbinutils-gdb.git gdb: implement linux namespace support for fileio_lstat and vFile::lstat The new algorithm to look for a build-id-based debug file (introduced by commit 22836ca88591ac7efacf06d5b6db191763fd8aba) makes use of fileio_lstat. As lstat was not supported by linux-namespace.c, all lstat calls would be performed on the host and not inside the namespace. Fixed by adding namespace lstat support. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32956 Approved-By: Andrew Burgess --- diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 2f98060506b..8f5f978074f 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -4585,6 +4585,20 @@ linux_nat_target::fileio_open (struct inferior *inf, const char *filename, return fd; } +/* Implementation of to_fileio_lstat. */ + +int +linux_nat_target::fileio_lstat (struct inferior *inf, const char *filename, + struct stat *sb, fileio_error *target_errno) +{ + int r = linux_mntns_lstat (linux_nat_fileio_pid_of (inf), filename, sb); + + if (r == -1) + *target_errno = host_to_fileio_error (errno); + + return r; +} + /* Implementation of to_fileio_readlink. */ std::optional diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h index 6e539918ced..21ec309dafb 100644 --- a/gdb/linux-nat.h +++ b/gdb/linux-nat.h @@ -108,6 +108,9 @@ public: const char *filename, fileio_error *target_errno) override; + int fileio_lstat (struct inferior *inf, const char *filename, + struct stat *sb, fileio_error *target_errno) override; + int fileio_unlink (struct inferior *inf, const char *filename, fileio_error *target_errno) override; diff --git a/gdb/nat/linux-namespaces.c b/gdb/nat/linux-namespaces.c index a3a38d82e64..8fe320663f5 100644 --- a/gdb/nat/linux-namespaces.c +++ b/gdb/nat/linux-namespaces.c @@ -233,6 +233,12 @@ enum mnsh_msg_type MNSH_RET_INT. */ MNSH_REQ_SETNS, + /* A request that the helper call lstat. The single + argument (the filename) should be passed in BUF, and + should include a terminating NUL character. The helper + should respond with a MNSH_RET_INTSTR. */ + MNSH_REQ_LSTAT, + /* A request that the helper call open. Arguments should be passed in BUF, INT1 and INT2. The filename (in BUF) should include a terminating NUL character. The helper @@ -284,6 +290,10 @@ mnsh_debug_print_message (enum mnsh_msg_type type, res += "ERROR"; break; + case MNSH_REQ_LSTAT: + res += "LSTAT"; + break; + case MNSH_REQ_SETNS: res += "SETNS"; break; @@ -511,6 +521,20 @@ mnsh_handle_setns (int sock, int fd, int nstype) return mnsh_return_int (sock, result, errno); } + +/* Handle a MNSH_REQ_LSTAT message. Must be async-signal-safe. */ + +static ssize_t +mnsh_handle_lstat (int sock, const char *filename) +{ + struct stat sb; + int stat_ok = lstat (filename, &sb); + + return mnsh_return_intstr (sock, stat_ok, &sb, + stat_ok == -1 ? 0 : sizeof (sb), + errno); +} + /* Handle a MNSH_REQ_OPEN message. Must be async-signal-safe. */ static ssize_t @@ -571,6 +595,11 @@ mnsh_main (int sock) response = mnsh_handle_setns (sock, fd, int1); break; + case MNSH_REQ_LSTAT: + if (size > 0 && buf[size - 1] == '\0') + response = mnsh_handle_lstat (sock, buf); + break; + case MNSH_REQ_OPEN: if (size > 0 && buf[size - 1] == '\0') response = mnsh_handle_open (sock, buf, int1, int2); @@ -761,6 +790,10 @@ mnsh_maybe_mourn_peer (void) mnsh_send_message (helper->sock, MNSH_REQ_OPEN, -1, flags, mode, \ filename, strlen (filename) + 1) +#define mnsh_send_lstat(helper, filename) \ + mnsh_send_message (helper->sock, MNSH_REQ_LSTAT, -1, 0, 0, \ + filename, strlen (filename) + 1) + #define mnsh_send_unlink(helper, filename) \ mnsh_send_message (helper->sock, MNSH_REQ_UNLINK, -1, 0, 0, \ filename, strlen (filename) + 1) @@ -943,6 +976,42 @@ linux_mntns_access_fs (pid_t pid) /* See nat/linux-namespaces.h. */ +int +linux_mntns_lstat (pid_t pid, const char *filename, + struct stat *sb) +{ + enum mnsh_fs_code access = linux_mntns_access_fs (pid); + + if (access == MNSH_FS_ERROR) + return -1; + + if (access == MNSH_FS_DIRECT) + return lstat (filename, sb); + + gdb_assert (access == MNSH_FS_HELPER); + + struct linux_mnsh *helper = linux_mntns_get_helper (); + + ssize_t size = mnsh_send_lstat (helper, filename); + if (size < 0) + return -1; + + int stat_ok, error; + size = mnsh_recv_intstr (helper, &stat_ok, &error, sb, sizeof (*sb)); + + if (size < 0) + { + stat_ok = -1; + errno = error; + } + else + gdb_assert (stat_ok == -1 || size == sizeof (*sb)); + + return stat_ok; +} + +/* See nat/linux-namespaces.h. */ + int linux_mntns_open_cloexec (pid_t pid, const char *filename, int flags, mode_t mode) diff --git a/gdb/nat/linux-namespaces.h b/gdb/nat/linux-namespaces.h index cfaccde91e9..a9a99f164e0 100644 --- a/gdb/nat/linux-namespaces.h +++ b/gdb/nat/linux-namespaces.h @@ -64,6 +64,11 @@ enum linux_ns_type extern int linux_ns_same (pid_t pid, enum linux_ns_type type); +/* Like lstat(2), but in the mount namespace of process PID. */ + +extern int linux_mntns_lstat (pid_t pid, const char *filename, + struct stat *sb); + /* Like gdb_open_cloexec, but in the mount namespace of process PID. */ diff --git a/gdbserver/hostio.cc b/gdbserver/hostio.cc index 8b4d74d5942..ca8e4918e47 100644 --- a/gdbserver/hostio.cc +++ b/gdbserver/hostio.cc @@ -525,7 +525,7 @@ handle_stat (char *own_buf, int *new_packet_len) static void handle_lstat (char *own_buf, int *new_packet_len) { - int bytes_sent; + int ret, bytes_sent; char *p; struct stat st; struct fio_stat fst; @@ -540,7 +540,12 @@ handle_lstat (char *own_buf, int *new_packet_len) return; } - if (lstat (filename, &st) == -1) + if (hostio_fs_pid != 0) + ret = the_target->multifs_lstat (hostio_fs_pid, filename, &st); + else + ret = lstat (filename, &st); + + if (ret == -1) { hostio_error (own_buf); return; diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc index 1d223c11db9..e507558c64e 100644 --- a/gdbserver/linux-low.cc +++ b/gdbserver/linux-low.cc @@ -6049,6 +6049,12 @@ linux_process_target::multifs_open (int pid, const char *filename, return linux_mntns_open_cloexec (pid, filename, flags, mode); } +int +linux_process_target::multifs_lstat (int pid, const char *filename, struct stat *sb) +{ + return linux_mntns_lstat (pid, filename, sb); +} + int linux_process_target::multifs_unlink (int pid, const char *filename) { diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h index 7d3e35fa9b0..e1c88ee0bb2 100644 --- a/gdbserver/linux-low.h +++ b/gdbserver/linux-low.h @@ -304,6 +304,8 @@ public: int multifs_open (int pid, const char *filename, int flags, mode_t mode) override; + int multifs_lstat (int pid, const char *filename, struct stat *st) override; + int multifs_unlink (int pid, const char *filename) override; ssize_t multifs_readlink (int pid, const char *filename, char *buf, diff --git a/gdbserver/target.cc b/gdbserver/target.cc index e812785d397..c400174c47c 100644 --- a/gdbserver/target.cc +++ b/gdbserver/target.cc @@ -772,6 +772,13 @@ process_stratum_target::multifs_open (int pid, const char *filename, return open (filename, flags, mode); } +int +process_stratum_target::multifs_lstat (int pid, const char *filename, + struct stat *sb) +{ + return lstat (filename, sb); +} + int process_stratum_target::multifs_unlink (int pid, const char *filename) { diff --git a/gdbserver/target.h b/gdbserver/target.h index af788e256eb..4d3f80f736d 100644 --- a/gdbserver/target.h +++ b/gdbserver/target.h @@ -441,6 +441,12 @@ public: virtual int multifs_open (int pid, const char *filename, int flags, mode_t mode); + /* Multiple-filesystem-aware lstat. Like lstat(2), but operating in + the filesystem as it appears to process PID. Systems where all + processes share a common filesystem should not override this. + The default behavior is to use lstat(2). */ + virtual int multifs_lstat (int pid, const char *filename, struct stat *sb); + /* Multiple-filesystem-aware unlink. Like unlink(2), but operates in the filesystem as it appears to process PID. Systems where all processes share a common filesystem should not override this.