int 0x80会覆盖寄存器的值吗?

4
我写了一个程序,它应该像一个 while 循环一样运行,打印出一定次数的文本字符串。
以下是代码:
global _start


    section .data
    
    msg db "Hello World!",10    ; define the message
    msgl equ $ - msg            ; define message length
                                ; use minimal size of storage space

    imax dd 0x00001000          ; defines imax to be big!

    section .text
_start:


    mov r8, 0x10          ; <s> put imax in r8d, this will be our 'i' </s>
                          ; just attempt 10 iterations
_loop_entry:                    ; loop entry point
    mov eax, 4                  ; setup the message to print
    mov ebx, 1                  ; write, stdout, message, length
    mov ecx, msg
    mov edx, msgl
    int 0x80                    ; print message
                                ; this is valid because registers do not change
    dec r8                      ; decrease i and jump on not zero
    cmp r8,1                    ; compare values to jump
    jnz _loop_entry


    mov rax, 1                  ; exit with zero
    mov rbx, 0
    int 0x80

我遇到的问题是程序进入了一个无限循环。我在gdb中运行它并发现原因是:
调用int 0x80来打印消息,这一步是正确的,但在中断结束后,r8寄存器的内容被设置为零,而不是应有的值。r8是计数器所在的位置,用于计算(递减)字符串被打印的次数。
int 0x80是否会修改寄存器的值?我注意到rax、rbx、rcx和rdx没有受到同样的影响。
测试结果
答案:是的!它确实修改了r8。
我在程序中做了两件事情。首先,我现在使用cmp r8, 0来得到正确次数的Hello World!,其次,我添加了:
mov [i], r8                 ; put away i

_loop_entry:之后,我还加入了。
mov r8, [i]                 ; get i back

在第一个int 0x80之后。

这是我的现在工作的程序。更多关于与C ++性能的信息即将到来。

;
;   main.asm
; 
; 
;   To be used with main.asm, as a test to see if optimized c++
;   code can be beaten by me, writing a for / while loop myself. 
; 
; 


;  Absolute minimum code to be competative with asm.


global _start


    section .data
    
    msg db "Hello World!",10    ; define the message
    msgl equ $ - msg            ; define message length
                                ; use minimal size of storage space

    imax dd 0x00001000          ; defines imax to be big!
    i dd 0x0                    ; defines i
    
    section .text
_start:


    mov r8, 0x10          ; put imax in r8d, this will be our 'i'
_loop_entry:                    ; loop entry point
    mov [i], r8                 ; put away i
    mov eax, 4                  ; setup the message to print
    mov ebx, 1                  ; write, stdout, message, length
    mov ecx, msg
    mov edx, msgl
    int 0x80                    ; print message
                                ; this is valid because registers do not change
    mov r8, [i]                 ; get i back
    dec r8                      ; decrease i and jump on not zero
    cmp r8,0                    ; compare values to jump
    jnz _loop_entry


    mov rax, 1                  ; exit with zero
    mov rbx, 0
    int 0x80

在64位代码中使用32位的int 0x80 ABI会将r8-r11寄存器清零并保留其他所有内容。 - Peter Cordes
1个回答

6
int 0x80只是引发了软件中断。在您的情况下,它被用于进行系统调用。是否会影响任何寄存器取决于您调用的特定系统调用以及平台的系统调用调用约定。请阅读文档获取详细信息。
具体来说,参考《System V应用程序二进制接口x86-64™架构处理器增强版》[PDF链接],附录A,x86-64 Linux内核约定
引用如下:

C库和Linux内核之间的接口与用户级应用程序相同......

对于用户级应用程序,r8是一个可用于临时存储数据的寄存器,这意味着它是由调用者保存的。如果您希望在系统调用期间保留它,则需要自己实现。

好的,作为一个测试,我将在 int 0x80 的两侧保存和恢复 r8 到内存中。我会向您更新。 - FreelanceConsultant
ABI文档没有定义64位模式下如何处理寄存器r8-r15或RAX的高半部分等32位int 0x80 ABI。你的论点是无效的,因为64位syscall保留了r8,而函数调用则不然。它是类似的,但有显著的差异。无论如何,在64位代码中,int 0x80会将r8清零。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接