你如何让 gcc 的 __builtin_frame_address 与 -O2 一起工作?

Posted

技术标签:

【中文标题】你如何让 gcc 的 __builtin_frame_address 与 -O2 一起工作?【英文标题】:How do you get gcc's __builtin_frame_address to work with -O2? 【发布时间】:2013-02-06 17:19:43 【问题描述】:

如果我在没有 -O 参数的情况下编译附加的代码,它可以正常工作。但是,如果我使用 -O2 对其进行编译,则无法在回溯中打印出中间函数。最初,我认为一切都已优化,因此我在每个例程中调用了 printf 以排除这种情况。它仍然具有相同的输出。

预期结果:gcc -rdynamic -g test.c -o test -L/usr/local/lib -lexecinfo

./测试 深度=11 ./test: f0 (0x40d952) ./test: f1 (0x40da0e) ./test: f2 (0x40da1e) ./test: f3 (0x40da2e) ./test: f4 (0x40da3e) ./test: f5 (0x40da4e) ./test: f6 (0x40da5e) ./test: f7 (0x40da6e) ./test: 主 (0x40da89) ./test: _start (0x40080e)

意外结果:gcc -O2 -rdynamic -g test.c -o test -L/usr/local/lib -lexecinfo

./测试 深度=2 ./test: f0 (0x40794b)

#include <stdio.h>
#include <dlfcn.h>

#define CALLSTACK_MAXLEN 64

//
// We use this macro instead of a for loop in backtrace() because the 
// documentation says that you have to use a constant, not a variable.
//
#define BT(X)                                                          \
        case X:                                                         \
                if (!__builtin_frame_address(X))                       \
                        return X;                                       \
                                                                       \
                                                                        \
                trace[X].address = __builtin_return_address(X);         \
                break;                                                  \


struct call 
        const void *address;
        const char *function;
        const char *object;
;

struct call trace[CALLSTACK_MAXLEN];

int
backtrace(int depth) 
        int         i;
        Dl_info     dlinfo;

        for (i = 0; i < depth; i++) 
                switch (i) 
                        BT(  0);  
                        BT(  1);
                        BT(  2);
                        BT(  3);
                        BT(  4);
                        BT(  5);
                        BT(  6);
                        BT(  7);
                        BT(  8);
                        BT(  9);
                        BT( 10);
                        BT( 11);
                        BT( 12);
                        BT( 13);
                        BT( 14);
                        BT( 15);
                        BT( 16);
                        BT( 17);
                        BT( 18);
                        BT( 19);
                        default:  return i;
                

                if (dladdr(trace[i].address, &dlinfo) != 0) 
                        trace[i].function = dlinfo.dli_sname;
                        trace[i].object = dlinfo.dli_fname;
                
        

        return i;


void
f0() 
        int i;
        int depth;

        depth = backtrace(CALLSTACK_MAXLEN);
        printf("DEPTH=%d\n", depth);

        for (i = 0 ; trace[i].object != NULL; i++) 
                printf("%s: %s (%p)\n", trace[i].object, trace[i].function, trace[i].address);
        


void f1()  f0(); 
void f2()  f1(); 
void f3()  f2(); 
void f4()  f3(); 
void f5()  f4(); 
void f6()  f5(); 
void f7()  f6(); 

int main(int argc, char **argv) 
        f7();
        return 0;

【问题讨论】:

【参考方案1】:

原因是尾递归优化。即使关闭了内联,尾递归也会将调用更改为跳转,例如

f6:
.LFB29:
  .cfi_startproc
  xorl  %eax, %eax
  jmp f5

所以你必须:

    排除内联

    void __attribute__ ((noinline)) f1()  f0(); 
    void __attribute__ ((noinline)) f2()  f1(); 
    void __attribute__ ((noinline)) f3()  f2(); 
    void __attribute__ ((noinline)) f4()  f3(); 
    void __attribute__ ((noinline)) f5()  f4(); 
    void __attribute__ ((noinline)) f6()  f5(); 
    void __attribute__ ((noinline)) f7()  f6(); 
    

    使用 -fno-optimize-sibling-calls 编译并保留帧指针

    gcc -O2 -rdynamic -g -o bfa bfa.c -ldl -fno-optimize-sibling-calls -fno-omit-frame-pointer

输出是:

$ ./bfa 
DEPTH=10
./bfa: f0 (0x400f23)
./bfa: f1 (0x400f8b)
./bfa: f2 (0x400f9b)
./bfa: f3 (0x400fab)
./bfa: f4 (0x400fbb)
./bfa: f5 (0x400fcb)
./bfa: f6 (0x400fdb)
./bfa: f7 (0x400feb)
./bfa: main (0x400ffb)
/lib/libc.so.6: __libc_start_main (0x7fdfbae51c4d)

根据需要。

【讨论】:

感谢您的解释和使用新的 gcc 参数 (-f)。

以上是关于你如何让 gcc 的 __builtin_frame_address 与 -O2 一起工作?的主要内容,如果未能解决你的问题,请参考以下文章

gcc内嵌汇编简介

如何让 GCC 使用不同的标准库?

如何在 avr-gcc 中定义定时器

gcc - 如何在结构定义中结合 __attribute__((dllexport)) 和 [[nodiscard]]?

如何在 gcc 中静态初始化 __m128i 数组?

关于在linux下用gcc编译头文件