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

Newline byte 0Ah being ignored by x86_64 system call print program

我按照一个简单的教程创建了一个 NASM x86_64 程序,该程序使用定义的函数打印变量,并在末尾添加新行。 sprintLF 调用 sprint ,它反过来打印 rax 中设置了适当系统调用的任何内容。在 return 上,sprintLF 用 0Ah 更新 rax 换行代码,然后将其推送到堆栈,并在再次调用 sprint 之前将 rax 重新分配给 0Ah 的堆栈地址,并将换行代码写入标准输出。在我在 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.

在另一个程序中,我尝试在将参数放入堆栈后打印参数,所以我只能猜测系统调用不喜欢从堆栈中获取它的值,但我是汇编新手,这是把我弄糊涂了。

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

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

解决这个问题的方法是使用 64 位 syscall 接口。不幸的是,传入的系统调用号和寄存器参数已经改变。 Ryan Chapman's blog 有很好的 table 的 64 位 syscall 系统调用号及其参数。

来自table的sys_write系统调用号和参数是:

根据此信息,您可以通过执行以下操作将 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 调用转换为 syscall,以 Ryan Chapman 的 table 为指导。我把它留作 OP 的练习。