x86 frame pointer and __builtin_setjmp/__builtin_longjmp

Hi,

I’m trying to understand how __builtin_setjmp/longjmp are supposed to interact with the frame pointer on x86_64. In particular, what is the expected behavior when the compiler chooses not to use rsp or rbp to address local variables?

When built with Clang, the following program will segfault, but it is fine when built with GCC. The target is x86_64 linux.

int main(int argc, char *argv[]) {

void *buf[20];

attribute((aligned(64))) char q; // realign the stack

char *p = __builtin_alloca(argc); // dynamic alloca

if (__builtin_setjmp(buf)) {

*p = ‘p’;

q = ‘q’;

return 0;

}

asm(“movq $0, %rbx”);

__builtin_longjmp(buf, 1);

}

LLVM is choosing to use rbx as a base pointer to access p and q, but during builtin_setjmp, rbx is not saved; when the longjmp is executed rbx may have a garbage value. GCC on the other hand, is using rbp, which is saved in the jump buffer. Is this a bug in LLVM, or am I using __builtin_setjmp/longjmp incorrectly?

Note: I’m explicitly clobbering rbx, but the compiler can clobber it on its own if __builtin_longjmp is called from another function.

Ben

Hi,

I’m trying to understand how __builtin_setjmp/longjmp are supposed to
interact with the frame pointer on x86_64. In particular, what is
the expected behavior when the compiler chooses not to use rsp or
rbp to address local variables?

When built with Clang, the following program will segfault, but it is
fine when built with GCC. The target is x86_64 linux.

int main(int argc, char *argv[]) {

void *buf[20];

__attribute__((__aligned__(64))) char q; // realign the stack

char *p = __builtin_alloca(argc); // dynamic alloca

if (__builtin_setjmp(buf)) {

*p = 'p';

q = 'q';

return 0;

}

asm("movq $0, %rbx");

__builtin_longjmp(buf, 1);

}

LLVM is choosing to use rbx as a base pointer to access p and q, but
during builtin_setjmp, rbx is not saved; when the longjmp is
executed rbx may have a garbage value. GCC on the other hand, is
using rbp, which is saved in the jump buffer. Is this a bug in LLVM,
or am I using __builtin_setjmp/longjmp incorrectly?

IMHO, this is an LLVM bug. The SjLj lowing code should save and restore the base pointer (unless stack realignment is disabled, etc.) whenever the base pointer might be in use.

This also highlights the odd design of this feature, which splits the implementation between Clang and the target lowering code. Specifically, I mean that Clang generates intrinsics to save the frame pointer, etc. into the buffer, and this is part of the protocol that the lowering code needs to understand. Can anyone explain the rationale for this split-responsibility design?

-Hal