dajee
dajee

Reputation: 964

Obtaining frame pointer in C

I'm trying to get the FP in my C program, I tried two different ways, but they both differ from what I get when I run GDB.

The first way I tried, I made a protocol function in C for the Assembly function:

int* getEbp();

and my code looks like this:

int* ebp = getEbp(); 
printf("ebp: %08x\n", ebp); // value i get here is 0xbfe2db58

while( esp <= ebp )       
    esp -= 4;

printf( "ebp: %08x, esp" ); //value i get here is 0xbfe2daec

My assembly code

getEbp:
    movl %ebp, %eax
    ret

I tried making the prototype function to just return an int, but that also doesn't match up with my GDB output. We are using x86 assembly.

EDIT: typos, and my getEsp function looks exactly like the other one:

getEsp:
    movl %esp, %eax
    ret

Upvotes: 0

Views: 6795

Answers (2)

scottt
scottt

Reputation: 7238

  1. For reading a register, it's indeed best to use GCC extended inline assembly syntax.
  2. Your getEbp() looks like it should work if you compiled it in a separate assembler file.
  3. Your getEsp() is obviously incorrect since it doesn't take the return address pushed by the caller into account.

Here's a code snippet that gets ebp through extended inline asm and does stack unwinding by chasing the frame pointer:

struct stack_frame {
        struct stack_frame *prev;
        void *return_addr;
} __attribute__((packed));
typedef struct stack_frame stack_frame;

void backtrace_from_fp(void **buf, int size)
{
        int i;
        stack_frame *fp;

        __asm__("movl %%ebp, %[fp]" :  /* output */ [fp] "=r" (fp));

        for(i = 0; i < size && fp != NULL; fp = fp->prev, i++)
                buf[i] = fp->return_addr;
}

I'll show two working implementations of reading the registers below. The pure asm functions are get_ebp() and get_esp() in getbp.S. The other set implemented as inline functions are get_esp_inline() and get_ebp_inline() at the top of test-getbp.c.

In getbp.S

.section .text
/* obviously incurring the cost of a function call
   to read a register is inefficient */
.global get_ebp
get_ebp:
movl %ebp, %eax
ret

.global get_esp
get_esp:
/* 4: return address pushed by caller */
lea 4(%esp), %eax
ret

In test-getbp.c

#include <stdio.h>
#include <stdint.h>

/* see http://sourceware.org/systemtap/wiki/UserSpaceProbeImplementation */
#include <sys/sdt.h>

int32_t *get_ebp(void);
int32_t *get_esp(void);

__attribute__((always_inline)) uintptr_t *get_ebp_inline(void)
{
    uintptr_t *r;
    __asm__ volatile ("movl %%ebp, %[r]" : /* output */ [r] "=r" (r));
    return r;
}

__attribute__((always_inline)) uintptr_t *get_esp_inline(void)
{
    uintptr_t *r;
    __asm__ volatile ("movl %%esp, %[r]" : /* output */ [r] "=r" (r));
    return r;
}

int main(int argc, char **argv)
{
    uintptr_t *bp, *sp;

    /* allocate some random data on the stack just for fun */
    int a[10] = { 1, 3, 4, 9 };
    fprintf(fopen("/dev/null", "r"), "%d\n", a[3]);

    STAP_PROBE(getbp, getbp); /* a static probe is like a named breakpoint */
    bp = get_ebp();
    sp = get_esp();
    printf("asm: %p, %p\n", (void*)bp, (void*)sp);
    bp = get_ebp_inline();
    sp = get_esp_inline();
    printf("inline: %p, %p\n", (void*)bp, (void*)sp);
    return 0;
}

We can now write a GDB script to dump ebp and esp while making use of the getbp static probe defined in test-getbp.c above.

In test-getbp.gdb

file test-getbp

set breakpoint pending on
break -p getbp
commands
silent
printf "gdb: 0x%04x, 0x%04x\n", $ebp, $esp
continue
end

run
quit

To verify that the functions return the same data as GDB:

$ gdb -x test-getbp.gdb
< ... >
gdb: 0xffffc938, 0xffffc920
asm: 0xffffc938, 0xffffc920
inline: 0xffffc938, 0xffffc920
< ... >

Disassembling test-getbp main() produces:

   0x08048370 <+0>: push   %ebp
   0x08048371 <+1>: mov    %esp,%ebp
   0x08048373 <+3>: push   %ebx
   0x08048374 <+4>: and    $0xfffffff0,%esp
   0x08048377 <+7>: sub    $0x10,%esp
   0x0804837a <+10>:    movl   $0x8048584,0x4(%esp)
   0x08048382 <+18>:    movl   $0x8048586,(%esp)
   0x08048389 <+25>:    call   0x8048360 <fopen@plt>
   0x0804838e <+30>:    movl   $0x9,0x8(%esp)
   0x08048396 <+38>:    movl   $0x8048590,0x4(%esp)
   0x0804839e <+46>:    mov    %eax,(%esp)
   0x080483a1 <+49>:    call   0x8048350 <fprintf@plt>
   0x080483a6 <+54>:    nop
   0x080483a7 <+55>:    call   0x80484e4 <get_ebp>
   0x080483ac <+60>:    mov    %eax,%ebx
   0x080483ae <+62>:    call   0x80484e7 <get_esp>
   0x080483b3 <+67>:    mov    %ebx,0x4(%esp)
   0x080483b7 <+71>:    movl   $0x8048594,(%esp)
   0x080483be <+78>:    mov    %eax,0x8(%esp)
   0x080483c2 <+82>:    call   0x8048320 <printf@plt>
   0x080483c7 <+87>:    mov    %ebp,%eax
   0x080483c9 <+89>:    mov    %esp,%edx
   0x080483cb <+91>:    mov    %edx,0x8(%esp)
   0x080483cf <+95>:    mov    %eax,0x4(%esp)
   0x080483d3 <+99>:    movl   $0x80485a1,(%esp)
   0x080483da <+106>:   call   0x8048320 <printf@plt>
   0x080483df <+111>:   xor    %eax,%eax
   0x080483e1 <+113>:   mov    -0x4(%ebp),%ebx
   0x080483e4 <+116>:   leave  
   0x080483e5 <+117>:   ret 

The nop at <main+54> is the static probe. See the code around the two printf calls for how the registers are read.

BTW, this loop in your code seems strange to me:

while( esp <= ebp )       
    esp -= 4;

Don't you mean

while (esp < ebp)
    esp +=4 

?

Upvotes: 8

Strcpy
Strcpy

Reputation: 51

Because you're relying on implementation specific details, you need to provide more information about your target to get an accurate answer. You didn't specify architecture, compiler or operating system, which are really required to answer your question.

Making an educated guess based on the register names you referenced and the fact that you're using at&t syntax, I'm going to assume this is i386 and you're using gcc.

The simplest way to achieve this is using gcc variable attributes, you can try this, which is a gcc specific syntax to request a specific register.

#include <stdint.h>
#include <stdio.h>

int main(int argc, char **argv)
{
     const uintptr_t register framep asm("ebp");

     fprintf(stderr, "val: %#x\n", framep);

     return 0;
 }

An alternative is to use inline assembly to load the value, like this:

#include <stdint.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    uintptr_t framep;

    asm("movl %%ebp, %0" : "=r" (framep));

    fprintf(stderr, "val: %#x\n", framep);

    return 0;
}

This requests a 32bit register for a write-operation (= modifier), and loads it onto framep. The compiler takes care of extracting the values you declare.

In gdb, you can print the value and verify it matches the output.

(gdb) b main
Breakpoint 1 at 0x40117f: file ebp2.c, line 8.
(gdb) r
Starting program: /home/zero/a.exe
[New Thread 4664.0x1290]
[New Thread 4664.0x13c4]

Breakpoint 1, main (argc=1, argv=0x28ac50) at ebp2.c:8
8           asm("movl %%ebp, %0" : "=r" (framep));
(gdb) n
10          fprintf(stderr, "val: %#x\n", framep);
(gdb) p/x framep
$1 = 0x28ac28
(gdb) p/x $ebp
$2 = 0x28ac28
(gdb) c
Continuing.
val: 0x28ac28
[Inferior 1 (process 4664) exited normally]
(gdb) q

Remember that you cannot rely on this behaviour, even on x86 gcc can be configured to not use the frame pointer and keeps track of stack usage manually. This is generally called FPO by Microsoft, or omit-frame-pointer on other platforms. This trick frees up another register for general purpose use, but makes debugging a little more complicated.

You're correct that eax is generally used for return values where possible in x86 calling conventions, I have no idea why the comments on your post claim the stack is used.

Upvotes: 5

Related Questions