* [RFC] Don't allow setting register in non-innermost frame @ 2012-08-17 2:21 Yao Qi 2012-08-20 20:19 ` Doug Evans 0 siblings, 1 reply; 22+ messages in thread From: Yao Qi @ 2012-08-17 2:21 UTC (permalink / raw) To: gdb-patches Hi, When playing with 'set $reg=0xXXX', I am thinking about the expected behaviour of setting register when the frame is not innermost. Current GDB will invalidate regcaches and reinit frache_caches for any register changes (on innermost and non-innermost frame). However, the semantics of 'setting register on non-innermost frame' is tricky. On innermost frame, 'setting register' means writing something to register in cpu, while on non-innermost frame, it means writing something to either real register (regcache) or memory (frame). On the other head, I am trying to figure out a situation that user has to set register on non-innermost frame. Why does this user have to do that? Literally, IMO, 'set $reg=0xXXXX' means 'setting the cpu register $reg to value 0xXXX', which is simple and clear. I propose to restrict setting register in innermost frame only. WDYT? Regression tested on x86_64-linux. Surprisingly, we don't have a test case for 'set register', so I add one. In the future, I plan to add more tests here for 'set register', such as 'set some bits of a register' and 'set multiple registers in one time', to cover the different paths in value_assign for 'lval_register'. gdb: * valops.c (value_assign): Emit error if get_next_frame returns non-NULL. gdb/testsuite: * gdb.base/set-reg.exp: New. --- gdb/testsuite/gdb.base/set-reg.c | 40 +++++++++++++ gdb/testsuite/gdb.base/set-reg.exp | 108 ++++++++++++++++++++++++++++++++++++ gdb/valops.c | 12 ++++ 3 files changed, 160 insertions(+), 0 deletions(-) create mode 100644 gdb/testsuite/gdb.base/set-reg.c create mode 100644 gdb/testsuite/gdb.base/set-reg.exp diff --git a/gdb/testsuite/gdb.base/set-reg.c b/gdb/testsuite/gdb.base/set-reg.c new file mode 100644 index 0000000..7ce350d --- /dev/null +++ b/gdb/testsuite/gdb.base/set-reg.c @@ -0,0 +1,40 @@ +/* This test is part of GDB, the GNU debugger. + + Copyright 2012 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/>. */ + +int +inner (int x) +{ + return x + 2; +} + +static short +middle (int x) +{ + return 2 * inner (x); +} + +short +top (int x) +{ + return 2 * middle (x); +} + +int +main (int argc, char **argv) +{ + return top (argc); +} diff --git a/gdb/testsuite/gdb.base/set-reg.exp b/gdb/testsuite/gdb.base/set-reg.exp new file mode 100644 index 0000000..abe7764 --- /dev/null +++ b/gdb/testsuite/gdb.base/set-reg.exp @@ -0,0 +1,108 @@ +# Copyright 2012 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 +set executable $testfile + +if { [gdb_compile $srcdir/$subdir/$srcfile $binfile executable {debug}] != "" } { + untested "Failed to compile" + return -1 +} + +# A value in register in previous frame. +set reg_prev_frame_in_reg "unknown" +# A value in memory in previous frame. +set reg_prev_frame_in_memory "unknown" + +if [is_amd64_regs_target] { + set reg_prev_frame_in_reg "rax" + set reg_prev_frame_in_memory "rbp" +} elseif [is_x86_like_target] { + set reg_prev_frame_in_reg "eax" + set reg_prev_frame_in_memory "ebp" +} else { + # set reg_prev_frame_in_reg for other arch. + +} + +# Test setting register on innermost frame and non-innermost frame. + +proc test_set_reg_on_frame {} {with_test_prefix "on frame" { + global executable + global reg_prev_frame_in_reg + global reg_prev_frame_in_memory + global decimal + global hex + global expect_out + global gdb_prompt + + clean_restart $executable + + if ![runto_main] { + return -1 + } + + # Name of register is not set, skip the rest of test. + if { [string equal $reg_prev_frame_in_reg "unknown"] } { + return 1 + } + + gdb_test "break inner" "Breakpoint.*at.* file .*, line.*" + gdb_test "continue" "Continuing.\r\n\r\nBreakpoint.*inner.*at.*" \ + "continue to inner" + + gdb_test_sequence "bt" "bt before setting reg" { + "\[\r\n\]#0 .* inner \\(x=1\\) at " + "\[\r\n\]#1 .* middle \\(x=1\\) at " + "\[\r\n\]#2 .* top \\(x=1\\) at " + "\[\r\n\]#3 .* main \\(.*\\) at" + } + + set reg_val "" + gdb_test_multiple "p/x \$${reg_prev_frame_in_reg}" "print register ${reg_prev_frame_in_reg}" { + -re ".*${decimal} = ($hex).*$gdb_prompt $" { + set reg_val $expect_out(1,string) + } + } + + gdb_test_no_output "set \$${reg_prev_frame_in_reg}=${reg_val}" \ + "set register ${reg_prev_frame_in_reg} on inner frame" + + # Stack is unchanged. + gdb_test_sequence "bt" "bt after setting reg" { + "\[\r\n\]#0 .* inner \\(x=1\\) at " + "\[\r\n\]#1 .* middle \\(x=1\\) at " + "\[\r\n\]#2 .* top \\(x=1\\) at " + "\[\r\n\]#3 .* main \\(.*\\) at" + } + + gdb_test "up" + gdb_test "set \$${reg_prev_frame_in_reg}=${reg_val}" \ + "Frame in which this value is assigned to is not innermost.*" \ + "set register ${reg_prev_frame_in_reg} on non-inner frame" + + gdb_test "set \$${reg_prev_frame_in_memory}=${reg_val}" \ + "Frame in which this value is assigned to is not innermost.*" \ + "set register ${reg_prev_frame_in_memory} on non-inner frame" + + gdb_test_sequence "bt" "bt after up" { + "\[\r\n\]#0 .* inner \\(x=1\\) at " + "\[\r\n\]#1 .* middle \\(x=1\\) at " + "\[\r\n\]#2 .* top \\(x=1\\) at " + "\[\r\n\]#3 .* main \\(.*\\) at" + } +}} + +test_set_reg_on_frame diff --git a/gdb/valops.c b/gdb/valops.c index 8167cd4..14540b8 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -1241,6 +1241,18 @@ value_assign (struct value *toval, struct value *fromval) and then restore the new frame afterwards. */ old_frame = get_frame_id (deprecated_safe_get_selected_frame ()); + /* TOVAL is a register, although VALUE_LVAL(TOVAL) may not be + lval_register. */ + if (VALUE_REGNUM (toval) >= 0) + { + /* Figure out which frame this is in currently. */ + struct frame_info *frame = frame_find_by_id (VALUE_FRAME_ID (toval)); + + if (get_next_frame (frame) != NULL) + error (_("Frame in which this value is assigned to is" + " not innermost.")); + } + switch (VALUE_LVAL (toval)) { case lval_internalvar: -- 1.7.7.6 ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-08-17 2:21 [RFC] Don't allow setting register in non-innermost frame Yao Qi @ 2012-08-20 20:19 ` Doug Evans 2012-08-21 3:27 ` Yao Qi 2012-08-23 16:25 ` Tom Tromey 0 siblings, 2 replies; 22+ messages in thread From: Doug Evans @ 2012-08-20 20:19 UTC (permalink / raw) To: Yao Qi; +Cc: gdb-patches On Thu, Aug 16, 2012 at 7:20 PM, Yao Qi <yao@codesourcery.com> wrote: > Hi, > When playing with 'set $reg=0xXXX', I am thinking about the expected > behaviour of setting register when the frame is not innermost. Current > GDB will invalidate regcaches and reinit frache_caches for any register > changes (on innermost and non-innermost frame). However, the semantics > of 'setting register on non-innermost frame' is tricky. On innermost > frame, 'setting register' means writing something to register in cpu, > while on non-innermost frame, it means writing something to either > real register (regcache) or memory (frame). On the other head, I am > trying to figure out a situation that user has to set register on > non-innermost frame. Why does this user have to do that? > > Literally, IMO, 'set $reg=0xXXXX' means 'setting the cpu register $reg > to value 0xXXX', which is simple and clear. I propose to restrict > setting register in innermost frame only. WDYT? > > Regression tested on x86_64-linux. Surprisingly, we don't have a test > case for 'set register', so I add one. In the future, I plan to add > more tests here for 'set register', such as 'set some bits of a register' > and 'set multiple registers in one time', to cover the different > paths in value_assign for 'lval_register'. I wonder if there'll be a bit of a gnome vs kde battle here. :-) [the analogy isn't entirely accurate, but it's the best one I could think of] fwiw, I've been able to work around corrupt, or otherwise not completely useful, core files by setting registers in non-innermost frames. Granted, I knew what was happening underneath the covers, so to speak, and I could have done things differently, but I like this capability. If gdb had started out disallowing changing registers in non-innermost frames, we mightn't be thinking of restricting it now. "It's easier to relax restrictions than it is to impose them after the fact." I'd like to hear more justification for this change. I *could* accept a warning when changing a register in a non-innermost frame, fwiw. ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-08-20 20:19 ` Doug Evans @ 2012-08-21 3:27 ` Yao Qi 2012-08-23 16:25 ` Tom Tromey 1 sibling, 0 replies; 22+ messages in thread From: Yao Qi @ 2012-08-21 3:27 UTC (permalink / raw) To: Doug Evans; +Cc: gdb-patches On 08/21/2012 04:19 AM, Doug Evans wrote: > fwiw, I've been able to work around corrupt, or otherwise not > completely useful, core files by setting registers in non-innermost > frames. > Granted, I knew what was happening underneath the covers, so to speak, > and I could have done things differently, but I like this capability. > Yeah, it is convenient, but sometimes it is misleading. People who are clear on what is happening underneath can modify the frame/memory directly where the registers are saved. > If gdb had started out disallowing changing registers in non-innermost > frames, we mightn't be thinking of restricting it now. > "It's easier to relax restrictions than it is to impose them after the fact." Even if the fact is not clear or misleading sometimes? > I'd like to hear more justification for this change. Unfortunately, I don't have extra justification here. I am writing a new MI notification when register is modified by 'set $reg=FOO' in console, similar to the patches on 'command parameter change' notification. When the register is modified in innermost frame, a MI notification (with frame info) can be sent to MI frontend, and frontend can update its register contents, such as "register view". However, if register is modified in non-innermost frame, the notification is not useful to frontend. Then, I started to think of the meaning of 'setting register in non-innermost frame', and proposed this change finally. -- Yao ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-08-20 20:19 ` Doug Evans 2012-08-21 3:27 ` Yao Qi @ 2012-08-23 16:25 ` Tom Tromey 2012-08-29 9:51 ` Yao Qi 2012-09-07 16:46 ` Jan Kratochvil 1 sibling, 2 replies; 22+ messages in thread From: Tom Tromey @ 2012-08-23 16:25 UTC (permalink / raw) To: Doug Evans; +Cc: Yao Qi, gdb-patches >>>>> "Doug" == Doug Evans <dje@google.com> writes: Doug> fwiw, I've been able to work around corrupt, or otherwise not Doug> completely useful, core files by setting registers in non-innermost Doug> frames. Doug> Granted, I knew what was happening underneath the covers, so to speak, Doug> and I could have done things differently, but I like this capability. It does seem like a useful power user tool. I'm swayed by this argument. Doug> I *could* accept a warning when changing a register in a non-innermost Doug> frame, fwiw. That would be ok by me. Tom ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-08-23 16:25 ` Tom Tromey @ 2012-08-29 9:51 ` Yao Qi 2012-09-04 22:37 ` dje 2012-09-07 16:46 ` Jan Kratochvil 1 sibling, 1 reply; 22+ messages in thread From: Yao Qi @ 2012-08-29 9:51 UTC (permalink / raw) To: Tom Tromey; +Cc: Doug Evans, gdb-patches On 08/24/2012 12:24 AM, Tom Tromey wrote: > Doug> fwiw, I've been able to work around corrupt, or otherwise not > Doug> completely useful, core files by setting registers in non-innermost > Doug> frames. > Doug> Granted, I knew what was happening underneath the covers, so to speak, > Doug> and I could have done things differently, but I like this capability. > > It does seem like a useful power user tool. > I'm swayed by this argument. OK, looks like I underestimate the power of it. :) > > Doug> I*could* accept a warning when changing a register in a non-innermost > Doug> frame, fwiw. > > That would be ok by me. Anyway, this new patch is to emit warning if user modifies register on non-innermost frame. Regression tested on x86_64-linux native and gdbserver. OK to apply? Note that this test case should be tuned for each target, because of registers. So far, this test is run effectively on x86 and x86_64. -- Yao gdb/testsuite: 2012-08-29 Yao Qi <yao@codesourcery.com> * gdb.base/set-reg.exp: New. * gdb.base/set-reg.c: New. gdb: 2012-08-29 Yao Qi <yao@codesourcery.com> * valops.c (value_assign): Emit warning if get_next_frame returns non-NULL. --- gdb/testsuite/gdb.base/set-reg.c | 40 +++++++++++++ gdb/testsuite/gdb.base/set-reg.exp | 108 ++++++++++++++++++++++++++++++++++++ gdb/valops.c | 12 ++++ 3 files changed, 160 insertions(+), 0 deletions(-) create mode 100644 gdb/testsuite/gdb.base/set-reg.c create mode 100644 gdb/testsuite/gdb.base/set-reg.exp diff --git a/gdb/testsuite/gdb.base/set-reg.c b/gdb/testsuite/gdb.base/set-reg.c new file mode 100644 index 0000000..7ce350d --- /dev/null +++ b/gdb/testsuite/gdb.base/set-reg.c @@ -0,0 +1,40 @@ +/* This test is part of GDB, the GNU debugger. + + Copyright 2012 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/>. */ + +int +inner (int x) +{ + return x + 2; +} + +static short +middle (int x) +{ + return 2 * inner (x); +} + +short +top (int x) +{ + return 2 * middle (x); +} + +int +main (int argc, char **argv) +{ + return top (argc); +} diff --git a/gdb/testsuite/gdb.base/set-reg.exp b/gdb/testsuite/gdb.base/set-reg.exp new file mode 100644 index 0000000..3eace34 --- /dev/null +++ b/gdb/testsuite/gdb.base/set-reg.exp @@ -0,0 +1,108 @@ +# Copyright 2012 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 +set executable $testfile + +if { [gdb_compile $srcdir/$subdir/$srcfile $binfile executable {debug}] != "" } { + untested "Failed to compile" + return -1 +} + +# A value in register in previous frame. +set reg_prev_frame_in_reg "unknown" +# A value in memory in previous frame. +set reg_prev_frame_in_memory "unknown" + +if [is_amd64_regs_target] { + set reg_prev_frame_in_reg "rax" + set reg_prev_frame_in_memory "rbp" +} elseif [is_x86_like_target] { + set reg_prev_frame_in_reg "eax" + set reg_prev_frame_in_memory "ebp" +} else { + # set reg_prev_frame_in_reg for other arch. + +} + +# Test setting register on innermost frame and non-innermost frame. + +proc test_set_reg_on_frame {} {with_test_prefix "on frame" { + global executable + global reg_prev_frame_in_reg + global reg_prev_frame_in_memory + global decimal + global hex + global expect_out + global gdb_prompt + + clean_restart $executable + + if ![runto_main] { + return -1 + } + + # Name of register is not set, skip the rest of test. + if { [string equal $reg_prev_frame_in_reg "unknown"] } { + return 1 + } + + gdb_test "break inner" "Breakpoint.*at.* file .*, line.*" + gdb_test "continue" "Continuing.\r\n\r\nBreakpoint.*inner.*at.*" \ + "continue to inner" + + gdb_test_sequence "bt" "bt before setting reg" { + "\[\r\n\]#0 .* inner \\(x=1\\) at " + "\[\r\n\]#1 .* middle \\(x=1\\) at " + "\[\r\n\]#2 .* top \\(x=1\\) at " + "\[\r\n\]#3 .* main \\(.*\\) at" + } + + set reg_val "" + gdb_test_multiple "p/x \$${reg_prev_frame_in_reg}" "print register ${reg_prev_frame_in_reg}" { + -re ".*${decimal} = ($hex).*$gdb_prompt $" { + set reg_val $expect_out(1,string) + } + } + + gdb_test_no_output "set \$${reg_prev_frame_in_reg}=${reg_val}" \ + "set register ${reg_prev_frame_in_reg} on inner frame" + + # Stack is unchanged. + gdb_test_sequence "bt" "bt after setting reg" { + "\[\r\n\]#0 .* inner \\(x=1\\) at " + "\[\r\n\]#1 .* middle \\(x=1\\) at " + "\[\r\n\]#2 .* top \\(x=1\\) at " + "\[\r\n\]#3 .* main \\(.*\\) at" + } + + gdb_test "up" + gdb_test "set \$${reg_prev_frame_in_reg}=${reg_val}" \ + "Frame in which this value is assigned to is not innermost.*" \ + "set register ${reg_prev_frame_in_reg} on non-inner frame" + + gdb_test_sequence "bt" "bt after up" { + "\[\r\n\]#0 .* inner \\(x=1\\) at " + "\[\r\n\]#1 .* middle \\(x=1\\) at " + "\[\r\n\]#2 .* top \\(x=1\\) at " + "\[\r\n\]#3 .* main \\(.*\\) at" + } + + gdb_test "set \$${reg_prev_frame_in_memory}=${reg_val}" \ + "Frame in which this value is assigned to is not innermost.*" \ + "set register ${reg_prev_frame_in_memory} on non-inner frame" +}} + +test_set_reg_on_frame diff --git a/gdb/valops.c b/gdb/valops.c index 17696ee..cb84c87 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -1241,6 +1241,18 @@ value_assign (struct value *toval, struct value *fromval) and then restore the new frame afterwards. */ old_frame = get_frame_id (deprecated_safe_get_selected_frame ()); + /* TOVAL is a register, although VALUE_LVAL(TOVAL) may not be + lval_register. */ + if (VALUE_REGNUM (toval) >= 0) + { + /* Figure out which frame this is in currently. */ + struct frame_info *frame = frame_find_by_id (VALUE_FRAME_ID (toval)); + + if (get_next_frame (frame) != NULL) + warning (_("Frame in which this value is assigned to is" + " not innermost.")); + } + switch (VALUE_LVAL (toval)) { case lval_internalvar: -- 1.7.7.6 ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-08-29 9:51 ` Yao Qi @ 2012-09-04 22:37 ` dje 2012-09-07 10:01 ` Yao Qi 0 siblings, 1 reply; 22+ messages in thread From: dje @ 2012-09-04 22:37 UTC (permalink / raw) To: Yao Qi; +Cc: Tom Tromey, gdb-patches Yao Qi writes: > On 08/24/2012 12:24 AM, Tom Tromey wrote: > > Doug> fwiw, I've been able to work around corrupt, or otherwise not > > Doug> completely useful, core files by setting registers in non-innermost > > Doug> frames. > > Doug> Granted, I knew what was happening underneath the covers, so to speak, > > Doug> and I could have done things differently, but I like this capability. > > > > It does seem like a useful power user tool. > > I'm swayed by this argument. > > OK, looks like I underestimate the power of it. :) > > > > > Doug> I*could* accept a warning when changing a register in a non-innermost > > Doug> frame, fwiw. > > > > That would be ok by me. > > Anyway, this new patch is to emit warning if user modifies register on > non-innermost frame. Regression tested on x86_64-linux native and > gdbserver. OK to apply? > > Note that this test case should be tuned for each target, because of > registers. So far, this test is run effectively on x86 and x86_64. > > -- > Yao > > gdb/testsuite: > > 2012-08-29 Yao Qi <yao@codesourcery.com> > > * gdb.base/set-reg.exp: New. > * gdb.base/set-reg.c: New. > > gdb: > > 2012-08-29 Yao Qi <yao@codesourcery.com> > > * valops.c (value_assign): Emit warning if get_next_frame returns > non-NULL. The description here is insufficient. I look at it and think "Eh?" (e.g. it says nothing about register values). The point is that we're warning about assigning to registers in non-innermost frames, so say that. * valops.c (value_assign): Emit warning when assigning to registers in non-innermost frames. I think this is NEWS-worthy. > --- > gdb/testsuite/gdb.base/set-reg.c | 40 +++++++++++++ > gdb/testsuite/gdb.base/set-reg.exp | 108 ++++++++++++++++++++++++++++++++++++ > gdb/valops.c | 12 ++++ > 3 files changed, 160 insertions(+), 0 deletions(-) > create mode 100644 gdb/testsuite/gdb.base/set-reg.c > create mode 100644 gdb/testsuite/gdb.base/set-reg.exp > > diff --git a/gdb/testsuite/gdb.base/set-reg.c b/gdb/testsuite/gdb.base/set-reg.c > new file mode 100644 > index 0000000..7ce350d > --- /dev/null > +++ b/gdb/testsuite/gdb.base/set-reg.c > @@ -0,0 +1,40 @@ > +/* This test is part of GDB, the GNU debugger. > + > + Copyright 2012 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/>. */ > + > +int > +inner (int x) > +{ > + return x + 2; > +} > + > +static short The mixture short vs int, extern vs static, is excessive. How about make inner,middle,top all "static int" ? > +middle (int x) > +{ > + return 2 * inner (x); > +} > + > +short > +top (int x) > +{ > + return 2 * middle (x); > +} > + > +int > +main (int argc, char **argv) > +{ > + return top (argc); > +} > diff --git a/gdb/testsuite/gdb.base/set-reg.exp b/gdb/testsuite/gdb.base/set-reg.exp > new file mode 100644 > index 0000000..3eace34 > --- /dev/null > +++ b/gdb/testsuite/gdb.base/set-reg.exp > @@ -0,0 +1,108 @@ > +# Copyright 2012 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 > +set executable $testfile > + > +if { [gdb_compile $srcdir/$subdir/$srcfile $binfile executable {debug}] != "" } { > + untested "Failed to compile" > + return -1 > +} > + > +# A value in register in previous frame. --> # A value in register in previous frame. E.g,. call-clobbered register. > +set reg_prev_frame_in_reg "unknown" > +# A value in memory in previous frame. --> # A (register) value in memory in previous frame. E.g., call-saved register. > +set reg_prev_frame_in_memory "unknown" > + > +if [is_amd64_regs_target] { > + set reg_prev_frame_in_reg "rax" > + set reg_prev_frame_in_memory "rbp" > +} elseif [is_x86_like_target] { > + set reg_prev_frame_in_reg "eax" > + set reg_prev_frame_in_memory "ebp" > +} else { > + # set reg_prev_frame_in_reg for other arch. Remove extra blank line before }. > + > +} > + > +# Test setting register on innermost frame and non-innermost frame. > + > +proc test_set_reg_on_frame {} {with_test_prefix "on frame" { Does prefix "on frame" provide anything useful here? [IOW, what would we lose if we remove it?] > + global executable > + global reg_prev_frame_in_reg > + global reg_prev_frame_in_memory > + global decimal > + global hex > + global expect_out > + global gdb_prompt > + > + clean_restart $executable > + > + if ![runto_main] { > + return -1 We don't put much effort into caring about return values when, e.g., runto_main fails (throughout the testsuite). But here, how about just "return". > + } > + > + # Name of register is not set, skip the rest of test. > + if { [string equal $reg_prev_frame_in_reg "unknown"] } { > + return 1 > + } Why have this exit test here? Seems pretty inefficient. How about doing the test in the caller (top level)? [And even skip the compilation step if there's no point.] > + > + gdb_test "break inner" "Breakpoint.*at.* file .*, line.*" > + gdb_test "continue" "Continuing.\r\n\r\nBreakpoint.*inner.*at.*" \ > + "continue to inner" > + > + gdb_test_sequence "bt" "bt before setting reg" { > + "\[\r\n\]#0 .* inner \\(x=1\\) at " > + "\[\r\n\]#1 .* middle \\(x=1\\) at " > + "\[\r\n\]#2 .* top \\(x=1\\) at " > + "\[\r\n\]#3 .* main \\(.*\\) at" > + } > + > + set reg_val "" > + gdb_test_multiple "p/x \$${reg_prev_frame_in_reg}" "print register ${reg_prev_frame_in_reg}" { > + -re ".*${decimal} = ($hex).*$gdb_prompt $" { > + set reg_val $expect_out(1,string) > + } > + } If reg_val is "" at this point the test is in trouble. Better watch for it and handle it appropriately. Plus you assign reg_val to both the in_reg and in_memory regs. Given that one is rax and the other is rbp, that seems awkward. One thing that's missing is that if we're going to test assigning to registers, IWBN to test that the assignment worked. If we assign the value it already had, the assignment may have failed but we wouldn't (as easily) find out. > + > + gdb_test_no_output "set \$${reg_prev_frame_in_reg}=${reg_val}" \ > + "set register ${reg_prev_frame_in_reg} on inner frame" > + > + # Stack is unchanged. > + gdb_test_sequence "bt" "bt after setting reg" { > + "\[\r\n\]#0 .* inner \\(x=1\\) at " > + "\[\r\n\]#1 .* middle \\(x=1\\) at " > + "\[\r\n\]#2 .* top \\(x=1\\) at " > + "\[\r\n\]#3 .* main \\(.*\\) at" > + } > + > + gdb_test "up" > + gdb_test "set \$${reg_prev_frame_in_reg}=${reg_val}" \ > + "Frame in which this value is assigned to is not innermost.*" \ > + "set register ${reg_prev_frame_in_reg} on non-inner frame" > + > + gdb_test_sequence "bt" "bt after up" { > + "\[\r\n\]#0 .* inner \\(x=1\\) at " > + "\[\r\n\]#1 .* middle \\(x=1\\) at " > + "\[\r\n\]#2 .* top \\(x=1\\) at " > + "\[\r\n\]#3 .* main \\(.*\\) at" > + } > + > + gdb_test "set \$${reg_prev_frame_in_memory}=${reg_val}" \ > + "Frame in which this value is assigned to is not innermost.*" \ > + "set register ${reg_prev_frame_in_memory} on non-inner frame" > +}} > + > +test_set_reg_on_frame > diff --git a/gdb/valops.c b/gdb/valops.c > index 17696ee..cb84c87 100644 > --- a/gdb/valops.c > +++ b/gdb/valops.c > @@ -1241,6 +1241,18 @@ value_assign (struct value *toval, struct value *fromval) > and then restore the new frame afterwards. */ > old_frame = get_frame_id (deprecated_safe_get_selected_frame ()); > > + /* TOVAL is a register, although VALUE_LVAL(TOVAL) may not be > + lval_register. */ IWBN to expand on this a bit. E.g., "A call-saved register saved in memory will have VALUE_REGNUM >= 0 but VALUE_LVAL == lval_memory." Or some such. > + if (VALUE_REGNUM (toval) >= 0) > + { > + /* Figure out which frame this is in currently. */ > + struct frame_info *frame = frame_find_by_id (VALUE_FRAME_ID (toval)); > + > + if (get_next_frame (frame) != NULL) > + warning (_("Frame in which this value is assigned to is" > + " not innermost.")); IWBN to mention that this warning explicitly applies to registers. Maybe something like: "Assigning to register in non-innermost frame." > + } > + > switch (VALUE_LVAL (toval)) > { > case lval_internalvar: > -- > 1.7.7.6 ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-09-04 22:37 ` dje @ 2012-09-07 10:01 ` Yao Qi 2012-09-07 10:11 ` Eli Zaretskii 0 siblings, 1 reply; 22+ messages in thread From: Yao Qi @ 2012-09-07 10:01 UTC (permalink / raw) To: dje; +Cc: Tom Tromey, gdb-patches On 09/05/2012 06:36 AM, dje@google.com wrote: > > > > gdb/testsuite: > > > > 2012-08-29 Yao Qi <yao@codesourcery.com> > > > > * gdb.base/set-reg.exp: New. > > * gdb.base/set-reg.c: New. > > > > gdb: > > > > 2012-08-29 Yao Qi <yao@codesourcery.com> > > > > * valops.c (value_assign): Emit warning if get_next_frame returns > > non-NULL. > > The description here is insufficient. > I look at it and think "Eh?" (e.g. it says nothing about register values). > The point is that we're warning about assigning to registers in non-innermost > frames, so say that. > > * valops.c (value_assign): Emit warning when assigning to registers > in non-innermost frames. > OK. > I think this is NEWS-worthy. > I add a NEWS entry. > > + > > +int > > +inner (int x) > > +{ > > + return x + 2; > > +} > > + > > +static short > > The mixture short vs int, extern vs static, is excessive. > How about make inner,middle,top all "static int" ? > Sure. The set-reg.c is copied from other c file in test suite, and I didn't change it much. > > +# A value in register in previous frame. > > --> > # A value in register in previous frame. E.g,. call-clobbered register. > > > +set reg_prev_frame_in_reg "unknown" > > +# A value in memory in previous frame. > > --> > # A (register) value in memory in previous frame. E.g., call-saved register. > Fixed. > > +set reg_prev_frame_in_memory "unknown" > > + > > +if [is_amd64_regs_target] { > > + set reg_prev_frame_in_reg "rax" > > + set reg_prev_frame_in_memory "rbp" > > +} elseif [is_x86_like_target] { > > + set reg_prev_frame_in_reg "eax" > > + set reg_prev_frame_in_memory "ebp" > > +} else { > > + # set reg_prev_frame_in_reg for other arch. > > Remove extra blank line before }. > This blank line is removed. > > + > > +} > > + > > +# Test setting register on innermost frame and non-innermost frame. > > + > > +proc test_set_reg_on_frame {} {with_test_prefix "on frame" { > > Does prefix "on frame" provide anything useful here? > [IOW, what would we lose if we remove it?] > At this moment, it doesn't. However, I plan to add other tests in set-reg.exp in the future, so I add this prefix to avoid duplicated result. > > + global executable > > + global reg_prev_frame_in_reg > > + global reg_prev_frame_in_memory > > + global decimal > > + global hex > > + global expect_out > > + global gdb_prompt > > + > > + clean_restart $executable > > + > > + if ![runto_main] { > > + return -1 > > We don't put much effort into caring about return values when, e.g., > runto_main fails (throughout the testsuite). > But here, how about just "return". > OK. > > + } > > + > > + # Name of register is not set, skip the rest of test. > > + if { [string equal $reg_prev_frame_in_reg "unknown"] } { > > + return 1 > > + } > > Why have this exit test here? Seems pretty inefficient. > How about doing the test in the caller (top level)? > [And even skip the compilation step if there's no point.] > This check can be moved to the very beginning of test, even before compilation step. > > + > > + gdb_test "break inner" "Breakpoint.*at.* file .*, line.*" > > + gdb_test "continue" "Continuing.\r\n\r\nBreakpoint.*inner.*at.*" \ > > + "continue to inner" > > + > > + gdb_test_sequence "bt" "bt before setting reg" { > > + "\[\r\n\]#0 .* inner \\(x=1\\) at " > > + "\[\r\n\]#1 .* middle \\(x=1\\) at " > > + "\[\r\n\]#2 .* top \\(x=1\\) at " > > + "\[\r\n\]#3 .* main \\(.*\\) at" > > + } > > + > > + set reg_val "" > > + gdb_test_multiple "p/x \$${reg_prev_frame_in_reg}" "print register ${reg_prev_frame_in_reg}" { > > + -re ".*${decimal} = ($hex).*$gdb_prompt $" { > > + set reg_val $expect_out(1,string) > > + } > > + } > > If reg_val is "" at this point the test is in trouble. > Better watch for it and handle it appropriately. > OK, it is handled in new version. > Plus you assign reg_val to both the in_reg and in_memory regs. > Given that one is rax and the other is rbp, that seems awkward. > > One thing that's missing is that if we're going to test assigning > to registers, IWBN to test that the assignment worked. > If we assign the value it already had, the assignment may have failed > but we wouldn't (as easily) find out. > The original intention of this test is to verify that an error or warning is emitted when changing register on non-inner most frame. So it doesn't cover 'checking register content is really changed', as you pointed out. I am fine to add them. Note that modifying 'ebp' on non-inner most frame will get registers in mess, so I don't check the contents of 'ebp'. > > + > > + gdb_test_no_output "set \$${reg_prev_frame_in_reg}=${reg_val}" \ > > + "set register ${reg_prev_frame_in_reg} on inner frame" > > + > > + # Stack is unchanged. > > + gdb_test_sequence "bt" "bt after setting reg" { > > + "\[\r\n\]#0 .* inner \\(x=1\\) at " > > + "\[\r\n\]#1 .* middle \\(x=1\\) at " > > + "\[\r\n\]#2 .* top \\(x=1\\) at " > > + "\[\r\n\]#3 .* main \\(.*\\) at" > > + } > > + > > + gdb_test "up" > > + gdb_test "set \$${reg_prev_frame_in_reg}=${reg_val}" \ > > + "Frame in which this value is assigned to is not innermost.*" \ > > + "set register ${reg_prev_frame_in_reg} on non-inner frame" > > + > > + gdb_test_sequence "bt" "bt after up" { > > + "\[\r\n\]#0 .* inner \\(x=1\\) at " > > + "\[\r\n\]#1 .* middle \\(x=1\\) at " > > + "\[\r\n\]#2 .* top \\(x=1\\) at " > > + "\[\r\n\]#3 .* main \\(.*\\) at" > > + } > > + > > + gdb_test "set \$${reg_prev_frame_in_memory}=${reg_val}" \ > > + "Frame in which this value is assigned to is not innermost.*" \ > > + "set register ${reg_prev_frame_in_memory} on non-inner frame" > > +}} > > + > > +test_set_reg_on_frame > > diff --git a/gdb/valops.c b/gdb/valops.c > > index 17696ee..cb84c87 100644 > > --- a/gdb/valops.c > > +++ b/gdb/valops.c > > @@ -1241,6 +1241,18 @@ value_assign (struct value *toval, struct value *fromval) > > and then restore the new frame afterwards. */ > > old_frame = get_frame_id (deprecated_safe_get_selected_frame ()); > > > > + /* TOVAL is a register, although VALUE_LVAL(TOVAL) may not be > > + lval_register. */ > > IWBN to expand on this a bit. > > E.g., > "A call-saved register saved in memory will have VALUE_REGNUM >= 0 > but VALUE_LVAL == lval_memory." > Or some such. > Copied your sentence to the comment directly :). > > + if (VALUE_REGNUM (toval) >= 0) > > + { > > + /* Figure out which frame this is in currently. */ > > + struct frame_info *frame = frame_find_by_id (VALUE_FRAME_ID (toval)); > > + > > + if (get_next_frame (frame) != NULL) > > + warning (_("Frame in which this value is assigned to is" > > + " not innermost.")); > > IWBN to mention that this warning explicitly applies to registers. > Maybe something like: > "Assigning to register in non-innermost frame." > That is better. -- Yao gdb/testsuite: 2012-09-07 Yao Qi <yao@codesourcery.com> * gdb.base/set-reg.exp: New. * gdb.base/set-reg.c: New. gdb: 2012-09-07 Yao Qi <yao@codesourcery.com> Doug Evans <dje@google.com> * valops.c (value_assign): Emit warning when assigning to registers in non-innermost frames. * NEWS: Mention it. --- gdb/NEWS | 3 + gdb/testsuite/gdb.base/set-reg.c | 40 ++++++++++ gdb/testsuite/gdb.base/set-reg.exp | 139 ++++++++++++++++++++++++++++++++++++ gdb/valops.c | 12 +++ 4 files changed, 194 insertions(+), 0 deletions(-) create mode 100644 gdb/testsuite/gdb.base/set-reg.c create mode 100644 gdb/testsuite/gdb.base/set-reg.exp diff --git a/gdb/NEWS b/gdb/NEWS index dba6937..5f1eb67 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -17,6 +17,9 @@ * The 'cd' command now defaults to using '~' (the home directory) if not given an argument. +* GDB will display a warning when assigning to registers in + non-innermost frames. + * New configure options --enable-libmcheck/--disable-libmcheck diff --git a/gdb/testsuite/gdb.base/set-reg.c b/gdb/testsuite/gdb.base/set-reg.c new file mode 100644 index 0000000..03938eb --- /dev/null +++ b/gdb/testsuite/gdb.base/set-reg.c @@ -0,0 +1,40 @@ +/* This test is part of GDB, the GNU debugger. + + Copyright 2012 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/>. */ + +static int +inner (int x) +{ + return x + 2; +} + +static int +middle (int x) +{ + return 2 * inner (x); +} + +static int +top (int x) +{ + return 2 * middle (x); +} + +int +main (int argc, char **argv) +{ + return top (argc); +} diff --git a/gdb/testsuite/gdb.base/set-reg.exp b/gdb/testsuite/gdb.base/set-reg.exp new file mode 100644 index 0000000..5ea65da --- /dev/null +++ b/gdb/testsuite/gdb.base/set-reg.exp @@ -0,0 +1,139 @@ +# Copyright 2012 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 +set executable $testfile + +# A value in register in previous frame. E.g,. call-clobbered register. +set reg_prev_frame_in_reg "unknown" +# A (register) value in memory in previous frame. E.g., call-saved register. +set reg_prev_frame_in_memory "unknown" + +if [is_amd64_regs_target] { + set reg_prev_frame_in_reg "rax" + set reg_prev_frame_in_memory "rbp" +} elseif [is_x86_like_target] { + set reg_prev_frame_in_reg "eax" + set reg_prev_frame_in_memory "ebp" +} else { + # set reg_prev_frame_in_reg for other arch. +} + +# Name of register is not set, skip the rest of test. +if { [string equal $reg_prev_frame_in_reg "unknown"] } { + return +} + +if { [gdb_compile $srcdir/$subdir/$srcfile $binfile executable {debug}] != "" } { + untested "Failed to compile" + return -1 +} + +proc set_register { register check_value } {with_test_prefix "${register} ${check_value}" { + global decimal + global gdb_prompt + + set reg_val "" + gdb_test_multiple "p/d \$${register}" "print register ${register}" { + -re ".*${decimal} = ($decimal).*$gdb_prompt $" { + set reg_val $expect_out(1,string) + } + } + if { $reg_val == "" } { + fail "get the value of register ${register}" + return + } + + # Assign the same value to register. + gdb_test_no_output "set \$${register}=${reg_val}" \ + "set register ${register} on inner frame (unchanged)" + + # Assign the different value to register. + set reg_val [expr $reg_val + 4] + gdb_test_no_output "set \$${register}=${reg_val}" \ + "set register ${register} on inner frame (changed)" + gdb_test "p/d \$${register}" ".*${decimal} = $reg_val.*" \ + "register ${register} is changed" + + # Restore register. + set reg_val [expr $reg_val - 4] + gdb_test_no_output "set \$${register}=${reg_val}" \ + "set register ${register} on inner frame (restored)" + gdb_test "p/d \$${register}" ".*${decimal} = $reg_val.*" \ + "register ${register} is restored" + + gdb_test "up" + + gdb_test_multiple "p/d \$${register}" "print register ${register}" { + -re ".*${decimal} = ($decimal).*$gdb_prompt $" { + set reg_val $expect_out(1,string) + } + } + if { $reg_val == "" } { + fail "get the value of register ${register}" + return + } + # Assign the same value to register. + gdb_test "set \$${register}=${reg_val}" \ + "Assigning to register in non-innermost frame.*" \ + "set register ${register} on non-inner frame" + + if { $check_value } { + # Assign the different value to register. + set reg_val [expr $reg_val + 4] + gdb_test "set \$${register}=${reg_val}" \ + "Assigning to register in non-innermost frame.*" \ + "set register ${register} on non-inner frame (changed)" + gdb_test "p/d \$${register}" ".*${decimal} = $reg_val.*" \ + "register ${register} is changed" + # Restore register. + set reg_val [expr $reg_val - 4] + gdb_test "set \$${register}=${reg_val}" \ + "Assigning to register in non-innermost frame.*" \ + "set register ${register} on non-inner frame (restored)" + gdb_test "p/d \$${register}" ".*${decimal} = $reg_val.*" \ + "register ${register} is restored" + } + gdb_test "down" +}} + +# Test setting register on innermost frame and non-innermost frame. + +proc test_set_reg_on_frame {} {with_test_prefix "on frame" { + global executable + global reg_prev_frame_in_reg + global reg_prev_frame_in_memory + global decimal + global hex + global expect_out + global gdb_prompt + + clean_restart $executable + + if ![runto_main] { + return + } + + gdb_test "break inner" "Breakpoint.*at.* file .*, line.*" + gdb_test "continue" "Continuing.\r\n\r\nBreakpoint.*inner.*at.*" \ + "continue to inner" + + set_register ${reg_prev_frame_in_reg} 1 + # Change the register saved on frame may clobber the frame, and get + # the contents of registers in mess. Don't check register value. + set_register ${reg_prev_frame_in_memory} 0 +}} + +test_set_reg_on_frame diff --git a/gdb/valops.c b/gdb/valops.c index 17696ee..ae19601 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -1241,6 +1241,18 @@ value_assign (struct value *toval, struct value *fromval) and then restore the new frame afterwards. */ old_frame = get_frame_id (deprecated_safe_get_selected_frame ()); + /* TOVAL is a register, although VALUE_LVAL(TOVAL) may not be + lval_register. A call-saved register saved in memory will have + 'VALUE_REGNUM >= 0' but 'VALUE_LVAL == lval_memory'. */ + if (VALUE_REGNUM (toval) >= 0) + { + /* Figure out which frame this is in currently. */ + struct frame_info *frame = frame_find_by_id (VALUE_FRAME_ID (toval)); + + if (get_next_frame (frame) != NULL) + warning (_("Assigning to register in non-innermost frame.")); + } + switch (VALUE_LVAL (toval)) { case lval_internalvar: -- 1.7.7.6 ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-09-07 10:01 ` Yao Qi @ 2012-09-07 10:11 ` Eli Zaretskii 2012-09-07 10:21 ` Yao Qi 0 siblings, 1 reply; 22+ messages in thread From: Eli Zaretskii @ 2012-09-07 10:11 UTC (permalink / raw) To: Yao Qi; +Cc: dje, tromey, gdb-patches > Date: Fri, 7 Sep 2012 18:00:37 +0800 > From: Yao Qi <yao@codesourcery.com> > CC: Tom Tromey <tromey@redhat.com>, <gdb-patches@sourceware.org> > > > I think this is NEWS-worthy. > > > > I add a NEWS entry. Thanks. But its text says "when assigning" without explaining enough for the reader to understand what "assigning" are we talking about. So I suggest to mention the commands that are affected ('set', IIUC), and perhaps give an example. ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-09-07 10:11 ` Eli Zaretskii @ 2012-09-07 10:21 ` Yao Qi 2012-09-07 11:27 ` Eli Zaretskii 0 siblings, 1 reply; 22+ messages in thread From: Yao Qi @ 2012-09-07 10:21 UTC (permalink / raw) To: Eli Zaretskii; +Cc: dje, tromey, gdb-patches On 09/07/2012 06:10 PM, Eli Zaretskii wrote: >> Date: Fri, 7 Sep 2012 18:00:37 +0800 >> From: Yao Qi <yao@codesourcery.com> >> CC: Tom Tromey <tromey@redhat.com>, <gdb-patches@sourceware.org> >> >>> I think this is NEWS-worthy. >>> >> >> I add a NEWS entry. > > Thanks. But its text says "when assigning" without explaining enough > for the reader to understand what "assigning" are we talking about. > So I suggest to mention the commands that are affected ('set', IIUC), > and perhaps give an example. > How about them below? * GDB will display a warning when assigning to registers, for example "set $eax=0x1", in non-innermost frames. or * GDB will display a warning when assigning to registers by command "set" in non-innermost frames. -- Yao ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-09-07 10:21 ` Yao Qi @ 2012-09-07 11:27 ` Eli Zaretskii 2012-09-07 13:14 ` Yao Qi 0 siblings, 1 reply; 22+ messages in thread From: Eli Zaretskii @ 2012-09-07 11:27 UTC (permalink / raw) To: Yao Qi; +Cc: dje, tromey, gdb-patches > Date: Fri, 7 Sep 2012 18:19:28 +0800 > From: Yao Qi <yao@codesourcery.com> > CC: <dje@google.com>, <tromey@redhat.com>, <gdb-patches@sourceware.org> > > On 09/07/2012 06:10 PM, Eli Zaretskii wrote: > >> Date: Fri, 7 Sep 2012 18:00:37 +0800 > >> From: Yao Qi <yao@codesourcery.com> > >> CC: Tom Tromey <tromey@redhat.com>, <gdb-patches@sourceware.org> > >> > >>> I think this is NEWS-worthy. > >>> > >> > >> I add a NEWS entry. > > > > Thanks. But its text says "when assigning" without explaining enough > > for the reader to understand what "assigning" are we talking about. > > So I suggest to mention the commands that are affected ('set', IIUC), > > and perhaps give an example. > > > > How about them below? > > * GDB will display a warning when assigning to registers, for example > "set $eax=0x1", in non-innermost frames. > > or > > * GDB will display a warning when assigning to registers by command > "set" in non-innermost frames. If "set" is the only command, then the second variant is better. But isn't the same true of the MI equivalent? ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-09-07 11:27 ` Eli Zaretskii @ 2012-09-07 13:14 ` Yao Qi 2012-09-07 14:32 ` Eli Zaretskii 0 siblings, 1 reply; 22+ messages in thread From: Yao Qi @ 2012-09-07 13:14 UTC (permalink / raw) To: Eli Zaretskii; +Cc: dje, tromey, gdb-patches On 09/07/2012 07:27 PM, Eli Zaretskii wrote: >> >How about them below? >> > >> >* GDB will display a warning when assigning to registers, for example >> > "set $eax=0x1", in non-innermost frames. >> > >> >or >> > >> >* GDB will display a warning when assigning to registers by command >> > "set" in non-innermost frames. > If "set" is the only command, then the second variant is better. But > isn't the same true of the MI equivalent? It is true to the MI command "-var-assign". How about this? * GDB will display a warning when assigning to registers by CLI command "set" or MI command "-var-assign" in non-innermost frames. -- Yao ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-09-07 13:14 ` Yao Qi @ 2012-09-07 14:32 ` Eli Zaretskii 0 siblings, 0 replies; 22+ messages in thread From: Eli Zaretskii @ 2012-09-07 14:32 UTC (permalink / raw) To: Yao Qi; +Cc: dje, tromey, gdb-patches > Date: Fri, 7 Sep 2012 21:12:54 +0800 > From: Yao Qi <yao@codesourcery.com> > CC: <dje@google.com>, <tromey@redhat.com>, <gdb-patches@sourceware.org> > > On 09/07/2012 07:27 PM, Eli Zaretskii wrote: > >> >How about them below? > >> > > >> >* GDB will display a warning when assigning to registers, for example > >> > "set $eax=0x1", in non-innermost frames. > >> > > >> >or > >> > > >> >* GDB will display a warning when assigning to registers by command > >> > "set" in non-innermost frames. > > If "set" is the only command, then the second variant is better. But > > isn't the same true of the MI equivalent? > > It is true to the MI command "-var-assign". How about this? > > * GDB will display a warning when assigning to registers by CLI command > "set" or MI command "-var-assign" in non-innermost frames. Fine, thanks. ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-08-23 16:25 ` Tom Tromey 2012-08-29 9:51 ` Yao Qi @ 2012-09-07 16:46 ` Jan Kratochvil 2012-09-09 2:31 ` Yao Qi 2012-09-10 2:02 ` Yao Qi 1 sibling, 2 replies; 22+ messages in thread From: Jan Kratochvil @ 2012-09-07 16:46 UTC (permalink / raw) To: Tom Tromey; +Cc: Doug Evans, Yao Qi, gdb-patches On Thu, 23 Aug 2012 18:24:46 +0200, Tom Tromey wrote: > >>>>> "Doug" == Doug Evans <dje@google.com> writes: > Doug> I *could* accept a warning when changing a register in a non-innermost > Doug> frame, fwiw. > > That would be ok by me. I do not find correct even the warning. It is absolutely normal user operation: volatile int v = 42; void __attribute__ ((noinline, noclone)) f (void) { v++; } int main (void) { int i = v; f (); return i; } gcc-4.7.1-5.fc18.x86_64 gcc -Wall -g -O2 readelf -wio <2><61>: Abbrev Number: 4 (DW_TAG_variable) <62> DW_AT_name : i <6a> DW_AT_location : 0x0 (location list) Offset Begin End Expression 00000000 0000000000400407 000000000040040f (DW_OP_reg3 (rbx)) 00000000 000000000040040f 0000000000400410 (DW_OP_reg0 (rax)) 00000000 <End of list> (gdb) b f Breakpoint 1 at 0x400500: file 70.c, line 5. (gdb) run Breakpoint 1, f () at 70.c:5 (gdb) up #1 0x000000000040040c in main () at 70.c:11 11 f (); (gdb) set variable i=20 warning: Assigning to register in non-innermost frame. Why? I would find correct to print such warning for: (gdb) set $rbx=20 There it should happen only if user has explicitly specified the register itself. Also I would find more appropriate to call 'query' in such case but that is a nitpick. Thanks, Jan ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-09-07 16:46 ` Jan Kratochvil @ 2012-09-09 2:31 ` Yao Qi 2012-09-10 2:02 ` Yao Qi 1 sibling, 0 replies; 22+ messages in thread From: Yao Qi @ 2012-09-09 2:31 UTC (permalink / raw) To: Jan Kratochvil; +Cc: Tom Tromey, Doug Evans, gdb-patches On 09/08/2012 12:45 AM, Jan Kratochvil wrote: > (gdb) set variable i=20 > warning: Assigning to register in non-innermost frame. > > Why? > That is a surprise to me. > I would find correct to print such warning for: > (gdb) set $rbx=20 > There it should happen only if user has explicitly specified the register > itself. That is my original motivation of this patch. I'll go back to see how to differentiate register and variables in evaluation. -- Yao ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-09-07 16:46 ` Jan Kratochvil 2012-09-09 2:31 ` Yao Qi @ 2012-09-10 2:02 ` Yao Qi 2012-09-10 7:47 ` Jan Kratochvil 2012-09-11 17:12 ` Tom Tromey 1 sibling, 2 replies; 22+ messages in thread From: Yao Qi @ 2012-09-10 2:02 UTC (permalink / raw) To: Jan Kratochvil; +Cc: Tom Tromey, Doug Evans, gdb-patches On 09/08/2012 12:45 AM, Jan Kratochvil wrote: > (gdb) set variable i=20 > warning: Assigning to register in non-innermost frame. > > Why? > > I would find correct to print such warning for: > (gdb) set $rbx=20 > There it should happen only if user has explicitly specified the register > itself. I checked the difference of value of register and value of variable (saved in register), TYPE_OBJFILE_OWNED (value_type (toval)) is the only difference between them. IIUC, type of variable is owned by an objfile, while type of register is not (alloc_type vs. alloc_type_arch), so looks we can use it to differentiate values in register and values in variables. This new patch includes TYPE_OBJFILE_OWNED into the condition, and adjust the NEWS entry discussed with Eli before. -- Yao gdb/testsuite: 2012-09-10 Yao Qi <yao@codesourcery.com> * gdb.base/set-reg.exp: New. * gdb.base/set-reg.c: New. gdb: 2012-09-10 Yao Qi <yao@codesourcery.com> Doug Evans <dje@google.com> * valops.c (value_assign): Emit warning when assigning to registers in non-innermost frames. * NEWS: Mention it. --- gdb/NEWS | 3 + gdb/testsuite/gdb.base/set-reg.c | 40 ++++++++++ gdb/testsuite/gdb.base/set-reg.exp | 139 ++++++++++++++++++++++++++++++++++++ gdb/valops.c | 16 ++++ 4 files changed, 198 insertions(+), 0 deletions(-) create mode 100644 gdb/testsuite/gdb.base/set-reg.c create mode 100644 gdb/testsuite/gdb.base/set-reg.exp diff --git a/gdb/NEWS b/gdb/NEWS index dba6937..f7476e4 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -17,6 +17,9 @@ * The 'cd' command now defaults to using '~' (the home directory) if not given an argument. +* GDB will display a warning when assigning to registers by CLI command + "set" or MI command "-var-assign" in non-innermost frames. + * New configure options --enable-libmcheck/--disable-libmcheck diff --git a/gdb/testsuite/gdb.base/set-reg.c b/gdb/testsuite/gdb.base/set-reg.c new file mode 100644 index 0000000..03938eb --- /dev/null +++ b/gdb/testsuite/gdb.base/set-reg.c @@ -0,0 +1,40 @@ +/* This test is part of GDB, the GNU debugger. + + Copyright 2012 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/>. */ + +static int +inner (int x) +{ + return x + 2; +} + +static int +middle (int x) +{ + return 2 * inner (x); +} + +static int +top (int x) +{ + return 2 * middle (x); +} + +int +main (int argc, char **argv) +{ + return top (argc); +} diff --git a/gdb/testsuite/gdb.base/set-reg.exp b/gdb/testsuite/gdb.base/set-reg.exp new file mode 100644 index 0000000..5ea65da --- /dev/null +++ b/gdb/testsuite/gdb.base/set-reg.exp @@ -0,0 +1,139 @@ +# Copyright 2012 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 +set executable $testfile + +# A value in register in previous frame. E.g,. call-clobbered register. +set reg_prev_frame_in_reg "unknown" +# A (register) value in memory in previous frame. E.g., call-saved register. +set reg_prev_frame_in_memory "unknown" + +if [is_amd64_regs_target] { + set reg_prev_frame_in_reg "rax" + set reg_prev_frame_in_memory "rbp" +} elseif [is_x86_like_target] { + set reg_prev_frame_in_reg "eax" + set reg_prev_frame_in_memory "ebp" +} else { + # set reg_prev_frame_in_reg for other arch. +} + +# Name of register is not set, skip the rest of test. +if { [string equal $reg_prev_frame_in_reg "unknown"] } { + return +} + +if { [gdb_compile $srcdir/$subdir/$srcfile $binfile executable {debug}] != "" } { + untested "Failed to compile" + return -1 +} + +proc set_register { register check_value } {with_test_prefix "${register} ${check_value}" { + global decimal + global gdb_prompt + + set reg_val "" + gdb_test_multiple "p/d \$${register}" "print register ${register}" { + -re ".*${decimal} = ($decimal).*$gdb_prompt $" { + set reg_val $expect_out(1,string) + } + } + if { $reg_val == "" } { + fail "get the value of register ${register}" + return + } + + # Assign the same value to register. + gdb_test_no_output "set \$${register}=${reg_val}" \ + "set register ${register} on inner frame (unchanged)" + + # Assign the different value to register. + set reg_val [expr $reg_val + 4] + gdb_test_no_output "set \$${register}=${reg_val}" \ + "set register ${register} on inner frame (changed)" + gdb_test "p/d \$${register}" ".*${decimal} = $reg_val.*" \ + "register ${register} is changed" + + # Restore register. + set reg_val [expr $reg_val - 4] + gdb_test_no_output "set \$${register}=${reg_val}" \ + "set register ${register} on inner frame (restored)" + gdb_test "p/d \$${register}" ".*${decimal} = $reg_val.*" \ + "register ${register} is restored" + + gdb_test "up" + + gdb_test_multiple "p/d \$${register}" "print register ${register}" { + -re ".*${decimal} = ($decimal).*$gdb_prompt $" { + set reg_val $expect_out(1,string) + } + } + if { $reg_val == "" } { + fail "get the value of register ${register}" + return + } + # Assign the same value to register. + gdb_test "set \$${register}=${reg_val}" \ + "Assigning to register in non-innermost frame.*" \ + "set register ${register} on non-inner frame" + + if { $check_value } { + # Assign the different value to register. + set reg_val [expr $reg_val + 4] + gdb_test "set \$${register}=${reg_val}" \ + "Assigning to register in non-innermost frame.*" \ + "set register ${register} on non-inner frame (changed)" + gdb_test "p/d \$${register}" ".*${decimal} = $reg_val.*" \ + "register ${register} is changed" + # Restore register. + set reg_val [expr $reg_val - 4] + gdb_test "set \$${register}=${reg_val}" \ + "Assigning to register in non-innermost frame.*" \ + "set register ${register} on non-inner frame (restored)" + gdb_test "p/d \$${register}" ".*${decimal} = $reg_val.*" \ + "register ${register} is restored" + } + gdb_test "down" +}} + +# Test setting register on innermost frame and non-innermost frame. + +proc test_set_reg_on_frame {} {with_test_prefix "on frame" { + global executable + global reg_prev_frame_in_reg + global reg_prev_frame_in_memory + global decimal + global hex + global expect_out + global gdb_prompt + + clean_restart $executable + + if ![runto_main] { + return + } + + gdb_test "break inner" "Breakpoint.*at.* file .*, line.*" + gdb_test "continue" "Continuing.\r\n\r\nBreakpoint.*inner.*at.*" \ + "continue to inner" + + set_register ${reg_prev_frame_in_reg} 1 + # Change the register saved on frame may clobber the frame, and get + # the contents of registers in mess. Don't check register value. + set_register ${reg_prev_frame_in_memory} 0 +}} + +test_set_reg_on_frame diff --git a/gdb/valops.c b/gdb/valops.c index 17696ee..7ac23e3 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -1241,6 +1241,22 @@ value_assign (struct value *toval, struct value *fromval) and then restore the new frame afterwards. */ old_frame = get_frame_id (deprecated_safe_get_selected_frame ()); + /* TOVAL is a register, although VALUE_LVAL(TOVAL) may not be + lval_register. A call-saved register saved in memory will have + 'VALUE_REGNUM >= 0' but 'VALUE_LVAL == lval_memory'. We also have to + avoid emitting warning when assign value to some local variables which + are stored in registers, TYPE_OBJFILE_OWNED helps to differentiate + we are assigning to a register explicitly or to a variable saved in + register. */ + if (VALUE_REGNUM (toval) >= 0 && !TYPE_OBJFILE_OWNED (type)) + { + /* Figure out which frame this is in currently. */ + struct frame_info *frame = frame_find_by_id (VALUE_FRAME_ID (toval)); + + if (get_next_frame (frame) != NULL) + warning (_("Assigning to register in non-innermost frame.")); + } + switch (VALUE_LVAL (toval)) { case lval_internalvar: -- 1.7.7.6 ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-09-10 2:02 ` Yao Qi @ 2012-09-10 7:47 ` Jan Kratochvil 2012-09-10 19:43 ` Jan Kratochvil 2012-09-11 17:12 ` Tom Tromey 1 sibling, 1 reply; 22+ messages in thread From: Jan Kratochvil @ 2012-09-10 7:47 UTC (permalink / raw) To: Yao Qi; +Cc: Tom Tromey, Doug Evans, gdb-patches On Mon, 10 Sep 2012 04:00:58 +0200, Yao Qi wrote: > --- /dev/null > +++ b/gdb/testsuite/gdb.base/set-reg.c [...] > +main (int argc, char **argv) Two spaces. [...] > --- a/gdb/valops.c > +++ b/gdb/valops.c > @@ -1241,6 +1241,22 @@ value_assign (struct value *toval, struct value *fromval) > and then restore the new frame afterwards. */ > old_frame = get_frame_id (deprecated_safe_get_selected_frame ()); > > + /* TOVAL is a register, although VALUE_LVAL(TOVAL) may not be GNU formatting applies also to comments: VALUE_LVAL (TOVAL) > + lval_register. A call-saved register saved in memory will have > + 'VALUE_REGNUM >= 0' but 'VALUE_LVAL == lval_memory'. We also have to > + avoid emitting warning when assign value to some local variables which > + are stored in registers, TYPE_OBJFILE_OWNED helps to differentiate > + we are assigning to a register explicitly or to a variable saved in > + register. */ > + if (VALUE_REGNUM (toval) >= 0 && !TYPE_OBJFILE_OWNED (type)) There should be a better comment at value->regnum: /* Register number if the value is from a register. */ short regnum; as currently it looks to me that value->regnum is not defined for value->lval != lval_register This your patch IMO exploits side-effect behavior of value_of_register function implementation, it would be good to document we depend now on this REGNUM meaning in both value->regnum and in value_of_register. > + { > + /* Figure out which frame this is in currently. */ > + struct frame_info *frame = frame_find_by_id (VALUE_FRAME_ID (toval)); > + > + if (get_next_frame (frame) != NULL) This is not safe, I do not have a countercase reproducer but in general frame_find_by_id can return NULL and even the code below checks for it: case lval_register: [...] frame = frame_find_by_id (VALUE_FRAME_ID (toval)); [...] if (!frame) error (_("Value being assigned to is no longer active.")); Something could call reinit_frame_cache in the meantime (see the issues from PR 13866) and then frame_ids may become stale. Either put there also the error check/call or I would find easier: if (frame_relative_level (frame) == 0) > + warning (_("Assigning to register in non-innermost frame.")); Are you / other people really against a query() here? This way if one does the non-zero frame assignment it will print the warning. User says oops, I did not want to do it - but the damage has been already done, unintended memory is overwritten and there is no way back. I was suggesting something like: if (!query (_("Really assign to stored register in non-innermost frame? "))) error (_("Not confirmed.")); I understand you are more concerned with MI but if I read correctly MI will answer it as 'y', unaware whether the query message gets propagated to your MI frontend so maybe you would like: if (query (_("Really assign to stored register in non-innermost frame? "))) warning (_("Assigning to register in non-innermost frame.")); else error (_("Not confirmed.")); > + } > + > switch (VALUE_LVAL (toval)) > { > case lval_internalvar: > -- > 1.7.7.6 Thanks, Jan ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-09-10 7:47 ` Jan Kratochvil @ 2012-09-10 19:43 ` Jan Kratochvil 0 siblings, 0 replies; 22+ messages in thread From: Jan Kratochvil @ 2012-09-10 19:43 UTC (permalink / raw) To: Yao Qi; +Cc: Tom Tromey, Doug Evans, gdb-patches On Mon, 10 Sep 2012 09:46:39 +0200, Jan Kratochvil wrote: > I understand you are more concerned with MI but if I read correctly MI will > answer it as 'y', unaware whether the query message gets propagated to your MI > frontend so maybe you would like: > > if (query (_("Really assign to stored register in non-innermost frame? "))) > warning (_("Assigning to register in non-innermost frame.")); > else > error (_("Not confirmed.")); or to prevent double-warning in CLI mode: if (!query (_("Really assign to stored register in non-innermost frame? "))) error (_("Not confirmed.")); else if (ui_out_is_mi_like_p (uiout)) warning (_("Assigning to register in non-innermost frame.")); Thanks, Jan ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-09-10 2:02 ` Yao Qi 2012-09-10 7:47 ` Jan Kratochvil @ 2012-09-11 17:12 ` Tom Tromey 2012-09-11 17:19 ` Jan Kratochvil 2012-09-12 0:51 ` Yao Qi 1 sibling, 2 replies; 22+ messages in thread From: Tom Tromey @ 2012-09-11 17:12 UTC (permalink / raw) To: Yao Qi; +Cc: Jan Kratochvil, Doug Evans, gdb-patches >>>>> "Yao" == Yao Qi <yao@codesourcery.com> writes: Yao> I checked the difference of value of register and value of variable Yao> (saved in register), TYPE_OBJFILE_OWNED (value_type (toval)) is the Yao> only difference between them. IIUC, type of variable is owned by an Yao> objfile, while type of register is not (alloc_type vs. alloc_type_arch), Yao> so looks we can use it to differentiate values in register and values Yao> in variables. I would rather not use this approach. My reason is that there is no obvious connection between TYPE_OBJFILE_OWNED and register-ness -- and it is the sort of invariant that is very difficult to ensure will remain true over time. If lval_register can't work, then another choice would be a new flag on struct value. This would be somewhat ugly but, I think, more robust. As Jan points out, it does seem strange to warn about the set rather than query. A warning comes too late. After re-reading the whole thread I wonder whether pressing forward with this patch is best. IIUC this didn't arise from any user complaint but rather just thinking about the problem. And, while I agree it could be confusing in some situations, in practice it seems that perhaps it has never actually bitten anybody. Tom ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-09-11 17:12 ` Tom Tromey @ 2012-09-11 17:19 ` Jan Kratochvil 2012-09-11 17:23 ` Tom Tromey 2012-09-12 0:51 ` Yao Qi 1 sibling, 1 reply; 22+ messages in thread From: Jan Kratochvil @ 2012-09-11 17:19 UTC (permalink / raw) To: Tom Tromey; +Cc: Yao Qi, Doug Evans, gdb-patches On Tue, 11 Sep 2012 19:12:31 +0200, Tom Tromey wrote: > I would rather not use this approach. > My reason is that there is no obvious connection between > TYPE_OBJFILE_OWNED and register-ness -- and it is the sort of invariant > that is very difficult to ensure will remain true over time. > > If lval_register can't work, then another choice would be a new flag on > struct value. This would be somewhat ugly but, I think, more robust. I also did not like TYPE_OBJFILE_OWNED too much, I was thinking putting it possibly to evaluate_subexp_standard <BINOP_ASSIGN> checking there LHS (left hand side) expression, not the LHS value. Sure one can easily create an expression escaping such check but it should catch the normal problematic case; if there is any. Thanks, Jan ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-09-11 17:19 ` Jan Kratochvil @ 2012-09-11 17:23 ` Tom Tromey 0 siblings, 0 replies; 22+ messages in thread From: Tom Tromey @ 2012-09-11 17:23 UTC (permalink / raw) To: Jan Kratochvil; +Cc: Yao Qi, Doug Evans, gdb-patches >>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes: Jan> I also did not like TYPE_OBJFILE_OWNED too much, I was thinking putting it Jan> possibly to Jan> evaluate_subexp_standard <BINOP_ASSIGN> Jan> checking there LHS (left hand side) expression, not the LHS value. Yeah, I almost mentioned that approach as well. I agree it would probably be ok; but it also requires checking whether any language overrides this opcode. Tom ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-09-11 17:12 ` Tom Tromey 2012-09-11 17:19 ` Jan Kratochvil @ 2012-09-12 0:51 ` Yao Qi 2012-09-12 13:19 ` Jan Kratochvil 1 sibling, 1 reply; 22+ messages in thread From: Yao Qi @ 2012-09-12 0:51 UTC (permalink / raw) To: Tom Tromey; +Cc: Jan Kratochvil, Doug Evans, gdb-patches On 09/12/2012 01:12 AM, Tom Tromey wrote: > After re-reading the whole thread I wonder whether pressing forward with > this patch is best. IIUC this didn't arise from any user complaint but > rather just thinking about the problem. And, while I agree it could be > confusing in some situations, in practice it seems that perhaps it has > never actually bitten anybody. I also realize that this patch becomes "unnatural" :) so I drop it. I'll revisit it when we do have the need to fix this problem. The test case is still useful, as we don't have test for setting registers in testsuite. Is the test case OK? -- Yao gdb/testsuite 2012-09-12 Yao Qi <yao@codesourcery.com> * gdb.base/set-reg.exp: New. * gdb.base/set-reg.c: New. --- gdb/testsuite/gdb.base/set-reg.c | 40 +++++++++++ gdb/testsuite/gdb.base/set-reg.exp | 136 ++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+), 0 deletions(-) create mode 100644 gdb/testsuite/gdb.base/set-reg.c create mode 100644 gdb/testsuite/gdb.base/set-reg.exp diff --git a/gdb/testsuite/gdb.base/set-reg.c b/gdb/testsuite/gdb.base/set-reg.c new file mode 100644 index 0000000..86d6496 --- /dev/null +++ b/gdb/testsuite/gdb.base/set-reg.c @@ -0,0 +1,40 @@ +/* This test is part of GDB, the GNU debugger. + + Copyright 2012 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/>. */ + +static int +inner (int x) +{ + return x + 2; +} + +static int +middle (int x) +{ + return 2 * inner (x); +} + +static int +top (int x) +{ + return 2 * middle (x); +} + +int +main (int argc, char **argv) +{ + return top (argc); +} diff --git a/gdb/testsuite/gdb.base/set-reg.exp b/gdb/testsuite/gdb.base/set-reg.exp new file mode 100644 index 0000000..3353a10 --- /dev/null +++ b/gdb/testsuite/gdb.base/set-reg.exp @@ -0,0 +1,136 @@ +# Copyright 2012 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 +set executable $testfile + +# A value in register in previous frame. E.g,. call-clobbered register. +set reg_prev_frame_in_reg "unknown" +# A (register) value in memory in previous frame. E.g., call-saved register. +set reg_prev_frame_in_memory "unknown" + +if [is_amd64_regs_target] { + set reg_prev_frame_in_reg "rax" + set reg_prev_frame_in_memory "rbp" +} elseif [is_x86_like_target] { + set reg_prev_frame_in_reg "eax" + set reg_prev_frame_in_memory "ebp" +} else { + # set reg_prev_frame_in_reg for other arch. +} + +# Name of register is not set, skip the rest of test. +if { [string equal $reg_prev_frame_in_reg "unknown"] } { + return +} + +if { [gdb_compile $srcdir/$subdir/$srcfile $binfile executable {debug}] != "" } { + untested "Failed to compile" + return -1 +} + +proc set_register { register check_value } {with_test_prefix "${register} ${check_value}" { + global decimal + global gdb_prompt + + set reg_val "" + gdb_test_multiple "p/d \$${register}" "print register ${register}" { + -re ".*${decimal} = ($decimal).*$gdb_prompt $" { + set reg_val $expect_out(1,string) + } + } + if { $reg_val == "" } { + fail "get the value of register ${register}" + return + } + + # Assign the same value to register. + gdb_test_no_output "set \$${register}=${reg_val}" \ + "set register ${register} on inner frame (unchanged)" + + # Assign the different value to register. + set reg_val [expr $reg_val + 4] + gdb_test_no_output "set \$${register}=${reg_val}" \ + "set register ${register} on inner frame (changed)" + gdb_test "p/d \$${register}" ".*${decimal} = $reg_val.*" \ + "register ${register} is changed" + + # Restore register. + set reg_val [expr $reg_val - 4] + gdb_test_no_output "set \$${register}=${reg_val}" \ + "set register ${register} on inner frame (restored)" + gdb_test "p/d \$${register}" ".*${decimal} = $reg_val.*" \ + "register ${register} is restored" + + gdb_test "up" + + gdb_test_multiple "p/d \$${register}" "print register ${register}" { + -re ".*${decimal} = ($decimal).*$gdb_prompt $" { + set reg_val $expect_out(1,string) + } + } + if { $reg_val == "" } { + fail "get the value of register ${register}" + return + } + # Assign the same value to register. + gdb_test_no_output "set \$${register}=${reg_val}" \ + "set register ${register} on non-inner frame" + + if { $check_value } { + # Assign the different value to register. + set reg_val [expr $reg_val + 4] + gdb_test_no_output "set \$${register}=${reg_val}" \ + "set register ${register} on non-inner frame (changed)" + gdb_test "p/d \$${register}" ".*${decimal} = $reg_val.*" \ + "register ${register} is changed" + # Restore register. + set reg_val [expr $reg_val - 4] + gdb_test_no_output "set \$${register}=${reg_val}" \ + "set register ${register} on non-inner frame (restored)" + gdb_test "p/d \$${register}" ".*${decimal} = $reg_val.*" \ + "register ${register} is restored" + } + gdb_test "down" +}} + +# Test setting register on innermost frame and non-innermost frame. + +proc test_set_reg_on_frame {} {with_test_prefix "on frame" { + global executable + global reg_prev_frame_in_reg + global reg_prev_frame_in_memory + global decimal + global hex + global expect_out + global gdb_prompt + + clean_restart $executable + + if ![runto_main] { + return + } + + gdb_test "break inner" "Breakpoint.*at.* file .*, line.*" + gdb_test "continue" "Continuing.\r\n\r\nBreakpoint.*inner.*at.*" \ + "continue to inner" + + set_register ${reg_prev_frame_in_reg} 1 + # Change the register saved on frame may clobber the frame, and get + # the contents of registers in mess. Don't check register value. + set_register ${reg_prev_frame_in_memory} 0 +}} + +test_set_reg_on_frame -- 1.7.7.6 ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [RFC] Don't allow setting register in non-innermost frame 2012-09-12 0:51 ` Yao Qi @ 2012-09-12 13:19 ` Jan Kratochvil 0 siblings, 0 replies; 22+ messages in thread From: Jan Kratochvil @ 2012-09-12 13:19 UTC (permalink / raw) To: Yao Qi; +Cc: Tom Tromey, Doug Evans, gdb-patches On Wed, 12 Sep 2012 02:50:17 +0200, Yao Qi wrote: > I also realize that this patch becomes "unnatural" :) so I drop it. > I'll revisit it when we do have the need to fix this problem. The test > case is still useful, as we don't have test for setting registers in > testsuite. Is the test case OK? In such case could it be more arch-independent? It could either use register int var; and 'info addr var' to set that register, independently of arch. Or it could 'info reg' and also set some of the register(s) printed. Currently the testcase seems pretty complicated to me for a single 'set $reg=num' test. Thanks, Jan ^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2012-09-12 13:19 UTC | newest] Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2012-08-17 2:21 [RFC] Don't allow setting register in non-innermost frame Yao Qi 2012-08-20 20:19 ` Doug Evans 2012-08-21 3:27 ` Yao Qi 2012-08-23 16:25 ` Tom Tromey 2012-08-29 9:51 ` Yao Qi 2012-09-04 22:37 ` dje 2012-09-07 10:01 ` Yao Qi 2012-09-07 10:11 ` Eli Zaretskii 2012-09-07 10:21 ` Yao Qi 2012-09-07 11:27 ` Eli Zaretskii 2012-09-07 13:14 ` Yao Qi 2012-09-07 14:32 ` Eli Zaretskii 2012-09-07 16:46 ` Jan Kratochvil 2012-09-09 2:31 ` Yao Qi 2012-09-10 2:02 ` Yao Qi 2012-09-10 7:47 ` Jan Kratochvil 2012-09-10 19:43 ` Jan Kratochvil 2012-09-11 17:12 ` Tom Tromey 2012-09-11 17:19 ` Jan Kratochvil 2012-09-11 17:23 ` Tom Tromey 2012-09-12 0:51 ` Yao Qi 2012-09-12 13:19 ` Jan Kratochvil
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox