从x86程序集中的堆栈中提取的数字打印ASCII字符时的不当行为



我正在努力了解如何在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。

最新更新