免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1509 | 回复: 1
打印 上一主题 下一主题

一个关于do_page_falut的疑惑 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-04-11 10:58 |只看该作者 |倒序浏览
对于用户态栈的扩展,最终是在do_page_falut中完成的:
   
  if (!(vma->vm_flags & VM_GROWSDOWN))
        goto bad_area;
    if (error_code & 4) {
        /*
         * Accessing the stack below %esp is always a bug.
         * The large cushion allows instructions like enter
         * and pusha to work.  ("enter $65535,$31" pushes
         * 32 pointers and then decrements %esp by 65535.)
         */

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (address + 65536 + 32 * sizeof(unsigned long) < regs->esp)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto bad_area;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;if (expand_stack(vma, address))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;goto bad_area;


     代码注释以及前景分析以及论坛上此前的帖子对address为什么要+ 65536 + 32 * sizeof(unsigned long)都做了解释,即pushall以及enter指令会去访问位于esp以下的地址。
   我的疑惑是: 根据intel文档,pushall指令操作过程为:
IF 64-bit Mode
THEN #UD
FI;
IF OperandSize = 32 (* PUSHAD instruction *)
THEN
Temp ← (ESP);
Push(EAX);
Push(ECX);
Push(EDX);
Push(EBX);
Push(Temp)

Push(EBP);
Push(ESI);
Push(EDI);
ELSE (* OperandSize = 16, PUSHA instruction *)
Temp ← (SP);
Push(AX);
Push(CX);
Push(DX);
Push(BX);
Push(Temp);
Push(BP);
Push(SI);
Push(DI);
FI


push的操作:
ELSE IF StackAddrSize = 32
THEN
IF OperandSize = 32
THEN
ESP ← (ESP &#8722; 4);
IF (SRC is FS or GS)
THEN
TEMP = ZeroExtend32(SRC);
ELSE IF (SRC is IMMEDIATE)
TEMP = SignExtend32(SRC); FI;
ELSE
TEMP = SRC;
FI;
SS:ESP ← TEMP; (* Push doubleword *)
ELSE (* OperandSize = 16*)
ESP ← (ESP &#8722; 2);
SS:ESP ← SRC; (* Push word *)
FI



从其中可以看出,pushall的操作相当于多个push;而push指令是先执行esp的减操作,然后才存实际的数据。而触发pagefault的,应该是后面的存数据的动作,这是esp已经减小。

现在假如执行一条pushall指令时发生了page fault, 这条pushall指令的地址address被保存在了CR2中,esp被硬件自动保存在了内核栈,这时的esp应该是减小后的esp。进入do_page_fault后,address的值肯定是大于硬件保存的esp值。也就是说,上面的if判断中+32似乎是多余的啊。

如果我的理解有问题,那应该就在于上面红色的那句,也许硬件并不是这么实现的,比如可能整个push指令是一个原子操作,当后面存数据时发生pagefault时esp自动恢复到之前的值了。

论坛徽章:
0
2 [报告]
发表于 2009-04-11 11:44 |只看该作者
只有 pusha / pushd 的形式,没有 pushall 的形式

若 pusha / pushd  发生 #PF 异常,CR2 的地址应该是第 1 个 push 动作的 esp 值

被保存的 esp 不是减少 esp 后的值,是 push 时的值,processor 会临时保存 esp: temp_esp = esp;



如:
    push esp
----------------------------
processor 执行的是:
    old_esp = esp;          /* 原来的 esp 值 */
       push old_esp;           /* push 原来的 esp 值 */



:wink:

[ 本帖最后由 mik 于 2009-4-11 11:53 编辑 ]
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP