This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH V2] ABI changes for MPX.


Code reflects what is presented in the ABI document:
https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI
Here new class POINTER was added.  GDB code is modified to mirror
this new class. (page 134)

When using the return command, execution of a function is aborted
and present values are returned from that point.  That can cause
bound violations in the MPX context.  To avoid such side-effects
a new set variable was added "mpx-bnd-init-on-return" which controls
the initialization of bound register when using the return command.

As bound initialization it is understood the set of the BND register
to zero allowing the associated pointer to access the whole memory.

As default the value of "mpx-bnd-init-on-return" is set to 1.  So
bound register are initilized when using the "return" command.


2015-12-15  Walfred Tedeschi  <walfred.tedeschi@intel.com>

	* amd64-tdep.h (amd64_regnum): Add MPX registers.
	* amd64-tdep.c (amd64_reg_class): Add new class AMD64_POINTER.
	(amd64_merge_classes): Add AMD64_POINTER to merging classes rules.
	(amd64_classify): Add AMD64_POINTER.
	(mpx_bnd_init_on_return): New.
	(amd64_return_value): Add bound register.
	(amd64_return_value): Set bound values for returning.
	(amd64_push_arguments): Add new AMD64_POINTER class.
	(amd64_push_dummy_call): Initialize bound registers before call.
	(show_mpx_init_on_return): New funtion.
	(amd64_init_abi): Add new commands set/show mpx-bnd-init-on-return.

doc:
	gdb.texinfo: (Intel(R) Memory Protection Extensions): Add
	documentation on performing program function calls.

testsuite:
	amd64-mpx-call.c: New.
	amd64-mpx-call.exp: New.

---
 gdb/amd64-tdep.c                          | 109 ++++++++++++++++++++++++--
 gdb/amd64-tdep.h                          |   3 +-
 gdb/doc/gdb.texinfo                       |  23 ++++++
 gdb/testsuite/gdb.arch/amd64-mpx-call.c   | 126 ++++++++++++++++++++++++++++++
 gdb/testsuite/gdb.arch/amd64-mpx-call.exp |  92 ++++++++++++++++++++++
 5 files changed, 345 insertions(+), 8 deletions(-)
 create mode 100644 gdb/testsuite/gdb.arch/amd64-mpx-call.c
 create mode 100644 gdb/testsuite/gdb.arch/amd64-mpx-call.exp

diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 6096ce9..dee900f 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -461,6 +461,7 @@ amd64_pseudo_register_write (struct gdbarch *gdbarch,
 
 enum amd64_reg_class
 {
+  AMD64_POINTER,
   AMD64_INTEGER,
   AMD64_SSE,
   AMD64_SSEUP,
@@ -492,18 +493,22 @@ amd64_merge_classes (enum amd64_reg_class class1, enum amd64_reg_class class2)
   if (class1 == AMD64_MEMORY || class2 == AMD64_MEMORY)
     return AMD64_MEMORY;
 
-  /* Rule (d): If one of the classes is INTEGER, the result is INTEGER.  */
+  /* Rule (d): If one of the classes is POINTER, the result is POINTER.  */
+  if (class1 == AMD64_POINTER || class2 == AMD64_POINTER)
+    return AMD64_POINTER;
+
+  /* Rule (e): If one of the classes is INTEGER, the result is INTEGER.  */
   if (class1 == AMD64_INTEGER || class2 == AMD64_INTEGER)
     return AMD64_INTEGER;
 
-  /* Rule (e): If one of the classes is X87, X87UP, COMPLEX_X87 class,
+  /* Rule (f): If one of the classes is X87, X87UP, COMPLEX_X87 class,
      MEMORY is used as class.  */
   if (class1 == AMD64_X87 || class1 == AMD64_X87UP
       || class1 == AMD64_COMPLEX_X87 || class2 == AMD64_X87
       || class2 == AMD64_X87UP || class2 == AMD64_COMPLEX_X87)
     return AMD64_MEMORY;
 
-  /* Rule (f): Otherwise class SSE is used.  */
+  /* Rule (g): Otherwise class SSE is used.  */
   return AMD64_SSE;
 }
 
@@ -636,14 +641,17 @@ amd64_classify (struct type *type, enum amd64_reg_class theclass[2])
 
   theclass[0] = theclass[1] = AMD64_NO_CLASS;
 
+  /* Arguments of types (pointer and reference) are of the class pointer.  */
+  if (code == TYPE_CODE_PTR || code == TYPE_CODE_REF)
+    theclass[0] = AMD64_POINTER;
+
   /* Arguments of types (signed and unsigned) _Bool, char, short, int,
      long, long long, and pointers are in the INTEGER class.  Similarly,
      range types, used by languages such as Ada, are also in the INTEGER
      class.  */
-  if ((code == TYPE_CODE_INT || code == TYPE_CODE_ENUM
+  else if ((code == TYPE_CODE_INT || code == TYPE_CODE_ENUM
        || code == TYPE_CODE_BOOL || code == TYPE_CODE_RANGE
-       || code == TYPE_CODE_CHAR
-       || code == TYPE_CODE_PTR || code == TYPE_CODE_REF)
+       || code == TYPE_CODE_CHAR)
       && (len == 1 || len == 2 || len == 4 || len == 8))
     theclass[0] = AMD64_INTEGER;
 
@@ -693,16 +701,25 @@ amd64_classify (struct type *type, enum amd64_reg_class theclass[2])
     amd64_classify_aggregate (type, theclass);
 }
 
+/* Defines whether bound registers will be initialized when "return"
+   command is used.  Default is one avoiding bound violations.  */
+static int mpx_bnd_init_on_return = 1;
+
 static enum return_value_convention
 amd64_return_value (struct gdbarch *gdbarch, struct value *function,
 		    struct type *type, struct regcache *regcache,
 		    gdb_byte *readbuf, const gdb_byte *writebuf)
 {
   enum amd64_reg_class theclass[2];
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   int len = TYPE_LENGTH (type);
   static int integer_regnum[] = { AMD64_RAX_REGNUM, AMD64_RDX_REGNUM };
   static int sse_regnum[] = { AMD64_XMM0_REGNUM, AMD64_XMM1_REGNUM };
+  static int bnd_regnum[]
+    = {AMD64_BND0R_REGNUM, AMD64_BND0R_REGNUM, AMD64_BND2R_REGNUM,
+	AMD64_BND3R_REGNUM};
   int integer_reg = 0;
+  int bnd_reg = 0;
   int sse_reg = 0;
   int i;
 
@@ -730,6 +747,28 @@ amd64_return_value (struct gdbarch *gdbarch, struct value *function,
 
 	  regcache_raw_read_unsigned (regcache, AMD64_RAX_REGNUM, &addr);
 	  read_memory (addr, readbuf, TYPE_LENGTH (type));
+
+	  /*  If the class is memory, Boundary has to be stored in the bnd0 in
+	      case the application is MPX enabled.
+
+	      When the command "return" is issued, here writebuf not null,
+	      the state of bnd0 might not be ready to be returned
+	      (may be holding the bounds of another pointer).
+	      The variable mpx_bnd_init_on_return control initialization
+	      of bnd0. Zero value in the register mean all memory range,
+	      see Instruction Set Architecture.  In case bnd0 is not reset
+	      unrelated bound violations may occur.
+	      Otherwise, mpx_bnd_init_on_return is zero, the actual
+	      register value will be returned.  */
+
+	  if (writebuf != NULL
+	      && mpx_bnd_init_on_return && AMD64_BND0R_REGNUM > 0)
+	    {
+	      gdb_byte bnd_buf[16];
+
+	      memset (bnd_buf, 0, 16);
+	      regcache_raw_write (regcache, AMD64_BND0R_REGNUM, bnd_buf);
+	    }
 	}
 
       return RETURN_VALUE_ABI_RETURNS_ADDRESS;
@@ -765,6 +804,7 @@ amd64_return_value (struct gdbarch *gdbarch, struct value *function,
   for (i = 0; len > 0; i++, len -= 8)
     {
       int regnum = -1;
+      int bnd_reg_count = -1;
       int offset = 0;
 
       switch (theclass[i])
@@ -775,6 +815,13 @@ amd64_return_value (struct gdbarch *gdbarch, struct value *function,
 	  regnum = integer_regnum[integer_reg++];
 	  break;
 
+	case AMD64_POINTER:
+	  /* 3. If the class is POINTER, same rules of integer apply.  */
+	  regnum = integer_regnum[integer_reg++];
+	  /* But we need to allocate a bnd reg.  */
+	  bnd_reg_count = bnd_regnum[bnd_reg++];
+	  break;
+
 	case AMD64_SSE:
 	  /* 4. If the class is SSE, the next available SSE register
              of the sequence %xmm0, %xmm1 is used.  */
@@ -823,6 +870,17 @@ amd64_return_value (struct gdbarch *gdbarch, struct value *function,
 				 writebuf + i * 8);
     }
 
+  if (I387_BND0R_REGNUM (tdep) > 0)
+    {
+      gdb_byte bnd_buf[16];
+      int i, init_bnd;
+
+      memset (bnd_buf, 0, 16);
+      if (writebuf && mpx_bnd_init_on_return)
+	for (i = 0; i < I387_NUM_BND_REGS; i++)
+	  regcache_raw_write (regcache, AMD64_BND0R_REGNUM + i, bnd_buf);
+    }
+
   return RETURN_VALUE_REGISTER_CONVENTION;
 }
 
@@ -876,7 +934,7 @@ amd64_push_arguments (struct regcache *regcache, int nargs,
          this argument.  */
       for (j = 0; j < 2; j++)
 	{
-	  if (theclass[j] == AMD64_INTEGER)
+	  if (theclass[j] == AMD64_INTEGER || theclass[j] == AMD64_POINTER)
 	    needed_integer_regs++;
 	  else if (theclass[j] == AMD64_SSE)
 	    needed_sse_regs++;
@@ -907,6 +965,7 @@ amd64_push_arguments (struct regcache *regcache, int nargs,
 
 	      switch (theclass[j])
 		{
+		case AMD64_POINTER:
 		case AMD64_INTEGER:
 		  regnum = integer_regnum[integer_reg++];
 		  break;
@@ -966,8 +1025,23 @@ amd64_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
 		       int struct_return, CORE_ADDR struct_addr)
 {
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
   gdb_byte buf[8];
 
+  /* When MPX is enabled, all bnd registers have to be initialized
+     before the call.  This avoids an undesired  bound violation
+     during function's execution.  */
+
+  if (I387_BND0R_REGNUM (tdep) > 0)
+    {
+      gdb_byte bnd_buf[16];
+      int i;
+
+      memset (bnd_buf, 0, 16);
+      for (i = 0; i < I387_NUM_BND_REGS; i++)
+	regcache_raw_write (regcache, AMD64_BND0R_REGNUM + i, bnd_buf);
+    }
+
   /* Pass arguments.  */
   sp = amd64_push_arguments (regcache, nargs, args, sp, struct_return);
 
@@ -2932,6 +3006,19 @@ static const int amd64_record_regmap[] =
   AMD64_DS_REGNUM, AMD64_ES_REGNUM, AMD64_FS_REGNUM, AMD64_GS_REGNUM
 };
 
+/*  Show state of the setting variable mpx-bnd-init-on-return.  */
+static void
+show_mpx_bnd_init_on_return (struct ui_file *file, int from_tty,
+			  struct cmd_list_element *c, const char *value)
+{
+  if (mpx_bnd_init_on_return == 1)
+    fprintf_filtered (file,
+		    _("BND registers will be initialized on return.\n"));
+  else
+    fprintf_filtered (file,
+		    _("BND registers will not be initialized on return.\n"));
+}
+
 void
 amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 {
@@ -2982,6 +3069,14 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 
   if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.mpx") != NULL)
     {
+      add_setshow_boolean_cmd ("mpx-bnd-init-on-return", no_class,
+				&mpx_bnd_init_on_return, _("\
+Set the bnd registers to INIT state when returning from a call."), _("\
+Show the state of the mpx-bnd-init-on-return."),
+			    NULL,
+			    NULL,
+			    show_mpx_bnd_init_on_return,
+			    &setlist, &showlist);
       tdep->mpx_register_names = amd64_mpx_names;
       tdep->bndcfgu_regnum = AMD64_BNDCFGU_REGNUM;
       tdep->bnd0r_regnum = AMD64_BND0R_REGNUM;
diff --git a/gdb/amd64-tdep.h b/gdb/amd64-tdep.h
index 704225e..76a89b9 100644
--- a/gdb/amd64-tdep.h
+++ b/gdb/amd64-tdep.h
@@ -66,7 +66,8 @@ enum amd64_regnum
   AMD64_YMM0H_REGNUM,		/* %ymm0h */
   AMD64_YMM15H_REGNUM = AMD64_YMM0H_REGNUM + 15,
   AMD64_BND0R_REGNUM = AMD64_YMM15H_REGNUM + 1,
-  AMD64_BND3R_REGNUM = AMD64_BND0R_REGNUM + 3,
+  AMD64_BND1R_REGNUM, AMD64_BND2R_REGNUM,
+  AMD64_BND3R_REGNUM,
   AMD64_BNDCFGU_REGNUM,
   AMD64_BNDSTATUS_REGNUM,
   AMD64_XMM16_REGNUM,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b82f3c6..fd7fc24 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -22064,6 +22064,29 @@ whose bounds are to be changed, @var{lbound} and @var{ubound} are new values
 for lower and upper bounds respectively.
 @end table
 
+While calling functions from the debugger, of an Intel(R) MPX enabled program,
+boundary registers have to be initialized before performing the call, to avoid
+boundary violations while performing the call.  A bound is defined to be
+initialized when the pointer associated to that boundary can access the whole
+memory, in this case the register bound register associated to it has value 0,
+e.g. if the register associated is bnd0raw its value will be @{0x0, 0x0@}.
+It is possible to change the boundary values, if desired, by placing
+a breakpoint at prologue's end and setting bound registers as wished.
+After the call is performed bound register might be keept or not for
+further investigations.
+
+While the using the @command{return} bounds can propagate through
+execution causing a boundary violation.
+The behaviour of initializing bounds when using @command{return}
+can be controlled and vizualized via the following commands:
+@table @code
+@kindex set mpx-bnd-init-on-return
+When set to 1 bound registers will be initialized when returning from a
+calling a program function
+@kindex show mpx-bnd-init-on-return
+Show the state of mpx-bnd-init-on-return.
+@end table
+
 @node Alpha
 @subsection Alpha
 
diff --git a/gdb/testsuite/gdb.arch/amd64-mpx-call.c b/gdb/testsuite/gdb.arch/amd64-mpx-call.c
new file mode 100644
index 0000000..7f12e3e
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-mpx-call.c
@@ -0,0 +1,126 @@
+/* Test for inferior function calls MPX context.
+
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+   Contributed by Intel Corp.  <walfred.tedeschi@intel.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include "x86-cpuid.h"
+
+#define OUR_SIZE    5
+
+int gx[OUR_SIZE];
+int ga[OUR_SIZE];
+int gb[OUR_SIZE];
+int gc[OUR_SIZE];
+int gd[OUR_SIZE];
+
+unsigned int
+have_mpx (void)
+{
+  unsigned int eax, ebx, ecx, edx;
+
+  if (!__get_cpuid (1, &eax, &ebx, &ecx, &edx))
+    return 0;
+
+  if ((ecx & bit_OSXSAVE) == bit_OSXSAVE)
+    {
+      if (__get_cpuid_max (0, NULL) < 7)
+	return 0;
+
+      __cpuid_count (7, 0, eax, ebx, ecx, edx);
+
+      if ((ebx & bit_MPX) == bit_MPX)
+	return 1;
+      else
+	return 0;
+    }
+  return 0;
+}
+
+int
+bp1 (int value)
+{
+  return 1;
+}
+
+int
+bp2 (int value)
+{
+  return 1;
+}
+
+int
+upper (int *p, int *a, int *b, int *c, int *d, int len)
+{
+  int value;
+
+  value = * (p + len);
+  value = * (a + len);
+  value = * (b + len);
+  value = * (c + len);
+  value = * (d + len);
+
+  value = value - value + 1;
+  return value;
+}
+
+int *
+upper_ptr (int *p, int *a, int *b, int *c, int *d, int len)
+{
+  int value;
+
+  value = *(p + len);
+  value = *(a + len);
+  value = *(b + len);
+  value = *(c + len);
+  value = *(d + len);  /* bkpt 2.  */
+  free (p);
+  p = calloc (OUR_SIZE * 2, sizeof (int));
+
+  return ++p;
+}
+
+
+int
+main (void)
+{
+  if (have_mpx ())
+    {
+      int sx[OUR_SIZE];
+      int sa[OUR_SIZE];
+      int sb[OUR_SIZE];
+      int sc[OUR_SIZE];
+      int sd[OUR_SIZE];
+      int *x, *a, *b, *c, *d;
+
+      x = calloc (OUR_SIZE, sizeof (int));
+      a = calloc (OUR_SIZE, sizeof (int));
+      b = calloc (OUR_SIZE, sizeof (int));
+      c = calloc (OUR_SIZE, sizeof (int));
+      d = calloc (OUR_SIZE, sizeof (int));
+
+      upper (sx, sa, sb, sc, sd, 0);  /* bkpt 1.  */
+      x = upper_ptr (sx, sa, sb, sc, sd, 0);
+
+      free (x); /* bkpt 3.  */
+      free (a);
+      free (b);
+      free (c);
+      free (d);
+    }
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/amd64-mpx-call.exp b/gdb/testsuite/gdb.arch/amd64-mpx-call.exp
new file mode 100644
index 0000000..6b756b2
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-mpx-call.exp
@@ -0,0 +1,92 @@
+# Copyright (C) 2015 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <walfred.tedeschi@intel.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+if { ![istarget i?86-*-*] && ![istarget x86_64-*-* ] } {
+    verbose "Skipping x86 MPX tests."
+    return
+}
+
+standard_testfile
+
+set comp_flags "-mmpx -fcheck-pointer-bounds -I${srcdir}/../nat"
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} \
+ [list debug nowarnings additional_flags=${comp_flags}]] } {
+    return -1
+}
+
+if ![runto_main] {
+    untested "could not run to main"
+    return -1
+}
+
+gdb_test_multiple "print have_mpx ()" "have mpx" {
+    -re ".. = 1\r\n$gdb_prompt " {
+        pass "check whether processor supports MPX"
+    }
+    -re ".. = 0\r\n$gdb_prompt " {
+        verbose "processor does not support MPX; skipping MPX tests"
+        return
+    }
+}
+
+# Needed by the return command.
+gdb_test_no_output "set confirm off"
+
+set break "bkpt 1."
+gdb_breakpoint [gdb_get_line_number "${break}"]
+gdb_continue_to_breakpoint "${break}" ".*${break}.*"
+gdb_test "p upper (x, a, b, c, d, 0)" "= 1"\
+  "test the call of int function - int"
+gdb_test "p upper_ptr (x, a, b, c, d, 0)"\
+  "= \\\(int \\\*\\\) $hex" "test the call of function - ptr"
+
+set break "bkpt 2."
+gdb_breakpoint [gdb_get_line_number "${break}"]
+gdb_continue_to_breakpoint "${break}" ".*${break}.*"
+set break "bkpt 3."
+gdb_breakpoint [gdb_get_line_number "${break}"]
+gdb_test "p \$bound0 = \$bnd0" "" "Saving the\
+  bnd0 result on a convenience variable 1"
+
+gdb_test "return a"
+gdb_test "p \(\$bnd0\.ubound == \$bound0\.ubound\)"\
+  "= 0" "return with bnd initialization off - ubound"
+gdb_test "p \(\$bnd0\.lbound ==\$bound0\.lbound\)"\
+  "= 0" "return with bnd initialization off - lbound"
+
+runto_main
+gdb_test_no_output "set mpx-bnd-init-on-return off"\
+  Turn off initialization on return"
+
+set break "bkpt 2."
+gdb_breakpoint [gdb_get_line_number "${break}"]
+gdb_continue_to_breakpoint "${break}" ".*${break}.*"
+set break "bkpt 3."
+gdb_breakpoint [gdb_get_line_number "${break}"]
+gdb_test "p \$bound0 = \$bnd0" "" "Saving the\
+  bnd0 result on a convenience variable 2"
+gdb_test "return a"
+gdb_test "p \(\$bnd0\.ubound == \$bound0\.ubound\)"\
+  "= 1" "return with bnd initialization on - ubound"
+gdb_test "p \(\$bnd0\.lbound ==\$bound0\.lbound\)"\
+  "= 1" "return with bnd initialization on - lbound"
+
+gdb_test "show mpx-bnd-init-on-return"\
+  "BND registers will not be initialized on return."\
+  "double check initialization on return"
-- 
2.1.4


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]