tags:

views:

473

answers:

6

I know that if I am inside some fuction foo() which is called somewhere from bar() function, then this return address is pushed on stack.

    #include <stdio.h>

    void foo()
    {
            unsigned int x;
            printf("inside foo %x   \n", &x );
    }
    int main()
    {
            foo();
            printf("in main\n");
            return 0;
    }

In above code, I will get address of first pushed local variable on stack when foo function is active. How can I access the return address (main called foo) that is pushed somewhere before this variable on stack? Is that location fixed and can be accessed relative to first local variable? How can I modify it?

EDIT: My environment is Ubuntu 9.04 on x86 processor with gcc compiler.

+2  A: 

When you declare local variables, they are also on the stack - x, for instance.

If you then declare an int * xptr and initialize it to &x, it will point at x.

Nothing (much) stops you from decrementing that pointer to peek a little before, or incrementing it to look later. Somewhere around there is your return address.

Carl Smotricz
Thank you Carl, it will be very nice if I can get the exact offset from x (positive/negative).
vinit dhatrak
That will depend on your compiler. You should run and look at your program in a debugger, that will show you all the necessary details. If you don't have a debugger, print out a few words around x and experiment a bit! This will be a learning experience.
Carl Smotricz
+6  A: 

There is a gcc builtin for this: void * __builtin_return_address (unsigned int level)

See http://gcc.gnu.org/onlinedocs/gcc/Return-Address.html

On some architectures, you can find it on the stack relative to the first parameter. On ia32, for example, the parameters are pushed (in opposite order) and then a call is made that will push the return address. Remember that the stack almost always (and on ia32) grows downward. Although technically you need the ABI or calling conventions (sometimes called linkage conventions) for your language and hardware platform, in practice you can usually guess if you know how the procedure call machine op works.

The relationship between the first parameter to a function and the position of the return address on the stack is far more likely to be a reliably fixed value than the relationship between a local and the return address. However, you can certainly print out the address of a local, and of the first parameter, and you will often find the PC right in between.

$ expand < ra.c
#include <stdio.h>

int main(int ac, char **av) {
  printf("%p\n", __builtin_return_address(0));
  return 0;
}
$ cc -Wall ra.c; ./a.out
0xb7e09775
$
DigitalRoss
@DigitalRoss does not seem to be working, can you paste a simple example?
vinit dhatrak
oops, my mistake .. works perfectly !!!
vinit dhatrak
@DigitalRoss so you mean return address is in-between first parameter to function and local variables?
vinit dhatrak
In general, yes. The parameters get pushed, then the PC, then stack is further decremented for locals and any temporaries that the compiler needs. Since you can take the address of a parameter and of a local, you can track down your PC. Remember that negative subscripts work just fine in C. `f(int p) { printf("%p", ( }`
DigitalRoss
oh okk .. I guess this is how __builtin_return_address must be able to determine the return address. Also I guess, this negative subscription will work with first parameter due to C calling convention of pushing parameters from right to left. Correct me if I am wrong here.
vinit dhatrak
As DigitalRoss says, though, this is all very architecture-dependent. On ARM (which I mention since the question is tagged "linux gcc", not "linux gcc x86"), the link pointer is in r14, not (necessarily) on the stack at all. You return with `MOV r15,r14` or `BX R14`. So on gcc you should always use the builtin rather than trying to guess the calling convention.
Steve Jessop
@onebyone Nice input. didnt think of other arch. Thank you.
vinit dhatrak
Actually, the way __builtin_return_address determines the location of the PC is: it has access to the code generator, so it can pull it from a register or whatever. It may not be perfectly reliable, in theory, gcc could ship with a broken builtin because it isn't needed to compile conforming programs or any of gcc's extensions to the C language. I think I recall seeing it broken on gcc-alpha once.
DigitalRoss
@DigitalRoss @onebyone @ALL ... hey I just realized, this will not solve my problem. I wanted a address on stack which contains return address so that I can modify it in future. I dont want the actually address. :)
vinit dhatrak
I know, that's why I gave you various *other* suggestions...
DigitalRoss
+2  A: 

To know where the return address is you need to know what the calling convention is. This will typically be set by the compiler and depends on the platform, but you can force it in platform specific ways, for example using __declspec(stdcall) on windows. An optimizing compiler may also invent its own calling convention for functions that don't have external scope.

Barring the use of compiler built-ins to get the return address you would have to resort to inline assembler to get the value. Other techniques that appear to work in debug would be very vunerable to compiler optimizations messing them up.

Rob Walker
A: 

You can probe around the stack like so

// assuming a 32 bit machine here
void digInStack(void) {

    int i;
    long sneak[1];

    // feel free to adjust the search limits
    for( i = -32; i <= 32; ++i) {
        printf("offset %3d: data 0x%08X\n", i, sneak[i]);
    }
 }

You can get away with this because C is famous for not be very particular in how you index an array. Here, you declare a dummy array on the stack, and then peek around +/- relative to that.

As Rob Walker pointed out, you definitely need to know your compilers calling convention, to understand the data you are looking at. You might print out the address of a few functions, and look for values that are in a similar range, and intuit where the return address is, relative to the dummy array.

Word of caution - read all you want, but don't modify anything using that array, unless either (a) you are absolutely sure about what part of the stack it is you are modifying, or (b) just want to see an interesting/unpredictable crash mode.

JustJeff
A: 

— Built-in Function:

void * __builtin_return_address (unsigned int level)
  • This function returns the return address of the current function, or of one of its callers. The level argument is number of frames to scan up the call stack. A value of 0 yields the return address of the current function, a value of 1 yields the return address of the caller of the current function, and so forth. When inlining the expected behavior is that the function will return the address of the function that will be returned to. To work around this behavior use the noinline function attribute.

    The level argument must be a constant integer.

    On some machines it may be impossible to determine the return address of any function other than the current one; in such cases, or when the top of the stack has been reached, this function will return 0 or a random value. In addition, __builtin_frame_address may be used to determine if the top of the stack has been reached.

    Additional post-processing of the returned value may be needed, see __builtin_extract_return_address.

    This function should only be used with a nonzero argument for debugging purposes.

— Built-in Function:

void * __builtin_extract_return_address (void *addr)
  • The address as returned by __builtin_return_address may have to be fed through this function to get the actual encoded address. For example, on the 31-bit S/390 platform the highest bit has to be masked out, or on SPARC platforms an offset has to be added for the true next instruction to be executed.

    If no fixup is needed, this function simply passes through addr.

— Built-in Function:

void * __builtin_frob_return_address (void *addr)
  • This function does the reverse of __builtin_extract_return_address.

— Built-in Function:

void * __builtin_frame_address (unsigned int level)
  • This function is similar to __builtin_return_address, but it returns the address of the function frame rather than the return address of the function. Calling __builtin_frame_address with a value of 0 yields the frame address of the current function, a value of 1 yields the frame address of the caller of the current function, and so forth.

    The frame is the area on the stack which holds local variables and saved registers. The frame address is normally the address of the first word pushed on to the stack by the function. However, the exact definition depends upon the processor and the calling convention. If the processor has a dedicated frame pointer register, and the function has a frame, then __builtin_frame_address will return the value of the frame pointer register.

    On some machines it may be impossible to determine the frame address of any function other than the current one; in such cases, or when the top of the stack has been reached, this function will return 0 if the first frame pointer is properly initialized by the startup code.

    This function should only be used with a nonzero argument for debugging purposes.

Nadir SOUALEM
@Nadir, Please mention original link instead of copying material. http://gcc.gnu.org/onlinedocs/gcc/Return-Address.html
vinit dhatrak
A: 

Please also note that in general there is no guarantee made by the C language that your return address is on a stack, or indeed anywhere in RAM, at all.

There are processor architectures that store the return address in a register, resorting to RAM only when calls start to nest. There are other architectures where there is a separate stack for return addresses, that is not readable by the CPU. Both of these can still have C compilers implemented for them.

This is why you need to be clearer with your environment.

unwind
@unwind I have edited the question to specify my environment, I will keep in mind about this in future.
vinit dhatrak