4

Given stack pointer value, is it possible to determine the value of the passed arguments to the function? Where are the arguments stored in the stack frame.

Lets say, executing gcc compiled ELF binary on x86 architecture on Linux platform:

int foo(int a, int b)
{
...
}

foo(a,b) is called from main() and I know the stack pointer(SP) value which is pointing to foo() now. How can I retrive the value of arguments a and b?

EDIT: If stack grows from smaller address to larger address, and arguments are passed right to left using cdecl, can I obtain args value like this:

b = *(SP + 1);
a = *(SP + 2);

EDIT: The following program prints the value of functions args a, b using above arch and specifications.

void foo(int a, int b)
{
        int i;
        register int stackptr asm("sp");
        int *sp = (int *)stackptr;
        printf("\n\ta=%d b=%d\n", a, b);
        for (i=0; i<16; i++) {
                printf("*(sp + %d) = %d\n", i, *(sp +i));
        }
}

int main()
{
        foo(3, 8);
        foo(9, 2);
        foo(1, 4);
        return 0;
}

The output of above code is:

        a=3 b=8
*(sp + 0) = 134514016
*(sp + 1) = 0
*(sp + 2) = 0
*(sp + 3) = 134513373
*(sp + 4) = 8239384
*(sp + 5) = 134513228
*(sp + 6) = 6
*(sp + 7) = -1076716032
*(sp + 8) = 134513456
*(sp + 9) = 0
*(sp + 10) = -1076715960
*(sp + 11) = 134513759
*(sp + 12) = 3  //value of arg a
*(sp + 13) = 8  //value of arg b
*(sp + 14) = 134513817
*(sp + 15) = 10612724

        a=9 b=2
*(sp + 0) = 134514016
*(sp + 1) = 0
*(sp + 2) = 0
*(sp + 3) = 134513373
*(sp + 4) = 8239384
*(sp + 5) = 134513228
*(sp + 6) = 6
*(sp + 7) = -1076716032
*(sp + 8) = 134513456
*(sp + 9) = 0
*(sp + 10) = -1076715960
*(sp + 11) = 134513779
*(sp + 12) = 9  //value of arg a
*(sp + 13) = 2  //value of arg b
*(sp + 14) = 134513817
*(sp + 15) = 10612724

        a=1 b=4
*(sp + 0) = 134514016
*(sp + 1) = 0
*(sp + 2) = 0
*(sp + 3) = 134513373
*(sp + 4) = 8239384
*(sp + 5) = 134513228
*(sp + 6) = 6
*(sp + 7) = -1076716032
*(sp + 8) = 134513456
*(sp + 9) = 0
*(sp + 10) = -1076715960
*(sp + 11) = 134513799
*(sp + 12) = 1  //value of arg a
*(sp + 13) = 4  //value of arg b 
*(sp + 14) = 134513817
*(sp + 15) = 10612724

Why function arguments are stored from offset 12 of SP? Also notice values at offset 0 to 10 are always same, and value at offset 11 increases by 20 on each invocation of function foo().

UPDATE: I found that gcc has in-build function to retrieve frame pointer address

void * __builtin_frame_address (unsigned int level)

When I print values at offsets starting from __builtin_frame_address(0) the function arguments start from offset 2. How can I confirm that this behavior is always consistent?

manav m-n
  • 11,136
  • 23
  • 74
  • 97
  • 3
    It depends on the underlying architecture and its calling conventions... – Jeff Mercado Dec 12 '12 at 06:53
  • 5
    You reference `a` and `b`? If you want to do it purely based on the SP, then you have to know all about the memory layout of your compiler on your platform. You've not specified which platform or compiler, so no-one can help you much. – Jonathan Leffler Dec 12 '12 at 06:53
  • 1
    You can't possibly know this generically. If the arguments are passed in registers, they might never land on the stack, and if they do, you don't know where on the stack they're going to land. – Lily Ballard Dec 12 '12 at 06:56
  • 2
    x86, as in 32-bit IA32, or x86/64? On which platform? Solaris, Linux, BSD (which one), Mac OS X, Windows, QNX, ...? Unix systems tend to have a documented ABI (Application Binary Interface) which specifies these details. I assume Windows has the equivalent. – Jonathan Leffler Dec 12 '12 at 06:58
  • 1
    Yay! Now those who know the answer have a chance of helping you. You could look up the [Linux Standards Base](http://www.linuxfoundation.org/collaborate/workgroups/lsb); it should cover some of this (LSB 3.1 has a document 'Linux Standards Base Core Specification for IA32', but the current version is 4.1; OTOH, it won't have changed all that much). – Jonathan Leffler Dec 12 '12 at 07:08

3 Answers3

2

You must know the calling convention to know what order the arguments are pushed onto the stack, or even if they are on the stack. Many pass the first few arguments in registers. Even on x86, you have fastcall, pascal, register, stdcall and cdecl, just to name a few.

EDIT: Don't forget that printf is also a function, and local variables also go on the stack. So, in your sample app, you have your parameters (since it's cdecl), then your locals, then your function saved state and return address, then parameters to printf (maybe, not sure if it's cdecl or fastcall), then printf's locals by the time anything actually makes it to the screen.

Donnie
  • 45,732
  • 10
  • 64
  • 86
  • In C, is argument list passed from left to right, or vice versa? – manav m-n Dec 12 '12 at 07:00
  • The order of the arguments depends on the compiler and the platform. It could be either; it could be neither (if the machine is register rich), depending on the rules. – Jonathan Leffler Dec 12 '12 at 07:02
  • I am using gcc compiler, x86 arch and linux OS. Isn't there any C99 standard on the order of argument passing? – manav m-n Dec 12 '12 at 07:06
  • 2
    No; the C standard doesn't say anything about order of evaluation of function arguments, or order of passing. The C standard doesn't say that they have to be passed on the stack. Some machines might use registers instead; some might use other mechanisms altogether (though I'm not sure there are many such systems in general use). The C standard leaves these decisions up to the implementation (and this level of detail isn't even 'implementation defined', so it does not have to be documented by the implementation) in order to allow the implementation maximum latitude to achieve speed, etc. – Jonathan Leffler Dec 12 '12 at 07:12
  • 1
    Assuming x86 and cdecl, they're right to left. However, if it's fastcall, given that you have two int args, they're in ECX and EDX. Unless it was compiled by a borland compiler, then they're in EAX and EDX. Unless they're not because some unusual compiler was used. Basically - it's complicated. – Donnie Dec 12 '12 at 07:38
2

There's no easy way and certainly there's no portable way (for the same source file this could even change between gcc 4.1 and gcc 4.2) but gdb sure can do it. Using gcc, you can probably find all you need analyzing the DWARF info.

gdb also uses prologue analysis to detect how local variables are assigned in the stack (among other things) but I'm not sure if something like a "calling analysis" exists in gdb's source. May be reading prologue-value.h in gdb's source could help you.

Nico Brailovsky
  • 318
  • 2
  • 7
0

The local variables are allocated on the stack, thus variables i, stackptr and sp are allocated on the call stack. So, if we print all the stack records we will find these variables, then the return pointer, then the saved frame pointer (if saved) and then the function parameters. Hence, in example above the args start at 12.

If you want to access immediately to the function call parameters you should start from the frame pointer address obtained using __builtin_frame_address(unsigned int level). Arguments are pushed on the stack before the saved frame pointer, thus you have to add an offset equal to the frame pointer address size if you start from the beginning of the saved frame pointer record on the stack. Hence, in example above, args start at offset 2.

manav m-n
  • 11,136
  • 23
  • 74
  • 97