Why is this Assembly Code not reading the same bytes over again but instead returns and End Of File? - linux

This is the code I have:
section .bss
bufflen equ 1024
buff: resb bufflen
whatread: resb 8
section .data
section .text
global main
main:
nop
read:
mov eax,3 ; Specify sys_read
mov ebx,0 ; Specify standard input
mov ecx,buff ; Where to read to...
mov edx,bufflen ; How long to read
int 80h ; Tell linux to do its magic
; Eax currently has the return value from linux system call..
add eax, 30h ; Convert number to ASCII digit
mov [whatread],eax ; Store how many byte reads info to memory at loc whatread
mov eax,4 ; Specify sys_write
mov ebx,1 ; Specify standart output
mov ecx,whatread ; Get the address of whatread in ecx
mov edx,4 ; number of bytes to be written
int 80h ; Tell linux to do its work
mov eax,3 ; Specify sys_read
mov ebx,0 ; Specify standard input
mov ecx,buff ; Where to read to...
mov edx,bufflen ; How long to read
int 80h ; Tell linux to do its magic
; Eax currently has the return value from linux system call..
add eax, 30h ; Convert number to ASCII digit
mov [whatread],eax ; Store how many byte reads info to memory at loc whatread
mov eax,4 ; Specify sys_write
mov ebx,1 ; Specify standart output
mov ecx,whatread ; Get the address of whatread in ecx
mov edx,4 ; number of bytes to be written
int 80h ; Tell linux to do its work
mov eax, 1;
mov ebx, 0;
int 80h
and I have a file called all.txt with contents:
61 62 63 0A 64 65 66 0A (abc - new line - def - new line)
Here is a sample run:
koray#koray-VirtualBox:~/asm/buffasm$ ./buff < all.txt
80koray#koray-VirtualBox:~/asm/buffasm$
So the first read of the code tries to read 1024 bytes from the file. The file itself has 8 bytes.. Then it prints 8 in the console which is fine I guess. But then, why does it print a '0' which means an end of file? I would expect it to read the same 8 bytes again? Why is it trying to read the next 1024 bytes?

There is no logic the program to read the same bytes again. They are already read and in the buffer. The input buffer (filled by the redirected file) is empty, so no more bytes can be read. That is why the sys_read ends with 0 read bytes.
The same logic will be if it was not STDIN, but a real file (of course, previously opened by sys_open).
BTW, returned count of 0 is actually what is called EOF.

Related

Segmentation fault in my assembly program

I am getting a segmentation fault from this simple starting program.
I am using Ubuntu 16.10 and kdbg for debugging. Affter reaching starting __int 80h__, it stops moving to the next line.
section .bss ; section containing uninitialized data
BUFFLEN equ 1024 ; length of buffer
Buff: resb BUFFLEN ; text buffer itself
section .data ; section containing initialzed data
section .text ; secttion containing code
global _start ; linker needs to find the entry point!
_start:
nop ; this no-op keeps gdb happy
; read buffer full of text form stdin:
read:
mov eax, 3 ; specify sys_read call
mov ebx, 0 ; specify file descriptor 0 : standard input
mov ecx, Buff ; pass offset of the buffer to read to
mov edx, BUFFLEN ; pass number of bytes to be read at one pass
int 80h ; call sys_read to fill the buffer
mov esi,eax ; copy sys_read return value for safekeeping
cmp eax, 0 ; if eax = 0 , sys_read reached EOF on stdin
je Done ; jump if Equal ( to o, form compare)
; set up the register for the process buffer step:
mov ecx, esi ; place the number of bytes read into ecx
mov ebp, Buff ; pace address of buffer into ebp
dec ebp ; adjust the count to offset
; go through the buffer and cnvert lowercase to uppercase characters:
Scan:
cmp byte [ebp+ecx], 61h ; test input char agaisnst lowercase 'a'
jb Next ; if Below 'a' in ASCII, not lowercase
cmp byte [ebp+ecx], 7Ah ; test against lowercase 'z'
ja Next
sub byte [ebx+ecx], 20h ; subtract 20h to give uppercase..
Next:
dec ecx ; Decrement counter
jnz Scan ; if characters reamin, loop back
; Write the buffer full of processed text to stdout:
Write:
mov eax,4 ; Specify sys_write call
mov ebx, 1 ; Specify file descriptor 1 : stdout
mov ecx, Buff ; pass the offset of the buffer
mov edx, esi ; pass the # of bytes of data in the buffer
int 80h ; make sys_write kernel call
jmp read ; loop back and load another buffer full
Done:
mov eax, 1 ; Code for Exit sys_call
mov ebx, 0 ; return code of Zero
int 80h
I used these commands:
nasm -f elf -g -F stabs uppercaser1.asm
ld -m elf_i386 -o uppercaser1 uppercaser1.o
./uppercaser < inputflie
I think this code is generally public so I am posting with that in mind.
You should use the code as a guide to understanding what may be wrong in your code; however, it does not conform to any coding standard so it is pointless just to copy and paste and turn it in as an assignment; assuming that is the case.
You will never increase your assembly programming skills by merely doing a copy/paste.
lsb_release -a
...
Description: Ubuntu 16.04.3 LTS
nasm -f elf32 -g uppercase1.s -o uppercase1.o && ld -m elf_i386 uppercase1.o -o uppercase1
section .bss
Buff resb 1
section .data
section .text
global _start
_start:
nop
Read:
mov eax, 3 ; read syscall
mov ebx, 0 ; stdin
mov ecx, Buff ; pass address of the buffer to read to
mov edx, 1 ; read one char or one byte
int 0x80 ;
cmp eax, 0 ; if syscall returns returns 0
je Exit ;
cmp byte [Buff], 0x61 ; lower case a
jb Write ; jump if byte is below a in ASCII chart
cmp byte [Buff], 0x7a ; lower case z
ja Write ; jump if byte is above z in ASCII chart
sub byte [Buff], 0x20 ; changes the value in the buffer to an uppercase char
Write:
mov eax, 4 ; write syscall
mov ebx, 1 ; stdout
mov ecx, Buff ; what to print
mov edx, 1 ; length is one byte - each char is a byte
int 0x80
jmp Read ; go back to Read
Exit:
mov eax, 1
mov ebx, 0
int 0x80
Sample output:
david#ubuntuserver00A:~/asm$ ./uppercase1 < uppercase1.s
SECTION .BSS
BUFF RESB 1
SECTION .DATA
SECTION .TEXT
GLOBAL _START
_START:
NOP
READ:
MOV EAX, 3 ; READ SYSCALL
MOV EBX, 0 ; STDIN
MOV ECX, BUFF ; PASS ADDRESS OF THE BUFFER TO READ TO
MOV EDX, 1 ; READ ONE CHAR OR ONE BYTE
INT 0X80 ;
CMP EAX, 0 ; IF SYSCALL RETURNS RETURNS 0
JE EXIT ;
CMP BYTE [BUFF], 0X61 ; LOWER CASE A
JB WRITE ; JUMP IF BYTE IS BELOW A IN ASCII CHART
CMP BYTE [BUFF], 0X7A ; LOWER CASE Z
JA WRITE ; JUMP IF BYTE IS ABOVE Z IN ASCII CHART
SUB BYTE [BUFF], 0X20 ; CHANGES THE VALUE IN THE BUFFER TO AN UPPERCASE CHAR
WRITE:
MOV EAX, 4 ; WRITE SYSCALL
MOV EBX, 1 ; STDOUT
MOV ECX, BUFF ; WHAT TO PRINT
MOV EDX, 1 ; LENGTH IS ONE BYTE - EACH CHAR IS A BYTE
INT 0X80
JMP READ ; GO BACK TO READ
EXIT:
MOV EAX, 1
MOV EBX, 0
INT 0X80

x86 - Converting a string from stdin to integer to use elsewhere

I am trying to make a program in x86 NASM that reads two integers from stdin and then does something with them (greatest common divisor, but thats not the question here). The question I have, is now that I have the two input integers, how do I go about turning them into actual integers to use as such? Here is the code I use to grab the strings of digits
SECTION .data
prompt: db "Enter a positive integer: "
plen: equ $-prompt
SECTION .bss
inbuf: resb 20
inbuf2: resb 20
SECTION .text
global _start
_start:
; Here I am grabbing the two integers to use in the function
nop
mov eax, 4 ; write
mov ebx, 1 ; to standard output
mov ecx, prompt ; the prompt string
mov edx, plen ; of length plen
int 80H ; interrupt with the syscall
mov eax, 3 ; read
mov ebx, 0 ; from standard input
mov ecx, inbuf ; into the input buffer
mov edx, 30 ; upto 30 bytes
int 80H ; interrupt with the syscall
mov eax, 4 ; write
mov ebx, 1 ; to standard output
mov ecx, prompt ; the prompt string
mov edx, plen ; of length plen
int 80H ; interrupt with the syscall
mov eax, 3 ; read
mov ebx, 0 ; from standard input
mov ecx, inbuf2 ; into the input buffer
mov edx, 30 ; upto 30 bytes
int 80H ; interrupt with the syscall

Why do I need to use [ ] (square brackets) when moving data from register to memory, but not when other way around?

This is the code I have and it works fine:
section .bss
bufflen equ 1024
buff: resb bufflen
whatread: resb 4
section .data
section .text
global main
main:
nop
read:
mov eax,3 ; Specify sys_read
mov ebx,0 ; Specify standard input
mov ecx,buff ; Where to read to...
mov edx,bufflen ; How long to read
int 80h ; Tell linux to do its magic
; Eax currently has the return value from linux system call..
add eax, 30h ; Convert number to ASCII digit
mov [whatread],eax ; Store how many bytes has been read to memory at loc **whatread**
mov eax,4 ; Specify sys_write
mov ebx,1 ; Specify standart output
mov ecx,whatread ; Get the address of whatread to ecx
mov edx,4 ; number of bytes to be written
int 80h ; Tell linux to do its work
mov eax, 1;
mov ebx, 0;
int 80h
Here is a simple run and output:
koray#koray-VirtualBox:~/asm/buffasm$ nasm -f elf -g -F dwarf buff.asm
koray#koray-VirtualBox:~/asm/buffasm$ gcc -o buff buff.o
koray#koray-VirtualBox:~/asm/buffasm$ ./buff
p
2koray#koray-VirtualBox:~/asm/buffasm$ ./buff
ppp
4koray#koray-VirtualBox:~/asm/buffasm$
My question is: What is with these 2 instructions:
mov [whatread],eax ; Store how many byte reads info to memory at loc whatread
mov ecx,whatread ; Get the address of whatread in ecx
Why the first one works with [] but the other one without?
When I try replacing the second line above with:
mov ecx,[whatread] ; Get the address of whatread in ecx
the executable will not run properly, it will not shown anything in the console.
Using brackets and not using brackets are basically two different things:
A bracket means that the value in the memory at the given address is meant.
An expression without a bracket means that the address (or value) itself is meant.
Examples:
mov ecx, 1234
Means: Write the value 1234 to the register ecx
mov ecx, [1234]
Means: Write the value that is stored in memory at address 1234 to the register ecx
mov [1234], ecx
Means: Write the value stored in ecx to the memory at address 1234
mov 1234, ecx
... makes no sense (in this syntax) because 1234 is a constant number which cannot be changed.
Linux "write" syscall (INT 80h, EAX=4) requires the address of the value to be written, not the value itself!
This is why you do not use brackets at this position!

How should I work with dynamically-sized input in NASM Assembly?

I'm trying to learn assembly with NASM on 64 bit Linux.
I managed to make a program that reads two numbers and adds them. The first thing I realized was that the program will only work with one-digit numbers (and results):
; Calculator
SECTION .data
msg1 db "Enter the first number: "
msg1len equ $-msg1
msg2 db "Enter the second number: "
msg2len equ $-msg2
msg3 db "The result is: "
msg3len equ $-msg3
SECTION .bss
num1 resb 1
num2 resb 1
result resb 1
SECTION .text
global main
main:
; Ask for the first number
mov EAX,4
mov EBX,1
mov ECX,msg1
mov EDX,msg1len
int 0x80
; Read the first number
mov EAX,3
mov EBX,1
mov ECX,num1
mov EDX,2
int 0x80
; Ask for the second number
mov EAX,4
mov EBX,1
mov ECX,msg2
mov EDX,msg2len
int 0x80
; Read the second number
mov EAX,3
mov EBX,1
mov ECX,num2
mov EDX,2
int 0x80
; Prepare to announce the result
mov EAX,4
mov EBX,1
mov ECX,msg3
mov EDX,msg3len
int 0x80
; Do the sum
; Store read values to EAX and EBX
mov EAX,[num1]
mov EBX,[num2]
; From ASCII to decimal
sub EAX,'0'
sub EBX,'0'
; Add
add EAX,EBX
; Convert back to EAX
add EAX,'0'
; Save the result back to the variable
mov [result],EAX
; Print result
mov EAX,4
mov EBX,1
mov ECX,result
mov EDX,1
int 0x80
As you can see, I reserve one byte for the first number, another for the second, and one more for the result. This isn't very flexible. I would like to make additions with numbers of any size.
How should I approach this?
First of all you are generating a 32-bit program, not a 64-bit program. This is no problem as Linux 64-bit can run 32-bit programs if they are either statically linked (this is the case for you) or the 32-bit shared libraries are installed.
Your program contains a real bug: You are reading and writing the "EAX" register from a 1-byte field in RAM:
mov EAX, [num1]
This will normally work on little-endian computers (x86). However if the byte you want to read is at the end of the last memory page of your program you'll get a bus error.
Even more critical is the write command:
mov [result], EAX
This command will overwrite 3 bytes of memory following the "result" variable. If you extend your program by additional bytes:
num1 resb 1
num2 resb 1
result resb 1
newVariable1 resb 1
You'll overwrite these variables! To correct your program you must use the AL (and BL) register instead of the complete EAX register:
mov AL, [num1]
mov BL, [num2]
...
mov [result], AL
Another finding in your program is: You are reading from file handle #1. This is the standard output. Your program should read from file handle #0 (standard input):
mov EAX, 3 ; read
mov EBX, 0 ; standard input
...
int 0x80
But now the answer to the actual question:
The C library functions (e.g. fgets()) use buffered input. Doing it like this would be a bit to complicated for the beginning so reading one byte at a time could be a possibility.
Thinking the way "how would I solve this problem using a high-level language like C". If you don't use libraries in your assembler program you can only use system calls (section 2 man pages) as functions (e.g. you cannot use "fgets()" but only "read()").
In your case a C program reading a number from standard input could look like this:
int num1;
char c;
...
num1 = 0;
while(1)
{
if(read(0,&c,1)!=1) break;
if(c=='\r' || c=='\n') break;
num1 = 10*num1 + c - '0';
}
Now you may think about the assembler code (I typically use GNU assembler, which has another syntax, so maybe this code contains some bugs):
c resb 1
num1 resb 4
...
; Set "num1" to 0
mov EAX, 0
mov [num1], EAX
; Here our while-loop starts
next_digit:
; Read one character
mov EAX, 3
mov EBX, 0
mov ECX, c
mov EDX, 1
int 0x80
; Check for the end-of-input
cmp EAX, 1
jnz end_of_loop
; This will cause EBX to be 0.
; When modifying the BL register the
; low 8 bits of EBX are modified.
; The high 24 bits remain 0.
; So clearing the EBX register before
; reading an 8-bit number into BL is
; a method for converting an 8-bit
; number to a 32-bit number!
xor EBX, EBX
; Load the character read into BL
; Check for "\r" or "\n" as input
mov BL, [c]
cmp BL, 10
jz end_of_loop
cmp BL, 13
jz end_of_loop
; read "num1" into EAX
mov EAX, [num1]
; Multiply "num1" with 10
mov ECX, 10
mul ECX
; Add one digit
sub EBX, '0'
add EAX, EBX
; write "num1" back
mov [num1], EAX
; Do the while loop again
jmp next_digit
; The end of the loop...
end_of_loop:
; Done
Writing decimal numbers with more digits is more difficult!

file read buffer is empty in nasm

I managed to build a NASM tutorial code for dealing with files. It outputs the contents of a file to stdout just fine, but when I try to access the data buffer, it contains just zeros. For example in the code below in middle loop EBX is always set to 0, when it should contain file bytes.
section .data
bufsize dw 1024
section .bss
buf resb 1024
section .text ; declaring our .text segment
global _start ; telling where program execution should start
_start: ; this is where code starts getting exec'ed
; get the filename in ebx
pop ebx ; argc
pop ebx ; argv[0]
pop ebx ; the first real arg, a filename
; open the file
mov eax, 5 ; open(
mov ecx, 0 ; read-only mode
int 80h ; );
; read the file
mov eax, 3 ; read(
mov ebx, eax ; file_descriptor,
mov ecx, buf ; *buf,
mov edx, bufsize ; *bufsize
int 80h ; );
mov ecx, 20
loop:
mov eax, 20
sub eax, ecx
mov ebx, [buf+eax*4]
loop loop
; write to STDOUT
mov eax, 4 ; write(
mov ebx, 1 ; STDOUT,
mov ecx, buf ; *buf
int 80h ; );
; exit
mov eax, 1 ; exit(
mov ebx, 0 ; 0
int 80h ; );
For example in the code below in middle loop EBX is always set to 0, when it should contain file bytes.
How are you determining this? (Running under a debugger, perhaps?)
Your code has an unfortunate bug:
; read the file
mov eax, 3 ; read(
mov ebx, eax ; file_descriptor,
You're overwriting EAX (which contains the file descriptor returned by the open syscall, if the open succeeded) with the value 3, before it gets moved to EBX as the file descriptor argument to read.
Normally, a process will start out with file descriptors 0, 1 and 2 assigned to stdin, stdout and stderr, and the first file descriptor that you explicitly open will be 3, so you'll get away with it!
But you might not be so lucky if you're running with a debugger. File descriptor 3 might be something else, and the read might fail (you don't check to see if the returned value is a negative error code), or read something completely unexpected...

Resources