6
\$\begingroup\$

I am new assembly programming in Linux (x86_64) and I want to make sure that I am programing in a correct way. I wrote a program that just takes an input from the user and then writes his input to stdout

SYS_WRITE   equ 1 ; write text to stdout
SYS_READ    equ 0 ; read text from stdin
SYS_EXIT    equ 60 ; terminate the program
STDOUT      equ 1 ; stdout

section .bss
    uinput resb 24 ; 24 bytes for user string
    uinput_len equ $ - uinput ; get length of user input

section .data
    text db "You wroted: "
    text_len equ $ - text

section .text
    global _start

_start:
    mov rax, SYS_READ
    mov rdi, STDOUT
    mov rsi, uinput
    mov rdx, uinput_len
    syscall

    mov rax, SYS_WRITE
    mov rdi, STDOUT
    mov rsi, text
    mov rdx, text_len
    syscall

    mov rax, SYS_WRITE
    mov rdi, STDOUT
    mov rsi, uinput
    mov rdx, uinput_len
    syscall

    mov rax, SYS_EXIT
    mov rsi, 0 ; successful exit
    syscall

Am I doing this experiment correctly? Which suggestions do you find to improve this code? Could you please provide some resources to deepen in good practices (and if posible more features or effective techniques)?

Thank you.

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Three small errors

section .data
   text db "You wroted: "
   text_len equ $ - text

A small spelling error (typo). Correct is: "You wrote: " without the d.


mov rax, SYS_READ
mov rdi, STDOUT
mov rsi, uinput
mov rdx, uinput_len
syscall

For SYS_READ you need to use STDIN instead of STDOUT.


mov rax, SYS_EXIT
mov rsi, 0 ; successful exit
syscall

The first parameter goes in the RDI register instead of RSI.

Three small improvements

section .bss
   uinput resb 24 ; 24 bytes for user string
   uinput_len equ $ - uinput ; get length of user input

It's strange to see a calculation for the uinput_len variable given that the length is a hardcoded 24. What you can write is:

section .bss
    uinput_len equ     24         ; 24 bytes for user input
    uinput     resb    uinput_len

Be nice for the person that uses your program and show a prompt of some kind before expecting an input.


For the final result you currently show the whole inputbuffer. What if the user didn't input that much characters? Best to only show the characters that were effectively inputted. You obtain this count in the RAX register upon returning from SYS_READ. e.g. If the user inputs 5 characters then RAX will hold 6. Those 5 characters plus the terminating newline character (0Ah).

Same code, different style

You should offset you tail comments so that they all start in the same column. This will improve readability.
And because readability is very very important, I've applied the same rule to the labels, mnemonics, and operands.

    SYS_READ   equ     0          ; read text from stdin
    SYS_WRITE  equ     1          ; write text to stdout
    SYS_EXIT   equ     60         ; terminate the program
    STDIN      equ     0          ; standard input
    STDOUT     equ     1          ; standard output
; --------------------------------
section .bss
    uinput_len equ     24         ; 24 bytes for user input
    uinput     resb    uinput_len ; buffer for user input
; --------------------------------
section .data
    prompt     db      "Please input some text: "
    prompt_len equ     $ - prompt
    text       db      10, "You wrote: "
    text_len   equ     $ - text
; --------------------------------
section .text
    global _start

_start:
    mov     rdx, prompt_len
    mov     rsi, prompt
    mov     rdi, STDOUT
    mov     rax, SYS_WRITE
    syscall

    mov     rdx, uinput_len
    mov     rsi, uinput
    mov     rdi, STDIN
    mov     rax, SYS_READ
    syscall                      ; -> RAX
    push    rax                  ; (1)

    mov     rdx, text_len
    mov     rsi, text
    mov     rdi, STDOUT
    mov     rax, SYS_WRITE
    syscall

    pop     rdx                  ; (1)
    mov     rsi, uinput
    mov     rdi, STDOUT
    mov     rax, SYS_WRITE
    syscall

    xor     edi, edi             ; successful exit
    mov     rax, SYS_EXIT
    syscall

Instead of mov rdi, 0, I've used xor edi, edi which is shorter and faster and leaves the same result (0) in the RDI register.

I always prefer to write the function number directly above the syscall instruction. I find this clearer. As a consequence I've also inversed the order of the other parameters, again for clarity.



You can learn a lot about 64-bit Linux programming from the .PDF that you can download here
It provides good examples that deal with console input and console output and more.

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.