第80部分- Linux x86 64位汇编 使用文件
我们知道C或者C++进行程序设计有函数fopen/read/write。汇编中如何呢?
汇编语言程序中处理数据文件时必须使用特定的顺序。通过Linux系统调用执行。
打开
先来按下open系统调用如下:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
通过arch/x86/entry/syscalls/syscall_64.tbl中查看:
2 common open __x64_sys_open
系统调用号是2.
打开模式如下:
例如创建文件并且打开用于读写访问:
可以使用$0102
新数据文件追加到已有的文件,可以使用:
$02002
模式可以入下:
#define S_IRWXU 00700 文件所有者可读可写可执行
#define S_IRUSR 00400 文件所有者可读
#define S_IWUSR 00200 文件所有者可写
#define S_IXUSR 00100 文件所有者可执行
#define S_IRWXG 00070 文件用户组可写可读可执行
#define S_IRGRP 00040 文件用户组可读
#define S_IWGRP 00020 文件用户组可写
#define S_IXGRP 00010 文件用户组可执行
#define S_IRWXO 00007 其他用户可写可读可执行
#define S_IROTH 00004 其他用户可读
#define S_IWOTH 00002 其他用户可写
#define S_IXOTH 00001 其他用户可执行
-
- read文件
读文件使用系统调用
ssize_t read(int fd, void *buf, size_t count);
系统调用号是0.
写入文件
写入文件使用系统调用write.
ssize_t write(int fd, const void *buf, size_t count);
系统调用号是1.
关闭文件
int close(int fd);
系统调用号是3.
我们来看下示例如下。
写示例
.section .data
filename:
.asciz "cpuid.txt"
output:
.asciz "The processor Vendor ID is 'xxxxxxxxxxxx'\n"
.section .bss
.lcomm filehandle, 4
.section .text
.globl _start
_start:
movl $0, %eax
cpuid;//调用cpuid指令
movl $output, %edi;//保存结果到字符串中
movl %ebx, 28(%edi)
movl %edx, 32(%edi)
movl %ecx, 36(%edi)
movl $2, %eax;//打开文件,系统调用open
movq $filename, %rdi;//文件名
movq $01101, %rsi;//打开模式,重新创建用于写。
movq $0644, %rdx;//权限。
syscall
test %eax, %eax;//测试返回值
js badfile
movl %eax, filehandle;//否则返回保存eax文件句柄到filehandle
movl $1, %eax;//进行写
movq filehandle, %rdi;//参数句柄
movq $output, %rsi;//参数字符串
movq $42, %rdx;//参数字符串数量
syscall
test %eax, %eax
js badfile
movq $3, %rax;//关闭
movq filehandle, %rdi;//对应的句柄
syscall
badfile:
movq %rax, %rbx
movq $60, %rax
syscall;//退出
as -g -o cpuidfile.o cpuidfile.s
ld -o cpuidfile cpuidfile.o
然后执行,
./cpuidfile可以发现执行成功。
读写示例
读后写入文件的示例
.section .bss
.lcomm buffer, 10
.lcomm filehandle, 4
.section .text
.globl _start
_start:
nop
movq %rsp, %rbp;//赋值rsp给rbp。
movl $2, %eax;//打开文件open系统调用
movq 16(%rbp), %rdi;//使用输入的参数为文件名字
movq $00, %rsi;//flag为只读
movq $0444, %rdx;//权限为只读
syscall
test %eax, %eax
js badfile
movl %eax, filehandle;//保存文件句柄,给read/write的系统调用使用
read_loop:
movl $0, %eax
movq filehandle, %rdi;//文件句柄
movq $buffer, %rsi
movq $10, %rdx;//每次10个
syscall
test %eax, %eax
jz done;//读完跳出
js done
movl %eax, %edx
movl $1, %eax;//写入系统调用
movq $1, %rdi;//写到stdout
movq $buffer, %rsi;//字符串
syscall
test %eax, %eax
js badfile
jmp read_loop
done:
movl $3, %eax
movq filehandle, %rdi
syscall
badfile:
movl %eax, %ebx
movl $60, %eax
syscall
as -g -o readwrite.o readwrite.s
ld -o readwrite readwrite.o
这里需要有个参数,就是一个文件名字。
将文件中的内容独处后放入到缓存中,然后将将缓存中写到stdout中。每次10个字节,使用更大块的长度可以减少read调用次数提高性能。
读后处理写入示例
相比上一个示例,这个示例增加数据处理的步骤。从文件读取数据,然后处理数据,最后把数据写入到另一个文件中。
.section .bss
.lcomm buffer, 10
.lcomm infilehandle, 8
.lcomm outfilehandle, 8
.lcomm size, 4
.section .text
.globl _start
_start:
# open input file, specified by the first command line param
movl $2, %eax;//open系统调用
movq 16(%rsp), %rdi;//第一个参数,程序运行后栈指向的分别是参数数量/程序名字
movq $00, %rsi;//只读
movq $0444, %rdx;//权限
syscall
test %rax, %rax
js badfile
movq %rax, infilehandle;//保存输入文件的句柄
movl $2, %eax;// open系统调用,打开另一个文件
movq 24(%rsp), %rdi;// 第二个参数
movq $01101, %rsi;//创建新文件,模式是写,
movq $0644, %rdx;//权限
syscall
test %rax, %rax;//出现负数则跳转
js badfile
movq %rax, outfilehandle;//保存输出文件句柄
# 从输入文件句柄中读取
read_loop:
movl $0, %eax;// read系统调用
movq infilehandle, %rdi
movq $buffer, %rsi
movq $10, %rdx;//每次读取10个字节
syscall
test %rax, %rax
jz done
js badfile
movl %eax, size
movq $buffer,%rdi;//字符串参数指针
movq size,%rsi;// 第二个参数,已读出的字节数量。
call convert;//调用convert函数
# 将转换后的数据存放 output file
movl $1, %eax ;// write系统调用
movq outfilehandle, %rdi
movq $buffer,%rsi
syscall
test %rax,%rax
js badfile
jmp read_loop
done:;//读取完毕后跳转到done处
movl $3,%eax
movq outfilehandle,%rdi
syscall;//关闭输出文件句柄
movl $3,%eax
movq infilehandle,%rdi
syscall;//关闭输入文件句柄
badfile:
movl %eax,%ebx
movl $60,%eax
syscall
.type convert,@function
convert:
movq %rsi,%rcx;//第二个参数是字符数量,赋值为rcx.
movq %rdi,%rsi;//第二个参数是rdi指向源字符串的内存位置,要赋值给rsi
convert_loop:
lodsb;//加载字符串字节到al寄存器,rsi指向源字符串的内存位置
cmpb $0x61,%al;//是否小于0x61,即97,小于则跳过
jl skip
cmpb $0x7a,%al;//是否大于于0x7a,即122,大于也跳过
jg skip
sub $0x20,%al;//改成小写
skip:
stosb;//重新加载al到rdi的字符串,就是源字符串地址。
loop convert_loop
ret
as -g -o readchange.o readchange.s
ld -o readchange readchange.o
可以执行如下
#readchange readchange.s out.txt
会将readchange.s文件中的内容转为大写并保存到了Out.txt文件中。