setjmp和longjmp实现 [英] setjmp and longjmp implementation

查看:135
本文介绍了setjmp和longjmp实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基本上,我的问题简而言之是我对setjmp和longjmp的实现 不起作用.我之所以不以这种形式询问(代码审阅)的原因是,我是汇编语言的新手,我几乎没有背景并且仍在学习,但仍不确定代码(请仔细阅读直到最后).

basically my problem in short is my implementation for setjmp and longjmp doesn't work. the reason why i'm asking in this form not in (code review) is that i'm new to assembly i've little background and still learning but still not sure about the code (please read until the end).

首先,我使用三个不同的编译器在两个平台上执行了代码 这就是为什么我确定我对汇编器做错了的原因.

first i'v executed the code on two platforms with three different compilers and that's the reason why i'm sure that i'm doing something wrong with the assembler.

平台: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

platforms: mac OS 10.12.5 x86_64 , ubuntu linux x86 compilers: Apple LLVM clang 8.0.0 x86_x64 ,clang 3.9.1 x86_x64 ,gcc 6.3 x86

我已经在所有平台上以32位模式编译了代码,因此在此示例中,在linux和mac上生成的机器代码是32位.

i've compiled the code in 32bit mode on all platforms so the generated machine code on linux and mac with this example is 32bit.

我将在此处发布的代码是在Apple clang下编译的,没有使用-m32标志生成32位机器代码的优化

the code i will post here is the was compiled under Apple clang with no optimization using -m32 flag to generate 32bit machine code

 #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/

注意:我已经调试了源于问题的源代码:

Note: I've debugged the source code the problem comes from the:

 jmp *%ecx

但是我认为问题出在setjmp和我存储上下文的方式上 特别是该行:

but i think the problem is with setjmp and the way i'm storing the context and specially that line:

 lea   4(%esp), %ecx     # get previous value of esp, before call\n"

这也是我没有得到的代码的一部分.

which is also the part of the code that i'm not getting it.

我还知道由我的编译器生成的用于调用和清理setjmp和longjmp堆栈的代码以及本例中使用的调用约定(CDECL).

i'm also aware of the code that was generated by my compiler for calling and cleaning the stack of setjmp and longjmp and the calling convention that was used (CDECL) in my case.

非常感谢您的帮助.

推荐答案

与此有关的许多问题.正如fuz所说,您不应使用这样的内联汇编.使用单独的asm文件,或至少使用约束,最好不要依赖特定的堆栈布局.

Many problems with this. As fuz said, you should not use inline assembly like this. Use separate asm file, or at least constraints and better not rely on a particular stack layout.

无论如何,偏移量都是错误的,参数与ebp的偏移量为正值而不是负数,第一个为8(%ebp).另外,您还得到了错误的返回地址,该地址位于4(%esp),因为(%esp)是保存的ebp.此外,由于函数序言已保存ebp,因此您将不会保存调用方的ebp,而是保存esp的副本.

Anyway you got the offsets wrong, arguments are at positive offset from ebp not negative, with first being 8(%ebp). Also you got the return address wrong, it's at 4(%esp) since (%esp) is the saved ebp. Furthermore, since the function prologue saved ebp you are not saving the caller's ebp but a copy of esp.

固定版本(仍然只能在32位模式下使用堆栈args调用约定):

The fixed version (still only works in 32-bit mode for calling conventions with stack args):

请参阅整个功能上Godbolt编译探险

// optimize("no-omit-frame-pointer") doesn't seem to work
// we still don't get a frame-point unless we force -O0 for the function with optimize(0)
__attribute__((noinline, noclone, returns_twice, optimize(0)))
int Setjmp(jmp_buf var){
    // relies on the compiler to make a stack-frame
    // because we're using inline asm inside a function instead of at global scope
     __asm__(
             "    mov 8(%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), %ecx\n"
             "    mov    %ecx, 12(%eax) # jmp_buf[3] = ebp\n"
             "    lea    8(%ebp), %ecx  # get previous value of esp, before call\n"
             "    mov    %ecx, 16(%eax) # jmp_buf[4] = esp before call\n"
             "    mov    4(%ebp), %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;
}

__attribute__((noinline, noclone, optimize(0)))
void Longjmp(jmp_buf var,int m){
    __asm__("    mov  8(%ebp),%edx # get pointer to jmp_buf, passed as argument 1 on stack\n"
            "    mov  12(%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");
}

如果在全局范围内使用asm语句,则无需使用__attribute__东西与编译器进行对抗,以确保其发出您期望的序言.您也可以跳过设置EBP,以便直接拥有呼叫者的EBP.

If you used an asm statement at global scope, you wouldn't need to fight against the compiler with __attribute__ stuff to make sure it emits the prologue you expect. You could also skip setting up EBP so you'd have the caller's EBP directly.

asm(".globl SetJmp \n"
    "SetJmp:       \n\t"
    "   push   %ebp  \n\t"
    "   mov    %esp, %ebp  \n\t"

    "...  your current implementation    \n\t"

    "   xor    %eax,%eax   \n\t"
    "   pop    %ebp        \n\t"
    "   ret                \n\t"
 );

这篇关于setjmp和longjmp实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆