问题 x86汇编:在Linux上进行系统调用之前是否应该保存所有寄存器?


我有以下代码打开文件,将其读入缓冲区然后关闭文件。

关闭文件系统调用要求文件描述符号在ebx寄存器中。 ebx寄存器在读取系统调用之前获取文件描述符编号。我的问题是,在进行读取系统调用之前,我应该将ebx寄存器保存在堆栈中还是某个地方(可以将80h垃圾邮件放入ebx寄存器吗?)。然后恢复ebx寄存器以进行紧密系统调用?或者我的代码是否优良且安全?

我运行了下面的代码并且它可以工作,我只是不确定它是否通常被认为是良好的汇编实践,因为我没有在int 80h读取调用之前保存ebx寄存器。

;; open up the input file 
mov eax,5        ; open file system call number
mov ebx,[esp+8]  ; null terminated string file name, first command line parameter
mov ecx,0o       ; access type: O_RDONLY
int 80h          ; file handle or negative error number put in eax
test eax,eax
js Error         ; test sign flag (SF) for negative number which signals error

;; read in the full input file
mov ebx,eax            ; assign input file descripter
mov eax,3              ; read system call number
mov ecx,InputBuff      ; buffer to read into
mov edx,INPUT_BUFF_LEN ; total bytes to read
int 80h
test eax,eax
js Error               ; if eax is negative then error
jz Error               ; if no bytes were read then error
add eax,InputBuff      ; add size of input to the begining of InputBuff location
mov [InputEnd],eax     ; assign address of end of input

;; close the input file
;; file descripter is already in ebx
mov eax,6       ; close file system call number
int 80h         

4970
2018-04-24 18:42


起源

建议:对读取结果进行一次测试 <= 0 在快速路径上,然后将其排序 Error。这减少了代码通常需要的分支预测历史记录条目的数量。 jle 会工作,因为 test eax,eax 清除溢出并携带标志,并根据结果以相同的方式设置SF和ZF cmp eax, 0 确实。 - Peter Cordes


答案:


int 80h 除了将返回值放入之外,召唤本身不会破坏任何东西 eax。所以你拥有的代码片段很好。 (但是如果你的代码片段是一个更大的例程的一部分,它应该被通常的Linux x86 ABI之后的其他代码调用,你将需要保留 ebx进入日常工作时可能还有其他寄存器,退出时恢复。)

可以在中找到内核中的相关代码 arch/x86/kernel/entry_32.S。由于广泛使用宏和各种细节(支持系统调用跟踪,DWARF调试注释等),因此有点难以理解,但是: int 80h 处理程序是 system_call (我链接的版本中的第493行);寄存器通过 SAVE_ALL 宏观(第497行);然后他们又通过了 RESTORE_REGS (第534行)就在返回之前。


11
2018-04-25 16:52



一般来说,根据 syscall(2),“有些架构可能会不加区别地破坏此处未列出的其他寄存器”。具体来说,x86-64系统调用(用 syscall) 做 撞 rcx 和 r11,根据我的阅读 这个entry_64.S的写法。这得到了以下事实的支持 sysret 指令(由entry_64.S使用) RIP=RCX,和 RFLAGS=R11和一些细分的东西。执行后,您将返回用户模式。 AFAICT,x86-64系统调用保留除R11,RCX和RAX之外的所有内容。 - Peter Cordes
我无法在代码中找到任何特定的文档,甚至是注释,说明amd64或i386的确切情况。我觉得奇怪的是,像这样的重要一点只是留给读者解决宏。我的意思是在实践中,它主要只是需要该信息的libc实现者,但鉴于Linux承诺保持稳定的ABI,我不认为它会发生变化。所以它可以写下来。 (我想,在这里,所以我投了赞成票:P) - Peter Cordes
更新是的, syscall 本身clobbers RCX / R11,和 使用Linux系统调用 syscall 只有那些+ rax。但是IIRC,这是我的编辑,其中包括;我不记得我是否找到过Linux的源代码以外的外部文档。 - Peter Cordes


是的,您应该保存并恢复为 http://www.linuxjournal.com/files/linuxjournal.com/linuxjournal/articles/040/4048/4048l1.html


1
2018-04-24 18:55



该代码正在保存和恢复 %ebx 这样它就可以用它来传递参数 - 而不是因为它 int $0x80 本身正在腐蚀它。 - Matthew Slattery