assembly 当存在具有相同值的其他寄存器时,不必要地将寄存器推入堆栈,为什么?

omjgkv6w  于 2022-11-24  发布在  其他
关注(0)|答案(1)|浏览(222)

我正在学习汇编套接字编程,有人告诉我要写这段代码:

section .text
global _start

_start:
    push    byte 6
    push    byte 1
    push    byte 2
    mov     ecx, esp
    mov     ebx, 1
    mov     eax, 102
    int     80h
 
    ;confusing part
    mov     edi, eax ;why is this (and pushing it) necessary?
    push    dword 0x00000000
    push    word 0x2923
    push    word 2
    mov     ecx, esp
    push    byte 16
    push    ecx      
    push    edi      ;why not push eax directly? Doesn't sys_socketcall put the file descriptor back into eax?
    mov     ecx, esp 
    mov     ebx, 2
    mov     eax, 102
    int     80h

我在我不理解的部分添加了注解。可以在这里找到不相关部分的完整代码。
为什么他们要把寄存器的值移到另一个寄存器呢?难道他们不能把原来的值推进去吗?

ctzwtxfj

ctzwtxfj1#

mov edi,eax是保持fd在第二个int 80h * 之后 * 可用的一种方法。**如果您决定从现在开始将套接字fd保留在EDI中,作为它的永久宿主,那么它对push edi**是有意义的。但是,push eax是可以工作的,因为此时在EAX中仍有它的副本。
在第一次介绍这一点的“第30课”中,这似乎是毫无意义的;但是如果你看后面的“课程”,他们是在这段代码的基础上构建的,使用它没有改变,并在后面添加了更多的系统调用(比如listen,它也使用push edi)。在后面的代码中,你仍然需要从socket()推送fd,而不是从bind()推送返回值。
这段代码有点笨拙,比如不移除一直压入的结构体,所以如果你在循环中这样做,最终会耗尽堆栈空间。通常你会在系统调用后使用add esp, whatever来撤销压入。
或者使用mov存储到堆栈,用新的args结构替换现有的args结构。如果文件描述符在同一个位置,你可以把它留在那里。(或者在当前代码中,push dword [esp+24]或下一个代码块中的东西,但是如果你要再次存储它,把fd放在寄存器中会更有意义。)
push byte 1也很奇怪,它仍然将ESP调整4并存储0x00000001,而push word 2只将ESP调整2并存储0x0002。(How many bytes does the push instruction push onto the stack when I don't specify the operand size?
(x86仅支持将字和双字操作数大小的操作数用于push,但支持imm 8和全角立即数。如果要强制使用1字节立即数,语法为push strict byte 1,但NASM已经这样做了,因为NASM中默认启用优化至少已有十年。)
顺便说一句,从Linux 4.3开始,对于套接字API中的每个系统调用,i386都有单独的系统调用号,如bindlisten,因此您不需要将结构体推到堆栈上并调用通用的socketcall入口点。The man page表示x86-64和ARM根本没有socketcall系统调用,始终只为每个系统调用提供单独的syscall入口点。

相关问题