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

Wu Zhou
2005-11-07T04:49:10Z

Hi Daniel,

On Sun, 6 Nov 2005, Daniel Jacobowitz wrote:

> On Fri, Nov 04, 2005 at 11:18:00AM +0800, Wu Zhou wrote:
> > > 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!
>
> It's not quite as simple as Mark makes it out to be - in concept, sure,
> but not in execution. You have to _allocate_ the space on the stack;
> not just find empty space off the side of the stack and write the
> argument there.
>
> By the type that we're calling value_arg_coerce, we've already
> allocated some space on the stack, and saved the old stack pointer.
> But we'll allocate more space on the stack below, when we push
> arguments. And you have to be careful to keep the stack aligned
> properly through all of this. Read through the surrounding bits of
> infcall.c to see how this works for struct returns; that is the closest
> analogue we have today.
>
> Maybe if you pass the sp value by reference to value_arg_coerce and
> adjust it there...

You are quite right. Following your pointer, I made another patch and it
now passed with both g77 (3.4.4) and gfortran (4.0.1) on a x86 box. I
didn't consider the red zone in AMD64 architecture, so maybe it won't work
on it. I will try to find a chance to test it on other platform, such as
ppc64 or any other platform I can get access to.

Appended is the patch. Any comments and suggestion are highly
appreciated!

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 7 Nov 2005 04:36:39 -0000
*************** Unwinding of stack if a signal is receiv
*** 100,106 ****

static struct value *
value_arg_coerce (struct value *arg, struct type *param_type,
! int is_prototyped)
{
struct type *arg_type = check_typedef (value_type (arg));
struct type *type
--- 100,106 ----

static struct value *
value_arg_coerce (struct value *arg, struct type *param_type,
! int is_prototyped, CORE_ADDR *sp)
{
struct type *arg_type = check_typedef (value_type (arg));
struct type *type
*************** value_arg_coerce (struct value *arg, str
*** 109,118 ****
switch (TYPE_CODE (type))
{
case TYPE_CODE_REF:
if (TYPE_CODE (arg_type) != TYPE_CODE_REF
&& TYPE_CODE (arg_type) != TYPE_CODE_PTR)
{
! arg = value_addr (arg);
deprecated_set_value_type (arg, param_type);
return arg;
}
--- 109,119 ----
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)
{
! arg = value_addr_stack (arg, sp);
deprecated_set_value_type (arg, param_type);
return arg;
}
*************** 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 ----
*************** call_function_by_hand (struct value *fun
*** 528,534 ****
else
param_type = NULL;

! args[i] = value_arg_coerce (args[i], param_type, prototyped);

/* elz: this code is to handle the case in which the function
to be called has a pointer to function as parameter and the
--- 528,534 ----
else
param_type = NULL;

! args[i] = value_arg_coerce (args[i], param_type, prototyped, &sp);

/* elz: this code is to handle the case in which the function
to be called has a pointer to function as parameter and the
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 7 Nov 2005 04:36:47 -0000
*************** value_addr (struct value *arg1)
*** 886,891 ****
--- 886,963 ----
return arg2;
}

+ /* Mostly the same as value_addr. But when the argument passed is a constant
+ and the value is not a lval, we allocate a block of memory for that value
+ on the stack and return the value which contains that address. This is
+ only used for value_arg_coerce at this time. */
+
+ struct value *
+ value_addr_stack (struct value *arg1, CORE_ADDR *sp)
+ {
+ struct value *arg2;
+
+ struct type *type = check_typedef (value_type (arg1));
+ if (TYPE_CODE (type) == TYPE_CODE_REF)
+ {
+ /* Copy the value, but change the type from (T&) to (T*).
+ We keep the same location information, which is efficient,
+ and allows &(&X) to get the location containing the reference. */
+ arg2 = value_copy (arg1);
+ deprecated_set_value_type (arg2, lookup_pointer_type (TYPE_TARGET_TYPE (type)));
+ return arg2;
+ }
+ if (TYPE_CODE (type) == TYPE_CODE_FUNC)
+ return value_coerce_function (arg1);
+
+ if (TYPE_CODE (type) == TYPE_CODE_INT && VALUE_LVAL (arg1) == not_lval)
+ {
+ int len = TYPE_LENGTH (type);
+ CORE_ADDR addr;
+
+ if (gdbarch_frame_align_p (current_gdbarch))
+ *sp = gdbarch_frame_align (current_gdbarch, *sp);
+ if (INNER_THAN (1, 2))
+ {
+ /* stack grows downward */
+ if (gdbarch_frame_align_p (current_gdbarch))
+ *sp = gdbarch_frame_align (current_gdbarch, *sp - len);
+ else
+ *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;
+ if (gdbarch_frame_align_p (current_gdbarch))
+ *sp = gdbarch_frame_align (current_gdbarch, *sp + len);
+ else
+ *sp += 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)
+ + value_offset (arg1)
+ + value_embedded_offset (arg1)));
+
+ /* This may be a pointer to a base subobject; so remember the
+ full derived object's type ... */
+ arg2 = value_change_enclosing_type (arg2, lookup_pointer_type (value_enclosing_type (arg1)));
+ /* ... and also the relative position of the subobject in the full object */
+ set_value_pointed_to_offset (arg2, value_embedded_offset (arg1));
+ return arg2;
+ }
+
/* Given a value of a pointer type, apply the C unary * operator to it. */

struct value *

Regards
- Wu Zhou