关于非本地跳转
csapp p_795
如下所述的问题经过讨论并未被解决……
以下示例中未以ARM为例,因为我查到的ARM的ABI规范中,寄存器均由callee保存,或者callee与caller均不保存,因此很容易可以解决下列问题。以下以i386为例.
关于非本地跳转存在的疑问(以setjmp、longjmp为例)
函数调用之间需遵循ABI规范
有些寄存器是由caller保存(以备被callee使用而修改原来的值),当从callee返回后恢复这些寄存器;
有些寄存器是由callee保存(以备被callee使用而修改原来的值),当即将从callee返回时恢复这些寄存器;
某些寄存器可以随便使用,即不需要caller保存恢复,也不需要callee保存恢复。
问题:
A函数调用setjmp函数而后返回到A函数继续执行,由于一些临时变量的存在因此A函数的栈空间中会继续填入新值,而后A函数调用了B函数,B函数调用了longjmp函数,此时会恢复在调用setjmp时保存的context,再次从setjmp返回,继而在A函数中继续执行。
A函数在调用setjmp是会将caller save registers保存在自己的栈空间,而后传入实参到栈上(以x86为例,ARM通过寄存器传递),当从setjmp函数返回后A函数将从栈上恢复caller save registers,继续执行;无论是第一次从setjmp返回,还是由于在B函数调用了longjmp函数进而从setjmp返回,A函数都应从栈上恢复caller save registers.
图示如下:
A函数调用setjmp之前的栈:
|-------------|
| %ecx |
|-------------|
| %edx |
|-------------|
A函数传实参(由于是数组,演变成指针)
|-------------|
| %ecx |
|-------------|
| %edx |
|-------------|
| jmp_buf地址 |
|-------------|
| 下条指令地址| --------->弹出caller save registers
|-------------|
首次从setjmp返回后,A函数继续运行相应的指令,恢复caller save registers;
由于已经恢复caller save registers已经恢复,因此原保存%ecx、%edx的栈空间可以被其它临时量继续占用,因此该部分栈空间内容已经不再是%ecx、%edx;
第二次从setjmp返回后,A函数仍然会运行同样的指令,恢复所谓的"caller save registers",但是此时恢复出的值显然是不对的。
这个问题讨论有些久了,可是没有得到很好的解答,对于mips而言应该也会遇到的同样的问题,ABI规范上已经说明有些寄存器须由caller保存与恢复。
写了几个程序(x86与mips)均未出现图示中的类似情形,是不是自己哪里理解错了?!!