Page 1 of 1

NASM - Linux Dynamic Memory Allocation/Read file

#1 GunnerInc  Icon User is offline

  • "Hurry up and wait"
  • member icon




Reputation: 856
  • View blog
  • Posts: 2,246
  • Joined: 28-March 11

Post icon  Posted 28 July 2012 - 03:16 PM

Here I will show how to Allocate memory as needed. Sure, we could create a buffer in the .bss section that hopefully is large enough, but I don't like that. We will learn how to get the size of a file, allocate memory to read the file contents to, close the file and "deallocate" the memory.

We will be using the following System Calls:
sys_read

Quote

ssize_t read(int fd, void *buf, size_t count)

ebx = file descriptor
ecx = pointer to buffer
edx = buffer size
eax = system call - sys_read
On return, eax contains bytes read


sys_write

Quote

ssize_t sys_write(unsigned int fd, const char * buf, size_t count)

ebx = file descriptor
ecx = pointer to buffer
edx = count of characters to write
eax = system call - sys_write
On return, eax contains bytes written


sys_open

Quote

int sys_open(const char *pathname, int flags, mode_t mode)

ebx = pointer to NULL terminated filename
ecx = flags
edx = mode
eax = system call - sys_open
On return, eax contains the file descriptor


sys_close

Quote

int sys_close(int fd)

ebx = file descriptor
eax == system call - sys_close
On return, eax contains 0 on success


sys_newstat

Quote

int sys_newstat(const char *path, struct stat *buf)

ebx = pointer to NULL terminated filename
ecx = pointer to stat structure
eax = system call - sys_stat
On return, eax contains 0 on success


sys_brk

Quote

int sys_brk(void *addr)

ebx = 0 to get address of Program Break (end of .bss section)
ebx = new address of Program Break to allocate memory
eax = system call - sys_brk
On return, eax contains address of Program Break


You will have to look at the man pages for the system call to see what gets returned on error. I do no error checking here, you will have to add your own.

We will use those system calls to:
  • Get the size of a file (a test file containing 8053 bytes of Lorem Ipsum)
  • Get the end of the program break and extend it by 8053 bytes.
  • Open test file
  • Read contents to temp buffer created with sys_brk
  • Write contents to terminal
  • Close file
  • Set program break back to original.


sys_exit        equ     1
sys_read        equ     3
sys_write       equ     4
sys_open		equ		5
sys_close		equ		6
sys_brk			equ		45
sys_newstat		equ		106

O_RDONLY		equ		0
O_WRONLY		equ		1
O_RDWR			equ		2

stdin           equ     0
stdout          equ     1
stderr          equ     2

struc STAT        
    .st_dev:        resd 1       
    .st_ino:        resd 1    
    .st_mode:       resw 1    
    .st_nlink:      resw 1    
    .st_uid:        resw 1    
    .st_gid:        resw 1    
    .st_rdev:       resd 1        
    .st_size:       resd 1    
    .st_blksize:    resd 1    
    .st_blocks:     resd 1    
    .st_atime:      resd 1    
    .st_atime_nsec: resd 1    
    .st_mtime:      resd 1    
    .st_mtime_nsec: resd 1
    .st_ctime:      resd 1    
    .st_ctime_nsec: resd 1    
    .unused4:       resd 1    
    .unused5:       resd 1    
endstruc

%define sizeof(x) x %+ _size

SECTION     .data
szFile    	db      "TEST", 0
File_Len    equ     $-szFile


SECTION     .bss
stat		resb	sizeof(STAT)
Org_Break   resd    1
TempBuf		resd	1

SECTION     .text
global      _start
    
_start:
	;~ Get file size
	mov		ebx, szFile
	mov		ecx, stat
	mov		eax, sys_newstat
	int		80H

	;~ Get end of bss section
	xor		ebx, ebx
	mov		eax, sys_brk
	int		80H
	mov		[Org_Break], eax
	mov		[TempBuf], eax
	push	eax
	
	; extend it by file size
	pop		ebx
	add		ebx, dword [stat + STAT.st_size]
	mov		eax, sys_brk
	int		80H
	
	;~ open file
	mov		ebx, szFile
	mov		ecx, O_RDONLY
	xor		edx, edx
	mov		eax, sys_open
	int		80H
    xchg    eax, esi
	
	;~ read in file to buffer
	mov     ebx, esi
	mov		ecx, [TempBuf]
	mov		edx, dword [stat + STAT.st_size]
	mov		eax, sys_read
	int		80H

	;~ display to terminal
	mov		ebx, stdout
	mov		ecx, [TempBuf]
	mov		edx, eax
	mov		eax, sys_write
	int		80H
	
	;~ close file
	mov		ebx, esi 
	mov		eax, sys_close
	int		80H

	;~ "free" memory
	mov     ebx, [Org_Break]
    mov     eax, sys_brk
    int     80H
   	
Exit:  
    mov     eax, sys_exit
    xor     ebx, ebx
    int     80H


Ok, so the first thing we need to do before creating a buffer is to get the size or the file we are going to read. There are a few ways of doing this, I chose sys_newstat.
	;~ Get file size
	mov		ebx, szFile
	mov		ecx, stat
	mov		eax, sys_newstat
	int		80H

This is a straight forward call, put the pointer to the file name to "stat" in ebx, this could be an absolute path and file name, or just a the file name if it is in the directory of the program (relative path). Next in ecx we put the address of the buffer to hold the "stat" info. If all went well, eax should contain 0 and our stat buffer will contain info, otherwise it will contain ERRNO.

Now, the file size will be at offset 20 from our stat pointer base.
We could access the file size like this: mov eax, dword [stat + 20] but I am all for writing code that is "self documenting" so we will access the file size like this: mov eax, [stat + STAT.st_size]

Now that we have the file size, we need to know the address of the end of our .bss section, this is called the Program Break and we get this by calling sys_brk:
	;~ Get end of bss section
	xor		ebx, ebx
	mov		eax, sys_brk
	int		80H
	mov		[Org_Break], eax
	mov		[TempBuf], eax
	push	eax

To get the address, we set ebx to zero, and on return eax will contain the address of the Program Break. Here I save it to Org_Break so I set the break back to this later. I save it to TempBuf, so I can, well, use this new buffer I will create. After that, I push the address of the break contained in eax onto the stack.

So, how do we extend this break to create a buffer? We take the address of the Program Break, and add to it the number of bytes we want the buffer to be. This will give us a new address that the system will set the new Program Break to:
	; extend it by file size
	pop		ebx
	add		ebx, dword [stat + STAT.st_size]
	mov		eax, sys_brk
	int		80H

What we do here is, pop the address we pushed onto the stack with push eax and put it in ebx - pop ebx, next we add the file size to this address and call sys_brk again.

Lets make sure it works:

Attached Image

Hmm seems good, let's change the file contents, save it and try again:

Attached Image

Great, it works! Now that we have a buffer to hold the file contents, we need to open the file, read the file, display the contents to the terminal, and close the file.

	; open file
	mov		ebx, szFile
	mov		ecx, O_RDONLY
	xor		edx, edx
	mov		eax, sys_open
	int		80H
    xchg    eax, esi

Another "simple" call, ebx needs a pointer to the file to open, the same rule for sys_stat filename apply here. ecx is the access mode for the file - it MUST contain one of the following:
O_RDONLY, O_WRONLY, or O_RDWR, you could also bitwise OR file creation and file status flags here.
edx contains the file permissions. This is ignored if O_CREAT is not specified in the flags parameter.

If the file was opened eax will contain the File Descriptor. Here we save it to esi since this is one of the few registers that are saved across system calls.

Now we can read the file contents into our new buffer:
	; read in file to buffer
	mov     ebx, esi
	mov		ecx, [TempBuf]
	mov		edx, dword [stat + STAT.st_size]
	mov		eax, sys_read
	int		80H

ebx is the File Descriptor of the open file to read from, ecx is the address of the buffer to hold the file contents, edx is the number of bytes to read, here we will read the whole file into our buffer. On return, eax will contain the number of bytes read.

Now let's display 8053 bytes of Lorem Ipsum to the terminal:
	; display to terminal
	mov		ebx, stdout
	mov		ecx, [TempBuf]
	mov		edx, eax
	mov		eax, sys_write
	int		80H

ebx - let's write to terminal, ecx is a pointer to the text to dispaly, edx is the number of bytes to display.

Now close the file:
	; close file
	mov		ebx, esi 
	mov		eax, sys_close
	int		80H

ebx is the File Descriptor to close, it is saved in esi from earlier.

Now "Free" the memory we allocated:
	; "free" memory
	mov     ebx, [Org_Break]
    mov     eax, sys_brk
    int     80H

This is setting the Program Break back to what it was when the program started.

And the sample output:

Attached Image

A TODO for you:
Take what you learned from NASM - Linux Getting command line parameters and modify the code in this tutorial to accept the file to open from the command line.

Attached File(s)



Is This A Good Question/Topic? 3
  • +

Replies To: NASM - Linux Dynamic Memory Allocation/Read file

#2 danzar  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 8
  • View blog
  • Posts: 108
  • Joined: 10-December 08

Posted 29 July 2012 - 08:59 AM

Thank you! I am learning a lot from your NASM tut's
Was This Post Helpful? 1
  • +
  • -

#3 GunnerInc  Icon User is offline

  • "Hurry up and wait"
  • member icon




Reputation: 856
  • View blog
  • Posts: 2,246
  • Joined: 28-March 11

Posted 29 July 2012 - 04:46 PM

Well, that is good to know! That is one of the reasons I do it!
Was This Post Helpful? 1
  • +
  • -

Page 1 of 1