简体   繁体   中英

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

I followed a simple tutorial in the creation of a NASM x86_64 program which uses the defined functions to print variables with a new line added to the end. sprintLF calls sprint which in turn prints whatever is in rax having set up the appropriate system call. On return sprintLF updates rax with 0Ah the newline code which it then pushes to the stack and reassigns rax to the stack address of 0Ah before calling sprint again with the newline code to be written to stdout. Below all of the code I have debugged sprint in gdb which shows that all the correct registers have the values associated with system call 4 stored and I am at a loss as to why the variable strings are successfully being printed but the newlines are not.

THE CALLING CODE

 ;; 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   

UTILITY FUNCTIONS

; -------------------------------------------------------------------------------------------------------------------                                                                                       
; 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

The commands used to execute the code and the output follow:

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.

In another program where I attempt to print arguments after putting them into the stack the same occurs so I can only guess the system call doesn't like taking its value from the stack but I am new to assembly and this is confusing me.

You have been trying to convert 32-bit Linux code that uses int0x80 to 64-bit code. Although this can work for a lot of cases it doesn't work for everything. int 0x80 is the 32-bit system call interface, but with IA32 compatibility built into the Linux kernel (the default for most distros) you are allowed to still use int 0x80 . The catch is that only the lower 32-bits of the registers are recognized when the kernel processes your int 0x80 request.

The code in your first question didn't exhibit any problems, but this code doesn't work. The reason is that the stack pointer in RSP is usually an address that can't be addressed with with a 32-bit value. When you do mov rax,rsp the full 64-bit value of RSP is moved to RAX, but sprint 's int 0x80 call will only see the bottom 32-bits of RAX (the EAX register).

The way around this is to use the 64-bit syscall interface. Unfortunately, the system call numbers and the registers parameters are passed in have changed. Ryan Chapman's blog has a nice table of the 64-bit syscall system call numbers and their parameters.

The sys_write system call number and parameters from the table are:

在此处输入图片说明

Based on this information you can transform sprint to use syscall interface by doing this:

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

This is rather inefficient and it can be cleaned up. I present it this way so you you can understand the changes from the perspective of your original code. I have commented the lines of relevance.

Note : You should really convert all the int 0x80 calls to syscall using Ryan Chapman's table as a guide. I leave that as an exercise for the OP.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM