从 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 exitint 作为参数,在 x86 上它是 4 个字节,因此 movb [=29=], %dil 通常是不够的。在您的情况下没关系,因为您之前将 RDI 设置为 1 并且 RDI 保留在 syscall.

通常,您可以使用 xor %edi, %edi(请参阅 )有效地将 64 位寄存器置零。同样,如果您的 $value 是 32 位或更少,您可以执行 movl $value, %eax 而不是 mov $value, %rax,避免在生成的代码中使用不需要的指令前缀,同时保持相同的行为。