我正在努力了解如何在Linux中正确使用x86-64程序集中的堆栈。我正在向堆栈中推送一些数字,然后我想取堆栈中的顶部数字并打印适当的ASCII值。但它每次都打印不同的东西。请帮助我理解下面的代码为什么会这样。
.section .bss
.comm x, 4
.section .text
.globl _start
_start:
push $52
push $65
mov %rsp, (x)
mov $1, %rax
mov $1, %rdi
mov $x, %rsi
mov $1, %rdx
syscall
mov $60, %rax
movb $0, %dil
syscall
此外,如果你能给我一些可以学习在组装中使用堆栈的来源,我将不胜感激。
指令mov %rsp, (x)
将RSP(当前堆栈指针(的值写入内存中的位置x
,该位置位于.bss
部分中。然后,您正在执行mov $x, %rsi
:这将x
的地址移动到RSI中,因此RSI将指向您的.bss
变量,该变量保存堆栈指针的值。当您尝试从RSI指向的内存中发出write
系统调用读取时,您的输出将是保存在那里的RSP的最低有效字节。由于堆栈的位置在每次执行程序时都会发生变化,这就是为什么每次都会得到不同的输出。
此外,您使用.comm x, 4
在.bss
中保留空间,但使用mov %rsp, (x)
将RSP移动到其中,这是一个8字节的移动。
要将值从堆栈打印到标准输出,您真正想做的只是mov %rsp, %rsi
。你根本不需要x
:
.section .text
.globl _start
_start:
push $65
mov $1, %rax
mov $1, %rdi
mov %rsp, %rsi
mov $1, %rdx
syscall
mov $60, %rax
mov $0, %rdi
syscall
上面的代码应该输出A
并退出。
如果要在.bss
中使用中间变量,则必须执行两次移动,才能使用中间暂存寄存器将值从堆栈复制到其中(因为mov
一次只能获取一个内存操作数(:
.section .bss
.comm x, 4
.section .text
.globl _start
_start:
push $65
movb (%rsp), %al
movb %al, (x)
mov $1, %rax
mov $1, %rdi
mov $x, %rsi
mov $1, %rdx
syscall
mov $60, %rax
mov $0, %rdi
syscall
此外,请注意,在Linux上,exit
采用int
作为参数,在x86上为4字节,因此movb $0, %dil
通常是不够的。在您的情况下,这是可以的,因为您之前将RDI设置为1
,并且RDI保留在syscall
上。
通常,您可以使用xor %edi, %edi
(请参阅在x86程序集中将寄存器设置为零的最佳方式是什么:xor、mov或and?(来有效地将64位寄存器清零。类似地,如果您的$value
为32位或更低,则可以使用movl $value, %eax
而不是mov $value, %rax
,从而避免在生成的代码中使用不必要的指令前缀,同时保持相同的beahvior。