堆栈在 32 位和 64 位处理器中有何不同
how stack differs in 32 bit and 64 bit processors
我在 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 [=11=]x10,%rsp
0x0000000000401066 <+8>: movq [=11=]x493564,-0x10(%rbp) ; <- this line
0x000000000040106e <+16>: movq [=11=]x0,-0x8(%rbp)
0x0000000000401076 <+24>: mov -0x10(%rbp),%rax
0x000000000040107a <+28>: lea -0x10(%rbp),%rcx
0x000000000040107e <+32>: mov [=11=]x0,%edx
0x0000000000401083 <+37>: mov %rcx,%rsi
0x0000000000401086 <+40>: mov %rax,%rdi
0x0000000000401089 <+43>: callq 0x433510 <execve>
0x000000000040108e <+48>: mov [=11=]x0,%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 [=12=]x8,%esp
0x8000136 <main+6>: movl [=12=]x80027b8,0xfffffff8(%ebp) ; <- this line
0x800013d <main+13>: movl [=12=]x0,0xfffffffc(%ebp)
0x8000144 <main+20>: pushl [=12=]x0
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 [=12=]xc,%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 位系统中如何前进的区别以及它是什么单位(位、字节或字)。
char * args[2];
是两个指针的数组。在 32 位机器上,指针(地址,但不一定)是 32 位宽,而在 64 位机器上,指针(而不是地址)是 64 位宽。
也就是说,当前栈帧中栈上的区域必须是2 * pointer_width
,所以32位机器是8
,64位机器是16
机器。
您可以在下面的代码中看到
movl [=11=]x80027b8, -0x8(%ebp)
movl [=11=]x0, -0x4(%ebp)
... 向指针移动 32 位双字(一个字符串地址,一个 NULL
)。
另一方面,64 位代码需要两个双字(8 字节)来存储地址。
movq [=12=]x0, -0x8(%rbp)
movq [=12=]x493564, -0x10(%rbp)
我在 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 [=11=]x10,%rsp
0x0000000000401066 <+8>: movq [=11=]x493564,-0x10(%rbp) ; <- this line
0x000000000040106e <+16>: movq [=11=]x0,-0x8(%rbp)
0x0000000000401076 <+24>: mov -0x10(%rbp),%rax
0x000000000040107a <+28>: lea -0x10(%rbp),%rcx
0x000000000040107e <+32>: mov [=11=]x0,%edx
0x0000000000401083 <+37>: mov %rcx,%rsi
0x0000000000401086 <+40>: mov %rax,%rdi
0x0000000000401089 <+43>: callq 0x433510 <execve>
0x000000000040108e <+48>: mov [=11=]x0,%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 [=12=]x8,%esp
0x8000136 <main+6>: movl [=12=]x80027b8,0xfffffff8(%ebp) ; <- this line
0x800013d <main+13>: movl [=12=]x0,0xfffffffc(%ebp)
0x8000144 <main+20>: pushl [=12=]x0
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 [=12=]xc,%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 位系统中如何前进的区别以及它是什么单位(位、字节或字)。
char * args[2];
是两个指针的数组。在 32 位机器上,指针(地址,但不一定)是 32 位宽,而在 64 位机器上,指针(而不是地址)是 64 位宽。
也就是说,当前栈帧中栈上的区域必须是2 * pointer_width
,所以32位机器是8
,64位机器是16
机器。
您可以在下面的代码中看到
movl [=11=]x80027b8, -0x8(%ebp)
movl [=11=]x0, -0x4(%ebp)
... 向指针移动 32 位双字(一个字符串地址,一个 NULL
)。
另一方面,64 位代码需要两个双字(8 字节)来存储地址。
movq [=12=]x0, -0x8(%rbp)
movq [=12=]x493564, -0x10(%rbp)