Reputation: 964
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
Reputation: 7238
getEbp()
looks like it should work if you compiled it in a separate assembler file.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
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