summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuis Machado <luis.machado@linaro.org>2020-06-15 14:24:53 -0300
committerLuis Machado <luis.machado@linaro.org>2021-03-24 14:54:50 -0300
commit1e735120b95236b1f0fd8c383e64b02b64909d7c (patch)
tree9db3580d29028767a59b98cb49c7d2cb142e704d
parent93e447c605e549f183681ed60e729d3d50f4d113 (diff)
downloadbinutils-gdb-1e735120b95236b1f0fd8c383e64b02b64909d7c.tar.gz
Refactor parsing of /proc/<pid>/smaps
The Linux kernel exposes the information about MTE-protected pages via the proc filesystem, more specifically through the smaps file. What we're looking for is a mapping with the 'mt' flag, which tells us that mapping was created with a PROT_MTE flag and, thus, is capable of using memory tagging. We already parse that file for other purposes (core file generation/filtering), so this patch refactors the code to make the parsing of the smaps file reusable for memory tagging. The function linux_address_in_memtag_page uses the refactored code to allow querying for memory tag support in a particular address, and it gets used in the next patch. gdb/ChangeLog: 2021-03-24 Luis Machado <luis.machado@linaro.org> * linux-tdep.c (struct smaps_vmflags) <memory_tagging>: New flag bit. (struct smaps_data): New struct. (decode_vmflags): Handle the 'mt' flag. (parse_smaps_data): New function, refactored from linux_find_memory_regions_full. (linux_address_in_memtag_page): New function. (linux_find_memory_regions_full): Refactor into parse_smaps_data. * linux-tdep.h (linux_address_in_memtag_page): New prototype.
-rw-r--r--gdb/ChangeLog12
-rw-r--r--gdb/linux-tdep.c357
-rw-r--r--gdb/linux-tdep.h4
3 files changed, 251 insertions, 122 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index da4bb26c21c..9d155b8f5e5 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,17 @@
2021-03-24 Luis Machado <luis.machado@linaro.org>
+ * linux-tdep.c (struct smaps_vmflags) <memory_tagging>: New flag
+ bit.
+ (struct smaps_data): New struct.
+ (decode_vmflags): Handle the 'mt' flag.
+ (parse_smaps_data): New function, refactored from
+ linux_find_memory_regions_full.
+ (linux_address_in_memtag_page): New function.
+ (linux_find_memory_regions_full): Refactor into parse_smaps_data.
+ * linux-tdep.h (linux_address_in_memtag_page): New prototype.
+
+2021-03-24 Luis Machado <luis.machado@linaro.org>
+
* linux-tdep.c (linux_find_memory_regions_full): Use std::string
instead of char arrays.
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 17ed9cac8a7..38c5ecf5b59 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -88,8 +88,33 @@ struct smaps_vmflags
/* Is this a MAP_SHARED mapping (VM_SHARED, "sh"). */
unsigned int shared_mapping : 1;
+
+ /* Memory map has memory tagging enabled. */
+
+ unsigned int memory_tagging : 1;
};
+/* Data structure that holds the information contained in the
+ /proc/<pid>/smaps file. */
+
+struct smaps_data
+{
+ ULONGEST start_address;
+ ULONGEST end_address;
+ std::string filename;
+ struct smaps_vmflags vmflags;
+ bool read;
+ bool write;
+ bool exec;
+ bool priv;
+ bool has_anonymous;
+ bool mapping_anon_p;
+ bool mapping_file_p;
+
+ ULONGEST inode;
+ ULONGEST offset;
+};
+
/* Whether to take the /proc/PID/coredump_filter into account when
generating a corefile. */
@@ -476,6 +501,8 @@ decode_vmflags (char *p, struct smaps_vmflags *v)
v->exclude_coredump = 1;
else if (strcmp (s, "sh") == 0)
v->shared_mapping = 1;
+ else if (strcmp (s, "mt") == 0)
+ v->memory_tagging = 1;
}
}
@@ -1271,6 +1298,180 @@ typedef int linux_dump_mapping_p_ftype (filter_flags filterflags,
ULONGEST addr,
ULONGEST offset);
+/* 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. */
+
+static std::vector<struct smaps_data>
+parse_smaps_data (const char *data,
+ const std::string maps_filename)
+{
+ char *line, *t;
+
+ gdb_assert (data != nullptr);
+
+ line = strtok_r ((char *) data, "\n", &t);
+
+ std::vector<struct smaps_data> smaps;
+
+ while (line != NULL)
+ {
+ ULONGEST addr, endaddr, offset, inode;
+ const char *permissions, *device, *filename;
+ struct smaps_vmflags v;
+ size_t permissions_len, device_len;
+ int read, write, exec, priv;
+ int has_anonymous = 0;
+ int mapping_anon_p;
+ int mapping_file_p;
+
+ memset (&v, 0, sizeof (v));
+ read_mapping (line, &addr, &endaddr, &permissions, &permissions_len,
+ &offset, &device, &device_len, &inode, &filename);
+ mapping_anon_p = mapping_is_anonymous_p (filename);
+ /* If the mapping is not anonymous, then we can consider it
+ to be file-backed. These two states (anonymous or
+ file-backed) seem to be exclusive, but they can actually
+ coexist. For example, if a file-backed mapping has
+ "Anonymous:" pages (see more below), then the Linux
+ kernel will dump this mapping when the user specified
+ that she only wants anonymous mappings in the corefile
+ (*even* when she explicitly disabled the dumping of
+ file-backed mappings). */
+ mapping_file_p = !mapping_anon_p;
+
+ /* Decode permissions. */
+ read = (memchr (permissions, 'r', permissions_len) != 0);
+ write = (memchr (permissions, 'w', permissions_len) != 0);
+ exec = (memchr (permissions, 'x', permissions_len) != 0);
+ /* 'private' here actually means VM_MAYSHARE, and not
+ VM_SHARED. In order to know if a mapping is really
+ private or not, we must check the flag "sh" in the
+ VmFlags field. This is done by decode_vmflags. However,
+ if we are using a Linux kernel released before the commit
+ 834f82e2aa9a8ede94b17b656329f850c1471514 (3.10), we will
+ not have the VmFlags there. In this case, there is
+ really no way to know if we are dealing with VM_SHARED,
+ so we just assume that VM_MAYSHARE is enough. */
+ priv = memchr (permissions, 'p', permissions_len) != 0;
+
+ /* Try to detect if region should be dumped by parsing smaps
+ counters. */
+ for (line = strtok_r (NULL, "\n", &t);
+ line != NULL && line[0] >= 'A' && line[0] <= 'Z';
+ line = strtok_r (NULL, "\n", &t))
+ {
+ char keyword[64 + 1];
+
+ if (sscanf (line, "%64s", keyword) != 1)
+ {
+ warning (_("Error parsing {s,}maps file '%s'"),
+ maps_filename.c_str ());
+ break;
+ }
+
+ if (strcmp (keyword, "Anonymous:") == 0)
+ {
+ /* Older Linux kernels did not support the
+ "Anonymous:" counter. Check it here. */
+ has_anonymous = 1;
+ }
+ else if (strcmp (keyword, "VmFlags:") == 0)
+ decode_vmflags (line, &v);
+
+ if (strcmp (keyword, "AnonHugePages:") == 0
+ || strcmp (keyword, "Anonymous:") == 0)
+ {
+ unsigned long number;
+
+ if (sscanf (line, "%*s%lu", &number) != 1)
+ {
+ warning (_("Error parsing {s,}maps file '%s' number"),
+ maps_filename.c_str ());
+ break;
+ }
+ if (number > 0)
+ {
+ /* Even if we are dealing with a file-backed
+ mapping, if it contains anonymous pages we
+ consider it to be *also* an anonymous
+ mapping, because this is what the Linux
+ kernel does:
+
+ // Dump segments that have been written to.
+ if (vma->anon_vma && FILTER(ANON_PRIVATE))
+ goto whole;
+
+ Note that if the mapping is already marked as
+ file-backed (i.e., mapping_file_p is
+ non-zero), then this is a special case, and
+ this mapping will be dumped either when the
+ user wants to dump file-backed *or* anonymous
+ mappings. */
+ mapping_anon_p = 1;
+ }
+ }
+ }
+ /* Save the smaps entry to the vector. */
+ struct smaps_data map;
+
+ map.start_address = addr;
+ map.end_address = endaddr;
+ map.filename = filename;
+ map.vmflags = v;
+ map.read = read? true : false;
+ map.write = write? true : false;
+ map.exec = exec? true : false;
+ map.priv = priv? true : false;
+ map.has_anonymous = has_anonymous;
+ map.mapping_anon_p = mapping_anon_p? true : false;
+ map.mapping_file_p = mapping_file_p? true : false;
+ map.offset = offset;
+ map.inode = inode;
+
+ smaps.emplace_back (map);
+ }
+
+ return smaps;
+}
+
+/* See linux-tdep.h. */
+
+bool
+linux_address_in_memtag_page (CORE_ADDR address)
+{
+ if (current_inferior ()->fake_pid_p)
+ return false;
+
+ pid_t pid = current_inferior ()->pid;
+
+ std::string smaps_file = string_printf ("/proc/%d/smaps", pid);
+
+ gdb::unique_xmalloc_ptr<char> data
+ = target_fileio_read_stralloc (NULL, smaps_file.c_str ());
+
+ if (data == nullptr)
+ return false;
+
+ /* Parse the contents of smaps into a vector. */
+ std::vector<struct smaps_data> smaps
+ = parse_smaps_data (data.get (), smaps_file);
+
+ for (const smaps_data &map : smaps)
+ {
+ /* Is the address within [start_address, end_address) in a page
+ mapped with memory tagging? */
+ if (address >= map.start_address
+ && address < map.end_address
+ && map.vmflags.memory_tagging)
+ return true;
+ }
+
+ return false;
+}
+
/* List memory regions in the inferior for a corefile. */
static int
@@ -1321,137 +1522,49 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch,
/* Older Linux kernels did not support /proc/PID/smaps. */
maps_filename = string_printf ("/proc/%d/maps", pid);
data = target_fileio_read_stralloc (NULL, maps_filename.c_str ());
+
+ if (data == nullptr)
+ return 1;
}
- if (data != NULL)
+ /* Parse the contents of smaps into a vector. */
+ std::vector<struct smaps_data> smaps
+ = parse_smaps_data (data.get (), maps_filename.c_str ());
+
+ for (const struct smaps_data &map : smaps)
{
- char *line, *t;
+ int should_dump_p = 0;
- line = strtok_r (data.get (), "\n", &t);
- while (line != NULL)
+ if (map.has_anonymous)
{
- ULONGEST addr, endaddr, offset, inode;
- const char *permissions, *device, *filename;
- struct smaps_vmflags v;
- size_t permissions_len, device_len;
- int read, write, exec, priv;
- int has_anonymous = 0;
- int should_dump_p = 0;
- int mapping_anon_p;
- int mapping_file_p;
-
- memset (&v, 0, sizeof (v));
- read_mapping (line, &addr, &endaddr, &permissions, &permissions_len,
- &offset, &device, &device_len, &inode, &filename);
- mapping_anon_p = mapping_is_anonymous_p (filename);
- /* If the mapping is not anonymous, then we can consider it
- to be file-backed. These two states (anonymous or
- file-backed) seem to be exclusive, but they can actually
- coexist. For example, if a file-backed mapping has
- "Anonymous:" pages (see more below), then the Linux
- kernel will dump this mapping when the user specified
- that she only wants anonymous mappings in the corefile
- (*even* when she explicitly disabled the dumping of
- file-backed mappings). */
- mapping_file_p = !mapping_anon_p;
-
- /* Decode permissions. */
- read = (memchr (permissions, 'r', permissions_len) != 0);
- write = (memchr (permissions, 'w', permissions_len) != 0);
- exec = (memchr (permissions, 'x', permissions_len) != 0);
- /* 'private' here actually means VM_MAYSHARE, and not
- VM_SHARED. In order to know if a mapping is really
- private or not, we must check the flag "sh" in the
- VmFlags field. This is done by decode_vmflags. However,
- if we are using a Linux kernel released before the commit
- 834f82e2aa9a8ede94b17b656329f850c1471514 (3.10), we will
- not have the VmFlags there. In this case, there is
- really no way to know if we are dealing with VM_SHARED,
- so we just assume that VM_MAYSHARE is enough. */
- priv = memchr (permissions, 'p', permissions_len) != 0;
-
- /* Try to detect if region should be dumped by parsing smaps
- counters. */
- for (line = strtok_r (NULL, "\n", &t);
- line != NULL && line[0] >= 'A' && line[0] <= 'Z';
- line = strtok_r (NULL, "\n", &t))
- {
- char keyword[64 + 1];
-
- if (sscanf (line, "%64s", keyword) != 1)
- {
- warning (_("Error parsing {s,}maps file '%s'"),
- maps_filename.c_str ());
- break;
- }
-
- if (strcmp (keyword, "Anonymous:") == 0)
- {
- /* Older Linux kernels did not support the
- "Anonymous:" counter. Check it here. */
- has_anonymous = 1;
- }
- else if (strcmp (keyword, "VmFlags:") == 0)
- decode_vmflags (line, &v);
-
- if (strcmp (keyword, "AnonHugePages:") == 0
- || strcmp (keyword, "Anonymous:") == 0)
- {
- unsigned long number;
-
- if (sscanf (line, "%*s%lu", &number) != 1)
- {
- warning (_("Error parsing {s,}maps file '%s' number"),
- maps_filename.c_str ());
- break;
- }
- if (number > 0)
- {
- /* Even if we are dealing with a file-backed
- mapping, if it contains anonymous pages we
- consider it to be *also* an anonymous
- mapping, because this is what the Linux
- kernel does:
-
- // Dump segments that have been written to.
- if (vma->anon_vma && FILTER(ANON_PRIVATE))
- goto whole;
-
- Note that if the mapping is already marked as
- file-backed (i.e., mapping_file_p is
- non-zero), then this is a special case, and
- this mapping will be dumped either when the
- user wants to dump file-backed *or* anonymous
- mappings. */
- mapping_anon_p = 1;
- }
- }
- }
-
- if (has_anonymous)
- should_dump_p = should_dump_mapping_p (filterflags, &v, priv,
- mapping_anon_p,
- mapping_file_p,
- filename, addr, offset);
- else
- {
- /* Older Linux kernels did not support the "Anonymous:" counter.
- If it is missing, we can't be sure - dump all the pages. */
- should_dump_p = 1;
- }
-
- /* Invoke the callback function to create the corefile segment. */
- if (should_dump_p)
- func (addr, endaddr - addr, offset, inode,
- read, write, exec, 1, /* MODIFIED is true because we
- want to dump the mapping. */
- filename, obfd);
+ should_dump_p
+ = should_dump_mapping_p (filterflags, &map.vmflags,
+ map.priv,
+ map.mapping_anon_p,
+ map.mapping_file_p,
+ map.filename.c_str (),
+ map.start_address,
+ map.offset);
+ }
+ else
+ {
+ /* Older Linux kernels did not support the "Anonymous:" counter.
+ If it is missing, we can't be sure - dump all the pages. */
+ should_dump_p = 1;
}
- return 0;
+ /* Invoke the callback function to create the corefile segment. */
+ if (should_dump_p)
+ {
+ func (map.start_address, map.end_address - map.start_address,
+ map.offset, map.inode, map.read, map.write, map.exec,
+ 1, /* MODIFIED is true because we want to dump
+ the mapping. */
+ map.filename.c_str (), obfd);
+ }
}
- return 1;
+ return 0;
}
/* A structure for passing information through
diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h
index 7db6eceeab1..28b60e46579 100644
--- a/gdb/linux-tdep.h
+++ b/gdb/linux-tdep.h
@@ -43,6 +43,10 @@ DEF_ENUM_FLAGS_TYPE (enum linux_siginfo_extra_field_values,
struct type *linux_get_siginfo_type_with_fields (struct gdbarch *gdbarch,
linux_siginfo_extra_fields);
+/* Return true if ADDRESS is within the boundaries of a page mapped with
+ memory tagging protection. */
+bool linux_address_in_memtag_page (CORE_ADDR address);
+
typedef char *(*linux_collect_thread_registers_ftype) (const struct regcache *,
ptid_t,
bfd *, char *, int *,