■
gccのビルトイン関数には関数のリターンアドレスやフレームポインタなどを取得することができる関数がある。この関数を使用しsetjmp/longjmpの実装は以下の様になる。たぶんバグはないはず?
jmp.c
#include <stdio.h> #include <stdlib.h> #define JMP_BUFFSIZE 6 typedef struct { unsigned long __jmp_buf[JMP_BUFFSIZE]; } jmp_buf[1]; jmp_buf jmp_buffer; int setjmp(jmp_buf env) { void *return_addr = __builtin_return_address(0); asm volatile("movl %%edx, 0(%%ecx)\n\t" /* return address */ "movl %%ebx, 4(%%ecx)\n\t" "movl %%esp, 8(%%ecx)\n\t" "movl %%ebp, 12(%%ecx)\n\t" "movl %%esi, 16(%%ecx)\n\t" "movl %%edi, 20(%%ecx)" :: "d" (return_addr), "c" (env)); return 0; } int longjmp(jmp_buf env, int val) { asm volatile("movl 0(%%edx), %%ecx\n\t" /* return address */ "movl 4(%%edx), %%ebx\n\t" "movl 8(%%edx), %%esp\n\t" "movl 12(%%edx), %%ebp\n\t" "movl 16(%%edx), %%esi\n\t" "movl 20(%%edx), %%edi\n\t" "cmpl $0, %%eax\n\t" /* if val = 0 */ "jne 1f\n\t" "movl $1, %%eax\n\t" /* then val = 1 */ "1: jmp *%%ecx" :: "a" (val), "d" (env)); /* NOTREACHED */ return 0; } void func2(void) { longjmp(jmp_buffer, 100); } void func1(void) { func2(); } int main(void) { int ret = 0; ret = setjmp(jmp_buffer); if (ret != 0) { printf("longjmp: %d\n", ret); exit(0); } func1(); printf("Error: cannot reach this code\n"); return ret; }
コンパイルと実行
$ gcc jmp.c $ ./a.out longjmp: 100