assemblyterminalx86-64nasmyasm

Question about cursor movement during read system calls on the terminal


I am pretty new to Assembly and I am following some tutorials that I found on google / youtube (Mostly this).
I am trying to fly on my own and write my own little program, but obviously, it doesn't go as it goes while following a tutorial ;)
I am trying to understand why I am not getting the output that I am expecting and also, how can I debug to understand where/what my error is/are.

I am using : - yasm 1.3.0 - Ubuntu 18.04.3 LTS (Bionic Beaver)

I am compiling with following options :

yasm -Worphon-labels -g dwarf2 -f elf64 <my_file.asm> -l my_file.lst
ld -g -o my_file my_file.o

Here's my code :

section .data
  msgOne   db   "Number One ? ", 0x0
  msgOne_len equ $-msgOne

  msgTwo   db   "Number Two ? ", 0x0
  msgTwo_len equ $-msgTwo

section .bss
  numOne resb 8
  numTwo resb 8

section .text
global _start
_start
  mov rax, 1
  mov rdi, 1
  mov rsi, msgOne
  mov rdx, msgOne_len
  syscall

  mov rax, 0
  mov rdi, 0
  mov rsi, numOne
  mov rdx, 8
  syscall

  mov rax, 1
  mov rdi, 1
  mov rsi, msgTwo
  mov rdx, msgTwo_len
  syscall

  mov rax, 0
  mov rdi, 0
  mov rsi, numTwo
  mov rdx, 8
  syscall

  mov rax, 60
  mov rdi, 0
  syscall

I am expecting the following output :

Number One ? <waiting for input> ; when provided, next line
Number Two ? <waiting for input> ; when provided, next line
<program exit>

But I am getting the following :

Number One ? <waiting for input> ; say we type in 1
Number Two ? 1
_cursor is now here_             ; say we type in 1
<program exit>

There are few things that confuse me :
- Why is there a new line if I did not provide 0xA between both string display?
- Why is the '1' on the 2nd prompt appearing and why is the cursor on 3rd line?
- How can I debug my exec to try to figure out what is going on ?

I tried to use edb GUI. I knwo how to set a breakpoint / step into but I can't figure out what I am looking for and where?
I also try the classic gdb but I'm clueless how to get some debug information, other than "run, next, next, next ... program completed".

(In case you are wondering, I am learning Assembly by pure curiosity! Next in the pipeline is C :D)


Solution

  • When you run this program with input connected to a terminal (instead of redirected from a file or something), the terminal is still in "echo" mode, with line editing provided by the kernel. So the newline typed by the user affects the cursor.

    When I run it, the final result in my terminal after typing 1 enter twice:

    peter@volta:/tmp$ ./foo 
    Number One ? 1
    Number Two ? 2
    peter@volta:/tmp$ 
    

    If I used control-D to "submit" text (so read would return) without typing a newline, output looks like this:

    peter@volta:/tmp$ ./foo 
    Number One ? 1Number Two ? 1peter@volta:/tmp$ 
    

    To learn more about this stuff, google POSIX TTY and terminal input.


    Debugging:

    For GDB, use layout reg to show registers while you single step. Also you can run strace ./my_program to trace system calls. See the bottom of https://stackoverflow.com/tags/x86/info for debugging tipes.

    Strace output, recorded to a file with -o so it doesn't mix with the actual terminal output:

    peter@volta:/tmp$ strace -o foo.tr ./foo 
    Number One ? 1
    Number Two ? 1
    peter@volta:/tmp$ cat foo.tr
    execve("./foo", ["./foo"], 0x7fffbac22f80 /* 54 vars */) = 0
    write(1, "Number One ? \0", 14)         = 14
    read(0, "1\n", 8)                       = 2
    write(1, "Number Two ? \0", 14)         = 14
    read(0, "1\n", 8)                       = 2
    exit(0)                                 = ?
    +++ exited with 0 +++
    

    Notice that your prompts included a 0 byte. In a terminal it prints as zero-width but it's still a bad idea. write takes a pointer + length and works on arbitrary binary bytes. You should just leave out the terminating zero because you have explicit-length strings.