`

assembly - record in file

阅读更多

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

 

------


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics