0

I have the following C/C++ code, which uses __builtin_return_address:

#include <stdio.h>

#ifdef __clang__
# define optnone __attribute__((optnone))
#else
# define optnone __attribute__((optimize("O0")))
#endif

void *f() {
    return __builtin_extract_return_addr(__builtin_return_address(2));
}

optnone void nest1() {
    printf("%p\n", f());
}

optnone void nest2() {
    nest1();
}

optnone void nest3() {
    nest2();
}

optnone void nest4() {
    nest3();
}

optnone int main() {
    nest4();
}

GCC generates the following assembly and works fine (does not crash):

f:
        push    rbp
        mov     rbp, rsp
        mov     rax, QWORD PTR [rbp+0]
        pop     rbp
        mov     rax, QWORD PTR [rax]
        mov     rax, QWORD PTR [rax+8]
        ret

Clang compiles the following assembly, and crashes:

f:                                      # @f
        push    rbp
        mov     rbp, rsp
        mov     rax, qword ptr [rbp]
        mov     rax, qword ptr [rax]
        mov     rax, qword ptr [rax + 8]
        pop     rbp
        ret

What is the reason of the crash?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847

1 Answers1

6

__builtin_return_address(), as you can see from the assembly, attempts to walk the chain of frame pointers on the stack. But this only works if the callers above you have actually set up stack frames and pushed their frame pointers. You can see that this is the case for gcc (each of the other functions starts with push rbp / mov rbp, rsp) but not for clang. You could force clang to do so with -fno-omit-frame-pointer and then your code works again.

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
  • 1
    Presumably that's what the OP hoped `__attribute__((optnone))` would do, but it doesn't if the command-line optimization settings include `-O2` or something else that enables `-fomit-frame-pointer`. It does block inlining at least. And `optimize("O0")` does nothing at all for clang; it says "unknown attribute". https://godbolt.org/z/7q4vbP – Peter Cordes Jan 09 '21 at 07:22