【问题标题】:how stack differs in 32 bit and 64 bit processors堆栈在 32 位和 64 位处理器中有何不同
【发布时间】:2015-05-30 12:54:24
【问题描述】:

我在 gdb 中为 32 位和 64 位英特尔处理器反汇编了以下代码。

void main() {
5      char *args[2];
6   
7      args[0] = "/bin/sh";
8      args[1] = NULL;
9      execve(args[0],args,NULL);
10     exit(0);
11  }

以下是生成的汇编代码。

对于 64 位

Dump of assembler code for function main:
   0x000000000040105e <+0>: push   %rbp
   0x000000000040105f <+1>: mov    %rsp,%rbp
   0x0000000000401062 <+4>: sub    $0x10,%rsp
   0x0000000000401066 <+8>: movq   $0x493564,-0x10(%rbp) ; <- this line
   0x000000000040106e <+16>:    movq   $0x0,-0x8(%rbp)
   0x0000000000401076 <+24>:    mov    -0x10(%rbp),%rax
   0x000000000040107a <+28>:    lea    -0x10(%rbp),%rcx
   0x000000000040107e <+32>:    mov    $0x0,%edx
   0x0000000000401083 <+37>:    mov    %rcx,%rsi
   0x0000000000401086 <+40>:    mov    %rax,%rdi
   0x0000000000401089 <+43>:    callq  0x433510 <execve>
   0x000000000040108e <+48>:    mov    $0x0,%edi
   0x0000000000401093 <+53>:    callq  0x407560 <exit>

对于 32 位

Dump of assembler code for function main:
0x8000130 <main>:       pushl  %ebp
0x8000131 <main+1>:     movl   %esp,%ebp
0x8000133 <main+3>:     subl   $0x8,%esp
0x8000136 <main+6>:     movl   $0x80027b8,0xfffffff8(%ebp) ; <- this line
0x800013d <main+13>:    movl   $0x0,0xfffffffc(%ebp)
0x8000144 <main+20>:    pushl  $0x0
0x8000146 <main+22>:    leal   0xfffffff8(%ebp),%eax
0x8000149 <main+25>:    pushl  %eax
0x800014a <main+26>:    movl   0xfffffff8(%ebp),%eax
0x800014d <main+29>:    pushl  %eax
0x800014e <main+30>:    call   0x80002bc <__execve>
0x8000153 <main+35>:    addl   $0xc,%esp
0x8000156 <main+38>:    movl   %ebp,%esp
0x8000158 <main+40>:    popl   %ebp
0x8000159 <main+41>:    ret
End of assembler dump.

在上面我标记的汇编代码是我没有理解的地方。 64 位系统声明从 rbp 偏移 16 位(我想学习的位、字节或字),而 32 位系统声明从 ebp 偏移 8 位。 谁能解释一下堆栈指针在 32 位和 64 位系统中的前进方式以及它是什么单位(位、字节或字)的区别。

【问题讨论】:

  • 64 位系统声明从 rbp 偏移 16 位(我想学习的位、字节或字),而 32 位系统声明从 ebp 偏移 8 位 - 我相信它是以字节为单位,而不是位。
  • 字节。偏移量不同,因为地址占用 8 个字节,而不是 64 位模式下的 4 个。

标签: c assembly gdb disassembly


【解决方案1】:
char * args[2];

是一个由两个指针组成的数组。在 32 位机器上,指针(地址,但不一定)是 32 位宽,而在 64 位机器上,指针(而不是地址)是 64 位宽。

这意味着,当前堆栈帧中的堆栈区域必须是2 * pointer_width,这使得8 用于32 位机器,16 用于64 位机器。

您可以在下面的代码中看到

movl   $0x80027b8, -0x8(%ebp)
movl   $0x0, -0x4(%ebp)

... 将 32 位双字移动到指针(一个字符串地址和一个 NULL)。

另一方面,64 位代码需要两个双字(8 个字节)来存储地址。

movq   $0x0, -0x8(%rbp)
movq   $0x493564, -0x10(%rbp)

【讨论】:

  • “指针(而不是地址)是 64 位宽”是什么意思?在LP64 模式下,指针 地址总是 64 位宽,所以你的回答让我很困惑。
  • @EmployedRussian 对不起,我的意思是别的。地址可能是 64 位宽,但前提是它们是虚拟的。据我所知,物理地址目前不超过 52 位。当然,在这种情况下,我们谈论的是虚拟地址,因此该声明不适用。
  • movl $0x0, -0xC(%ebp) 应该是 movl $0x0, -0x4(%ebp),因为 0xfffffffc 是十进制的 -4。有意义,因为在内存中 arg 应该在 arg++ 之前(arg[0] 在 arg[1] 之前)。
猜你喜欢
  • 2014-02-05
  • 2010-09-19
  • 2011-04-19
  • 2014-05-27
  • 2021-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-06
相关资源
最近更新 更多