mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 12:34:43 +00:00
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
This commit is contained in:
262
gdb/testsuite/gdb.arch/x86-fsgs.c
Normal file
262
gdb/testsuite/gdb.arch/x86-fsgs.c
Normal file
@@ -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;
|
||||
}
|
||||
191
gdb/testsuite/gdb.arch/x86-fsgs.exp
Normal file
191
gdb/testsuite/gdb.arch/x86-fsgs.exp
Normal file
@@ -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
|
||||
}
|
||||
@@ -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} {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user