]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Add new register access interface to expr.c
authorZoran Zaric <Zoran.Zaric@amd.com>
Tue, 22 Sep 2020 13:30:29 +0000 (14:30 +0100)
committerZoran Zaric <zoran.zaric@amd.com>
Fri, 5 Nov 2021 11:29:37 +0000 (11:29 +0000)
DWARF expression evaluator is currently using get_frame_register_bytes
and put_frame_register_bytes interface for register access.

The problem with evaluator using this interface is that it allows a
bleed out register access. This means that if the caller specifies a
larger amount of data then the size of a specified register, the
operation will continue accessing the neighboring registers until a
full amount of data has been reached.

DWARF specification does not define this behavior, so a new simplified
register access interface is needed instead.

* dwarf2/expr.c (read_from_register): New function.
(write_to_register): New function.
(rw_pieced_value): Now calls the read_from_register and
write_to_register functions.

gdb/dwarf2/expr.c

index 652161955d5265ff48922f21606e5b5b9f8de55b..0ca8528298a66defd002daac45879fceaa318130 100644 (file)
@@ -99,6 +99,96 @@ read_addr_from_reg (frame_info *frame, int reg)
   return address_from_register (regnum, frame);
 }
 
+/* Read register REGNUM's contents in a given FRAME context.
+
+   The data read is offsetted by OFFSET, and the number of bytes read
+   is defined by LENGTH.  The data is then copied into the
+   caller-managed buffer BUF.
+
+   If the register is optimized out or unavailable for the given
+   FRAME, the OPTIMIZED and UNAVAILABLE outputs are set
+   accordingly  */
+
+static void
+read_from_register (frame_info *frame, int regnum,
+                   CORE_ADDR offset, gdb::array_view<gdb_byte> buf,
+                   int *optimized, int *unavailable)
+{
+  gdbarch *arch = get_frame_arch (frame);
+  int regsize = register_size (arch, regnum);
+  int numregs = gdbarch_num_cooked_regs (arch);
+  int length = buf.size ();
+
+  /* If a register is wholly inside the OFFSET, skip it.  */
+  if (frame == NULL || !regsize
+      || offset + length > regsize || numregs < regnum)
+    {
+      *optimized = 0;
+      *unavailable = 1;
+      return;
+    }
+
+  gdb::byte_vector temp_buf (regsize);
+  enum lval_type lval;
+  CORE_ADDR address;
+  int realnum;
+
+  frame_register (frame, regnum, optimized, unavailable,
+                 &lval, &address, &realnum, temp_buf.data ());
+
+  if (!*optimized && !*unavailable)
+     memcpy (buf.data (), (char *) temp_buf.data () + offset, length);
+
+  return;
+}
+
+/* Write register REGNUM's contents in a given FRAME context.
+
+   The data written is offsetted by OFFSET, and the number of bytes
+   written is defined by LENGTH.  The data is copied from
+   caller-managed buffer BUF.
+
+   If the register is optimized out or unavailable for the given
+   FRAME, the OPTIMIZED and UNAVAILABLE outputs are set
+   accordingly. */
+
+static void
+write_to_register (frame_info *frame, int regnum,
+                  CORE_ADDR offset, gdb::array_view<gdb_byte> buf,
+                  int *optimized, int *unavailable)
+{
+  gdbarch *arch = get_frame_arch (frame);
+  int regsize = register_size (arch, regnum);
+  int numregs = gdbarch_num_cooked_regs (arch);
+  int length = buf.size ();
+
+  /* If a register is wholly inside of OFFSET, skip it.  */
+  if (frame == NULL || !regsize
+     || offset + length > regsize || numregs < regnum)
+    {
+      *optimized = 0;
+      *unavailable = 1;
+      return;
+    }
+
+  gdb::byte_vector temp_buf (regsize);
+  enum lval_type lval;
+  CORE_ADDR address;
+  int realnum;
+
+  frame_register (frame, regnum, optimized, unavailable,
+                 &lval, &address, &realnum, temp_buf.data ());
+
+  if (!*optimized && !*unavailable)
+    {
+      memcpy ((char *) temp_buf.data () + offset, buf.data (), length);
+
+      put_frame_register (frame, regnum, temp_buf.data ());
+    }
+
+  return;
+}
+
 struct piece_closure
 {
   /* Reference count.  */
@@ -242,24 +332,19 @@ rw_pieced_value (value *v, value *from, bool check_optimized)
            if (from == nullptr)
              {
                /* Read mode.  */
-               if (!get_frame_register_bytes (frame, gdb_regnum,
-                                              bits_to_skip / 8,
-                                              buffer, &optim, &unavail))
+               read_from_register (frame, gdb_regnum, bits_to_skip / 8,
+                                   buffer, &optim, &unavail);
+
+               if (optim)
                  {
-                   if (optim)
-                     {
-                       if (check_optimized)
-                         return true;
-                       mark_value_bits_optimized_out (v, offset,
-                                                      this_size_bits);
-                     }
-                   if (unavail && !check_optimized)
-                     mark_value_bits_unavailable (v, offset,
-                                                  this_size_bits);
-                   break;
+                   if (check_optimized)
+                     return true;
+                   mark_value_bits_optimized_out (v, offset, this_size_bits);
                  }
-
-               if (!check_optimized)
+               if (unavail)
+                 mark_value_bits_unavailable (v, offset, this_size_bits);
+               /* Only copy data if valid.  */
+               if (!optim && !unavail && !check_optimized)
                  copy_bitwise (v_contents, offset,
                                buffer.data (), bits_to_skip % 8,
                                this_size_bits, bits_big_endian);
@@ -271,9 +356,8 @@ rw_pieced_value (value *v, value *from, bool check_optimized)
                  {
                    /* Data is copied non-byte-aligned into the register.
                       Need some bits from original register value.  */
-                   get_frame_register_bytes (frame, gdb_regnum,
-                                             bits_to_skip / 8,
-                                             buffer, &optim, &unavail);
+                   read_from_register (frame, gdb_regnum, bits_to_skip / 8,
+                                       buffer, &optim, &unavail);
                    if (optim)
                      throw_error (OPTIMIZED_OUT_ERROR,
                                   _("Can't do read-modify-write to "
@@ -289,9 +373,8 @@ rw_pieced_value (value *v, value *from, bool check_optimized)
                copy_bitwise (buffer.data (), bits_to_skip % 8,
                              from_contents, offset,
                              this_size_bits, bits_big_endian);
-               put_frame_register_bytes (frame, gdb_regnum,
-                                         bits_to_skip / 8,
-                                         buffer);
+               write_to_register (frame, gdb_regnum, bits_to_skip / 8,
+                                  buffer, &optim, &unavail);
              }
          }
          break;