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
------
推荐阅读
-
PHP fopen()和 file_get_contents()应用与差异介绍
-
Linux使用vim编辑文件保存时报E514:write error (file system full?)问题解决
-
File类---Day28
-
PHP下通过file_get_contents的代理使用方法
-
php中file_get_content 和curl以及fopen 效率分析
-
为PHP安装imagick时出现Cannot locate header file MagickWand.h错误的解决方法
-
python升级带来的yum异常(解决错误File "/usr/bin/yum", line 30 except KeyboardInterrupt, e:)
-
Android数据持久化之File机制分析
-
详解no input file specified 三种解决方法
-
浅谈Android Studio 3.0 工具新特性的使用 Android Profiler 、Device File Explorer