x86_64系统调用打印程序忽略换行字节0Ah

3
我按照一个简单的教程创建了一个NASM x86_64程序,该程序使用定义函数打印带有换行符的变量。sprintLF调用了sprint,然后打印出rax中设置适当系统调用的任何内容。返回时,sprintLF使用0Ah换行代码更新rax,然后将其推送到堆栈,并重新分配rax到0Ah的堆栈地址,然后再次调用sprint,并在stdout中写入换行代码。在整个代码下面,我已经在gdb中调试了sprint,显示所有正确的寄存器都存储有系统调用4的值,并且我不知道为什么变量字符串成功被打印,但换行符没有。

调用代码如下:

 ;; Hello World Program (Externam file include)                                                                                                                                                      
    ;; Compile with: nasm -f elf64 helloworld-if.asm                                                                                                                                                   
    ;; Link with ld helloworld-if.o -o helloworld-if                                                                                                                                                  
    ;; Run with ./helloworld-inc                                                                                                                                                                        

    %include        'function.asm' ; include our external file                                                                                                                                          

    SECTION .data
    msg1    db      'Hello, brave new world!', 0h           ;our first message string add null terminating byte                                                                                         
    msg2    db      'This is how we recycle in NASM.', 0h   ; our second message string add null terminating byte                                                                                       

    SECTION .text
    global _start

_start:

    mov     rax, msg1       ; mov the address of our first message string into RAX                                                                                                                      
    call    sprintLF                ; call our string printing function                                                                                                                                 

    mov     rax, msg2       ; move the address of our second message string into RAX                                                                                                                    
    call    sprintLF                ; call our string printing function                                                                                                                                 

    call    quit            ; call our quit function   

实用函数

; -------------------------------------------------------------------------------------------------------------------                                                                                       
; int slen(String message)                                                                                                                                                                                  
; String length calculation function                                                                                                                                                                        


slen:                           ; this is our first function declaration                                                                                                                                    

    push    rbx             ; push the value in RBX onto the stack to preserve it while we use RBX in this function                                                                                     
    mov     rbx, rax        ; move this address in RAX into RBX ( Both point to the same segment in memory)                                                                                             

nextchar:

    cmp     byte [rax], 0   ; this is the same as lesson 3                                                                                                                                              
    jz      finished
    inc     rax
    jmp     nextchar

finished:

    sub     rax, rbx
    pop     rbx             ; pop the value on the stack back into RBX                                                                                                                                  
    ret                     ; return to where the function was called                                                                                                                                   


    ;; ---------------------------------------------------------------------------------------------------------                                                                                        
    ;; void sprint(String message)                                                                                                                                                                      
    ;; String printing function                                                                                                                                                                         
sprint:

    push    rdx
    push    rcx
    push    rbx
    push    rax
    call    slen

    mov     rdx, rax
    pop     rax

    mov     rcx, rax
    mov     rbx, 1
    mov     rax, 4
    int     80h

    pop     rbx
    pop     rcx
    pop     rdx
    ret

    ;; ----------------------------------------------------------------------------------------------------------                                                                                       
    ;; void sprintLF(String message)                                                                                                                                                                    
    ;; String printing with line feed function                                                                                                                                                          

sprintLF:
    call    sprint

    push    rax             ; push rax onto the stack to preserve it while         we use the rax register in this function                                                                                     
    mov     rax, 0Ah        ; push 0Ah into rax, 0Ah is the ascii         character for a linefeed                                                                                                              
    push    rax             ; push the linefeede onto the stack so we     can get the address                                                                                                               
    mov     rax, rsp        ; move the address of the current stack     pointer into rax for sprint -> because write requires a memory address     
    call    sprint          ; call our sprint function                                                                                                                                                  
    pop     rax             ; restore out linefeed character from the     stack                                                                                                                             
    pop     rax             ; return to our program                                                                                                                                                     
    ret

        ;; -----------------------------------------------------------------------------------------------------------                                                                                      
    ;; void exit()                                                                                                                                                                                      
    ;; Exit program restore resources                                                                                                                                                                   
quit:

    mov     rbx, 0
    mov     rax, 1
    int     80h
    ret

执行代码所使用的命令及输出结果如下:
nasm -f elf64 helloworld-if.asm
ld helloworld-if.o -o hellworld-if
./hellworld-if

Hello, brave new world!This is how we recycle in NASM.

在另一个程序中,我试图将参数放入堆栈后打印它们,但同样的情况发生了,所以我只能猜测系统调用不喜欢从堆栈中取值,但我是汇编新手,这让我感到困惑。

1
slen 成功返回 1,但你必须正确处理 0x80,这就解释了为什么示例是针对 32 位架构完成的,即使它是一个 64 位示例。这相当令人恼火,但我并不感到惊讶,再次感谢。 - nrmad
@jww:这并没有什么帮助,因为那是针对DOS的,而这是针对Linux的。 - Michael Petch
这也是一个关于在64位代码中使用32位int 0x80 Linux ABI的问题的复制,但是Michael Petch对此提供了一个具体的答案,即如何将堆栈指针传递给int 0x80 - Peter Cordes
1个回答

2
你一直在尝试将使用 int0x80 的32位Linux代码转换为64位代码。虽然这对许多情况有效,但并非全部情况都适用。 int 0x80 是32位系统调用接口,但是在Linux内核中内置了IA32兼容性(大多数发行版的默认设置),因此仍然可以使用int 0x80。问题在于,当内核处理你的int 0x80请求时,只有寄存器的低32位被识别。

你第一个问题中的代码没有出现任何问题,但是这段代码不起作用。原因是RSP中的堆栈指针通常是不能用32位值寻址的地址。当你执行mov rax,rsp时,RAX中移动了RSP的完整64位值,但是sprintint 0x80调用只会看到 RAX 的底部32位(即EAX寄存器)。

这个问题的解决方法是使用64位syscall接口。不幸的是,系统调用号和传递的寄存器参数已经发生了变化。 Ryan Chapman's blog提供了一个很好的表格列出了64位syscall系统调用号及其参数。
列表中的sys_write系统调用号和参数如下:

enter image description here

根据这些信息,您可以通过以下方式将sprint转换为使用syscall接口:
sprint:
    push    r11                ; R11 and RCX are clobbered by syscall as well
    push    rcx
    push    rdx
    push    rsi
    push    rdi
    push    rax
    call    slen

    mov     rdx, rax           ; RDX = number of characters to print
    pop     rax

    mov     rsi, rax           ; RSI = address of characters to print
    mov     rdi, 1             ; RDI = file descriptor (1=STDOUT)
    mov     rax, 1             ; System call number 1 = sys_write
    syscall                    ; 64-bit system call (rather than int 0x80)

    pop     rdi
    pop     rsi
    pop     rdx
    pop     rcx
    pop     r11
    ret

这是相当低效的,可以进行清理。我以这种方式呈现它,这样您就可以从原始代码的角度理解更改。我已经注释了相关行。
注意:您应该真正将所有的int 0x80调用转换为使用Ryan Chapman的表作为指南的syscall。我把这留给OP来练习。

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