欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

assembly - record in file

程序员文章站 2022-06-05 18:37:06
...

maintain record in file using assembly,

 

------

table & record

 

design a simple table which has several fields with fixed-length,

the records are saved in file permanently,


------

operations on records

 

insert:

    add a record to end of file,

select:

    get a record by line number, and print it,


------

code

 

 

files list:

* record_insert.s

      insert a new record

* record_select.s

      select & print a record by line number(start from 0)

* record_const.s

      record relative constants,

* linux_const.s

      linux relative constants,

* string_util.s

string util functions,


 

record_insert.s:

 

# record operations - insert

.include "record_const.s"
.include "linux_const.s"
.include "string_util.s"

.section .data
.equ ST_ARGC, 4
.equ ST_ARGV_0, 8	# program path
.equ ST_ARGV_1, 12	# output file path
.equ ST_ARGV_2, 16	# field - id
.equ ST_ARGV_3, 20	# field - name
.equ ST_ARGV_4, 24	# field - age
.equ ST_ARGV_5, 28	# field - email
.equ ST_ARGV_6, 32	# field - address
.equ ST_RESERVE_SIZE, 4
.equ ST_FD_OUT, -4

.section .bss
.equ BUF_SIZE, RECORD_SIZE_FULL		# last byte is '\n'
.lcomm BUF_DATA, BUF_SIZE

.section .text
.globl _start

_start:
pushl %ebp
movl %esp, %ebp
subl $ST_RESERVE_SIZE, %esp	# reserve stack

prepare_buf:
pushl ST_ARGV_2(%ebp)		# id
call str_to_int
addl $4, %esp
movl $BUF_DATA, %ebx
movl %eax, RECORD_ID(%ebx)
pushl $RECORD_SIZE_NAME		# name
movl $BUF_DATA+RECORD_NAME, %ebx
pushl %ebx
pushl ST_ARGV_3(%ebp)
call str_copy
addl $12, %esp
pushl ST_ARGV_4(%ebp)		# age
call str_to_int
addl $4, %esp
movl $BUF_DATA, %ebx
movl %eax, RECORD_AGE(%ebx)
pushl $RECORD_SIZE_EMAIL	# email
movl $BUF_DATA+RECORD_EMAIL, %ebx
pushl %ebx
pushl ST_ARGV_5(%ebp)
call str_copy
addl $12, %esp
pushl $RECORD_SIZE_ADDRESS	# address
movl $BUF_DATA+RECORD_ADDRESS, %ebx
pushl %ebx
pushl ST_ARGV_6(%ebp)
call str_copy
addl $12, %esp

open_file:
open_file_append:
movl $SYS_OPEN, %eax
movl ST_ARGV_1(%ebp), %ebx
movl $MODE_WRONLY_APPEND, %ecx
movl $0644, %edx
int $LINUX_SYSCALL
movl %eax, ST_FD_OUT(%ebp)
cmpl $0, %eax
jg write_record

open_file_create:		# output file not exists, close it first, then create it
movl $SYS_CLOSE, %eax
movl ST_FD_OUT(%ebp), %ebx
int $LINUX_SYSCALL
movl $SYS_OPEN, %eax
movl ST_ARGV_1(%ebp), %ebx
movl $MODE_WRONLY_TRUNC, %ecx
int $LINUX_SYSCALL
movl %eax, ST_FD_OUT(%ebp)
cmpl $0, %eax
jl exit				# can't open/create output file, exit

write_record:
movl $BUF_DATA, %eax
addl $BUF_SIZE, %eax
movb $END_OF_LINE, -1(%eax)	# prepare last char of record
movl $SYS_WRITE, %eax
movl ST_FD_OUT(%ebp), %ebx
movl $BUF_DATA, %ecx
movl $BUF_SIZE, %edx
int $LINUX_SYSCALL

close_file:
movl $SYS_CLOSE, %eax
movl ST_FD_OUT(%ebp), %ebx
int $LINUX_SYSCALL

exit:
movl %ebp, %esp
popl %ebp
movl $SYS_EXIT, %eax
int $LINUX_SYSCALL

 

record_select.s:

 

# record operations - select & print record by line number

.include "record_const.s"
.include "linux_const.s"
.include "string_util.s"

.section .data
.equ ST_ARGC, 4
.equ ST_ARGV_0, 8	# program path
.equ ST_ARGV_1, 12	# input file path
.equ ST_ARGV_2, 16	# field - line number string, start from 0
.equ ST_RESERVE_SIZE, 4
.equ ST_LINE_NUM, -4	# converted line number
.LC0:
	.string "%10s:\t%s\n"
.LC1:
	.string "%10s:\t%d\n"
.LC2:
	.string "line %d not exists\n"

.section .bss
.equ BUF_SIZE, RECORD_SIZE_FULL		# last byte is '\n'
.lcomm BUF_DATA, BUF_SIZE

.section .text
.globl _start

_start:
pushl %ebp
movl %esp, %ebp
subl $ST_RESERVE_SIZE, %esp	# reserve stack
pushl ST_ARGV_2(%ebp)
call str_to_int
addl $4, %esp
movl %eax, ST_LINE_NUM(%ebp)
imull $RECORD_SIZE_FULL, %eax
pushl %eax		# record offset, TODO figure this according to command-line param
pushl ST_ARGV_1(%ebp)
call load_record
addl $8, %esp
cmpl $0, %eax
je line_not_exists	# specified line not exists
pushl $BUF_DATA
call print_record
addl $4, %esp
jmp exit

line_not_exists:
pushl ST_LINE_NUM(%ebp)
pushl $.LC2
call printf
addl $8, %esp

exit:
movl %ebp, %esp
popl %ebp
movl $SYS_EXIT, %eax
int $LINUX_SYSCALL

# function - load a record into buffer
# params
#	@param 0: file path
#	@param 1: record offset(byte) in file
# storage
# 
# return: actual read size(byte), stored in %eax
# 
.type load_record, @function
load_record:
.equ ST_LOADRECORD_PARAM_FILE_PATH, 8
.equ ST_LOADRECORD_PARAM_OFFSET, 12
.equ ST_LOADRECORD_RESERVE_SIZE, 8
.equ ST_LOADRECORD_FD_IN, -4
.equ ST_LOADRECORD_ACTUAL_READ_SIZE, -8
pushl %ebp
movl %esp, %ebp
subl $ST_LOADRECORD_RESERVE_SIZE, %esp	# reserve stack
load_record_open_file:
movl $SYS_OPEN, %eax
movl ST_LOADRECORD_PARAM_FILE_PATH(%ebp), %ebx
movl $MODE_RDONLY, %ecx
movl $0644, %edx
int $LINUX_SYSCALL
movl %eax, ST_LOADRECORD_FD_IN(%ebp)
load_record_seek:
movl $SYS_SEEK, %eax
movl ST_LOADRECORD_FD_IN(%ebp), %ebx
movl ST_LOADRECORD_PARAM_OFFSET(%ebp), %ecx
movl $0, %edx
int $LINUX_SYSCALL
load_record_read:
movl $SYS_READ, %eax
movl ST_LOADRECORD_FD_IN(%ebp), %ebx
movl $BUF_DATA, %ecx
movl $BUF_SIZE, %edx
int $LINUX_SYSCALL
movl %eax, ST_LOADRECORD_ACTUAL_READ_SIZE(%ebp)
load_record_close_file:
movl $SYS_CLOSE, %eax
movl ST_LOADRECORD_FD_IN(%ebp), %ebx
int $LINUX_SYSCALL

load_record_end:
movl ST_LOADRECORD_ACTUAL_READ_SIZE(%ebp), %eax
movl %ebp, %esp
popl %ebp
ret

# function - print record to stdout
# params
#	@param 0: buf_data, start address of buffer
# storage
# 
# return: 
# 
.type print_record, @function
.equ ST_PRINTRECORD_PARAM_BUF_DATA, 8
.equ ST_PRINTRECORD_RESERVE_SIZE, 12
.equ ST_PRINTRECORD_FIELD, -4		# printf param - string -> field address in buffer, int -> value
.equ ST_PRINTRECORD_TITLE, -8		# printf param - field title
.equ ST_PRINTRECORD_STRING, -12	# printf param - string
print_record:
pushl %ebp
movl %esp, %ebp
subl $ST_PRINTRECORD_RESERVE_SIZE, %esp	# reserve stack
movl ST_PRINTRECORD_PARAM_BUF_DATA(%ebp), %edx		# print id
movl RECORD_ID(%edx), %ecx
movl %ecx, ST_PRINTRECORD_FIELD(%ebp)
movl $RECORD_FIELD_TITLE_ID, ST_PRINTRECORD_TITLE(%ebp)
movl $.LC1, ST_PRINTRECORD_STRING(%ebp)
call printf
movl ST_PRINTRECORD_PARAM_BUF_DATA(%ebp), %edx		# print name
addl $RECORD_NAME, %edx
movl %edx, ST_PRINTRECORD_FIELD(%ebp)
movl $RECORD_FIELD_TITLE_NAME, ST_PRINTRECORD_TITLE(%ebp)
movl $.LC0, ST_PRINTRECORD_STRING(%ebp)
call printf
movl ST_PRINTRECORD_PARAM_BUF_DATA(%ebp), %edx		# print age
movl RECORD_AGE(%edx), %ecx
movl %ecx, ST_PRINTRECORD_FIELD(%ebp)
movl $RECORD_FIELD_TITLE_AGE, ST_PRINTRECORD_TITLE(%ebp)
movl $.LC1, ST_PRINTRECORD_STRING(%ebp)
call printf
movl ST_PRINTRECORD_PARAM_BUF_DATA(%ebp), %edx		# print email
addl $RECORD_EMAIL, %edx
movl %edx, ST_PRINTRECORD_FIELD(%ebp)
movl $RECORD_FIELD_TITLE_EMAIL, ST_PRINTRECORD_TITLE(%ebp)
movl $.LC0, ST_PRINTRECORD_STRING(%ebp)
call printf
movl ST_PRINTRECORD_PARAM_BUF_DATA(%ebp), %edx		# print address
addl $RECORD_ADDRESS, %edx
movl %edx, ST_PRINTRECORD_FIELD(%ebp)
movl $RECORD_FIELD_TITLE_ADDRESS, ST_PRINTRECORD_TITLE(%ebp)
movl $.LC0, ST_PRINTRECORD_STRING(%ebp)
call printf

print_record_end:
movl %ebp, %esp
popl %ebp
ret
 

record_const.s:

 

# record constants, include field size/offset/.. ,
# 
# end of each string field is 0, which takes 1 byte,
# each int field is 4 byte,
# end of each record is '\n', which takes 1 byte,

.equ RECORD_SIZE_ID, 4
.equ RECORD_SIZE_NAME, 40
.equ RECORD_SIZE_AGE, 4
.equ RECORD_SIZE_EMAIL, 60
.equ RECORD_SIZE_ADDRESS, 252
.equ RECORD_SIZE_EXTRA, 1	# extra size, 1 byte for a new line char '\n' at the end of record,
# total size of record, = sum of all fields' size
.equ RECORD_SIZE, RECORD_SIZE_ID+RECORD_SIZE_NAME+RECORD_SIZE_AGE+RECORD_SIZE_EMAIL+RECORD_SIZE_ADDRESS
.equ RECORD_SIZE_FULL, RECORD_SIZE+RECORD_SIZE_EXTRA
# offset address of record fields, = previous_field_offset + previous_field_size, of cause the first field has offset = 0
.equ RECORD_ID, 0
.equ RECORD_NAME, RECORD_ID+RECORD_SIZE_ID
.equ RECORD_AGE, RECORD_NAME+RECORD_SIZE_NAME
.equ RECORD_EMAIL, RECORD_AGE+RECORD_SIZE_AGE
.equ RECORD_ADDRESS, RECORD_EMAIL+RECORD_SIZE_EMAIL
# record filed title
.section .data
RECORD_FIELD_TITLE_ID:
	.string "id"
RECORD_FIELD_TITLE_NAME:
	.string "name"
RECORD_FIELD_TITLE_AGE:
	.string "age"
RECORD_FIELD_TITLE_EMAIL:
	.string "email"
RECORD_FIELD_TITLE_ADDRESS:
	.string "address"
 

linux_const.s:

 

#Common Linux Definitions
#System Call Numbers
.equ SYS_EXIT, 1
.equ SYS_READ, 3
.equ SYS_WRITE, 4
.equ SYS_OPEN, 5
.equ SYS_CLOSE, 6
.equ SYS_SEEK, 19
.equ SYS_BRK, 45
#System Call Interrupt Number
.equ LINUX_SYSCALL, 0x80
# file open mode
.equ MODE_RDONLY, 0
.equ MODE_WRONLY, 0101
.equ MODE_WRONLY_TRUNC, 03101
.equ MODE_WRONLY_APPEND, 02001
#Standard File Descriptors
.equ STDIN, 0
.equ STDOUT, 1
.equ STDERR, 2
#Common Status Codes
.equ END_OF_FILE, 0
# string
.equ END_OF_STRING, 0
.equ END_OF_LINE, '\n'
 

string_util.s:

 

# string utils

.section .text
# functionn - convert string to int
# params
#       @param 0: start address of string
# storage
#       %eax:   result
#       %ebx:   number of each char
#       %ecx:   start address of string
#       %edi:   index
# 
# return: converted int value, stored in %eax
# 
.type str_to_int, @function
.equ ST_STRTOINT_PARAM_STR, 8
str_to_int:
pushl %ebp
movl %esp, %ebp
movl $0, %eax
movl ST_STRTOINT_PARAM_STR(%ebp), %ecx
movl $0, %edi

str_to_int_loop:
movzbl (%ecx, %edi, 1), %ebx
cmpl $END_OF_STRING, %ebx
je str_to_int_end
subl $0x30, %ebx
imull $0xa, %eax
addl %ebx, %eax
incl %edi
jmp str_to_int_loop

str_to_int_end:
movl %ebp, %esp
popl %ebp
ret

# function - copy a string, from one location to another in memory
# params
#       @param 0: start address of source str
#       @param 1: start address of target str
#       @param 2: max size(byte) limit of str(include '\0'),
# storage
#       %eax: start address of source str
#       %ebx: start address of target str
#       %cl:  current char
#       %edx: str max size limit
#       %edi: index
# 
# return: actual size(byte) of copyed str, stored in %eax
# 
.type str_copy, @function
str_copy:
.equ ST_READPARAMSTR_PARAM_SOURCE, 8
.equ ST_READPARAMSTR_PARAM_TARGET, 12
.equ ST_READPARAMSTR_PARAM_MAX_SIZE, 16
pushl %ebp
movl %esp, %ebp
movl ST_READPARAMSTR_PARAM_SOURCE(%ebp), %eax
movl ST_READPARAMSTR_PARAM_TARGET(%ebp), %ebx
movl ST_READPARAMSTR_PARAM_MAX_SIZE(%ebp), %edx
decl %edx
movl $0, %edi

str_copy_loop:
cmpl %edx, %edi
jl read_param_char
read_param_limit_reach:                 # reach field size limit
movb $END_OF_STRING, (%ebx,%edi,1)
incl %edi
jmp str_copy_end
read_param_char:                        # read a char
movzbl (%eax,%edi,1), %ecx
movb %cl, (%ebx,%edi,1)
incl %edi
cmpb $END_OF_STRING, %cl
je str_copy_end
jmp str_copy_loop

str_copy_end:
movl %edi, %eax
movl %ebp, %esp
popl %ebp
ret

 

 

------

how to use

 

environment:

      hardware: x86 architecture,

      software: linux(ubuntu 10.04 LTS), gcc

 

compile:

      as -gstabs record_insert.s -o a; ld a -o insert.out -lc -dynamic-linker /lib/ld-linux.so.2

      as -gstabs record_select.s -o a; ld a -o select.out -lc -dynamic-linker /lib/ld-linux.so.2

 

insert:

      ./insert.out /tmp/a.txt 4 monica 26 friends_monica@gmail.com "USA NK, central park"

 

select:

      ./select.out /tmp/a.txt 0

 

------