[PATCH v1] ABI changes for Intel(R) MPX.

Walfred Tedeschi
2015-10-26T12:55:31Z

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.

New set and show command was added as option for initializing bounds when
returning from inferior calls or not.

2015-08-25 Walfred Tedeschi <walfred.tedeschi@intel.com>

* 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 | 97 +++++++++++++++++++++--
gdb/doc/gdb.texinfo | 17 ++++
gdb/testsuite/gdb.arch/amd64-mpx-call.c | 124 ++++++++++++++++++++++++++++++
gdb/testsuite/gdb.arch/amd64-mpx-call.exp | 90 ++++++++++++++++++++++
4 files changed, 321 insertions(+), 7 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 0fa4d54..2fa555d 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -473,6 +473,7 @@ amd64_pseudo_register_write (struct gdbarch *gdbarch,

enum amd64_reg_class
{
+ AMD64_POINTER,
AMD64_INTEGER,
AMD64_SSE,
AMD64_SSEUP,
@@ -504,18 +505,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;
}

@@ -648,14 +653,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;

@@ -705,16 +713,22 @@ amd64_classify (struct type *type, enum amd64_reg_class theclass[2])
amd64_classify_aggregate (type, theclass);
}

+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;

@@ -742,6 +756,20 @@ 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
+ the case the application is MPX enabled.
+ In order to be return a boundary value mpx_bnd_init_on_return
+ has to be 0. Otherwise the actual value present in the register
+ will be returned. */
+
+ if (writebuf && 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;
@@ -777,6 +805,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])
@@ -787,6 +816,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 applies. */
+ 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. */
@@ -835,6 +871,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;
}
\f
@@ -888,7 +935,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++;
@@ -919,6 +966,7 @@ amd64_push_arguments (struct regcache *regcache, int nargs,

switch (theclass[j])
{
+ case AMD64_POINTER:
case AMD64_INTEGER:
regnum = integer_regnum[integer_reg++];
break;
@@ -978,8 +1026,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 undesired bound violations while executing the call.
+ At this point in time we don need to worry about the */
+
+ 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);

@@ -2944,6 +3007,18 @@ static const int amd64_record_regmap[] =
AMD64_DS_REGNUM, AMD64_ES_REGNUM, AMD64_FS_REGNUM, AMD64_GS_REGNUM
};

+static void
+show_mpx_init_on_return (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ if (mpx_bnd_init_on_return > 0)
+ 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)
{
@@ -2994,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_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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 3c1f785..1d8e667 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -22042,6 +22042,23 @@ 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 of an MPX enabled program boundary registers have
+to be initialized before performing the call. Intended to avoid unexpected
+side effects, as receiving a bound violation signal while performing
+the operation. Nevertheless is possible to change the boundary values if
+desired in placing a breakpoint at the end of the prologue and setting
+bound registers as wished.
+After the call is performed bound register might be keept or not for
+further investigations. The behaviour of initializing bounds on returning
+from a program function calls can be controlled and vizualized via the 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..726bc76
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-mpx-call.c
@@ -0,0 +1,124 @@
+/*
+* Copyright 2012 Free Software Foundation, Inc.
+*
+* Contributed by Intel Corp. <christian.himpel@intel.com>,
+* <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/>.
+*
+* This test was converted from idb/test/td/runreg/Biendian/bi.c_bitfield
+*
+*/
+#include <stdio.h>
+#include "x86-cpuid.h"
+
+#define MYTYPE int
+#define OUR_SIZE 5
+
+MYTYPE gx[OUR_SIZE];
+MYTYPE ga[OUR_SIZE];
+MYTYPE gb[OUR_SIZE];
+MYTYPE gc[OUR_SIZE];
+MYTYPE 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 (MYTYPE value)
+{
+ return 1;
+}
+
+int
+bp2 (MYTYPE value)
+{
+ return 1;
+}
+
+void
+upper (MYTYPE * p, MYTYPE * a, MYTYPE * b, MYTYPE * c, MYTYPE * d, int len)
+{
+ MYTYPE value;
+ value = *(p + len);
+ value = *(a + len);
+ value = *(b + len);
+ value = *(c + len);
+ value = *(d + len);
+}
+
+MYTYPE *
+upper_ptr (MYTYPE * p, MYTYPE * a, MYTYPE * b, MYTYPE * c, MYTYPE * d, int len)
+{
+ MYTYPE 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 (MYTYPE));
+ return ++p;
+}
+
+
+int
+main (void)
+{
+ if (have_mpx ())
+ {
+ MYTYPE sx[OUR_SIZE];
+ MYTYPE sa[OUR_SIZE];
+ MYTYPE sb[OUR_SIZE];
+ MYTYPE sc[OUR_SIZE];
+ MYTYPE sd[OUR_SIZE];
+ MYTYPE *x, *a, *b, *c, *d;
+
+ x = calloc (OUR_SIZE, sizeof (MYTYPE));
+ a = calloc (OUR_SIZE, sizeof (MYTYPE));
+ b = calloc (OUR_SIZE, sizeof (MYTYPE));
+ c = calloc (OUR_SIZE, sizeof (MYTYPE));
+ d = calloc (OUR_SIZE, sizeof (MYTYPE));
+
+ 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..d293778
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-mpx-call.exp
@@ -0,0 +1,90 @@
+# Copyright 2012 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
+}
+
+send_gdb "print have_mpx ()\r"
+gdb_expect {
+ -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
+ }
+ -re ".*$gdb_prompt $" {
+ fail "check whether processor supports MPX"
+ }
+ timeout {
+ fail "check whether processor supports MPX (timeout)"
+ }
+}
+
+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)" "" "test the call of a function"
+gdb_test "p upper_ptr (x, a, b, c, d, 0)" "" "test the call of a function"
+
+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" "" "nm"
+gdb_test "return a"
+gdb_test "p \(\$bnd0\.ubound == \$bound0\.ubound\ && \$bnd0\.lbound ==\
+\$bound0\.lbound\ \)" "0" "after return with initialization off"
+
+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" "" "nm"
+gdb_test "return a"
+gdb_test "p \(\$bnd0\.ubound == \$bound0\.ubound\ && \$bnd0\.lbound ==\
+\$bound0\.lbound\ \)" "1" "after return with initialization on"
+
+gdb_test "show mpx-bnd-init-on-return" "BND registers will not be initialized\
+on return." "double check of initialization on return"
+
+send_gdb "quit\n"
--
2.1.4