c - 在C中获取帧指针

标签 c assembly x86

我试图在我的 C 程序中获取 FP,我尝试了两种不同的方法,但它们都与我在运行 GDB 时得到的不同。

我尝试的第一种方法是用 C 为 Assembly 函数创建一个协议(protocol)函数:

int* getEbp();

我的代码是这样的:

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

我的汇编代码

getEbp:
    movl %ebp, %eax
    ret

我尝试让原型(prototype)函数只返回一个 int,但这也与我的 GDB 输出不匹配。我们正在使用 x86 程序集。

编辑:拼写错误,我的 getEsp 函数看起来和另一个函数完全一样:

getEsp:
    movl %esp, %eax
    ret

最佳答案

  1. 要读取寄存器,最好使用GCC extended inline assembly syntax。 .
  2. 你的 getEbp()如果您将它编译在一个单独的汇编程序文件中,它看起来应该可以工作。
  3. 你的 getEsp()显然不正确,因为它没有考虑调用者推送的返回地址。

这是获取 ebp 的代码片段通过扩展的内联 asm 并通过跟踪帧指针来展开堆栈:

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;
}

我将在下面展示读取寄存器的两个有效实现。纯汇编函数是 get_ebp()get_esp()getbp.S .另一个作为内联函数实现的集合是 get_esp_inline()get_ebp_inline()test-getbp.c 的顶部.

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

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;
}

我们现在可以编写一个 GDB 脚本来转储 ebpesp同时利用 getbp test-getbp.c 中定义的静态探针以上。

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

验证函数返回与 GDB 相同的数据:

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

拆卸test-getbp main()产生:

   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 

nop<main+54>是静态探头。查看代码围绕两个printf调用寄存器的读取方式。

顺便说一句,你代码中的这个循环对我来说似乎很奇怪:

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

你不是说

while (esp < ebp)
    esp +=4 

?

关于c - 在C中获取帧指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13437158/

相关文章:

c++ - 为什么 icc 会为一个简单的 main 生成奇怪的程序集?

c++ - RSA_private_decrypt错误

c - 这段代码是否尽可能高效?

c - 将参数传递给生成的进程在 C 中不起作用

程序集引导加载程序未跳转到内核

windows - 我可以从 Windows 中的用户模式程序读取 CPU 性能计数器吗?

c - 在 C 的另一个模块中声明 extern 数组

c - 模拟中断而不激活中断

debugging - 显示 "No function contains program counter for selected frame"时如何强制 GDB 反汇编代码?

c - 浮点乘法执行速度较慢,具体取决于 C 中的操作数