longjmp如何运作?

Asl*_*986 8 c linux security assembly

我需要了解longjmp函数如何工作; 我知道它的作用,但我需要知道它是如何做到的.

我试图在gdb中disas代码,但我无法理解一些步骤.代码是:

0xb7ead420 <siglongjmp+0>:      push   %ebp
0xb7ead421 <siglongjmp+1>:      mov    %esp,%ebp
0xb7ead423 <siglongjmp+3>:      sub    $0x18,%esp
0xb7ead426 <siglongjmp+6>:      mov    %ebx,-0xc(%ebp)
0xb7ead429 <siglongjmp+9>:      call   0xb7e9828f <_Unwind_Find_FDE@plt+119>
0xb7ead42e <siglongjmp+14>:     add    $0x12bbc6,%ebx
0xb7ead434 <siglongjmp+20>:     mov    %esi,-0x8(%ebp)
0xb7ead437 <siglongjmp+23>:     mov    0xc(%ebp),%esi
0xb7ead43a <siglongjmp+26>:     mov    %edi,-0x4(%ebp)
0xb7ead43d <siglongjmp+29>:     mov    0x8(%ebp),%edi
0xb7ead440 <siglongjmp+32>:     mov    %esi,0x4(%esp)
0xb7ead444 <siglongjmp+36>:     mov    %edi,(%esp)
0xb7ead447 <siglongjmp+39>:     call   0xb7ead4d0
0xb7ead44c <siglongjmp+44>:     mov    0x18(%edi),%eax
0xb7ead44f <siglongjmp+47>:     test   %eax,%eax
0xb7ead451 <siglongjmp+49>:     jne    0xb7ead470 <siglongjmp+80>
0xb7ead453 <siglongjmp+51>:     test   %esi,%esi
0xb7ead455 <siglongjmp+53>:     mov    $0x1,%eax
0xb7ead45a <siglongjmp+58>:     cmove  %eax,%esi
0xb7ead45d <siglongjmp+61>:     mov    %esi,0x4(%esp)
0xb7ead461 <siglongjmp+65>:     mov    %edi,(%esp)
0xb7ead464 <siglongjmp+68>:     call   0xb7ead490
0xb7ead469 <siglongjmp+73>:     lea    0x0(%esi,%eiz,1),%esi
0xb7ead470 <siglongjmp+80>:     lea    0x1c(%edi),%eax
0xb7ead473 <siglongjmp+83>:     movl   $0x0,0x8(%esp)
0xb7ead47b <siglongjmp+91>:     mov    %eax,0x4(%esp)
0xb7ead47f <siglongjmp+95>:     movl   $0x2,(%esp)
0xb7ead486 <siglongjmp+102>:    call   0xb7ead890 <sigprocmask>
0xb7ead48b <siglongjmp+107>:    jmp    0xb7ead453 <siglongjmp+51>
Run Code Online (Sandbox Code Playgroud)

有人可以简单地向我解释代码,或者指出我在哪里可以找到系统中的源代码?

wal*_*lyk 5

大多数情况下,它会恢复寄存器和堆栈,就像它们在相应时一样setjmp().需要一些额外的清理(修复信号处理和展开挂起的堆栈处理程序),以及返回一个不同的值作为setjmp的表观返回值,但恢复状态是操作的本质.

为了使它工作,堆栈不能低于调用setjmp的点.Longjmp是一种野蛮的方式,只是将调用堆栈(或函数调用嵌套序列)中调用的所有内容都忘记在同一级别,主要是通过简单地将堆栈指针设置为调用setjmp时的相同帧.

为了使它干净利落,longjmp()调用所有出口处理程序以获取中间函数,这样它们就可以删除变量,以及函数返回时通常进行的其他清理操作.将堆栈重置为深度较低的点将释放所有auto变量,但如果其中一个变量为a FILE *,则需要关闭文件并释放i/o缓冲区.

  • C`longjmp`函数不调用"中间函数的退出处理程序",这在C语言中不存在.这个答案错误地暗示了所述"中间函数"中的已分配资源将被解除分配,这在C中是完全不可能的. (4认同)
  • 展开堆栈帧不是“longjmp”标准行为的一部分。这是一个有害的扩展,它使本应为“O(1)”的操作突然变成“O(n)”。它需要展开堆栈帧的唯一方法是平台是否将其调用堆栈实现为一个复杂的结构,而不是简单地递增和递减堆栈指针。 (3认同)
  • 如果他们跳过任何析构函数等,他们就会有不明确的行为. (2认同)
  • 此外,我认为调用析构函数可能会限制`longjmp()`的有用性,例如,如果你将它用作穷人的setcontext()的构建块(如果这是可行的话,我没有仔细考虑过)它). (2认同)

R..*_*R.. 5

以下是longjmp标准i386 ABI中的i386代码,没有任何与C++交互的疯狂扩展,异常,清理函数,信号掩码等:

    mov 4(%esp),%edx
    mov 8(%esp),%eax
    test %eax,%eax
    jnz 1f
    inc %eax
1:
    mov (%edx),%ebx
    mov 4(%edx),%esi
    mov 8(%edx),%edi
    mov 12(%edx),%ebp
    mov 16(%edx),%ecx
    mov %ecx,%esp
    mov 20(%edx),%ecx
    jmp *%ecx
Run Code Online (Sandbox Code Playgroud)

  • 这不是gcc的代码,而是glibc的代码.并且在C中没有"清理"."longjmp"函数被指定为纯粹的跳转.如果你跳过需要清理的任何东西,那么最好你的程序有资源泄漏,最坏的情况是调用未定义的行为.这是C语言.如果你不喜欢它,不要使用`longjmp`.不要坚持改变`longjmp`以满足您基于另一种语言的要求. (6认同)

Bjo*_*rnD 0

您向 setjmp() 传递一个缓冲区参数。然后它将当前的寄存器信息等存储到该缓冲区中。然后调用 longjmp() 从缓冲区中恢复这些值。此外,沃利克所说的。