How to read and display the contents of a text file in nasm? - linux

I want to read and display the contents of a text file using nasm and Linux system calls. My text file is named "new.txt". I wrote the following code and am receiving no output on the terminal.
section .data
line db "This is George,",
db " and his first line.", 0xa, 0
len equ $ - line
line2 db "This is line number 2.", 0xa, 0
len2 equ $ - line2
filename: db 'ThisIsATestFile.txt', 0
section .bss
bssbuf: resb len ;any int will do here, even 0,
file: resb 4 ;since pointer is allocated anyway
global _start
section .text
_start:
; open file in read-only mode
mov eax, 5 ;sys_open file with fd in ebx
mov ebx, filename ;file to be opened
mov ecx, 0 ;O_RDONLY
int 80h
cmp eax, 0 ;check if fd in eax > 0 (ok)
jbe error ;can not open file
mov ebx, eax ;store new (!) fd of the same file
; read from file into bss data buffer
mov eax, 3 ;sys_read
mov ecx, bssbuf ;pointer to destination buffer
mov edx, len ;length of data to be read
int 80h
js error ;file is open but cannot be read
cmp eax, len ;check number of bytes read
jb close ;must close file first
; write bss data buffer to stderr
mov eax, 4 ;sys_write
push ebx ;save fd on stack for sys_close
mov ebx, 2 ;fd of stderr which is unbuffered
mov ecx, bssbuf ;pointer to buffer with data
mov edx, len ;length of data to be written
int 80h
pop ebx ;restore fd in ebx from stack
close:
mov eax, 6 ;sys_close file
int 80h
mov eax, 1 ;sys_exit
mov ebx, 0 ;ok
int 80h
error:
mov ebx, eax ;exit code
mov eax, 1 ;sys_exit
int 80h

When you write
jb close ;must close file first
you, in fact, are jumping to close, not calling it.
Once you reach close, you just exit after you close the file:
close:
mov eax, 6 ;sys_close file
int 80h
mov eax, 1 ;sys_exit
mov ebx, 0 ;ok
int 80h
Perhaps you want to either call close in some way (and then ret from close) or jump back to where you were to continue what you were doing?
Also, keep in mind that jbe is an unsigned comparison, so when you write:
cmp eax, 0 ;check if fd in eax > 0 (ok)
jbe error ;can not open file
You in fact will not detect negative numbers. Consider jle instead. (You can consult this handy resource about jumps to use.)

Related

NASM read file char by char

I am new to NASM and i have a project from school.
For start i need to know how to read char by char from .txt
after a long research this is my code:
section .text
global _start
_start:
;open the file
mov eax, 5 ;sys call open
mov ebx, file_name ;file name
mov ecx, 0 ;read only
mov edx, 0777 ; exe by all
int 0x80
mov [fd_in], eax
;read
loop:
mov eax, 3 ;sys call read
mov ebx, [fd_in] ;file descriptor
mov ecx, buff
mov edx, 1
int 0x80
cmp eax, 0 ;cmp EOF
je eof
;print
mov eax, 4 ;sys call write
mov ebx, 1 ;std out
mov ecx, buff
mov edx, 1
int 0x80
jmp loop
eof:
mov eax, 1
int 0x80
section .data
file_name db 'hey.txt'
section .bss
fd_in resb 1
buff resb 1
but it only print endlessly to fist char in my .txt file.
BTW in the .txt file it says "Hey my name is blah".
i would love any help or suggestions.
thanks.

Assembly on Linux: unexpected behaviour from Assembly [duplicate]

This question already has answers here:
In NASM labels next to each other in memory are printing both strings instead of first one
(1 answer)
How does $ work in NASM, exactly?
(2 answers)
Closed 4 years ago.
running the code below generates a file with Welcome to jj Shashwat as content. what i didn't get is why does it writes Shashwat at the end of the file, Shashwat is in a totally different variable. Any idea why does this happen?
section .text
global _start ;must be declared for using gcc
_start:
;create the file
mov eax, 8
mov ebx, file_name
mov ecx, 0777 ;read, write and execute by all
int 0x80 ;call kernel
mov [fd_out], eax
; close the file
mov eax, 6
mov ebx, [fd_out]
;open the file for reading
mov eax, 5
mov ebx, file_name
mov ecx, 2 ;for read/write access
mov edx, 0777 ;read, write and execute by all
int 0x80
mov [fd_out], eax
; write into the file
mov edx,len ;number of bytes
mov ecx, msg ;message to write
mov ebx, [fd_out] ;file descriptor
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
; close the file
mov eax, 6
mov ebx, [fd_out]
mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel
section .data
file_name db 'myfile.txt', 0
msg db 'Welcome to jj', 0
mgafter db ' Shashwat', 0
lntwo equ $-mgafter
len equ $-msg
section .bss
fd_out resb 1
fd_in resb 1
info resb 26
That's because you said len equ $-msg after defining both msg and msgafter, so len is set to the length of both msg and msgafter, making your write call write both strings. This is because len equ $-msg means “set len to be the difference between the current location ($) and the location of msg.”
To fix this, move the len equ $-msg line right after the definition of msg.

Create/Write to file x86 Linux

I have been trying to teach myself some 32-bit x86 (NASM). I am trying to have a user input a file name, open/create the file, then take a user's message and write that message to the file. I have gone through it in GDB and all syscalls returned correctly. After the program runs the file appears to be created improperly and nothing is written to it. I ahev seen some of the other question that are similar but my code seems to be nearly the same as their's so I can't seem to figure out what the heck is going on.
Here is my noob code:
global _start
section .data
fmsg: db "Enter Filename: ", 0
.len: equ $ - fmsg
umsg: db "Enter message: ", 0
.len: equ $ - umsg
buff: times 50 db 0 ;array for user string
.blen: equ $ - buff
fname: times 50 db 0 ;array for filename
.flen: equ $ - fname
;modes
O_RDONLY: db 0 ;read-only
O_WRONLY: db 1 ;wirte-only
O_RDWR: db 2 ;read and write
;flags
O_CREAT: dw 100o ;create file if file doesnt exists
O_TRUNC: dw 1000o ;truncate file
O_APPEND: dw 2000o ;append to file
section .bss
fd: resd 1 ;file descriptor
bret: resd 1 ;read buffer return value
fret: resd 1 ;read filename return value
tmp: resd 1 ;temp 4 byte variable
section .text
_start:
fprompt: ;Print prompt
mov eax, 0x4 ;syscall 4 - write()
mov ebx, 0x1 ;file desc 1 - stdout
mov ecx, fmsg ;print message
mov edx, fmsg.len ;length of message
int 80h ;syscall interupt
filein:
mov eax, 0x3 ;syscall 3 - read()
mov ebx, 0x0 ;file desc 0 - stdin
mov ecx, fname ;dst buffer
mov edx, fname.flen ;length of buffer
int 80h ;syscall interupt
mov [fret], eax ;save return value to file return variable
cmp eax, edx ;read max bytes or more?
jb fileopen ;jmp is bytes read < max
mov bl, [ecx+eax-1] ;grab last byte # last index before '\0'
cmp bl, 10 ;does it = '\n' ?
je clean1
inc DWORD [fret] ;len++
clean1: ;loop to clear excess input, if any
mov eax, 0x3 ;syscall 3 - read()
mov ebx, 0x0 ;file desc 0 - stdin
mov ecx, tmp ;temp buffer
mov edx, 0x1 ;read only 1 byte
int 80h ;;syscall interupt
test eax, eax ;EOF?
jz fileopen ;Yes, jump to pback
mov al, [tmp] ;put character into lower 8 bits of EAX
cmp al, 10 ;is it = to lf ?
jne clean1 ;no, jump to begining of loop
fileopen:
mov eax, 0x05
mov ebx, fname ;filename
or ecx, O_CREAT ;if it doesn't exist create the file
or ecx, O_TRUNC ;truncate
mov edx, O_WRONLY ;write only
int 80h ;syscall interupt
mov [fd], eax ;save file descripor
prompt2:
mov eax, 0x4 ;syscall 4 - write()
mov ebx, 0x1 ;file desc 1 - stdout
mov ecx, umsg ;print message
mov edx, umsg.len ;length of message
int 80h
userin:
mov eax, 0x3 ;syscall 3 - read()
mov ebx, 0x0 ;file desc 0 - stdin
mov ecx, buff ;dst buffer
mov edx, buff.blen ;length of buffer
int 80h ;syscall interupt
mov [bret], eax ;save return value to buff return variable
cmp eax, edx ;read max bytes or more?
jb writetofile ;jmp is bytes read < max
mov bl, [ecx+eax-1] ;grab last byte # last index before '\0'
cmp bl, 10 ;does it = '\n' ?
je clean2
inc DWORD [bret] ;len++
clean2: ;loop to clear excess input, if any
mov eax, 0x3 ;syscall 3 - read()
mov ebx, 0x0 ;file desc 0 - stdin
mov ecx, tmp ;temp buffer
mov edx, 0x1 ;read only 1 byte
int 80h ;syscall
test eax, eax ;EOF?
jz writetofile ;Yes, jump to pback
mov al, [tmp] ;put character into lower 8 bits of EAX
cmp al, 10 ;is it = to lf ?
jne clean2 ;no, jump to begining of loop
writetofile:
mov eax, 0x4 ;syscall 4 - write()
mov ebx, [fd] ;file desc 1 - stdout
mov ecx, buff ;print message
mov edx, [bret] ;length of message
int 80h ;syscall interupt
closefile:
mov eax, 0x6 ;syscall 6 - close()
mov ebx, [fd] ;file desc
int 80h ;syscall interupt
exit: ;return 0
mov eax, 1 ;syscall 1 - exit()
mov ebx, 0 ;return val
int 80h ;syscall interupt
Here is an example of what I get after running it:
The file "test.txt?" shows up and also shows up as an executable even though I set only read/write for the file. Even when I try to open it there is nothing there. Any thoughts? Also As I mentioned, i am new and teaching myself so if you have any good tips on improvement for other areas of the program please let me know! :)
We have multiple errors (or one big one) in the following three lines of code:
or ecx, O_CREAT ;if it doesn't exist create the file
or ecx, O_TRUNC ;truncate
mov edx, O_WRONLY ;write only
The problem:
What values do the registers ecx and edx have after these lines?
You perform two or operations with the ecx register but obviously it is not initialized at that moment!
This means that you can be sure that the bits representing O_CREAT and O_TRUNC (whatever these values mean - see below) are set but you don't know which values the other bits have.
The O_WRONLY bit should be set in ecx, not in edx. edx should contain the desired file mode instead.
Unfortunately there are two different types of assembler - I don't know which type NASM is of:
One type of assembler would interpret the first instruction as: or ecx, [O_CREAT]
The other type would interpret it as: or ecx, address_of(O_CREAT)
In the first case the instruction mov edx, O_WRONLY will read four bytes starting with the O_WRONLY byte into the edx register so edx will have the value 0x400201 (O_CREAT*0x10000+O_RDWR*0x100+O_WRONLY).
In the second case edx will contain the address of O_WRONLY rather than the value.
The value will be wrong in any case.
Storing your mode constants in memory is really inefficient, even if you were doing it right (which you aren't).
You could trace what system calls you're actually making by running strace ./writefile.
You use O_RDONLY: db 0 to store a byte of static data (in the read/write section instead of .rodata for some reason). Instead, you should define assembler constants with equ:
O_RDONLY equ 0
O_WRONLY equ 1
O_RDWR equ 2
O_CREAT equ 100o ;create file if file doesnt exists
O_TRUNC equ 1000o ;truncate file
O_APPEND equ 2000o ;append to file
Then you can write
mov ecx, O_CREAT | O_TRUNC | O_WRONLY
mov edx, 0777o ; mode is the permission bits if open() creates the file
See the open(2) man page to learn how its args work.
The assembler will do the OR for you at assemble time, instead of having the CPU do 2 loads from memory.
What you actually wrote:
or ecx, O_CREAT ;if it doesn't exist create the file
or ecx, O_TRUNC ;truncate
assembles to two or ecx, imm32 instructions, which OR the addresses of the two dw locations into the original value of ecx. If you had written
movzx ecx, word [O_CREAT]
or cx, [O_TRUNC]
or cx, [O_WRONLY]
your code would have worked, but that would be really silly. (And on some CPUs cause a partial-register stall when something reads the full ecx after you wrote only the low 16 bits.)
If you'd written or ecx, [O_TRUNC], it would do a 32-bit load, so you'd effectively be doing ecx |= (2000o << 16) | 1000o. i.e. the word stored at O_APPEND would be ORed into the high 16 bits of ECX, where it may have a different meaning.
Similarly, mov edx, O_WRONLY assembles to mov edx, imm32, where the address is the immediate. This is why you ended up with weird garbage for the file mode (including the sticky bit set).
Use a debugger.

NASM, read a file and print the content

I have this piece of code in NASM (for linux) that supposed to open an existing file, read it and print the content on the screen, but does not work, can someone tell me what am I doing wrong?(hello.txt is the name of the file)
section .data
file db "./hello.txt", 0
len equ 1024
section .bss
buffer: resb 1024
section .text
global _start
_start:
mov ebx, [file] ; name of the file
mov eax, 5
mov ecx, 0
int 80h
mov eax, 3
mov ebx, eax
mov ecx, buffer
mov edx, len
int 80h
mov eax, 4
mov ebx, 1
mov ecx, buffer
mov edx, len
int 80h
mov eax, 6
int 80h
mov eax, 1
mov ebx, 0
int 80h
mov ebx, [file] ; name of the file
mov eax, 5
mov ecx, 0
int 80h
Is wrong. Loose the square brackets around file. You are passing the file name instead of a pointer to the filename.
mov ebx, file ; const char *filename
mov eax, 5
mov ecx, 0
int 80h
I see here a lot of mistakes, in order:
mov ebx, [file] ; name of the file
mov eax, 5
mov ecx, 0
int 80h
Here, as said, u must lose square brackets (because the function needs a pointer, not a value)
mov eax, 3
mov ebx, eax
mov ecx, buffer
mov edx, len
int 80h
Here, u must save the file descriptor from eax, before writing there value 3, else u just loose it
mov eax, 4
mov ebx, 1
mov ecx, buffer
mov edx, len
int 80h
Well. Here u using ebx register, so better way is to save file descriptor in memory. And for display, you take 1024 bytes from buffer, which is not correct. After reading from the file, the eax register will contain the number of characters read, so after reading from the file it will be better to store the value from the eax register in edx
mov eax, 6
int 80h
Again. U close the file, but ebx contains dirt, although it must contain a file descriptor
Correct code must look like this:
section .data
file db "text.txt",0 ;filename ends with '\0' byte
section .bss
descriptor resb 4 ;memory for storing descriptor
buffer resb 1024
len equ 1024
section .start
global _start
_start:
mov eax,5 ;open
mov ebx,file ;filename
mov ecx,0 ;read only
int 80h ;open filename for read only
mov [descriptor],eax ;storing the descriptor
mov eax,3 ;read from file
mov ebx,[descriptor] ;your file descriptor
mov ecx,buffer ;read to buffer
mov edx,len ;read len bytes
int 80h ;read len bytes to buffer from file
mov edx,eax ;storing count of readed bytes to edx
mov eax,4 ;write to file
mov ebx,1 ;terminal
mov ecx,buffer ;from buffer
int 80h ;write to terminal all readed bytes from buffer
mov eax,6 ;close file
mov ebx,[descriptor] ;your file descriptor
int 80h ;close your file
mov eax,1
mov ebx,0
int 80h
This is not a perfect code, but it should work

NASM loop over bytes

Currently I'm trying to loop over every single byte in a buffer (read from a file) and compare it to see if any of them is a whitespace, and write them to STDOUT. For some reason the program compiles and runs fine, but produces zero output.
section .data
bufsize dw 1024
section .bss
buf resb 1024
section .text
global _start
_start:
; open the file provided form cli in read mode
mov edi, 0
pop ebx
pop ebx
pop ebx
mov eax, 5
mov ecx, 0
int 80h
; write the contents in to the buffer 'buf'
mov eax, 3
mov ebx, eax
mov ecx, buf
mov edx, bufsize
int 80h
; write the value at buf+edi to STDOUT
mov eax, 4
mov ebx, 1
mov ecx, [buf+edi]
mov edx, 1
int 80h
; if not equal to whitespace, jump to the loop
cmp byte [buf+edi], 0x20
jne loop
loop:
; increment the loop counter
add edi, 1
mov eax, 4
mov ebx, 1
mov ecx, [buf+edi]
int 80h
; compare the value at buf+edi with the HEX for whitespace
cmp byte [buf+edi], 0x20
jne loop
; exit the program
mov eax, 1
mov ebx, 0
int 80h
The main problem was that I didn't given the address of bufsize ([bufsize]), also the loops had some problems.
Here's the fixed version, thanks everyone for your input.
section .data
bufsize dd 1024
section .bss
buf: resb 1024
section .text
global _start
_start:
; open the file provided form cli in read mode
mov edi, 0
pop ebx
pop ebx
pop ebx
mov eax, 5
mov ecx, 0
int 80h
; write the contents in to the buffer 'buf'
mov eax, 3
mov ebx, eax
mov ecx, buf
mov edx, [bufsize]
int 80h
; write the value at buf+edi to STDOUT
; if equal to whitespace, done
loop:
cmp byte [buf+edi], 0x20
je done
mov eax, 4
mov ebx, 1
lea ecx, [buf+edi]
mov edx, 1
int 80h
; increment the loop counter
add edi, 1
jmp loop
done:
; exit the program
mov eax, 1
mov ebx, 0
int 80h

Resources