diff --git a/gdb/eval.c b/gdb/eval.c index c379209..5095666 100644 --- a/gdb/eval.c +++ b/gdb/eval.c @@ -39,6 +39,7 @@ #include "valprint.h" #include "gdb_obstack.h" #include "objfiles.h" +#include "common/vec.h" #include /* This is defined in valops.c */ @@ -60,6 +61,12 @@ static LONGEST init_array_element (struct value *, struct value *, struct expression *, int *, enum noside, LONGEST, LONGEST); +typedef struct value *value_ptr; +DEF_VEC_P (value_ptr); +typedef VEC (value_ptr) value_vec; + +static value_vec *expr_stack_mirror_vec = NULL; + struct value * evaluate_subexp (struct type *expect_type, struct expression *exp, int *pos, enum noside noside) @@ -138,8 +145,60 @@ struct value * evaluate_expression (struct expression *exp) { int pc = 0; + struct value *res; + struct cleanup *cleanups; + + expr_stack_mirror_vec = VEC_alloc (value_ptr, 5); + cleanups = make_cleanup (VEC_cleanup (value_ptr), &expr_stack_mirror_vec); + + res = evaluate_subexp (NULL_TYPE, exp, &pc, EVAL_NORMAL); + do_cleanups (cleanups); + + return res; + +} + +/* Add value V to the expression stack. */ + +void +add_value_to_expression_stack (struct value *v) +{ + if (expr_stack_mirror_vec == NULL) + return; + + VEC_safe_push (value_ptr, expr_stack_mirror_vec, v); +} + +/* Return an address after skipping over the current values on the expression + stack. SP is the current stack frame pointer. Non-zero DOWNWARD indicates + that the stack grows downwards/backwards. */ - return evaluate_subexp (NULL_TYPE, exp, &pc, EVAL_NORMAL); +CORE_ADDR +skip_current_expression_stack (CORE_ADDR sp, int downward) +{ + CORE_ADDR addr = sp; + + if (!VEC_empty (value_ptr, expr_stack_mirror_vec)) + { + struct value *v = VEC_last (value_ptr, expr_stack_mirror_vec); + CORE_ADDR val_addr = value_address (v); + + if (downward) + { + gdb_assert (sp >= val_addr); + addr = val_addr; + } + else + { + struct type *type; + + gdb_assert (sp <= val_addr); + type = value_type (v); + addr = val_addr + TYPE_LENGTH (type); + } + } + + return addr; } /* Evaluate an expression, avoiding all memory references diff --git a/gdb/infcall.c b/gdb/infcall.c index e60d1d4..baf12ad 100644 --- a/gdb/infcall.c +++ b/gdb/infcall.c @@ -533,6 +533,13 @@ call_function_by_hand (struct value *function, int nargs, struct value **args) { CORE_ADDR old_sp = get_frame_sp (frame); + /* Skip over the stack mirrors that might have been generated during the + evaluation of the current expression. */ + if (gdbarch_inner_than (gdbarch, 1, 2)) + old_sp = skip_current_expression_stack (old_sp, 1); + else + old_sp = skip_current_expression_stack (old_sp, 0); + if (gdbarch_frame_align_p (gdbarch)) { sp = gdbarch_frame_align (gdbarch, old_sp); @@ -719,10 +726,16 @@ call_function_by_hand (struct value *function, int nargs, struct value **args) } /* Reserve space for the return structure to be written on the - stack, if necessary. Make certain that the value is correctly - aligned. */ + stack unless the return type is void. Make certain that the value is + correctly aligned. + + We reserve space on the stack even if the language ABI and the target + ABI do not require that the return value be passed as an hidden first + argument. This is because we want to store the return value as an + on-stack mirror of the actual value returned by this function. This + enables us to have chained function calls in expressions. */ - if (struct_return || hidden_first_param_p) + if (TYPE_CODE (values_type) != TYPE_CODE_VOID) { if (gdbarch_inner_than (gdbarch, 1, 2)) { @@ -1060,13 +1073,18 @@ When the function is done executing, GDB will silently stop."), At this stage, leave the RETBUF alone. */ restore_infcall_control_state (inf_status); - /* Figure out the value returned by the function. */ - retval = allocate_value (values_type); - if (hidden_first_param_p) - read_value_memory (retval, 0, 1, struct_addr, - value_contents_raw (retval), - TYPE_LENGTH (values_type)); + { + struct value *mirror; + + mirror = value_from_contents_and_address (values_type, NULL, + struct_addr); + add_value_to_expression_stack (mirror); + retval = allocate_value_mirrored_on_stack (mirror); + read_value_memory (retval, 0, 1, struct_addr, + value_contents_raw (retval), + TYPE_LENGTH (values_type)); + } else if (TYPE_CODE (target_values_type) != TYPE_CODE_VOID) { /* If the function returns void, don't bother fetching the @@ -1077,16 +1095,28 @@ When the function is done executing, GDB will silently stop."), case RETURN_VALUE_REGISTER_CONVENTION: case RETURN_VALUE_ABI_RETURNS_ADDRESS: case RETURN_VALUE_ABI_PRESERVES_ADDRESS: + retval = allocate_value (values_type); gdbarch_return_value (gdbarch, function, values_type, retbuf, value_contents_raw (retval), NULL); + setup_stack_mirror (retval, struct_addr); break; case RETURN_VALUE_STRUCT_CONVENTION: - read_value_memory (retval, 0, 1, struct_addr, - value_contents_raw (retval), - TYPE_LENGTH (values_type)); + { + struct value *mirror; + + mirror = value_from_contents_and_address (values_type, NULL, + struct_addr); + retval = allocate_value_mirrored_on_stack (mirror); + add_value_to_expression_stack (mirror); + read_value_memory (retval, 0, 1, struct_addr, + value_contents_raw (retval), + TYPE_LENGTH (values_type)); + } break; } } + else + retval = allocate_value (values_type); do_cleanups (retbuf_cleanup); diff --git a/gdb/testsuite/gdb.cp/chained-calls.cc b/gdb/testsuite/gdb.cp/chained-calls.cc new file mode 100644 index 0000000..1a30631 --- /dev/null +++ b/gdb/testsuite/gdb.cp/chained-calls.cc @@ -0,0 +1,113 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2014 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 . */ + +class S +{ +public: + S () { } + S (S &obj); + + S operator+ (const S &s); + + int a; +}; + +S::S (S &obj) +{ + a = obj.a; +} + +S +S::operator+ (const S &s) +{ + S res; + + res.a = a + s.a; + + return res; +} + +S +f (int i) +{ + S s; + + s.a = i; + + return s; +} + +int +g (const S &s) +{ + return s.a; +} + +class A +{ +public: + int a; +}; + +A +p () +{ + A a; + a.a = 12345678; + return a; +} + +A +q (const A &a) +{ + return a; +} + +class B +{ +public: + int b[1024]; +}; + +B +makeb () +{ + B b; + int i; + + for (i = 0; i < 1024; i++) + b.b[i] = i; + + return b; +} + +int +getb (const B &b, int i) +{ + return b.b[i]; +} + +int +main () +{ + int i = g(f(0)); + A a = q(p()); + + B b = makeb (); + + return i + getb(b, 0); /* Break here */ +} diff --git a/gdb/testsuite/gdb.cp/chained-calls.exp b/gdb/testsuite/gdb.cp/chained-calls.exp new file mode 100644 index 0000000..ac20c85 --- /dev/null +++ b/gdb/testsuite/gdb.cp/chained-calls.exp @@ -0,0 +1,37 @@ +# Copyright 2014 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 . + +# This file is part of the gdb testsuite + +if {[skip_cplus_tests]} { continue } + +standard_testfile .cc + +if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} { + return -1 +} + +if {![runto_main]} { + return -1 +} + +gdb_breakpoint [gdb_get_line_number "Break here"] +gdb_continue_to_breakpoint "Break here" + +gdb_test "p g(f(12345))" ".*12345.*" "g(f())" +gdb_test "p q(p())" ".*12345678.*" "q(p())" +gdb_test "p g(f(6700) + f(89))" ".*6789.*" "g(f() + f())" +gdb_test "p g(f(g(f(300) + f(40))) + f(5))" ".*345.*" "g(f(g(f() + f())) + f())" +gdb_test "p getb(makeb(), 789)" ".*789" "getb(makeb(), ...)" diff --git a/gdb/value.h b/gdb/value.h index 555a980..58fe7d5 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -823,6 +823,10 @@ extern struct value *evaluate_subexp (struct type *expect_type, extern struct value *evaluate_subexpression_type (struct expression *exp, int subexp); +extern void add_value_to_expression_stack (struct value *v); + +extern CORE_ADDR skip_current_expression_stack (CORE_ADDR sp, int downward); + extern void fetch_subexp_value (struct expression *exp, int *pc, struct value **valp, struct value **resultp, struct value **val_chain,