Re: The root cause for SEGV in evaluating fortran function call, any solution or suggestion?

Wu Zhou
2005-11-04T03:52:09Z

I also added a testcase for this. It should be easy to test in the coding
process. I tested it with gfortran and mainline gdb, it report 5PASS +
1FAIL ('p res_(2)' reports "Attempt to take address of value not located
in memory"). The test with g77 and mainline gdb shows 4 PASS and 2 FAIL
(SEGV with 'p res_(2)' and 'p res_(b)'). And test with the g77 and the
latest patched gdb, it showed 5PASS + 1FAIL (error output with 'p res_(2)').

Do you think that it make sense for this goes into the testsuite tree?

Here is the patch:

Index: gdb.fortran/func-call.f
===================================================================
RCS file: gdb.fortran/func-call.f
diff -N gdb.fortran/func-call.f
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- gdb.fortran/func-call.f 4 Nov 2005 03:47:22 -0000
***************
*** 0 ****
--- 1,27 ----
+ c Copyright 2005 Free Software Foundation, Inc.
+
+ c This program is free software; you can redistribute it and/or modify
+ c it under the terms of the GNU General Public License as published by
+ c the Free Software Foundation; either version 2 of the License, or
+ c (at your option) any later version.
+ c
+ c This program is distributed in the hope that it will be useful,
+ c but WITHOUT ANY WARRANTY; without even the implied warranty of
+ c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ c GNU General Public License for more details.
+ c
+ c You should have received a copy of the GNU General Public License
+ c along with this program; if not, write to the Free Software
+ c Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ PROGRAM funcall
+ integer a, b
+ b = 2
+ a = res(b)
+ END PROGRAM
+
+ function res(m)
+ integer m
+ integer res
+ res = m * m
+ end function
Index: gdb.fortran/func-call.exp
===================================================================
RCS file: gdb.fortran/func-call.exp
diff -N gdb.fortran/func-call.exp
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- gdb.fortran/func-call.exp 4 Nov 2005 03:47:22 -0000
***************
*** 0 ****
--- 1,79 ----
+ # Copyright 2005 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 2 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, write to the Free Software
+ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ # This file was written by Wu Zhou. (woodzltc@cn.ibm.com)
+
+ if $tracelevel then {
+ strace $tracelevel
+ }
+
+ set testfile "func-call"
+ set srcfile ${testfile}.f
+ set binfile ${objdir}/${subdir}/${testfile}
+
+ if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug f77 quiet}] != "" } {
+ untested "Couldn't compile ${srcfile}"
+ return -1
+ }
+
+ gdb_exit
+ gdb_start
+ gdb_reinitialize_dir $srcdir/$subdir
+ gdb_load ${binfile}
+
+ # To test "print res_(2)"
+
+ if ![runto MAIN__] then {
+ perror "couldn't run to breakpoint MAIN__"
+ continue
+ }
+
+ gdb_breakpoint [gdb_get_line_number "a = res(b)"]
+ gdb_continue_to_breakpoint "initialize argument b"
+ gdb_test_multiple "print res_(2)" "print res_(2)" {
+ -re ".*\[0-9\]+ = 4.*$gdb_prompt $" {
+ pass "use constant 2 as the argument"
+ }
+ -re "Program received signal SIGSEGV, Segmentation fault.*$gdb_prompt $" {
+ fail "print res_(2) get SEGV"
+ }
+ -re ".*$gdb_prompt $" {
+ fail "other unexpected error"
+ }
+ }
+
+ # To test "print res_(b)"
+
+ if ![runto MAIN__] then {
+ perror "couldn't run to breakpoint MAIN__"
+ continue
+ }
+
+ gdb_breakpoint [gdb_get_line_number "a = res(b)"]
+ gdb_continue_to_breakpoint "initialize argument b"
+ gdb_test "print res_(b)" ".*\[0-9\]+ = 4.*" "use variable 'b' as the argument"
+
+ # To test "print res_(&b)"
+
+ if ![runto MAIN__] then {
+ perror "couldn't run to breakpoint MAIN__"
+ continue
+ }
+
+ gdb_breakpoint [gdb_get_line_number "a = res(b)"]
+ gdb_continue_to_breakpoint "initialize argument b"
+ gdb_test "print res_(&b)" ".*\[0-9\]+ = 4.*" "use the address of 'b' as the argument"
+

On Fri, 4 Nov 2005, Wu Zhou wrote:

> Hi Mark,
>
> On Thu, 3 Nov 2005, Mark Kettenis wrote:
>
> > > Date: Thu, 3 Nov 2005 11:14:51 +0800 (CST)
> > > From: Wu Zhou <woodzltc@cn.ibm.com>
> > >
> > > Maybe we can convert the argument to its pointer before we enter into
> > > call_function_by_hand (evaluate_subexp_standard: case OP_FUNCALL)?
> > > Normally what function you will use to allocate memory on the stack? I am
> > > not very familar with that kind of code. Thanks!
> >
> > Allocating memory on the stack is actually quite eazy. Just
> > substract/add the amount of space you need from/to the stack pointer,
> > and use the new/old stack pointer as the address for the memory.
> > Whether you should substract or add depends on whether the stack grows
> > downward or upward. Use gdbarch_inner_than(gdbarch, 1, 2) to check.
> > There's quite a bit of code in infcall.c that uses this trick.
> >
>
> Thanks. I did some tests following this way. But didn't get any success.
> So I had to post here again to see if anybody can help me out.
>
> My basic idea is to create a value which hold the address to the original
> argument. This is done in valur_addr for these argument which is not lval
> and whose type is TYPE_CODE_INT. Then I use the above method to get a new
> value which hold the address to the original address. Although it doesn't
> report SEGV or "can not access memory" message, it didn't ouptut the
> correct result I expected. I expect 4 (which is 2 * 2), but it return
> different number for me every time I run it.
>
> Following is the changed I made to valur_arg_coerce and value_addr. Could
> anyone help me pointed out what is the reason why it fail. Thanks a lot!
>
> Index: infcall.c
> ===================================================================
> RCS file: /cvs/src/src/gdb/infcall.c,v
> retrieving revision 1.73
> diff -c -3 -p -r1.73 infcall.c
> *** infcall.c 2 Sep 2005 19:02:44 -0000 1.73
> --- infcall.c 4 Nov 2005 03:11:35 -0000
> *************** value_arg_coerce (struct value *arg, str
> *** 109,114 ****
> --- 109,115 ----
> switch (TYPE_CODE (type))
> {
> case TYPE_CODE_REF:
> + case TYPE_CODE_PTR:
> if (TYPE_CODE (arg_type) != TYPE_CODE_REF
> && TYPE_CODE (arg_type) != TYPE_CODE_PTR)
> {
> *************** value_arg_coerce (struct value *arg, str
> *** 154,160 ****
> type = lookup_pointer_type (TYPE_TARGET_TYPE (type));
> break;
> case TYPE_CODE_UNDEF:
> - case TYPE_CODE_PTR:
> case TYPE_CODE_STRUCT:
> case TYPE_CODE_UNION:
> case TYPE_CODE_VOID:
> --- 155,160 ----
> Index: valops.c
> ===================================================================
> RCS file: /cvs/src/src/gdb/valops.c,v
> retrieving revision 1.161
> diff -c -3 -p -r1.161 valops.c
> *** valops.c 27 May 2005 04:39:32 -0000 1.161
> --- valops.c 4 Nov 2005 03:11:49 -0000
> *************** value_addr (struct value *arg1)
> *** 868,877 ****
> }
> if (TYPE_CODE (type) == TYPE_CODE_FUNC)
> return value_coerce_function (arg1);
> !
> if (VALUE_LVAL (arg1) != lval_memory)
> error (_("Attempt to take address of value not located in memory."));
>
> /* Get target memory address */
> arg2 = value_from_pointer (lookup_pointer_type (value_type (arg1)),
> (VALUE_ADDRESS (arg1)
> --- 868,905 ----
> }
> if (TYPE_CODE (type) == TYPE_CODE_FUNC)
> return value_coerce_function (arg1);
> ! /*
> if (VALUE_LVAL (arg1) != lval_memory)
> error (_("Attempt to take address of value not located in memory."));
> + */
> +
> + if (TYPE_CODE (type) == TYPE_CODE_INT && VALUE_LVAL (arg1) == not_lval)
> + {
> + int len = TYPE_LENGTH (type);
> + CORE_ADDR addr;
> + CORE_ADDR sp = read_sp ();
> + if (INNER_THAN (1, 2))
> + {
> + /* stack grows downward */
> + sp -= len;
> + /* ... so the address of the thing we push is the
> + stack pointer after we push it. */
> + addr = sp;
> + }
> + else
> + {
> + /* The stack grows up, so the address of the thing
> + we push is the stack pointer before we push it. */
> + addr = sp;
> + sp += len;
> + }
> +
> + addr = (CORE_ADDR) malloc (len);
> + write_memory (addr, value_contents_all (arg1), len);
> + arg2 = value_from_pointer (lookup_pointer_type (type), addr);
>
> + return arg2;
> + }
> /* Get target memory address */
> arg2 = value_from_pointer (lookup_pointer_type (value_type (arg1)),
> (VALUE_ADDRESS (arg1)
>

Regards
- Wu Zhou