我有一个 _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访问它们要好得多,但我必须使用这种方式 *
我的问题是:这样做可以吗?它会弄乱堆栈吗?
注意:* 这可能是一个非常愚蠢的问题,但我是一个初学者在组装。所以纠正我,如果任何我说的这个问题是错误的!*
1条答案
按热度按时间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? *)