I want my Assembly Code to takes user input and outputs it along with other text but the output isn't correct - linux

My code works but the output isn't just right because when I enter the name, the output becomes this; "What is your member name? Welcome to the club, Bob!!!!!!!!!!!!!!!!!!, enjoy the party." with the "!!!!!!" at the end of the name. What am I doing wrong?
Here is my assembly code:
section .data
prompt: db "What is your member name? "
prompt_len: equ $-prompt
greet1: db "Welcome to the club, "
greet1_len: equ $-greet1
greet2: db ", enjoy the party."
greet2_len: equ $-greet2
inputbuffer_len: equ 256
inputbuffer: times inputbuffer_len db '!'
STDIN: equ 0
STDOUT: equ 1
SYS_READ: equ 0
SYS_WRITE: equ 1
SYS_EXIT: equ 60
section .text
global _start
_start:
mov rdx, prompt_len ;output prompt
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, prompt
syscall
mov rax, SYS_READ ;user input here
mov rdi, STDIN
mov rsi, inputbuffer
mov rdx, inputbuffer_len
syscall
mov rdx, greet1_len ; output "Welcome to the club, "
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, greet1
syscall
mov rdx, rax ;output user's inputted name
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, inputbuffer
syscall
mov rdx, greet2_len ; output ", enjoy the party."
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, greet2
syscall
mov rax, SYS_EXIT
mov rdi, 0
syscall

The length of Bob!!!!!!!!!!!!!!!!!! is the length of Welcome to the club, .
This is no coincidence.
Following the write(2) system call rax contains the number of successfully written Bytes.
(This might be less than the desired number of Bytes as the manual page describes.)
Like David C. Rankin commented you will need to mind the return value of read(2).
On success, read(2) returns the number of Bytes read in rax.
However, you are overwriting this value for and with the intervening write(2) system call.
Store and recall somewhere the number of successfully read Bytes (e. g. push/pop) and you’re good.
PS:
You could save one write(2) system call by rearranging the buffer to follow after greet_1.
Then you could write(2) rax + greet1_len Bytes at once.
But one problem at a time.

Related

Clear input buffer Assembly x86 (NASM)

Edit: This is similar to this: Reset a string variable to print multitple user inputs in a loop (NASM Assembly). But it is not the same issue.
From the other post, I was able to prevent additional characters from being printed. However, I still cannot prevent those additional characters from being read when the program goes back to the point in which it asks the user for input.
I'm creating a program that asks an user for input, and then prints it. Afterwards, it asks the user to enter 'y' if they want to print another text, or press anything else to close the program.
My issue is that if the user enters more characters than expected, those extra characters don't go away, and when the program goes back to ask the user for input, there's no chance to enter input because the program takes the remaining characters from the last time it received input.
For example:
The user is asked to enter text to print, and they enter: "Heyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
Leftover is "yyy"
At this point, the program should ask the user to enter 'y' to repeat the process, or anything else to close the program.
Output:
Heyyyyyyyyyyyyyyyyyyyyyyyyyy
Wanna try again? If yes, enter y. If not, enter anything else to close the program
Enter your text:
Output: yy
Wanna try again? If yes, enter y. If not, enter anything else to close the program.
And only now it asks for user input again
Since "yyy" is still in buffer, the user doesn't get a chance to actually enter input in this case.
What can I do to fix this issue? I've just started to learn about this recently, so I'd appreciate any help.
Thanks.
This is what I've done
prompt db "Type your text here.", 0h
retry db "Wanna try again? If yes, enter y. If not, enter anything else to close the program"
section .bss
text resb 50
choice resb 2
section .text
global _start
_start:
mov rax, 1 ;Just asking the user to enter input
mov rdi, 1
mov rsi, prompt
mov rdx, 21
syscall
mov rax, 0 ;Getting input and saving it on var text
mov rdi, 0
mov rsi, text
mov rdx, 50
syscall
mov r8, rax ;This is what I added to prevent additional characters
;from being printed
mov rax, 1 ;Printing the user input
mov rdi, 1
mov rsi, text
mov rdx, r8
syscall
mov rax, 1 ;Asking if user wants to try again
mov rdi, 1
mov rsi, retry
mov rdx, 83
syscall
mov rax, 0 ;Getting input and saving it on var choice
mov rdi, 0
mov rsi, choice
mov rdx, 2
syscall
mov r8b, [choice] ;If choice is different from y, go to end and close the program. Otherwhise, go back to start.
cmp byte r8b, 'y'
jne end
jmp _start
end:
mov rax, 60
mov rdi, 0
syscall
The simple way to clear stdin is to check if the 2nd char in choice is the '\n' (0xa). If it isn't, then characters remain in stdin unread. You already know how to read from stdin, so in that case, just read stdin until the '\n' is read1, e.g.
mov rax, 0 ;Getting input and saving it on var choice
mov rdi, 0
mov rsi, choice
mov rdx, 2
syscall
cmp byte [choice], 'y' ; check 1st byte of choice, no need for r8b
jne end
cmp byte [choice + 1], 0xa ; is 2nd char '\n' (if yes done, jump start)
je _start
empty: ; chars remain in stdin unread
mov rax, 0 ; read 1-char from stdin into choice
mov rdi, 0
mov rsi, choice
mov rdx, 1
syscall
cmp byte [choice], 0xa ; check if char '\n'?
jne empty ; if not, repeat
jmp _start
Beyond that, you should determine your prompt lengths when you declare them, e.g.
prompt db "Type your text here. ", 0h
plen equ $-prompt
retry db "Try again (y/n)? ", 0h
rlen equ $-retry
That way you do not have to hardcode lengths in case you change your prompts, e.g.
_start:
mov rax, 1 ;Just asking the user to enter input
mov rdi, 1
mov rsi, prompt
mov rdx, plen
syscall
mov rax, 0 ;Getting input and saving it on var text
mov rdi, 0
mov rsi, text
mov rdx, 50
syscall
mov r8, rax
mov rax, 1 ;Printing the user input
mov rdi, 1
mov rsi, text
mov rdx, r8
syscall
mov rax, 1 ;Asking if user wants to try again
mov rdi, 1
mov rsi, retry
mov rdx, rlen
syscall
If you put it altogether, you can do:
prompt db "Type your text here. ", 0h
plen equ $-prompt
retry db "Try again (y/n)? ", 0h
rlen equ $-retry
section .bss
text resb 50
choice resb 2
section .text
global _start
_start:
mov rax, 1 ;Just asking the user to enter input
mov rdi, 1
mov rsi, prompt
mov rdx, plen
syscall
mov rax, 0 ;Getting input and saving it on var text
mov rdi, 0
mov rsi, text
mov rdx, 50
syscall
mov r8, rax
mov rax, 1 ;Printing the user input
mov rdi, 1
mov rsi, text
mov rdx, r8
syscall
mov rax, 1 ;Asking if user wants to try again
mov rdi, 1
mov rsi, retry
mov rdx, rlen
syscall
mov rax, 0 ;Getting input and saving it on var choice
mov rdi, 0
mov rsi, choice
mov rdx, 2
syscall
cmp byte [choice], 'y' ; check 1st byte of choice, no need for r8b
jne end
cmp byte [choice + 1], 0xa ; is 2nd char '\n' (if yes done, jump start)
je _start
empty: ; chars remain in stdin unread
mov rax, 0 ; read 1-char from stdin into choice
mov rdi, 0
mov rsi, choice
mov rdx, 1
syscall
cmp byte [choice], 0xa ; check if char '\n'?
jne empty ; if not, repeat
jmp _start
end:
mov rax, 60
mov rdi, 0
syscall
Example Use/Output
$ ./bin/emptystdin
Type your text here. abc
abc
Try again (y/n)? y
Type your text here. def
def
Try again (y/n)? yes please!
Type your text here. geh
geh
Try again (y/n)? yyyyyyyyyyyyyeeeeeeeeeesssssssss!!!!
Type your text here. ijk
ijk
Try again (y/n)? n
Now even a cat stepping on the keyboard at your (y/n)? prompt won't cause problems. There are probably more elegant ways to handle this that would be more efficient that repetitive reads, with syscall, but this will handle the issue.
Additional Considerations And Error-Checks
As mentioned above, the simplistic reading and checking of a character-at-a-time isn't a very efficient approach, though it is conceptually the easiest extension without making other changes. #PeterCordes makes a number of good points in the comments below related to approaches that are more efficient and more importantly about error conditions that can arise that should be protected against as well.
For starters when you are looking for information on the individual system call use, Anatomy of a system call, part 1 provides a bit of background on approaching their use supplemented by the Linux manual page, for read man 2 read for details on the parameter types and return type and values.
The original solution above does not address what happens if the user generates a manual end-of-file by pressing Ctrl+d or if a read error actually occurs. It simply addressed the user-input and emptying stdin question asked. With any user-input, before you use the value, you must validate that the input succeeded by checking the return. (not just for the yes/no input, but all inputs). For purposes here, you can consider zero input (manual end-of-file) or a negative return (read error) as a failed input.
To check whether you have at least one valid character of input, you can simply check the return (read returns the number of characters read, sys_read placing that value in rax after the syscall). A zero or negative value indicating no input was received. A check could be:
cmp rax, 0 ; check for 0 bytes read or error
jle error
You can write a short diagnostic to the user and then handle the error as wanted, this example simply exits after outputting a diagnostic, e.g.
readerr db 0xa, "eof or read error", 0xa, 0x0
rderrsz equ $-readerr
...
; your call to read here
cmp rax, 0 ; check for 0 bytes read or error
jle error
...
error:
mov rax, 1 ; output the readerr string and jmp to end
mov rdi, 1
mov rsi, readerr
mov rdx, rderrsz
syscall
jmp end
Now moving on to a more efficient manner for emptying stdin. The biggest hindrance indicate in the original answer was the repeated system calls to sys_read to read one character at a time reusing your 2-byte choice buffer. The obvious solution is to make choice bigger, or just use stack space to read more characters each time. (you can look at the comments for a couple of approaches) Here, for example we will increase choice to 128-bytes which in the case of the anticipate "y\n" input will only make use of two of those bytes, but in the case of an excessively long input will read 128-bytes at a time until the '\n' is found. For setup you have:
choicesz equ 128
...
section .bss
text resb 50
choice resb 128
Now after you ask for (y/n)? your read would be:
mov rax, 0 ; Getting input and saving it on var choice
mov rdi, 0
mov rsi, choice
mov rdx, choicesz
syscall
cmp rax, 0 ; check for 0 bytes read (eof) or error
jle error
cmp byte [choice], 'y' ; check 1st byte of choice, no need for r8b
jne end ; not a 'y', doesn't matter what's in stdin, end
Now there are two conditions to check. First, compare the number of characters read with your buffer size choicesz and if the number of characters read is less than choicesz, no characters remain unread in stdin. Second, if the return equals the buffer size, you may or may not have characters remaining in stdin. You need to check the last character in the buffer to see if it is the '\n' to indicate whether you have read all the input. If the last character is other than the '\n' characters remain unread (unless the user just happened to generate a manual end-of-file at the 128th character) You can check as:
empty:
cmp eax, choicesz ; compare chars read and buffer size
jb _start ; buffer not full - nothing remains in stdin
cmp byte [choice + choicesz - 1], 0xa ; if full - check if last byte \n, done
je _start
mov rax, 0 ; fill choice again from stdin and repeat checks
mov rdi, 0
mov rsi, choice
mov rdx, choicesz
syscall
cmp rax, 0 ; check for 0 bytes read or error
jle error
jmp empty
(note: as noted above, there is a further case to cover, not covered here, such as where the user enters valid input, but then generates a manual end-of-file instead of just pressing Enter after the 128th character (or a multiple of 128). There you can't just look for a '\n' it doesn't exist, and if there are no more chacters and call sys_read again, it will block wating on input. Conceivably you will need to use a non-blocking read and putback of a single character to break that ambiguity -- that is left to you)
A comlete example with the improvements would be:
prompt db "Type your text here. ", 0x0
plen equ $-prompt
retry db "Try again (y/n)? ", 0x0
rlen equ $-retry
textsz equ 50
choicesz equ 128
readerr db 0xa, "eof or read error", 0xa, 0x0
rderrsz equ $-readerr
section .bss
text resb 50
choice resb 128
section .text
global _start
_start:
mov rax, 1 ; Just asking the user to enter input
mov rdi, 1
mov rsi, prompt
mov rdx, plen
syscall
mov rax, 0 ; Getting input and saving it on var text
mov rdi, 0
mov rsi, text
mov rdx, textsz
syscall
cmp rax, 0 ; check for 0 bytes read or error
jle error
mov r8, rax
mov rax, 1 ; Printing the user input
mov rdi, 1
mov rsi, text
mov rdx, r8
syscall
mov rax, 1 ; Asking if user wants to try again
mov rdi, 1
mov rsi, retry
mov rdx, rlen
syscall
mov rax, 0 ; Getting input and saving it on var choice
mov rdi, 0
mov rsi, choice
mov rdx, choicesz
syscall
cmp rax, 0 ; check for 0 bytes read (eof) or error
jle error
cmp byte [choice], 'y' ; check 1st byte of choice, no need for r8b
jne end ; not a 'y', doesn't matter what's in stdin, end
empty:
cmp eax, choicesz ; compare chars read and buffer size
jb _start ; buffer not full - nothing remains in stdin
cmp byte [choice + choicesz - 1], 0xa ; if full - check if last byte \n, done
je _start
mov rax, 0 ; fill choice again from stdin and repeat checks
mov rdi, 0
mov rsi, choice
mov rdx, choicesz
syscall
cmp rax, 0 ; check for 0 bytes read or error
jle error
jmp empty
end:
mov rax, 60
mov rdi, 0
syscall
error:
mov rax, 1 ; output the readerr string and jmp to end
mov rdi, 1
mov rsi, readerr
mov rdx, rderrsz
syscall
jmp end
There are surely more efficient ways to optimize this, but for purposes of discussion of "How do I empty stdin?", this second approach with the buffer size used alieviates the repetitive calls to sys_read to read one character at-a-time is a good step forward. "How do it completely optimize the check?" is a whole separate question.
Let me know if you have further questions.
Footnotes:
1. In this circumstance where the user is typing input, the user generates a '\n' by pressing Enter, allowing you to check for the '\n' as the final character in emptying stdin. The user can also generate a manual end-of-file by pressing Ctrl+d so the '\n' isn't guaranteed. There are many still other ways stdin can be filled, such as redirecting a file as input where there should be a ending '\n' to be POSIX compliant, there too that isn't a guarantee.

Nasm x86_64: Why can't I write and read from the same file?

I have a problem with file handling in Nasm x86_64.
I have opend correctly the file and i can write into it o read from it, but if I try to read something from the file after i have wrote something into it i don't get anything.
So i get read or write from a file.
The strange thing is that if i first read write I don't have any problem and everything works fine, so the problem is only when i first write and then read.
Could someone help me to solve this problem and to figure out the cause?
Here is the code to open the file:
mov rax, SYS_OPEN
mov rdi, filename
mov rsi, O_CREAT+O_RDWR+O_APPEND
mov rdx, 0744o
syscall
push rax
Code to close the file:
mov rax, SYS_CLOSE
mov rdi, r11
syscall
Code to print a string:
mov rdx, rax
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, temp
syscall
Code to of getLength (the argument is the string of which I want to get the length):
%macro getLength 1
mov r10, %1
mov r11, r10
%%begin:
cmp byte [r11], 10
je %%end
inc r11
jmp %%begin
%%end:
sub r11, r10
%endmacro
The code to write:
getLength msg
mov rax, SYS_WRITE
mov rdi, [rsp]
mov rsi, msg
mov rdx, r11
syscall
Code to read:
mov rax, SYS_READ
mov rdi, [rsp]
mov rsi, temp ;buffer to store the string read
mov rdx, 10
syscall
Both the code to read and the code to write works perfectly alone, the problem is when I use the code to read after the code to write.
So this code works.
%include "./standardlib.inc"
section .data
filename db "./file.txt", 0
msg db "hello", 10
section .bss
temp resb 10
section .text
global _start:
_start:
mov rax, SYS_OPEN
mov rdi, filename
mov rsi, O_CREAT+O_RDWR+O_APPEND
mov rdx, 0744o
syscall
push rax
mov rax, SYS_READ
mov rdi, [rsp]
mov rsi, temp
mov rdx, 10
syscall
mov rdx, rax
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, temp
syscall
getLength msg
mov rax, SYS_WRITE
mov rdi, [rsp]
mov rsi, msg
mov rdx, r11
syscall
mov rax, SYS_CLOSE
mov rdi, r11
syscall
exit
This coe doesn't work:
%include "./standardlib.inc"
section .data
filename db "./file.txt", 0
msg db "hello", 10
section .bss
temp resb 10
section .text
global _start:
_start:
mov rax, SYS_OPEN
mov rdi, filename
mov rsi, O_CREAT+O_RDWR+O_APPEND
mov rdx, 0744o
syscall
push rax
getLength msg
mov rax, SYS_WRITE
mov rdi, [rsp]
mov rsi, msg
mov rdx, r11
syscall
mov rax, SYS_READ
mov rdi, [rsp]
mov rsi, temp
mov rdx, 10
syscall
mov rdx, rax
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, temp
syscall
mov rax, SYS_CLOSE
mov rdi, r11
syscall
exit
So i understood that I have to use lseek to return to the beginning of the file.
Is this a good invocation for sys_lseek?
mov rax, 8 ;sys_lseek syscall ID
mov rdi, [rsp] ;file descriptor
mov rsi, 0 ;The offset
mov rdx, 0 ;I imagine the value of SEEK_SET
sys_lseek
I suppose that the offset value is wrong and I should have used ftell to find it, but I can't figure out how to call it.
ftell
Since the file are read sequentially, after a call to sys_read in append mode the cursor is moved to the end of the file, so if you try to read from that position you won't read anything.
To solve this problem you have to reposition the cursor at the beginning of the file.
To do that you can use the lseek system call.
mov rax, 8 ;system call Id for sys_lseek
mov rdi, [rsp] ;file descriptor
mov rsi, 0 ;offset value, so number of characters to move the cursor
mov rdx, 0 ;It indicates the initial position from which move the cursor, in this case the value 0 indicates that the initial position is the beginning of the file
syscall
After a call to this system call the cursor position will be at the beginning of the file and you will be able to read from it.

Linux system call for X86 64 echo program

I'm still learning assembly so my question may be trivial.
I'm trying to write an echo program with syscall, in which I get a user input and give it as output on the next line.
section .text
global _start
_start:
mov rax,0
mov rdx, 13
syscall
mov rsi, rax
mov rdx, 13
mov rax, 1
syscall
mov rax, 60
mov rdi, 0
syscall
I'm assuming all you want to do is return the input to the output stream, so to do that you need to do a few things.
First, create a section .bss in your code. This is for initializing data. You will initialize a string with any name you want and do so with label resb sizeInBits. for demonstration it will be a 32 bit string called echo.
Extra note, the ';' character is used for comments similar to what // is in c++.
Example code
section .data
text db "Please enter something: " ;This is 24 characters long.
section .bss
echo resb 32 ;Reserve 32 bits (4 bytes) into string
section .text
global _start
_start:
call _printText
call _getInput
call _printInput
mov rax, 60 ;Exit code
mov rdi, 0 ;Exit with code 0
syscall
_getInput:
mov rax, 0 ;Set ID flag to SYS_READ
mov rdi, 0 ;Set first argument to standard input
; SYS_READ works as such
;SYS_READ(fileDescriptor, buffer, count)
;File descriptors are: 0 -> standard input, 1 -> standard output, 2 -> standard error
;The buffer is the location of the string to write
;And the count is how long the string is
mov rsi, echo ;Store the value of echo in rsi
mov rdx, 32 ;Due to echo being 32 bits, set rdx to 32.
syscall
ret ;Return to _start
_printText:
mov rax, 1
mov rdi, 1
mov rsi, text ;Set rsi to text so that it can display it.
mov rdx, 24 ;The length of text is 24 characters, and 24 bits.
syscall
ret ;Return to _start
_printInput:
mov rax, 1
mov rdi, 1
mov rsi, echo ;Set rsi to the value of echo
mov rdx, 32 ;Set rdx to 32 because echo reserved 32 bits
syscall
ret ;Return to _start

Assembly, read in 2 ints

I've just started to learn assembler (2 days ago) for x86 arch (but I program on x86_64 see below). I want to read in 2 numbers and for that I use Linux system calls (64 bit system). Well I looked up the corresponding numbers for read/write in unitstd_64.h and seems to work. But one thing bothers me (first the code):
section .data
prompt1 db "Enter a number: ", 0
lenMsg equ $-prompt1
outmsg db "Entered: ", 0
lenOut equ $-outmsg
section .bss
input1 resd 1
input2 resd 1
segment .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, prompt1
mov rdx, lenMsg
syscall
;read input number 1
mov rax, 0
mov rdi, 2
mov rsi, input1
mov rdx, 1
syscall
;prompt another number
mov rax, 1
mov rdi, 1
mov rsi, prompt1
mov rdx, lenMsg
syscall
;read input number 1
mov rax, 0
mov rdi, 2
mov rsi, input2
mov rdx, 1
syscall
;exit correctly
mov rax, 60
mov rdi, 0
syscall
The program does the following:
Shows prompt1
Let the user enter a number
Shows prompt1 again
quits (should'nt it let the user enter a number instead of quitting?)
Why is the fourth syscall simply ignored? Thanks in advance.
edit:
I use nasm. Object file created with nasm -f elf64 bla.asm. Linked with ld -o bla bla.o

How to read from and write to files using NASM for x86-64bit

I have a NASM program for 64bit Linux system which works with standard I/O devices and it looks something like that:
section .data
prompt db "Enter your text: ", 10
length equ $ - prompt
text times 255 db 0
textSize equ $ - text
section .text
global main
main:
mov rax, 1
mov rdi, 1
mov rsi, prompt
mov rdx, length
syscall ;print prompt
mov rax, 0
mov rdi, 0
mov rsi, text
mov rdx, textSize
syscall ;read text input from keyboard
mov rcx, rax ; rcx - character counter
mov rsi, text ; a pointer to the current character starting from the beginning.
****
exit:
mov rax, 60
mov rdi, 0
syscall
I need the program to read from and write to the files, but I can't find anywhere which syscalls has to be used and how they should be used to achieve these results. So, I am wondering if someone of you could help me. Thanks in advance.
Use system calls "open" and "close":
Open a file under 64-bit Linux:
rax = 2
rdi = pointer to NUL-terminated filename
rsi = something like O_WRONLY
rdx = file flags if creating a file (e.g. 0644 = rw-r--r--)
syscall
now rax contains the file hanle
Close a file:
rax = 3
rdi = file handle
syscall
Reading/writing from/to a file:
rax = 0 or 1 (like keyboard/screen in/output)
rdi = file handle (instead of 0/1)
...
;this program is only overwrite the result.txt
;to write it on bottom you need to lseek to the end
section .data
prompt db "Enter your text: ",10
length equ $ - prompt
text times 255 db 0
textsize equ $-text
fname db "result.txt",0
fd dq 0
thefox dq 0
global _start
section .text
_start:
mov al,1
mov dil,al
mov esi,prompt
mov dl,length
syscall
mov al,0
mov dil,al
mov rsi,text
mov rdx,textsize
syscall
mov [thefox],rax
mov rax,2
mov rdi,fname
mov rsi,0102o
mov rdx,0666o
syscall
mov [fd],rax
mov rdx,[thefox]
mov rsi,text
mov rdi,[fd]
mov rax,1
syscall
mov rdi,[fd]
mov rax,3
syscall
mov rax,60
syscall

Resources