Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH v1 0/8] gdb: various refactoring in linux-tdep
@ 2026-07-03 18:58 Matthieu Longo
  2026-07-03 18:58 ` [PATCH v1 1/8] gdb/linux-tdep: use pid_t consistently for process IDs Matthieu Longo
                   ` (7 more replies)
  0 siblings, 8 replies; 15+ messages in thread
From: Matthieu Longo @ 2026-07-03 18:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Luis Machado, Luis Machado, Andrew Burgess, Matthieu Longo

This patch series contains a number of refactorings and preparatory changes for upcoming AArch64 features support (POE, a.k.a Memory Protection Keys [1] and POE2) in GDB.

Patch 1 makes the use of pid_t for process IDs consistent throughout the relevant code in gdb/linux-tdep.c.
Patch 2 is an obvious fix changing the return type of linux_fill_prpsinfo from an integer to a boolean.
Patch 3 adds an optional length parameter to target_fileio_read_stralloc.
Patch 4 is a cosmetic change to gdb::array_view, but improving documentation.
Patch 5 adds gdb::replace algorithm for iterators and ranges.
Patch 6 simplifies the boiler plates required to read data from files in /proc, while also providing convenient typed views.
Patch 7 adds support for parsing the "ProtectionKey" field, when present, from /proc/PID/smaps.
Patch 8 adds helpers to read AT_HWCAP3, which exposes Linux kernel's capability bits needed for upcoming GDB features.

All changes were tested by building GDB on x86_64 and AArch64 and running the existing GDB testsuite.

[1]: https://docs.kernel.org/core-api/protection-keys.html

Regards,
Matthieu


Matthieu Longo (8):
  gdb/linux-tdep: use pid_t consistently for process IDs
  gdb/linux-tdep: change linux_fill_prpsinfo to return bool
  target_fileio_read_stralloc: add an optional length parameter
  gdb support: add index_type to array_view for documentation purpose
  gdb support: add gdb::replace algorithm for iterators and ranges
  gdb: introduce helper class file_reader_t
  gdb/linux-tdep: parse ProtectionKey in /proc/PID/smaps
  gdb/linux: add helpers to read AT_HWCAP3

 gdb/amd64-linux-tdep.c  |  12 +-
 gdb/inferior.h          |   2 +-
 gdb/linux-tdep.c        | 329 +++++++++++++++++++++-------------------
 gdb/linux-tdep.h        |  11 ++
 gdb/sparc64-tdep.c      |  12 +-
 gdb/target.c            |  10 +-
 gdb/target.h            |  67 +++++++-
 gdbserver/linux-low.cc  |  10 ++
 gdbserver/linux-low.h   |   4 +
 gdbsupport/array-view.h |  37 ++++-
 10 files changed, 314 insertions(+), 180 deletions(-)

-- 
2.55.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v1 1/8] gdb/linux-tdep: use pid_t consistently for process IDs
  2026-07-03 18:58 [PATCH v1 0/8] gdb: various refactoring in linux-tdep Matthieu Longo
@ 2026-07-03 18:58 ` Matthieu Longo
  2026-07-05 21:22   ` Tom Tromey
  2026-07-03 18:58 ` [PATCH v1 2/8] gdb/linux-tdep: change linux_fill_prpsinfo to return bool Matthieu Longo
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: Matthieu Longo @ 2026-07-03 18:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Luis Machado, Luis Machado, Andrew Burgess, Matthieu Longo

Use `pid_t' consistently instead of a mix of `pid_t', `int', and `long'
when representing process IDs in gdb/linux-tdep.c.

Also change inferior::pid to `pid_t' so that the inferior's process ID
uses the appropriate type throughout GDB.

Finally, factor out path templates "/proc/%d/{maps,smaps}" into named
constants.
---
 gdb/inferior.h   |  2 +-
 gdb/linux-tdep.c | 41 ++++++++++++++++++++---------------------
 2 files changed, 21 insertions(+), 22 deletions(-)

diff --git a/gdb/inferior.h b/gdb/inferior.h
index 9c031035a23..dc71cbdb264 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -590,7 +590,7 @@ class inferior : public refcounted_object,
 
   /* Actual target inferior id, usually, a process id.  This matches
      the ptid_t.pid member of threads of this inferior.  */
-  int pid = 0;
+  pid_t pid = 0;
   /* True if the PID was actually faked by GDB.  */
   bool fake_pid_p = false;
 
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index d5b0e6e7011..9f8f9f1af5e 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -48,6 +48,9 @@
 
 #include <algorithm>
 
+constexpr const char *PROC_PID_SMAPS = "/proc/%d/smaps";
+constexpr const char *PROC_PID_MAPS = "/proc/%d/maps";
+
 /* This enum represents the values that the user can choose when
    informing the Linux kernel about which memory mappings will be
    dumped in a corefile.  They are described in the file
@@ -842,9 +845,7 @@ static void
 linux_info_proc (struct gdbarch *gdbarch, const char *args,
 		 enum info_proc_what what)
 {
-  /* A long is used for pid instead of an int to avoid a loss of precision
-     compiler warning from the output of strtoul.  */
-  long pid;
+  pid_t pid;
   int cmdline_f = (what == IP_MINIMAL || what == IP_CMDLINE || what == IP_ALL);
   int cwd_f = (what == IP_MINIMAL || what == IP_CWD || what == IP_ALL);
   int environ_f = (what == IP_ENVIRON || what == IP_ALL);
@@ -876,10 +877,10 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
   if (args && args[0])
     error (_("Too many parameters: %s"), args);
 
-  gdb_printf (_("process %ld\n"), pid);
+  gdb_printf (_("process %d\n"), pid);
   if (cmdline_f)
     {
-      xsnprintf (filename, sizeof filename, "/proc/%ld/cmdline", pid);
+      xsnprintf (filename, sizeof filename, "/proc/%d/cmdline", pid);
       gdb_byte *buffer;
       LONGEST len = target_fileio_read_alloc (nullptr, filename, &buffer);
 
@@ -901,7 +902,7 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
     }
   if (cwd_f)
     {
-      xsnprintf (filename, sizeof filename, "/proc/%ld/cwd", pid);
+      xsnprintf (filename, sizeof filename, "/proc/%d/cwd", pid);
       std::optional<std::string> contents
 	= target_fileio_readlink (NULL, filename, &target_errno);
       if (contents.has_value ())
@@ -911,7 +912,7 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
     }
   if (environ_f)
     {
-      xsnprintf (filename, sizeof filename, "/proc/%ld/environ", pid);
+      xsnprintf (filename, sizeof filename, "/proc/%d/environ", pid);
       gdb_byte *buffer;
       LONGEST len = target_fileio_read_alloc (nullptr, filename, &buffer);
 
@@ -935,7 +936,7 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
     }
   if (exe_f)
     {
-      xsnprintf (filename, sizeof filename, "/proc/%ld/exe", pid);
+      xsnprintf (filename, sizeof filename, "/proc/%d/exe", pid);
       std::optional<std::string> contents
 	= target_fileio_readlink (NULL, filename, &target_errno);
       if (contents.has_value ())
@@ -945,7 +946,7 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
     }
   if (mappings_f)
     {
-      xsnprintf (filename, sizeof filename, "/proc/%ld/maps", pid);
+      xsnprintf (filename, sizeof filename, "/proc/%d/maps", pid);
       gdb::unique_xmalloc_ptr<char> map
 	= target_fileio_read_stralloc (NULL, filename);
       if (map != NULL)
@@ -990,7 +991,7 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
     }
   if (status_f)
     {
-      xsnprintf (filename, sizeof filename, "/proc/%ld/status", pid);
+      xsnprintf (filename, sizeof filename, "/proc/%d/status", pid);
       gdb::unique_xmalloc_ptr<char> status
 	= target_fileio_read_stralloc (NULL, filename);
       if (status)
@@ -1000,7 +1001,7 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
     }
   if (stat_f)
     {
-      xsnprintf (filename, sizeof filename, "/proc/%ld/stat", pid);
+      xsnprintf (filename, sizeof filename, "/proc/%d/stat", pid);
       gdb::unique_xmalloc_ptr<char> statstr
 	= target_fileio_read_stralloc (NULL, filename);
       if (statstr)
@@ -1673,7 +1674,7 @@ linux_process_address_in_memtag_page (CORE_ADDR address)
 
   pid_t pid = current_inferior ()->pid;
 
-  std::string smaps_file = string_printf ("/proc/%d/smaps", pid);
+  std::string smaps_file = string_printf (PROC_PID_SMAPS, pid);
 
   gdb::unique_xmalloc_ptr<char> data
     = target_fileio_read_stralloc (NULL, smaps_file.c_str ());
@@ -1731,7 +1732,6 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch,
 				linux_dump_mapping_p_ftype *should_dump_mapping_p,
 				linux_find_memory_region_ftype func)
 {
-  pid_t pid;
   /* Default dump behavior of coredump_filter (0x33), according to
      Documentation/filesystems/proc.txt from the Linux kernel
      tree.  */
@@ -1744,7 +1744,7 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch,
   if (current_inferior ()->fake_pid_p)
     return false;
 
-  pid = current_inferior ()->pid;
+  pid_t pid = current_inferior ()->pid;
 
   if (use_coredump_filter)
     {
@@ -1763,7 +1763,7 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch,
 	}
     }
 
-  std::string maps_filename = string_printf ("/proc/%d/smaps", pid);
+  std::string maps_filename = string_printf (PROC_PID_SMAPS, pid);
 
   gdb::unique_xmalloc_ptr<char> data
     = target_fileio_read_stralloc (NULL, maps_filename.c_str ());
@@ -1771,7 +1771,7 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch,
   if (data == NULL)
     {
       /* Older Linux kernels did not support /proc/PID/smaps.  */
-      maps_filename = string_printf ("/proc/%d/maps", pid);
+      maps_filename = string_printf (PROC_PID_MAPS, pid);
       data = target_fileio_read_stralloc (NULL, maps_filename.c_str ());
 
       if (data == nullptr)
@@ -2805,7 +2805,6 @@ static bool
 linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
 {
   char filename[100];
-  long pid;
 
   if (target_auxv_search (AT_SYSINFO_EHDR, &range->start) <= 0)
     return false;
@@ -2844,7 +2843,7 @@ linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
   if (current_inferior ()->fake_pid_p)
     return false;
 
-  pid = current_inferior ()->pid;
+  pid_t pid = current_inferior ()->pid;
 
   /* Note that reading /proc/PID/task/PID/maps (1) is much faster than
      reading /proc/PID/maps (2).  The later identifies thread stacks
@@ -2854,7 +2853,7 @@ linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
      a few thousand threads, (1) takes a few milliseconds, while (2)
      takes several seconds.  Also note that "smaps", what we read for
      determining core dump mappings, is even slower than "maps".  */
-  xsnprintf (filename, sizeof filename, "/proc/%ld/task/%ld/maps", pid, pid);
+  xsnprintf (filename, sizeof filename, "/proc/%d/task/%d/maps", pid, pid);
   gdb::unique_xmalloc_ptr<char> data
     = target_fileio_read_stralloc (NULL, filename);
   if (data != NULL)
@@ -3168,9 +3167,9 @@ linux_address_in_shadow_stack_mem_range
   if (!target_has_execution () || current_inferior ()->fake_pid_p)
     return false;
 
-  const int pid = current_inferior ()->pid;
+  pid_t pid = current_inferior ()->pid;
 
-  std::string smaps_file = string_printf ("/proc/%d/smaps", pid);
+  std::string smaps_file = string_printf (PROC_PID_SMAPS, pid);
 
   gdb::unique_xmalloc_ptr<char> data
     = target_fileio_read_stralloc (nullptr, smaps_file.c_str ());
-- 
2.55.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v1 2/8] gdb/linux-tdep: change linux_fill_prpsinfo to return bool
  2026-07-03 18:58 [PATCH v1 0/8] gdb: various refactoring in linux-tdep Matthieu Longo
  2026-07-03 18:58 ` [PATCH v1 1/8] gdb/linux-tdep: use pid_t consistently for process IDs Matthieu Longo
@ 2026-07-03 18:58 ` Matthieu Longo
  2026-07-03 18:58 ` [PATCH v1 3/8] target_fileio_read_stralloc: add an optional length parameter Matthieu Longo
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Matthieu Longo @ 2026-07-03 18:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Luis Machado, Luis Machado, Andrew Burgess, Matthieu Longo

Change linux_fill_prpsinfo() to return a boolean instead of an integer, since
it only reports success or failure.
Replace the returned integer values 1 and 0 with true and false respectively.
---
 gdb/linux-tdep.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 9f8f9f1af5e..0da33ba379b 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -2258,7 +2258,7 @@ linux_corefile_parse_exec_context (struct gdbarch *gdbarch, bfd *cbfd)
    return 1 since some information was already recorded.  It will only return
    0 iff nothing can be gathered.  */
 
-static int
+static bool
 linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
 {
   /* The filename which we will use to obtain some info about the process.
@@ -2297,14 +2297,14 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
     {
       /* No program name was read, so we won't be able to retrieve more
 	 information about the process.  */
-      return 0;
+      return false;
     }
   if (fname.get ()[buf_len - 1] != '\0')
     {
       warning (_("target file %s "
 		 "does not contain a trailing null character"),
 	       filename);
-      return 0;
+      return false;
     }
 
   memset (p, 0, sizeof (*p));
@@ -2336,9 +2336,9 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
   if (proc_stat == NULL || *proc_stat == '\0')
     {
       /* Despite being unable to read more information about the
-	 process, we return 1 here because at least we have its
+	 process, we return true here because at least we have its
 	 command line, PID and arguments.  */
-      return 1;
+      return true;
     }
 
   /* Ok, we have the stats.  It's time to do a little parsing of the
@@ -2359,7 +2359,7 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
   /* ps command also relies on no trailing fields ever contain ')'.  */
   proc_stat = strrchr (proc_stat, ')');
   if (proc_stat == NULL)
-    return 1;
+    return true;
   proc_stat++;
 
   proc_stat = skip_spaces (proc_stat);
@@ -2384,8 +2384,8 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
     {
       /* Again, we couldn't read the complementary information about
 	 the process state.  However, we already have minimal
-	 information, so we just return 1 here.  */
-      return 1;
+	 information, so we just return true here.  */
+      return true;
     }
 
   /* Filling the structure fields.  */
@@ -2413,8 +2413,8 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
 
   if (proc_status == NULL || *proc_status == '\0')
     {
-      /* Returning 1 since we already have a bunch of information.  */
-      return 1;
+      /* Returning true since we already have a bunch of information.  */
+      return true;
     }
 
   /* Extracting the UID.  */
@@ -2443,7 +2443,7 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
 	p->pr_gid = strtol (tmpstr, &tmpstr, 10);
     }
 
-  return 1;
+  return true;
 }
 
 /* Build the note section for a corefile, and return it in a malloc
-- 
2.55.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v1 3/8] target_fileio_read_stralloc: add an optional length parameter
  2026-07-03 18:58 [PATCH v1 0/8] gdb: various refactoring in linux-tdep Matthieu Longo
  2026-07-03 18:58 ` [PATCH v1 1/8] gdb/linux-tdep: use pid_t consistently for process IDs Matthieu Longo
  2026-07-03 18:58 ` [PATCH v1 2/8] gdb/linux-tdep: change linux_fill_prpsinfo to return bool Matthieu Longo
@ 2026-07-03 18:58 ` Matthieu Longo
  2026-07-03 18:58 ` [PATCH v1 4/8] gdb support: add index_type to array_view for documentation purpose Matthieu Longo
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Matthieu Longo @ 2026-07-03 18:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Luis Machado, Luis Machado, Andrew Burgess, Matthieu Longo

---
 gdb/target.c | 10 +++++++---
 gdb/target.h |  2 +-
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/gdb/target.c b/gdb/target.c
index 5d937f3ae85..e4907ca815a 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -3547,7 +3547,8 @@ target_fileio_read_alloc (struct inferior *inf, const char *filename,
 /* See target.h.  */
 
 gdb::unique_xmalloc_ptr<char>
-target_fileio_read_stralloc (struct inferior *inf, const char *filename)
+target_fileio_read_stralloc (struct inferior *inf, const char *filename,
+			     size_t *len)
 {
   gdb_byte *buffer;
   char *bufstr;
@@ -3556,17 +3557,20 @@ target_fileio_read_stralloc (struct inferior *inf, const char *filename)
   transferred = target_fileio_read_alloc_1 (inf, filename, &buffer, 1);
   bufstr = (char *) buffer;
 
+  if (len != nullptr)
+    *len = (transferred < 0 ? 0 : transferred);
+
   if (transferred < 0)
     return gdb::unique_xmalloc_ptr<char> (nullptr);
 
   if (transferred == 0)
     return make_unique_xstrdup ("");
 
-  bufstr[transferred] = 0;
+  bufstr[transferred] = '\0';
 
   /* Check for embedded NUL bytes; but allow trailing NULs.  */
   for (i = strlen (bufstr); i < transferred; i++)
-    if (bufstr[i] != 0)
+    if (bufstr[i] != '\0')
       {
 	warning (_("target file %s "
 		   "contained unexpected null characters"),
diff --git a/gdb/target.h b/gdb/target.h
index 22653138491..4215553033c 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -2336,7 +2336,7 @@ extern LONGEST target_fileio_read_alloc (struct inferior *inf,
    are returned as allocated but empty strings.  A warning is issued
    if the result contains any embedded NUL bytes.  */
 extern gdb::unique_xmalloc_ptr<char> target_fileio_read_stralloc
-    (struct inferior *inf, const char *filename);
+    (struct inferior *inf, const char *filename, size_t *len = nullptr);
 
 /* Invalidate the target associated with open handles that were open
    on target TARG, since we're about to close (and maybe destroy) the
-- 
2.55.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v1 4/8] gdb support: add index_type to array_view for documentation purpose
  2026-07-03 18:58 [PATCH v1 0/8] gdb: various refactoring in linux-tdep Matthieu Longo
                   ` (2 preceding siblings ...)
  2026-07-03 18:58 ` [PATCH v1 3/8] target_fileio_read_stralloc: add an optional length parameter Matthieu Longo
@ 2026-07-03 18:58 ` Matthieu Longo
  2026-07-03 19:55   ` Pedro Alves
  2026-07-03 18:58 ` [PATCH v1 5/8] gdb support: add gdb::replace algorithm for iterators and ranges Matthieu Longo
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: Matthieu Longo @ 2026-07-03 18:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Luis Machado, Luis Machado, Andrew Burgess, Matthieu Longo

The semantic of slice (size_type start, size_type size) might be
slightly ambiguous if the variable names are omitted. Making the
index type different from the size type removes this ambiguity.
This change is purely cosmetic.
---
 gdbsupport/array-view.h | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/gdbsupport/array-view.h b/gdbsupport/array-view.h
index 8431d7f5add..e7baea728d4 100644
--- a/gdbsupport/array-view.h
+++ b/gdbsupport/array-view.h
@@ -87,6 +87,7 @@ class array_view
   using value_type = T;
   using reference = T &;
   using const_reference = const T &;
+  using index_type = ptrdiff_t;
   using size_type = size_t;
   using const_iterator = const T *;
   using iterator = T *;
@@ -165,14 +166,14 @@ class array_view
   constexpr iterator end () const noexcept { return m_array + m_size; }
   constexpr const_iterator cend () const noexcept { return m_array + m_size; }
 
-  constexpr reference operator[] (size_t index) noexcept
+  constexpr reference operator[] (index_type index) noexcept
   {
 #if defined(_GLIBCXX_DEBUG)
     gdb_assert (index < m_size);
 #endif
     return m_array[index];
   }
-  constexpr const_reference operator[] (size_t index) const noexcept
+  constexpr const_reference operator[] (index_type index) const noexcept
   {
 #if defined(_GLIBCXX_DEBUG)
     gdb_assert (index < m_size);
@@ -187,7 +188,7 @@ class array_view
 
   /* Return a new array view over SIZE elements starting at START.  */
   [[nodiscard]]
-  constexpr array_view<T> slice (size_type start, size_type size) const noexcept
+  constexpr array_view<T> slice (index_type start, size_type size) const noexcept
   {
 #if defined(_GLIBCXX_DEBUG)
     gdb_assert (start + size <= m_size);
@@ -198,7 +199,7 @@ class array_view
   /* Return a new array view over all the elements after START,
      inclusive.  */
   [[nodiscard]]
-  constexpr array_view<T> slice (size_type start) const noexcept
+  constexpr array_view<T> slice (index_type start) const noexcept
   {
 #if defined(_GLIBCXX_DEBUG)
     gdb_assert (start <= m_size);
@@ -216,7 +217,7 @@ class array_view
    The two array views must have the same length.  */
 
 template <typename U, typename T>
-void copy (gdb::array_view<U> src, gdb::array_view<T> dest)
+void copy (array_view<U> src, array_view<T> dest)
 {
   gdb_assert (dest.size () == src.size ());
   if (dest.data () < src.data ())
-- 
2.55.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v1 5/8] gdb support: add gdb::replace algorithm for iterators and ranges
  2026-07-03 18:58 [PATCH v1 0/8] gdb: various refactoring in linux-tdep Matthieu Longo
                   ` (3 preceding siblings ...)
  2026-07-03 18:58 ` [PATCH v1 4/8] gdb support: add index_type to array_view for documentation purpose Matthieu Longo
@ 2026-07-03 18:58 ` Matthieu Longo
  2026-07-03 18:58 ` [PATCH v1 6/8] gdb: introduce helper class file_reader_t Matthieu Longo
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Matthieu Longo @ 2026-07-03 18:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Luis Machado, Luis Machado, Andrew Burgess, Matthieu Longo

Add a gdb::replace helper that mirrors the behavior of std::replace
for iterator pairs, together with a convenience overload accepting a
range.

This provides a C++17-compatible replacement for the C++20 std::replace/
std::ranges::replace algorithms, allowing callers to use a consistent
interface until GDB transitions to C++20. The helpers should be removed
once the C++ standard library implementations become available.
---
 gdbsupport/array-view.h | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/gdbsupport/array-view.h b/gdbsupport/array-view.h
index e7baea728d4..fa2b8889614 100644
--- a/gdbsupport/array-view.h
+++ b/gdbsupport/array-view.h
@@ -226,6 +226,32 @@ void copy (array_view<U> src, array_view<T> dest)
     std::copy_backward (src.begin (), src.end (), dest.end ());
 }
 
+/* Replace all occurrences of a value in the provided range.
+
+   Note: this helper is a reimplementation of std::replace, only available
+   from C++20 onwards, and consequently, should be removed once we switch
+   to C++20.  */
+
+template <class ForwardIt, typename T>
+void replace (ForwardIt first, ForwardIt last,
+	      const T &old_value, const T &new_value)
+{
+  for (auto it = first; it != last; ++it)
+  {
+    if (*it == old_value)
+      *it = new_value;
+  }
+}
+
+/* Replace all occurrences of a value in the provided array view.
+   Note: from C++20 onwards, std::ranges::replace should be used instead.  */
+
+template <class Range, typename T>
+void replace (Range r, const T &old_value, const T &new_value)
+{
+  replace (r.begin (), r.end (), old_value, new_value);
+}
+
 /* Compare LHS and RHS for (deep) equality.  That is, whether LHS and
    RHS have the same sizes, and whether each pair of elements of LHS
    and RHS at the same position compares equal.  */
-- 
2.55.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v1 6/8] gdb: introduce helper class file_reader_t
  2026-07-03 18:58 [PATCH v1 0/8] gdb: various refactoring in linux-tdep Matthieu Longo
                   ` (4 preceding siblings ...)
  2026-07-03 18:58 ` [PATCH v1 5/8] gdb support: add gdb::replace algorithm for iterators and ranges Matthieu Longo
@ 2026-07-03 18:58 ` Matthieu Longo
  2026-07-03 18:58 ` [PATCH v1 7/8] gdb/linux-tdep: parse ProtectionKey in /proc/PID/smaps Matthieu Longo
  2026-07-03 18:58 ` [PATCH v1 8/8] gdb/linux: add helpers to read AT_HWCAP3 Matthieu Longo
  7 siblings, 0 replies; 15+ messages in thread
From: Matthieu Longo @ 2026-07-03 18:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Luis Machado, Luis Machado, Andrew Burgess, Matthieu Longo

Wrap all the boilerplate code required to read a file in a new helper
class: file_reader_t. The class owns the file contents together with
the file path, and provides convenient accessors for the data, size and
typed views. It supports both null-terminated text files and binary files.

This helper eliminates repeated calls to target_fileio_read_stralloc
and target_fileio_read_alloc, remove explicit memory management with
gdb::unique_xmalloc_ptr, and simplifies the casting logic when working
with binary data.

The patch converts some of the existing Linux, AMD64, and SPARC code that
reads files from /proc to use file_reader_t.
The availability of array_views allows to also simplify the logic in
several places, where null-terminating characters are replaced by spaces,
or where the file content is iterated line by line.
In the last case, a new helper function, extract_string_view_from_buffer,
encapsulates the logic for such iterations where string are separated by
tokens.
---
 gdb/amd64-linux-tdep.c |  12 +-
 gdb/linux-tdep.c       | 254 +++++++++++++++++++----------------------
 gdb/sparc64-tdep.c     |  12 +-
 gdb/target.h           |  65 +++++++++++
 4 files changed, 195 insertions(+), 148 deletions(-)

diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index a5ac26654cf..e7841917eaf 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -1851,14 +1851,11 @@ amd64_linux_lam_untag_mask ()
   if (inf->fake_pid_p)
     return DEFAULT_TAG_MASK;
 
-  const std::string filename = string_printf ("/proc/%d/status", inf->pid);
-  gdb::unique_xmalloc_ptr<char> status_file
-    = target_fileio_read_stralloc (nullptr, filename.c_str ());
-
-  if (status_file == nullptr)
+  file_reader_t<char> proc_status (string_printf ("/proc/%d/status", inf->pid));
+  if (!proc_status)
     return DEFAULT_TAG_MASK;
 
-  std::string_view status_file_view (status_file.get ());
+  std::string_view status_file_view (proc_status.data ());
   constexpr std::string_view untag_mask_str = "untag_mask:\t";
   const size_t found = status_file_view.find (untag_mask_str);
   if (found != std::string::npos)
@@ -1870,7 +1867,8 @@ amd64_linux_lam_untag_mask ()
       unsigned long long result = std::strtoul (start, &endptr, 0);
       if (errno != 0 || endptr == start)
 	error (_("Failed to parse untag_mask from file %ps."),
-	       styled_string (file_name_style.style (), filename.c_str ()));
+	       styled_string (file_name_style.style (),
+			      proc_status.c_filepath ()));
 
       return result;
     }
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 0da33ba379b..e6697669f3d 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -839,6 +839,27 @@ dump_note_entry_p (filter_flags filterflags, const smaps_data &map)
   return true;
 }
 
+/* In a character buffer where entries are separated by a SEPARATOR character,
+   extract the string view starting at START.
+   Return the extracted view and the iterator to the next entry.  */
+
+static std::pair<gdb::array_view<char>, gdb::array_view<char>::iterator>
+extract_string_view_from_buffer (gdb::array_view<char> &buffer,
+				 gdb::array_view<char>::iterator start,
+				 char separator = '\0')
+{
+  if (start < buffer.begin () || start >= buffer.end ())
+    return std::make_pair (gdb::array_view<char> (), buffer.end ());
+
+  auto it = std::find (start, buffer.end (), separator);
+  if (it == buffer.end ())
+    return std::make_pair (gdb::array_view<char> (), buffer.end ());
+
+  auto next_start = std::next (it);
+  return std::make_pair
+    (gdb::array_view<char> (start, next_start), next_start);
+}
+
 /* Implement the "info proc" command.  */
 
 static void
@@ -880,25 +901,20 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
   gdb_printf (_("process %d\n"), pid);
   if (cmdline_f)
     {
-      xsnprintf (filename, sizeof filename, "/proc/%d/cmdline", pid);
-      gdb_byte *buffer;
-      LONGEST len = target_fileio_read_alloc (nullptr, filename, &buffer);
-
-      if (len > 0)
+      file_reader_t<gdb_byte> cmdline_freader
+	(string_printf ("/proc/%d/cmdline", pid));
+      if (cmdline_freader)
 	{
-	  gdb::unique_xmalloc_ptr<char> cmdline ((char *) buffer);
-	  ssize_t pos;
-
-	  for (pos = 0; pos < len - 1; pos++)
-	    {
-	      if (buffer[pos] == '\0')
-		buffer[pos] = ' ';
-	    }
-	  buffer[len - 1] = '\0';
-	  gdb_printf ("cmdline = '%s'\n", buffer);
+	  gdb::array_view<char> cmdline = cmdline_freader.cast_view<char> ();
+	  gdb_assert (cmdline[ cmdline.size () - 1] == '\0');
+	  /* Replace null characters splitting the arguments in the command
+	     line by spaces, except for the last one.  */
+	  gdb::replace (cmdline.slice (0, cmdline.size () - 1), '\0', ' ');
+	  gdb_printf ("cmdline = '%s'\n", cmdline.data ());
 	}
       else
-	warning (_("unable to open /proc file '%s'"), filename);
+	warning (_("unable to open /proc file '%s'"),
+		 cmdline_freader.c_filepath());
     }
   if (cwd_f)
     {
@@ -912,27 +928,25 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
     }
   if (environ_f)
     {
-      xsnprintf (filename, sizeof filename, "/proc/%d/environ", pid);
-      gdb_byte *buffer;
-      LONGEST len = target_fileio_read_alloc (nullptr, filename, &buffer);
-
-      if (len > 0)
+      file_reader_t<gdb_byte> environ_freader
+	(string_printf ("/proc/%d/environ", pid));
+      if (environ_freader)
 	{
-	  gdb::unique_xmalloc_ptr<char> dealloc ((char *) buffer);
 	  gdb_printf (_("Environment variables:\n\n"));
-
+	  gdb::array_view<char> buffer = environ_freader.cast_view<char> ();
 	  /* Entries are separated by the null character.
 	     Print each environment variable, line by line.  */
-	  gdb_byte *buffer_end = buffer + len;
-	  while (buffer < buffer_end)
+	  for (auto it = buffer.begin (); it != buffer.end ();)
 	    {
-	      gdb_printf ("  %s\n", buffer);
-	      /* +1 for the null character.  */
-	      buffer += strlen ((char *) buffer) + 1;
+	      auto [ntbs, next_start]
+		= extract_string_view_from_buffer (buffer, it, '\0');
+	      gdb_printf ("  %s\n", ntbs.data ());
+	      it = next_start;
 	    }
 	}
       else
-	warning (_("unable to open /proc file '%s'"), filename);
+	warning (_("unable to open /proc file '%s'"),
+		 environ_freader.c_filepath());
     }
   if (exe_f)
     {
@@ -946,10 +960,8 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
     }
   if (mappings_f)
     {
-      xsnprintf (filename, sizeof filename, "/proc/%d/maps", pid);
-      gdb::unique_xmalloc_ptr<char> map
-	= target_fileio_read_stralloc (NULL, filename);
-      if (map != NULL)
+      file_reader_t<char> map_freader (string_printf (PROC_PID_MAPS, pid));
+      if (map_freader)
 	{
 	  gdb_printf (_("Mapped address spaces:\n\n"));
 	  ui_out_emit_table emitter (current_uiout, 6, -1, "ProcMappings");
@@ -963,12 +975,16 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
 	  current_uiout->table_header (0, ui_left, "objfile", "File");
 	  current_uiout->table_body ();
 
-	  char *saveptr;
-	  for (const char *line = strtok_r (map.get (), "\n", &saveptr);
-	       line != nullptr;
-	       line = strtok_r (nullptr, "\n", &saveptr))
+	  auto content = map_freader.view ();
+	  for (auto it = content.begin (); it != content.end ();)
 	    {
-	      struct mapping m = read_mapping (line);
+	      auto [line, next_line_begin]
+		= extract_string_view_from_buffer (content, it, '\n');
+	      it = next_line_begin;
+
+	      /* read_mapping() expects a null-terminated string.  */
+	      *std::prev (it) = '\0';
+	      struct mapping m = read_mapping (line.data ());
 
 	      ui_out_emit_tuple tuple_emitter (current_uiout, nullptr);
 	      current_uiout->field_core_addr ("start", gdbarch, m.addr);
@@ -987,26 +1003,24 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
 	    }
 	}
       else
-	warning (_("unable to open /proc file '%s'"), filename);
+	warning (_("unable to open /proc file '%s'"),
+		 map_freader.c_filepath ());
     }
   if (status_f)
     {
-      xsnprintf (filename, sizeof filename, "/proc/%d/status", pid);
-      gdb::unique_xmalloc_ptr<char> status
-	= target_fileio_read_stralloc (NULL, filename);
-      if (status)
-	gdb_puts (status.get ());
+      file_reader_t<char> status_freader (string_printf ("/proc/%d/status", pid));
+      if (status_freader)
+	gdb_puts (status_freader.data ());
       else
-	warning (_("unable to open /proc file '%s'"), filename);
+	warning (_("unable to open /proc file '%s'"),
+		 status_freader.c_filepath ());
     }
   if (stat_f)
     {
-      xsnprintf (filename, sizeof filename, "/proc/%d/stat", pid);
-      gdb::unique_xmalloc_ptr<char> statstr
-	= target_fileio_read_stralloc (NULL, filename);
-      if (statstr)
+      file_reader_t<char> stat_freader (string_printf ("/proc/%d/stat", pid));
+      if (stat_freader)
 	{
-	  const char *p = statstr.get ();
+	  const char *p = stat_freader.data ();
 
 	  gdb_printf (_("Process: %s\n"),
 		      pulongest (strtoulst (p, &p, 10)));
@@ -1133,7 +1147,8 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
 #endif
 	}
       else
-	warning (_("unable to open /proc file '%s'"), filename);
+	warning (_("unable to open /proc file '%s'"),
+		 stat_freader.c_filepath());
     }
 }
 
@@ -1514,18 +1529,17 @@ parse_smaps_key_value (const char *keyword, const char *line,
 /* Helper function to parse the contents of /proc/<pid>/smaps into a data
    structure, for easy access.
 
-   DATA is the contents of the smaps file.  The parsed contents are stored
-   into the SMAPS vector.  */
+   FREADER is a wrapper around the contents of the smaps file.
+   The parsed contents are stored into the SMAPS vector.  */
 
 static std::vector<struct smaps_data>
-parse_smaps_data (const char *data,
-		  const std::string &maps_filename)
+parse_smaps_data (const file_reader_t<char> &freader)
 {
   char *line, *t;
 
-  gdb_assert (data != nullptr);
+  gdb_assert (freader);
 
-  line = strtok_r ((char *) data, "\n", &t);
+  line = strtok_r (freader.data (), "\n", &t);
 
   std::vector<struct smaps_data> smaps;
 
@@ -1581,8 +1595,8 @@ parse_smaps_data (const char *data,
 
 	  if (sscanf (line, "%64s", keyword) != 1)
 	    {
-	      warning (_("Error parsing {s,}maps file '%s'"),
-		       maps_filename.c_str ());
+	      warning (_("Error parsing keyword in {s,}maps file '%s'"),
+		       freader.c_filepath ());
 	      break;
 	    }
 
@@ -1596,12 +1610,12 @@ parse_smaps_data (const char *data,
 	    decode_vmflags (line, &v);
 
 	  if (parse_smaps_key_value (keyword, line, "Rss:",
-				     maps_filename,
+				     freader.filepath (),
 				     &rss))
 	    continue;
 
 	  if (parse_smaps_key_value (keyword, line, "Swap:",
-				     maps_filename,
+				     freader.filepath (),
 				     &swap))
 	    continue;
 
@@ -1612,8 +1626,9 @@ parse_smaps_data (const char *data,
 
 	      if (sscanf (line, "%*s%lu", &number) != 1)
 		{
-		  warning (_("Error parsing {s,}maps file '%s' number"),
-			   maps_filename.c_str ());
+		  warning (_("Error parsing numeric value associated with "
+			     "key '%s' in {s,}maps file '%s'"),
+			   keyword, freader.c_filepath ());
 		  break;
 		}
 	      if (number > 0)
@@ -1674,17 +1689,12 @@ linux_process_address_in_memtag_page (CORE_ADDR address)
 
   pid_t pid = current_inferior ()->pid;
 
-  std::string smaps_file = string_printf (PROC_PID_SMAPS, pid);
-
-  gdb::unique_xmalloc_ptr<char> data
-    = target_fileio_read_stralloc (NULL, smaps_file.c_str ());
-
-  if (data == nullptr)
+  file_reader_t<char> smaps_freader (string_printf (PROC_PID_SMAPS, pid));
+  if (!smaps_freader)
     return false;
 
   /* Parse the contents of smaps into a vector.  */
-  std::vector<struct smaps_data> smaps
-    = parse_smaps_data (data.get (), smaps_file);
+  std::vector<struct smaps_data> smaps = parse_smaps_data (smaps_freader);
 
   for (const smaps_data &map : smaps)
     {
@@ -1748,39 +1758,30 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch,
 
   if (use_coredump_filter)
     {
-      std::string core_dump_filter_name
-	= string_printf ("/proc/%d/coredump_filter", pid);
-
-      gdb::unique_xmalloc_ptr<char> coredumpfilterdata
-	= target_fileio_read_stralloc (NULL, core_dump_filter_name.c_str ());
-
-      if (coredumpfilterdata != NULL)
+      file_reader_t<char> coredump_filter_freader
+	(string_printf ("/proc/%d/coredump_filter", pid));
+      if (coredump_filter_freader)
 	{
 	  unsigned int flags;
 
-	  sscanf (coredumpfilterdata.get (), "%x", &flags);
+	  sscanf (coredump_filter_freader.data (), "%x", &flags);
 	  filterflags = (enum filter_flag) flags;
 	}
     }
 
-  std::string maps_filename = string_printf (PROC_PID_SMAPS, pid);
-
-  gdb::unique_xmalloc_ptr<char> data
-    = target_fileio_read_stralloc (NULL, maps_filename.c_str ());
+  std::vector<struct smaps_data> smaps;
 
-  if (data == NULL)
+  file_reader_t<char> smaps_freader (string_printf (PROC_PID_SMAPS, pid));
+  if (!smaps_freader)
     {
       /* Older Linux kernels did not support /proc/PID/smaps.  */
-      maps_filename = string_printf (PROC_PID_MAPS, pid);
-      data = target_fileio_read_stralloc (NULL, maps_filename.c_str ());
-
-      if (data == nullptr)
+      file_reader_t<char> maps_freader (string_printf (PROC_PID_MAPS, pid));
+      if (!maps_freader)
 	return false;
+      smaps = parse_smaps_data (maps_freader);
     }
-
-  /* Parse the contents of smaps into a vector.  */
-  std::vector<struct smaps_data> smaps
-    = parse_smaps_data (data.get (), maps_filename);
+  else
+    smaps = parse_smaps_data (smaps_freader);
 
   for (const struct smaps_data &map : smaps)
     {
@@ -2261,9 +2262,6 @@ linux_corefile_parse_exec_context (struct gdbarch *gdbarch, bfd *cbfd)
 static bool
 linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
 {
-  /* The filename which we will use to obtain some info about the process.
-     We will basically use this to store the `/proc/PID/FILENAME' file.  */
-  char filename[100];
   /* The basename of the executable.  */
   const char *basename;
   /* Temporary buffer.  */
@@ -2287,23 +2285,24 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
 
   /* Obtaining PID and filename.  */
   pid = inferior_ptid.pid ();
-  xsnprintf (filename, sizeof (filename), "/proc/%d/cmdline", (int) pid);
-  /* The full name of the program which generated the corefile.  */
-  gdb_byte *buf = nullptr;
-  LONGEST buf_len = target_fileio_read_alloc (nullptr, filename, &buf);
-  gdb::unique_xmalloc_ptr<char> fname ((char *)buf);
+  file_reader_t<gdb_byte> cmdline_freader
+    (string_printf ("/proc/%d/cmdline", pid));
+  if (!cmdline_freader)
+    return false;
 
-  if (buf_len < 1 || fname.get () == nullptr || fname.get ()[0] == '\0')
+  /* The full name of the program which generated the corefile.  */
+  gdb::array_view<char> cmdline = cmdline_freader.cast_view<char> ();
+  if (cmdline.size () < 1 || cmdline[0] == '\0')
     {
       /* No program name was read, so we won't be able to retrieve more
 	 information about the process.  */
       return false;
     }
-  if (fname.get ()[buf_len - 1] != '\0')
+  if (cmdline[cmdline.size () - 1] != '\0')
     {
       warning (_("target file %s "
 		 "does not contain a trailing null character"),
-	       filename);
+	       cmdline_freader.c_filepath ());
       return false;
     }
 
@@ -2313,27 +2312,23 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
   p->pr_pid = pid;
 
   /* Copying the program name.  Only the basename matters.  */
-  basename = lbasename (fname.get ());
+  basename = lbasename (cmdline.data ());
   strncpy (p->pr_fname, basename, sizeof (p->pr_fname) - 1);
   p->pr_fname[sizeof (p->pr_fname) - 1] = '\0';
 
   const std::string &infargs = current_inferior ()->args ();
 
   /* The arguments of the program.  */
-  std::string psargs = fname.get ();
+  std::string psargs = cmdline.data ();
   if (!infargs.empty ())
     psargs += ' ' + infargs;
 
   strncpy (p->pr_psargs, psargs.c_str (), sizeof (p->pr_psargs) - 1);
   p->pr_psargs[sizeof (p->pr_psargs) - 1] = '\0';
 
-  xsnprintf (filename, sizeof (filename), "/proc/%d/stat", (int) pid);
-  /* The contents of `/proc/PID/stat'.  */
-  gdb::unique_xmalloc_ptr<char> proc_stat_contents
-    = target_fileio_read_stralloc (NULL, filename);
-  char *proc_stat = proc_stat_contents.get ();
-
-  if (proc_stat == NULL || *proc_stat == '\0')
+  file_reader_t<char> stat_freader (string_printf ("/proc/%d/stat", pid));
+  char *proc_stat = stat_freader.data ();
+  if (!stat_freader || *proc_stat == '\0')
     {
       /* Despite being unable to read more information about the
 	 process, we return true here because at least we have its
@@ -2405,13 +2400,9 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
 
   /* Finally, obtaining the UID and GID.  For that, we read and parse the
      contents of the `/proc/PID/status' file.  */
-  xsnprintf (filename, sizeof (filename), "/proc/%d/status", (int) pid);
-  /* The contents of `/proc/PID/status'.  */
-  gdb::unique_xmalloc_ptr<char> proc_status_contents
-    = target_fileio_read_stralloc (NULL, filename);
-  char *proc_status = proc_status_contents.get ();
-
-  if (proc_status == NULL || *proc_status == '\0')
+  file_reader_t<char> status_freader (string_printf ("/proc/%d/status", pid));
+  char *proc_status = status_freader.data ();
+  if (!status_freader || *proc_status == '\0')
     {
       /* Returning true since we already have a bunch of information.  */
       return true;
@@ -2804,8 +2795,6 @@ linux_gdb_signal_to_target (struct gdbarch *gdbarch,
 static bool
 linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
 {
-  char filename[100];
-
   if (target_auxv_search (AT_SYSINFO_EHDR, &range->start) <= 0)
     return false;
 
@@ -2853,15 +2842,14 @@ linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
      a few thousand threads, (1) takes a few milliseconds, while (2)
      takes several seconds.  Also note that "smaps", what we read for
      determining core dump mappings, is even slower than "maps".  */
-  xsnprintf (filename, sizeof filename, "/proc/%d/task/%d/maps", pid, pid);
-  gdb::unique_xmalloc_ptr<char> data
-    = target_fileio_read_stralloc (NULL, filename);
-  if (data != NULL)
+  file_reader_t<char> task_maps_freader
+    (string_printf ("/proc/%d/task/%d/maps", pid, pid));
+  if (task_maps_freader)
     {
       char *line;
       char *saveptr = NULL;
 
-      for (line = strtok_r (data.get (), "\n", &saveptr);
+      for (line = strtok_r (task_maps_freader.data (), "\n", &saveptr);
 	   line != NULL;
 	   line = strtok_r (NULL, "\n", &saveptr))
 	{
@@ -2880,7 +2868,8 @@ linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
 	}
     }
   else
-    warning (_("unable to open /proc file '%s'"), filename);
+    warning (_("unable to open /proc file '%s'"),
+	     task_maps_freader.c_filepath ());
 
   return false;
 }
@@ -3169,16 +3158,11 @@ linux_address_in_shadow_stack_mem_range
 
   pid_t pid = current_inferior ()->pid;
 
-  std::string smaps_file = string_printf (PROC_PID_SMAPS, pid);
-
-  gdb::unique_xmalloc_ptr<char> data
-    = target_fileio_read_stralloc (nullptr, smaps_file.c_str ());
-
-  if (data == nullptr)
+  file_reader_t<char> smaps_freader (string_printf (PROC_PID_SMAPS, pid));
+  if (!smaps_freader)
     return false;
 
-  const std::vector<smaps_data> smaps
-    = parse_smaps_data (data.get (), smaps_file);
+  const std::vector<smaps_data> smaps = parse_smaps_data (smaps_freader);
 
   auto find_addr_mem_range = [&addr] (const smaps_data &map)
     {
diff --git a/gdb/sparc64-tdep.c b/gdb/sparc64-tdep.c
index 93db3417a2a..811b76f27d8 100644
--- a/gdb/sparc64-tdep.c
+++ b/gdb/sparc64-tdep.c
@@ -305,14 +305,13 @@ adi_is_addr_mapped (CORE_ADDR vaddr, size_t cnt)
   size_t i = 0;
 
   pid_t pid = inferior_ptid.pid ();
-  snprintf (filename, sizeof filename, "/proc/%ld/adi/maps", (long) pid);
-  gdb::unique_xmalloc_ptr<char> data
-    = target_fileio_read_stralloc (NULL, filename);
-  if (data)
+  file_reader_t<char> adi_maps_freader
+    (string_printf ("/proc/%d/adi/maps", pid));
+  if (adi_maps_freader)
     {
       adi_stat_t adi_stat = get_adi_info (pid);
       char *saveptr;
-      for (char *line = strtok_r (data.get (), "\n", &saveptr);
+      for (char *line = strtok_r (adi_maps_freader.data (), "\n", &saveptr);
 	   line;
 	   line = strtok_r (NULL, "\n", &saveptr))
 	{
@@ -329,7 +328,8 @@ adi_is_addr_mapped (CORE_ADDR vaddr, size_t cnt)
 	}
       }
   else
-    warning (_("unable to open /proc file '%s'"), filename);
+    warning (_("unable to open /proc file '%s'"),
+	     adi_maps_freader.c_filepath ());
 
   return false;
 }
diff --git a/gdb/target.h b/gdb/target.h
index 4215553033c..d6fc101f205 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -2338,6 +2338,71 @@ extern LONGEST target_fileio_read_alloc (struct inferior *inf,
 extern gdb::unique_xmalloc_ptr<char> target_fileio_read_stralloc
     (struct inferior *inf, const char *filename, size_t *len = nullptr);
 
+/* Helper class for reading a file.  */
+template <typename T>
+class file_reader_t
+{
+  /* The filepath of the file being read.  */
+  std::string m_filepath;
+  /* Smart pointer to the data.  */
+  gdb::unique_xmalloc_ptr<T> m_data;
+  /* Size of the data.  */
+  size_t m_size;
+
+public:
+  file_reader_t (const std::string &filepath)
+    : m_filepath (filepath)
+    , m_size (0)
+  {
+    if constexpr (std::is_same_v<T, char>)
+      m_data = target_fileio_read_stralloc (nullptr, m_filepath.c_str (),
+					    &m_size);
+    else
+      {
+	gdb_byte *buf = nullptr;
+	m_size = target_fileio_read_alloc (nullptr, m_filepath.c_str (), &buf);
+	m_data = gdb::unique_xmalloc_ptr<T> (reinterpret_cast<T *>(buf));
+      }
+  }
+
+  /* Return true if the file was read successfully.  */
+  operator bool () const noexcept
+  { return m_data != nullptr && m_size > 0; }
+
+  /* Return a pointer to the data.  */
+  T *data () const noexcept
+  { return m_data.get (); }
+
+  /* Return the size of the data.  */
+  size_t size () const noexcept
+  {
+    /* For char buffers, size() corresponds to the size of the read data. Some
+       null-terminator characters are possibly scattered throughout the data.
+       Consequently, strlen() might not reflect the actual size.  */
+    return m_size;
+  }
+
+  /* Return a span of the data.  */
+  gdb::array_view<T> view () const noexcept
+  { return gdb::array_view<T> (m_data.get (), size ()); }
+
+  /* Return a span of the data, cast to a different type.  */
+  template <typename U>
+  gdb::array_view<U> cast_view () const noexcept
+  {
+    return gdb::array_view<U> (reinterpret_cast<U *> (m_data.get ()),
+			       size () * sizeof (T) / sizeof (U));
+  }
+
+  /* Return the filepath of the file that was read.  */
+  const std::string &filepath () const noexcept
+  { return m_filepath; }
+
+  /* Return the filepath of the file that was read as a C string.  */
+  const char *c_filepath () const noexcept
+  { return m_filepath.c_str (); }
+};
+
 /* Invalidate the target associated with open handles that were open
    on target TARG, since we're about to close (and maybe destroy) the
    target.  The handles remain open from the client's perspective, but
-- 
2.55.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v1 7/8] gdb/linux-tdep: parse ProtectionKey in /proc/PID/smaps
  2026-07-03 18:58 [PATCH v1 0/8] gdb: various refactoring in linux-tdep Matthieu Longo
                   ` (5 preceding siblings ...)
  2026-07-03 18:58 ` [PATCH v1 6/8] gdb: introduce helper class file_reader_t Matthieu Longo
@ 2026-07-03 18:58 ` Matthieu Longo
  2026-07-03 18:58 ` [PATCH v1 8/8] gdb/linux: add helpers to read AT_HWCAP3 Matthieu Longo
  7 siblings, 0 replies; 15+ messages in thread
From: Matthieu Longo @ 2026-07-03 18:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Luis Machado, Luis Machado, Andrew Burgess, Matthieu Longo

Memory Protection Keys provide a mechanism for enforcing page-based
protections without requiring modification of the page tables
when an application changes protection domains. [1]

The "ProtectionKey" field may be present in /proc/PID/smaps x86_64
and AArch64 systems since Linux 4.9, when the kernel is built with
Memory Protection Keys support.

Add a new 'pkey' field to `struct smaps_data', and populate it when
the "ProtectionKey" field is present.

This prepares for displaying the protection key in 'info proc mappings'.

[1]: https://docs.kernel.org/core-api/protection-keys.html,
     https://lkml.iu.edu/1512.0/03058.html
---
 gdb/linux-tdep.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index e6697669f3d..5312dad176f 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -127,6 +127,7 @@ struct smaps_data
 
   ULONGEST rss;
   ULONGEST swap;
+  std::optional<int> pkey;
 };
 
 /* Whether to take the /proc/PID/coredump_filter into account when
@@ -1552,6 +1553,7 @@ parse_smaps_data (const file_reader_t<char> &freader)
       int mapping_file_p;
       ULONGEST rss = -1;
       ULONGEST swap = -1;
+      int pkey = -1;
 
       memset (&v, 0, sizeof (v));
       struct mapping m = read_mapping (line);
@@ -1652,6 +1654,16 @@ parse_smaps_data (const file_reader_t<char> &freader)
 		  mapping_anon_p = 1;
 		}
 	    }
+
+	  if (streq (keyword, "ProtectionKey:"))
+	    {
+	      if (sscanf (line, "%*s%d", &pkey) != 1)
+		{
+		  warning (_("Error parsing %s's value in {s,}maps file '%s'"),
+			   keyword, freader.c_filepath ());
+		  break;
+		}
+	    }
 	}
       /* Save the smaps entry to the vector.  */
 	struct smaps_data map;
@@ -1671,6 +1683,7 @@ parse_smaps_data (const file_reader_t<char> &freader)
 	map.inode = m.inode;
 	map.rss = rss;
 	map.swap = swap;
+	map.pkey.emplace (pkey);
 
 	smaps.emplace_back (map);
     }
-- 
2.55.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v1 8/8] gdb/linux: add helpers to read AT_HWCAP3
  2026-07-03 18:58 [PATCH v1 0/8] gdb: various refactoring in linux-tdep Matthieu Longo
                   ` (6 preceding siblings ...)
  2026-07-03 18:58 ` [PATCH v1 7/8] gdb/linux-tdep: parse ProtectionKey in /proc/PID/smaps Matthieu Longo
@ 2026-07-03 18:58 ` Matthieu Longo
  2026-07-07 10:39   ` Yury Khrustalev
  7 siblings, 1 reply; 15+ messages in thread
From: Matthieu Longo @ 2026-07-03 18:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Luis Machado, Luis Machado, Andrew Burgess, Matthieu Longo

Add linux_get_hwcap3 helpers to GDB and gdbserver, mirroring the
existing linux_get_hwcap and linux_get_hwcap2 interfaces.

The new helpers retrieve the AT_HWCAP3 auxiliary vector entry either
from explicitly supplied auxv data or from the current inferior.

This prepares for future features that depend on HWCAP3 capability
bits.
---
 gdb/linux-tdep.c       | 19 +++++++++++++++++++
 gdb/linux-tdep.h       | 11 +++++++++++
 gdbserver/linux-low.cc | 10 ++++++++++
 gdbserver/linux-low.h  |  4 ++++
 4 files changed, 44 insertions(+)

diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 5312dad176f..e00a9786f0b 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -3138,6 +3138,25 @@ linux_get_hwcap2 ()
 			   current_inferior ()->arch ());
 }
 
+/* See linux-tdep.h.  */
+
+CORE_ADDR
+linux_get_hwcap3 (const std::optional<gdb::byte_vector> &auxv,
+		  target_ops *target, gdbarch *gdbarch)
+{
+  return linux_get_hwcap_helper (auxv, target, gdbarch, AT_HWCAP3);
+}
+
+/* See linux-tdep.h.  */
+
+CORE_ADDR
+linux_get_hwcap3 ()
+{
+  return linux_get_hwcap3 (target_read_auxv (),
+			   current_inferior ()->top_target (),
+			   current_inferior ()->arch ());
+}
+
 /* Display whether the gcore command is using the
    /proc/PID/coredump_filter file.  */
 
diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h
index c19839fde2c..01c81d26691 100644
--- a/gdb/linux-tdep.h
+++ b/gdb/linux-tdep.h
@@ -91,6 +91,17 @@ extern CORE_ADDR linux_get_hwcap2 (const std::optional<gdb::byte_vector> &auxv,
 
 extern CORE_ADDR linux_get_hwcap2 ();
 
+/* Fetch the AT_HWCAP3 entry from auxv data AUXV.  Use TARGET and GDBARCH to
+   parse auxv entries.
+
+   On error, 0 is returned.  */
+extern CORE_ADDR linux_get_hwcap3 (const std::optional<gdb::byte_vector> &auxv,
+				   struct target_ops *target, gdbarch *gdbarch);
+
+/* Same as the above, but obtain all the inputs from the current inferior.  */
+
+extern CORE_ADDR linux_get_hwcap3 ();
+
 /* Returns true if ADDR belongs to a shadow stack memory range.  If this
    is the case, assign the shadow stack memory range to RANGE
    [start_address, end_address).  */
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index ade5e9e2a1c..7b29b3ddf7f 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -7198,6 +7198,16 @@ linux_get_hwcap2 (int pid, int wordsize)
   return hwcap2;
 }
 
+/* See linux-low.h.  */
+
+CORE_ADDR
+linux_get_hwcap3 (int pid, int wordsize)
+{
+  CORE_ADDR hwcap3 = 0;
+  linux_get_auxv (pid, wordsize, AT_HWCAP3, &hwcap3);
+  return hwcap3;
+}
+
 #ifdef HAVE_LINUX_REGSETS
 void
 initialize_regsets_info (struct regsets_info *info)
diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h
index 03e11202955..276d60eafd6 100644
--- a/gdbserver/linux-low.h
+++ b/gdbserver/linux-low.h
@@ -977,4 +977,8 @@ CORE_ADDR linux_get_hwcap (int pid, int wordsize);
 
 CORE_ADDR linux_get_hwcap2 (int pid, int wordsize);
 
+/* Fetch the AT_HWCAP3 entry from the auxv vector, where entries are length
+   WORDSIZE, of process with pid PID.  If no entry was found, return 0.  */
+CORE_ADDR linux_get_hwcap3 (int pid, int wordsize);
+
 #endif /* GDBSERVER_LINUX_LOW_H */
-- 
2.55.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v1 4/8] gdb support: add index_type to array_view for documentation purpose
  2026-07-03 18:58 ` [PATCH v1 4/8] gdb support: add index_type to array_view for documentation purpose Matthieu Longo
@ 2026-07-03 19:55   ` Pedro Alves
  2026-07-06 10:51     ` Matthieu Longo
  0 siblings, 1 reply; 15+ messages in thread
From: Pedro Alves @ 2026-07-03 19:55 UTC (permalink / raw)
  To: Matthieu Longo, gdb-patches; +Cc: Luis Machado, Luis Machado, Andrew Burgess

On 2026-07-03 19:58, Matthieu Longo wrote:
> The semantic of slice (size_type start, size_type size) might be
> slightly ambiguous if the variable names are omitted. Making the
> index type different from the size type removes this ambiguity.
> This change is purely cosmetic.

You've changed the type from size_t (unsigned) to ptrdiff_t (signed).  That's not cosmetic.

The code was using size_type (and size_t) for indexing, as that's what standard containers
use.  There's no std::vector::index_type, etc.

Also this:

 > -void copy (gdb::array_view<U> src, gdb::array_view<T> dest)
 > +void copy (array_view<U> src, array_view<T> dest)

... seem unrelated.


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v1 1/8] gdb/linux-tdep: use pid_t consistently for process IDs
  2026-07-03 18:58 ` [PATCH v1 1/8] gdb/linux-tdep: use pid_t consistently for process IDs Matthieu Longo
@ 2026-07-05 21:22   ` Tom Tromey
  2026-07-06  9:30     ` Matthieu Longo
  0 siblings, 1 reply; 15+ messages in thread
From: Tom Tromey @ 2026-07-05 21:22 UTC (permalink / raw)
  To: Matthieu Longo; +Cc: gdb-patches, Luis Machado, Luis Machado, Andrew Burgess

>>>>> "Matthieu" == Matthieu Longo <matthieu.longo@arm.com> writes:

Matthieu> Also change inferior::pid to `pid_t' so that the inferior's process ID
Matthieu> uses the appropriate type throughout GDB.

I don't think this is desirable as pid_t is a host type, but the
inferior's process ID has to possibly represent a target scalar.

Probably the current use of 'int' here is also wrong, see those recent
patches I sent and the corresponding bugs.

Instead it should be ptid_t::pid_type.

... and then, ptid_t::pid_type should be LONGEST or, even better, a new
enum with LONGEST as the base type, to avoid doing any arithmetic on it.

I started work on this a while ago but it's a long slog & I haven't
finished.

Tom

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v1 1/8] gdb/linux-tdep: use pid_t consistently for process IDs
  2026-07-05 21:22   ` Tom Tromey
@ 2026-07-06  9:30     ` Matthieu Longo
  0 siblings, 0 replies; 15+ messages in thread
From: Matthieu Longo @ 2026-07-06  9:30 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, Luis Machado, Luis Machado, Andrew Burgess

On 05/07/2026 22:22, Tom Tromey wrote:
>>>>>> "Matthieu" == Matthieu Longo <matthieu.longo@arm.com> writes:
> 
> Matthieu> Also change inferior::pid to `pid_t' so that the inferior's process ID
> Matthieu> uses the appropriate type throughout GDB.
> 
> I don't think this is desirable as pid_t is a host type, but the
> inferior's process ID has to possibly represent a target scalar.
> 
> Probably the current use of 'int' here is also wrong, see those recent
> patches I sent and the corresponding bugs.
> 
> Instead it should be ptid_t::pid_type.
> 
> ... and then, ptid_t::pid_type should be LONGEST or, even better, a new
> enum with LONGEST as the base type, to avoid doing any arithmetic on it.
> 
> I started work on this a while ago but it's a long slog & I haven't
> finished.
> 
> Tom

I will remove that from the patch series.
Since you already started working on it, it is probably better to tackle it as a whole.
Thanks for your review.

Matthieu

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v1 4/8] gdb support: add index_type to array_view for documentation purpose
  2026-07-03 19:55   ` Pedro Alves
@ 2026-07-06 10:51     ` Matthieu Longo
  0 siblings, 0 replies; 15+ messages in thread
From: Matthieu Longo @ 2026-07-06 10:51 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches; +Cc: Luis Machado, Luis Machado, Andrew Burgess

On 03/07/2026 20:55, Pedro Alves wrote:
> On 2026-07-03 19:58, Matthieu Longo wrote:
>> The semantic of slice (size_type start, size_type size) might be
>> slightly ambiguous if the variable names are omitted. Making the
>> index type different from the size type removes this ambiguity.
>> This change is purely cosmetic.
> 
> You've changed the type from size_t (unsigned) to ptrdiff_t (signed).  That's not cosmetic.
> 

Oups ! Sorry.

> The code was using size_type (and size_t) for indexing, as that's what standard containers
> use.  There's no std::vector::index_type, etc.
> 
> Also this:
> 
>  > -void copy (gdb::array_view<U> src, gdb::array_view<T> dest)
>  > +void copy (array_view<U> src, array_view<T> dest)
> 
> ... seem unrelated.
> 

Since this copy() is already inside the gdb namespace, it is redundant.
Since it was trivial, I included inside this patch.

I think at this stage, it is better to drop this patch.
Thanks for your feedback.

Matthieu

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v1 8/8] gdb/linux: add helpers to read AT_HWCAP3
  2026-07-03 18:58 ` [PATCH v1 8/8] gdb/linux: add helpers to read AT_HWCAP3 Matthieu Longo
@ 2026-07-07 10:39   ` Yury Khrustalev
  2026-07-07 15:51     ` Matthieu Longo
  0 siblings, 1 reply; 15+ messages in thread
From: Yury Khrustalev @ 2026-07-07 10:39 UTC (permalink / raw)
  To: Matthieu Longo; +Cc: gdb-patches, Luis Machado, Luis Machado, Andrew Burgess

On Fri, Jul 03, 2026 at 07:58:53PM +0100, Matthieu Longo wrote:
> Add linux_get_hwcap3 helpers to GDB and gdbserver, mirroring the
> existing linux_get_hwcap and linux_get_hwcap2 interfaces.
> 
> The new helpers retrieve the AT_HWCAP3 auxiliary vector entry either
> from explicitly supplied auxv data or from the current inferior.
> 
> This prepares for future features that depend on HWCAP3 capability
> bits.

Could you also add the same for HWCAP4?

Thanks,
Yury


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v1 8/8] gdb/linux: add helpers to read AT_HWCAP3
  2026-07-07 10:39   ` Yury Khrustalev
@ 2026-07-07 15:51     ` Matthieu Longo
  0 siblings, 0 replies; 15+ messages in thread
From: Matthieu Longo @ 2026-07-07 15:51 UTC (permalink / raw)
  To: Yury Khrustalev; +Cc: gdb-patches

On 07/07/2026 11:39, Yury Khrustalev wrote:
> On Fri, Jul 03, 2026 at 07:58:53PM +0100, Matthieu Longo wrote:
>> Add linux_get_hwcap3 helpers to GDB and gdbserver, mirroring the
>> existing linux_get_hwcap and linux_get_hwcap2 interfaces.
>>
>> The new helpers retrieve the AT_HWCAP3 auxiliary vector entry either
>> from explicitly supplied auxv data or from the current inferior.
>>
>> This prepares for future features that depend on HWCAP3 capability
>> bits.
> 
> Could you also add the same for HWCAP4?
> 
> Thanks,
> Yury
> 

Addressed in the next revision.
https://inbox.sourceware.org/gdb-patches/20260707154900.94542-1-matthieu.longo@arm.com/

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-07-07 15:53 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-03 18:58 [PATCH v1 0/8] gdb: various refactoring in linux-tdep Matthieu Longo
2026-07-03 18:58 ` [PATCH v1 1/8] gdb/linux-tdep: use pid_t consistently for process IDs Matthieu Longo
2026-07-05 21:22   ` Tom Tromey
2026-07-06  9:30     ` Matthieu Longo
2026-07-03 18:58 ` [PATCH v1 2/8] gdb/linux-tdep: change linux_fill_prpsinfo to return bool Matthieu Longo
2026-07-03 18:58 ` [PATCH v1 3/8] target_fileio_read_stralloc: add an optional length parameter Matthieu Longo
2026-07-03 18:58 ` [PATCH v1 4/8] gdb support: add index_type to array_view for documentation purpose Matthieu Longo
2026-07-03 19:55   ` Pedro Alves
2026-07-06 10:51     ` Matthieu Longo
2026-07-03 18:58 ` [PATCH v1 5/8] gdb support: add gdb::replace algorithm for iterators and ranges Matthieu Longo
2026-07-03 18:58 ` [PATCH v1 6/8] gdb: introduce helper class file_reader_t Matthieu Longo
2026-07-03 18:58 ` [PATCH v1 7/8] gdb/linux-tdep: parse ProtectionKey in /proc/PID/smaps Matthieu Longo
2026-07-03 18:58 ` [PATCH v1 8/8] gdb/linux: add helpers to read AT_HWCAP3 Matthieu Longo
2026-07-07 10:39   ` Yury Khrustalev
2026-07-07 15:51     ` Matthieu Longo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox