aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/NEWS4
-rw-r--r--gdb/doc/gdb.texinfo20
-rw-r--r--gdb/i386-tdep.c273
-rw-r--r--gdb/testsuite/gdb.arch/i386-mpx-map.c93
-rw-r--r--gdb/testsuite/gdb.arch/i386-mpx-map.exp76
5 files changed, 465 insertions, 1 deletions
diff --git a/gdb/NEWS b/gdb/NEWS
index bbfb55d..11ccd4a 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -90,6 +90,10 @@ tui enable
tui disable
Explicit commands for enabling and disabling tui mode.
+show mpx bound
+set mpx bound on i386 and amd64
+ Support for bound table investigation on Intel(R) MPX enabled applications.
+
* New options
set debug dwarf-die
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 9ea846a..9e9138b 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -22127,7 +22127,7 @@ be returned in a register.
@kindex show struct-convention
Show the current setting of the convention to return @code{struct}s
from functions.
-@end table
+
@subsubsection Intel(R) @dfn{Memory Protection Extensions} (MPX).
@cindex Intel(R) Memory Protection Extensions (MPX).
@@ -22162,6 +22162,24 @@ counterpart. When the bnd0@dots{}bnd3 registers are displayed via
Python, the display includes the memory size, in bits, accessible to
the pointer.
+Bounds can also be stored in bounds tables, which are stored in
+application memory. These tables store bounds for pointers by specifying
+the bounds pointer's value along with its bounds. Evaluating and changing
+bounds located in bound tables is therefore interesting while investigating
+bugs on MPX context. @value{GDBN} provides commands for this purpose:
+
+@item show mpx bound @var{pointer}
+@kindex show mpx bound
+Display bounds of the given @var{pointer}.
+
+@item set mpx bound @var{pointer}, @var{lbound}, @var{ubound}
+@kindex set mpx bound
+Set the bounds of a pointer in the bound table.
+This command takes three parameters: @var{pointer} is the pointers
+whose bounds are to be changed, @var{lbound} and @var{ubound} are new values
+for lower and upper bounds respectively.
+@end table
+
@node Alpha
@subsection Alpha
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index b4ffe1b..6304591 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -8619,6 +8619,251 @@ i386_target_description (uint64_t xcr0)
}
}
+#define MPX_BASE_MASK (~(ULONGEST) 0xfff)
+
+/* Find the bound directory base address. */
+
+static unsigned long
+i386_mpx_bd_base (void)
+{
+ struct regcache *rcache;
+ struct gdbarch_tdep *tdep;
+ ULONGEST ret;
+ enum register_status regstatus;
+ struct gdb_exception except;
+
+ rcache = get_current_regcache ();
+ tdep = gdbarch_tdep (get_regcache_arch (rcache));
+
+ regstatus = regcache_raw_read_unsigned (rcache, tdep->bndcfgu_regnum, &ret);
+
+ if (regstatus != REG_VALID)
+ error (_("BNDCFGU register invalid, read status %d."), regstatus);
+
+ return ret & MPX_BASE_MASK;
+}
+
+/* Check if the current target is MPX enabled. */
+
+static int
+i386_mpx_enabled (void)
+{
+ const struct gdbarch_tdep *tdep = gdbarch_tdep (get_current_arch ());
+ const struct target_desc *tdesc = tdep->tdesc;
+
+ return (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.mpx") != NULL);
+}
+
+#define MPX_BD_MASK 0xfffffff00000ULL /* select bits [47:20] */
+#define MPX_BT_MASK 0x0000000ffff8 /* select bits [19:3] */
+#define MPX_BD_MASK_32 0xfffff000 /* select bits [31:12] */
+#define MPX_BT_MASK_32 0x00000ffc /* select bits [11:2] */
+
+/* Find the bound table entry given the pointer location and the base
+ address of the table. */
+
+static CORE_ADDR
+i386_mpx_get_bt_entry (CORE_ADDR ptr, CORE_ADDR bd_base)
+{
+ CORE_ADDR offset1;
+ CORE_ADDR offset2;
+ CORE_ADDR mpx_bd_mask, bd_ptr_r_shift, bd_ptr_l_shift;
+ CORE_ADDR bt_mask, bt_select_r_shift, bt_select_l_shift;
+ CORE_ADDR bd_entry_addr;
+ CORE_ADDR bt_addr;
+ CORE_ADDR bd_entry;
+ struct gdbarch *gdbarch = get_current_arch ();
+ struct type *data_ptr_type = builtin_type (gdbarch)->builtin_data_ptr;
+
+
+ if (gdbarch_ptr_bit (gdbarch) == 64)
+ {
+ mpx_bd_mask = MPX_BD_MASK;
+ bd_ptr_r_shift = 20;
+ bd_ptr_l_shift = 3;
+ bt_select_r_shift = 3;
+ bt_select_l_shift = 5;
+ bt_mask = MPX_BT_MASK;
+ }
+ else
+ {
+ mpx_bd_mask = MPX_BD_MASK_32;
+ bd_ptr_r_shift = 12;
+ bd_ptr_l_shift = 2;
+ bt_select_r_shift = 2;
+ bt_select_l_shift = 4;
+ bt_mask = MPX_BT_MASK_32;
+ }
+
+ offset1 = ((ptr & mpx_bd_mask) >> bd_ptr_r_shift) << bd_ptr_l_shift;
+ bd_entry_addr = bd_base + offset1;
+ bd_entry = read_memory_typed_address (bd_entry_addr, data_ptr_type);
+
+ if ((bd_entry & 0x1) == 0)
+ error (_("Invalid bounds directory entry at %s."),
+ paddress (get_current_arch (), bd_entry_addr));
+
+ /* Clearing status bit. */
+ bd_entry--;
+ bt_addr = bd_entry & ~bt_select_r_shift;
+ offset2 = ((ptr & bt_mask) >> bt_select_r_shift) << bt_select_l_shift;
+
+ return bt_addr + offset2;
+}
+
+/* Print routine for the mpx bounds. */
+
+static void
+i386_mpx_print_bounds (const CORE_ADDR bt_entry[4])
+{
+ struct ui_out *uiout = current_uiout;
+ long long int size;
+ struct gdbarch *gdbarch = get_current_arch ();
+ CORE_ADDR onecompl = ~((CORE_ADDR) 0);
+ int bounds_in_map = ((~bt_entry[1] == 0 && bt_entry[0] == onecompl) ? 1 : 0);
+
+ if (bounds_in_map == 1)
+ {
+ ui_out_text (uiout, "Null bounds on map:");
+ ui_out_text (uiout, " pointer value = ");
+ ui_out_field_core_addr (uiout, "pointer-value", gdbarch, bt_entry[2]);
+ ui_out_text (uiout, ".");
+ ui_out_text (uiout, "\n");
+ }
+ else
+ {
+ ui_out_text (uiout, "{lbound = ");
+ ui_out_field_core_addr (uiout, "lower-bound", gdbarch, bt_entry[0]);
+ ui_out_text (uiout, ", ubound = ");
+
+ /* The upper bound is stored in 1's complement. */
+ ui_out_field_core_addr (uiout, "upper-bound", gdbarch, ~bt_entry[1]);
+ ui_out_text (uiout, "}: pointer value = ");
+ ui_out_field_core_addr (uiout, "pointer-value", gdbarch, bt_entry[2]);
+
+ if (gdbarch_ptr_bit (gdbarch) == 64)
+ size = ( (~(int64_t) bt_entry[1]) - (int64_t) bt_entry[0]);
+ else
+ size = ( ~((int32_t) bt_entry[1]) - (int32_t) bt_entry[0]);
+
+ /* In case the bounds are 0x0 and 0xffff... the difference will be -1.
+ -1 represents in this sense full memory access, and there is no need
+ one to the size. */
+
+ size = (size > -1 ? size + 1 : size);
+ ui_out_text (uiout, ", size = ");
+ ui_out_field_fmt (uiout, "size", "%lld", size);
+
+ ui_out_text (uiout, ", metadata = ");
+ ui_out_field_core_addr (uiout, "metadata", gdbarch, bt_entry[3]);
+ ui_out_text (uiout, "\n");
+ }
+}
+
+/* Implement the command "show mpx bound". */
+
+static void
+i386_mpx_info_bounds (char *args, int from_tty)
+{
+ CORE_ADDR bd_base = 0;
+ CORE_ADDR addr;
+ CORE_ADDR bt_entry_addr = 0;
+ CORE_ADDR bt_entry[4];
+ int i;
+ struct gdbarch *gdbarch = get_current_arch ();
+ struct type *data_ptr_type = builtin_type (gdbarch)->builtin_data_ptr;
+
+ if (!i386_mpx_enabled ())
+ error (_("Intel(R) Memory Protection Extensions not\
+ supported on this target."));
+
+ if (args == NULL)
+ error (_("Address of pointer variable expected."));
+
+ addr = parse_and_eval_address (args);
+
+ bd_base = i386_mpx_bd_base ();
+ bt_entry_addr = i386_mpx_get_bt_entry (addr, bd_base);
+
+ memset (bt_entry, 0, sizeof (bt_entry));
+
+ for (i = 0; i < 4; i++)
+ bt_entry[i] = read_memory_typed_address (bt_entry_addr
+ + i * data_ptr_type->length,
+ data_ptr_type);
+
+ i386_mpx_print_bounds (bt_entry);
+}
+
+/* Implement the command "set mpx bound". */
+
+static void
+i386_mpx_set_bounds (char *args, int from_tty)
+{
+ CORE_ADDR bd_base = 0;
+ CORE_ADDR addr, lower, upper;
+ CORE_ADDR bt_entry_addr = 0;
+ CORE_ADDR bt_entry[2];
+ const char *input = args;
+ int i;
+ struct gdbarch *gdbarch = get_current_arch ();
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ struct type *data_ptr_type = builtin_type (gdbarch)->builtin_data_ptr;
+
+ if (!i386_mpx_enabled ())
+ error (_("Intel(R) Memory Protection Extensions not supported\
+ on this target."));
+
+ if (args == NULL)
+ error (_("Pointer value expected."));
+
+ addr = value_as_address (parse_to_comma_and_eval (&input));
+
+ if (input[0] == ',')
+ ++input;
+ if (input[0] == '\0')
+ error (_("wrong number of arguments: missing lower and upper bound."));
+ lower = value_as_address (parse_to_comma_and_eval (&input));
+
+ if (input[0] == ',')
+ ++input;
+ if (input[0] == '\0')
+ error (_("Wrong number of arguments; Missing upper bound."));
+ upper = value_as_address (parse_to_comma_and_eval (&input));
+
+ bd_base = i386_mpx_bd_base ();
+ bt_entry_addr = i386_mpx_get_bt_entry (addr, bd_base);
+ for (i = 0; i < 2; i++)
+ bt_entry[i] = read_memory_typed_address (bt_entry_addr
+ + i * data_ptr_type->length,
+ data_ptr_type);
+ bt_entry[0] = (uint64_t) lower;
+ bt_entry[1] = ~(uint64_t) upper;
+
+ for (i = 0; i < 2; i++)
+ write_memory_unsigned_integer (bt_entry_addr + i * data_ptr_type->length,
+ data_ptr_type->length, byte_order,
+ bt_entry[i]);
+}
+
+static struct cmd_list_element *mpx_set_cmdlist, *mpx_show_cmdlist;
+
+/* Helper function for the CLI commands. */
+
+static void
+set_mpx_cmd (char *args, int from_tty)
+{
+ help_list (mpx_set_cmdlist, "set mpx", all_commands, gdb_stdout);
+}
+
+/* Helper function for the CLI commands. */
+
+static void
+show_mpx_cmd (char *args, int from_tty)
+{
+ cmd_show_list (mpx_show_cmdlist, from_tty, "");
+}
+
/* Provide a prototype to silence -Wmissing-prototypes. */
void _initialize_i386_tdep (void);
@@ -8649,6 +8894,34 @@ is \"default\"."),
NULL, /* FIXME: i18n: */
&setlist, &showlist);
+ /* Add "mpx" prefix for the set commands. */
+
+ add_prefix_cmd ("mpx", class_support, set_mpx_cmd, _("\
+Set Intel(R) Memory Protection Extensions specific variables."),
+ &mpx_set_cmdlist, "set tdesc ",
+ 0 /* allow-unknown */, &setlist);
+
+ /* Add "mpx" prefix for the show commands. */
+
+ add_prefix_cmd ("mpx", class_support, show_mpx_cmd, _("\
+Show Intel(R) Memory Protection Extensions specific variables."),
+ &mpx_show_cmdlist, "show mpx ",
+ 0 /* allow-unknown */, &showlist);
+
+ /* Add "bound" command for the show mpx commands list. */
+
+ add_cmd ("bound", no_class, i386_mpx_info_bounds,
+ "Show the memory bounds for a given array/pointer storage\
+ in the bound table.",
+ &mpx_show_cmdlist);
+
+ /* Add "bound" command for the set mpx commands list. */
+
+ add_cmd ("bound", no_class, i386_mpx_set_bounds,
+ "Set the memory bounds for a given array/pointer storage\
+ in the bound table.",
+ &mpx_set_cmdlist);
+
gdbarch_register_osabi_sniffer (bfd_arch_i386, bfd_target_coff_flavour,
i386_coff_osabi_sniffer);
diff --git a/gdb/testsuite/gdb.arch/i386-mpx-map.c b/gdb/testsuite/gdb.arch/i386-mpx-map.c
new file mode 100644
index 0000000..8a9094c
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/i386-mpx-map.c
@@ -0,0 +1,93 @@
+/* Test program for MPX map allocated bounds.
+
+ Copyright 2015 Free Software Foundation, Inc.
+
+ Contributed by Intel Corp. <walfred.tedeschi@intel.com>
+ <mircea.gherzan@intel.com>
+
+ This file is part of GDB.
+
+ 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 <stdlib.h>
+#include "x86-cpuid.h"
+
+#ifndef NOINLINE
+#define NOINLINE __attribute__ ((noinline))
+#endif
+
+#define SIZE 5
+
+typedef int T;
+
+unsigned int have_mpx (void) NOINLINE;
+
+unsigned int NOINLINE
+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;
+}
+
+void
+foo (T *p)
+{
+ T *x;
+
+#if defined __GNUC__ && !defined __INTEL_COMPILER
+ __bnd_store_ptr_bounds (p, &p);
+#endif
+
+ x = p + SIZE - 1;
+
+#if defined __GNUC__ && !defined __INTEL_COMPILER
+ __bnd_store_ptr_bounds (x, &x);
+#endif
+
+ return; /* after-assign */
+}
+
+int
+main (void)
+{
+ if (have_mpx ())
+ {
+ T *a = NULL;
+
+ a = calloc (SIZE, sizeof (T)); /* after-decl */
+#if defined __GNUC__ && !defined __INTEL_COMPILER
+ __bnd_store_ptr_bounds (a, &a);
+#endif
+
+ foo (a); /* after-alloc */
+ free (a);
+ }
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/i386-mpx-map.exp b/gdb/testsuite/gdb.arch/i386-mpx-map.exp
new file mode 100644
index 0000000..47efb16
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/i386-mpx-map.exp
@@ -0,0 +1,76 @@
+# Copyright 2015 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <walfred.tedeschi@intel.com>,
+# <mircea.gherzan@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 "-fmpx -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
+}
+
+set supports_mpx 0
+set test "probe MPX support"
+
+gdb_test_multiple "print have_mpx()" $test {
+ -re ".. = 1\r\n$gdb_prompt $" {
+ pass $test
+ set supports_mpx 1
+ }
+ -re ".. = 0\r\n$gdb_prompt $" {
+ pass $test
+ }
+}
+
+if { !$supports_mpx } {
+ unsupported "processor does not support MPX"
+ return
+}
+
+gdb_breakpoint [ gdb_get_line_number "after-decl" ]
+gdb_breakpoint [ gdb_get_line_number "after-alloc" ]
+gdb_breakpoint [ gdb_get_line_number "after-assign" ]
+
+gdb_test "show mpx bound 0x0" "Invalid bounds directory entry at $hex." "NULL address of the pointer"
+
+gdb_continue_to_breakpoint "after-decl" ".*after-decl.*"
+gdb_test "show mpx bound a" "Invalid bounds directory entry at $hex." "pointer instead of pointer address"
+
+gdb_continue_to_breakpoint "after-alloc" ".*after-alloc.*"
+gdb_test "show mpx bound a" "\\\{lbound = $hex, ubound = $hex\\\}: pointer value = $hex, size = \[8, 4\], metadata = 0x0+" "pointer after allocation"
+
+gdb_continue_to_breakpoint "after-assign" ".*after-assign.*"
+gdb_test "show mpx bound x" "\\\{lbound = $hex, ubound = $hex\\\}: pointer value = $hex, size = \[8, 4\], metadata = 0x0+" "pointer after assignment"
+gdb_test "set mpx bound 0x0, 0x1, 0x2" "Invalid bounds directory entry at $hex." "set mpx bound: NULL address of the pointer"
+gdb_test_no_output "set mpx bound x, 0xcafebabe, 0xdeadbeef" "set mpx bound: set bounds for a valid pointer address"
+gdb_test "show mpx bound x" "\\\{lbound = .*cafebabe, ubound = .*deadbeef\\\}: pointer value = $hex, size = $decimal, metadata = 0x0+" "set mpx bound: bounds map entry after set mpx bound"
+
+
+gdb_test "set mpx bound 0x0, 0x1 0x2" "A syntax error in expression.*" "set mpx bound: Controlling syntax error, missing comma "
+gdb_test "set mpx bound 0x0, 0x1" "Wrong number of arguments.*" "set mpx bound: Controlling syntax error, missing argument "