【发布时间】:2017-09-07 10:48:54
【问题描述】:
基本上我的问题是我对 setjmp 和 longjmp 的实现 不起作用。我之所以以这种形式而不是在(代码审查)中提问的原因是我是汇编新手,我没有什么背景并且仍在学习但仍然不确定代码(请阅读到最后)。
首先我用三个不同的编译器在两个平台上执行了代码 这就是为什么我确定我在使用汇编程序时做错了什么的原因。
平台:mac OS 10.12.5 x86_64,ubuntu linux x86 编译器:Apple LLVM clang 8.0.0 x86_x64 ,clang 3.9.1 x86_x64 ,gcc 6.3 x86
我已经在所有平台上以 32 位模式编译了代码,因此在 linux 和 mac 上生成的机器代码是 32 位的。
我将在这里发布的代码是在 Apple clang 下编译的,没有使用 -m32 标志进行优化以生成 32 位机器码
#include <cstdio>
typedef unsigned long jmp_buf[6];
int Setjmp(jmp_buf var){
__asm__(
" mov -4(%ebp), %eax # get pointer to jmp_buf, passed as argument on stack\n"
" mov %ebx, (%eax) # jmp_buf[0] = ebx\n"
" mov %esi, 4(%eax) # jmp_buf[1] = esi\n"
" mov %edi, 8(%eax) # jmp_buf[2] = edi\n"
" mov %ebp, 12(%eax) # jmp_buf[3] = ebp\n"
" lea 4(%esp), %ecx # get previous value of esp, before call\n"
" mov %ecx, 16(%eax) # jmp_buf[4] = esp before call\n"
" mov (%esp), %ecx # get saved caller eip from top of stack\n"
" mov %ecx, 20(%eax) #jmp_buf[5] = saved eip\n"
" xor %eax, %eax #eax = 0\n"
);
return 0;
}
void Longjmp(jmp_buf var,int m){
__asm__(" mov -4(%ebp),%edx # get pointer to jmp_buf, passed as argument 1 on stack\n"
" mov -8(%ebp),%eax #get int val in eax, passed as argument 2 on stack\n"
" test %eax,%eax # is int val == 0?\n"
" jnz 1f\n"
" inc %eax # if so, eax++\n"
"1:\n"
" mov (%edx),%ebx # ebx = jmp_buf[0]\n"
" mov 4(%edx),%esi # esi = jmp_buf[1]\n"
" mov 8(%edx),%edi #edi = jmp_buf[2]\n"
" mov 12(%edx),%ebp # ebp = jmp_buf[3]\n"
" mov 16(%edx),%ecx # ecx = jmp_buf[4]\n"
" mov %ecx,%esp # esp = ecx\n"
" mov 20(%edx),%ecx # ecx = jmp_buf[5]\n"
" jmp *%ecx # eip = ecx");
}
void fancy_func(jmp_buf env);
int main() {
jmp_buf env;
int ret = Setjmp(env);
if (ret == 0) {
puts("just returning from setjmp!");
fancy_func(env);
} else {
puts("now returning from longjmp and exiting!");
}
}
void fancy_func(jmp_buf env) {
puts("doing fancy stuff");
Longjmp(env, 1);
}
我正在关注本教程:http://vmresu.me/blog/2016/02/09/lets-understand-setjmp-slash-longjmp/
注意:我已经调试过源代码问题出在:
jmp *%ecx
但我认为问题在于 setjmp 以及我存储上下文的方式 特别是那一行:
lea 4(%esp), %ecx # get previous value of esp, before call\n"
这也是我没有得到它的代码部分。
我还知道我的编译器生成的用于调用和清理 setjmp 和 longjmp 堆栈的代码以及在我的案例中使用的调用约定 (CDECL)。
非常感谢您的帮助。
【问题讨论】:
-
C++ 不是 C 不是 C++!这可能是你的问题。你为什么要使用它们?只是不要。在 C++ 中,使用异常、
setjmp等是一些比较有问题的 C 遗留问题(使用 C++ 时),在 C 中应该谨慎使用它们。 -
哦,还有:阅读 How to Ask 并听从建议。
-
你为什么要用内联汇编编程?堆栈布局在内联汇编中基本上是不可预测的,你不能只假设编译器推送基指针或类似的东西。
-
我希望它们用于协程,这就是我标记 C 和 C++ 的原因
-
@jacky 那你为什么不在 libc 中使用
setjmp实现呢?