aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarkus Metzger <markus.t.metzger@intel.com>2019-11-21 12:01:13 +0100
committerMarkus Metzger <markus.t.metzger@intel.com>2019-12-17 16:04:40 +0100
commit03bad9e43d43bc7382e9487142ee4fa988bc5576 (patch)
tree4ff3483ee0f96fb3e23091e7e92f678c35b3808f
parent3e7fa952aa12f56bafe02e0dab47eb9c690e9f47 (diff)
downloadfsf-binutils-gdb-03bad9e43d43bc7382e9487142ee4fa988bc5576.zip
fsf-binutils-gdb-03bad9e43d43bc7382e9487142ee4fa988bc5576.tar.gz
fsf-binutils-gdb-03bad9e43d43bc7382e9487142ee4fa988bc5576.tar.bz2
gdb, testsuite: test changing FS and GS segment selectors and bases
Signed-off-by: Markus Metzger <markus.t.metzger@intel.com> testsuite/ * lib/gdb.exp (skip_fsgsbase_tests, skip_arch_set_fs_tests) (skip_arch_set_gs_tests): New. * gdb.arch/x86-fsgs.c: New. * gdb.arch/x86-fsgs.exp: New. Change-Id: I7da32d4b57fd8b34153e7385263ec82318d32a98
-rw-r--r--gdb/testsuite/gdb.arch/x86-fsgs.c262
-rw-r--r--gdb/testsuite/gdb.arch/x86-fsgs.exp191
-rw-r--r--gdb/testsuite/gdb.base/gcore.exp16
-rw-r--r--gdb/testsuite/lib/gdb.exp269
4 files changed, 738 insertions, 0 deletions
diff --git a/gdb/testsuite/gdb.arch/x86-fsgs.c b/gdb/testsuite/gdb.arch/x86-fsgs.c
new file mode 100644
index 0000000..654e687
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/x86-fsgs.c
@@ -0,0 +1,262 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2019 Free Software Foundation, Inc.
+
+ 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 <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <asm/ldt.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/mman.h>
+
+#if HAVE_ARCH_SET_FS || HAVE_ARCH_SET_GS
+# include <asm/prctl.h>
+# include <sys/prctl.h>
+#endif /* HAVE_ARCH_SET_FS || HAVE_ARCH_SET_GS */
+
+struct segs {
+ int initial;
+ int other;
+ int twentythree;
+};
+static struct segs *segs;
+
+static unsigned int
+setup_ldt (unsigned int entry, void *base, size_t size)
+{
+ struct user_desc ud;
+ int errcode;
+
+ memset (&ud, 0, sizeof (ud));
+ ud.entry_number = entry;
+ ud.base_addr = (unsigned int) (unsigned long) base;
+ ud.limit = (unsigned int) size;
+
+ /* The base is 32-bit. */
+ if ((unsigned long) ud.base_addr != (unsigned long) base)
+ return 0u;
+
+ errcode = syscall(SYS_modify_ldt, 1, &ud, sizeof(ud));
+ if (errcode != 0)
+ return 0u;
+
+ return (ud.entry_number << 3) | 7;
+}
+
+int
+read_fs (void)
+{
+ int value;
+
+ __asm__ volatile ("mov %%fs:0x0, %0" : "=rm"(value) :: "memory");
+
+ return value;
+}
+
+int
+read_gs (void)
+{
+ int value;
+
+ __asm__ volatile ("mov %%gs:0x0, %0" : "=rm"(value) :: "memory");
+
+ return value;
+}
+
+int
+switch_fs_read (unsigned int fs)
+{
+ __asm__ volatile ("mov %0, %%fs" :: "rm"(fs) : "memory");
+
+ return read_fs ();
+}
+
+void
+test_fs (unsigned int selector)
+{
+ int value;
+
+ value = switch_fs_read (selector); /* l.1 */
+ value = read_fs (); /* l.2 */
+ value = read_fs (); /* l.3 */
+} /* l.4 */
+
+int
+switch_gs_read (unsigned int gs)
+{
+ __asm__ volatile ("mov %0, %%gs" :: "rm"(gs) : "memory");
+
+ return read_gs ();
+}
+
+void
+test_gs (unsigned int selector)
+{
+ int value;
+
+ value = switch_gs_read (selector); /* l.1 */
+ value = read_gs (); /* l.2 */
+ value = read_gs (); /* l.3 */
+} /* l.4 */
+
+#if HAVE_WRFSGSBASE
+
+int
+wrfsbase_read (void *fsbase)
+{
+ __asm__ volatile ("wrfsbase %0" :: "r"(fsbase) : "memory");
+
+ return read_fs ();
+}
+
+static void
+test_wrfsbase (void *base)
+{
+ int value;
+
+ value = wrfsbase_read (base); /* l.1 */
+ value = read_fs (); /* l.2 */
+ value = read_fs (); /* l.3 */
+} /* l.4 */
+
+int
+wrgsbase_read (void *gsbase)
+{
+ __asm__ volatile ("wrgsbase %0" :: "r"(gsbase) : "memory");
+
+ return read_gs ();
+}
+
+static void
+test_wrgsbase (void *base)
+{
+ int value;
+
+ value = wrgsbase_read (base); /* l.1 */
+ value = read_gs (); /* l.2 */
+ value = read_gs (); /* l.3 */
+} /* l.4 */
+
+#endif /* HAVE_WRFSGSBASE */
+
+#if HAVE_ARCH_SET_FS
+
+int
+arch_set_fs_read (void *fsbase)
+{
+ int errcode;
+
+ errcode = syscall (SYS_arch_prctl, ARCH_SET_FS, fsbase);
+ if (errcode != 0)
+ return 0;
+
+ return read_fs ();
+}
+
+static void
+test_arch_set_fs (void *base)
+{
+ int value;
+
+ value = arch_set_fs_read (base); /* l.1 */
+ value = read_fs (); /* l.2 */
+ value = read_fs (); /* l.3 */
+} /* l.4 */
+
+#endif /* HAVE_ARCH_SET_FS */
+
+#if HAVE_ARCH_SET_GS
+
+int
+arch_set_gs_read (void *gsbase)
+{
+ int errcode;
+
+ errcode = syscall (SYS_arch_prctl, ARCH_SET_GS, gsbase);
+ if (errcode != 0)
+ return 0;
+
+ return read_gs ();
+}
+
+static void
+test_arch_set_gs (void *base)
+{
+ int value;
+
+ value = arch_set_gs_read (base); /* l.1 */
+ value = read_gs (); /* l.2 */
+ value = read_gs (); /* l.3 */
+} /* l.4 */
+
+#endif /* HAVE_ARCH_SET_GS */
+
+int
+main (void)
+{
+ unsigned int selector;
+
+ segs = mmap (NULL, sizeof (*segs), PROT_READ | PROT_WRITE,
+#ifdef __x86_64__
+ MAP_32BIT |
+#endif
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (segs == MAP_FAILED)
+ {
+ perror ("failed to mmap 32-bit memory");
+ abort ();
+ }
+
+ segs->initial = 42;
+ segs->other = -42;
+ segs->twentythree = 23;
+
+ selector = setup_ldt (0xb7 >> 3, &segs->other, sizeof (segs->other));
+ if (selector == 0u)
+ {
+ perror ("failed to setup LDT[0xb7>>3] = &segs->other");
+ abort ();
+ }
+
+ selector = setup_ldt (0xa7 >> 3, &segs->initial, sizeof (segs->initial));
+ if (selector == 0u)
+ {
+ perror ("failed to setup LDT[0xa7>>3] = &segs->initial");
+ abort ();
+ }
+
+ test_fs (selector);
+ test_gs (selector);
+
+#if HAVE_ARCH_SET_FS
+ test_arch_set_fs (&segs->initial);
+#endif /* HAVE_ARCH_SET_FS */
+
+#if HAVE_ARCH_SET_GS
+ test_arch_set_gs (&segs->initial);
+#endif /* HAVE_ARCH_SET_GS */
+
+#if HAVE_WRFSGSBASE
+ test_wrfsbase (&segs->initial);
+ test_wrgsbase (&segs->initial);
+#endif /* HAVE_WRFSGSBASE */
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/x86-fsgs.exp b/gdb/testsuite/gdb.arch/x86-fsgs.exp
new file mode 100644
index 0000000..5c4282d
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/x86-fsgs.exp
@@ -0,0 +1,191 @@
+# Copyright (C) 2019 Free Software Foundation, Inc.
+#
+# 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/>.
+
+
+standard_testfile
+
+if { ![istarget x86_64-*-* ] && ![istarget i?86-*-* ] } {
+ verbose "Skipping ${testfile}."
+ return
+}
+
+if { [skip_modify_ldt_tests] } {
+ untested "cannot setup LDT"
+ return
+}
+
+set skip_fsgsbase [skip_fsgsbase_tests]
+set skip_arch_set_fs [skip_arch_set_fs_tests]
+set skip_arch_set_gs [skip_arch_set_gs_tests]
+
+set flags { debug }
+set flags [concat $flags additional_flags=-DHAVE_WRFSGSBASE=!$skip_fsgsbase]
+set flags [concat $flags additional_flags=-DHAVE_ARCH_SET_FS=!$skip_arch_set_fs]
+set flags [concat $flags additional_flags=-DHAVE_ARCH_SET_GS=!$skip_arch_set_gs]
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} ${flags}] } {
+ return -1
+}
+
+if { ![runto_main] } {
+ untested "failed to run to main"
+ return -1
+}
+
+proc test_switch { seg } {
+ global skip_fsgsbase skip_arch_set_fs skip_arch_set_gs
+
+ # The inferior provides a new segment selector
+ gdb_test "p switch_${seg}_read (0xa7)" "= 42"
+
+ # The inferior provides the segment base via WRFS/GSBASE.
+ if { $skip_fsgsbase } {
+ untested "FSGSBASE"
+ } else {
+ gdb_test "p wr${seg}base_read (&segs->twentythree)" "= 23"
+ }
+
+ # The inferior provides the segment base via arch_prctl ().
+ if { (($seg == "fs" && $skip_arch_set_fs) ||
+ ($seg == "gs" && $skip_arch_set_gs)) } {
+ untested "arch_prctl(ARCH_SET_FS/GS)"
+ } else {
+ gdb_test "p arch_set_${seg}_read (&segs->twentythree)" "= 23"
+ }
+}
+
+proc test { seg } {
+ global hex gdb_prompt
+
+ # Check that the target overrides any garbage we put into FS/GS and
+ # FS/GSBASE.
+ gdb_test "p/x \$${seg} = 0xb7" "= 0xb7"
+ # On 32-bit kernels, FS/GSBASE are not defined
+ if { ![istarget i?86-*-* ] } {
+ gdb_test "p/x \$${seg}_base = &segs->twentythree" "= $hex"
+ }
+ gdb_test "next" " l\.2 \\\*/"
+
+ # Since we want to use the same function for different scenarios, we
+ # don't check the actual register values but we check the effect.
+ gdb_test "p value" "= 42"
+
+ # Change the segment selector to point to the 'other' segment.
+ with_test_prefix $seg {
+ gdb_test "p/x \$${seg} = 0xb7" "= 0xb7"
+ gdb_test "p/x &segs->other" $hex
+
+ # Some kernels are nice enough to update the base for us.
+ set testname "${seg}_base"
+ if { ![istarget "x86_64-*-*"] } {
+ untested $testname
+ } else {
+ gdb_test_multiple "p/x \$${seg}_base" $testname {
+ -re "= 0x0\r\n$gdb_prompt $" {
+ pass $testname
+ }
+ -re "= $hex.*\r\n$gdb_prompt $" {
+ gdb_test "p (int *)\$${seg}_base == &segs->other" "= 1" \
+ $testname
+ }
+ -re "$gdb_prompt $" {
+ fail $testname
+ }
+ }
+ }
+
+ # Inferior calls will use the 'other' segment.
+ gdb_test "p read_${seg} ()" "= -42"
+
+ # Inferior calls may switch the segment again. This will be
+ # undone when we restore the register state after returning from
+ # the inferior call. Test a few different scenarios.
+ test_switch $seg
+
+ # When we resume, we will read from the 'other' segment as we did
+ # in the inferior call above. We do this check at the end to
+ # check that inferior calls are not able to override the state.
+ gdb_test "next" " l\.3 \\\*/"
+ gdb_test "p value" "= -42"
+ }
+
+ # Only 64-bit kernels provide FS/GSBASE.
+ if { ![istarget "x86_64-*-*"] } {
+ untested ${seg}_base
+ } elseif { [is_ilp32_target] } {
+ # Even though a 64-bit kernel provides FS/GSBASE for the current
+ # FS/GS selector, it does not allow changing the base independent
+ # of the selector.
+ #
+ # Trying to do that while setting the selector to zero, as tests
+ # do below, results in an inferior crash while trying to use that
+ # zero selector.
+ untested ${seg}_base
+ } else {
+ # Change the segment base to point to 'twentythree'.
+ with_test_prefix ${seg}_base {
+ # We also need to set the selector to zero. And we need to do
+ # so before changing the base.
+ gdb_test "p/x \$${seg} = 0x0" "= 0x0"
+ gdb_test "p/x \$${seg}_base = &segs->twentythree" "= $hex"
+
+ # Inferior calls will use the 'twentythree' segment.
+ gdb_test "p read_${seg} ()" "= 23"
+
+ # Check inferior calls switching the segment again.
+ test_switch $seg
+
+ # When we resume, we will read from the 'twentythreee' segment
+ # as we did in the inferior call above. We do this check at
+ # the end to check that inferior calls are not able to
+ # override the state.
+ gdb_test "next" " l\.4 \\\*/"
+ gdb_test "p value" "= 23"
+ }
+ }
+}
+
+proc test_one { function seg } {
+ global decimal
+
+ gdb_breakpoint $function
+ gdb_continue_to_breakpoint $function "$function .* l\.1 \\\*/"
+
+ with_test_prefix $function {
+ test $seg
+ }
+}
+
+test_one test_fs fs
+test_one test_gs gs
+
+if { $skip_arch_set_fs } {
+ untested "arch_prctl(ARCH_SET_FS)"
+} else {
+ test_one test_arch_set_fs fs
+}
+
+if { $skip_arch_set_gs } {
+ untested "arch_prctl(ARCH_SET_GS)"
+} else {
+ test_one test_arch_set_gs gs
+}
+
+if { $skip_fsgsbase } {
+ untested "FSGSBASE"
+} else {
+ test_one test_wrfsbase fs
+ test_one test_wrgsbase gs
+}
diff --git a/gdb/testsuite/gdb.base/gcore.exp b/gdb/testsuite/gdb.base/gcore.exp
index 5027005..a9f9aa5 100644
--- a/gdb/testsuite/gdb.base/gcore.exp
+++ b/gdb/testsuite/gdb.base/gcore.exp
@@ -51,6 +51,22 @@ set pre_corefile_local_array \
set pre_corefile_extern_array \
[capture_command_output "print extern_array" "$print_prefix"]
+# On IA, a 64-bit kernel provides fs_base and gs_base for 32-bit inferiors
+# via ptrace, yet does not write them into the corefile. Neither does
+# GDB.
+#
+# They will appear '<unavailable>' when reading the corefile back in.
+# Adjust the above output to reflect this behaviour.
+if { [istarget x86_64-*-* ] && [is_ilp32_target] } {
+ regsub -all "\(\[fg\]s_base *\)$hex *-?$decimal" $pre_corefile_regs \
+ "\\1<unavailable>" pre_corefile_regs
+ verbose -log "adjusted general regs: $pre_corefile_regs"
+
+ regsub -all "\(\[fg\]s_base *\)$hex *-?$decimal" $pre_corefile_allregs \
+ "\\1<unavailable>" pre_corefile_allregs
+ verbose -log "adjusted all regs: $pre_corefile_allregs"
+}
+
set corefile [standard_output_file gcore.test]
set core_supported [gdb_gcore_cmd "$corefile" "save a corefile"]
if {!$core_supported} {
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index b6c5e00..a52e93b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -6837,5 +6837,274 @@ gdb_caching_proc skip_ctf_tests {
} executable "additional_flags=-gt"]
}
+# Run a test on the target to see if it supports WRFSBASE and WRGSBASE.
+# Return 0 if so, 1 if it does not.
+
+gdb_caching_proc skip_fsgsbase_tests {
+ global srcdir subdir gdb_prompt inferior_exited_re
+
+ set me "skip_fsgsbase_tests"
+
+ # Compile a test program.
+ set src {
+ int seg;
+ int main() {
+ void *old;
+
+ __asm__ volatile ("rdfsbase %0" : "=rm"(old));
+ __asm__ volatile ("wrfsbase %0" :: "r"(&seg));
+ __asm__ volatile ("wrfsbase %0" :: "r"(old));
+
+ __asm__ volatile ("rdgsbase %0" : "=rm"(old));
+ __asm__ volatile ("wrgsbase %0" :: "r"(&seg));
+ __asm__ volatile ("wrgsbase %0" :: "r"(old));
+
+ return 0;
+ }
+ }
+ if {![gdb_simple_compile $me $src executable]} {
+ return 1
+ }
+
+ # No error message, compilation succeeded so now run it via gdb.
+
+ gdb_exit
+ gdb_start
+ gdb_reinitialize_dir $srcdir/$subdir
+ gdb_load "$obj"
+ gdb_run_cmd
+ gdb_expect {
+ -re ".*Illegal instruction.*${gdb_prompt} $" {
+ verbose -log "$me: FSGSBASE support not detected."
+ set skip_fsgsbase_tests 1
+ }
+ -re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
+ verbose -log "$me: FSGSBASE support detected."
+ set skip_fsgsbase_tests 0
+ }
+ default {
+ warning "\n$me: default case taken."
+ set skip_fsgsbase_tests 1
+ }
+ }
+ gdb_exit
+ remote_file build delete $obj
+
+ verbose "$me: returning $skip_fsgsbase_tests" 2
+ return $skip_fsgsbase_tests
+}
+
+# Run a test on the target to see if it supports arch_prctl(ARCH_SET_FS).
+# Return 0 if so, 1 if it does not.
+
+gdb_caching_proc skip_arch_set_fs_tests {
+ global srcdir subdir gdb_prompt inferior_exited_re
+
+ set me "skip_arch_set_fs_tests"
+
+ # The system call is not available to 32-bit.
+ if { [is_ilp32_target] } {
+ return 1
+ }
+
+ # Compile a test program.
+ set src {
+ #include <stdlib.h>
+ #include <assert.h>
+ #include <unistd.h>
+ #include <sys/syscall.h>
+ #include <asm/prctl.h>
+ #include <sys/prctl.h>
+
+ int seg;
+ int main() {
+ unsigned long old;
+ int errcode;
+
+ errcode = syscall (SYS_arch_prctl, ARCH_GET_FS, &old);
+ assert (errcode == 0);
+
+ errcode = syscall (SYS_arch_prctl, ARCH_SET_FS, &seg);
+ assert (errcode == 0);
+
+ errcode = syscall (SYS_arch_prctl, ARCH_SET_FS, old);
+ assert (errcode == 0);
+
+ return 0;
+ }
+ }
+ if {![gdb_simple_compile $me $src executable]} {
+ return 1
+ }
+
+ # No error message, compilation succeeded so now run it via gdb.
+
+ gdb_exit
+ gdb_start
+ gdb_reinitialize_dir $srcdir/$subdir
+ gdb_load "$obj"
+ gdb_run_cmd
+ gdb_expect {
+ -re ".*Assertion `errcode == 0' failed.*${gdb_prompt} $" {
+ verbose -log "$me: ARCH_SET_FS support not detected."
+ set skip_arch_set_fs_tests 1
+ }
+ -re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
+ verbose -log "$me: ARCH_SET_FS support detected."
+ set skip_arch_set_fs_tests 0
+ }
+ default {
+ warning "\n$me: default case taken."
+ set skip_arch_set_fs_tests 1
+ }
+ }
+ gdb_exit
+ remote_file build delete $obj
+
+ verbose "$me: returning $skip_arch_set_fs_tests" 2
+ return $skip_arch_set_fs_tests
+}
+
+# Run a test on the target to see if it supports arch_prctl(ARCH_SET_GS).
+# Return 0 if so, 1 if it does not.
+
+gdb_caching_proc skip_arch_set_gs_tests {
+ global srcdir subdir gdb_prompt inferior_exited_re
+
+ set me "skip_arch_set_gs_tests"
+
+ # The system call is not available to 32-bit.
+ if { [is_ilp32_target] } {
+ return 1
+ }
+
+ # Compile a test program.
+ set src {
+ #include <stdlib.h>
+ #include <assert.h>
+ #include <unistd.h>
+ #include <sys/syscall.h>
+ #include <asm/prctl.h>
+ #include <sys/prctl.h>
+
+ int seg;
+ int main() {
+ unsigned long old;
+ int errcode;
+
+ errcode = syscall (SYS_arch_prctl, ARCH_GET_GS, &old);
+ assert (errcode == 0);
+
+ errcode = syscall (SYS_arch_prctl, ARCH_SET_GS, &seg);
+ assert (errcode == 0);
+
+ errcode = syscall (SYS_arch_prctl, ARCH_SET_GS, old);
+ assert (errcode == 0);
+
+ return 0;
+ }
+ }
+ if {![gdb_simple_compile $me $src executable]} {
+ return 1
+ }
+
+ # No error message, compilation succeeded so now run it via gdb.
+
+ gdb_exit
+ gdb_start
+ gdb_reinitialize_dir $srcdir/$subdir
+ gdb_load "$obj"
+ gdb_run_cmd
+ gdb_expect {
+ -re ".*Assertion `errcode == 0' failed.*${gdb_prompt} $" {
+ verbose -log "$me: ARCH_SET_GS support not detected."
+ set skip_arch_set_gs_tests 1
+ }
+ -re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
+ verbose -log "$me: ARCH_SET_GS support detected."
+ set skip_arch_set_gs_tests 0
+ }
+ default {
+ warning "\n$me: default case taken."
+ set skip_arch_set_gs_tests 1
+ }
+ }
+ gdb_exit
+ remote_file build delete $obj
+
+ verbose "$me: returning $skip_arch_set_gs_tests" 2
+ return $skip_arch_set_gs_tests
+}
+
+# Run a test on the target to see if it supports modify_ldt.
+# Return 0 if so, 1 if it does not.
+
+gdb_caching_proc skip_modify_ldt_tests {
+ global srcdir subdir gdb_prompt inferior_exited_re
+
+ set me "skip_modify_ldt_tests"
+
+ # Compile a test program.
+ set src {
+ #include <stdlib.h>
+ #include <assert.h>
+ #include <unistd.h>
+ #include <sys/types.h>
+ #include <sys/syscall.h>
+ #include <asm/ldt.h>
+
+ int seg;
+ int main() {
+ struct user_desc ud;
+ int errcode;
+
+ memset (&ud, 0, sizeof (ud));
+ ud.entry_number = 0xa7u >> 3;
+ ud.base_addr = (unsigned int) (unsigned long) &seg;
+ ud.limit = (unsigned int) sizeof (seg);
+
+ errcode = syscall (SYS_modify_ldt, 1, &ud, sizeof(ud));
+ assert (errcode == 0);
+
+ return 0;
+ }
+ }
+
+ if {![gdb_simple_compile $me $src executable]} {
+ return 1
+ }
+
+ # No error message, compilation succeeded so now run it via gdb.
+
+ gdb_exit
+ gdb_start
+ gdb_reinitialize_dir $srcdir/$subdir
+ gdb_load "$obj"
+ gdb_run_cmd
+ gdb_expect {
+ -re ".*Assertion `errcode.*${gdb_prompt} $" {
+ verbose -log "$me: modify_ldt support not detected."
+ set skip_modify_ldt_tests 1
+ }
+ -re ".*Assertion `ud.base_addr.*${gdb_prompt} $" {
+ verbose -log "$me: struct user_desc truncates base."
+ set skip_modify_ldt_tests 1
+ }
+ -re ".*$inferior_exited_re normally.*${gdb_prompt} $" {
+ verbose -log "$me: modify_ldt working."
+ set skip_modify_ldt_tests 0
+ }
+ default {
+ warning "\n$me: default case taken."
+ set skip_modify_ldt_tests 1
+ }
+ }
+ gdb_exit
+ remote_file build delete $obj
+
+ verbose "$me: returning $skip_modify_ldt_tests" 2
+ return $skip_modify_ldt_tests
+}
+
# Always load compatibility stuff.
load_lib future.exp