assembly 在NASM x86_64中,我可以将返回地址保存在寄存器中,然后在'ret'之前将其压回到堆栈中吗

o7jaxewo  于 2022-11-30  发布在  其他
关注(0)|答案(1)|浏览(121)

我有一个 _write 函数,它需要字符串的指针和字符串长度作为参数。

section .data
  string_literal_at_index_0: db 0x48,0x65,0x6c,0x6c,0x6f,0x20,0x77,0x6f,0x72,0x6c,0x64,0x21

global _start
section .text
_start:
   push string_literal_at_index_0 ; argument for the write function
   push 12 ; argument for the write function
   call _write
   mov rax, 60
   mov rdi, 0
   syscall
   
_write:
   pop r8 ; pop return address so i can acess the arguments
   mov rax, 1
   mov rdi, 1
   pop rdx
   pop rsi
   syscall
   push r8 ; push the return address back
   ret

我必须弹出返回地址,以便访问函数的参数

注意:* 我知道使用EBP访问它们要好得多,但我必须使用这种方式 *

我的问题是:这样做可以吗?它会弄乱堆栈吗?

注意:* 这可能是一个非常愚蠢的问题,但我是一个初学者在组装。所以纠正我,如果任何我说的这个问题是错误的!*

ih99xse1

ih99xse11#

是的,你 * 可以 * 这样做,但你不必这样做。你可以像mov rsi, [rsp+16]一样直接访问相对于RSP的堆栈参数,而不需要RBP,特别是在根本不需要改变RSP的简单函数中。
x86-64 System V(用于非Windows系统)对于函数调用和syscall几乎是一样的,所以除非你有6个以上的整数/指针参数,否则你根本不需要堆栈参数。
但是如果你真的想发明自己的stack-args-only调用约定,让被调用者从堆栈中弹出它们(比如32位stdcall),你可以这样做(通常你仍然会使用mov来访问堆栈,而不是弹出+恢复返回地址,并在最后返回ret 16)。
对于这个write(1, buf, size) Package 函数,一个更好的自定义调用约定是取RSI中的指针和RDX中的大小,这样就不需要将它们复制到任何地方,只需设置RAX和RDI(__NR_write = STDOUT_FILENO = 1),然后是syscall/ret

在标准的x86-64 System V约定中,您需要返回未修改的RSP,因此您需要在最后的push/ret之前执行sub rsp, 16(或两次伪推送),以将返回地址放回原来的位置,而不是最高参数所在的位置。

你的调用者希望能够访问它自己的相对于RSP的堆栈帧,所以它需要知道在call返回后RSP将指向哪里。只要调用者和被调用者就应该发生的事情达成一致,你就可以了。
(See Agner Fog's calling conventions doc,并查看简单函数的编译器输出,以了解它们如何访问参数并将其传递到寄存器中。* How to remove "noise" from GCC/clang assembly output? *)

相关问题