首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >setjmp和longjmp实现

setjmp和longjmp实现
EN

Stack Overflow用户
提问于 2017-09-07 10:48:54
回答 1查看 2.6K关注 0票数 0

简单地说,我的问题是我对setjmp的实现不起作用。我之所以以这种形式问这个问题,而不是(代码评审),是因为我刚开始组装,我没有什么背景,而且还在学习,但仍然不确定代码(请阅读到结尾)。

首先,我用三个不同的编译器在两个平台上执行代码,这就是为什么我确信我对汇编程序做错了什么。

平台: mac 10.12.5 x86_64,ubuntu x86编译器: Apple clang 8.0.0 x86_x64,clang 3.9.1 x86_x64,gcc 6.3 x86

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

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

代码语言:javascript
复制
 #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/

注意:我已经调试了源代码,问题来自:

代码语言:javascript
复制
 jmp *%ecx

但我认为问题在于setjmp和我存储上下文的方式,特别是这一行:

代码语言:javascript
复制
 lea   4(%esp), %ecx     # get previous value of esp, before call\n"

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

我还知道编译器为调用和清理setjmp和longjmp堆栈而生成的代码,以及在我的示例中使用的调用约定(CDECL)。

非常感谢你的帮助。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-09-07 12:35:41

这方面有很多问题。正如fuz所说,您不应该像这样使用内联程序集。使用单独的asm文件,或者至少使用约束,最好不要依赖特定的堆栈布局。

无论如何,你弄错了偏移量,参数在上是正数,与ebp抵消了,而不是负的,首先是8(%ebp)。另外,您弄错了回信地址,它在4(%esp),因为(%esp)是保存的ebp。此外,由于函数序言保存了ebp,所以不是保存调用方的ebp,而是保存esp的副本。

固定版本(仍然只适用于使用堆栈args调用约定的32位模式):

请参阅整个函数关于戈德波特编译器浏览器的最终asm

代码语言:javascript
复制
// 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。

代码语言:javascript
复制
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"
 );
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46094444

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档