从 x86 程序集中的堆栈中获取的数字打印 ASCII 字符时的不当行为
Inappropriate behaviour while printing an ASCII character from a number taken from the stack in x86 assembly
我正在尝试了解如何在 Linux 中正确使用 x86-64 程序集中的堆栈。我将一些数字压入堆栈,然后我想获取堆栈中的顶部数字并打印适当的 ASCII 值。但它每次都打印不同的东西。请帮助我理解为什么下面的代码会这样。
.section .bss
.comm x, 4
.section .text
.globl _start
_start:
push
push
mov %rsp, (x)
mov , %rax
mov , %rdi
mov $x, %rsi
mov , %rdx
syscall
mov , %rax
movb [=10=], %dil
syscall
此外,如果您能给我一些资源,我可以在其中学习如何在汇编中使用堆栈,我将不胜感激。
指令 mov %rsp, (x)
将 RSP(当前堆栈指针)的值写入内存中的位置 x
,即在您的 .bss
部分中。然后,您正在做 mov $x, %rsi
:这会将 x
的地址移动到 RSI 中,因此 RSI 将指向您的 .bss
变量,该变量保存堆栈指针的值。当您尝试从 RSI 指向的内存发出 write
系统调用读取时,您的输出将是您保存在那里的 RSP 的最低有效字节。由于堆栈的位置在每次执行程序时都会发生变化,这就是为什么每次都会得到不同的输出。
此外,您使用 .comm x, 4
在 .bss
中保留 space,但是您使用 mov %rsp, (x)
将 RSP 移入其中,这是一个 8 字节的移动。
将堆栈中的值打印到标准输出,您真正想要做的只是 mov %rsp, %rsi
。你根本不需要 x
:
.section .text
.globl _start
_start:
push
mov , %rax
mov , %rdi
mov %rsp, %rsi
mov , %rdx
syscall
mov , %rax
mov [=10=], %rdi
syscall
以上代码应输出A
并退出。
如果你想在 .bss
中使用中间变量,你将不得不执行两次移动,使用中间暂存寄存器将值从堆栈复制到其中(因为 mov
可以一次只取一个内存操作数):
.section .bss
.comm x, 4
.section .text
.globl _start
_start:
push
movb (%rsp), %al
movb %al, (x)
mov , %rax
mov , %rdi
mov $x, %rsi
mov , %rdx
syscall
mov , %rax
mov [=11=], %rdi
syscall
另外,请注意 Linux exit
将 int
作为参数,在 x86 上它是 4 个字节,因此 movb [=29=], %dil
通常是不够的。在您的情况下没关系,因为您之前将 RDI 设置为 1
并且 RDI 保留在 syscall
.
通常,您可以使用 xor %edi, %edi
(请参阅 )有效地将 64 位寄存器置零。同样,如果您的 $value
是 32 位或更少,您可以执行 movl $value, %eax
而不是 mov $value, %rax
,避免在生成的代码中使用不需要的指令前缀,同时保持相同的行为。
我正在尝试了解如何在 Linux 中正确使用 x86-64 程序集中的堆栈。我将一些数字压入堆栈,然后我想获取堆栈中的顶部数字并打印适当的 ASCII 值。但它每次都打印不同的东西。请帮助我理解为什么下面的代码会这样。
.section .bss
.comm x, 4
.section .text
.globl _start
_start:
push
push
mov %rsp, (x)
mov , %rax
mov , %rdi
mov $x, %rsi
mov , %rdx
syscall
mov , %rax
movb [=10=], %dil
syscall
此外,如果您能给我一些资源,我可以在其中学习如何在汇编中使用堆栈,我将不胜感激。
指令 mov %rsp, (x)
将 RSP(当前堆栈指针)的值写入内存中的位置 x
,即在您的 .bss
部分中。然后,您正在做 mov $x, %rsi
:这会将 x
的地址移动到 RSI 中,因此 RSI 将指向您的 .bss
变量,该变量保存堆栈指针的值。当您尝试从 RSI 指向的内存发出 write
系统调用读取时,您的输出将是您保存在那里的 RSP 的最低有效字节。由于堆栈的位置在每次执行程序时都会发生变化,这就是为什么每次都会得到不同的输出。
此外,您使用 .comm x, 4
在 .bss
中保留 space,但是您使用 mov %rsp, (x)
将 RSP 移入其中,这是一个 8 字节的移动。
将堆栈中的值打印到标准输出,您真正想要做的只是 mov %rsp, %rsi
。你根本不需要 x
:
.section .text
.globl _start
_start:
push
mov , %rax
mov , %rdi
mov %rsp, %rsi
mov , %rdx
syscall
mov , %rax
mov [=10=], %rdi
syscall
以上代码应输出A
并退出。
如果你想在 .bss
中使用中间变量,你将不得不执行两次移动,使用中间暂存寄存器将值从堆栈复制到其中(因为 mov
可以一次只取一个内存操作数):
.section .bss
.comm x, 4
.section .text
.globl _start
_start:
push
movb (%rsp), %al
movb %al, (x)
mov , %rax
mov , %rdi
mov $x, %rsi
mov , %rdx
syscall
mov , %rax
mov [=11=], %rdi
syscall
另外,请注意 Linux exit
将 int
作为参数,在 x86 上它是 4 个字节,因此 movb [=29=], %dil
通常是不够的。在您的情况下没关系,因为您之前将 RDI 设置为 1
并且 RDI 保留在 syscall
.
通常,您可以使用 xor %edi, %edi
(请参阅 $value
是 32 位或更少,您可以执行 movl $value, %eax
而不是 mov $value, %rax
,避免在生成的代码中使用不需要的指令前缀,同时保持相同的行为。