【问题标题】:A modification to %esp cause SIGSEGV对 %esp 的修改导致 SIGSEGV
【发布时间】:2013-03-27 10:41:49
【问题描述】:

有时我在参加编程比赛时使用下面的代码来避免堆栈溢出。

int main()
{
  static const int _STACK_SIZE = MAXN*10;
  static int _STACK[_STACK_SIZE*2], _ESP;
  __asm__ __volatile__
  (
      "movl %%esp, %0\n" 
      "movl %1, %%esp\n":
      "=g"(_ESP):
      "g"(_STACK + _STACK_SIZE):
  );


  // Do Something..


  __asm__ __volatile__
  (  
      "movl %0, %%esp\n":
      :
      "g"(_ESP):
  );
}

据我所知,这个 asm 代码备份 %esp 并将堆栈移动到 _STACK[]


我的问题:为什么这段代码会在 x86-64 Linux 服务器上导致 SIGSEGV(它在我自己的 x86 Linux 上运行良好)?以及如何解决?

我猜,可能是因为%esp是一个64位指针??

我试图删除__asm__ __volatile__("movl %0, %%esp\n": : "g"(_ESP):);,它似乎运行良好?

【问题讨论】:

    标签: linux assembly g++ 64-bit x86-64


    【解决方案1】:

    这是因为在 x86-64 汇编中修改 32 位寄存器(例如 esp)会将相应 64 位寄存器的最高 32 位归零,在本例中为 rsp。在 x86-64 中,堆栈指针 rsp 是一个 64 位寄存器。

    x86-64 Linux 总是将堆栈指针放在虚拟地址空间的用户空间范围的顶部附近,例如0x7fffffffe6d0,因此它总是在虚拟地址空间的低 32 位之外。 RSP 的高半部分非零且 ESP != RSP。

    因此,通过修改esp,您可以使rsp 指向您的程序没有访问权限的地址,从而导致分段错误。在 x86-64 代码中,您通常根本不使用 esp,您应该在 x86-64 代码中将所有 esp 实例替换为 rsp

    【讨论】:

    • 谢谢!知道了!那么,将esp 改为rsp && int 改为long ?
    • “问题应该只出现在总内存小于 4 GiB 的计算机上”您的意思是“超过 4 GiB”..?
    • 关于按需分页虚拟内存操作系统的标准神话。您将“总内存”与虚拟内存地址空间的大小混为一谈。后者要大得多,机器上的 RAM 量无关紧要。
    • @abcdabcd987 在 Linux 中 long 应该是正确的类型,您可能还需要检查 stackoverflow.com/questions/384502/…
    • @abcdabcd987 - 更改为 rsp 仍然不够。 x86-64 ELF ABI 指定一个 16 字节对齐的堆栈。此外,gcc 不知道堆栈的更改,因此对rbp(帧指针)的任何使用都可能失败。
    猜你喜欢
    • 2013-11-27
    • 2017-11-09
    • 1970-01-01
    • 2010-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-06
    相关资源
    最近更新 更多