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

问题描述 投票:2回答:1

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

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

linux assembly stack x86-64 system-calls
1个回答
1
投票

您一直在尝试将使用int0x80的32位Linux代码转换为64位代码。虽然这可以适用于很多情况,但它并不适用于所有情况。 int 0x80是32位系统调用接口,但由于内核内置了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有一个很好的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

这样效率很低,可以清理。我以这种方式呈现它,以便您可以从原始代码的角度理解更改。我评论了相关的方面。

注意:您应该使用Ryan Chapman的表作为指南将所有int 0x80调用转换为syscall。我把它作为OP的练习。

© www.soinside.com 2019 - 2024. All rights reserved.