I'm working on a setjmp
/longjmp
custom implementation for x86-64 systems which saves the whole context of the CPU (namely, all xmm, fpu stack, etc; not only callee-save registers). This is written directly in assembly.
The code works fine in minimal examples (when calling it directly from an assembly source). The problem arises when using it with C code, due to the way parameters are passed to the homebrew setjmp
/longjmp
functions. In fact, SysV ABI for x64_64 systems dictates that arguments should be passed via registers (if they are at most 6). The signature of my functions are:
long long set_jmp(exec_context_t *env);
__attribute__ ((__noreturn__)) void long_jmp(exec_context_t *env, long long val);
Of course, this cannot work as is. In fact, when I enter set_jmp
, rdi
and rsi
have already been clobbered to keep a pointer to env
and val
. The same applies to long_jmp
with respect to rdi
.
Is there any way to force GCC, e.g. by relying on some attribute, to force argument passing via stack? This would be much more elegant than wrapping set_jmp
and long_jmp
with some define which manually pushes the clobbered registers on stack, so as to retrieve them later.
You can avoid overwriting the registers by calling the function using inline assembly.
#include <stdio.h>
static void foo(void)
{
int i;
asm volatile ("mov 16(%%rbp), %0" : "=g" (i));
printf("%d\n", i);
}
#define foo(x) ({ int _i = (x); \
asm ("push %0\ncall %P1\nadd $8, %%rsp\n" : : "g"(_i), "i"(foo)); })
int main(int argc, char *argv[])
{
foo(argc-1);
return 0;
}
Here, an integer is pushed on the stack, and the function foo is called. foo makes the value available in its local variable i. After returning, the stack pointer is adjusted back to its original value.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With