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

时间:2019-03-12 18:22:43

标签: linux assembly stack x86-64 system-calls

在创建NASM x86_64程序时,我遵循了一个简单的教程,该程序使用定义的函数来打印变量,并在末尾添加新行。 sprintLF调用sprint,依次设置适当的系统调用,然后打印rax中的所有内容。在返回时,sprintLF用0Ah更新rax的换行代码,然后将其推入堆栈,并将rax重新分配给0Ah的堆栈地址,然后再次调用sprint并将换行代码写入标准输出。在所有代码下面,我已在gdb中调试了sprint,该操作表明所有正确的寄存器都存储了与系统调用4相关的值,而我困惑于为什么成功打印变量字符串而不能换行的原因。 / p>

呼叫代码

 ;; 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 个答案:

答案 0 :(得分:1)

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

第一个问题中的代码没有任何问题,但是此代码不起作用。原因是RSP中的堆栈指针通常是一个不能用32位值寻址的地址。当您执行mov rax,rsp时,RSP的完整64位值将移至RAX,但是sprint的{​​{1}}调用将仅看到RAX的低32位(EAX寄存器)

解决方法是使用64位int 0x80接口。不幸的是,传递的系统调用号和寄存器参数已更改。 Ryan Chapman's blog有一个很好的表格,其中包含64位syscall的系统调用号及其参数。

表中的syscall系统调用号码和参数是:

enter image description here

根据此信息,您可以通过执行以下操作将sys_write转换为使用sprint界面:

syscall

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

注意:您应该真正使用Ryan Chapman的表格将所有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。我将其保留为OP的练习。